From 6213be5fe03c764b385b04fce68cb4687de46a10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Tue, 29 Oct 2024 17:59:23 +0100 Subject: [PATCH 0001/1144] autorisation du mode paysage --- app.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.json b/app.json index ff7ee71a1..83421fc94 100644 --- a/app.json +++ b/app.json @@ -4,7 +4,7 @@ "slug": "papillonvex", "scheme": "papillon", "version": "7.3.0", - "orientation": "portrait", + "orientation": "default", "icon": "./assets/icon.png", "userInterfaceStyle": "automatic", "primaryColor": "#32AB8E", From 8bbd009c69d6c12bcb47bbc2f40c15a50b317239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Tue, 29 Oct 2024 18:16:15 +0100 Subject: [PATCH 0002/1144] =?UTF-8?q?nouvelle=20formule=20de=20d=C3=A9tect?= =?UTF-8?q?ion=20d'une=20tablette?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Home/Header.tsx | 7 +++++-- src/router/helpers/PapillonTabNavigator.tsx | 7 +++++-- src/router/screens/account/stack.tsx | 7 +++++-- src/views/account/Homeworks/HomeworksHeader.tsx | 9 ++++++--- src/views/account/Lessons/LessonsHeader.tsx | 7 +++++-- 5 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/components/Home/Header.tsx b/src/components/Home/Header.tsx index 17e2226f7..a3ccc32e5 100644 --- a/src/components/Home/Header.tsx +++ b/src/components/Home/Header.tsx @@ -44,8 +44,11 @@ const Header: React.FC<{ const [addonsTitle, setAddonsTitle] = useState([]); const [click, setClick] = useState(false); - const dims = Dimensions.get("window"); - const tablet = dims.width > 600; + const dims = Dimensions.get("screen"); + const tabletWidth = dims.width; + const tabletHeight = dims.height; + const tabletDiagl = (tabletWidth / tabletHeight) * 10; + const tablet = tabletDiagl >= 6.9; useEffect(() => { // On récupère le fichier principal de chaque extension. diff --git a/src/router/helpers/PapillonTabNavigator.tsx b/src/router/helpers/PapillonTabNavigator.tsx index eb3997c1d..d793e110f 100644 --- a/src/router/helpers/PapillonTabNavigator.tsx +++ b/src/router/helpers/PapillonTabNavigator.tsx @@ -430,8 +430,11 @@ const BottomTabNavigator: React.ComponentType = ({ screenOptions, ...rest }) => { - const dims = Dimensions.get("window"); - const tablet = dims.width > 600; + const dims = Dimensions.get("screen"); + const tabletWidth = dims.width; + const tabletHeight = dims.height; + const tabletDiagl = (tabletWidth / tabletHeight) * 10; + const tablet = tabletDiagl >= 6.9; const { state, descriptors, navigation, NavigationContent } = useNavigationBuilder(TabRouter, { diff --git a/src/router/screens/account/stack.tsx b/src/router/screens/account/stack.tsx index 52602b913..9b032c2d1 100644 --- a/src/router/screens/account/stack.tsx +++ b/src/router/screens/account/stack.tsx @@ -65,8 +65,11 @@ const AccountStackScreen: Screen<"AccountStack"> = () => { } }, [params]); - const dims = Dimensions.get("window"); - const tablet = dims.width > 600; + const dims = Dimensions.get("screen"); + const tabletWidth = dims.width; + const tabletHeight = dims.height; + const tabletDiagl = (tabletWidth / tabletHeight) * 10; + const tablet = tabletDiagl >= 6.9; let newAccountScreens = screens; diff --git a/src/views/account/Homeworks/HomeworksHeader.tsx b/src/views/account/Homeworks/HomeworksHeader.tsx index 919e76588..03aae4709 100644 --- a/src/views/account/Homeworks/HomeworksHeader.tsx +++ b/src/views/account/Homeworks/HomeworksHeader.tsx @@ -13,15 +13,18 @@ import { epochWMToCalendarWeekNumber } from "@/utils/epochWeekNumber"; const HeaderCalendar: React.FC<{ epochWeekNumber: number, oldPageIndex: number, showPicker: () => void, changeIndex: (index: number) => void }> = ({ epochWeekNumber, oldPageIndex, showPicker, changeIndex }) => { const { colors } = useTheme(); - const dims = Dimensions.get("window"); - const tablet = dims.width > 600; + const dims = Dimensions.get("screen"); + const tabletWidth = dims.width; + const tabletHeight = dims.height; + const tabletDiagl = (tabletWidth / tabletHeight) * 10; + const tablet = tabletDiagl >= 6.9; const index = epochWeekNumber; return ( = () => { - const dims = Dimensions.get("window"); - const tablet = dims.width > 600; + const dims = Dimensions.get("screen"); + const tabletWidth = dims.width; + const tabletHeight = dims.height; + const tabletDiagl = (tabletWidth / tabletHeight) * 10; + const tablet = tabletDiagl >= 6.9; return ( Date: Tue, 29 Oct 2024 18:27:00 +0100 Subject: [PATCH 0003/1144] =?UTF-8?q?int=C3=A9gration=20changement=20autom?= =?UTF-8?q?atique=20de=20rotation=20sur=20Header.tsx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Home/Header.tsx | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/components/Home/Header.tsx b/src/components/Home/Header.tsx index a3ccc32e5..9107a167a 100644 --- a/src/components/Home/Header.tsx +++ b/src/components/Home/Header.tsx @@ -43,12 +43,21 @@ const Header: React.FC<{ const [addons] = useState([]); const [addonsTitle, setAddonsTitle] = useState([]); const [click, setClick] = useState(false); + const [tablet, setTablet] = useState(false); - const dims = Dimensions.get("screen"); - const tabletWidth = dims.width; - const tabletHeight = dims.height; - const tabletDiagl = (tabletWidth / tabletHeight) * 10; - const tablet = tabletDiagl >= 6.9; + useEffect(() => { + const updateTabletOrientation = () => { + const dims = Dimensions.get("screen"); + const tabletWidth = dims.width; + const tabletHeight = dims.height; + const tabletDiagl = (tabletWidth / tabletHeight) * 10; + setTablet(tabletDiagl >= 6.9); + }; + updateTabletOrientation(); // 1ère fois qu'on arrive sur la page + + const subscription = Dimensions.addEventListener("change", updateTabletOrientation); // Dès qu'on change de rotation + return () => subscription.remove(); // Permet d'éviter les fuites de mémoire + }, []); useEffect(() => { // On récupère le fichier principal de chaque extension. From dc1328611fd2c51ece1c26a2626c84a80b2d38b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Tue, 29 Oct 2024 18:32:02 +0100 Subject: [PATCH 0004/1144] =?UTF-8?q?int=C3=A9gration=20changement=20autom?= =?UTF-8?q?atique=20de=20rotation=20sur=20PapillonTabNavigator.tsx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/helpers/PapillonTabNavigator.tsx | 22 +++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/router/helpers/PapillonTabNavigator.tsx b/src/router/helpers/PapillonTabNavigator.tsx index d793e110f..0dfa43cbc 100644 --- a/src/router/helpers/PapillonTabNavigator.tsx +++ b/src/router/helpers/PapillonTabNavigator.tsx @@ -8,7 +8,7 @@ import { } from "@react-navigation/native"; import { Text } from "react-native"; -import React, { useEffect, useRef } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { View, Dimensions } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; @@ -430,11 +430,21 @@ const BottomTabNavigator: React.ComponentType = ({ screenOptions, ...rest }) => { - const dims = Dimensions.get("screen"); - const tabletWidth = dims.width; - const tabletHeight = dims.height; - const tabletDiagl = (tabletWidth / tabletHeight) * 10; - const tablet = tabletDiagl >= 6.9; + const [tablet, setTablet] = useState(false); + + useEffect(() => { + const updateTabletOrientation = () => { + const dims = Dimensions.get("screen"); + const tabletWidth = dims.width; + const tabletHeight = dims.height; + const tabletDiagl = (tabletWidth / tabletHeight) * 10; + setTablet(tabletDiagl >= 6.9); + }; + updateTabletOrientation(); // 1ère fois qu'on arrive sur la page + + const subscription = Dimensions.addEventListener("change", updateTabletOrientation); // Dès qu'on change de rotation + return () => subscription.remove(); // Permet d'éviter les fuites de mémoire + }, []); const { state, descriptors, navigation, NavigationContent } = useNavigationBuilder(TabRouter, { From 9a6fd6c6d67aad9801801ad768d1617367bfcc12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Wed, 30 Oct 2024 15:14:29 +0100 Subject: [PATCH 0005/1144] quelques optimisations --- src/router/helpers/PapillonTabNavigator.tsx | 57 +++++++++++++-------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/src/router/helpers/PapillonTabNavigator.tsx b/src/router/helpers/PapillonTabNavigator.tsx index 0dfa43cbc..7a36ad915 100644 --- a/src/router/helpers/PapillonTabNavigator.tsx +++ b/src/router/helpers/PapillonTabNavigator.tsx @@ -233,32 +233,20 @@ const BasePapillonBar: React.FC, "N ); }; -export const LargePapillonBar: React.FC, "NavigationContent">> = ({ state, descriptors, navigation }) => { +const LargePapillonBar: React.FC, "NavigationContent">> = ({ state, descriptors, navigation }) => { const theme = useTheme(); const insets = useSafeAreaInsets(); const account = useCurrentAccount(store => store.account!); - const [shouldShowLabel, setShouldShowLabel] = React.useState(true); - - const [transparentTabBar, setTransparentTabBar] = React.useState(false); - const [hideTabBar, setHideTabBar] = React.useState(false); - - const [showTabBackground, setShowTabBackground] = React.useState(false); + const [hideTabBar, setHideTabBar] = useState(false); useEffect(() => { - setShouldShowLabel(!account.personalization.hideTabTitles); - setShowTabBackground(account.personalization.showTabBackground ?? false); - setTransparentTabBar(account.personalization.transparentTabBar ?? false); setHideTabBar(account.personalization.hideTabBar ?? false); }, [account.personalization]); const bottomAnim = useSharedValue(1); - const animatedStyles = useAnimatedStyle(() => ({ - opacity: withTiming(bottomAnim.value, { duration: 200 }), - })); - - React.useEffect(() => { + useEffect(() => { bottomAnim.value = hideTabBar ? 0 : 1; }, [hideTabBar]); @@ -446,6 +434,39 @@ const BottomTabNavigator: React.ComponentType = ({ return () => subscription.remove(); // Permet d'éviter les fuites de mémoire }, []); + if (tablet) { + const { state, descriptors, navigation, NavigationContent } = + useNavigationBuilder(TabRouter, { + initialRouteName, + backBehavior, + children, + screenOptions, + }); + + return ( + + + + + + + ); + } + const { state, descriptors, navigation, NavigationContent } = useNavigationBuilder(TabRouter, { initialRouteName, @@ -472,11 +493,7 @@ const BottomTabNavigator: React.ComponentType = ({ navigation = { navigation } descriptors = { descriptors } /> - {!tablet ? - - : - - } + ); From e3267517f5de45e19316b8a519ff45ef266dfd1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Mon, 11 Nov 2024 16:48:53 +0100 Subject: [PATCH 0006/1144] code correct pour les devoirs --- src/views/account/Homeworks/Homeworks.tsx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 7900b1bc8..581991e56 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -62,11 +62,17 @@ const formatDate = (date: string | number | Date): string => { const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const flatListRef: React.MutableRefObject = useRef(null) as any as React.MutableRefObject; - const { width } = Dimensions.get("window"); - const finalWidth = width - (width > 600 ? ( - 320 > width * 0.35 ? width * 0.35 : + + const dims = Dimensions.get("screen"); + const tabletWidth = dims.width; + const tabletHeight = dims.height; + const tabletDiagl = (tabletWidth / tabletHeight) * 10; + const tablet = tabletDiagl >= 6.9; + const finalWidth = tabletWidth - (tablet ? ( + 320 > tabletWidth * 0.35 ? tabletWidth * 0.35 : 320 ) : 0); + const insets = useSafeAreaInsets(); const outsideNav = route.params?.outsideNav; @@ -108,7 +114,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { length: finalWidth, offset: finalWidth * index, index, - }), [width]); + }), [finalWidth]); const keyExtractor = useCallback((item: any) => item.toString(), []); @@ -444,7 +450,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { ]} layout={animPapillon(LinearTransition)} > - {width > 370 ? "Semaine" : "sem."} + {tabletWidth > 370 ? "Semaine" : "sem."} = ({ route, navigation }) => { /> } - {showPickerButtons && !searchHasFocus && width > 330 && + {showPickerButtons && !searchHasFocus && tabletWidth > 330 && Date: Mon, 11 Nov 2024 16:56:13 +0100 Subject: [PATCH 0007/1144] affichage correct de l'emploi du temps --- src/views/account/Lessons/Lessons.tsx | 106 +++++++++++++++----------- 1 file changed, 60 insertions(+), 46 deletions(-) diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index 58cc3082c..21aa4a7a0 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -39,6 +39,16 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const loadedWeeks = useRef>(new Set()); const currentlyLoadingWeeks = useRef>(new Set()); + const dims = Dimensions.get("screen"); + const tabletWidth = dims.width; + const tabletHeight = dims.height; + const tabletDiagl = (tabletWidth / tabletHeight) * 10; + const tablet = tabletDiagl >= 6.9; + const finalWidth = tabletWidth - (tablet ? ( + 320 > tabletWidth * 0.35 ? tabletWidth * 0.35 : + 320 + ) : 0); + useEffect(() => { // add all week numbers in timetables to loadedWeeks for (const week in timetables) { @@ -77,8 +87,8 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const loadTimetableWeek = async (weekNumber: number, force = false) => { if ( (currentlyLoadingWeeks.current.has(weekNumber) || - loadedWeeks.current.has(weekNumber)) && - !force + loadedWeeks.current.has(weekNumber)) && + !force ) { return; } @@ -127,51 +137,55 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { return date; }); }); - const renderItem = useCallback(({ item: date }: { item: Date }) => { - const weekNumber = getWeekFromDate(date); - return ( - - 0 - } - refreshAction={() => loadTimetableWeek(weekNumber, true)} - loading={loadingWeeks.includes(weekNumber)} - /> - - ); - }, - [ - pickerDate, - timetables, - loadingWeeks, - outsideNav, - insets, - getAllLessonsForDay, - loadTimetableWeek, - ], + const renderItem = useCallback( + ({ item: date }: { item: Date }) => { + const weekNumber = getWeekFromDate(date); + return ( + + 0 + } + refreshAction={() => loadTimetableWeek(weekNumber, true)} + loading={loadingWeeks.includes(weekNumber)} + /> + + ); + }, + [ + pickerDate, + timetables, + loadingWeeks, + outsideNav, + insets, + finalWidth, + getAllLessonsForDay, + loadTimetableWeek, + ] ); - const onViewableItemsChanged = useCallback(({ viewableItems }: { viewableItems: ViewToken[] }) => { - if (viewableItems.length > 0) { - const newDate = viewableItems[0].item; - setPickerDate(newDate); - loadTimetableWeek(getWeekFromDate(newDate), false); - } - }, - [loadTimetableWeek], + const onViewableItemsChanged = useCallback( + ({ viewableItems }: { viewableItems: ViewToken[] }) => { + if (viewableItems.length > 0) { + const newDate = viewableItems[0].item; + setPickerDate(newDate); + loadTimetableWeek(getWeekFromDate(newDate), false); + } + }, + [loadTimetableWeek] ); - const getItemLayout = useCallback((_: any, index: number) => ({ - length: Dimensions.get("window").width, - offset: Dimensions.get("window").width * index, - index, - }), - [], + const getItemLayout = useCallback( + (_: any, index: number) => ({ + length: finalWidth, + offset: finalWidth * index, + index, + }), + [finalWidth] ); return ( @@ -238,8 +252,8 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { label: "Importer un iCal", onPress: () => { navigation.navigate("LessonsImportIcal", {}); - } - } + }, + }, ]} > = ({ route, navigation }) => { newDate.setHours(0, 0, 0, 0); setPickerDate(newDate); const index = data.findIndex( - (d) => d.getTime() === newDate.getTime(), + (d) => d.getTime() === newDate.getTime() ); if (index !== -1) { flatListRef.current?.scrollToIndex({ index, animated: true }); From 2dacdacb6a252ada90eb3973f2f1ab50e13f44a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Mon, 11 Nov 2024 17:01:28 +0100 Subject: [PATCH 0008/1144] =?UTF-8?q?bon=20code=20pour=20d=C3=A9tecter=20l?= =?UTF-8?q?es=20tablet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/helpers/PapillonTabNavigator.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/router/helpers/PapillonTabNavigator.tsx b/src/router/helpers/PapillonTabNavigator.tsx index 5ad09f7ec..38ffdf0db 100644 --- a/src/router/helpers/PapillonTabNavigator.tsx +++ b/src/router/helpers/PapillonTabNavigator.tsx @@ -422,8 +422,11 @@ const BottomTabNavigator: React.ComponentType = ({ screenOptions, ...rest }) => { - const dims = Dimensions.get("window"); - const tablet = dims.width > 600; + const dims = Dimensions.get("screen"); + const tabletWidth = dims.width; + const tabletHeight = dims.height; + const tabletDiagl = (tabletWidth / tabletHeight) * 10; + const tablet = tabletDiagl >= 6.9; const mainNavigator = useRef(null); // Track previous index to determine direction From 9fd16ab4869fabc8ca37057de260111630950440 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 17 Nov 2024 19:52:38 +0100 Subject: [PATCH 0009/1144] =?UTF-8?q?affichage=20correct=20de=20"Voici=20l?= =?UTF-8?q?es=20=C3=A9tablissements[...]"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../login/pronote/PronoteInstanceSelector.tsx | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/src/views/login/pronote/PronoteInstanceSelector.tsx b/src/views/login/pronote/PronoteInstanceSelector.tsx index b73d6cf6a..8c2a87ae0 100644 --- a/src/views/login/pronote/PronoteInstanceSelector.tsx +++ b/src/views/login/pronote/PronoteInstanceSelector.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; import type { Screen } from "@/router/helpers/types"; -import { TextInput, TouchableOpacity, View, StyleSheet, ActivityIndicator, Keyboard, KeyboardEvent } from "react-native"; +import { TextInput, TouchableOpacity, View, StyleSheet, ActivityIndicator, Keyboard, KeyboardEvent, SafeAreaView } from "react-native"; import pronote from "pawnote"; import Reanimated, { LinearTransition, FlipInXDown, FadeInUp, FadeOutUp, ZoomIn, ZoomOut, Easing, ZoomInEasyDown } from "react-native-reanimated"; import determinateAuthenticationView from "@/services/pronote/determinate-authentication-view"; @@ -101,17 +101,10 @@ const PronoteInstanceSelector: Screen<"PronoteInstanceSelector"> = ({ }, [search, originalInstances]); return ( - + - + {!keyboardOpen && = ({ } @@ -264,7 +256,7 @@ const PronoteInstanceSelector: Screen<"PronoteInstanceSelector"> = ({ )} - + ); }; @@ -272,9 +264,7 @@ const styles = StyleSheet.create({ container: { flex: 1, alignItems: "center", - justifyContent: "center", gap: 20, - paddingTop: -40, }, overScrollContainer: { From a492677f368dd3841be3723670b0ed1dbe2d5c81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 17 Nov 2024 19:55:48 +0100 Subject: [PATCH 0010/1144] Affichage correct du "Quelle est ta[...]" --- src/views/welcome/ColorSelector.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/welcome/ColorSelector.tsx b/src/views/welcome/ColorSelector.tsx index a8d7a50e1..523bac445 100644 --- a/src/views/welcome/ColorSelector.tsx +++ b/src/views/welcome/ColorSelector.tsx @@ -165,6 +165,7 @@ const ColorSelector: Screen<"ColorSelector"> = ({ route, navigation }) => { message={"Quelle est ta couleur préférée ?"} numberOfLines={1} width={280} + offsetTop={"10%"} /> From 44249b17f54f2ab641ac22b2942e914e8a9cdbfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 17 Nov 2024 19:59:39 +0100 Subject: [PATCH 0011/1144] =?UTF-8?q?m=C3=AAme=20chose=20pour=20skolengo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../login/skolengo/SkolengoInstanceSelector.tsx | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/views/login/skolengo/SkolengoInstanceSelector.tsx b/src/views/login/skolengo/SkolengoInstanceSelector.tsx index c3d94611a..3b3574e2b 100644 --- a/src/views/login/skolengo/SkolengoInstanceSelector.tsx +++ b/src/views/login/skolengo/SkolengoInstanceSelector.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; import type { Screen } from "@/router/helpers/types"; -import { TextInput, TouchableOpacity, View, StyleSheet, ActivityIndicator, Keyboard, KeyboardEvent } from "react-native"; +import { TextInput, TouchableOpacity, View, StyleSheet, ActivityIndicator, Keyboard, KeyboardEvent, SafeAreaView } from "react-native"; import Reanimated, { LinearTransition, FlipInXDown, FadeInUp, FadeOutUp, ZoomIn, ZoomOut, Easing, ZoomInEasyDown } from "react-native-reanimated"; import MaskStars from "@/components/FirstInstallation/MaskStars"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; @@ -120,17 +120,10 @@ const SkolengoInstanceSelector: Screen<"SkolengoInstanceSelector"> = ({ }, [search, geoInstances]); return ( - + - + {!keyboardOpen && = ({ )} - + ); }; @@ -305,9 +298,7 @@ const styles = StyleSheet.create({ container: { flex: 1, alignItems: "center", - justifyContent: "center", gap: 20, - paddingTop: -40, }, overScrollContainer: { From 29b1b832d0a8e58f33c3cc56ca20c6e8673cd692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 17 Nov 2024 20:07:30 +0100 Subject: [PATCH 0012/1144] Affichage correct de "Dans quelle ville[...]" --- src/views/login/pronote/PronoteManualLocation.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/views/login/pronote/PronoteManualLocation.tsx b/src/views/login/pronote/PronoteManualLocation.tsx index 3d58e0973..5937d4f8a 100644 --- a/src/views/login/pronote/PronoteManualLocation.tsx +++ b/src/views/login/pronote/PronoteManualLocation.tsx @@ -108,14 +108,14 @@ const PronoteManualLocation: Screen<"PronoteManualLocation"> = ({ navigation }) style={[ { flex: 1, - paddingTop: insets.top + 44, + paddingTop: "14%", justifyContent: "center", } ]} > - {municipalities.results.length == 0 && ( + {!keyboardOpen && municipalities.results.length == 0 && ( = ({ navigation }) numberOfLines={2} width={250} noFlex - style={{ marginTop: 20 }} /> )} From 9401516244f8f3c7adbfe4eb0b5e7ce87834490b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 17 Nov 2024 20:13:04 +0100 Subject: [PATCH 0013/1144] affichage correct de la page pour indiquer l'url --- src/views/login/pronote/PronoteManualURL.tsx | 49 ++++++++++++++------ 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/src/views/login/pronote/PronoteManualURL.tsx b/src/views/login/pronote/PronoteManualURL.tsx index 84b7ce8d9..3d4003640 100644 --- a/src/views/login/pronote/PronoteManualURL.tsx +++ b/src/views/login/pronote/PronoteManualURL.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; import type { RouteParameters, Screen } from "@/router/helpers/types"; -import { TextInput, View, StyleSheet, TouchableOpacity } from "react-native"; +import { TextInput, View, StyleSheet, TouchableOpacity, KeyboardEvent, Keyboard } from "react-native"; import { useTheme } from "@react-navigation/native"; import determinateAuthenticationView from "@/services/pronote/determinate-authentication-view"; @@ -21,9 +21,28 @@ const PronoteManualURL: Screen<"PronoteManualURL"> = ({ route, navigation }) => const {colors} = theme; const insets = useSafeAreaInsets(); const [instanceURL, setInstanceURL] = useState(""); + const [keyboardOpen, setKeyboardOpen] = useState(false); const {showAlert} = useAlert(); + const keyboardDidShow = (event: KeyboardEvent) => { + setKeyboardOpen(true); + }; + + const keyboardDidHide = () => { + setKeyboardOpen(false); + }; + + useEffect(() => { + Keyboard.addListener("keyboardDidShow", keyboardDidShow); + Keyboard.addListener("keyboardDidHide", keyboardDidHide); + + return () => { + Keyboard.removeAllListeners("keyboardDidShow"); + Keyboard.removeAllListeners("keyboardDidHide"); + }; + }, []); + // find url in route params useEffect(() => { if (route.params?.url) { @@ -80,24 +99,26 @@ const PronoteManualURL: Screen<"PronoteManualURL"> = ({ route, navigation }) => - - - + + + {!keyboardOpen && ( + + )} Date: Wed, 4 Dec 2024 15:10:19 +0100 Subject: [PATCH 0014/1144] new modern PULL_REQUEST_TEMPLATE.md --- .github/PULL_REQUEST_TEMPLATE.md | 58 +++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index b62ba586b..2f89dbfd6 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,29 +1,49 @@ -# 🚀 Nouvelle Pull Request +# ✨ Nouvelle Pull Request -Proposez vos modifications pour améliorer Papillon +Merci de contribuer à l'amélioration de Papillon ! -## Informations importantes +Tu te poses des questions sur les pull requests (PR) ? Une documentation a spécialement été crée ici => https://gitbook.getpapillon.xyz/organisation/outils-internes/github -Merci de vous référer à la documentation sur la contribution si vous avez des questions à propos des pull requests (https://gitbook.getpapillon.xyz/organisation/outils-internes/github) +## Avant toute chose... -## Checklist d'avant pull request +Pour nous aider à tester ta PR, merci de cocher une des cases suivantes (_en rajoutant un `x` dans les crochets_) : -Veuillez cocher toutes les cases applicables en remplaçant [ ] par [x]. +Type de pull request : -- [ ] Vous avez testé de build le projet avec vos modifications et ce build **a réussi** -- [ ] Vous respectez les conventions de codage et de nommage du projet -- [ ] Vous utilisez la **tabulation** pour l'indentation afin de maintenir un code lisible -- [ ] Cette pull request **n'est pas un duplicata** d'une autre -- [ ] Cette pull request est prête à être **revue** (review) et **fusionnée** (merge) -- [ ] Il n'y a pas de **`TODO`** (aka des annotations pour du code manquant) dans vos modifications -- [ ] Il n'y a pas **d'erreurs de langue** dans votre code (grammaire, vocabulaire, conjugaison, orthographe) -- [ ] Les détails des changements ont été décrits ci-dessous -- [ ] Cette pull-request n'est pas une **"breaking-change"** (des modifications qui vont entraîner la modification du fonctionnement de certaines fonctionnalités déjà existantes) +- [ ] **Breaking change** (_des modifications avec un impact sur les fonctionnalités actuelles_) +- [ ] **Feat** (_ajoute une amélioration/nouveauté_) +- [ ] **Fix** (_permet de corriger un bug_) +- [ ] **Chore** (_des modifications en dehors du dossier `src`_) +- [ ] **Styles** (_change/ajoute du style_) -## Changelogs proposés +## Résumé des changements effectués -Décrivez les modifications que vous avez effectuées. + -## Informations supplémentaires +_Les modifications_ -Ajoutez ici toute information supplémentaire si nécessaire. +## Capture(s) d'écran (pour rendre le test de ta PR rapide) + +_Le(s) capture(s) d'écran_ + +## Issue(s) en rapport + +> [!NOTE] +> +> Cette section permet de "linker" des issues à ta PR. Cela signifie qu'une fois ta PR mergée, les issues listées ci-dessous seront automatiquement fermées + +- Closed #(_le numéro de l'issue_) + +_S'il y en a plusieurs, continuer à les lister_ + +- Closed #(_le numéro de l'issue_) + + From 9e451f1b17a114c3f9d1c56f152d1bf2122c6fd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Wed, 4 Dec 2024 15:10:55 +0100 Subject: [PATCH 0015/1144] indent FUNDING.yaml --- .github/FUNDING.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/FUNDING.yaml b/.github/FUNDING.yaml index cd15ae767..2948bd41b 100644 --- a/.github/FUNDING.yaml +++ b/.github/FUNDING.yaml @@ -1 +1 @@ -ko_fi: thepapillonapp \ No newline at end of file +ko_fi: thepapillonapp From b8a984161478e7ebc0a88c3d4b9af51e35b0e70f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Wed, 4 Dec 2024 15:11:32 +0100 Subject: [PATCH 0016/1144] Delete preview.yml --- .github/workflows/preview.yml | 40 ----------------------------------- 1 file changed, 40 deletions(-) delete mode 100644 .github/workflows/preview.yml diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml deleted file mode 100644 index 31cde8b79..000000000 --- a/.github/workflows/preview.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: preview -on: pull_request - -jobs: - update: - name: EAS Update - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: write - steps: - - name: Check for EXPO_TOKEN - run: | - if [ -z "${{ secrets.EXPO_TOKEN }}" ]; then - echo "You must provide an EXPO_TOKEN secret linked to this project's Expo account in this repo's secrets. Learn more: https://docs.expo.dev/eas-update/github-actions" - exit 1 - fi - - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: 18.x - cache: yarn - - - name: Setup EAS - uses: expo/expo-github-action@v8 - with: - eas-version: latest - token: ${{ secrets.EXPO_TOKEN }} - - - name: Install dependencies - run: yarn install - - - name: Create preview - uses: expo/expo-github-action/preview@v8 - with: - command: eas update --auto From 500e5429b04935efbdc799271693f7a9363a4d74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Wed, 4 Dec 2024 15:36:08 +0100 Subject: [PATCH 0017/1144] modern bug.yaml --- .github/ISSUE_TEMPLATE/bug.yaml | 88 ++++++++++++++++----------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yaml b/.github/ISSUE_TEMPLATE/bug.yaml index 56d9af7b9..6a45a7db4 100644 --- a/.github/ISSUE_TEMPLATE/bug.yaml +++ b/.github/ISSUE_TEMPLATE/bug.yaml @@ -1,89 +1,89 @@ name: 🐛 Signaler un bug -description: Signalez un bug pour nous aider à améliorer Papillon -title: '[Bug]: ' -labels: [bug] +description: Signaler des bugs nous permet à améliorer Papillon ! +title: "[Bug]: " + body: - type: textarea attributes: label: Description du bug - description: Une description claire et concise du bug. + description: Plus de détails nous permettent de trouver plus vite le bug ! + placeholder: La connexion à mon établissement ne fonctionne pas, j'ai un chargement infini lors de la connexion validations: required: true + - type: textarea attributes: label: Étapes à reproduire - description: 'Étapes pour reproduire le bug :' + description: Comment pouvons-nous reproduire le bug ? placeholder: | - 1. [ex: Etape 1] - 2. [ex: Etape 1] - value: | - 1. - 2. + 1. Ouvrir l'application + 2. Se connecter à l'établissement + 3. Observer le comportement validations: required: true + - type: textarea attributes: label: Comportement attendu - description: Une description claire et concise de ce que vous attendiez. + description: Ce que Papillon doit faire + placeholder: Que la connexion à mon établissement fonctionne et qu'il n'y ait pas de chargement infini validations: required: true + - type: input attributes: label: Appareil + description: Sur quel appareil tu as rencontré ce bug ? placeholder: iPhone 13, Samsung Galaxy S23... validations: required: true + - type: input attributes: - label: Version du système d`exploitation - placeholder: iOS 18, Android 14... - validations: - required: true - - type: input - attributes: - label: Version - placeholder: '7.0.1-beta' - value: '7.0.1-beta' + label: Version du système d'exploitation + description: Tu trouveras ce détail dans : Paramètres (Téléphone) -> À propos (Android)/Général (Apple) + placeholder: iOS 18, Android 15... validations: required: true + - type: dropdown attributes: - label: Environnement + label: Papillon testé depuis + description: Tu as testé/installé Papillon depuis... options: - - Application native Android - - Application native iOS - - Prébuild de développement (iOS/Android) + - Play/Apple Store (Version Stable) + - Play Store/TestFlight (Version Bêta) + - Expo Go (Version Dev) validations: required: true - - type: dropdown + + - type: input attributes: - label: Source de l'application - options: - - Stores (Play Store / App Store) - - Github + label: Version utilisée + description: Pour trouver la version de Papillon il faut aller : Paramètres (de Papillon) -> Version affichée en bas de la page + placeholder: "7.5.0" validations: required: true + - type: dropdown attributes: - label: Service scolaire + label: Service scolaire/cantine + description: Chaque personne dans l'équipe de Papillon gère un service (🎒 = Service scolaire et 🍽️ = Service de cantine) options: - - 🦋 Pronote - - 🟦 EcoleDirecte - - 🟡 Skolengo - - 🔴 Turboself - - 🟣 ARD-GEC - - ⭕ Autre + - 🎒🦋 Pronote + - 🎒🟦 ÉcoleDirecte + - 🎒🟡 Skolengo + - 🎒🏫 Universités et autres (à préciser dans la description du bug) + - 🍽️🔴 Turboself + - 🍽️🟣 ARD + - 🍽️🔵 Izly validations: required: true + - type: textarea attributes: - label: "Captures d'écran / vidéo" - description: "Si possible, ajoutez des captures d'écran pour aider à expliquer votre problème." - validations: - required: false - - type: textarea - attributes: - label: Contexte supplémentaire - description: Des liens ? Des références ? Tout ce qui peut nous donner plus de détails sur le problème que vous rencontrez ! + label: "Capture(s) d'écran / vidéo" + description: Cela permettra que le bug soit trouvé et résolu encore plus vite + placeholder: Il faut cliquer sur l'icône 📎 pour pouvoir importer une/des photo(s)/vidéo(s) validations: required: false From 8cabed368780f225057b008be63cffbde8f18dae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Wed, 4 Dec 2024 15:36:49 +0100 Subject: [PATCH 0018/1144] oups --- .github/ISSUE_TEMPLATE/bug.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yaml b/.github/ISSUE_TEMPLATE/bug.yaml index 6a45a7db4..d01ea3f6e 100644 --- a/.github/ISSUE_TEMPLATE/bug.yaml +++ b/.github/ISSUE_TEMPLATE/bug.yaml @@ -41,7 +41,7 @@ body: - type: input attributes: label: Version du système d'exploitation - description: Tu trouveras ce détail dans : Paramètres (Téléphone) -> À propos (Android)/Général (Apple) + description: Paramètres (du Téléphone) -> À propos (Android)/Général (Apple) placeholder: iOS 18, Android 15... validations: required: true @@ -60,7 +60,7 @@ body: - type: input attributes: label: Version utilisée - description: Pour trouver la version de Papillon il faut aller : Paramètres (de Papillon) -> Version affichée en bas de la page + description: Paramètres (de Papillon) -> Version affichée en bas de la page placeholder: "7.5.0" validations: required: true From c2ec7d03a98e36636d210427f976a1f9a208c460 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Fri, 13 Dec 2024 22:23:11 +0100 Subject: [PATCH 0019/1144] suppression `enhancement.yaml` + rregroupement de ce fichier sur `feature.yaml` --- .github/ISSUE_TEMPLATE/enhancement.yaml | 23 ----------------------- .github/ISSUE_TEMPLATE/feature.yaml | 17 ++++++++++------- 2 files changed, 10 insertions(+), 30 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/enhancement.yaml diff --git a/.github/ISSUE_TEMPLATE/enhancement.yaml b/.github/ISSUE_TEMPLATE/enhancement.yaml deleted file mode 100644 index 5ac3ed2bb..000000000 --- a/.github/ISSUE_TEMPLATE/enhancement.yaml +++ /dev/null @@ -1,23 +0,0 @@ -name: 💅 Amélioration -description: Suggérez une amélioration. -title: '[Enhancement]: ' -labels: [enhancement] -body: - - type: textarea - attributes: - label: Description du problème - description: "Ce n'est pas assez intuitif ? Un contraste n'est pas assez prononcé ?" - validations: - required: true - - type: textarea - attributes: - label: "Description de l'amélioration" - description: Une description claire et concise de l'amélioration. - validations: - required: true - - type: textarea - attributes: - label: Contexte supplémentaire - description: "Ajoutez tout autre contexte ou capture d'écran permettant de clarifier votre question." - validations: - required: false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml index e94ae1608..88f7b1c28 100644 --- a/.github/ISSUE_TEMPLATE/feature.yaml +++ b/.github/ISSUE_TEMPLATE/feature.yaml @@ -1,17 +1,20 @@ -name: ✨ Fonctionnalité -description: Suggérez une fonctionnalité. -title: '[Feature]: ' -labels: [feature] +name: ✨ Amélioration/Fonctionnalité +description: Comment pouvons-nous améliorer Papillon ? +title: "[Feature]: " + body: - type: textarea attributes: label: Description de la fonctionnalité - description: "Ajout d'un générateur de poneys ? D'un calcul de l'aura de l'élève ?" + description: Fait place à ta créativité en détaillant ce que tu veux + placeholder: Ça serait cool une intégration d'un simulateur permettant de savoir si on aura le bac/brevet validations: required: true + - type: textarea attributes: - label: Contexte supplémentaire - description: "Ajoutez tout autre contexte ou design permettant de clarifier votre idée." + label: Davantage de détails ? + description: Une image pour montrer à quoi ça ressemblerait ? Ce n'est pas obligatoire + placeholder: Ah, même si j'ai des idées, je ne suis pas designer 😂 validations: required: false From 67d48c8188692aff66e88ad07bf1655ab24b4d61 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Fri, 13 Dec 2024 22:27:25 +0100 Subject: [PATCH 0020/1144] update `config.yaml` --- .github/ISSUE_TEMPLATE/config.yaml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yaml b/.github/ISSUE_TEMPLATE/config.yaml index 8ee1e2daf..fa7b37ec2 100644 --- a/.github/ISSUE_TEMPLATE/config.yaml +++ b/.github/ISSUE_TEMPLATE/config.yaml @@ -1,11 +1,10 @@ blank_issues_enabled: false + contact_links: - - name: Votre problème concerne une faille de sécurité ? - url: https://github.com/PapillonApp/Papillon/security/advisories/ - about: Merci de nous contacter via ce formulaire. - - name: Vous avez un problème personnel ? + - name: Une faille de sécurité ? + url: https://github.com/PapillonApp/Papillon/security/advisories/new + about: Merci de remplir ce formulaire + + - name: Une question ? Un problème personnel ? url: https://discord.gg/WXntBawAk8 - about: Merci de nous contacter via notre serveur Discord. - - name: Vous avez une question ? - url: https://discord.gg/WXntBawAk8 - about: Merci de nous contacter via notre serveur Discord. \ No newline at end of file + about: L'équipe Papillon est disponible sur Discord, nous te répondrons From caa68f1a7b59b98cb1f0b50f4d84f9c7c5346e1a Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Fri, 13 Dec 2024 22:30:35 +0100 Subject: [PATCH 0021/1144] fix extention `config` incorrect --- .github/ISSUE_TEMPLATE/{config.yaml => config.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/ISSUE_TEMPLATE/{config.yaml => config.yml} (100%) diff --git a/.github/ISSUE_TEMPLATE/config.yaml b/.github/ISSUE_TEMPLATE/config.yml similarity index 100% rename from .github/ISSUE_TEMPLATE/config.yaml rename to .github/ISSUE_TEMPLATE/config.yml From 336d4d9f02f5d192bd3ff6726daea5aa32618768 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Fri, 13 Dec 2024 22:32:37 +0100 Subject: [PATCH 0022/1144] extentions de fichiers incorrect --- .github/ISSUE_TEMPLATE/{bug.yaml => bug.yml} | 0 .github/ISSUE_TEMPLATE/{feature.yaml => feature.yml} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename .github/ISSUE_TEMPLATE/{bug.yaml => bug.yml} (100%) rename .github/ISSUE_TEMPLATE/{feature.yaml => feature.yml} (100%) diff --git a/.github/ISSUE_TEMPLATE/bug.yaml b/.github/ISSUE_TEMPLATE/bug.yml similarity index 100% rename from .github/ISSUE_TEMPLATE/bug.yaml rename to .github/ISSUE_TEMPLATE/bug.yml diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yml similarity index 100% rename from .github/ISSUE_TEMPLATE/feature.yaml rename to .github/ISSUE_TEMPLATE/feature.yml From 588e6221596544e118e38b9a5925ea59da07d5a2 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Fri, 13 Dec 2024 22:34:12 +0100 Subject: [PATCH 0023/1144] encore une fois --- .github/{FUNDING.yaml => FUNDING.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/{FUNDING.yaml => FUNDING.yml} (100%) diff --git a/.github/FUNDING.yaml b/.github/FUNDING.yml similarity index 100% rename from .github/FUNDING.yaml rename to .github/FUNDING.yml From 60c5ab7de3d9c207849b98ed39a7eb44de549810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Fri, 13 Dec 2024 22:56:33 +0100 Subject: [PATCH 0024/1144] =?UTF-8?q?belle=20am=C3=A9lioration=20du=20fich?= =?UTF-8?q?ier=20`checks.yml`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/checks.yml | 57 ++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 30d27a45c..f1eb779d8 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -1,30 +1,51 @@ -name: Checks +name: Checks (TypeScript + ESLint) + on: - push: - branches: [ "main" ] pull_request: - # The branches below must be a subset of the branches above branches: [ "main" ] jobs: - eslint: - name: Typescript build & ESlint scanning + lint: + name: 🛠️ TypeScript and ESLint runs-on: ubuntu-latest - permissions: - contents: read - security-events: write - actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + steps: - - name: Checkout code + - name: 📥 Checkout code uses: actions/checkout@v3 - - name: Pnpm setup - uses: pnpm/action-setup@v2 + - name: ⚙️ Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: 18 + cache: 'npm' + + - name: 💾 Install dependencies + run: npm install + + - name: 🔍 Run TypeScript and ESLint checks + id: lint + run: | + npm run lint > lint.log 2>&1 || true + continue-on-error: true + + - name: 📂 Post logs on failure + if: failure() && steps.lint.outcome == 'failure' + run: | + echo "### :x: TypeScript and ESLint checks failed!" >> comment.txt + echo '```' >> comment.txt + cat lint.log >> comment.txt + echo '```' >> comment.txt + + - name: 👁️‍🗨️ Comment on Pull Request + if: failure() && steps.lint.outcome == 'failure' + uses: marocchino/sticky-pull-request-comment@v2 with: - version: latest + header: ⚠️ Erreur(s) détectée(s) par TypeScript/ESLint ! + message: | + :x: Voici les détails : - - name: Environnement setup - run: pnpm install + file://comment.txt - - name: Typescript and Eslint scanning - run: pnpm lint + - name: 🧹 Clean up + if: always() + run: rm -f lint.log From 4e1771fc611fb0557f2c6a20783f264116e83109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Fri, 13 Dec 2024 23:08:36 +0100 Subject: [PATCH 0025/1144] update des commentaires --- .github/PULL_REQUEST_TEMPLATE.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 2f89dbfd6..fd4bba0dc 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -18,12 +18,6 @@ Type de pull request : ## Résumé des changements effectués - - _Les modifications_ ## Capture(s) d'écran (pour rendre le test de ta PR rapide) @@ -46,4 +40,4 @@ _S'il y en a plusieurs, continuer à les lister_ > [!WARNING] > -> Cette section est réservé aux bots (_liste des bots_), merci de laisser la section comme elle est --> +> Cette section est réservé à un bot (qui affichera un QR Code), merci de laisser la section comme elle est --> From 61a53dc5aebfda39d32bd50a97d7c7f292b0232a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 13 Dec 2024 23:23:54 +0100 Subject: [PATCH 0026/1144] test du workflow checks --- src/addons/addons.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/addons/addons.ts b/src/addons/addons.ts index 9315ca215..7a264ea1f 100644 --- a/src/addons/addons.ts +++ b/src/addons/addons.ts @@ -4,7 +4,7 @@ import {AddonDomain, AddonManifest, AddonPermission, AddonPlacement, AddonPlacem async function init_addons_folder () { if (!(await FileSystem.getInfoAsync(FileSystem.documentDirectory + "addons")).exists) { - await FileSystem.makeDirectoryAsync(FileSystem.documentDirectory + "addons"); + awat FileSystem.makeDirectoryAsync(FileSystem.documentDirectory + "addons"); log("Addons folder initialized at " + FileSystem.documentDirectory + "addons", String((new Error()).stack!)); } } From 5131593787f9f2ef0c06b3429a86876085a3be62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 13 Dec 2024 23:24:18 +0100 Subject: [PATCH 0027/1144] =?UTF-8?q?bien=20s=C3=BBr=20j'annule=20^^?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/addons/addons.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/addons/addons.ts b/src/addons/addons.ts index 7a264ea1f..9315ca215 100644 --- a/src/addons/addons.ts +++ b/src/addons/addons.ts @@ -4,7 +4,7 @@ import {AddonDomain, AddonManifest, AddonPermission, AddonPlacement, AddonPlacem async function init_addons_folder () { if (!(await FileSystem.getInfoAsync(FileSystem.documentDirectory + "addons")).exists) { - awat FileSystem.makeDirectoryAsync(FileSystem.documentDirectory + "addons"); + await FileSystem.makeDirectoryAsync(FileSystem.documentDirectory + "addons"); log("Addons folder initialized at " + FileSystem.documentDirectory + "addons", String((new Error()).stack!)); } } From 3dd5ef3e1d6aa9dd52038beddfbad65bbcdddc82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 13 Dec 2024 23:31:06 +0100 Subject: [PATCH 0028/1144] =?UTF-8?q?=C3=A7a=20devrait=20mieux=20marcher?= =?UTF-8?q?=20l=C3=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/checks.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index f1eb779d8..070234b05 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -25,11 +25,19 @@ jobs: - name: 🔍 Run TypeScript and ESLint checks id: lint run: | - npm run lint > lint.log 2>&1 || true + npm run lint > lint.log 2>&1 || echo "Lint failed" >> lint.log continue-on-error: true + - name: Set lint result + id: lint_result + run: | + if grep -q "Lint failed" lint.log; then + echo "LINT_FAILED=true" >> $GITHUB_ENV + else + echo "LINT_FAILED=false" >> $GITHUB_ENV + - name: 📂 Post logs on failure - if: failure() && steps.lint.outcome == 'failure' + if: env.LINT_FAILED == 'true' run: | echo "### :x: TypeScript and ESLint checks failed!" >> comment.txt echo '```' >> comment.txt @@ -37,7 +45,7 @@ jobs: echo '```' >> comment.txt - name: 👁️‍🗨️ Comment on Pull Request - if: failure() && steps.lint.outcome == 'failure' + if: env.LINT_FAILED == 'true' uses: marocchino/sticky-pull-request-comment@v2 with: header: ⚠️ Erreur(s) détectée(s) par TypeScript/ESLint ! @@ -48,4 +56,4 @@ jobs: - name: 🧹 Clean up if: always() - run: rm -f lint.log + run: rm -f lint.log comment.txt From 27765a61c727fdac5a3d39045693fbc30eb0523b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 13 Dec 2024 23:40:31 +0100 Subject: [PATCH 0029/1144] revert --- .github/workflows/checks.yml | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 070234b05..f1eb779d8 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -25,19 +25,11 @@ jobs: - name: 🔍 Run TypeScript and ESLint checks id: lint run: | - npm run lint > lint.log 2>&1 || echo "Lint failed" >> lint.log + npm run lint > lint.log 2>&1 || true continue-on-error: true - - name: Set lint result - id: lint_result - run: | - if grep -q "Lint failed" lint.log; then - echo "LINT_FAILED=true" >> $GITHUB_ENV - else - echo "LINT_FAILED=false" >> $GITHUB_ENV - - name: 📂 Post logs on failure - if: env.LINT_FAILED == 'true' + if: failure() && steps.lint.outcome == 'failure' run: | echo "### :x: TypeScript and ESLint checks failed!" >> comment.txt echo '```' >> comment.txt @@ -45,7 +37,7 @@ jobs: echo '```' >> comment.txt - name: 👁️‍🗨️ Comment on Pull Request - if: env.LINT_FAILED == 'true' + if: failure() && steps.lint.outcome == 'failure' uses: marocchino/sticky-pull-request-comment@v2 with: header: ⚠️ Erreur(s) détectée(s) par TypeScript/ESLint ! @@ -56,4 +48,4 @@ jobs: - name: 🧹 Clean up if: always() - run: rm -f lint.log comment.txt + run: rm -f lint.log From 97bf063c561dbe10b10b62aa03bb801ef299c2bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 13 Dec 2024 23:40:52 +0100 Subject: [PATCH 0030/1144] =?UTF-8?q?j'esp=C3=A8re=20que=20=C3=A7a=20va=20?= =?UTF-8?q?fonctionner?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/checks.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index f1eb779d8..4cbd04e85 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -25,11 +25,11 @@ jobs: - name: 🔍 Run TypeScript and ESLint checks id: lint run: | - npm run lint > lint.log 2>&1 || true + npm run lint > lint.log 2>&1 continue-on-error: true - name: 📂 Post logs on failure - if: failure() && steps.lint.outcome == 'failure' + if: steps.lint.outcome == 'failure' run: | echo "### :x: TypeScript and ESLint checks failed!" >> comment.txt echo '```' >> comment.txt @@ -37,7 +37,7 @@ jobs: echo '```' >> comment.txt - name: 👁️‍🗨️ Comment on Pull Request - if: failure() && steps.lint.outcome == 'failure' + if: steps.lint.outcome == 'failure' uses: marocchino/sticky-pull-request-comment@v2 with: header: ⚠️ Erreur(s) détectée(s) par TypeScript/ESLint ! From 79e4186d5c4c984c7c067632c34074b7cdaf45cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 13 Dec 2024 23:59:16 +0100 Subject: [PATCH 0031/1144] =?UTF-8?q?test=20d'une=20approche=20diff=C3=A9r?= =?UTF-8?q?ente?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/checks.yml | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 4cbd04e85..345fd35c8 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -28,23 +28,20 @@ jobs: npm run lint > lint.log 2>&1 continue-on-error: true - - name: 📂 Post logs on failure - if: steps.lint.outcome == 'failure' - run: | - echo "### :x: TypeScript and ESLint checks failed!" >> comment.txt - echo '```' >> comment.txt - cat lint.log >> comment.txt - echo '```' >> comment.txt - - name: 👁️‍🗨️ Comment on Pull Request if: steps.lint.outcome == 'failure' - uses: marocchino/sticky-pull-request-comment@v2 + uses: actions/github-script@v7 with: - header: ⚠️ Erreur(s) détectée(s) par TypeScript/ESLint ! - message: | - :x: Voici les détails : - - file://comment.txt + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const fs = require('fs'); + const lintLogContent = fs.readFileSync('lint.log', 'utf8'); + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `⚠️ Erreur(s) détectée(s) par TypeScript/ESLint !\n\`\`\`\n${lintLogContent}\n\`\`\`` + }); - name: 🧹 Clean up if: always() From e9bb866d4602c5d50240c83133805b7f30225a7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 14 Dec 2024 00:02:25 +0100 Subject: [PATCH 0032/1144] ah oui les autorisations --- .github/workflows/checks.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 345fd35c8..3b1f6a7ab 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -6,6 +6,7 @@ on: jobs: lint: + permissions: write-all name: 🛠️ TypeScript and ESLint runs-on: ubuntu-latest From 26a0178b5a192e73e5cddf2d873b4360cd1f795a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 14 Dec 2024 00:16:59 +0100 Subject: [PATCH 0033/1144] petite update sympa :) --- .github/workflows/start_build.yml | 63 ++++++++++++++++--------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/.github/workflows/start_build.yml b/.github/workflows/start_build.yml index d9a499267..6fc54d8b9 100644 --- a/.github/workflows/start_build.yml +++ b/.github/workflows/start_build.yml @@ -1,4 +1,4 @@ -name: Build and Deploy to Beta Channels +name: New Version (Build + Deploy) on: workflow_dispatch: @@ -7,7 +7,7 @@ jobs: clean: runs-on: ubuntu-latest steps: - - name: Clean environment (just for your comprehension but it do nothing) + - name: 🧹 Clean environment run: | rm -rf node_modules rm -rf ios/build @@ -19,27 +19,28 @@ jobs: needs: clean runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - name: 📥 Checkout code + uses: actions/checkout@v3 - - name: Setup Node.js + - name: ⚙️ Setup Node.js uses: actions/setup-node@v3 with: - node-version: '18' + node-version: 18 - - name: Install dependencies - run: npm ci + - name: 💾 Install dependencies + run: npm install - - name: Install Cocoapods + - name: 💾 Install Cocoapods run: sudo gem install cocoapods - - name: Install Pods and update Hermes Engine + - name: 💾 Install Pods and update Hermes Engine run: | cd ios rm -rf Pods Podfile.lock pod install pod update hermes-engine --no-repo-update - - name: Install Apple Certificate + - name: 💾 Install Apple Certificate env: BUILD_CERTIFICATE_BASE64: ${{ secrets.IOS_DISTRIBUTION_CERTIFICATE_BASE64 }} P12_PASSWORD: ${{ secrets.IOS_DISTRIBUTION_CERTIFICATE_PASSWORD }} @@ -83,7 +84,7 @@ jobs: # Clean up rm $CERTIFICATE_PATH - - name: Install Provisioning Profile + - name: 💾 Install Provisioning Profile env: PROVISIONING_PROFILE_BASE64: ${{ secrets.IOS_PROVISIONING_PROFILE_BASE64 }} run: | @@ -92,7 +93,7 @@ jobs: mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles cp $RUNNER_TEMP/profile.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/ - - name: Build iOS app + - name: 🍎 Build iOS app env: KEYCHAIN_PATH: $RUNNER_TEMP/app-signing.keychain run: | @@ -118,7 +119,7 @@ jobs: exit 1 fi - - name: Upload IPA + - name: 🛜 Upload IPA uses: actions/upload-artifact@v3 with: name: ios-app @@ -129,23 +130,24 @@ jobs: needs: clean runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: 📥 Checkout code + uses: actions/checkout@v3 - - name: Setup Node.js + - name: ⚙️ Setup Node.js uses: actions/setup-node@v3 with: - node-version: '18' + node-version: 18 - - name: Install dependencies - run: npm ci + - name: 💾 Install dependencies + run: npm install - - name: Setup Java + - name: ⚙️ Setup Java uses: actions/setup-java@v3 with: distribution: 'zulu' java-version: '17' - - name: Build Android app + - name: 🚀 Build Android app env: KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} @@ -159,13 +161,13 @@ jobs: ./gradlew assembleRelease ./gradlew bundleRelease - - name: Upload APK + - name: 🛜 Upload APK uses: actions/upload-artifact@v3 with: name: android-apk path: android/app/build/outputs/apk/release/app-release.apk - - name: Upload AAB + - name: 🛜 Upload AAB uses: actions/upload-artifact@v3 with: name: android-aab @@ -175,26 +177,27 @@ jobs: needs: [build_ios, build_android] runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - name: 📥 Checkout code + uses: actions/checkout@v3 - - name: Download iOS artifact + - name: 📡 Download iOS artifact uses: actions/download-artifact@v3 with: name: ios-app path: ios-app - - name: Download Android AAB artifact + - name: 📡 Download Android AAB artifact uses: actions/download-artifact@v3 with: name: android-aab path: android-aab - - name: Prepare artifacts + - name: 🛠️ Prepare artifacts run: | mv ios-app/Papillon.ipa ./Papillon.ipa mv android-aab/app-release.aab ./Papillon.aab - - name: Upload to TestFlight + - name: 🛜 Upload to TestFlight env: APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }} APP_STORE_CONNECT_API_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_ISSUER_ID }} @@ -209,7 +212,7 @@ jobs: echo "iOS build uploaded to TestFlight for beta testing" - - name: Upload Android Release to Play Store Beta + - name: 🛜 Upload Android Release to Play Store Beta uses: r0adkll/upload-google-play@v1.1.3 with: serviceAccountJsonPlainText: ${{ secrets.PLAY_STORE_JSON_KEY }} @@ -219,9 +222,9 @@ jobs: status: completed changesNotSentForReview: false - - name: Notify successful beta deployment + - name: ✅ Notify successful beta deployment run: | echo "Both iOS and Android builds have been successfully uploaded to their respective beta channels (TestFlight and Play Store Beta)." echo "To release to production:" echo "- For iOS: Go to App Store Connect, test the build in TestFlight, then submit for App Store review when ready (Vince will do this)." - echo "- For Android: Go to the Google Play Console, test the build in the beta track, then promote to production when ready (Vince will do this)." \ No newline at end of file + echo "- For Android: Go to the Google Play Console, test the build in the beta track, then promote to production when ready (Vince will do this)." From 1c072e7694186518480ea329820e634f6a4b28c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 15 Dec 2024 00:07:09 +0100 Subject: [PATCH 0034/1144] configuration gradle pour un apk par architecture --- android/app/build.gradle | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/android/app/build.gradle b/android/app/build.gradle index de1642d64..0487f9e2c 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -99,6 +99,14 @@ android { keyPassword 'android' } } + splits { + abi { + enable true + reset() + include "armeabi-v7a", "arm64-v8a", "x86", "x86_64" + universalApk false + } + } buildTypes { debug { signingConfig signingConfigs.debug From 3274f51f2a2ebb0033dc56c04794330ec0ad04f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 15 Dec 2024 00:08:25 +0100 Subject: [PATCH 0035/1144] update workflow pour un apk par architecture --- .github/workflows/start_build.yml | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/.github/workflows/start_build.yml b/.github/workflows/start_build.yml index 6fc54d8b9..8efb56936 100644 --- a/.github/workflows/start_build.yml +++ b/.github/workflows/start_build.yml @@ -147,7 +147,7 @@ jobs: distribution: 'zulu' java-version: '17' - - name: 🚀 Build Android app + - name: 🚀 Build APKs for specific ABIs env: KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} @@ -156,22 +156,16 @@ jobs: # Decode keystore echo $KEYSTORE_BASE64 | base64 --decode > android/app/release.keystore - # Build APK and AAB cd android - ./gradlew assembleRelease - ./gradlew bundleRelease + ./gradlew clean assembleRelease + ls app/build/outputs/apk/release/ - - name: 🛜 Upload APK + - name: 🛜 Upload split APKs uses: actions/upload-artifact@v3 with: - name: android-apk - path: android/app/build/outputs/apk/release/app-release.apk - - - name: 🛜 Upload AAB - uses: actions/upload-artifact@v3 - with: - name: android-aab - path: android/app/build/outputs/bundle/release/app-release.aab + name: split-apks + path: android/app/build/outputs/apk/release/*.apk + if-no-files-found: error deploy_to_beta_channels: needs: [build_ios, build_android] From 39e741b91a87d61702e6d5fe0dd849333d101a31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 15 Dec 2024 00:38:24 +0100 Subject: [PATCH 0036/1144] =?UTF-8?q?cr=C3=A9ation=20automatique=20des=20r?= =?UTF-8?q?eleases=20sur=20github=20avec=20int=C3=A9gration=20des=20fichie?= =?UTF-8?q?rs=20(ipa=20=20+=20aab=20+=20apk=20pour=20chaque=20structure)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/start_build.yml | 107 +++++++++++++++++++++--------- 1 file changed, 75 insertions(+), 32 deletions(-) diff --git a/.github/workflows/start_build.yml b/.github/workflows/start_build.yml index 8efb56936..37ac2b7fa 100644 --- a/.github/workflows/start_build.yml +++ b/.github/workflows/start_build.yml @@ -174,6 +174,35 @@ jobs: - name: 📥 Checkout code uses: actions/checkout@v3 + - name: 🔍 Extract version from package.json + id: extract_version + run: | + VERSION=$(jq -r '.version' package.json) + echo "VERSION=$VERSION" >> $GITHUB_ENV + + - name: 🔍 Get pull requests since last release + id: get_prs + run: | + LAST_TAG=$(git tag --sort=-creatordate | head -n 1) + REPO=${{ github.repository }} + PULL_REQUESTS=$(curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ + "https://api.github.com/repos/$REPO/pulls?state=closed&base=main" | \ + jq -r --arg LAST_TAG $LAST_TAG '.[] | select(.merged_at >= $LAST_TAG) | "- PR #\(.number): \(.title) by @\(.user.login)"') + echo "PULL_REQUESTS<> $GITHUB_ENV + echo "$PULL_REQUESTS" >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + + - name: 🔗 Generate comparison link + id: comparison_link + run: | + # Récupérer le dernier tag + LAST_TAG=$(git describe --tags --abbrev=0 HEAD^) + echo "LAST_TAG=$LAST_TAG" >> $GITHUB_ENV + + # Construire le lien de comparaison + COMPARE_URL="https://github.com/${{ github.repository }}/compare/$LAST_TAG...v${{ env.VERSION }}" + echo "COMPARE_URL=$COMPARE_URL" >> $GITHUB_ENV + - name: 📡 Download iOS artifact uses: actions/download-artifact@v3 with: @@ -186,39 +215,53 @@ jobs: name: android-aab path: android-aab - - name: 🛠️ Prepare artifacts - run: | - mv ios-app/Papillon.ipa ./Papillon.ipa - mv android-aab/app-release.aab ./Papillon.aab + - name: 📡 Download Android APKs + uses: actions/download-artifact@v3 + with: + name: split-apks + path: split-apks - - name: 🛜 Upload to TestFlight + - name: 🛜 Create GitHub Release + uses: comnoco/create-release@v2 env: - APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }} - APP_STORE_CONNECT_API_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_ISSUER_ID }} - APP_STORE_CONNECT_API_KEY_CONTENT: ${{ secrets.APP_STORE_CONNECT_API_KEY_CONTENT }} - run: | - echo "$APP_STORE_CONNECT_API_KEY_CONTENT" | base64 --decode > appstore_connect_api_key.p8 - - xcrun notarytool submit Papillon.ipa --key appstore_connect_api_key.p8 \ - --key-id "$APP_STORE_CONNECT_API_KEY_ID" \ - --issuer "$APP_STORE_CONNECT_API_ISSUER_ID" \ - --wait - - echo "iOS build uploaded to TestFlight for beta testing" + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: v${{ env.VERSION }} + release_name: "Version ${{ env.VERSION }}" + body: | + ### Les nouveautés depuis la dernière version : + ${{ env.PULL_REQUESTS }} - - name: 🛜 Upload Android Release to Play Store Beta - uses: r0adkll/upload-google-play@v1.1.3 + [Pour plus de détails...](${{ env.COMPARE_URL }}) + draft: false + prerelease: true + + - name: 📥 Upload IPA to release + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - serviceAccountJsonPlainText: ${{ secrets.PLAY_STORE_JSON_KEY }} - packageName: xyz.getpapillon.app - releaseFiles: Papillon.aab - track: beta - status: completed - changesNotSentForReview: false - - - name: ✅ Notify successful beta deployment - run: | - echo "Both iOS and Android builds have been successfully uploaded to their respective beta channels (TestFlight and Play Store Beta)." - echo "To release to production:" - echo "- For iOS: Go to App Store Connect, test the build in TestFlight, then submit for App Store review when ready (Vince will do this)." - echo "- For Android: Go to the Google Play Console, test the build in the beta track, then promote to production when ready (Vince will do this)." + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ios-app/Papillon.ipa + asset_name: Papillon.ipa + asset_content_type: application/octet-stream + + - name: 📥 Upload AAB to release + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: android-aab/app-release.aab + asset_name: Papillon.aab + asset_content_type: application/octet-stream + + - name: 📥 Upload split APKs to release + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: android/app/build/outputs/apk/release/*.apk + asset_name: Papillon-{arch}.apk + asset_content_type: application/octet-stream From 83ab9a4d4dcb9643431df6e78d57b341bf2e8f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 15 Dec 2024 00:39:42 +0100 Subject: [PATCH 0037/1144] rename --- .github/workflows/start_build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/start_build.yml b/.github/workflows/start_build.yml index 37ac2b7fa..0d799aa59 100644 --- a/.github/workflows/start_build.yml +++ b/.github/workflows/start_build.yml @@ -1,4 +1,4 @@ -name: New Version (Build + Deploy) +name: New Release (Build + Deploy) on: workflow_dispatch: @@ -167,7 +167,7 @@ jobs: path: android/app/build/outputs/apk/release/*.apk if-no-files-found: error - deploy_to_beta_channels: + deploy_new_release: needs: [build_ios, build_android] runs-on: macos-latest steps: From dad8aa7b106f037402fd818374b229bf16de1ae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 15 Dec 2024 12:06:00 +0100 Subject: [PATCH 0038/1144] adaptation `PronoteQRCode.tsx` pour les tablettes --- src/views/login/pronote/PronoteQRCode.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/views/login/pronote/PronoteQRCode.tsx b/src/views/login/pronote/PronoteQRCode.tsx index f10c2725e..73eb4c201 100644 --- a/src/views/login/pronote/PronoteQRCode.tsx +++ b/src/views/login/pronote/PronoteQRCode.tsx @@ -313,7 +313,10 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { Date: Sun, 15 Dec 2024 12:13:28 +0100 Subject: [PATCH 0039/1144] delete `maxWidth` --- src/views/account/Grades/Document.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/views/account/Grades/Document.tsx b/src/views/account/Grades/Document.tsx index 3e69dfb88..a25eb4439 100644 --- a/src/views/account/Grades/Document.tsx +++ b/src/views/account/Grades/Document.tsx @@ -354,7 +354,6 @@ const GradeDocument: Screen<"GradeDocument"> = ({ route, navigation }) => { style={{ flex: 1, width: "100%", - maxWidth: 500, backgroundColor: theme.colors.background, borderCurve: "continuous", overflow: "hidden", From 4cb26b244ec4d29978c02a9bf7145533595b7efa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 15 Dec 2024 12:21:45 +0100 Subject: [PATCH 0040/1144] adapt `ServiceSelector` for tablet --- .../ExternalAccount/ServiceSelector.tsx | 127 +++++++++--------- 1 file changed, 60 insertions(+), 67 deletions(-) diff --git a/src/views/settings/ExternalAccount/ServiceSelector.tsx b/src/views/settings/ExternalAccount/ServiceSelector.tsx index 11bd2592b..069e0d991 100644 --- a/src/views/settings/ExternalAccount/ServiceSelector.tsx +++ b/src/views/settings/ExternalAccount/ServiceSelector.tsx @@ -1,98 +1,91 @@ import React, { useState } from "react"; import type { Screen } from "@/router/helpers/types"; -import { useTheme } from "@react-navigation/native"; -import { CircleDashed, Star } from "lucide-react-native"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; -import { Image, View, StyleSheet, StatusBar, ScrollView } from "react-native"; +import { Image, View, StyleSheet } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; import Reanimated, { LinearTransition, FlipInXDown } from "react-native-reanimated"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; -import { - NativeItem, - NativeList, - NativeText, -} from "@/components/Global/NativeComponents"; import { AccountService } from "@/stores/account/types"; -import { useCurrentAccount } from "@/stores/account"; import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; -const ExternalAccountSelector: Screen<"ExternalAccountSelector"> = ({ navigation, route }) => { - const theme = useTheme(); - const { colors } = theme; - const insets = useSafeAreaInsets(); - const account = useCurrentAccount(store => store.account!); - +const ExternalAccountSelector: Screen<"ExternalAccountSelector"> = ({ navigation }) => { type Service = AccountService | "Other"; const [service, setService] = useState(null); return ( - + - - } - text="Turboself" - enabled={service === AccountService.Turboself} - onPress={() => setService(AccountService.Turboself)} - /> - + + } + text="Turboself" + enabled={service === AccountService.Turboself} + onPress={() => setService(AccountService.Turboself)} + /> + - - } - text="ARD" - enabled={service === AccountService.ARD} - onPress={() => setService(AccountService.ARD)} - /> - + + } + text="ARD" + enabled={service === AccountService.ARD} + onPress={() => setService(AccountService.ARD)} + /> + - - } - text="Izly" - enabled={service === AccountService.Izly} - onPress={() => setService(AccountService.Izly)} - /> + + } + text="Izly" + enabled={service === AccountService.Izly} + onPress={() => setService(AccountService.Izly)} + /> + - - - { - if (service) { - navigation.navigate("ExternalAccountSelectMethod", { service }); - } - }} - /> + + { + if (service) { + navigation.navigate("ExternalAccountSelectMethod", { service }); + } + }} + /> + ); From b0c59fed78450164226efb78ba766d2efc29208c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 15 Dec 2024 12:23:17 +0100 Subject: [PATCH 0041/1144] adapt `SelectMethod` for tablet --- src/views/settings/ExternalAccount/SelectMethod.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/settings/ExternalAccount/SelectMethod.tsx b/src/views/settings/ExternalAccount/SelectMethod.tsx index d1ce2e392..3eab1035c 100644 --- a/src/views/settings/ExternalAccount/SelectMethod.tsx +++ b/src/views/settings/ExternalAccount/SelectMethod.tsx @@ -29,7 +29,7 @@ const ExternalAccountSelectMethod: Screen<"ExternalAccountSelectMethod"> = ({ na message={`Sélectionnez votre méthode de connexion au service ${route.params.service === "Other" ? "(autre)" : AccountService[route.params.service]}`} width={300} numberOfLines={2} - offsetTop={insets.top} + offsetTop={"15%"} /> From c59bfcf63c930cb1ddd076af61b82705fac61195 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Mon, 16 Dec 2024 19:40:12 +0100 Subject: [PATCH 0042/1144] change `npm install` => `npm ci` --- .github/workflows/start_build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/start_build.yml b/.github/workflows/start_build.yml index 0d799aa59..f4acbb67f 100644 --- a/.github/workflows/start_build.yml +++ b/.github/workflows/start_build.yml @@ -28,7 +28,7 @@ jobs: node-version: 18 - name: 💾 Install dependencies - run: npm install + run: npm ci - name: 💾 Install Cocoapods run: sudo gem install cocoapods @@ -139,7 +139,7 @@ jobs: node-version: 18 - name: 💾 Install dependencies - run: npm install + run: npm ci - name: ⚙️ Setup Java uses: actions/setup-java@v3 From f0e56ac1829c01be28839ab53d12c7a30956c5d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Mon, 16 Dec 2024 20:00:05 +0100 Subject: [PATCH 0043/1144] =?UTF-8?q?int=C3=A9gration=20`npm=20run=20prebu?= =?UTF-8?q?ild`=20+=20optimisation=20du=20code=20pour=20plus=20de=20rapidi?= =?UTF-8?q?t=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/start_build.yml | 39 ++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/.github/workflows/start_build.yml b/.github/workflows/start_build.yml index f4acbb67f..9804d030b 100644 --- a/.github/workflows/start_build.yml +++ b/.github/workflows/start_build.yml @@ -14,6 +14,23 @@ jobs: rm -rf ~/Library/Developer/Xcode/DerivedData rm -rf android/app/build/ rm -rf $HOME/.gradle/caches/ + + - name: ⚙️ Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 18 + + - name: 💾 Install dependencies + run: npm ci + + - name: 📤 Upload node_modules + uses: actions/upload-artifact@v3 + with: + name: node_modules + path: node_modules + + - name: 🔄 Run Expo Prebuild + run: npm run prebuild build_ios: needs: clean @@ -21,14 +38,11 @@ jobs: steps: - name: 📥 Checkout code uses: actions/checkout@v3 - - - name: ⚙️ Setup Node.js - uses: actions/setup-node@v3 - with: - node-version: 18 - - name: 💾 Install dependencies - run: npm ci + - name: 📥 Download node_modules + uses: actions/download-artifact@v3 + with: + name: node_modules - name: 💾 Install Cocoapods run: sudo gem install cocoapods @@ -132,14 +146,11 @@ jobs: steps: - name: 📥 Checkout code uses: actions/checkout@v3 - - - name: ⚙️ Setup Node.js - uses: actions/setup-node@v3 - with: - node-version: 18 - - name: 💾 Install dependencies - run: npm ci + - name: 📥 Download node_modules + uses: actions/download-artifact@v3 + with: + name: node_modules - name: ⚙️ Setup Java uses: actions/setup-java@v3 From 918603112730d08b76f21eae7d3287c2fbba3a81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Tue, 17 Dec 2024 18:47:07 +0100 Subject: [PATCH 0044/1144] adaptation `Bienvenue sur Papillon !` pour tablettes --- src/views/welcome/FirstInstallation.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/welcome/FirstInstallation.tsx b/src/views/welcome/FirstInstallation.tsx index 1ee3a28e3..9c00098dc 100644 --- a/src/views/welcome/FirstInstallation.tsx +++ b/src/views/welcome/FirstInstallation.tsx @@ -61,6 +61,7 @@ const FirstInstallation: Screen<"FirstInstallation"> = ({ navigation }) => { message="Bienvenue sur Papillon !" numberOfLines={1} width={220} + offsetTop={"15%"} /> Date: Tue, 17 Dec 2024 19:27:27 +0100 Subject: [PATCH 0045/1144] =?UTF-8?q?navbar=20interactive=20d=C3=A9sormais?= =?UTF-8?q?=20!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/screens/account/stack.tsx | 33 +++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/router/screens/account/stack.tsx b/src/router/screens/account/stack.tsx index 9b032c2d1..6559fb840 100644 --- a/src/router/screens/account/stack.tsx +++ b/src/router/screens/account/stack.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useLayoutEffect } from "react"; +import React, { useEffect, useLayoutEffect, useState } from "react"; import { Dimensions, View } from "react-native"; import screens from "."; import { NativeStackNavigationOptions } from "@react-navigation/native-stack"; @@ -44,11 +44,14 @@ function TabBarContainer () { const AccountStackScreen: Screen<"AccountStack"> = () => { const account = useCurrentAccount(store => store.account); - const navigatorRef = React.useRef(); - const url = Linking.useURL(); const params = url && Linking.parse(url); + const [screenDimensions, setScreenDimensions] = useState<{ + width: number; + height: number; + }>(Dimensions.get("screen")); + useEffect(() => { if (params) { if (params.queryParams?.method == "importIcal") { @@ -65,9 +68,27 @@ const AccountStackScreen: Screen<"AccountStack"> = () => { } }, [params]); - const dims = Dimensions.get("screen"); - const tabletWidth = dims.width; - const tabletHeight = dims.height; + useEffect(() => { + const handleDimensionsChange = ({ + screen + }: { + screen: { width: number; height: number } + }) => { + setScreenDimensions(screen); + }; + + const subscription = Dimensions.addEventListener( + "change", + handleDimensionsChange + ); + + return () => { + subscription?.remove(); + }; + }, []); + + const tabletWidth = screenDimensions.width; + const tabletHeight = screenDimensions.height; const tabletDiagl = (tabletWidth / tabletHeight) * 10; const tablet = tabletDiagl >= 6.9; From 191f024b229504e6af2b6ddcc537c1192e9e9273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 22 Dec 2024 11:28:26 +0100 Subject: [PATCH 0046/1144] adaptation de la taille des boutons pour les tablettes avec synchro lors de la rotation --- .../FirstInstallation/ButtonCta.tsx | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/components/FirstInstallation/ButtonCta.tsx b/src/components/FirstInstallation/ButtonCta.tsx index 9d7b8074d..fe895e488 100644 --- a/src/components/FirstInstallation/ButtonCta.tsx +++ b/src/components/FirstInstallation/ButtonCta.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { Text, Pressable, StyleSheet, type StyleProp, type ViewStyle } from "react-native"; +import { Text, Pressable, StyleSheet, type StyleProp, type ViewStyle, Dimensions } from "react-native"; import Reanimated, { Easing, useSharedValue, withTiming } from "react-native-reanimated"; import { useTheme } from "@react-navigation/native"; import * as Haptics from "expo-haptics"; @@ -23,6 +23,35 @@ const ButtonCta: React.FC<{ }) => { const { colors } = useTheme(); + const [screenDimensions, setScreenDimensions] = useState<{ + width: number; + height: number; + }>(Dimensions.get("screen")); + + useEffect(() => { + const handleDimensionsChange = ({ + screen + }: { + screen: { width: number; height: number } + }) => { + setScreenDimensions(screen); + }; + + const subscription = Dimensions.addEventListener( + "change", + handleDimensionsChange + ); + + return () => { + subscription?.remove(); + }; + }, []); + + const tabletWidth = screenDimensions.width; + const tabletHeight = screenDimensions.height; + const tabletDiagl = (tabletWidth / tabletHeight) * 10; + const tablet = tabletDiagl >= 6.9; + const [pressed, setPressed] = useState(false); const scale = useSharedValue(1); const opacity = useSharedValue(1); @@ -58,6 +87,7 @@ const ButtonCta: React.FC<{ > Date: Sun, 22 Dec 2024 11:40:08 +0100 Subject: [PATCH 0047/1144] adaptation de `DuoListPressable` pour les tablettes avec synchro lors de la rotation --- .../FirstInstallation/DuoListPressable.tsx | 51 +++++++++++++++---- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/src/components/FirstInstallation/DuoListPressable.tsx b/src/components/FirstInstallation/DuoListPressable.tsx index e4408a5d8..65a05a3e4 100644 --- a/src/components/FirstInstallation/DuoListPressable.tsx +++ b/src/components/FirstInstallation/DuoListPressable.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useState } from "react"; -import { View, Text, Pressable, StyleSheet } from "react-native"; +import { View, Text, Pressable, StyleSheet, Dimensions } from "react-native"; import { useTheme } from "@react-navigation/native"; import * as Haptics from "expo-haptics"; @@ -26,6 +26,34 @@ const DuoListPressable: React.FC<{ }) => { const theme = useTheme(); const { colors } = theme; + const [screenDimensions, setScreenDimensions] = useState<{ + width: number; + height: number; + }>(Dimensions.get("screen")); + + useEffect(() => { + const handleDimensionsChange = ({ + screen + }: { + screen: { width: number; height: number } + }) => { + setScreenDimensions(screen); + }; + + const subscription = Dimensions.addEventListener( + "change", + handleDimensionsChange + ); + + return () => { + subscription?.remove(); + }; + }, []); + + const tabletWidth = screenDimensions.width; + const tabletHeight = screenDimensions.height; + const tabletDiagl = (tabletWidth / tabletHeight) * 10; + const tablet = tabletDiagl >= 6.9; const [pressed, setPressed] = useState(false); const scale = useSharedValue(1); @@ -59,11 +87,11 @@ const DuoListPressable: React.FC<{ style={{ transform: [{ scale: scale }], opacity: opacity, - width: "100%", }} > {text} @@ -133,7 +163,6 @@ const DuoListPressable: React.FC<{ const styles = StyleSheet.create({ pressable: { - width: "100%", borderWidth: 1.5, borderBottomWidth: 3, paddingHorizontal: 18, @@ -142,7 +171,7 @@ const styles = StyleSheet.create({ borderCurve: "continuous", flexDirection: "row", gap: 18, - alignItems: "center", + alignSelf: "center", }, text: { From 83955635bed274f816fa617630621d3f8ddda34f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 22 Dec 2024 11:52:07 +0100 Subject: [PATCH 0048/1144] =?UTF-8?q?ajout=20de=20la=20gestion=20de=20l'?= =?UTF-8?q?=C3=A9tat=20de=20la=20tablette=20dans=20le=20`BottomTabNavigato?= =?UTF-8?q?r`=20et=20passage=20de=20la=20prop=20`tablet`=20dans=20`Account?= =?UTF-8?q?Stack.Navigator`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cela permet d'éviter les erreur Rendered --- src/router/helpers/PapillonTabNavigator.tsx | 12 ++++-------- src/router/screens/account/stack.tsx | 6 +++++- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/router/helpers/PapillonTabNavigator.tsx b/src/router/helpers/PapillonTabNavigator.tsx index a4972ec07..79eeaa150 100644 --- a/src/router/helpers/PapillonTabNavigator.tsx +++ b/src/router/helpers/PapillonTabNavigator.tsx @@ -417,13 +417,9 @@ const BottomTabNavigator: React.ComponentType = ({ backBehavior, children, screenOptions, + tablet, ...rest }) => { - const dims = Dimensions.get("screen"); - const tabletWidth = dims.width; - const tabletHeight = dims.height; - const tabletDiagl = (tabletWidth / tabletHeight) * 10; - const tablet = tabletDiagl >= 6.9; const mainNavigator = useRef(null); // Track previous index to determine direction @@ -452,8 +448,8 @@ const BottomTabNavigator: React.ComponentType = ({ // Handle tab change animations useEffect(() => { - if(Platform.OS !== "ios") return; - if(tablet) return; + if (Platform.OS !== "ios") return; + if (tablet) return; if (state.index === previousIndex) return; // Determine animation direction @@ -492,7 +488,7 @@ const BottomTabNavigator: React.ComponentType = ({ clearTimeout(timeoutRef.current); } }; - }, [state.index, dims.width]); + }, [state.index, tablet]); // Create animated styles const animatedStyles = useAnimatedStyle(() => { diff --git a/src/router/screens/account/stack.tsx b/src/router/screens/account/stack.tsx index 6559fb840..e8b61f573 100644 --- a/src/router/screens/account/stack.tsx +++ b/src/router/screens/account/stack.tsx @@ -129,7 +129,11 @@ const AccountStackScreen: Screen<"AccountStack"> = () => { }, []); return ( - + {finalScreens.map((screen) => ( Date: Sun, 22 Dec 2024 12:23:40 +0100 Subject: [PATCH 0049/1144] =?UTF-8?q?stabilisation=20de=20la=20page=20Home?= =?UTF-8?q?works=20lors=20de=20la=20rotation=20de=20l'=C3=A9cran?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/Homeworks.tsx | 46 +++++++++++++++++++---- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 50ae974c4..04e265fbd 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -66,9 +66,32 @@ const formatDate = (date: string | number | Date): string => { const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const flatListRef: React.MutableRefObject = useRef(null) as any as React.MutableRefObject; - const dims = Dimensions.get("screen"); - const tabletWidth = dims.width; - const tabletHeight = dims.height; + const [screenDimensions, setScreenDimensions] = useState<{ + width: number; + height: number; + }>(Dimensions.get("screen")); + + useEffect(() => { + const handleDimensionsChange = ({ + screen + }: { + screen: { width: number; height: number } + }) => { + setScreenDimensions(screen); + }; + + const subscription = Dimensions.addEventListener( + "change", + handleDimensionsChange + ); + + return () => { + subscription?.remove(); + }; + }, []); + + const tabletWidth = screenDimensions.width; + const tabletHeight = screenDimensions.height; const tabletDiagl = (tabletWidth / tabletHeight) * 10; const tablet = tabletDiagl >= 6.9; const finalWidth = tabletWidth - (tablet ? ( @@ -76,6 +99,15 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { 320 ) : 0); + useEffect(() => { + if (flatListRef.current) { + flatListRef.current.scrollToIndex({ + index: data.indexOf(selectedWeek), + animated: false, + }); + } + }, [screenDimensions]); + const insets = useSafeAreaInsets(); const outsideNav = route.params?.outsideNav; @@ -335,7 +367,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const firstWeek = data[0]; const newWeeks = Array.from({ length: 50 }, (_, i) => firstWeek - 50 + i); setData(prevData => [...newWeeks, ...prevData]); - flatListRef.current?.scrollToIndex({ index: 50, animated: false }); + // flatListRef.current?.scrollToIndex({ index: 50, animated: false }); }; const onScroll: ScrollViewProps["onScroll"] = useCallback(({ nativeEvent }: NativeSyntheticEvent) => { @@ -361,7 +393,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const distance = Math.abs(index - currentIndex); const animated = distance <= 10; // Animate if the distance is 10 weeks or less - flatListRef.current?.scrollToIndex({ index, animated }); + flatListRef.current.scrollToIndex({ index, animated }); setSelectedWeek(weekNumber); } else { // If the week is not in the current data, update the data and scroll @@ -370,9 +402,9 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { // Use a timeout to ensure the FlatList has updated before scrolling setTimeout(() => { - flatListRef.current?.scrollToIndex({ index: 50, animated: false }); + flatListRef.current.scrollToIndex({ index: 50, animated: false }); setSelectedWeek(weekNumber); - }, 0); + }, 100); } }, [data, finalWidth]); From 37a0a813fed587e4b5f9af0076eda12bf75e1d5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 22 Dec 2024 14:10:40 +0100 Subject: [PATCH 0050/1144] =?UTF-8?q?ajout=20d'un=20hook=20`useScreenDimen?= =?UTF-8?q?sions`=20pour=20g=C3=A9rer=20les=20dimensions=20de=20l'=C3=A9cr?= =?UTF-8?q?an=20et=20simplification=20du=20code=20dans=20plusieurs=20compo?= =?UTF-8?q?sants=20+=20suppression=20des=20import=20non=20utilis=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FirstInstallation/ButtonCta.tsx | 34 ++-------- .../FirstInstallation/DuoListPressable.tsx | 33 ++-------- src/components/Home/Header.tsx | 21 ++---- src/hooks/useScreenDimensions.ts | 34 ++++++++++ src/router/helpers/PapillonTabNavigator.tsx | 12 ++-- src/router/screens/account/stack.tsx | 44 +++---------- src/views/account/Homeworks/Homeworks.tsx | 66 ++++--------------- .../account/Homeworks/HomeworksHeader.tsx | 13 ++-- src/views/account/Lessons/Lessons.tsx | 13 ++-- src/views/account/Lessons/LessonsHeader.tsx | 6 -- 10 files changed, 81 insertions(+), 195 deletions(-) create mode 100644 src/hooks/useScreenDimensions.ts diff --git a/src/components/FirstInstallation/ButtonCta.tsx b/src/components/FirstInstallation/ButtonCta.tsx index fe895e488..88ee8543d 100644 --- a/src/components/FirstInstallation/ButtonCta.tsx +++ b/src/components/FirstInstallation/ButtonCta.tsx @@ -1,8 +1,9 @@ import React, { useEffect, useState } from "react"; -import { Text, Pressable, StyleSheet, type StyleProp, type ViewStyle, Dimensions } from "react-native"; +import { Text, Pressable, StyleSheet, type StyleProp, type ViewStyle } from "react-native"; import Reanimated, { Easing, useSharedValue, withTiming } from "react-native-reanimated"; import { useTheme } from "@react-navigation/native"; import * as Haptics from "expo-haptics"; +import useScreenDimensions from "@/hooks/useScreenDimensions"; const ButtonCta: React.FC<{ value: string @@ -23,34 +24,7 @@ const ButtonCta: React.FC<{ }) => { const { colors } = useTheme(); - const [screenDimensions, setScreenDimensions] = useState<{ - width: number; - height: number; - }>(Dimensions.get("screen")); - - useEffect(() => { - const handleDimensionsChange = ({ - screen - }: { - screen: { width: number; height: number } - }) => { - setScreenDimensions(screen); - }; - - const subscription = Dimensions.addEventListener( - "change", - handleDimensionsChange - ); - - return () => { - subscription?.remove(); - }; - }, []); - - const tabletWidth = screenDimensions.width; - const tabletHeight = screenDimensions.height; - const tabletDiagl = (tabletWidth / tabletHeight) * 10; - const tablet = tabletDiagl >= 6.9; + const { isTablet } = useScreenDimensions(); const [pressed, setPressed] = useState(false); const scale = useSharedValue(1); @@ -87,7 +61,7 @@ const ButtonCta: React.FC<{ > { const theme = useTheme(); const { colors } = theme; - const [screenDimensions, setScreenDimensions] = useState<{ - width: number; - height: number; - }>(Dimensions.get("screen")); - useEffect(() => { - const handleDimensionsChange = ({ - screen - }: { - screen: { width: number; height: number } - }) => { - setScreenDimensions(screen); - }; - - const subscription = Dimensions.addEventListener( - "change", - handleDimensionsChange - ); - - return () => { - subscription?.remove(); - }; - }, []); - - const tabletWidth = screenDimensions.width; - const tabletHeight = screenDimensions.height; - const tabletDiagl = (tabletWidth / tabletHeight) * 10; - const tablet = tabletDiagl >= 6.9; + const { isTablet } = useScreenDimensions(); const [pressed, setPressed] = useState(false); const scale = useSharedValue(1); @@ -91,7 +66,7 @@ const DuoListPressable: React.FC<{ > ([]); const [addonsTitle, setAddonsTitle] = useState([]); const [click, setClick] = useState(false); - const [tablet, setTablet] = useState(false); - - useEffect(() => { - const updateTabletOrientation = () => { - const dims = Dimensions.get("screen"); - const tabletWidth = dims.width; - const tabletHeight = dims.height; - const tabletDiagl = (tabletWidth / tabletHeight) * 10; - setTablet(tabletDiagl >= 6.9); - }; - updateTabletOrientation(); // 1ère fois qu'on arrive sur la page - - const subscription = Dimensions.addEventListener("change", updateTabletOrientation); // Dès qu'on change de rotation - return () => subscription.remove(); // Permet d'éviter les fuites de mémoire - }, []); + const { isTablet } = useScreenDimensions(); useEffect(() => { // On récupère le fichier principal de chaque extension. @@ -95,7 +82,7 @@ const Header: React.FC<{ style={[styles.part, styles.header]} /> - {!tablet && ( + {!isTablet && ( tabs.filter(tab => !tab.enabled).length === 0 ? (() => Dimensions.get("screen")); + + useEffect(() => { + const handleDimensionsChange = ({ + screen, + }: { + screen: { width: number; height: number }; + }) => { + setScreenDimensions(screen); + }; + + const subscription = Dimensions.addEventListener( + "change", + handleDimensionsChange + ); + + return () => { + subscription?.remove(); + }; + }, []); + + return { + width: screenDimensions.width, + height: screenDimensions.height, + isTablet: (screenDimensions.width / screenDimensions.height) * 10 >= 6.9, + }; +} diff --git a/src/router/helpers/PapillonTabNavigator.tsx b/src/router/helpers/PapillonTabNavigator.tsx index 79eeaa150..ae5a146c8 100644 --- a/src/router/helpers/PapillonTabNavigator.tsx +++ b/src/router/helpers/PapillonTabNavigator.tsx @@ -7,7 +7,7 @@ import { import { Platform, Text } from "react-native"; import React, { useEffect, useMemo, useRef, useState } from "react"; -import { View, Dimensions } from "react-native"; +import { View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; @@ -417,7 +417,7 @@ const BottomTabNavigator: React.ComponentType = ({ backBehavior, children, screenOptions, - tablet, + isTablet, ...rest }) => { const mainNavigator = useRef(null); @@ -449,7 +449,7 @@ const BottomTabNavigator: React.ComponentType = ({ // Handle tab change animations useEffect(() => { if (Platform.OS !== "ios") return; - if (tablet) return; + if (isTablet) return; if (state.index === previousIndex) return; // Determine animation direction @@ -488,7 +488,7 @@ const BottomTabNavigator: React.ComponentType = ({ clearTimeout(timeoutRef.current); } }; - }, [state.index, tablet]); + }, [state.index, isTablet]); // Create animated styles const animatedStyles = useAnimatedStyle(() => { @@ -504,7 +504,7 @@ const BottomTabNavigator: React.ComponentType = ({ flex: 1, overflow: "hidden", // Prevent content from showing outside bounds during animation }, - tablet && { + isTablet && { flexDirection: "row-reverse", }, ]} @@ -532,7 +532,7 @@ const BottomTabNavigator: React.ComponentType = ({ /> - {!tablet ? ( + {!isTablet ? ( = () => { const url = Linking.useURL(); const params = url && Linking.parse(url); - const [screenDimensions, setScreenDimensions] = useState<{ - width: number; - height: number; - }>(Dimensions.get("screen")); - useEffect(() => { if (params) { if (params.queryParams?.method == "importIcal") { @@ -68,35 +64,13 @@ const AccountStackScreen: Screen<"AccountStack"> = () => { } }, [params]); - useEffect(() => { - const handleDimensionsChange = ({ - screen - }: { - screen: { width: number; height: number } - }) => { - setScreenDimensions(screen); - }; - - const subscription = Dimensions.addEventListener( - "change", - handleDimensionsChange - ); - - return () => { - subscription?.remove(); - }; - }, []); - - const tabletWidth = screenDimensions.width; - const tabletHeight = screenDimensions.height; - const tabletDiagl = (tabletWidth / tabletHeight) * 10; - const tablet = tabletDiagl >= 6.9; + const { isTablet } = useScreenDimensions(); let newAccountScreens = screens; if (account?.personalization.tabs) { let newTabs = account.personalization.tabs; - if (!tablet) { + if (!isTablet) { newTabs = newTabs.filter(tab => tab.enabled); } @@ -113,14 +87,14 @@ const AccountStackScreen: Screen<"AccountStack"> = () => { let mln = 5 - newAccountScreens.length; if (mln < 0) { mln = 0; } - if (tablet) { + if (isTablet) { mln = 0; } const mScreenLoop = new Array(mln).fill(0); let finalScreens = newAccountScreens; - if (!tablet) { + if (!isTablet) { finalScreens = newAccountScreens.splice(0, 5); } @@ -132,7 +106,7 @@ const AccountStackScreen: Screen<"AccountStack"> = () => { {finalScreens.map((screen) => ( ; - account: Account; - updateHomeworks: () => Promise; - loading: boolean; - getDayName: (date: string | number | Date) => string; -}; +import useScreenDimensions from "@/hooks/useScreenDimensions"; const formatDate = (date: string | number | Date): string => { return new Date(date).toLocaleDateString("fr-FR", { @@ -66,38 +49,11 @@ const formatDate = (date: string | number | Date): string => { const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const flatListRef: React.MutableRefObject = useRef(null) as any as React.MutableRefObject; - const [screenDimensions, setScreenDimensions] = useState<{ - width: number; - height: number; - }>(Dimensions.get("screen")); - - useEffect(() => { - const handleDimensionsChange = ({ - screen - }: { - screen: { width: number; height: number } - }) => { - setScreenDimensions(screen); - }; - - const subscription = Dimensions.addEventListener( - "change", - handleDimensionsChange - ); - - return () => { - subscription?.remove(); - }; - }, []); + const { width, height, isTablet } = useScreenDimensions(); - const tabletWidth = screenDimensions.width; - const tabletHeight = screenDimensions.height; - const tabletDiagl = (tabletWidth / tabletHeight) * 10; - const tablet = tabletDiagl >= 6.9; - const finalWidth = tabletWidth - (tablet ? ( - 320 > tabletWidth * 0.35 ? tabletWidth * 0.35 : - 320 - ) : 0); + const finalWidth = isTablet + ? width - Math.min(320, width * 0.35) + : width; useEffect(() => { if (flatListRef.current) { @@ -106,7 +62,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { animated: false, }); } - }, [screenDimensions]); + }, [width, height]); const insets = useSafeAreaInsets(); @@ -513,7 +469,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { ]} layout={animPapillon(LinearTransition)} > - {tabletWidth > 370 ? "Semaine" : "sem."} + {width > 370 ? "Semaine" : "sem."} = ({ route, navigation }) => { /> } - {showPickerButtons && !searchHasFocus && tabletWidth > 330 && + {showPickerButtons && !searchHasFocus && width > 330 && void, changeIndex: (index: number) => void }> = ({ epochWeekNumber, oldPageIndex, showPicker, changeIndex }) => { - const { colors } = useTheme(); - - const dims = Dimensions.get("screen"); - const tabletWidth = dims.width; - const tabletHeight = dims.height; - const tabletDiagl = (tabletWidth / tabletHeight) * 10; - const tablet = tabletDiagl >= 6.9; + const { width, isTablet } = useScreenDimensions(); const index = epochWeekNumber; return ( = ({ route, navigation }) => { const account = useCurrentAccount((store) => store.account!); @@ -43,13 +44,9 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const loadedWeeks = useRef>(new Set()); const currentlyLoadingWeeks = useRef>(new Set()); - const dims = Dimensions.get("screen"); - const tabletWidth = dims.width; - const tabletHeight = dims.height; - const tabletDiagl = (tabletWidth / tabletHeight) * 10; - const tablet = tabletDiagl >= 6.9; - const finalWidth = tabletWidth - (tablet ? ( - 320 > tabletWidth * 0.35 ? tabletWidth * 0.35 : + const { width, isTablet } = useScreenDimensions(); + const finalWidth = width - (isTablet ? ( + 320 > width * 0.35 ? width * 0.35 : 320 ) : 0); diff --git a/src/views/account/Lessons/LessonsHeader.tsx b/src/views/account/Lessons/LessonsHeader.tsx index 37d2a644d..429b3dfdf 100644 --- a/src/views/account/Lessons/LessonsHeader.tsx +++ b/src/views/account/Lessons/LessonsHeader.tsx @@ -22,12 +22,6 @@ interface HeaderCalendarProps { } const HeaderCalendar: React.FC = () => { - const dims = Dimensions.get("screen"); - const tabletWidth = dims.width; - const tabletHeight = dims.height; - const tabletDiagl = (tabletWidth / tabletHeight) * 10; - const tablet = tabletDiagl >= 6.9; - return ( Date: Sun, 22 Dec 2024 14:21:04 +0100 Subject: [PATCH 0051/1144] fix Rendered --- src/router/helpers/PapillonTabNavigator.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/router/helpers/PapillonTabNavigator.tsx b/src/router/helpers/PapillonTabNavigator.tsx index ae5a146c8..79eeaa150 100644 --- a/src/router/helpers/PapillonTabNavigator.tsx +++ b/src/router/helpers/PapillonTabNavigator.tsx @@ -7,7 +7,7 @@ import { import { Platform, Text } from "react-native"; import React, { useEffect, useMemo, useRef, useState } from "react"; -import { View } from "react-native"; +import { View, Dimensions } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; @@ -417,7 +417,7 @@ const BottomTabNavigator: React.ComponentType = ({ backBehavior, children, screenOptions, - isTablet, + tablet, ...rest }) => { const mainNavigator = useRef(null); @@ -449,7 +449,7 @@ const BottomTabNavigator: React.ComponentType = ({ // Handle tab change animations useEffect(() => { if (Platform.OS !== "ios") return; - if (isTablet) return; + if (tablet) return; if (state.index === previousIndex) return; // Determine animation direction @@ -488,7 +488,7 @@ const BottomTabNavigator: React.ComponentType = ({ clearTimeout(timeoutRef.current); } }; - }, [state.index, isTablet]); + }, [state.index, tablet]); // Create animated styles const animatedStyles = useAnimatedStyle(() => { @@ -504,7 +504,7 @@ const BottomTabNavigator: React.ComponentType = ({ flex: 1, overflow: "hidden", // Prevent content from showing outside bounds during animation }, - isTablet && { + tablet && { flexDirection: "row-reverse", }, ]} @@ -532,7 +532,7 @@ const BottomTabNavigator: React.ComponentType = ({ /> - {!isTablet ? ( + {!tablet ? ( Date: Sun, 22 Dec 2024 14:33:50 +0100 Subject: [PATCH 0052/1144] =?UTF-8?q?stabilisation=20de=20la=20page=20Less?= =?UTF-8?q?ons=20lors=20de=20la=20rotation=20de=20l'=C3=A9cran?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Lessons/Lessons.tsx | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index 189100a46..22786daea 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -44,7 +44,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const loadedWeeks = useRef>(new Set()); const currentlyLoadingWeeks = useRef>(new Set()); - const { width, isTablet } = useScreenDimensions(); + const { width, height, isTablet } = useScreenDimensions(); const finalWidth = width - (isTablet ? ( 320 > width * 0.35 ? width * 0.35 : 320 @@ -130,6 +130,28 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { }; const flatListRef = useRef(null); + + useEffect(() => { + if (flatListRef.current) { + const normalizeDate = (date: Date) => { + const newDate = new Date(date); + newDate.setHours(0, 0, 0, 0); + return newDate; + }; + + const index = data.findIndex( + (d) => normalizeDate(d).getTime() === normalizeDate(pickerDate).getTime() + ); + + if (index >= 0) { + flatListRef.current.scrollToIndex({ + index, + animated: false, + }); + } + } + }, [width, height, pickerDate]); + const [data, setData] = useState(() => { const today = new Date(); return Array.from({ length: 100 }, (_, i) => { From b903478d7549019e7509805e6552e981e18a2667 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 25 Dec 2024 19:50:49 +0100 Subject: [PATCH 0053/1144] =?UTF-8?q?fix(DuoListPressable):=20ajustement?= =?UTF-8?q?=20de=20la=20marge=20sup=C3=A9rieure=20pour=20le=20texte=20sans?= =?UTF-8?q?=20sous-texte?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/FirstInstallation/DuoListPressable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/FirstInstallation/DuoListPressable.tsx b/src/components/FirstInstallation/DuoListPressable.tsx index 6e759a9a6..4a3a8400c 100644 --- a/src/components/FirstInstallation/DuoListPressable.tsx +++ b/src/components/FirstInstallation/DuoListPressable.tsx @@ -103,7 +103,7 @@ const DuoListPressable: React.FC<{ styles.text, enabled && styles.text_enabled, enabled ? { color: colors.primary } : { color: colors.text + "88" }, - subtext ? { marginBottom: 2 } : { marginTop: 5 }, + subtext ? { marginBottom: 2 } : { marginTop: 2 }, ]} numberOfLines={1} ellipsizeMode="tail" From cc1831949af595c7640be0708f588135e45f1044 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 25 Dec 2024 19:51:47 +0100 Subject: [PATCH 0054/1144] =?UTF-8?q?fix(DuoListPressable):=20centrage=20d?= =?UTF-8?q?u=20contenu=20et=20ajustement=20de=20la=20marge=20sup=C3=A9rieu?= =?UTF-8?q?re=20pour=20le=20texte=20sans=20sous-texte?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/FirstInstallation/DuoListPressable.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/FirstInstallation/DuoListPressable.tsx b/src/components/FirstInstallation/DuoListPressable.tsx index 4a3a8400c..5767d83e0 100644 --- a/src/components/FirstInstallation/DuoListPressable.tsx +++ b/src/components/FirstInstallation/DuoListPressable.tsx @@ -93,6 +93,7 @@ const DuoListPressable: React.FC<{ {children} @@ -103,7 +104,7 @@ const DuoListPressable: React.FC<{ styles.text, enabled && styles.text_enabled, enabled ? { color: colors.primary } : { color: colors.text + "88" }, - subtext ? { marginBottom: 2 } : { marginTop: 2 }, + subtext ? { marginBottom: 2 } : { marginTop: 0 }, ]} numberOfLines={1} ellipsizeMode="tail" From 8487fbd3f3a4569e4dabb0253e59624d2380fc92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Wed, 25 Dec 2024 20:30:38 +0100 Subject: [PATCH 0055/1144] Suppression des commentaires --- .github/PULL_REQUEST_TEMPLATE.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index fd4bba0dc..32e2eb867 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -36,8 +36,3 @@ _S'il y en a plusieurs, continuer à les lister_ - Closed #(_le numéro de l'issue_) - From 1538e6cd28e9bfee5a11ff8eecfd91048d1eb89f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Wed, 25 Dec 2024 20:34:59 +0100 Subject: [PATCH 0056/1144] =?UTF-8?q?Derni=C3=A8re=20version=20de=20Papill?= =?UTF-8?q?on=20+=20impl=C3=A9mentation=20de=20Alise?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/bug.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index d01ea3f6e..c991b2396 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -61,7 +61,7 @@ body: attributes: label: Version utilisée description: Paramètres (de Papillon) -> Version affichée en bas de la page - placeholder: "7.5.0" + placeholder: "7.6.0" validations: required: true @@ -77,6 +77,7 @@ body: - 🍽️🔴 Turboself - 🍽️🟣 ARD - 🍽️🔵 Izly + - 🍽️🟤 Alise validations: required: true From 9e2f4f94f14a88aa875abc3d54e63e4303292acd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Thu, 26 Dec 2024 22:41:46 +0100 Subject: [PATCH 0057/1144] fix eslint error --- .../ExternalAccount/ServiceSelector.tsx | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/views/settings/ExternalAccount/ServiceSelector.tsx b/src/views/settings/ExternalAccount/ServiceSelector.tsx index 0ebf5db90..09d915b0a 100644 --- a/src/views/settings/ExternalAccount/ServiceSelector.tsx +++ b/src/views/settings/ExternalAccount/ServiceSelector.tsx @@ -51,18 +51,18 @@ const ExternalAccountSelector: Screen<"ExternalAccountSelector"> = ({ navigation /> - - } - text="ARD" - enabled={service === AccountService.ARD} - onPress={() => setService(AccountService.ARD)} - /> - + + } + text="ARD" + enabled={service === AccountService.ARD} + onPress={() => setService(AccountService.ARD)} + /> + Date: Fri, 27 Dec 2024 15:31:00 +0100 Subject: [PATCH 0058/1144] Update `npm install` to `npm ci` --- .github/workflows/checks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 3b1f6a7ab..017b40e5b 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -21,7 +21,7 @@ jobs: cache: 'npm' - name: 💾 Install dependencies - run: npm install + run: npm ci - name: 🔍 Run TypeScript and ESLint checks id: lint From ece2835c2b54511e3225a45f8ca2c35ae0d78c1d Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Wed, 1 Jan 2025 18:54:42 +0100 Subject: [PATCH 0059/1144] feat: added new route to types --- src/router/helpers/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/router/helpers/types.ts b/src/router/helpers/types.ts index ede29094a..532f99ba5 100644 --- a/src/router/helpers/types.ts +++ b/src/router/helpers/types.ts @@ -127,6 +127,7 @@ export type RouteParameters = { SettingsSubjects: undefined; SettingsExternalServices: undefined; SettingsMagic: undefined; + SettingsMultiservice: undefined; SettingsFlags: undefined; SettingsFlagsInfos: { title: string; value: any }; SettingsAddons: undefined; From c5ee85d94091b1a2e084486f70d61114ed264e93 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Wed, 1 Jan 2025 19:04:09 +0100 Subject: [PATCH 0060/1144] feat: created new settings screen --- src/views/settings/SettingsMultiservice.tsx | 57 +++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/views/settings/SettingsMultiservice.tsx diff --git a/src/views/settings/SettingsMultiservice.tsx b/src/views/settings/SettingsMultiservice.tsx new file mode 100644 index 000000000..7edf1a934 --- /dev/null +++ b/src/views/settings/SettingsMultiservice.tsx @@ -0,0 +1,57 @@ +import React from "react"; +import { Text, ScrollView, View, StyleSheet, Switch } from "react-native"; +import { useTheme } from "@react-navigation/native"; +import type { Screen } from "@/router/helpers/types"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import important_json from "@/utils/magic/regex/important.json"; // Ensure this file contains valid regex patterns +import MagicContainerCard from "@/components/Settings/MagicContainerCard"; +import { NativeIcon, NativeIconGradient, NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; +import { PlugZap } from "lucide-react-native"; +import { useCurrentAccount } from "@/stores/account"; + +const SettingsMultiservice: Screen<"SettingsMultiservice"> = ({ navigation }) => { + const theme = useTheme(); + const { colors } = theme; + const insets = useSafeAreaInsets(); + const account = useCurrentAccount(store => store.account); + const mutateProperty = useCurrentAccount(store => store.mutateProperty); + + return ( + + {/**/} + + + + mutateProperty("personalization", { MagicNews: value })} + /> + } + leading={ + } + colors={["#04ACDC", "#6FE3CD"]} + /> + } + > + + Multiservice + + + Connecte plusieurs services différents en un seul environnement virtuel, en associant chaque fonctionnalité à un service connecté. + + + + + ); +}; + + + +export default SettingsMultiservice; From 653f00eb1bebb1244fa00727e148bf3bb5b25078 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Wed, 1 Jan 2025 19:04:41 +0100 Subject: [PATCH 0061/1144] feat: created new route --- src/router/screens/settings/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/router/screens/settings/index.ts b/src/router/screens/settings/index.ts index 68a83b5cf..3be2b45bf 100644 --- a/src/router/screens/settings/index.ts +++ b/src/router/screens/settings/index.ts @@ -27,6 +27,7 @@ import IzlyActivation from "@/views/settings/ExternalAccount/IzlyActivation"; import TurboselfAccountSelector from "@/views/settings/ExternalAccount/TurboselfAccountSelector"; import SettingsApparence from "@/views/settings/SettingsApparence"; import ExternalAliseLogin from "@/views/settings/ExternalAccount/Alise"; +import SettingsMultiservice from "@/views/settings/SettingsMultiservice"; const settingsScreens = [ createScreen("Settings", Settings, { @@ -63,6 +64,9 @@ const settingsScreens = [ createScreen("SettingsMagic", SettingsMagic, { headerTitle: "Papillon Magic", }), + createScreen("SettingsMultiservice", SettingsMultiservice, { + headerTitle: "Multiservice", + }), createScreen("SettingsAddons", SettingsAddons, { headerTitle: "Extensions", }), From bdb836fb78f03ad1cc1d4fa306b4d3dd8f660340 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Wed, 1 Jan 2025 19:04:57 +0100 Subject: [PATCH 0062/1144] feat: added new settings tab --- src/views/settings/Settings.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index 8d46498b0..3e3c7049a 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -31,7 +31,8 @@ import { Sparkles, SunMoon, SwatchBook, WandSparkles, - X + X, + Blocks } from "lucide-react-native"; import { NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; @@ -196,6 +197,13 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { description: "Fonctionnalités intelligentes", onPress: () => navigation.navigate("SettingsMagic"), }, + { + icon: , + color: "#cb7712", + label: "Multiservice (Bêta)", + description: "Connecte plusieurs services en un seul espace de travail", + onPress: () => navigation.navigate("SettingsMultiservice"), + }, ], }, { From cabacd9453be2fb0adb8f7293fe9e8b2491b11ad Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Wed, 1 Jan 2025 19:08:25 +0100 Subject: [PATCH 0063/1144] fix: renamed to "MultiService" --- src/router/helpers/types.ts | 2 +- src/router/screens/settings/index.ts | 4 ++-- ...ttingsMultiservice.tsx => SettingsMultiService.tsx} | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) rename src/views/settings/{SettingsMultiservice.tsx => SettingsMultiService.tsx} (84%) diff --git a/src/router/helpers/types.ts b/src/router/helpers/types.ts index 532f99ba5..c9dd79d6d 100644 --- a/src/router/helpers/types.ts +++ b/src/router/helpers/types.ts @@ -127,7 +127,7 @@ export type RouteParameters = { SettingsSubjects: undefined; SettingsExternalServices: undefined; SettingsMagic: undefined; - SettingsMultiservice: undefined; + SettingsMultiService: undefined; SettingsFlags: undefined; SettingsFlagsInfos: { title: string; value: any }; SettingsAddons: undefined; diff --git a/src/router/screens/settings/index.ts b/src/router/screens/settings/index.ts index 3be2b45bf..10f0e4a3b 100644 --- a/src/router/screens/settings/index.ts +++ b/src/router/screens/settings/index.ts @@ -27,7 +27,7 @@ import IzlyActivation from "@/views/settings/ExternalAccount/IzlyActivation"; import TurboselfAccountSelector from "@/views/settings/ExternalAccount/TurboselfAccountSelector"; import SettingsApparence from "@/views/settings/SettingsApparence"; import ExternalAliseLogin from "@/views/settings/ExternalAccount/Alise"; -import SettingsMultiservice from "@/views/settings/SettingsMultiservice"; +import SettingsMultiService from "@/views/settings/SettingsMultiService"; const settingsScreens = [ createScreen("Settings", Settings, { @@ -64,7 +64,7 @@ const settingsScreens = [ createScreen("SettingsMagic", SettingsMagic, { headerTitle: "Papillon Magic", }), - createScreen("SettingsMultiservice", SettingsMultiservice, { + createScreen("SettingsMultiService", SettingsMultiService, { headerTitle: "Multiservice", }), createScreen("SettingsAddons", SettingsAddons, { diff --git a/src/views/settings/SettingsMultiservice.tsx b/src/views/settings/SettingsMultiService.tsx similarity index 84% rename from src/views/settings/SettingsMultiservice.tsx rename to src/views/settings/SettingsMultiService.tsx index 7edf1a934..ab594b56f 100644 --- a/src/views/settings/SettingsMultiservice.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -5,11 +5,11 @@ import type { Screen } from "@/router/helpers/types"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import important_json from "@/utils/magic/regex/important.json"; // Ensure this file contains valid regex patterns import MagicContainerCard from "@/components/Settings/MagicContainerCard"; -import { NativeIcon, NativeIconGradient, NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; +import { NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; import { PlugZap } from "lucide-react-native"; import { useCurrentAccount } from "@/stores/account"; -const SettingsMultiservice: Screen<"SettingsMultiservice"> = ({ navigation }) => { +const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => { const theme = useTheme(); const { colors } = theme; const insets = useSafeAreaInsets(); @@ -34,9 +34,9 @@ const SettingsMultiservice: Screen<"SettingsMultiservice"> = ({ navigation }) => /> } leading={ - } - colors={["#04ACDC", "#6FE3CD"]} + color="#cb7712" /> } > @@ -54,4 +54,4 @@ const SettingsMultiservice: Screen<"SettingsMultiservice"> = ({ navigation }) => -export default SettingsMultiservice; +export default SettingsMultiService; From 7a7a5934c75792797b540ad6454081cfe5ab5e16 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Wed, 1 Jan 2025 19:08:44 +0100 Subject: [PATCH 0064/1144] fix: renamed to "MultiService" --- src/views/settings/Settings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index 3e3c7049a..36d22ad72 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -202,7 +202,7 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { color: "#cb7712", label: "Multiservice (Bêta)", description: "Connecte plusieurs services en un seul espace de travail", - onPress: () => navigation.navigate("SettingsMultiservice"), + onPress: () => navigation.navigate("SettingsMultiService"), }, ], }, From 257440b1d1af84963a2fd696ab422a9d45fb855c Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Wed, 1 Jan 2025 19:25:00 +0100 Subject: [PATCH 0065/1144] feat: added decorative card --- src/components/News/Beta.tsx | 6 +- .../Settings/MultiServiceContainerCard.tsx | 62 +++++++++++++++++++ 2 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 src/components/Settings/MultiServiceContainerCard.tsx diff --git a/src/components/News/Beta.tsx b/src/components/News/Beta.tsx index da305cdb5..8fc1d5068 100644 --- a/src/components/News/Beta.tsx +++ b/src/components/News/Beta.tsx @@ -4,10 +4,10 @@ import { NativeText } from "../Global/NativeComponents"; import { LinearGradient } from "expo-linear-gradient"; -const BetaIndicator = () => { +const BetaIndicator: React.FC<{ colors?: string[] }> = (props) => { return ( { ); }; -export default BetaIndicator; \ No newline at end of file +export default BetaIndicator; diff --git a/src/components/Settings/MultiServiceContainerCard.tsx b/src/components/Settings/MultiServiceContainerCard.tsx new file mode 100644 index 000000000..38ac18c0d --- /dev/null +++ b/src/components/Settings/MultiServiceContainerCard.tsx @@ -0,0 +1,62 @@ +import React, { useState, useEffect } from "react"; +import type { Screen } from "@/router/helpers/types"; + +import { ScrollView, Image, Text, View } from "react-native"; +import LottieView from "lottie-react-native"; +import { NativeItem, NativeList, NativeText } from "../Global/NativeComponents"; +import { LinearGradient } from "expo-linear-gradient"; +import BetaIndicator from "../News/Beta"; + +const MultiServiceContainerCard = ({ theme }: { theme: any }) => { + const { colors } = theme; + const animationref = React.useRef(null); + + useEffect(() => { + animationref.current?.play(); + }, []); + + return ( + + + + Maybe une image ici, là j'attend l'avis des #designers + {/**/} + + + + + + Activer le multi service + + + + + Rassemble tes services scolaires en un espace virtuel unique, géré par Papillon. + + + + ); +}; + +export default MultiServiceContainerCard; From e52979dffdf1b37be7ecca83cfa3f1406d060dd8 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Wed, 1 Jan 2025 22:06:37 +0100 Subject: [PATCH 0066/1144] feat: added store types --- src/stores/multiService/types.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/stores/multiService/types.ts diff --git a/src/stores/multiService/types.ts b/src/stores/multiService/types.ts new file mode 100644 index 000000000..97218376b --- /dev/null +++ b/src/stores/multiService/types.ts @@ -0,0 +1,26 @@ +import {PapillonMultiServiceSpace, PrimaryAccount} from "@/stores/account/types"; + +export enum MultiServiceFeature { + Grades = "grades", + Timetable = "timetable", + Homeworks = "homeworks", + Attendance = "attendance", + News = "news" +} + +export interface MultiServiceSpace { + accountLocalID: string + name: string + featuresServices: { + [key in keyof typeof MultiServiceFeature]: PrimaryAccount | null + } +} + +export interface MultiServiceStore { + enabled?: boolean + spaces: MultiServiceSpace[] + create: (space: MultiServiceSpace, linkAccount: PapillonMultiServiceSpace) => void + remove: (localID: string) => void + update: (localID: string, key: T, value: A[T]) => void + getFeatureAccount: (feature: keyof typeof MultiServiceFeature, spaceLocalID: string) => PrimaryAccount | null | undefined +} From 46da1586eb7c3dd204c922fca0e2222d622aa165 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Wed, 1 Jan 2025 22:06:46 +0100 Subject: [PATCH 0067/1144] feat: added store definition --- src/stores/multiService/index.ts | 110 +++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 src/stores/multiService/index.ts diff --git a/src/stores/multiService/index.ts b/src/stores/multiService/index.ts new file mode 100644 index 000000000..eb1917ec0 --- /dev/null +++ b/src/stores/multiService/index.ts @@ -0,0 +1,110 @@ +import AsyncStorage from "@react-native-async-storage/async-storage"; +import { createJSONStorage, persist } from "zustand/middleware"; +import { create } from "zustand"; +import pronote from "pawnote"; + +import { + Account, + PrimaryAccount +} from "@/stores/account/types"; +import { reload } from "@/services/reload-account"; +import { useTimetableStore } from "../timetable"; +import { useHomeworkStore } from "../homework"; +import { useGradesStore } from "../grades"; +import { useNewsStore } from "../news"; +import { useAttendanceStore } from "../attendance"; +import { log } from "@/utils/logger/logger"; +import {MultiServiceSpace, MultiServiceStore} from "@/stores/multiService/types"; +import {useAccounts} from "@/stores/account"; + + +/** + * Store for the MultiService settings & states. + * Persisted, as we want to keep the virtual spaces between app restarts. + */ +export const useMultiService = create()( + persist( + (set, get) => ({ + // When opening the app for the first time, it's null. + enabled: undefined as (boolean | undefined), + + spaces: >[], + + // When creating, we don't want the "instance" to be stored. + create: (space, linkAccount) => { + log(`creating a virtual MultiService space with account id ${linkAccount.localID} (${space.name})`, "multiService:create"); + + set((state) => ({ + spaces: [...state.spaces, space] + })); + + const accountStore = useAccounts(); + accountStore.create(linkAccount); + + log(`stored ${space.name}, and created associated account ${linkAccount.localID}`, "multiService:create"); + }, + + remove: (localID) => { + log(`removing virtual MultiService space ${localID}`, "multiService:remove"); + + set((state) => ({ + spaces: state.spaces.filter( + (space) => space.accountLocalID !== localID + ) + })); + + const accountStore = useAccounts(); + accountStore.remove(localID); + + log(`removed ${localID}`, "multiService:remove"); + }, + + toggleEnabledState: () => { + set((state) => ({ + enabled: !state.enabled + })); + }, + + /** + * Mutates a given property for a given space + * and return the updated space. + */ + update: (localID, key, value) => { + // Find the account to update in the storage. + const space = get().spaces.find((space) => space.accountLocalID === localID); + if (!space) return null; + + let spaceMutated: MultiServiceSpace; + + // Mutate only featureServices and name properties. + if (key in ["featureServices", "name"]) { + spaceMutated = { + ...space, + [key]: value + }; + } + + // Save the update in the store and storage. + set((state) => ({ + spaces: state.spaces.map((space) => + space.accountLocalID === localID + ? spaceMutated + : space + ) + })); + }, + + getFeatureAccount: (feature, spaceLocalID) => { + // Find the account to update in the storage. + const space = get().spaces.find((space) => space.accountLocalID === spaceLocalID); + if (!space) return null; + + return space.featuresServices[feature]; + }, + }), + { + name: "multiservice-storage", + storage: createJSONStorage(() => AsyncStorage) + } + ) +); From 4d579d1fd5119dac5cc03ef26b00b82daf7bfdf8 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Wed, 1 Jan 2025 22:07:21 +0100 Subject: [PATCH 0068/1144] feat: added new account type for multiservice virtual space --- src/stores/account/types.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/stores/account/types.ts b/src/stores/account/types.ts index ebce54985..790ddee95 100644 --- a/src/stores/account/types.ts +++ b/src/stores/account/types.ts @@ -105,7 +105,8 @@ export enum AccountService { Onisep, Multi, Izly, - Alise + Alise, + PapillonMultiService } /** @@ -240,12 +241,19 @@ export interface IzlyAccount extends BaseExternalAccount { } } +export interface PapillonMultiServiceSpace extends BaseAccount { + service: AccountService.PapillonMultiService + instance: null + authentication: null +} + export type PrimaryAccount = ( | PronoteAccount | EcoleDirecteAccount | SkolengoAccount | MultiAccount | LocalAccount + | PapillonMultiServiceSpace ); export type ExternalAccount = ( | TurboselfAccount From d64299c7704c43de2fac58ce7b3aa5395ba3dff4 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Wed, 1 Jan 2025 22:12:58 +0100 Subject: [PATCH 0069/1144] fix: types for `SettingsExternalServices.tsx` --- src/views/settings/SettingsExternalServices.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/settings/SettingsExternalServices.tsx b/src/views/settings/SettingsExternalServices.tsx index 91e66cb07..d4fe6cd39 100644 --- a/src/views/settings/SettingsExternalServices.tsx +++ b/src/views/settings/SettingsExternalServices.tsx @@ -26,7 +26,8 @@ const serviceConfig = { [AccountService.Parcoursup]: { icon: BookmarkMinus, name: "Parcoursup" }, [AccountService.Onisep]: { icon: Compass, name: "Onisep" }, [AccountService.Local]: { icon: GraduationCap, name: "Local" }, - [AccountService.Multi]: { icon: GraduationCap, name: "Polytechnique Hauts-de-France" } + [AccountService.Multi]: { icon: GraduationCap, name: "Polytechnique Hauts-de-France" }, + [AccountService.PapillonMultiService]: { icon: GraduationCap, name: "Environnement virtuel Papillon" } }; const SettingsExternalServices: Screen<"SettingsExternalServices"> = ({ From e02f04a5d38b1c6e5b6115ba243af1296f3cf97d Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Wed, 1 Jan 2025 22:13:17 +0100 Subject: [PATCH 0070/1144] fix: account types for new PapillonMultiService account type --- src/stores/account/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stores/account/types.ts b/src/stores/account/types.ts index 790ddee95..97366ac01 100644 --- a/src/stores/account/types.ts +++ b/src/stores/account/types.ts @@ -245,6 +245,7 @@ export interface PapillonMultiServiceSpace extends BaseAccount { service: AccountService.PapillonMultiService instance: null authentication: null + identityProvider: undefined } export type PrimaryAccount = ( From 0302644d4adac2170d8e3920ff9c83a266d5139c Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Wed, 1 Jan 2025 22:15:43 +0100 Subject: [PATCH 0071/1144] fix: missing `toggleEnabledState` type definition --- src/stores/multiService/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stores/multiService/types.ts b/src/stores/multiService/types.ts index 97218376b..cc3a7e2b3 100644 --- a/src/stores/multiService/types.ts +++ b/src/stores/multiService/types.ts @@ -22,5 +22,6 @@ export interface MultiServiceStore { create: (space: MultiServiceSpace, linkAccount: PapillonMultiServiceSpace) => void remove: (localID: string) => void update: (localID: string, key: T, value: A[T]) => void + toggleEnabledState: () => void getFeatureAccount: (feature: keyof typeof MultiServiceFeature, spaceLocalID: string) => PrimaryAccount | null | undefined } From 25aa1406a6abb21d4bd78a33a866d5ce4942ea89 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Wed, 1 Jan 2025 22:17:45 +0100 Subject: [PATCH 0072/1144] fix: implemented simple enable functionnality --- src/views/settings/SettingsMultiService.tsx | 24 ++++++++++----------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index ab594b56f..90e1fe050 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -1,20 +1,18 @@ import React from "react"; -import { Text, ScrollView, View, StyleSheet, Switch } from "react-native"; +import { ScrollView, Switch } from "react-native"; import { useTheme } from "@react-navigation/native"; import type { Screen } from "@/router/helpers/types"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; -import important_json from "@/utils/magic/regex/important.json"; // Ensure this file contains valid regex patterns -import MagicContainerCard from "@/components/Settings/MagicContainerCard"; +import MultiServiceContainerCard from "@/components/Settings/MultiServiceContainerCard"; import { NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; import { PlugZap } from "lucide-react-native"; -import { useCurrentAccount } from "@/stores/account"; +import {useAccounts} from "@/stores/account"; +import {useMultiService} from "@/stores/multiService"; const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => { const theme = useTheme(); - const { colors } = theme; - const insets = useSafeAreaInsets(); - const account = useCurrentAccount(store => store.account); - const mutateProperty = useCurrentAccount(store => store.mutateProperty); + const toggleMultiService = useMultiService(store => store.toggleEnabledState); + const multiServiceEnabled = useMultiService(store => store.enabled); + const accounts = useAccounts(); return ( = ({ navigation }) => paddingHorizontal: 15, }} > - {/**/} + mutateProperty("personalization", { MagicNews: value })} + value={multiServiceEnabled ?? false} + onValueChange={() => toggleMultiService()} /> } leading={ @@ -44,7 +42,7 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => Multiservice - Connecte plusieurs services différents en un seul environnement virtuel, en associant chaque fonctionnalité à un service connecté. + Activer le multiservice te permet de créer ton premier espace virtuel. From bb753b8c0f3370a4e394948105249f4a5e2eab61 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Wed, 1 Jan 2025 23:20:33 +0100 Subject: [PATCH 0073/1144] fix: added image property --- src/stores/multiService/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stores/multiService/types.ts b/src/stores/multiService/types.ts index cc3a7e2b3..bd9e87fc4 100644 --- a/src/stores/multiService/types.ts +++ b/src/stores/multiService/types.ts @@ -11,6 +11,7 @@ export enum MultiServiceFeature { export interface MultiServiceSpace { accountLocalID: string name: string + image: string featuresServices: { [key in keyof typeof MultiServiceFeature]: PrimaryAccount | null } From 91c55606fb3cfa9f0d2b632bb482aea28b6929a0 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 00:06:04 +0100 Subject: [PATCH 0074/1144] fix: modified types and resolved hook breaking code --- src/stores/multiService/index.ts | 17 +---------------- src/stores/multiService/types.ts | 2 +- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/src/stores/multiService/index.ts b/src/stores/multiService/index.ts index eb1917ec0..8f1786c6a 100644 --- a/src/stores/multiService/index.ts +++ b/src/stores/multiService/index.ts @@ -1,18 +1,6 @@ import AsyncStorage from "@react-native-async-storage/async-storage"; import { createJSONStorage, persist } from "zustand/middleware"; import { create } from "zustand"; -import pronote from "pawnote"; - -import { - Account, - PrimaryAccount -} from "@/stores/account/types"; -import { reload } from "@/services/reload-account"; -import { useTimetableStore } from "../timetable"; -import { useHomeworkStore } from "../homework"; -import { useGradesStore } from "../grades"; -import { useNewsStore } from "../news"; -import { useAttendanceStore } from "../attendance"; import { log } from "@/utils/logger/logger"; import {MultiServiceSpace, MultiServiceStore} from "@/stores/multiService/types"; import {useAccounts} from "@/stores/account"; @@ -38,10 +26,7 @@ export const useMultiService = create()( spaces: [...state.spaces, space] })); - const accountStore = useAccounts(); - accountStore.create(linkAccount); - - log(`stored ${space.name}, and created associated account ${linkAccount.localID}`, "multiService:create"); + log(`stored ${space.name}, with account ${linkAccount.localID}`, "multiService:create"); }, remove: (localID) => { diff --git a/src/stores/multiService/types.ts b/src/stores/multiService/types.ts index bd9e87fc4..32245616d 100644 --- a/src/stores/multiService/types.ts +++ b/src/stores/multiService/types.ts @@ -11,7 +11,7 @@ export enum MultiServiceFeature { export interface MultiServiceSpace { accountLocalID: string name: string - image: string + image?: string featuresServices: { [key in keyof typeof MultiServiceFeature]: PrimaryAccount | null } From 27446555af684fc253f21038884020728e58dc5f Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 00:06:19 +0100 Subject: [PATCH 0075/1144] feat: PoC settings page to create spaces --- src/views/settings/SettingsMultiService.tsx | 242 +++++++++++++++++++- 1 file changed, 236 insertions(+), 6 deletions(-) diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index 90e1fe050..584c36573 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -1,23 +1,105 @@ -import React from "react"; -import { ScrollView, Switch } from "react-native"; -import { useTheme } from "@react-navigation/native"; -import type { Screen } from "@/router/helpers/types"; +import React, {useRef, useState} from "react"; +import {Alert, Image, ScrollView, Switch, TextInput, View} from "react-native"; +import {useTheme} from "@react-navigation/native"; +import type {Screen} from "@/router/helpers/types"; import MultiServiceContainerCard from "@/components/Settings/MultiServiceContainerCard"; -import { NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; -import { PlugZap } from "lucide-react-native"; +import {NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText} from "@/components/Global/NativeComponents"; +import {ImageIcon, PlugZap, Plus, Type} from "lucide-react-native"; import {useAccounts} from "@/stores/account"; import {useMultiService} from "@/stores/multiService"; +import BottomSheet from "@/components/Modals/PapillonBottomSheet"; +import * as ImagePicker from "expo-image-picker"; +import {animPapillon} from "@/utils/ui/animations"; +import {ZoomIn, ZoomOut} from "react-native-reanimated"; +import PapillonSpinner from "@/components/Global/PapillonSpinner"; +import {MultiServiceSpace} from "@/stores/multiService/types"; +import {AccountService, PapillonMultiServiceSpace} from "@/stores/account/types"; +import uuid from "@/utils/uuid-v4"; +import {defaultProfilePicture} from "@/utils/ui/default-profile-picture"; const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => { const theme = useTheme(); const toggleMultiService = useMultiService(store => store.toggleEnabledState); const multiServiceEnabled = useMultiService(store => store.enabled); + const multiServiceSpaces = useMultiService(store => store.spaces); + const createMultiServiceSpace = useMultiService(store => store.create); const accounts = useAccounts(); + const [spaceCreationSheetOpened, setSpaceCreationSheetOpened] = useState(false); + const [spaceName, setSpaceName] = useState(""); + const spaceNameRef = useRef(null); + + const [loadingImage, setLoadingImage] = useState(false); + const [selectedImage, setSelectedImage] = useState(null); + const selectPicture = async () => { + setLoadingImage(true); + + let result = await ImagePicker.launchImageLibraryAsync({ + mediaTypes: ImagePicker.MediaTypeOptions.Images, + allowsEditing: true, + aspect: [1, 1], + quality: 1, + base64: true, + }); + + if (!result.canceled) { + const img = "data:image/jpeg;base64," + result.assets[0].base64; + setSelectedImage(img); + } + + setLoadingImage(false); + }; + + const [loadingCreation, setLoading] = useState(false); + const createSpace = () => { + setLoading(true); + if (spaceName == "") { + Alert.alert("Aucun titre défini", "Vous devez définir un titre à l'environnement multi service pour pouvoir le créer."); + setLoading(false); + return; + } + + const localID = uuid(); + + const linkedAccount: PapillonMultiServiceSpace = { + isExternal: false, + linkedExternalLocalIDs: [], + authentication: null, + identity: {}, + identityProvider: undefined, + instance: null, + localID: localID, + name: spaceName, + personalization: {}, + service: AccountService.PapillonMultiService, + studentName: { // TODO + first: "", last: "" + } + }; + + const space: MultiServiceSpace = { + accountLocalID: localID, + featuresServices: { + Grades: null, + Timetable: null, + Homeworks: null, + Attendance: null, + News: null + }, + name: spaceName + }; + createMultiServiceSpace(space, linkedAccount); + accounts.create(linkedAccount); + setSpaceCreationSheetOpened(false); + setLoading(false); + setSelectedImage(null); + setSpaceName(""); + }; return ( @@ -46,6 +128,154 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => + + {multiServiceEnabled && ( + <> + + + {multiServiceSpaces.map(space => ( + + } + > + + {space.name} + + + ))} + setSpaceCreationSheetOpened(true)} + icon={} + > + + Nouvel espace + + + Créer un nouvel environnement multi service + + + + opened} opened={spaceCreationSheetOpened} contentContainerStyle={{ paddingHorizontal: 16 }}> + + + + + spaceNameRef.current?.focus()} + chevron={false} + icon={} + > + + Titre de l'espace + + + + selectPicture()} + icon={loadingImage ? : } + trailing={selectedImage && } + > + + Image + + + Définir une image + + + + + + + + + createSpace()} + trailing={loadingCreation && } + > + + Créer l'espace + + + setSpaceCreationSheetOpened(false)} + > + + Annuler + + + + + + + + )} ); }; From 24fa06dd3842145c905e91e08cf9b66862c6ba0f Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 00:07:56 +0100 Subject: [PATCH 0076/1144] fix: setting image --- src/views/settings/SettingsMultiService.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index 584c36573..4eeadf3f9 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -86,7 +86,8 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => Attendance: null, News: null }, - name: spaceName + name: spaceName, + image: selectedImage || undefined }; createMultiServiceSpace(space, linkedAccount); accounts.create(linkedAccount); From 60001235ffbd52c35247379f4b46c91432d60f93 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 00:15:24 +0100 Subject: [PATCH 0077/1144] fix: added identity provider name to MultiServiceSpace (to show space title in account selection) --- src/stores/account/types.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/stores/account/types.ts b/src/stores/account/types.ts index 97366ac01..6a44ec53e 100644 --- a/src/stores/account/types.ts +++ b/src/stores/account/types.ts @@ -245,7 +245,9 @@ export interface PapillonMultiServiceSpace extends BaseAccount { service: AccountService.PapillonMultiService instance: null authentication: null - identityProvider: undefined + identityProvider: { + name: string + } } export type PrimaryAccount = ( From 641679b678b0f5bfbc554a52e622ca8d1a5e4298 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 00:15:50 +0100 Subject: [PATCH 0078/1144] fix: enhanced PoC with customization... --- src/views/settings/SettingsMultiService.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index 4eeadf3f9..7c7f3ec67 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -66,14 +66,19 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => linkedExternalLocalIDs: [], authentication: null, identity: {}, - identityProvider: undefined, + identityProvider: { + name: "Environnement multiservice Papillon" + }, instance: null, localID: localID, name: spaceName, - personalization: {}, + personalization: { + profilePictureB64: selectedImage || undefined + }, service: AccountService.PapillonMultiService, studentName: { // TODO - first: "", last: "" + first: spaceName, + last: "" } }; From 50ecbf7eb01b5ab48c385e239b7d0d42709f1d77 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 00:18:21 +0100 Subject: [PATCH 0079/1144] fix: removed breaking hooks code --- src/stores/multiService/index.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/stores/multiService/index.ts b/src/stores/multiService/index.ts index 8f1786c6a..e092b7e92 100644 --- a/src/stores/multiService/index.ts +++ b/src/stores/multiService/index.ts @@ -38,9 +38,6 @@ export const useMultiService = create()( ) })); - const accountStore = useAccounts(); - accountStore.remove(localID); - log(`removed ${localID}`, "multiService:remove"); }, From 3f5970f21fe381e22e159efb3afe48de9d5edcde Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 00:26:23 +0100 Subject: [PATCH 0080/1144] feat: deleting spaces --- src/views/settings/SettingsMultiService.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index 7c7f3ec67..46c581f0d 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -4,7 +4,7 @@ import {useTheme} from "@react-navigation/native"; import type {Screen} from "@/router/helpers/types"; import MultiServiceContainerCard from "@/components/Settings/MultiServiceContainerCard"; import {NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText} from "@/components/Global/NativeComponents"; -import {ImageIcon, PlugZap, Plus, Type} from "lucide-react-native"; +import {ImageIcon, PlugZap, Plus, Type, Trash2} from "lucide-react-native"; import {useAccounts} from "@/stores/account"; import {useMultiService} from "@/stores/multiService"; import BottomSheet from "@/components/Modals/PapillonBottomSheet"; @@ -23,6 +23,7 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => const multiServiceEnabled = useMultiService(store => store.enabled); const multiServiceSpaces = useMultiService(store => store.spaces); const createMultiServiceSpace = useMultiService(store => store.create); + const deleteMultiServiceSpace = useMultiService(store => store.remove); const accounts = useAccounts(); const [spaceCreationSheetOpened, setSpaceCreationSheetOpened] = useState(false); @@ -154,6 +155,15 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => }} /> } + trailing={ + { + accounts.remove(space.accountLocalID); + deleteMultiServiceSpace(space.accountLocalID); + }} + color="#CF0029" + /> + } > {space.name} From 84750218f1d78f57702fde0eb2d72f7248972256 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 11:01:09 +0100 Subject: [PATCH 0081/1144] feat: added asset for multiservice --- assets/images/multiservice.png | Bin 0 -> 8065 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/images/multiservice.png diff --git a/assets/images/multiservice.png b/assets/images/multiservice.png new file mode 100644 index 0000000000000000000000000000000000000000..c15330b5019ca37e7c527fc291aee2f39b13e7db GIT binary patch literal 8065 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
Date: Sun, 19 Jan 2025 01:38:19 +0100 Subject: [PATCH 0280/1144] suppression flex: 1 non fonctionnel avec `MemoizedNativeList` --- src/components/Global/NativeComponents.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Global/NativeComponents.tsx b/src/components/Global/NativeComponents.tsx index f313cec9e..b8374c1f5 100644 --- a/src/components/Global/NativeComponents.tsx +++ b/src/components/Global/NativeComponents.tsx @@ -75,7 +75,6 @@ export const NativeList: React.FC = ({ > Date: Sun, 19 Jan 2025 16:09:08 +0100 Subject: [PATCH 0281/1144] feat(logs): add new stacktrace obtension methods Signed-off-by: Gabriel29306 --- src/utils/logger/logger.ts | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/utils/logger/logger.ts b/src/utils/logger/logger.ts index 915e8a78b..6f078a097 100644 --- a/src/utils/logger/logger.ts +++ b/src/utils/logger/logger.ts @@ -38,6 +38,24 @@ function get_file_from_stacktrace (stack: string): string return (res); } + +function obtain_function_name (from?: string): string { + const error = new Error(); // On génère une erreur pour obtenir la stacktrace + const stack = error.stack?.split("\n") || []; + + const relevantLine = stack + .slice(3) // Ignore les premières lignes (celle du logger) + .find((line) => line.includes("at ") && line.includes("http")) // Recherche une ligne pertinente + ?.trim(); + + // Extraire le nom de la fonction ou utiliser `from` si on trouve pas + let functionName = (relevantLine && RegExp(/at (\S+)\s\(/).exec(relevantLine)?.[1]) ?? from; + // `anon` cherche à matcher avec `anonymous` et `?anon_0_` qui sont des fonctions anonymes + if (!functionName || functionName.includes("anon")) functionName = "UNKOWN"; + + return functionName; +} + function save_logs_to_memory (log: string) { AsyncStorage.getItem("logs") @@ -122,4 +140,4 @@ const delete_logs = async () => { await AsyncStorage.removeItem("logs"); }; -export { log, error, warn, info, navigate, get_logs, get_brute_logs, delete_logs }; \ No newline at end of file +export { log, error, warn, info, navigate, get_logs, get_brute_logs, delete_logs }; From 0b68eaeb6bcbe7bd075191e8cf1f33b4ae448c1e Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sun, 19 Jan 2025 16:09:58 +0100 Subject: [PATCH 0282/1144] feat(logs): use new method Signed-off-by: Gabriel29306 --- src/utils/logger/logger.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils/logger/logger.ts b/src/utils/logger/logger.ts index 6f078a097..e8f4f7f6b 100644 --- a/src/utils/logger/logger.ts +++ b/src/utils/logger/logger.ts @@ -72,25 +72,25 @@ function save_logs_to_memory (log: string) } function log (message: string, from: string): void { - let log = get_message(0, get_iso_date(), get_file_from_stacktrace(from), message); + let log = get_message(0, get_iso_date(), obtain_function_name(from), message); save_logs_to_memory(log); console.log(log); } function error (message: string, from: string): void { - let log = get_message(1, get_iso_date(), get_file_from_stacktrace(from), message); + let log = get_message(1, get_iso_date(), obtain_function_name(from), message); save_logs_to_memory(log); console.log(log); } function warn (message: string, from: string): void { - let log = get_message(2, get_iso_date(), get_file_from_stacktrace(from), message); + let log = get_message(2, get_iso_date(), obtain_function_name(from), message); save_logs_to_memory(log); console.log(log); } function info (message: string, from: string): void { - let log = get_message(3, get_iso_date(), get_file_from_stacktrace(from), message); + let log = get_message(3, get_iso_date(), obtain_function_name(from), message); save_logs_to_memory(log); console.log(log); } From 81c39a7d7239da875f762d2e42d83ab625387aa6 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sun, 19 Jan 2025 16:10:35 +0100 Subject: [PATCH 0283/1144] feat(logs): remove old method Signed-off-by: Gabriel29306 --- src/utils/logger/logger.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/utils/logger/logger.ts b/src/utils/logger/logger.ts index e8f4f7f6b..bcd00b7bc 100644 --- a/src/utils/logger/logger.ts +++ b/src/utils/logger/logger.ts @@ -24,20 +24,6 @@ function get_message (type: number, date: string, from: string, message: string) ); } -function get_file_from_stacktrace (stack: string): string -{ - let res = ""; - try { - res = stack - .split("\n")[1] - .split(/\/\/localhost:\d\d\d\d\//g)[1] - .split("//&")[0]; - } catch (e) { - res = "UNKOWN"; - } - return (res); -} - function obtain_function_name (from?: string): string { const error = new Error(); // On génère une erreur pour obtenir la stacktrace From 465a0c78ddd936c03866de3bfd858bfb9fde342f Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sun, 19 Jan 2025 16:12:54 +0100 Subject: [PATCH 0284/1144] feat(logs): make `from` arg facultative Signed-off-by: Gabriel29306 --- src/utils/logger/logger.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils/logger/logger.ts b/src/utils/logger/logger.ts index bcd00b7bc..f9c5a247f 100644 --- a/src/utils/logger/logger.ts +++ b/src/utils/logger/logger.ts @@ -57,25 +57,25 @@ function save_logs_to_memory (log: string) }); } -function log (message: string, from: string): void { +function log (message: string, from?: string): void { let log = get_message(0, get_iso_date(), obtain_function_name(from), message); save_logs_to_memory(log); console.log(log); } -function error (message: string, from: string): void { +function error (message: string, from?: string): void { let log = get_message(1, get_iso_date(), obtain_function_name(from), message); save_logs_to_memory(log); console.log(log); } -function warn (message: string, from: string): void { +function warn (message: string, from?: string): void { let log = get_message(2, get_iso_date(), obtain_function_name(from), message); save_logs_to_memory(log); console.log(log); } -function info (message: string, from: string): void { +function info (message: string, from?: string): void { let log = get_message(3, get_iso_date(), obtain_function_name(from), message); save_logs_to_memory(log); console.log(log); From b2255a045109314e5c0963a7aa9efc42978b1cb0 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sun, 19 Jan 2025 16:15:27 +0100 Subject: [PATCH 0285/1144] lint Signed-off-by: Gabriel29306 --- src/components/Grades/AnimatedEmoji.tsx | 2 +- src/components/Grades/GradeModal.tsx | 4 +- src/router/navigator/atoms/MenuItem.tsx | 8 ++-- src/router/navigator/atoms/TabItem.tsx | 6 +-- src/router/navigator/menu.tsx | 10 ++--- src/router/navigator/navigator.tsx | 3 +- src/router/navigator/tabs.tsx | 4 +- src/views/account/Chat/Modals/Chat.tsx | 2 +- src/views/account/Grades/Document.tsx | 6 +-- .../account/Grades/Graph/GradesAverage.tsx | 40 +++++++++---------- .../login/pronote/PronoteInstanceSelector.tsx | 1 - .../PriceDetectionOnboarding.tsx | 4 +- .../ExternalAccount/ServiceSelector.tsx | 3 +- src/views/settings/SettingsDevLogs.tsx | 5 --- src/views/settings/SettingsProfile.tsx | 1 - src/views/settings/SettingsReactions.tsx | 3 +- 16 files changed, 39 insertions(+), 63 deletions(-) diff --git a/src/components/Grades/AnimatedEmoji.tsx b/src/components/Grades/AnimatedEmoji.tsx index 7e12350d4..5e3d9d1a4 100644 --- a/src/components/Grades/AnimatedEmoji.tsx +++ b/src/components/Grades/AnimatedEmoji.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { View, Text } from "react-native"; +import { View } from "react-native"; import Animated, { useAnimatedStyle, useSharedValue, diff --git a/src/components/Grades/GradeModal.tsx b/src/components/Grades/GradeModal.tsx index a022b3822..6b09b29d3 100644 --- a/src/components/Grades/GradeModal.tsx +++ b/src/components/Grades/GradeModal.tsx @@ -5,13 +5,11 @@ import { Image, TouchableOpacity, Text, - Platform, Alert } from "react-native"; -import { Download, Trash, Maximize2, Share, Delete } from "lucide-react-native"; +import { Download, Trash, Share } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { BlurView } from "expo-blur"; -import { ScrollView } from "react-native-gesture-handler"; import * as Sharing from "expo-sharing"; import * as FileSystem from "expo-file-system"; import * as MediaLibrary from "expo-media-library"; diff --git a/src/router/navigator/atoms/MenuItem.tsx b/src/router/navigator/atoms/MenuItem.tsx index 2a9b548ce..ff64a6de5 100644 --- a/src/router/navigator/atoms/MenuItem.tsx +++ b/src/router/navigator/atoms/MenuItem.tsx @@ -1,13 +1,11 @@ import * as React from "react"; -import { useCurrentAccount } from "@/stores/account"; -import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, View, Text, Platform } from "react-native"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { useTheme } from "@react-navigation/native"; +import { StyleSheet, Platform } from "react-native"; import LottieView from "lottie-react-native"; import colorsList from "@/utils/data/colors.json"; import { Pressable } from "react-native-gesture-handler"; import * as Haptics from "expo-haptics"; -import Reanimated, { FadeIn, FadeOut, LinearTransition, ZoomIn } from "react-native-reanimated"; +import Reanimated, { FadeIn, FadeOut, LinearTransition } from "react-native-reanimated"; import { anim2Papillon } from "@/utils/ui/animations"; const MenuItem: React.FC<{ diff --git a/src/router/navigator/atoms/TabItem.tsx b/src/router/navigator/atoms/TabItem.tsx index 90da917e3..cc8fe2536 100644 --- a/src/router/navigator/atoms/TabItem.tsx +++ b/src/router/navigator/atoms/TabItem.tsx @@ -1,8 +1,6 @@ import * as React from "react"; -import { useCurrentAccount } from "@/stores/account"; -import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, View, Text, Platform } from "react-native"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { useTheme } from "@react-navigation/native"; +import { StyleSheet, Platform } from "react-native"; import LottieView from "lottie-react-native"; import colorsList from "@/utils/data/colors.json"; import { Pressable } from "react-native-gesture-handler"; diff --git a/src/router/navigator/menu.tsx b/src/router/navigator/menu.tsx index d12793b8b..12082b86c 100644 --- a/src/router/navigator/menu.tsx +++ b/src/router/navigator/menu.tsx @@ -1,17 +1,13 @@ -import React, { useEffect, useMemo, useState } from "react"; +import React, { useMemo, useState } from "react"; import { useCurrentAccount } from "@/stores/account"; import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, View, ScrollView, Platform, Image, Text, StatusBar } from "react-native"; +import { StyleSheet, Platform, Image, Text, StatusBar } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import TabItem from "./atoms/TabItem"; -import Reanimated, { LinearTransition, useSharedValue } from "react-native-reanimated"; +import Reanimated from "react-native-reanimated"; import MenuItem from "./atoms/MenuItem"; -import AccountSwitcher from "@/components/Home/AccountSwitcher"; import ContextMenu from "@/components/Home/AccountSwitcherContextMenu"; -import { NativeText } from "@/components/Global/NativeComponents"; import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; -import { he } from "date-fns/locale"; const PapillonNavigatorMenu: React.FC, "NavigationContent">> = ({ state, descriptors, navigation }) => { const theme = useTheme(); diff --git a/src/router/navigator/navigator.tsx b/src/router/navigator/navigator.tsx index 027e57dec..888137f44 100644 --- a/src/router/navigator/navigator.tsx +++ b/src/router/navigator/navigator.tsx @@ -1,8 +1,7 @@ import { BottomTabView } from "@react-navigation/bottom-tabs"; import { createNavigatorFactory, TabRouter, useNavigationBuilder } from "@react-navigation/native"; import PapillonNavigatorTabs from "./tabs"; -import { memo, useEffect, useMemo, useState } from "react"; -import { Dimensions, View } from "react-native"; +import { View } from "react-native"; import PapillonNavigatorMenu from "./menu"; import useScreenDimensions from "@/hooks/useScreenDimensions"; diff --git a/src/router/navigator/tabs.tsx b/src/router/navigator/tabs.tsx index a82d23b88..257f2c0a3 100644 --- a/src/router/navigator/tabs.tsx +++ b/src/router/navigator/tabs.tsx @@ -1,11 +1,11 @@ import React, { useEffect, useMemo, useState } from "react"; import { useCurrentAccount } from "@/stores/account"; import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, View, Platform } from "react-native"; +import { StyleSheet, Platform } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import TabItem from "./atoms/TabItem"; -import Reanimated, { LinearTransition } from "react-native-reanimated"; +import Reanimated from "react-native-reanimated"; const PapillonNavigatorTabs: React.FC, "NavigationContent">> = ({ state, descriptors, navigation }) => { const theme = useTheme(); diff --git a/src/views/account/Chat/Modals/Chat.tsx b/src/views/account/Chat/Modals/Chat.tsx index 90f2d5538..6f8bb8d36 100644 --- a/src/views/account/Chat/Modals/Chat.tsx +++ b/src/views/account/Chat/Modals/Chat.tsx @@ -6,7 +6,7 @@ import type {Screen} from "@/router/helpers/types"; import { NativeText,} from "@/components/Global/NativeComponents"; import {useCurrentAccount} from "@/stores/account"; import type {ChatMessage, ChatRecipient} from "@/services/shared/Chat"; -import {ChevronLeft, File, Link, Send} from "lucide-react-native"; +import {ChevronLeft, Send} from "lucide-react-native"; import parse_initials from "@/utils/format/format_pronote_initials"; import InitialIndicator from "@/components/News/InitialIndicator"; import {PapillonModernHeader} from "@/components/Global/PapillonModernHeader"; diff --git a/src/views/account/Grades/Document.tsx b/src/views/account/Grades/Document.tsx index 2e6481f20..9d148bda8 100644 --- a/src/views/account/Grades/Document.tsx +++ b/src/views/account/Grades/Document.tsx @@ -7,18 +7,14 @@ import { import { getSubjectData } from "@/services/shared/Subject"; import { useTheme } from "@react-navigation/native"; import React, { useCallback, useEffect, useLayoutEffect, useState } from "react"; -import { Image, ScrollView, Text, View, Platform, TouchableOpacity, Modal } from "react-native"; +import { Image, ScrollView, Text, View, Platform, TouchableOpacity } from "react-native"; import * as StoreReview from "expo-store-review"; import { Asterisk, Calculator, - Download, - Expand, Maximize2, Scale, School, - SmilePlus, - Trash, UserMinus, UserPlus, Users, diff --git a/src/views/account/Grades/Graph/GradesAverage.tsx b/src/views/account/Grades/Graph/GradesAverage.tsx index d69d95739..f6e96a7d0 100644 --- a/src/views/account/Grades/Graph/GradesAverage.tsx +++ b/src/views/account/Grades/Graph/GradesAverage.tsx @@ -329,26 +329,26 @@ const GradesAverageGraph: React.FC = ({ - Moyenne classe - - {classAvg !== null ? ( - <> - - - /20 - - - ) : ( - Inconnue - )} - - + Moyenne classe + + {classAvg !== null ? ( + <> + + + /20 + + + ) : ( + Inconnue + )} + + {showDetails && maxAvg > 0 && minAvg > 0 ? ( diff --git a/src/views/login/pronote/PronoteInstanceSelector.tsx b/src/views/login/pronote/PronoteInstanceSelector.tsx index c2bfd27df..62158b4d4 100644 --- a/src/views/login/pronote/PronoteInstanceSelector.tsx +++ b/src/views/login/pronote/PronoteInstanceSelector.tsx @@ -8,7 +8,6 @@ import { ActivityIndicator, Keyboard, KeyboardEvent, - Text, } from "react-native"; import pronote from "pawnote"; import Reanimated, { diff --git a/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx b/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx index 36bc869d0..a22b06d77 100644 --- a/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx +++ b/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx @@ -1,11 +1,11 @@ -import React, { useState } from "react"; +import React from "react"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { BellRing } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { Image, View, StyleSheet, Text } from "react-native"; import { NativeText, } from "@/components/Global/NativeComponents"; -import { useAccounts, useCurrentAccount } from "@/stores/account"; +import { useCurrentAccount } from "@/stores/account"; import { TouchableOpacity } from "react-native-gesture-handler"; import BetaIndicator from "@/components/News/Beta"; diff --git a/src/views/settings/ExternalAccount/ServiceSelector.tsx b/src/views/settings/ExternalAccount/ServiceSelector.tsx index 6f751bd9f..6c148cfba 100644 --- a/src/views/settings/ExternalAccount/ServiceSelector.tsx +++ b/src/views/settings/ExternalAccount/ServiceSelector.tsx @@ -10,8 +10,7 @@ import { AccountService } from "@/stores/account/types"; import { useCurrentAccount } from "@/stores/account"; import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; -import { LinearGradient } from "expo-linear-gradient"; - + const ExternalAccountSelector: Screen<"ExternalAccountSelector"> = ({ navigation, route }) => { const theme = useTheme(); const { colors } = theme; diff --git a/src/views/settings/SettingsDevLogs.tsx b/src/views/settings/SettingsDevLogs.tsx index 70257a648..cc67637be 100644 --- a/src/views/settings/SettingsDevLogs.tsx +++ b/src/views/settings/SettingsDevLogs.tsx @@ -9,7 +9,6 @@ import { } from "@/components/Global/NativeComponents"; import React, { useEffect, useState } from "react"; import { - get_brute_logs, get_logs, Log, delete_logs, @@ -30,14 +29,10 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import { PressableScale } from "react-native-pressable-scale"; import { FadeInDown, - FadeInUp, - FadeOutDown, FadeOutUp, } from "react-native-reanimated"; import { animPapillon } from "@/utils/ui/animations"; -import News from "../account/News/News"; import { useTheme } from "@react-navigation/native"; -import Reanimated from "react-native-reanimated"; const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { const theme = useTheme(); diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index 56f717b6c..c7630b773 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -10,7 +10,6 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import * as Clipboard from "expo-clipboard"; import { TouchableOpacity } from "react-native-gesture-handler"; -import { th } from "date-fns/locale"; const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { const theme = useTheme(); diff --git a/src/views/settings/SettingsReactions.tsx b/src/views/settings/SettingsReactions.tsx index 49400a566..6b691d7f8 100644 --- a/src/views/settings/SettingsReactions.tsx +++ b/src/views/settings/SettingsReactions.tsx @@ -1,11 +1,10 @@ import React from "react"; -import { ScrollView, Text, View } from "react-native"; +import { ScrollView, View } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { useGradesStore } from "@/stores/grades"; import ReelGallery from "@/components/Settings/ReelGallery"; import MissingItem from "@/components/Global/MissingItem"; -import AnimatedEmoji from "@/components/Grades/AnimatedEmoji"; const SettingsReactions: Screen<"SettingsReactions"> = () => { const theme = useTheme(); From da5626418d7fc660e0cbad0d1596fff5b5e5ec0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Sun, 19 Jan 2025 18:52:41 +0100 Subject: [PATCH 0286/1144] Update src/utils/native/playSoundHaptics.ts Co-authored-by: ggkervran <73659505+Gabriel29306@users.noreply.github.com> --- src/utils/native/playSoundHaptics.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils/native/playSoundHaptics.ts b/src/utils/native/playSoundHaptics.ts index acfdaedd6..4a7544b70 100644 --- a/src/utils/native/playSoundHaptics.ts +++ b/src/utils/native/playSoundHaptics.ts @@ -2,6 +2,8 @@ import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; import { Sound } from "expo-av/build/Audio"; import * as Haptics from "expo-haptics"; +import type { AVPlaybackStatus } from "expo-av/build/AV.types"; + const useSoundHapticsWrapper = () => { const { enableHaptics, enableSon } = useThemeSoundHaptics(); From c527537eca39d23076b35dec799ae6f987e040d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Sun, 19 Jan 2025 18:53:06 +0100 Subject: [PATCH 0287/1144] Update src/utils/native/playSoundHaptics.ts Co-authored-by: ggkervran <73659505+Gabriel29306@users.noreply.github.com> --- src/utils/native/playSoundHaptics.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/utils/native/playSoundHaptics.ts b/src/utils/native/playSoundHaptics.ts index 4a7544b70..68486fc9d 100644 --- a/src/utils/native/playSoundHaptics.ts +++ b/src/utils/native/playSoundHaptics.ts @@ -24,6 +24,10 @@ const useSoundHapticsWrapper = () => { const playSound = async (srcSound: any) => { if (enableSon) { const { sound } = await Sound.createAsync(srcSound); + const onPlaybackStatusUpdate = async (status: AVPlaybackStatus) => { + if (status.isLoaded && status.didJustFinish) await sound.unloadAsync(); + }; + sound.setOnPlaybackStatusUpdate(onPlaybackStatusUpdate); await sound.setPositionAsync(0); await sound.playAsync(); } From 9e180ba9b6f0025fc811d482b178e1d069bf966c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 19 Jan 2025 20:04:18 +0100 Subject: [PATCH 0288/1144] =?UTF-8?q?chore:=20mise=20=C3=A0=20jour=20du=20?= =?UTF-8?q?fichier=20.gitignore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index afbf18c05..58fb6e7cd 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ node_modules/ .expo/ dist/ web-build/ +android/app/release/ # Native *.orig.* @@ -35,9 +36,3 @@ yarn-error.* *.tsbuildinfo .idea/ - -# Expo -.expo -dist/ -web-build/ -android/app/release/ \ No newline at end of file From 689a050436c8a630299ff9f77e13328b29a7583c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 19 Jan 2025 20:09:26 +0100 Subject: [PATCH 0289/1144] =?UTF-8?q?fix:=20d=C3=A9tection=20d'erreurs=20e?= =?UTF-8?q?slint/tsc=20alors=20que=20les=20fichiers=20sont=20vides?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/checks.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index e9c82173e..69d806c3d 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -75,13 +75,20 @@ jobs: cat tsc.log eslint.log > lint.log continue-on-error: true + - name: 📋 Check if lint logs are empty + id: check_logs + run: | + if [ -s lint.log ]; then + echo "is_empty=false" >> $GITHUB_ENV + else + echo "is_empty=true" >> $GITHUB_ENV + fi + - name: 📄 Output Logs run: cat lint.log - name: 👁️‍🗨️ Comment on Pull Request - if: env.LINT_EMPTY != 'true' - env: - LINT_EMPTY: ${{ steps.check_logs.outputs.is_empty }} + if: env.is_empty == 'false' uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} From 1b632c8d4459d5111d9878824669a7b56f7d1ada Mon Sep 17 00:00:00 2001 From: JyhuKo Date: Mon, 20 Jan 2025 17:27:57 +0100 Subject: [PATCH 0290/1144] better attendance date MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit j ai rienc chnagé c etait juste pour update la branch --- .../Attendance/Atoms/AttendanceItem.tsx | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/views/account/Attendance/Atoms/AttendanceItem.tsx b/src/views/account/Attendance/Atoms/AttendanceItem.tsx index c20aef38c..58f3306ac 100644 --- a/src/views/account/Attendance/Atoms/AttendanceItem.tsx +++ b/src/views/account/Attendance/Atoms/AttendanceItem.tsx @@ -1,4 +1,4 @@ -import { type ReactNode, useState } from "react"; +import { type ReactNode, type Dispatch, type SetStateAction, useState } from "react"; import type { Attendance } from "@/services/shared/Attendance"; import type { Absence } from "@/services/shared/Absence"; @@ -85,8 +85,33 @@ const AttendanceItem: React.FC = ({ } const timestamp = "fromTimestamp" in item ? item.fromTimestamp : item.timestamp; + const toTimestamp = "toTimestamp" in item ? item.toTimestamp : null; const not_justified = "justified" in item && !item.justified; const justification = "reasons" in item ? item.reasons || NO_JUSTICATION : "reason" in item ? item.reason.text : NO_JUSTICATION; + const dateString = toTimestamp + ? `Du ${new Date(timestamp).toLocaleDateString("fr-FR", { + day: "2-digit", + month: "2-digit", + year: new Date(timestamp).getFullYear() !== new Date().getFullYear() ? "2-digit" : undefined, + })} à ${new Date(timestamp).toLocaleTimeString("fr-FR", { + hour: "2-digit", + minute: "2-digit", + })}\nAu ${new Date(toTimestamp).toLocaleDateString("fr-FR", { + day: "2-digit", + month: "2-digit", + year: new Date(toTimestamp).getFullYear() !== new Date().getFullYear() ? "2-digit" : undefined, + })} à ${new Date(toTimestamp).toLocaleTimeString("fr-FR", { + hour: "2-digit", + minute: "2-digit", + })}` + : `${new Date(timestamp).toLocaleDateString("fr-FR", { + weekday: "long", + day: "2-digit", + month: "short", + })} à ${new Date(timestamp).toLocaleTimeString("fr-FR", { + hour: "2-digit", + minute: "2-digit", + })}`; return ( = ({ )} - {new Date(timestamp).toLocaleDateString("fr-FR", { - weekday: "long", - month: "short", - day: "numeric", - hour: "2-digit", - minute: "2-digit", - })} + {dateString} ); From 2a068bb18f33b9ee34c687092dcf881d7a1c2e13 Mon Sep 17 00:00:00 2001 From: JyhuKo Date: Mon, 20 Jan 2025 17:31:01 +0100 Subject: [PATCH 0291/1144] lint --- src/views/account/Attendance/Atoms/AttendanceItem.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views/account/Attendance/Atoms/AttendanceItem.tsx b/src/views/account/Attendance/Atoms/AttendanceItem.tsx index 58f3306ac..e96914e2d 100644 --- a/src/views/account/Attendance/Atoms/AttendanceItem.tsx +++ b/src/views/account/Attendance/Atoms/AttendanceItem.tsx @@ -1,4 +1,4 @@ -import { type ReactNode, type Dispatch, type SetStateAction, useState } from "react"; +import { type ReactNode, useState } from "react"; import type { Attendance } from "@/services/shared/Attendance"; import type { Absence } from "@/services/shared/Absence"; @@ -89,7 +89,7 @@ const AttendanceItem: React.FC = ({ const not_justified = "justified" in item && !item.justified; const justification = "reasons" in item ? item.reasons || NO_JUSTICATION : "reason" in item ? item.reason.text : NO_JUSTICATION; const dateString = toTimestamp - ? `Du ${new Date(timestamp).toLocaleDateString("fr-FR", { + ? `Du ${new Date(timestamp).toLocaleDateString("fr-FR", { day: "2-digit", month: "2-digit", year: new Date(timestamp).getFullYear() !== new Date().getFullYear() ? "2-digit" : undefined, @@ -104,7 +104,7 @@ const AttendanceItem: React.FC = ({ hour: "2-digit", minute: "2-digit", })}` - : `${new Date(timestamp).toLocaleDateString("fr-FR", { + : `${new Date(timestamp).toLocaleDateString("fr-FR", { weekday: "long", day: "2-digit", month: "short", From 5e04820ef488320ff09fd6cd3375b1c824795c22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Mon, 20 Jan 2025 19:09:18 +0100 Subject: [PATCH 0292/1144] adapt `app.config.ts` --- app.config.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app.config.ts b/app.config.ts index f0a1e0fc7..063cbb4b0 100644 --- a/app.config.ts +++ b/app.config.ts @@ -6,7 +6,7 @@ export default (): ExpoConfig => ({ slug: PackageJSON.name, scheme: "papillon", version: PackageJSON.version, - orientation: "portrait", + orientation: "default", icon: "./assets/icon.png", userInterfaceStyle: "automatic", primaryColor: "#32AB8E", @@ -77,6 +77,7 @@ export default (): ExpoConfig => ({ ], }, plugins: [ + "./plugins/notifee-mod.js", [ "expo-font", { From 45837d149fc833c0386e72f20df02ad7082383db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Mon, 20 Jan 2025 19:10:10 +0100 Subject: [PATCH 0293/1144] =?UTF-8?q?fix:=20me=20suis=20tromp=C3=A9=20de?= =?UTF-8?q?=20branch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.config.ts b/app.config.ts index 063cbb4b0..a42b248c1 100644 --- a/app.config.ts +++ b/app.config.ts @@ -6,7 +6,7 @@ export default (): ExpoConfig => ({ slug: PackageJSON.name, scheme: "papillon", version: PackageJSON.version, - orientation: "default", + orientation: "portrait", icon: "./assets/icon.png", userInterfaceStyle: "automatic", primaryColor: "#32AB8E", From f7046036d06f13be0d6138a7ebf95158772a40b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Mon, 20 Jan 2025 19:11:58 +0100 Subject: [PATCH 0294/1144] adapt file `app.config.ts` --- app.config.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app.config.ts b/app.config.ts index f0a1e0fc7..063cbb4b0 100644 --- a/app.config.ts +++ b/app.config.ts @@ -6,7 +6,7 @@ export default (): ExpoConfig => ({ slug: PackageJSON.name, scheme: "papillon", version: PackageJSON.version, - orientation: "portrait", + orientation: "default", icon: "./assets/icon.png", userInterfaceStyle: "automatic", primaryColor: "#32AB8E", @@ -77,6 +77,7 @@ export default (): ExpoConfig => ({ ], }, plugins: [ + "./plugins/notifee-mod.js", [ "expo-font", { From a228d56ad129afda0f04c45d94947c9833b77725 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Mon, 20 Jan 2025 23:17:59 +0100 Subject: [PATCH 0295/1144] running prebuild --- android/app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 8c4795317..ffa1e6e59 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -88,8 +88,8 @@ android { applicationId 'xyz.getpapillon.app' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 772 - versionName "7.7.2" + versionCode 780 + versionName "7.8.0" } signingConfigs { debug { From 3ae960f6795f31734ad12ea4100d9db14a31d1a2 Mon Sep 17 00:00:00 2001 From: Bulgus <88266443+Bulgus@users.noreply.github.com> Date: Tue, 21 Jan 2025 13:45:01 +0100 Subject: [PATCH 0296/1144] lint --- src/components/Templates/LoginView.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/Templates/LoginView.tsx b/src/components/Templates/LoginView.tsx index decbccea5..08a6387f1 100644 --- a/src/components/Templates/LoginView.tsx +++ b/src/components/Templates/LoginView.tsx @@ -92,11 +92,9 @@ const LoginView: React.FC<{ const translateError = (error: string | null): string | null => { if (!error) return null; - if (error.includes("challenge")) { return "Un CAPTCHA est requis pour se connecter, utilise l'ENT pour te connecter."; } - // Add other error translations here return error; }; From 096ba753ae4c6ecb0eb1c051cf4a00c70c92f7de Mon Sep 17 00:00:00 2001 From: Bulgus <88266443+Bulgus@users.noreply.github.com> Date: Tue, 21 Jan 2025 17:35:19 +0100 Subject: [PATCH 0297/1144] No translation function and only one error message to connect via ENT --- src/components/Templates/LoginView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Templates/LoginView.tsx b/src/components/Templates/LoginView.tsx index 08a6387f1..da05670fe 100644 --- a/src/components/Templates/LoginView.tsx +++ b/src/components/Templates/LoginView.tsx @@ -93,7 +93,7 @@ const LoginView: React.FC<{ const translateError = (error: string | null): string | null => { if (!error) return null; if (error.includes("challenge")) { - return "Un CAPTCHA est requis pour se connecter, utilise l'ENT pour te connecter."; + return "Impossible de se connecter, utilises la connexion par l'ENT"; } // Add other error translations here return error; @@ -174,7 +174,7 @@ const LoginView: React.FC<{ }} > }> - {translateError(error)} + Impossible de se connecter, vérifie tes identifiants ou utilise le portail de ton ENT pour te connecter. )} From a1f3aba5f0fd327510179bdc034f54cb8f521534 Mon Sep 17 00:00:00 2001 From: Bulgus <88266443+Bulgus@users.noreply.github.com> Date: Tue, 21 Jan 2025 17:46:14 +0100 Subject: [PATCH 0298/1144] Forgot to delete the function --- src/components/Templates/LoginView.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/components/Templates/LoginView.tsx b/src/components/Templates/LoginView.tsx index da05670fe..09b22b7e1 100644 --- a/src/components/Templates/LoginView.tsx +++ b/src/components/Templates/LoginView.tsx @@ -90,15 +90,6 @@ const LoginView: React.FC<{ onLogin(username, password, customFieldsDict); }; - const translateError = (error: string | null): string | null => { - if (!error) return null; - if (error.includes("challenge")) { - return "Impossible de se connecter, utilises la connexion par l'ENT"; - } - // Add other error translations here - return error; - }; - return ( Date: Wed, 22 Jan 2025 17:10:21 +0400 Subject: [PATCH 0299/1144] Add title + indent for eslint --- .../Home/Elements/AttendanceElement.tsx | 43 +++++++++++-------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/src/views/account/Home/Elements/AttendanceElement.tsx b/src/views/account/Home/Elements/AttendanceElement.tsx index 1e1834264..f9c86e928 100644 --- a/src/views/account/Home/Elements/AttendanceElement.tsx +++ b/src/views/account/Home/Elements/AttendanceElement.tsx @@ -92,24 +92,31 @@ const AttendanceElement: React.FC = ({ onImportance }) = if (!totalMissed || totalMissed.absences.length === 0) { return ( - - - - - + <> + + )} + /> + + + + + + ); } From 8b20e5a686bff45a57c2acd721a9e419fe1a5c71 Mon Sep 17 00:00:00 2001 From: CodeurIII Date: Wed, 22 Jan 2025 20:39:04 +0400 Subject: [PATCH 0300/1144] first commit --- src/views/account/Restaurant/Menu.tsx | 58 ++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index ef822a4aa..309e094a0 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -36,7 +36,7 @@ import { Balance } from "@/services/shared/Balance"; import { balanceFromExternal } from "@/services/balance"; import MissingItem from "@/components/Global/MissingItem"; import { animPapillon } from "@/utils/ui/animations"; -import Reanimated, { FadeIn, FadeInDown, FadeInUp, FadeOut, FadeOutDown, LinearTransition } from "react-native-reanimated"; +import Reanimated, { FadeIn, FadeInDown, FadeInUp, FadeOut, FadeOutDown, LinearTransition, ZoomIn, ZoomOut } from "react-native-reanimated"; import { reservationHistoryFromExternal } from "@/services/reservation-history"; import { qrcodeFromExternal } from "@/services/qrcode"; import { ReservationHistory } from "@/services/shared/ReservationHistory"; @@ -50,6 +50,9 @@ import { bookDayFromExternal, getBookingsAvailableFromExternal } from "@/service import AccountButton from "@/components/Restaurant/AccountButton"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; import PapillonHeader from "@/components/Global/PapillonHeader"; +import { PressableScale } from "react-native-pressable-scale"; +import { BlurView } from "expo-blur"; +import { ChevronLeft, ChevronRight} from "lucide-react-native"; const Menu: Screen<"Menu"> = ({ route, navigation }) => { const theme = useTheme(); @@ -367,8 +370,34 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { /> - {(currentMenu || (allBookings && allBookings.some((terminal) => terminal.days.some((day) => day.date?.toDateString() === pickerDate.toDateString())))) && + {(true || currentMenu || (allBookings && allBookings.some((terminal) => terminal.days.some((day) => day.date?.toDateString() === pickerDate.toDateString())))) && + + onDatePickerSelect(new Date(pickerDate.setDate(pickerDate.getDate() - 1)))} + activeScale={0.8} + > + + + + + setShowDatePicker(true)}> = ({ route, navigation }) => { {pickerDate.toLocaleDateString("fr-FR", { month: "long" })} + + onDatePickerSelect(new Date(pickerDate.setDate(pickerDate.getDate() + 1)))} + activeScale={0.8} + > + + + + + } From 982a5687592423c1b382b09e3ba40a09fd6fbc1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Wed, 22 Jan 2025 22:37:56 +0100 Subject: [PATCH 0301/1144] =?UTF-8?q?feat:=20D=C3=A9sormais,=20l'utilisate?= =?UTF-8?q?ur=20est=20notifi=C3=A9=20pour=20les=20nouveaux=20devoirs=20de?= =?UTF-8?q?=20la=20semaine=20prochaine=20!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Homeworks.ts | 76 +++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 21 deletions(-) diff --git a/src/background/data/Homeworks.ts b/src/background/data/Homeworks.ts index d238d1954..8a8432668 100644 --- a/src/background/data/Homeworks.ts +++ b/src/background/data/Homeworks.ts @@ -33,35 +33,64 @@ const fetchHomeworks = async (): Promise => { const firstDateEpoch = dateToEpochWeekNumber(firstDate); const SemaineAct = dateToEpochWeekNumber(new Date()); - const currentHomeworks = getHomeworks()[SemaineAct]; + const currentHwSemaineActuelle = getHomeworks()[SemaineAct]; + const currentHwSemaineProchaine = getHomeworks()[SemaineAct + 1]; await updateHomeworksState(account); - const updatedHomeworks = getHomeworks()[SemaineAct]; + const updatedHwSemaineActuelle = getHomeworks()[SemaineAct]; + const updatedHwSemaineProchaine = getHomeworks()[SemaineAct + 1]; - const differences = getDifferences(currentHomeworks, updatedHomeworks); + const differencesHwSemaineActuelle = getDifferences( + currentHwSemaineActuelle, + updatedHwSemaineProchaine + ); + const differencesHwSemaineProchaine = getDifferences( + currentHwSemaineProchaine, + updatedHwSemaineProchaine + ); + const differences = + differencesHwSemaineActuelle.length + differencesHwSemaineProchaine.length; if ( notificationsTypesPermissions?.enabled && notificationsTypesPermissions?.homeworks ) { - switch (differences.length) { + switch (differences) { case 0: break; case 1: - papillonNotify( - { - id: `${account.name}-homeworks`, - title: `[${account.name}] Nouveau devoir`, - subtitle: `Semaine ${( - ((SemaineAct - (firstDateEpoch % 52)) % 52) + - 1 - ).toString()}`, - body: `Un nouveau devoir en ${differences[0].subject} a été publié`, - ios: { - categoryId: account.name, + if (differencesHwSemaineActuelle.length === 1) { + papillonNotify( + { + id: `${account.name}-homeworks`, + title: `[${account.name}] Nouveau devoir`, + subtitle: `Semaine ${( + ((SemaineAct - (firstDateEpoch % 52)) % 52) + + 1 + ).toString()}`, + body: `Un nouveau devoir en ${differencesHwSemaineActuelle[0].subject} a été publié`, + ios: { + categoryId: account.name, + }, }, - }, - "Homeworks" - ); + "Homeworks" + ); + } else { + papillonNotify( + { + id: `${account.name}-homeworks`, + title: `[${account.name}] Nouveau devoir`, + subtitle: `Semaine ${( + ((SemaineAct - (firstDateEpoch % 52)) % 52) + + 2 + ).toString()}`, + body: `Un nouveau devoir en ${differencesHwSemaineProchaine[0].subject} a été publié`, + ios: { + categoryId: account.name, + }, + }, + "Homeworks" + ); + } break; default: papillonNotify( @@ -73,8 +102,13 @@ const fetchHomeworks = async (): Promise => { 1 ).toString()}`, body: ` - ${differences.length} nouveaux devoirs ont été publiés :
- ${differences + ${differences} nouveaux devoirs ont été publiés :
+ ${differencesHwSemaineActuelle + .flatMap((element) => { + return `- ${element.subject}`; + }) + .join("
")} + ${differencesHwSemaineProchaine .flatMap((element) => { return `- ${element.subject}`; }) @@ -90,7 +124,7 @@ const fetchHomeworks = async (): Promise => { } } - return updatedHomeworks; + return updatedHwSemaineActuelle ?? updatedHwSemaineProchaine; }; export { fetchHomeworks }; From 80b15ffb331ad76c2eb226f28a06879457533480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Wed, 22 Jan 2025 22:55:45 +0100 Subject: [PATCH 0302/1144] =?UTF-8?q?refactor:=20Les=20t=C3=A2ches=20sont?= =?UTF-8?q?=20ex=C3=A9cut=C3=A9es=20un=20par=20un=20pour=20=C3=A9viter=20q?= =?UTF-8?q?u'Android=20bloque=20+=20Re-register=20background=20task=20quan?= =?UTF-8?q?d=20Background=20task=20already=20registered?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/BackgroundTasks.ts | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 1943dc97a..bd9649485 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -5,7 +5,7 @@ import { BackgroundFetchResult } from "expo-background-fetch"; import { expoGoWrapper } from "@/utils/native/expoGoAlert"; import { fetchNews } from "./data/News"; -import { log, error, warn } from "@/utils/logger/logger"; +import { log, error, warn, info } from "@/utils/logger/logger"; import { getAccounts, getSwitchToFunction } from "./utils/accounts"; import { fetchHomeworks } from "./data/Homeworks"; import { fetchGrade } from "./data/Grades"; @@ -44,24 +44,23 @@ notifee.onBackgroundEvent(async ({ type, detail }) => { * @warning This task should not last more than 30 seconds * @returns BackgroundFetchResult.NewData */ -const backgroundFetch = async () => { +const backgroundFetch = () => { log("Running background fetch", "BackgroundEvent"); try { const accounts = getAccounts(); const switchTo = getSwitchToFunction(); - for (const account of accounts) { + accounts.forEach(async (account) => { await switchTo(account); - await Promise.all([ - fetchNews(), - fetchHomeworks(), - fetchGrade(), - fetchLessons(), - fetchAttendance(), - fetchEvaluation(), - ]); - } + + await fetchNews(); + await fetchHomeworks(); + await fetchGrade(); + await fetchLessons(); + await fetchAttendance(); + await fetchEvaluation(); + }); log("✅ Finish background fetch", "BackgroundEvent"); return BackgroundFetchResult.NewData; @@ -75,7 +74,7 @@ TaskManager.defineTask("background-fetch", backgroundFetch); const registerBackgroundTasks = async () => { await TaskManager.isTaskRegisteredAsync("background-fetch").then( - (isRegistered) => { + async (isRegistered) => { if (!isRegistered) { expoGoWrapper(async () => { await BackgroundFetch.registerTaskAsync("background-fetch", { @@ -87,7 +86,10 @@ const registerBackgroundTasks = async () => { log("✅ Background task registered", "BackgroundEvent"); }); } else { - warn("⚠️ Background task already registered", "BackgroundEvent"); + warn("⚠️ Background task already registered, unregister task...", "BackgroundEvent"); + await BackgroundFetch.unregisterTaskAsync("background-fetch"); + info("🔁 Re-register background task...", "BackgroundEvent"); + registerBackgroundTasks(); } } ); From ed86f0ed56c26b311a1fe56d2c663c734ff5f323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Wed, 22 Jan 2025 23:09:38 +0100 Subject: [PATCH 0303/1144] fix: add `pressAction` to fix no action when click in notification --- src/background/Notifications.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index 9dc4cf3c3..8cca8033b 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -101,6 +101,10 @@ const papillonNotify = async ( showTimestamp: true, smallIcon: "@mipmap/ic_launcher_foreground", color: "#32AB8E", + pressAction: { + id: "default", + launchActivity: "MainActivity", + } // à intégrer => `actions` }, }); From c99ab28fc43e8e99920e949238448196525ba737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Wed, 22 Jan 2025 23:55:26 +0100 Subject: [PATCH 0304/1144] =?UTF-8?q?fix:=20g=C3=A9rer=20les=20cas=20o?= =?UTF-8?q?=C3=B9=20il=20n'y=20a=20pas=20de=20devoirs=20pour=20la=20semain?= =?UTF-8?q?e=20actuelle=20et=20prochaine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Homeworks.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/background/data/Homeworks.ts b/src/background/data/Homeworks.ts index 8a8432668..80982d484 100644 --- a/src/background/data/Homeworks.ts +++ b/src/background/data/Homeworks.ts @@ -33,11 +33,11 @@ const fetchHomeworks = async (): Promise => { const firstDateEpoch = dateToEpochWeekNumber(firstDate); const SemaineAct = dateToEpochWeekNumber(new Date()); - const currentHwSemaineActuelle = getHomeworks()[SemaineAct]; - const currentHwSemaineProchaine = getHomeworks()[SemaineAct + 1]; + const currentHwSemaineActuelle = getHomeworks()[SemaineAct] ?? []; + const currentHwSemaineProchaine = getHomeworks()[SemaineAct + 1] ?? []; await updateHomeworksState(account); - const updatedHwSemaineActuelle = getHomeworks()[SemaineAct]; - const updatedHwSemaineProchaine = getHomeworks()[SemaineAct + 1]; + const updatedHwSemaineActuelle = getHomeworks()[SemaineAct] ?? []; + const updatedHwSemaineProchaine = getHomeworks()[SemaineAct + 1] ?? []; const differencesHwSemaineActuelle = getDifferences( currentHwSemaineActuelle, From 462a90a308d55fa4ef3aba763b62e14a919ff3cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Thu, 23 Jan 2025 00:09:13 +0100 Subject: [PATCH 0305/1144] =?UTF-8?q?fix:=20=C3=A9viter=20les=20ex=C3=A9cu?= =?UTF-8?q?tions=20simultan=C3=A9es=20de=20la=20t=C3=A2che=20de=20r=C3=A9c?= =?UTF-8?q?up=C3=A9ration=20en=20arri=C3=A8re-plan=20+=20r=C3=A9cup=C3=A9r?= =?UTF-8?q?ation=20des=20nouvelles=20informations=201=20par=201=20+=20quel?= =?UTF-8?q?ques=20optimisations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/BackgroundTasks.ts | 65 ++++++++++++++++--------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index bd9649485..2c86775d2 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -5,7 +5,7 @@ import { BackgroundFetchResult } from "expo-background-fetch"; import { expoGoWrapper } from "@/utils/native/expoGoAlert"; import { fetchNews } from "./data/News"; -import { log, error, warn, info } from "@/utils/logger/logger"; +import { log, error } from "@/utils/logger/logger"; import { getAccounts, getSwitchToFunction } from "./utils/accounts"; import { fetchHomeworks } from "./data/Homeworks"; import { fetchGrade } from "./data/Grades"; @@ -39,19 +39,22 @@ notifee.onBackgroundEvent(async ({ type, detail }) => { } }); -/** - * Background fetch function that fetches all the data - * @warning This task should not last more than 30 seconds - * @returns BackgroundFetchResult.NewData - */ -const backgroundFetch = () => { +let isBackgroundFetchRunning = false; + +const backgroundFetch = async () => { + if (isBackgroundFetchRunning) { + log("⚠️ Background fetch already running. Skipping...", "BackgroundEvent"); + return BackgroundFetchResult.NoData; + } + + isBackgroundFetchRunning = true; log("Running background fetch", "BackgroundEvent"); try { const accounts = getAccounts(); const switchTo = getSwitchToFunction(); - accounts.forEach(async (account) => { + for (const account of accounts) { await switchTo(account); await fetchNews(); @@ -60,44 +63,42 @@ const backgroundFetch = () => { await fetchLessons(); await fetchAttendance(); await fetchEvaluation(); - }); + } log("✅ Finish background fetch", "BackgroundEvent"); return BackgroundFetchResult.NewData; } catch (ERRfatal) { error(`❌ Task failed: ${ERRfatal}`, "BackgroundEvent"); return BackgroundFetchResult.Failed; + } finally { + isBackgroundFetchRunning = false; } }; TaskManager.defineTask("background-fetch", backgroundFetch); -const registerBackgroundTasks = async () => { - await TaskManager.isTaskRegisteredAsync("background-fetch").then( - async (isRegistered) => { - if (!isRegistered) { - expoGoWrapper(async () => { - await BackgroundFetch.registerTaskAsync("background-fetch", { - minimumInterval: 60 * 15, // 15 minutes - stopOnTerminate: false, // Maintenir après fermeture (Android) - startOnBoot: true, // Redémarrer au démarrage (Android) - }); - - log("✅ Background task registered", "BackgroundEvent"); - }); - } else { - warn("⚠️ Background task already registered, unregister task...", "BackgroundEvent"); - await BackgroundFetch.unregisterTaskAsync("background-fetch"); - info("🔁 Re-register background task...", "BackgroundEvent"); - registerBackgroundTasks(); - } - } - ); -}; - const unsetBackgroundFetch = async () => { await BackgroundFetch.unregisterTaskAsync("background-fetch"); log("✅ Background task unregistered", "BackgroundEvent"); }; +const registerBackgroundTasks = async () => { + const isRegistered = await TaskManager.isTaskRegisteredAsync("background-fetch"); + + if (isRegistered) { + log("⚠️ Background task already registered. Unregister background task.", "BackgroundEvent"); + await unsetBackgroundFetch(); + } + + expoGoWrapper(async () => { + await BackgroundFetch.registerTaskAsync("background-fetch", { + minimumInterval: 60 * 15, + stopOnTerminate: false, + startOnBoot: true, + }); + + log("✅ Background task registered", "BackgroundEvent"); + }); +}; + export { registerBackgroundTasks, unsetBackgroundFetch }; From e2f482c2d02b86465d178e18bf3c1772daba0272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Thu, 23 Jan 2025 07:39:53 +0100 Subject: [PATCH 0306/1144] fix: `launchActivity` not work --- src/background/Notifications.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index 8cca8033b..d4bcd76c8 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -103,7 +103,7 @@ const papillonNotify = async ( color: "#32AB8E", pressAction: { id: "default", - launchActivity: "MainActivity", + launchActivity: "xyz.getpapillon.app.MainActivity", } // à intégrer => `actions` }, From f472b6ac585ab90dae43b615d51f73344609c0a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Thu, 23 Jan 2025 07:45:42 +0100 Subject: [PATCH 0307/1144] =?UTF-8?q?fix:=20erreur=20d=C3=A9bile...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Homeworks.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/background/data/Homeworks.ts b/src/background/data/Homeworks.ts index 80982d484..a43c479f2 100644 --- a/src/background/data/Homeworks.ts +++ b/src/background/data/Homeworks.ts @@ -35,13 +35,15 @@ const fetchHomeworks = async (): Promise => { const SemaineAct = dateToEpochWeekNumber(new Date()); const currentHwSemaineActuelle = getHomeworks()[SemaineAct] ?? []; const currentHwSemaineProchaine = getHomeworks()[SemaineAct + 1] ?? []; + await updateHomeworksState(account); + const updatedHwSemaineActuelle = getHomeworks()[SemaineAct] ?? []; const updatedHwSemaineProchaine = getHomeworks()[SemaineAct + 1] ?? []; const differencesHwSemaineActuelle = getDifferences( currentHwSemaineActuelle, - updatedHwSemaineProchaine + updatedHwSemaineActuelle ); const differencesHwSemaineProchaine = getDifferences( currentHwSemaineProchaine, From f679dd8da8023610dbd77a942f05e30de3166998 Mon Sep 17 00:00:00 2001 From: CodeurIII Date: Thu, 23 Jan 2025 16:37:45 +0400 Subject: [PATCH 0308/1144] finish, waiting for review --- src/views/account/Restaurant/Menu.tsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 309e094a0..54632e3e8 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -370,7 +370,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { /> - {(true || currentMenu || (allBookings && allBookings.some((terminal) => terminal.days.some((day) => day.date?.toDateString() === pickerDate.toDateString())))) && + {(currentMenu || (allBookings && allBookings?.some((terminal) => terminal.days.some((day) => day.date?.toDateString() === pickerDate.toDateString())))) && = ({ route, navigation }) => { exiting={animPapillon(ZoomOut)} > onDatePickerSelect(new Date(pickerDate.setDate(pickerDate.getDate() - 1)))} + onPress={() => { + onDatePickerSelect(new Date(pickerDate.setDate(pickerDate.getDate() - 1))); + setRefreshCount(refreshCount + 1); + }} activeScale={0.8} > = ({ route, navigation }) => { exiting={animPapillon(ZoomOut)} > onDatePickerSelect(new Date(pickerDate.setDate(pickerDate.getDate() + 1)))} + onPress={() => { + onDatePickerSelect(new Date(pickerDate.setDate(pickerDate.getDate() + 1))); + setRefreshCount(refreshCount + 1); + }} activeScale={0.8} > Date: Thu, 23 Jan 2025 18:47:19 +0100 Subject: [PATCH 0309/1144] update version in `package-lock.json` --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index ef8f26b28..959245218 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.7.2", + "version": "7.8.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.7.2", + "version": "7.8.0", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.3", "@candlefinance/app-icon": "^0.4.5", From 815f7499071d4af1e4a4a90acf770c55f34e18f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Thu, 23 Jan 2025 18:47:36 +0100 Subject: [PATCH 0310/1144] =?UTF-8?q?fix:=20corriger=20l'ordre=20de=20tri?= =?UTF-8?q?=20des=20journaux=20de=20d=C3=A9veloppement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/SettingsDevLogs.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/settings/SettingsDevLogs.tsx b/src/views/settings/SettingsDevLogs.tsx index d9122843a..48bcbbe98 100644 --- a/src/views/settings/SettingsDevLogs.tsx +++ b/src/views/settings/SettingsDevLogs.tsx @@ -49,7 +49,7 @@ const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { get_logs().then((logs) => { setLogs( logs.sort( - (a, b) => new Date(b.date).getTime() - new Date(a.date).getTime() + (a, b) => new Date(a.date).getTime() - new Date(b.date).getTime() ) ); setLoading(false); From 424d4554404cf6e58c0ca1dbec6a44a928d24bd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Thu, 23 Jan 2025 19:36:18 +0100 Subject: [PATCH 0311/1144] =?UTF-8?q?feat:=20formatage=20de=20la=20date=20?= =?UTF-8?q?dans=20les=20journaux=20de=20d=C3=A9veloppement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/SettingsDevLogs.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/views/settings/SettingsDevLogs.tsx b/src/views/settings/SettingsDevLogs.tsx index 48bcbbe98..fe063c56c 100644 --- a/src/views/settings/SettingsDevLogs.tsx +++ b/src/views/settings/SettingsDevLogs.tsx @@ -35,6 +35,7 @@ import { animPapillon } from "@/utils/ui/animations"; import { useTheme } from "@react-navigation/native"; import { useAlert } from "@/providers/AlertProvider"; import MissingItem from "@/components/Global/MissingItem"; +import formatDate from "@/utils/format/format_date_complets"; const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { const { colors } = useTheme(); @@ -220,7 +221,11 @@ const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { } > {log.message} - {log.date} + + {formatDate(log.date)} à {new Date(log.date).getHours()}: + {new Date(log.date).getMinutes()}: + {new Date(log.date).getSeconds()} + {log.from} ); From ed0e28f787029c03b3ca406c147d0aab40cae8e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Thu, 23 Jan 2025 19:37:18 +0100 Subject: [PATCH 0312/1144] fix: supprimer `console.log` dans SettingsAbout --- src/views/settings/SettingsAbout.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/views/settings/SettingsAbout.tsx b/src/views/settings/SettingsAbout.tsx index e1ba896c2..702266a44 100644 --- a/src/views/settings/SettingsAbout.tsx +++ b/src/views/settings/SettingsAbout.tsx @@ -25,7 +25,6 @@ const SettingsAbout: Screen<"SettingsAbout"> = ({ navigation }) => { const fetchContributors = async () => { const fetchedContributors = await getContributors(); setContributors(fetchedContributors); - console.log(fetchedContributors[0]); }; useEffect(() => { From 3d9be4b9f47209cd26ac2b80579667028fe3269d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Thu, 23 Jan 2025 21:37:06 +0100 Subject: [PATCH 0313/1144] fix: function `expoGoWrapper` not working on iOS --- src/background/BackgroundTasks.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 2c86775d2..c5f4fd64d 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -2,7 +2,7 @@ import notifee, { EventType } from "@notifee/react-native"; import * as BackgroundFetch from "expo-background-fetch"; import * as TaskManager from "expo-task-manager"; import { BackgroundFetchResult } from "expo-background-fetch"; -import { expoGoWrapper } from "@/utils/native/expoGoAlert"; +import Constants from "expo-constants"; import { fetchNews } from "./data/News"; import { log, error } from "@/utils/logger/logger"; @@ -90,7 +90,7 @@ const registerBackgroundTasks = async () => { await unsetBackgroundFetch(); } - expoGoWrapper(async () => { + if (!Constants.appOwnership) { await BackgroundFetch.registerTaskAsync("background-fetch", { minimumInterval: 60 * 15, stopOnTerminate: false, @@ -98,7 +98,7 @@ const registerBackgroundTasks = async () => { }); log("✅ Background task registered", "BackgroundEvent"); - }); + } }; export { registerBackgroundTasks, unsetBackgroundFetch }; From 4cc61ac4354fecf127cdb45e333f502234abad8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Thu, 23 Jan 2025 21:38:46 +0100 Subject: [PATCH 0314/1144] using function --- src/background/BackgroundTasks.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index c5f4fd64d..8bae2ef90 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -2,7 +2,7 @@ import notifee, { EventType } from "@notifee/react-native"; import * as BackgroundFetch from "expo-background-fetch"; import * as TaskManager from "expo-task-manager"; import { BackgroundFetchResult } from "expo-background-fetch"; -import Constants from "expo-constants"; +import { isExpoGo } from "@/utils/native/expoGoAlert"; import { fetchNews } from "./data/News"; import { log, error } from "@/utils/logger/logger"; @@ -90,7 +90,7 @@ const registerBackgroundTasks = async () => { await unsetBackgroundFetch(); } - if (!Constants.appOwnership) { + if (!isExpoGo()) { await BackgroundFetch.registerTaskAsync("background-fetch", { minimumInterval: 60 * 15, stopOnTerminate: false, From a9555127a3001c3f80dfd5846046eaf252fce381 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Thu, 23 Jan 2025 21:48:50 +0100 Subject: [PATCH 0315/1144] =?UTF-8?q?fix:=20am=C3=A9liorer=20la=20gestion?= =?UTF-8?q?=20des=20logs=20en=20ajoutant=20une=20distinction=20pour=20les?= =?UTF-8?q?=20=C3=A9v=C3=A9nements=20en=20arri=C3=A8re-plan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/logger/logger.ts | 6 +++- src/views/settings/SettingsDevLogs.tsx | 41 +++++++++++++++----------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/utils/logger/logger.ts b/src/utils/logger/logger.ts index 48f9ec307..03cf5716e 100644 --- a/src/utils/logger/logger.ts +++ b/src/utils/logger/logger.ts @@ -33,7 +33,11 @@ function get_file_from_stacktrace (stack: string): string .split(/\/\/localhost:\d\d\d\d\//g)[1] .split("//&")[0]; } catch (e) { - res = "UNKOWN"; + if (stack === "BackgroundEvent") { + res = "BACKGROUND"; + } else { + res = "UNKOWN"; + } } return (res); } diff --git a/src/views/settings/SettingsDevLogs.tsx b/src/views/settings/SettingsDevLogs.tsx index d9122843a..152572ed0 100644 --- a/src/views/settings/SettingsDevLogs.tsx +++ b/src/views/settings/SettingsDevLogs.tsx @@ -25,6 +25,7 @@ import { Calendar, Folder, X, + SunMoon, } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { @@ -174,7 +175,9 @@ const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { leading={ + ) : log.type === "ERROR" ? ( ) : log.type === "WARN" ? ( @@ -195,23 +198,25 @@ const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { ) } color={ - log.type === "ERROR" - ? "#BE0B00" - : log.type === "WARN" - ? "#CF6B0F" - : log.type === "INFO" - ? "#0E7CCB" - : log.message.startsWith("User navigate into /") - ? "#28B463" - : log.message === "App in background" - ? "#1F618D" - : log.message.toLowerCase().includes("read") - ? "#D4AC02" - : log.message === "[timetable:updateClasses" - ? "#884EA0" - : log.message.toLowerCase().includes("folder") - ? "#CA6F1E" - : "#AAA" + log.from === "BACKGROUND" + ? "#34495E" + : log.type === "ERROR" + ? "#BE0B00" + : log.type === "WARN" + ? "#CF6B0F" + : log.type === "INFO" + ? "#0E7CCB" + : log.message.startsWith("User navigate into /") + ? "#28B463" + : log.message === "App in background" + ? "#1F618D" + : log.message.toLowerCase().includes("read") + ? "#D4AC02" + : log.message === "[timetable:updateClasses" + ? "#884EA0" + : log.message.toLowerCase().includes("folder") + ? "#CA6F1E" + : "#AAA" } style={{ marginLeft: -6, From 0c7e2200f3c421387deab5cefc97bf0bb83e9372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Thu, 23 Jan 2025 21:53:52 +0100 Subject: [PATCH 0316/1144] fix: Remplace `expoGoWrapper` with `isExpoGo` in `BackgroundTasks` --- src/background/BackgroundTasks.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index aaf05ef3e..bfac560d8 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -1,7 +1,7 @@ import * as BackgroundFetch from "expo-background-fetch"; import * as TaskManager from "expo-task-manager"; import { BackgroundFetchResult } from "expo-background-fetch"; -import { expoGoWrapper } from "@/utils/native/expoGoAlert"; +import { isExpoGo } from "@/utils/native/expoGoAlert"; import { useAccounts, useCurrentAccount } from "@/stores/account"; import { fetchNews } from "./data/News"; @@ -26,7 +26,7 @@ const backgroundFetch = async () => { }; const registerBackgroundTasks = async () => { - expoGoWrapper(async () => { + if (!isExpoGo()) { TaskManager.defineTask("background-fetch", () => backgroundFetch()); BackgroundFetch?.registerTaskAsync("background-fetch", { @@ -38,7 +38,7 @@ const registerBackgroundTasks = async () => { backgroundFetch(); console.log("[background fetch] Registered background fetch"); - }); + }; }; const unsetBackgroundFetch = async () => { From 68d28a771e82c16db47078d812ddd73ef85b98a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Thu, 23 Jan 2025 21:57:51 +0100 Subject: [PATCH 0317/1144] fix: Remplace `expoGoWrapper` with `isExpoGo` in `Notifications` --- src/background/Notifications.ts | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index 61921d120..5018f0e64 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -1,21 +1,26 @@ -import { expoGoWrapper } from "@/utils/native/expoGoAlert"; -import {Notification} from "@notifee/react-native"; +import { alertExpoGo, isExpoGo } from "@/utils/native/expoGoAlert"; +import { Notification } from "@notifee/react-native"; -const requestNotificationPermission = async () => { - return expoGoWrapper(async () => { - const notifee = (await import("@notifee/react-native")).default; - await notifee.requestPermission(); - }, true); +const requestNotificationPermission = () => { + return async () => { + if (!isExpoGo()) { + const notifee = (await import("@notifee/react-native")).default; + await notifee.requestPermission(); + } else { + alertExpoGo(); + return false; + } + }; }; const papillonNotify = async (props: Notification) => { - expoGoWrapper(async () => { + if (!isExpoGo()) { const notifee = (await import("@notifee/react-native")).default; await notifee.displayNotification({ ...props, title: props.title || "Coucou, c'est Papillon 👋", }); - }); + } }; export { From 2b11c45a924a394a5000e7375c7d14b7665bfb6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Thu, 23 Jan 2025 22:02:25 +0100 Subject: [PATCH 0318/1144] fix: Remplace `expoGoWrapper` with `isExpoGo` in `SettingsIcons` --- src/views/settings/SettingsIcons.tsx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/views/settings/SettingsIcons.tsx b/src/views/settings/SettingsIcons.tsx index 9591e7e07..e52000994 100644 --- a/src/views/settings/SettingsIcons.tsx +++ b/src/views/settings/SettingsIcons.tsx @@ -12,7 +12,7 @@ import colorsList from "@/utils/data/colors.json"; import { getIconName, setIconName } from "@candlefinance/app-icon"; import PapillonCheckbox from "@/components/Global/PapillonCheckbox"; -import { expoGoWrapper } from "@/utils/native/expoGoAlert"; +import { alertExpoGo, isExpoGo } from "@/utils/native/expoGoAlert"; import { useAlert } from "@/providers/AlertProvider"; type Icon = { @@ -47,11 +47,11 @@ const SettingsIcons: Screen<"SettingsIcons"> = ({ navigation }) => { const [currentIcon, setIcon] = React.useState("default"); useEffect(() => { - expoGoWrapper(() => { + if (!isExpoGo()) { getIconName().then((icon) => { setIcon(icon); }); - }); + }; }, []); const setNewIcon = (icon: Icon) => { @@ -61,16 +61,20 @@ const SettingsIcons: Screen<"SettingsIcons"> = ({ navigation }) => { const iconConstructName = icon.id + (colorItem ? "_" + colorItem.id : ""); - expoGoWrapper(() => { + if (!isExpoGo()) { setIconName(iconConstructName); setIcon(iconConstructName); - }, true); + } else { + alertExpoGo(); + }; } else { - expoGoWrapper(() => { + if (!isExpoGo()) { setIconName(icon.id); setIcon(icon.id); - }, true); + } else { + alertExpoGo(); + }; } }; From 177f5500c0f26b8c9fd0031e558cae84c12e1eef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Thu, 23 Jan 2025 22:03:28 +0100 Subject: [PATCH 0319/1144] fix: Remplace `expoGoWrapper` with `isExpoGo` in `ColorSelector` --- src/views/welcome/ColorSelector.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views/welcome/ColorSelector.tsx b/src/views/welcome/ColorSelector.tsx index a8d7a50e1..30f01bdf3 100644 --- a/src/views/welcome/ColorSelector.tsx +++ b/src/views/welcome/ColorSelector.tsx @@ -15,7 +15,7 @@ import { getIconName, setIconName } from "@candlefinance/app-icon"; import colorsList from "@/utils/data/colors.json"; import { removeColor } from "../settings/SettingsIcons"; -import { expoGoWrapper } from "@/utils/native/expoGoAlert"; +import { isExpoGo } from "@/utils/native/expoGoAlert"; type Color = typeof colorsList[number]; @@ -84,7 +84,7 @@ const ColorSelector: Screen<"ColorSelector"> = ({ route, navigation }) => { Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); playSound2(); - expoGoWrapper(() => { + if (!isExpoGo()) { getIconName().then((currentIcon) => { if (currentIcon.includes("_Dynamic_")) { const mainColor = color.hex.primary; @@ -96,7 +96,7 @@ const ColorSelector: Screen<"ColorSelector"> = ({ route, navigation }) => { setIconName(iconConstructName); } }); - }); + }; }; const ColorButton: React.FC<{ color: Color }> = ({ color }) => ( From 9f89ea0b2fbb18175b837dc7445c882b8e6b1287 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Thu, 23 Jan 2025 22:04:01 +0100 Subject: [PATCH 0320/1144] fix: Delete function `expoGoWrapper` --- src/utils/native/expoGoAlert.ts | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/utils/native/expoGoAlert.ts b/src/utils/native/expoGoAlert.ts index 09967880e..fcab0a6d0 100644 --- a/src/utils/native/expoGoAlert.ts +++ b/src/utils/native/expoGoAlert.ts @@ -11,18 +11,3 @@ export const alertExpoGo = async () => { "Sous Expo Go, les appels aux API natives sont indisponibles. Utilise un build de développement pour accéder à toutes les fonctionnalités.", ); }; -/** - * Function wrapper that only calls the function if the app is not Expo Go - * @param fn Founciton to call if not Expo Go - * @param alert Show an alert if the app is Expo Go - * @returns Execute the function if not Expo Go - */ -export const expoGoWrapper = (fn: () => void, alert?: boolean) => { - if (!isExpoGo()) { - return fn(); - } - else if (alert) { - alertExpoGo(); - return false; - } -}; \ No newline at end of file From ceb7da692eafcb11f1d5de3bd1304e03f63cc260 Mon Sep 17 00:00:00 2001 From: ggkervran <73659505+Gabriel29306@users.noreply.github.com> Date: Fri, 24 Jan 2025 11:45:06 +0100 Subject: [PATCH 0321/1144] fix(DevMenu): add status to grades for GradeReaction screen --- src/views/welcome/DevMenu.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/views/welcome/DevMenu.tsx b/src/views/welcome/DevMenu.tsx index 0b36a131f..78813ac72 100644 --- a/src/views/welcome/DevMenu.tsx +++ b/src/views/welcome/DevMenu.tsx @@ -107,12 +107,12 @@ const DevMenu: Screen<"DevMenu"> = ({ navigation }) => { subjectName: "Développement", description: "Typage avec Vince", timestamp: new Date().getTime(), - outOf: { value: 7 }, + outOf: { value: 7, status: null }, coefficient: 7, - student: { value: 7 }, - average: { value: 7 }, - max: { value: 7 }, - min: { value: 1 } + student: { value: 7, status: null }, + average: { value: 7, status: null }, + max: { value: 7, status: null }, + min: { value: 1, status: null } } })} > From 2cb4c05dd163410fcf79116e203f6d5964eafcf4 Mon Sep 17 00:00:00 2001 From: ggkervran <73659505+Gabriel29306@users.noreply.github.com> Date: Fri, 24 Jan 2025 11:50:35 +0100 Subject: [PATCH 0322/1144] fix(Subject): PickerDataItem => PickerData --- src/components/Global/PapillonPicker.tsx | 2 +- src/views/account/Grades/Subject/Subject.tsx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Global/PapillonPicker.tsx b/src/components/Global/PapillonPicker.tsx index 93777c422..e6b1f5c1e 100644 --- a/src/components/Global/PapillonPicker.tsx +++ b/src/components/Global/PapillonPicker.tsx @@ -10,7 +10,7 @@ import { NativeText } from "./NativeComponents"; import { BlurView } from "expo-blur"; import { Check } from "lucide-react-native"; -type PickerData = string[] | { label: string, icon?: JSX.Element, onPress: () => unknown, checked?: boolean }[]; +export type PickerData = string[] | { label: string, icon?: JSX.Element, onPress?: () => unknown, checked?: boolean }[]; interface PapillonPickerProps { children: React.ReactNode diff --git a/src/views/account/Grades/Subject/Subject.tsx b/src/views/account/Grades/Subject/Subject.tsx index a7b97dd21..45a989624 100644 --- a/src/views/account/Grades/Subject/Subject.tsx +++ b/src/views/account/Grades/Subject/Subject.tsx @@ -7,7 +7,7 @@ import Reanimated, { FadeInRight, FadeOutLeft, LinearTransition } from "react-na import { FlatList, View } from "react-native"; import SubjectItem from "./SubjectList"; import { useCallback, useMemo, useState } from "react"; -import PapillonPicker, { PickerDataItem } from "@/components/Global/PapillonPicker"; +import PapillonPicker, { PickerData } from "@/components/Global/PapillonPicker"; import { ArrowDownAZ, Calendar, ChevronDown, TrendingUp } from "lucide-react-native"; import { useTheme } from "@react-navigation/native"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; @@ -26,7 +26,7 @@ const sortingFunctions: Record = { 2: (a, b) => (b.average?.average?.value || 0) - (a.average?.average?.value || 0) }; -const sortings: PickerDataItem[] = [ +const sortings: PickerData[] = [ { label: "Alphabétique", icon: , @@ -56,7 +56,7 @@ const Subject: React.FC = ({ return [...gradesPerSubject].sort(sortFn); }, [gradesPerSubject, sorting]); - const handleSortingChange = useCallback((item: PickerDataItem) => { + const handleSortingChange = useCallback((item: PickerData) => { setIsLoading(true); // Use requestAnimationFrame to prevent UI blocking requestAnimationFrame(() => { From df869f67983972cbf92c6dd521fd25e9ee669e5a Mon Sep 17 00:00:00 2001 From: ggkervran <73659505+Gabriel29306@users.noreply.github.com> Date: Fri, 24 Jan 2025 11:52:41 +0100 Subject: [PATCH 0323/1144] fix: PickerData type is already a array --- src/views/account/Grades/Subject/Subject.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Grades/Subject/Subject.tsx b/src/views/account/Grades/Subject/Subject.tsx index 45a989624..bb5395fda 100644 --- a/src/views/account/Grades/Subject/Subject.tsx +++ b/src/views/account/Grades/Subject/Subject.tsx @@ -26,7 +26,7 @@ const sortingFunctions: Record = { 2: (a, b) => (b.average?.average?.value || 0) - (a.average?.average?.value || 0) }; -const sortings: PickerData[] = [ +const sortings: PickerData = [ { label: "Alphabétique", icon: , From c4ddf67d15c742dc1aec68fc797cce77dc4e874c Mon Sep 17 00:00:00 2001 From: ggkervran <73659505+Gabriel29306@users.noreply.github.com> Date: Fri, 24 Jan 2025 11:57:14 +0100 Subject: [PATCH 0324/1144] fix: is it going to work this way ? --- src/components/Global/PapillonPicker.tsx | 4 +++- src/views/account/Grades/Subject/Subject.tsx | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/Global/PapillonPicker.tsx b/src/components/Global/PapillonPicker.tsx index e6b1f5c1e..209a20b11 100644 --- a/src/components/Global/PapillonPicker.tsx +++ b/src/components/Global/PapillonPicker.tsx @@ -10,7 +10,9 @@ import { NativeText } from "./NativeComponents"; import { BlurView } from "expo-blur"; import { Check } from "lucide-react-native"; -export type PickerData = string[] | { label: string, icon?: JSX.Element, onPress?: () => unknown, checked?: boolean }[]; +export type PickerDataItem = { label: string, icon?: JSX.Element, onPress?: () => unknown, checked?: boolean }; + +type PickerData = string[] | PickerDataItem[]; interface PapillonPickerProps { children: React.ReactNode diff --git a/src/views/account/Grades/Subject/Subject.tsx b/src/views/account/Grades/Subject/Subject.tsx index bb5395fda..a7b97dd21 100644 --- a/src/views/account/Grades/Subject/Subject.tsx +++ b/src/views/account/Grades/Subject/Subject.tsx @@ -7,7 +7,7 @@ import Reanimated, { FadeInRight, FadeOutLeft, LinearTransition } from "react-na import { FlatList, View } from "react-native"; import SubjectItem from "./SubjectList"; import { useCallback, useMemo, useState } from "react"; -import PapillonPicker, { PickerData } from "@/components/Global/PapillonPicker"; +import PapillonPicker, { PickerDataItem } from "@/components/Global/PapillonPicker"; import { ArrowDownAZ, Calendar, ChevronDown, TrendingUp } from "lucide-react-native"; import { useTheme } from "@react-navigation/native"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; @@ -26,7 +26,7 @@ const sortingFunctions: Record = { 2: (a, b) => (b.average?.average?.value || 0) - (a.average?.average?.value || 0) }; -const sortings: PickerData = [ +const sortings: PickerDataItem[] = [ { label: "Alphabétique", icon: , @@ -56,7 +56,7 @@ const Subject: React.FC = ({ return [...gradesPerSubject].sort(sortFn); }, [gradesPerSubject, sorting]); - const handleSortingChange = useCallback((item: PickerData) => { + const handleSortingChange = useCallback((item: PickerDataItem) => { setIsLoading(true); // Use requestAnimationFrame to prevent UI blocking requestAnimationFrame(() => { From 8119ac4cbcb049c4d2b4a7883c89c3ac9233fc5c Mon Sep 17 00:00:00 2001 From: ggkervran <73659505+Gabriel29306@users.noreply.github.com> Date: Fri, 24 Jan 2025 12:01:56 +0100 Subject: [PATCH 0325/1144] fix(missing import): MissingItem --- src/views/settings/SettingsDevLogs.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/settings/SettingsDevLogs.tsx b/src/views/settings/SettingsDevLogs.tsx index 07704040b..9fc4ba9c2 100644 --- a/src/views/settings/SettingsDevLogs.tsx +++ b/src/views/settings/SettingsDevLogs.tsx @@ -34,6 +34,7 @@ import { import { animPapillon } from "@/utils/ui/animations"; import { useTheme } from "@react-navigation/native"; import { useAlert } from "@/providers/AlertProvider"; +import MissingItem from "@/components/Global/MissingItem.tsx"; const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { const { colors } = useTheme(); From 8c09208125f67b7409d6f3ba3d971880fbddcc75 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Fri, 24 Jan 2025 15:50:57 +0100 Subject: [PATCH 0326/1144] chore: revert `src/components/Global/PapillonPicker.tsx` to `8426b918` Signed-off-by: Gabriel29306 --- src/components/Global/PapillonPicker.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/Global/PapillonPicker.tsx b/src/components/Global/PapillonPicker.tsx index 209a20b11..93777c422 100644 --- a/src/components/Global/PapillonPicker.tsx +++ b/src/components/Global/PapillonPicker.tsx @@ -10,9 +10,7 @@ import { NativeText } from "./NativeComponents"; import { BlurView } from "expo-blur"; import { Check } from "lucide-react-native"; -export type PickerDataItem = { label: string, icon?: JSX.Element, onPress?: () => unknown, checked?: boolean }; - -type PickerData = string[] | PickerDataItem[]; +type PickerData = string[] | { label: string, icon?: JSX.Element, onPress: () => unknown, checked?: boolean }[]; interface PapillonPickerProps { children: React.ReactNode From 6f757546500f38f130b885ef703b359987f30f4d Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Fri, 24 Jan 2025 16:05:35 +0100 Subject: [PATCH 0327/1144] lint: the final show Signed-off-by: Gabriel29306 --- src/components/Global/PapillonPicker.tsx | 12 +++++++----- src/views/account/Grades/Subject/Subject.tsx | 8 ++++++-- src/views/settings/SettingsDevLogs.tsx | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/components/Global/PapillonPicker.tsx b/src/components/Global/PapillonPicker.tsx index 93777c422..0144790ca 100644 --- a/src/components/Global/PapillonPicker.tsx +++ b/src/components/Global/PapillonPicker.tsx @@ -10,17 +10,19 @@ import { NativeText } from "./NativeComponents"; import { BlurView } from "expo-blur"; import { Check } from "lucide-react-native"; -type PickerData = string[] | { label: string, icon?: JSX.Element, onPress: () => unknown, checked?: boolean }[]; +export type PickerDataItem = string | { label: string, icon?: JSX.Element, onPress?: () => unknown, checked?: boolean }; + +type PickerData = PickerDataItem[]; interface PapillonPickerProps { children: React.ReactNode data: PickerData - selected?: string + selected?: PickerDataItem contentContainerStyle?: StyleProp>> delay?: number, direction?: "left" | "right", animated?: boolean, - onSelectionChange?: (item: string) => unknown + onSelectionChange?: any } const PapillonPicker: React.FC = ({ @@ -37,7 +39,7 @@ const PapillonPicker: React.FC = ({ const [contentHeight, setContentHeight] = useState(0); const [opened, setOpened] = useState(false); - const handleSelectionChange = (item: string) => { + const handleSelectionChange = (item: PickerDataItem) => { if (onSelectionChange) { setTimeout(() => { onSelectionChange(item); @@ -115,7 +117,7 @@ const PapillonPicker: React.FC = ({ onPressItem(); } : () => { setOpened(false); - handleSelectionChange(item as string); + handleSelectionChange(item); }} style={[ styles.item diff --git a/src/views/account/Grades/Subject/Subject.tsx b/src/views/account/Grades/Subject/Subject.tsx index a7b97dd21..0a2173a00 100644 --- a/src/views/account/Grades/Subject/Subject.tsx +++ b/src/views/account/Grades/Subject/Subject.tsx @@ -109,7 +109,11 @@ const Subject: React.FC = ({ textTransform: "uppercase", }} > - {sortings[sorting].label} + { + typeof sortings[sorting] === "string" + ? sortings[sorting] + : sortings[sorting].label + } {isLoading && ( = ({ ); }; -export default Subject; \ No newline at end of file +export default Subject; diff --git a/src/views/settings/SettingsDevLogs.tsx b/src/views/settings/SettingsDevLogs.tsx index 9fc4ba9c2..d9122843a 100644 --- a/src/views/settings/SettingsDevLogs.tsx +++ b/src/views/settings/SettingsDevLogs.tsx @@ -34,7 +34,7 @@ import { import { animPapillon } from "@/utils/ui/animations"; import { useTheme } from "@react-navigation/native"; import { useAlert } from "@/providers/AlertProvider"; -import MissingItem from "@/components/Global/MissingItem.tsx"; +import MissingItem from "@/components/Global/MissingItem"; const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { const { colors } = useTheme(); From 884fbc0947d1b05485a27182d42e4c80cd4b2ebb Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Fri, 24 Jan 2025 16:06:37 +0100 Subject: [PATCH 0328/1144] lint: unused import Signed-off-by: Gabriel29306 --- src/views/account/Grades/Document.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Grades/Document.tsx b/src/views/account/Grades/Document.tsx index 75410991e..3ad5d10c2 100644 --- a/src/views/account/Grades/Document.tsx +++ b/src/views/account/Grades/Document.tsx @@ -6,7 +6,7 @@ import { } from "@/components/Global/NativeComponents"; import { getSubjectData } from "@/services/shared/Subject"; import { useTheme } from "@react-navigation/native"; -import React, { useCallback, useEffect, useLayoutEffect, useState } from "react"; +import React, { useEffect, useLayoutEffect, useState } from "react"; import { Image, ScrollView, Text, View, Platform } from "react-native"; import * as StoreReview from "expo-store-review"; import { From a88b935f9e2a663a9900e53801109c418a29d155 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Fri, 24 Jan 2025 16:18:16 +0100 Subject: [PATCH 0329/1144] feat(grade): Don't show category if there is no value (keep value with `N.Not`) Signed-off-by: Gabriel29306 --- src/views/account/Grades/Document.tsx | 2 +- src/views/account/Grades/Modals/Subject.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views/account/Grades/Document.tsx b/src/views/account/Grades/Document.tsx index 3ad5d10c2..e315bef49 100644 --- a/src/views/account/Grades/Document.tsx +++ b/src/views/account/Grades/Document.tsx @@ -164,7 +164,7 @@ const GradeDocument: Screen<"GradeDocument"> = ({ route, navigation }) => { : "??", bareme: "/" + grade.outOf.value, }, - ].filter(Boolean), + ].filter(Boolean).filter((value) => value.value != "??"), }, { title: "Influence", diff --git a/src/views/account/Grades/Modals/Subject.tsx b/src/views/account/Grades/Modals/Subject.tsx index 0dcd6b4a1..a148d4568 100644 --- a/src/views/account/Grades/Modals/Subject.tsx +++ b/src/views/account/Grades/Modals/Subject.tsx @@ -9,7 +9,7 @@ import { getCourseSpeciality } from "@/utils/format/format_cours_name"; import { AverageDiffGrade, getAverageDiffGrade } from "@/utils/grades/getAverages"; import { useTheme } from "@react-navigation/native"; import { Trophy, User, UserMinus, UserPlus, Users } from "lucide-react-native"; -import React, { useEffect, useLayoutEffect, useState } from "react"; +import { useEffect, useLayoutEffect, useState } from "react"; import { View, ScrollView } from "react-native"; import { Screen } from "@/router/helpers/types"; @@ -61,7 +61,7 @@ const GradeSubjectScreen: Screen<"GradeSubject"> = ({ route, navigation }) => { ? parseFloat((subject.average?.min?.value || -1).toString()).toFixed(2) : "??", }, - ]; + ].filter((value) => value.value != "??"); const subjectOutOf = subject.average?.outOf?.value || 20; From cd914ad2e1b5d00abea9e8602a1a727609ec2476 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Fri, 24 Jan 2025 17:26:24 +0100 Subject: [PATCH 0330/1144] feat(Grades/Subject): simplify logic Signed-off-by: Gabriel29306 --- src/views/account/Grades/Modals/Subject.tsx | 23 +++++++++------------ 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/views/account/Grades/Modals/Subject.tsx b/src/views/account/Grades/Modals/Subject.tsx index a148d4568..db1aa4292 100644 --- a/src/views/account/Grades/Modals/Subject.tsx +++ b/src/views/account/Grades/Modals/Subject.tsx @@ -9,7 +9,7 @@ import { getCourseSpeciality } from "@/utils/format/format_cours_name"; import { AverageDiffGrade, getAverageDiffGrade } from "@/utils/grades/getAverages"; import { useTheme } from "@react-navigation/native"; import { Trophy, User, UserMinus, UserPlus, Users } from "lucide-react-native"; -import { useEffect, useLayoutEffect, useState } from "react"; +import React, { useEffect, useLayoutEffect, useState } from "react"; import { View, ScrollView } from "react-native"; import { Screen } from "@/router/helpers/types"; @@ -32,34 +32,31 @@ const GradeSubjectScreen: Screen<"GradeSubject"> = ({ route, navigation }) => { fetchSubjectData(); }, [subject.average.subjectName]); + const studentAverage = parseFloat((subject.average?.average?.value || -1).toString()).toFixed(2); + const classAverage = parseFloat((subject.average?.classAverage?.value || -1).toString()).toFixed(2); + const highAverage = parseFloat((subject.average?.max?.value || -1).toString()).toFixed(2); + const lowAverage = parseFloat((subject.average?.min?.value || -1).toString()).toFixed(2); + const averages = [ { icon: , label: "Ta moyenne", - value: parseFloat((subject.average?.average?.value || -1).toString()).toFixed(2) !== "-1.00" - ? parseFloat((subject.average?.average?.value || -1).toString()).toFixed(2) - : "N.Not", + value: studentAverage !== "-1.00" ? studentAverage : "N.Not", }, { icon: , label: "Moy. de classe", - value: parseFloat((subject.average?.classAverage?.value || -1).toString()).toFixed(2) !== "-1.00" - ? parseFloat((subject.average?.classAverage?.value || -1).toString()).toFixed(2) - : "??", + value: classAverage !== "-1.00" ? classAverage : "??", }, { icon: , label: "Moy. la plus haute", - value: parseFloat((subject.average?.max?.value || -1).toString()).toFixed(2) !== "-1.00" - ? parseFloat((subject.average?.max?.value || -1).toString()).toFixed(2) - : "??", + value: highAverage !== "-1.00" ? highAverage : "??", }, { icon: , label: "Moy. la plus basse", - value: parseFloat((subject.average?.min?.value || -1).toString()).toFixed(2) !== "-1.00" - ? parseFloat((subject.average?.min?.value || -1).toString()).toFixed(2) - : "??", + value: lowAverage !== "-1.00" ? lowAverage : "??", }, ].filter((value) => value.value != "??"); From 544f1e2d18dd92133bc7e31393313291f9a130af Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Fri, 24 Jan 2025 18:44:55 +0100 Subject: [PATCH 0331/1144] lint: don't use `var` --- src/addons/addons.ts | 10 +++++----- src/components/Addons/AddonsWebview.tsx | 6 +++--- src/utils/epochWeekNumber.ts | 4 ++-- src/views/addon/AddonPage.tsx | 4 ++-- src/views/settings/SettingsSubjects.tsx | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/addons/addons.ts b/src/addons/addons.ts index 9315ca215..ea69bb329 100644 --- a/src/addons/addons.ts +++ b/src/addons/addons.ts @@ -1,6 +1,6 @@ import * as FileSystem from "expo-file-system"; -import {error, log } from "@/utils/logger/logger"; -import {AddonDomain, AddonManifest, AddonPermission, AddonPlacement, AddonPlacementManifest} from "@/addons/types"; +import { error, log } from "@/utils/logger/logger"; +import { AddonDomain, AddonManifest, AddonPermission, AddonPlacement, AddonPlacementManifest } from "@/addons/types"; async function init_addons_folder () { if (!(await FileSystem.getInfoAsync(FileSystem.documentDirectory + "addons")).exists) { @@ -207,9 +207,9 @@ async function get_home_widgets (): Promise { async function get_settings_widgets (): Promise { let addons = await get_addons_list(); - var res: Array = []; + let res: Array = []; addons.forEach((addon) => { - for (var i = 0; i < addon.placement.length; i++) { + for (let i = 0; i < addon.placement.length; i++) { if (addon.placement[i].placement == "PLACE_SETTINGS_PAGE") res.push({ index: i, @@ -220,4 +220,4 @@ async function get_settings_widgets (): Promise { return res; } -export { init_addons_folder, get_addons_list, get_home_widgets, get_settings_widgets }; \ No newline at end of file +export { init_addons_folder, get_addons_list, get_home_widgets, get_settings_widgets }; diff --git a/src/components/Addons/AddonsWebview.tsx b/src/components/Addons/AddonsWebview.tsx index 7247ee302..ac2183770 100644 --- a/src/components/Addons/AddonsWebview.tsx +++ b/src/components/Addons/AddonsWebview.tsx @@ -12,7 +12,7 @@ import Reanimated, { Easing, useSharedValue, withTiming } from "react-native-rea import type { NativeStackNavigationProp } from "@react-navigation/native-stack"; import type { RouteParameters } from "@/router/helpers/types"; import { get_iso_date } from "@/utils/logger/logger"; -import {AddonLogs} from "@/addons/types"; +import { AddonLogs } from "@/addons/types"; export type AddonHomePageInfo = { name: string, @@ -56,8 +56,8 @@ const AddonsWebview: React.FC = ({ function get_plugin_path () { let path = url.split("/"); - var res = ""; - for (var i = 0; i < path.length - 1; i++) { + let res = ""; + for (let i = 0; i < path.length - 1; i++) { res += path[i] + "/"; if (path[i] === "addons") { diff --git a/src/utils/epochWeekNumber.ts b/src/utils/epochWeekNumber.ts index 10de59d99..2d661d286 100644 --- a/src/utils/epochWeekNumber.ts +++ b/src/utils/epochWeekNumber.ts @@ -108,9 +108,9 @@ export const epochWMToCalendarWeekNumber = (epochWeekNumber: number): number => // Set Day to Sunday and make it the 7th day of the week date.setUTCDate(date.getUTCDate() + 4 - (date.getUTCDay()||7)); // Get first day of year - var yearStart = new Date(Date.UTC(date.getUTCFullYear(),0,1)); + let yearStart = new Date(Date.UTC(date.getUTCFullYear(),0,1)); // Calculate full weeks to nearest Thursday - var weekNo = Math.ceil(( ( (date.getTime() - yearStart.getTime()) / 86400000) + 1)/7); + let weekNo = Math.ceil(( ( (date.getTime() - yearStart.getTime()) / 86400000) + 1)/7); // Return array of year and week number return weekNo; }; diff --git a/src/views/addon/AddonPage.tsx b/src/views/addon/AddonPage.tsx index 12c9e69b2..960b715d3 100644 --- a/src/views/addon/AddonPage.tsx +++ b/src/views/addon/AddonPage.tsx @@ -33,8 +33,8 @@ const AddonPage: Screen<"AddonPage"> = ({ navigation, route }) => { data={data} requestNavigate={(url, data) => { //find the placement - var index = -1; - for(var i = 0; i < addon.manifest.placement.length; i++){ + let index = -1; + for(let i = 0; i < addon.manifest.placement.length; i++){ if(addon.manifest.placement[i].name == url){ index = i; break; diff --git a/src/views/settings/SettingsSubjects.tsx b/src/views/settings/SettingsSubjects.tsx index bbed830ea..18e82eca9 100644 --- a/src/views/settings/SettingsSubjects.tsx +++ b/src/views/settings/SettingsSubjects.tsx @@ -97,7 +97,7 @@ const SettingsSubjects: Screen<"SettingsSubjects"> = ({ navigation }) => { const handleSubjectEmojiChange = useCallback((subjectKey: string, newEmoji: string) => { let emoji = ""; if(newEmoji.length >= 1) { - var regexp = /((\ud83c[\udde6-\uddff]){2}|([#*0-9]\u20e3)|(\u00a9|\u00ae|[\u2000-\u3300]|[\ud83c-\ud83e][\ud000-\udfff])((\ud83c[\udffb-\udfff])?(\ud83e[\uddb0-\uddb3])?(\ufe0f?\u200d([\u2000-\u3300]|[\ud83c-\ud83e][\ud000-\udfff])\ufe0f?)?)*)/g; + let regexp = /((\ud83c[\udde6-\uddff]){2}|([#*0-9]\u20e3)|(\u00a9|\u00ae|[\u2000-\u3300]|[\ud83c-\ud83e][\ud000-\udfff])((\ud83c[\udffb-\udfff])?(\ud83e[\uddb0-\uddb3])?(\ufe0f?\u200d([\u2000-\u3300]|[\ud83c-\ud83e][\ud000-\udfff])\ufe0f?)?)*)/g; const emojiMatch = newEmoji.match(regexp); if(emojiMatch) { emoji = emojiMatch[emojiMatch.length - 1]; From 84f98b4981ea4b1f83ddffa15bba5aac3e02bb8e Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Fri, 24 Jan 2025 18:46:14 +0100 Subject: [PATCH 0332/1144] lint: inaccesible code --- src/components/Global/FileIcon.tsx | 12 +----------- src/utils/grades/getAverages.ts | 2 -- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/components/Global/FileIcon.tsx b/src/components/Global/FileIcon.tsx index fb9cbad05..f2a199459 100644 --- a/src/components/Global/FileIcon.tsx +++ b/src/components/Global/FileIcon.tsx @@ -7,33 +7,23 @@ export const AutoFileIcon = (props: any) => { switch (fileExt) { case "pdf": return ; - break; case "odt": return ; - break; case "doc": return ; - break; case "docx": return ; - break; case "mp3": return ; - break; case "mp4": return ; - break; case "wav": return ; - break; case "png": return ; - break; case "jpg": return ; - break; default: return ; - break; } -}; \ No newline at end of file +}; diff --git a/src/utils/grades/getAverages.ts b/src/utils/grades/getAverages.ts index 717875670..4460efe86 100644 --- a/src/utils/grades/getAverages.ts +++ b/src/utils/grades/getAverages.ts @@ -209,8 +209,6 @@ const getAveragesHistory = ( // remove NaN values return history.filter((x) => !isNaN(x.value)); - - return history; // Retourner l'historique généré } catch(e) { return []; From 31bd5de5a1cf036a073a6d332fbe481d7a381b8a Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Fri, 24 Jan 2025 18:47:24 +0100 Subject: [PATCH 0333/1144] lint: simplify conditions --- src/components/Global/NativeComponents.tsx | 4 ++-- src/components/Global/PapillonModernHeader.tsx | 6 +++--- src/views/account/Homeworks/Document.tsx | 15 ++++++--------- src/views/account/Lessons/Document.tsx | 2 +- src/views/settings/SettingsIcons.tsx | 4 ++-- src/views/settings/SettingsSubjects.tsx | 4 ++-- 6 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/components/Global/NativeComponents.tsx b/src/components/Global/NativeComponents.tsx index b91015f92..2815bfe90 100644 --- a/src/components/Global/NativeComponents.tsx +++ b/src/components/Global/NativeComponents.tsx @@ -228,8 +228,8 @@ export const NativeItem: React.FC = ({ return ( = ({ children, outsideNav justifyContent: "space-between", alignItems: "center", gap: 8, - backgroundColor: tint ? tint : theme.colors.text + "10", + backgroundColor: tint ?? theme.colors.text + "10", borderBottomColor: theme.colors.border, borderBottomWidth: 0.5, }]} @@ -175,8 +175,8 @@ export const PapillonHeaderAction: React.FC<{ return ( = ({ route }) => { { Alert.alert( - homework.returnType === "file_upload" + homework.returnType === HomeworkReturnType.FileUpload ? "Tu dois rendre ce devoir sur ton ENT" - : homework.returnType === "paper" + : homework.returnType === HomeworkReturnType.Paper ? "Tu dois rendre ce devoir en classe" : "Ce devoir est à rendre", - homework.returnType === "file_upload" + homework.returnType === HomeworkReturnType.FileUpload ? "Papillon ne permet pas de rendre des devoirs sur l'ENT. Tu dois le faire sur l'ENT de ton établissement" - : homework.returnType === "paper" - ? "Ton professeur t'indiquera comment rendre ce devoir" - : "Ton professeur t'indiquera comment rendre ce devoir", + : "Ton professeur t'indiquera comment rendre ce devoir", ); }} > @@ -144,9 +142,8 @@ const HomeworksDocument: Screen<"HomeworksDocument"> = ({ route }) => { > {homework.returnType === HomeworkReturnType.FileUpload ? "A rendre sur l'ENT" - : homework.returnType === HomeworkReturnType.Paper - ? "A rendre en classe" - : null} + : homework.returnType === HomeworkReturnType.Paper && "A rendre en classe" + } diff --git a/src/views/account/Lessons/Document.tsx b/src/views/account/Lessons/Document.tsx index bf75ce37c..20dbfaed5 100644 --- a/src/views/account/Lessons/Document.tsx +++ b/src/views/account/Lessons/Document.tsx @@ -300,7 +300,7 @@ const LessonDocument: Screen<"LessonDocument"> = ({ route, navigation }) => { return ( <> 0 ? true : false} + separator={(r.files?.length ?? 0) > 0} > {index > 0 && = ({ navigation }) => { } > {icon.name} - {(icon.author && icon.author.trim() !== "") && - {icon.author} + {(icon.author?.trim() !== "") && + {icon.author} } ))} diff --git a/src/views/settings/SettingsSubjects.tsx b/src/views/settings/SettingsSubjects.tsx index 18e82eca9..cd08abbab 100644 --- a/src/views/settings/SettingsSubjects.tsx +++ b/src/views/settings/SettingsSubjects.tsx @@ -174,7 +174,7 @@ const SettingsSubjects: Screen<"SettingsSubjects"> = ({ navigation }) => { const [customColor, setCustomColor] = useState(""); const renderSubjectItem = useCallback(({ item: subject, index }: { item: Item, index: number }) => { - if (!subject[0] || !subject[1] || !subject[1].emoji || !subject[1].pretty || !subject[1].color) + if (!subject[0] || !subject[1]?.emoji || !subject[1]?.pretty || !subject[1]?.color) return null; return ( @@ -456,4 +456,4 @@ const SettingsSubjects: Screen<"SettingsSubjects"> = ({ navigation }) => { ); }; -export default React.memo(SettingsSubjects); \ No newline at end of file +export default React.memo(SettingsSubjects); From 888f024156cc57e248d934b403d55d8b568d3b69 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Fri, 24 Jan 2025 18:49:06 +0100 Subject: [PATCH 0334/1144] lint: remove unecessary keywords `void`, `return`, `await` --- src/services/alise/bookings.ts | 4 ++-- src/views/addon/AddonPage.tsx | 9 ++++----- .../login/pronote/PronoteAuthenticationSelector.tsx | 2 +- src/views/login/pronote/PronoteManualLocation.tsx | 2 +- .../login/skolengo/SkolengoAuthenticationSelector.tsx | 2 +- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/services/alise/bookings.ts b/src/services/alise/bookings.ts index 69a32a25b..48960645a 100644 --- a/src/services/alise/bookings.ts +++ b/src/services/alise/bookings.ts @@ -2,7 +2,7 @@ import type { AliseAccount } from "@/stores/account/types"; import type { BookingDay, BookingTerminal } from "../shared/Booking"; export const getBookings = async (account: AliseAccount, force = false): Promise => { - const bookings = force ? await account.authentication.session.getBookings() : await account.authentication.bookings; + const bookings = force ? await account.authentication.session.getBookings() : account.authentication.bookings; return [{ id: "", terminalLabel: "Self", @@ -23,4 +23,4 @@ export const bookDay = async (account: AliseAccount, id: string, date: Date, boo canBook: bookedDay.canBook, booked: bookedDay.booked, }; -}; \ No newline at end of file +}; diff --git a/src/views/addon/AddonPage.tsx b/src/views/addon/AddonPage.tsx index 960b715d3..96be898f4 100644 --- a/src/views/addon/AddonPage.tsx +++ b/src/views/addon/AddonPage.tsx @@ -1,9 +1,9 @@ import AddonsWebview from "@/components/Addons/AddonsWebview"; -import {Alert, View} from "react-native"; -import {useSafeAreaInsets} from "react-native-safe-area-context"; +import { Alert, View } from "react-native"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; import React from "react"; -import {AddonPlacementManifest} from "@/addons/types"; -import {Screen} from "@/router/helpers/types"; +import { AddonPlacementManifest } from "@/addons/types"; +import { Screen } from "@/router/helpers/types"; const AddonPage: Screen<"AddonPage"> = ({ navigation, route }) => { const addon: AddonPlacementManifest = route.params?.addon; @@ -42,7 +42,6 @@ const AddonPage: Screen<"AddonPage"> = ({ navigation, route }) => { } if (index == -1) { Alert.alert("Error", "The requested page was not found."); //TODO: transfer error to webview - return; } else { let newAddon: AddonPlacementManifest = {manifest: addon.manifest, index: index}; // @ts-ignore "Very hard to type, need to think about" diff --git a/src/views/login/pronote/PronoteAuthenticationSelector.tsx b/src/views/login/pronote/PronoteAuthenticationSelector.tsx index a4005e444..9a46d819d 100644 --- a/src/views/login/pronote/PronoteAuthenticationSelector.tsx +++ b/src/views/login/pronote/PronoteAuthenticationSelector.tsx @@ -37,7 +37,7 @@ const PronoteAuthenticationSelector: Screen<"PronoteAuthenticationSelector"> = ( }; }, []); - const playSound = () => void sound?.replayAsync(); + const playSound = () => sound?.replayAsync(); const handleConfirmation = () => { switch (method) { diff --git a/src/views/login/pronote/PronoteManualLocation.tsx b/src/views/login/pronote/PronoteManualLocation.tsx index 3d58e0973..9ae94c89b 100644 --- a/src/views/login/pronote/PronoteManualLocation.tsx +++ b/src/views/login/pronote/PronoteManualLocation.tsx @@ -217,7 +217,7 @@ const PronoteManualLocation: Screen<"PronoteManualLocation"> = ({ navigation }) > void navigation.navigate("PronoteInstanceSelector", { + onPress={() => navigation.navigate("PronoteInstanceSelector", { longitude: municipality.geometry.coordinates[0], latitude: municipality.geometry.coordinates[1], })} diff --git a/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx b/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx index cbbddd69a..aad000aeb 100644 --- a/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx +++ b/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx @@ -36,7 +36,7 @@ const SkolengoAuthenticationSelector: Screen<"SkolengoAuthenticationSelector"> = }; }, []); - const playSound = () => void sound?.replayAsync(); + const playSound = () => sound?.replayAsync(); const handleConfirmation = () => { switch (method) { From 4de516437cf42e40f9ac284fe2d1e5bc933c75a3 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Fri, 24 Jan 2025 18:49:42 +0100 Subject: [PATCH 0335/1144] perf: add missing `break` --- src/services/chats.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/services/chats.ts b/src/services/chats.ts index c6d581274..fdb836c72 100644 --- a/src/services/chats.ts +++ b/src/services/chats.ts @@ -39,9 +39,11 @@ export const sendMessageInChat = async (account: T, chat: Ch case AccountService.Pronote: { const { sendMessageInChat } = await import("./pronote/chats"); await sendMessageInChat(account, chat, content); + break; } case AccountService.EcoleDirecte: { // TODO + break; } default: console.info("[sendMessageInChat]: Not Implementend."); From f5fc04d6495ad0cbad8d6bd3dd7d1e61ad8c8ba0 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Fri, 24 Jan 2025 18:50:01 +0100 Subject: [PATCH 0336/1144] lint: unecessary instruction --- src/widgets/Components/NextCourse.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/widgets/Components/NextCourse.tsx b/src/widgets/Components/NextCourse.tsx index 1cbec2206..2429116ad 100644 --- a/src/widgets/Components/NextCourse.tsx +++ b/src/widgets/Components/NextCourse.tsx @@ -66,7 +66,6 @@ const NextCourseWidget = forwardRef(({ hidden, setHidden, loading, setLoading }: useEffect(() => { if (nextCourse) { - setNextCourse(nextCourse); setHidden(false); } setLoading(false); From ac5569cba4ec82e75ad3d82677c2214078563fa1 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Fri, 24 Jan 2025 18:50:25 +0100 Subject: [PATCH 0337/1144] lint: simplify ratio (`1/1`=>`1`) --- src/views/welcome/ChangelogScreen.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/welcome/ChangelogScreen.tsx b/src/views/welcome/ChangelogScreen.tsx index 2181af7b4..444406d10 100644 --- a/src/views/welcome/ChangelogScreen.tsx +++ b/src/views/welcome/ChangelogScreen.tsx @@ -17,7 +17,7 @@ import { TouchableOpacity } from "react-native-gesture-handler"; import { PressableScale } from "react-native-pressable-scale"; import AsyncStorage from "@react-native-async-storage/async-storage"; -import {Screen} from "@/router/helpers/types"; +import { Screen } from "@/router/helpers/types"; interface Feature { title: string; @@ -81,7 +81,7 @@ const ChangelogScreen: Screen<"ChangelogScreen"> = ({ route, navigation }) => { onPress={() => navigation.goBack()} style={{ width: 32, - aspectRatio: 1 / 1, + aspectRatio: 1, backgroundColor: theme.colors.text + "18", alignItems: "center", justifyContent: "center", From 5ba5eb0c9e03cb44aeb01ccd0c99e61161a9c628 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Fri, 24 Jan 2025 18:51:07 +0100 Subject: [PATCH 0338/1144] lint: `useEffect` need to be called in every render. React rule --- src/views/account/Home/Elements/PopupRestauration.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/views/account/Home/Elements/PopupRestauration.tsx b/src/views/account/Home/Elements/PopupRestauration.tsx index ab1b0cbe0..e3117851f 100644 --- a/src/views/account/Home/Elements/PopupRestauration.tsx +++ b/src/views/account/Home/Elements/PopupRestauration.tsx @@ -1,4 +1,4 @@ -import React, {useEffect} from "react"; +import React, { useEffect } from "react"; import { View, Image, StyleSheet } from "react-native"; import { NativeList, NativeText } from "@/components/Global/NativeComponents"; import { useTheme } from "@react-navigation/native"; @@ -26,14 +26,14 @@ const PopupRestauration: React.FC = ({ onImportance }) = onImportance(2); }; - if (account.personalization?.popupRestauration === false) { - return null; - } - useEffect(() => { ImportanceHandler(); }, []); + if (account.personalization?.popupRestauration === false) { + return null; + } + return ( Date: Fri, 24 Jan 2025 20:02:42 +0100 Subject: [PATCH 0339/1144] =?UTF-8?q?fix:=20am=C3=A9liorer=20l'enregistrem?= =?UTF-8?q?ent=20des=20t=C3=A2ches=20en=20arri=C3=A8re-plan=20avec=20gesti?= =?UTF-8?q?on=20des=20erreurs=20et=20avertissements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/BackgroundTasks.ts | 34 ++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 8bae2ef90..2dae32667 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -3,9 +3,10 @@ import * as BackgroundFetch from "expo-background-fetch"; import * as TaskManager from "expo-task-manager"; import { BackgroundFetchResult } from "expo-background-fetch"; import { isExpoGo } from "@/utils/native/expoGoAlert"; +import Constants from "expo-constants"; import { fetchNews } from "./data/News"; -import { log, error } from "@/utils/logger/logger"; +import { log, error, warn } from "@/utils/logger/logger"; import { getAccounts, getSwitchToFunction } from "./utils/accounts"; import { fetchHomeworks } from "./data/Homeworks"; import { fetchGrade } from "./data/Grades"; @@ -83,21 +84,34 @@ const unsetBackgroundFetch = async () => { }; const registerBackgroundTasks = async () => { - const isRegistered = await TaskManager.isTaskRegisteredAsync("background-fetch"); + const isRegistered = await TaskManager.isTaskRegisteredAsync( + "background-fetch" + ); if (isRegistered) { - log("⚠️ Background task already registered. Unregister background task.", "BackgroundEvent"); + warn( + "⚠️ Background task already registered. Unregister background task...", + "BackgroundEvent" + ); await unsetBackgroundFetch(); } if (!isExpoGo()) { - await BackgroundFetch.registerTaskAsync("background-fetch", { - minimumInterval: 60 * 15, - stopOnTerminate: false, - startOnBoot: true, - }); - - log("✅ Background task registered", "BackgroundEvent"); + try { + await BackgroundFetch.registerTaskAsync("background-fetch", { + minimumInterval: 60 * 15, + stopOnTerminate: false, + startOnBoot: true, + }); + log("✅ Background task registered", "BackgroundEvent"); + } catch (err) { + error(`❌ Failed to register background task: ${err}`, "BackgroundEvent"); + } + } else { + error( + `🚨 Running in Expo Go (Constants => ${Constants.appOwnership}). Skipping background task registration...`, + "BackgroundEvent" + ); } }; From ecc96dbc31171f8491605a98bd72af120a3f9f2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 25 Jan 2025 11:58:21 +0100 Subject: [PATCH 0340/1144] feat: Ajouter le fichier `.env` avec la variable `EXPO_ENV` --- .env | 1 + 1 file changed, 1 insertion(+) create mode 100644 .env diff --git a/.env b/.env new file mode 100644 index 000000000..d31f8d1ad --- /dev/null +++ b/.env @@ -0,0 +1 @@ +EXPO_ENV=expo From d91cb9ff7ba0ccc1e19ca948aa62d877b3793ddf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 25 Jan 2025 11:59:59 +0100 Subject: [PATCH 0341/1144] =?UTF-8?q?feat:=20Ajouter=20la=20variable=20`EX?= =?UTF-8?q?PO=5FENV`=20dans=20la=20configuration=20et=20mettre=20=C3=A0=20?= =?UTF-8?q?jour=20`isExpoGo`=20pour=20l'utiliser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.config.ts | 3 +++ src/utils/native/expoGoAlert.ts | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app.config.ts b/app.config.ts index e54e6b3ec..8674ec9c1 100644 --- a/app.config.ts +++ b/app.config.ts @@ -76,6 +76,9 @@ export default (): ExpoConfig => ({ "android.permission.ACCESS_FINE_LOCATION", ], }, + extra: { + EXPO_ENV: "expo", + }, plugins: [ [ "expo-font", diff --git a/src/utils/native/expoGoAlert.ts b/src/utils/native/expoGoAlert.ts index fcab0a6d0..0169b6692 100644 --- a/src/utils/native/expoGoAlert.ts +++ b/src/utils/native/expoGoAlert.ts @@ -2,7 +2,8 @@ import { Alert } from "react-native"; import Constants from "expo-constants"; export const isExpoGo = () => { - return Constants.appOwnership === "expo"; + console.log(Constants.expoConfig?.extra?.EXPO_ENV); + return Constants.expoConfig?.extra?.EXPO_ENV === "expo"; }; export const alertExpoGo = async () => { From 4e22a88572ee065d8f128f4e75acf4fcce369815 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 25 Jan 2025 12:07:49 +0100 Subject: [PATCH 0342/1144] fix: Remplace `expoGoWrapper` with `isExpoGo` in `App` --- App.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/App.tsx b/App.tsx index 80503e092..ff0fe4eae 100644 --- a/App.tsx +++ b/App.tsx @@ -7,8 +7,9 @@ import AsyncStorage from "@react-native-async-storage/async-storage"; import { useAccounts, useCurrentAccount } from "@/stores/account"; import {AccountService, PrimaryAccount} from "@/stores/account/types"; import { log } from "@/utils/logger/logger"; -import { expoGoWrapper } from "@/utils/native/expoGoAlert"; +import { isExpoGo } from "@/utils/native/expoGoAlert"; import { atobPolyfill, btoaPolyfill } from "js-base64"; +import { registerBackgroundTasks } from "@/background/BackgroundTasks"; SplashScreen.preventAutoHideAsync(); @@ -105,10 +106,9 @@ export default function App () { "[Reanimated] Property ", ]); - expoGoWrapper(async () => { - const { registerBackgroundTasks } = await import("@/background/BackgroundTasks"); + if (!isExpoGo()) { registerBackgroundTasks(); - }); + }; }, []); const applyGlobalPolyfills = useCallback(() => { From 5e180b9e3056b47152bd7c7d57e746a1b27bace0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 25 Jan 2025 12:14:17 +0100 Subject: [PATCH 0343/1144] delete `console.log` --- src/utils/native/expoGoAlert.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/utils/native/expoGoAlert.ts b/src/utils/native/expoGoAlert.ts index 0169b6692..50c22ef84 100644 --- a/src/utils/native/expoGoAlert.ts +++ b/src/utils/native/expoGoAlert.ts @@ -2,7 +2,6 @@ import { Alert } from "react-native"; import Constants from "expo-constants"; export const isExpoGo = () => { - console.log(Constants.expoConfig?.extra?.EXPO_ENV); return Constants.expoConfig?.extra?.EXPO_ENV === "expo"; }; From a2037703b71e07bf6a4f6c736ba3416ac58d551d Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 25 Jan 2025 12:14:47 +0100 Subject: [PATCH 0344/1144] fix: `String((new Error()).stack!)` to `"get_addons_list"` --- src/addons/addons.ts | 70 ++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/addons/addons.ts b/src/addons/addons.ts index 9315ca215..5cc42cb3c 100644 --- a/src/addons/addons.ts +++ b/src/addons/addons.ts @@ -1,11 +1,11 @@ import * as FileSystem from "expo-file-system"; -import {error, log } from "@/utils/logger/logger"; -import {AddonDomain, AddonManifest, AddonPermission, AddonPlacement, AddonPlacementManifest} from "@/addons/types"; +import { error, log } from "@/utils/logger/logger"; +import { AddonDomain, AddonManifest, AddonPermission, AddonPlacement, AddonPlacementManifest } from "@/addons/types"; async function init_addons_folder () { if (!(await FileSystem.getInfoAsync(FileSystem.documentDirectory + "addons")).exists) { await FileSystem.makeDirectoryAsync(FileSystem.documentDirectory + "addons"); - log("Addons folder initialized at " + FileSystem.documentDirectory + "addons", String((new Error()).stack!)); + log("Addons folder initialized at " + FileSystem.documentDirectory + "addons", "get_addons_list"); } } @@ -27,88 +27,88 @@ function generate_addons_error (error: string, name: string): AddonManifest { async function get_addons_list (): Promise { init_addons_folder(); - log("Reading addons folder", String((new Error()).stack!)); + log("Reading addons folder", "get_addons_list"); let res: AddonManifest[] = []; let addons = await FileSystem.readDirectoryAsync(FileSystem.documentDirectory + "addons"); - log(`Found ${addons.length} folder to check...`, String((new Error()).stack!)); + log(`Found ${addons.length} folder to check...`, "get_addons_list"); for (let addon of addons) { - log(`| Starting check for folder ${addon}...`, String((new Error()).stack!)); + log(`| Starting check for folder ${addon}...`, "get_addons_list"); // Check if the addon is a directory let stat = await FileSystem.getInfoAsync(FileSystem.documentDirectory + "addons/" + addon); if (!stat.isDirectory) { - error("| Not a directory ! Skipping...", String((new Error()).stack!)); + error("| Not a directory ! Skipping...", "get_addons_list"); continue; } // Check if the addon has a manifest - log("| Searching for manifest.json...", String((new Error()).stack!)); + log("| Searching for manifest.json...", "get_addons_list"); let info = await FileSystem.getInfoAsync(FileSystem.documentDirectory + "addons/" + addon + "/manifest.json"); if (!info.exists) { - log("| manifest.json not found ! Skipping...", String((new Error()).stack!)); + log("| manifest.json not found ! Skipping...", "get_addons_list"); continue; } // Read the manifest - log("| Reading manifest.json...", String((new Error()).stack!)); + log("| Reading manifest.json...", "get_addons_list"); let file = await FileSystem.readAsStringAsync(FileSystem.documentDirectory + "addons/" + addon + "/manifest.json"); try { - log("| Parsing manifest.json...", String((new Error()).stack!)); + log("| Parsing manifest.json...", "get_addons_list"); let manifest: Partial = JSON.parse(file); // Check if the manifest has all the required fields - log("| Loading addons...", String((new Error()).stack!)); + log("| Loading addons...", "get_addons_list"); if (!manifest.name && typeof manifest.name !== "string") { - error(`| Missing properties "name" in ${addon} ! Plugin can't load, skipping...`, String((new Error()).stack!)); + error(`| Missing properties "name" in ${addon} ! Plugin can't load, skipping...`, "get_addons_list"); res.push(generate_addons_error("Missing properties \"name\"", addon)); continue; } if (!manifest.author && typeof manifest.author !== "string") { - error(`| Missing properties "author" in ${addon} ! Plugin can't load, skipping...`, String((new Error()).stack!)); + error(`| Missing properties "author" in ${addon} ! Plugin can't load, skipping...`, "get_addons_list"); res.push(generate_addons_error("Missing properties \"author\"", addon)); continue; } if (!manifest.version && typeof manifest.version !== "string") { - error(`| Missing properties "version" in ${addon} ! Plugin can't load, skipping...`, String((new Error()).stack!)); + error(`| Missing properties "version" in ${addon} ! Plugin can't load, skipping...`, "get_addons_list"); res.push(generate_addons_error("Missing properties \"version\"", addon)); continue; } if (!manifest.placement && !Array.isArray(manifest.placement)) { - error(`| Missing properties "placement" in ${addon} ! Plugin can't load, skipping...`, String((new Error()).stack!)); + error(`| Missing properties "placement" in ${addon} ! Plugin can't load, skipping...`, "get_addons_list"); res.push(generate_addons_error("Missing properties \"placement\"", addon)); continue; } if (manifest.placement.length === 0) { - error(`| Empty placement in ${addon} ! You must have 1 placement ! Plugin can't load, skipping...`, String((new Error()).stack!)); + error(`| Empty placement in ${addon} ! You must have 1 placement ! Plugin can't load, skipping...`, "get_addons_list"); res.push(generate_addons_error("Empty placement", addon)); continue; } if (!manifest.placement.every((p: AddonPlacement) => typeof p.placement === "string" && typeof p.main === "string")) { - error(`| Invalid placement in ${addon} ! Plugin can't load, skipping...`, String((new Error()).stack!)); + error(`| Invalid placement in ${addon} ! Plugin can't load, skipping...`, "get_addons_list"); res.push(generate_addons_error("Invalid placement", addon)); continue; } if (!manifest.permissions && !Array.isArray(manifest.permissions)) { - error(`| Missing properties "permissions" in ${addon} ! Plugin can't load, skipping...`, String((new Error()).stack!)); + error(`| Missing properties "permissions" in ${addon} ! Plugin can't load, skipping...`, "get_addons_list"); res.push(generate_addons_error("Missing properties \"permissions\"", addon)); continue; } if (!manifest.domains && !Array.isArray(manifest.domains)) { - error(`| Missing properties "domains" in ${addon} ! Plugin can't load, skipping...`, String((new Error()).stack!)); + error(`| Missing properties "domains" in ${addon} ! Plugin can't load, skipping...`, "get_addons_list"); res.push(generate_addons_error("Missing properties \"domains\"", addon)); continue; } // if icon is defined, check if it's a string and if it exists if (manifest.icon && typeof manifest.icon !== "string") { - error(`| Invalid icon in ${addon} ! Plugin can't load, skipping...`, String((new Error()).stack!)); + error(`| Invalid icon in ${addon} ! Plugin can't load, skipping...`, "get_addons_list"); res.push(generate_addons_error("Invalid icon", addon)); continue; } if (manifest.icon) { let icon = await FileSystem.getInfoAsync(FileSystem.documentDirectory + "addons/" + addon + "/" + manifest.icon); if (!icon.exists) { - error(`| Icon not found for ${addon} ! Are you sure there is a file at ${addon + "/" + manifest.icon } ! Plugin can't load, skipping...`, String((new Error()).stack!)); + error(`| Icon not found for ${addon} ! Are you sure there is a file at ${addon + "/" + manifest.icon } ! Plugin can't load, skipping...`, "get_addons_list"); res.push(generate_addons_error("Icon not found", addon)); continue; } @@ -117,20 +117,20 @@ async function get_addons_list (): Promise { // check if screenshot is an array of strings and if they exists if (manifest.screenshot && !Array.isArray(manifest.screenshot)) { - error(`| Invalid screenshot in ${addon} ! Plugin can't load, skipping...`, String((new Error()).stack!)); + error(`| Invalid screenshot in ${addon} ! Plugin can't load, skipping...`, "get_addons_list"); res.push(generate_addons_error("Invalid screenshot", addon)); continue; } if (manifest.screenshot) { for (let i = 0; i < manifest.screenshot.length; i++) { if (typeof manifest.screenshot[i] !== "string") { - error(`| Invalid screenshot in ${addon} ! Plugin can't load, skipping...`, String((new Error()).stack!)); + error(`| Invalid screenshot in ${addon} ! Plugin can't load, skipping...`, "get_addons_list"); res.push(generate_addons_error("Invalid screenshot", addon)); continue; } let screen = await FileSystem.getInfoAsync(FileSystem.documentDirectory + "addons/" + addon + "/" + manifest.screenshot[i]); if (!screen.exists) { - error(`| Screenshot not found for ${addon} ! Are you sure there is a file at ${addon + "/" + manifest.screenshot[i] } ! Plugin can't load, skipping...`, String((new Error()).stack!)); + error(`| Screenshot not found for ${addon} ! Are you sure there is a file at ${addon + "/" + manifest.screenshot[i] } ! Plugin can't load, skipping...`, "get_addons_list"); res.push(generate_addons_error("Screenshot not found", addon)); continue; } @@ -140,32 +140,32 @@ async function get_addons_list (): Promise { // check if development is a boolean if (manifest.development && typeof manifest.development !== "boolean") { - error(`| Invalid development in ${addon} ! Must be true or false ! Plugin can't load, skipping...`, String((new Error()).stack!)); + error(`| Invalid development in ${addon} ! Must be true or false ! Plugin can't load, skipping...`, "get_addons_list"); res.push(generate_addons_error("Invalid development", addon)); continue; } // check if minAppVersion is a string if (manifest.minAppVersion && typeof manifest.minAppVersion !== "string") { - error(`| Invalid minAppVersion in ${addon} ! Must be a string ! Plugin can't load, skipping...`, String((new Error()).stack!)); + error(`| Invalid minAppVersion in ${addon} ! Must be a string ! Plugin can't load, skipping...`, "get_addons_list"); res.push(generate_addons_error("Invalid minAppVersion", addon)); continue; } // check if license is a string if (manifest.license && typeof manifest.license !== "string") { - error(`| Invalid license in ${addon} ! Must be a string ! Plugin can't load, skipping...`, String((new Error()).stack!)); + error(`| Invalid license in ${addon} ! Must be a string ! Plugin can't load, skipping...`, "get_addons_list"); res.push(generate_addons_error("Invalid license", addon)); continue; } // check if description is a string if (manifest.description && typeof manifest.description !== "string") { - error(`| Invalid description in ${addon} ! Must be a string ! Plugin can't load, skipping...`, String((new Error()).stack!)); + error(`| Invalid description in ${addon} ! Must be a string ! Plugin can't load, skipping...`, "get_addons_list"); res.push(generate_addons_error("Invalid description", addon)); continue; } // check if placement is an array of AddonPlacement if (!manifest.placement.every((p: AddonPlacement) => typeof p.placement === "string" && typeof p.main === "string")) { - error(`| Invalid placement in ${addon} ! Plugin can't load, skipping...`, String((new Error()).stack!)); + error(`| Invalid placement in ${addon} ! Plugin can't load, skipping...`, "get_addons_list"); res.push(generate_addons_error("Invalid placement", addon)); continue; } @@ -176,23 +176,23 @@ async function get_addons_list (): Promise { // check if permissions is an array of AddonPermission if (!manifest.permissions.every((p: AddonPermission) => typeof p.name === "string" && typeof p.reason === "string")) { - error(`| Invalid permissions in ${addon} ! Plugin can't load, skipping...`, String((new Error()).stack!)); + error(`| Invalid permissions in ${addon} ! Plugin can't load, skipping...`, "get_addons_list"); res.push(generate_addons_error("Invalid permissions", addon)); continue; } // check if domains is an array of AddonDomain if (!manifest.domains.every((d: AddonDomain) => typeof d.domain === "string" && typeof d.reason === "string")) { - error(`| Invalid domains in ${addon} ! Plugin can't load, skipping...`, String((new Error()).stack!)); + error(`| Invalid domains in ${addon} ! Plugin can't load, skipping...`, "get_addons_list"); res.push(generate_addons_error("Invalid domains", addon)); continue; } // add the addon to the list - log(`| ${addon} is a valid addon !`, String((new Error()).stack!)); + log(`| ${addon} is a valid addon !`, "get_addons_list"); res.push(manifest as AddonManifest); } catch (e) { - error(`| Invalid JSON in ${addon} ! Plugin can't load, skipping...`, String((new Error()).stack!)); + error(`| Invalid JSON in ${addon} ! Plugin can't load, skipping...`, "get_addons_list"); res.push(generate_addons_error("Invalid JSON", addon)); } } @@ -220,4 +220,4 @@ async function get_settings_widgets (): Promise { return res; } -export { init_addons_folder, get_addons_list, get_home_widgets, get_settings_widgets }; \ No newline at end of file +export { init_addons_folder, get_addons_list, get_home_widgets, get_settings_widgets }; From f2fccfb6be029bc33833500c762b00b106911e9c Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 25 Jan 2025 12:16:14 +0100 Subject: [PATCH 0345/1144] fix: `[switchTo]` => `switchTo` --- src/stores/account/index.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index 77e9fc695..5729d02ad 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -83,12 +83,12 @@ export const useCurrentAccount = create()((set, get) => ({ // Account is currently not authenticated, if (typeof account.instance === "undefined") { - log("instance undefined, reloading...", "[switchTo]"); + log("instance undefined, reloading...", "switchTo"); // Automatically reconnect the main instance. const { instance, authentication } = await reload(account); get().mutateProperty("authentication", authentication); get().mutateProperty("instance", instance); - log("instance reload done !", "[switchTo]"); + log("instance reload done !", "switchTo"); } const accounts = useAccounts.getState().accounts; @@ -102,13 +102,13 @@ export const useCurrentAccount = create()((set, get) => ({ const { instance, authentication } = await reload(linkedAccount); linkedAccount.instance = instance; linkedAccount.authentication = authentication; - log("reloaded external", "[switchTo]"); + log("reloaded external", "switchTo"); } - log("reloaded all external accounts", "[switchTo]"); + log("reloaded all external accounts", "switchTo"); set({ linkedAccounts }); - log(`done reading ${account.name} and rehydrating stores.`, "[switchTo]"); + log(`done reading ${account.name} and rehydrating stores.`, "switchTo"); }, linkExistingExternalAccount: (account) => { From be4490990804396c1b675f6174e38df06710af19 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 25 Jan 2025 12:17:09 +0100 Subject: [PATCH 0346/1144] feat(logger): make log type the same length: `5` --- src/utils/logger/logger.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/logger/logger.ts b/src/utils/logger/logger.ts index f9c5a247f..122dbbb73 100644 --- a/src/utils/logger/logger.ts +++ b/src/utils/logger/logger.ts @@ -17,7 +17,7 @@ export function get_iso_date () { function get_message (type: number, date: string, from: string, message: string): string { return (format - .replaceAll("%TYPE%", type_list[type]) + .replaceAll("%TYPE%", type_list[type].padEnd(5)) .replaceAll("%DATE%", date) .replaceAll("%FROM%", from) .replaceAll("%MESSAGE%", message) From fd50a9825de0131f51cfbf307ed77734d1470921 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 25 Jan 2025 12:17:54 +0100 Subject: [PATCH 0347/1144] feat(logger): better detection normaly, there is, now, no `UNKOWN` --- src/utils/logger/logger.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/logger/logger.ts b/src/utils/logger/logger.ts index 122dbbb73..920095081 100644 --- a/src/utils/logger/logger.ts +++ b/src/utils/logger/logger.ts @@ -37,9 +37,9 @@ function obtain_function_name (from?: string): string { // Extraire le nom de la fonction ou utiliser `from` si on trouve pas let functionName = (relevantLine && RegExp(/at (\S+)\s\(/).exec(relevantLine)?.[1]) ?? from; // `anon` cherche à matcher avec `anonymous` et `?anon_0_` qui sont des fonctions anonymes - if (!functionName || functionName.includes("anon")) functionName = "UNKOWN"; + if (functionName?.includes("anon_0_") || functionName?.includes("anonymous")) functionName = ""; - return functionName; + return functionName || (from ?? "UNKOWN"); } function save_logs_to_memory (log: string) From 52f44989ca7ef31895d2fcbd0208151469022fb3 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 25 Jan 2025 14:11:12 +0100 Subject: [PATCH 0348/1144] feat(lint): unused vars --- eslint.config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/eslint.config.js b/eslint.config.js index 9c3af5f88..9c469f374 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -37,7 +37,8 @@ module.exports = [ "@stylistic/quotes": ["error", "double"], "@stylistic/semi": ["error", "always"], "@stylistic/space-before-function-paren": ["error", "always"], - "unused-imports/no-unused-imports": "error" + "unused-imports/no-unused-imports": "error", + "no-unused-vars": "error" } } ]; From f6166de2202f25ce2facd16baa67b66dad460ccc Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 25 Jan 2025 14:11:37 +0100 Subject: [PATCH 0349/1144] feat(tsconfig): add `jsx` option --- tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/tsconfig.json b/tsconfig.json index 27c53057d..79ffede57 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "expo/tsconfig.base", "compilerOptions": { + "jsx": "react-native", "module": "ES2020", "strict": true, "baseUrl": ".", From 7a50028558958c3617e61b32d5d3a24d9e13cc3d Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 25 Jan 2025 14:12:24 +0100 Subject: [PATCH 0350/1144] fix: lint --- src/components/Global/PapillonPicker.tsx | 12 +++++++----- src/views/account/Grades/Subject/Subject.tsx | 8 ++++++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/components/Global/PapillonPicker.tsx b/src/components/Global/PapillonPicker.tsx index 93777c422..0144790ca 100644 --- a/src/components/Global/PapillonPicker.tsx +++ b/src/components/Global/PapillonPicker.tsx @@ -10,17 +10,19 @@ import { NativeText } from "./NativeComponents"; import { BlurView } from "expo-blur"; import { Check } from "lucide-react-native"; -type PickerData = string[] | { label: string, icon?: JSX.Element, onPress: () => unknown, checked?: boolean }[]; +export type PickerDataItem = string | { label: string, icon?: JSX.Element, onPress?: () => unknown, checked?: boolean }; + +type PickerData = PickerDataItem[]; interface PapillonPickerProps { children: React.ReactNode data: PickerData - selected?: string + selected?: PickerDataItem contentContainerStyle?: StyleProp>> delay?: number, direction?: "left" | "right", animated?: boolean, - onSelectionChange?: (item: string) => unknown + onSelectionChange?: any } const PapillonPicker: React.FC = ({ @@ -37,7 +39,7 @@ const PapillonPicker: React.FC = ({ const [contentHeight, setContentHeight] = useState(0); const [opened, setOpened] = useState(false); - const handleSelectionChange = (item: string) => { + const handleSelectionChange = (item: PickerDataItem) => { if (onSelectionChange) { setTimeout(() => { onSelectionChange(item); @@ -115,7 +117,7 @@ const PapillonPicker: React.FC = ({ onPressItem(); } : () => { setOpened(false); - handleSelectionChange(item as string); + handleSelectionChange(item); }} style={[ styles.item diff --git a/src/views/account/Grades/Subject/Subject.tsx b/src/views/account/Grades/Subject/Subject.tsx index a7b97dd21..0a2173a00 100644 --- a/src/views/account/Grades/Subject/Subject.tsx +++ b/src/views/account/Grades/Subject/Subject.tsx @@ -109,7 +109,11 @@ const Subject: React.FC = ({ textTransform: "uppercase", }} > - {sortings[sorting].label} + { + typeof sortings[sorting] === "string" + ? sortings[sorting] + : sortings[sorting].label + } {isLoading && ( = ({ ); }; -export default Subject; \ No newline at end of file +export default Subject; From 49143975601e124b381fa11ec6d549e5314ee499 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 25 Jan 2025 14:36:45 +0100 Subject: [PATCH 0351/1144] fix(lint): ignore func args for unused var --- eslint.config.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/eslint.config.js b/eslint.config.js index 9c469f374..a45815615 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -38,7 +38,9 @@ module.exports = [ "@stylistic/semi": ["error", "always"], "@stylistic/space-before-function-paren": ["error", "always"], "unused-imports/no-unused-imports": "error", - "no-unused-vars": "error" + "no-unused-vars": ["error", { + "args": "none" + }] } } ]; From e4e777cd3edf77bc85f7386a2e4d06bddd3cf587 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 25 Jan 2025 14:37:25 +0100 Subject: [PATCH 0352/1144] lint: remove unused variables --- .../FirstInstallation/PapillonShineBubble.tsx | 4 +- .../Global/PapillonModernHeader.tsx | 3 -- src/components/Home/AccountSwitcher.tsx | 41 +------------- src/components/Home/Header.tsx | 5 +- .../ExternalServicesContainerCard.tsx | 3 +- .../Settings/IconsContainerCard.tsx | 3 +- .../Settings/MagicContainerCard.tsx | 3 +- src/components/Settings/ReelGallery.tsx | 8 +-- src/router/navigator/menu.tsx | 7 +-- .../local/utils/reduceIcalToCourse.ts | 4 +- src/services/menu.ts | 4 +- src/services/skolengo/data/grades.ts | 8 --- src/utils/magic/categorizeHomeworks.ts | 8 +-- src/views/account/Attendance/Attendance.tsx | 6 +-- src/views/account/Evaluation/Document.tsx | 6 +-- .../account/Grades/Graph/GradesAverage.tsx | 6 +-- src/views/account/Home/Home.tsx | 43 +++------------ src/views/account/Home/PlaceholderScreen.tsx | 14 +---- src/views/account/Homeworks/Homeworks.tsx | 29 ++-------- .../account/Homeworks/HomeworksHeader.tsx | 4 +- .../Lessons/Atoms/LessonsDatePicker.tsx | 9 ++-- .../Lessons/Options/LessonsImportIcal.tsx | 9 +--- src/views/account/News/Document.tsx | 21 ++------ src/views/account/News/News.tsx | 6 +-- .../BackgroundIdentityProvider.tsx | 4 +- .../providers/UnivIUTLannion.tsx | 5 +- .../providers/UnivSorbonneParisNord.tsx | 3 +- src/views/login/ServiceSelector.tsx | 12 +---- .../login/pronote/PronoteCredentials.tsx | 54 ------------------- .../login/pronote/PronoteManualLocation.tsx | 3 -- .../skolengo/SkolengoInstanceSelector.tsx | 10 ---- src/views/login/skolengo/SkolengoWebview.tsx | 7 ++- .../settings/ExternalAccount/PriceError.tsx | 13 ++--- .../ExternalAccount/QrcodeScanner.tsx | 15 ++---- .../ExternalAccount/ServiceSelector.tsx | 10 +--- src/views/settings/SettingsAbout.tsx | 37 +------------ src/views/settings/SettingsFlags.tsx | 4 +- src/views/settings/SettingsMagic.tsx | 3 -- src/views/settings/SettingsProfile.tsx | 6 +-- src/views/settings/SettingsReactions.tsx | 4 +- src/views/settings/SettingsTabs.tsx | 12 +---- src/views/settings/SettingsTrophies.tsx | 38 +------------ src/views/welcome/ChangelogScreen.tsx | 2 - src/views/welcome/FirstInstallation.tsx | 6 --- src/widgets/Components/RestaurantBalance.tsx | 1 - src/widgets/Components/RestaurantQRCode.tsx | 4 +- 46 files changed, 71 insertions(+), 436 deletions(-) diff --git a/src/components/FirstInstallation/PapillonShineBubble.tsx b/src/components/FirstInstallation/PapillonShineBubble.tsx index 24e908b86..9e2292753 100644 --- a/src/components/FirstInstallation/PapillonShineBubble.tsx +++ b/src/components/FirstInstallation/PapillonShineBubble.tsx @@ -74,12 +74,10 @@ const PapillonShineBubble: React.FC<{ bubbleScale.value = withSpring(1, { damping: 15, stiffness: 150 }); }, []); - const [sHeight, setSHeight] = React.useState(Dimensions.get("window").height / Dimensions.get("window").scale); const [sWidth, setSWidth] = React.useState(Dimensions.get("window").width / Dimensions.get("window").scale); useEffect(() => { Dimensions.addEventListener("change", ({ window }) => { - setSHeight(window.height / window.scale); setSWidth(window.width / window.scale); }); }, []); @@ -213,4 +211,4 @@ const papillon_ls_styles = StyleSheet.create({ } }); -export default PapillonShineBubble; \ No newline at end of file +export default PapillonShineBubble; diff --git a/src/components/Global/PapillonModernHeader.tsx b/src/components/Global/PapillonModernHeader.tsx index 9e2303fdf..3df4adf27 100644 --- a/src/components/Global/PapillonModernHeader.tsx +++ b/src/components/Global/PapillonModernHeader.tsx @@ -20,9 +20,6 @@ interface ModernHeaderProps { }; export const PapillonModernHeader: React.FC = (props) => { - const theme = useTheme(); - const insets = useSafeAreaInsets(); - if (props.native) { return ( diff --git a/src/components/Home/AccountSwitcher.tsx b/src/components/Home/AccountSwitcher.tsx index ab3ba3a9c..f7ba4bed7 100644 --- a/src/components/Home/AccountSwitcher.tsx +++ b/src/components/Home/AccountSwitcher.tsx @@ -4,10 +4,8 @@ import { useTheme } from "@react-navigation/native"; import { ChevronDown } from "lucide-react-native"; import Reanimated, { - interpolateColor, - LinearTransition, useAnimatedStyle, - ZoomIn, - ZoomOut, + LinearTransition, ZoomIn, + ZoomOut } from "react-native-reanimated"; import { useCurrentAccount } from "@/stores/account"; @@ -34,42 +32,7 @@ const AccountSwitcher: React.FC<{ const shouldHideName = account.personalization.hideNameOnHomeScreen || false; const shouldHidePicture = account.personalization.hideProfilePicOnHomeScreen || false; - const borderAnimatedStyle = useAnimatedStyle(() => ({ - borderWidth: 1, - borderRadius: 80, - borderColor: interpolateColor( - translationY?.value || 0, // Should think to pass a default value - [200, 251], - ["#ffffff50", colors.border], - ), - backgroundColor: interpolateColor( - translationY?.value || 0, // Should think to pass a default value - [200, 251], - ["#ffffff30", "transparent"], - ), - })); - - const textAnimatedStyle = useAnimatedStyle(() => ({ - color: interpolateColor( - translationY?.value || 0, // Should think to pass a default value - [200, 251], - ["#FFF", colors.text], - ), - fontSize: 16, - fontFamily: "semibold", - maxWidth: 140, - })); - - const AnimatedChevronDown = Animated.createAnimatedComponent(ChevronDown); - const iconAnimatedStyle = useAnimatedStyle(() => ({ - color: interpolateColor( - translationY?.value || 0, // Should think to pass a default value - [200, 251], - ["#FFF", colors.text], - ), - marginLeft: -6, - })); return ( void }> = ({icon, index, text, scrolled, onPress}) => { - const theme = useTheme(); - const { colors } = theme; const newIcon = React.cloneElement(icon, { size: 24, @@ -434,4 +431,4 @@ const styles = StyleSheet.create({ }, }); -export default Header; \ No newline at end of file +export default Header; diff --git a/src/components/Settings/ExternalServicesContainerCard.tsx b/src/components/Settings/ExternalServicesContainerCard.tsx index 4f9748b45..2e186f369 100644 --- a/src/components/Settings/ExternalServicesContainerCard.tsx +++ b/src/components/Settings/ExternalServicesContainerCard.tsx @@ -5,7 +5,6 @@ import LottieView from "lottie-react-native"; import { NativeItem, NativeList, NativeText } from "../Global/NativeComponents"; const ExternalServicesContainerCard = ({ theme }: { theme: any }) => { - const { colors } = theme; return ( @@ -39,4 +38,4 @@ const ExternalServicesContainerCard = ({ theme }: { theme: any }) => { ); }; -export default ExternalServicesContainerCard; \ No newline at end of file +export default ExternalServicesContainerCard; diff --git a/src/components/Settings/IconsContainerCard.tsx b/src/components/Settings/IconsContainerCard.tsx index 09860aad2..d16cf5ebc 100644 --- a/src/components/Settings/IconsContainerCard.tsx +++ b/src/components/Settings/IconsContainerCard.tsx @@ -5,7 +5,6 @@ import LottieView from "lottie-react-native"; import { NativeItem, NativeList, NativeText } from "../Global/NativeComponents"; const IconsContainerCard = ({ theme }: { theme: any }) => { - const { colors } = theme; const animationref = React.useRef(null); useEffect(() => { @@ -43,4 +42,4 @@ const IconsContainerCard = ({ theme }: { theme: any }) => { ); }; -export default IconsContainerCard; \ No newline at end of file +export default IconsContainerCard; diff --git a/src/components/Settings/MagicContainerCard.tsx b/src/components/Settings/MagicContainerCard.tsx index c0f3548c7..679570676 100644 --- a/src/components/Settings/MagicContainerCard.tsx +++ b/src/components/Settings/MagicContainerCard.tsx @@ -7,7 +7,6 @@ import { LinearGradient } from "expo-linear-gradient"; import BetaIndicator from "../News/Beta"; const MagicContainerCard = ({ theme }: { theme: any }) => { - const { colors } = theme; const animationref = React.useRef(null); useEffect(() => { @@ -57,4 +56,4 @@ const MagicContainerCard = ({ theme }: { theme: any }) => { ); }; -export default MagicContainerCard; \ No newline at end of file +export default MagicContainerCard; diff --git a/src/components/Settings/ReelGallery.tsx b/src/components/Settings/ReelGallery.tsx index deeceff85..c8c815e83 100644 --- a/src/components/Settings/ReelGallery.tsx +++ b/src/components/Settings/ReelGallery.tsx @@ -13,12 +13,6 @@ import { useGradesStore } from "@/stores/grades"; import GradeModal from "../Grades/GradeModal"; import { Reel } from "@/services/shared/Reel"; -interface ReelModalProps { - reel: Reel; - visible: boolean; - onClose: () => void; -} - // Components const GradeIndicator = ({ value, outOf, color }: { value: number; outOf: number; color: string }) => ( @@ -199,4 +193,4 @@ const styles = StyleSheet.create({ }, }); -export default ReelGallery; \ No newline at end of file +export default ReelGallery; diff --git a/src/router/navigator/menu.tsx b/src/router/navigator/menu.tsx index 12082b86c..d334f135c 100644 --- a/src/router/navigator/menu.tsx +++ b/src/router/navigator/menu.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useState } from "react"; +import React, { useMemo } from "react"; import { useCurrentAccount } from "@/stores/account"; import { useNavigationBuilder, useTheme } from "@react-navigation/native"; import { StyleSheet, Platform, Image, Text, StatusBar } from "react-native"; @@ -19,8 +19,6 @@ const PapillonNavigatorMenu: React.FC allTabs.find((route) => route.name === tab.name)) .filter(Boolean) || allTabs; - const [shouldOpenContextMenu, setShouldOpenContextMenu] = useState(false); - return ( (account: Account, date: Date): Promise
{ +export async function getMenu (account: Account, date: Date): Promise { switch (account.service) { case AccountService.Pronote: { const { getMenu } = await import("./pronote/menu"); @@ -12,4 +12,4 @@ export async function getMenu (account: Account, date: Date) return null; } } -} \ No newline at end of file +} diff --git a/src/services/skolengo/data/grades.ts b/src/services/skolengo/data/grades.ts index 805fe7d6a..3fe517f36 100644 --- a/src/services/skolengo/data/grades.ts +++ b/src/services/skolengo/data/grades.ts @@ -20,14 +20,6 @@ const getSubjectMinMax = (evalSubj: Evaluation): {min: GradeValue, max:GradeValu return {min: { value: minimum, disabled: false, status: null } , max: { value: maximum, disabled: false, status: null }, outOf}; }; -const getOverall = (evals: Evaluation[]): GradeValue =>{ - if(evals.filter(e=>e.average !== null).length === 0) - return { value: null, disabled: true, status: null }; - const sum = evals.filter(e=>e.average !== null).reduce((acc, e) => acc + (e.average! * (e.coefficient || 1)), 0); - const sumCoef = evals.filter(e=>e.average !== null).reduce((acc, e) => acc + (e.coefficient || 1), 0); - return { value: sum / sumCoef, disabled: false, status: null }; -}; - export const getGradesAndAverages = async (account: SkolengoAccount, periodName: string): Promise<{ grades: Grade[], averages: AverageOverview diff --git a/src/utils/magic/categorizeHomeworks.ts b/src/utils/magic/categorizeHomeworks.ts index 31b1afe9f..6deb26b9a 100644 --- a/src/utils/magic/categorizeHomeworks.ts +++ b/src/utils/magic/categorizeHomeworks.ts @@ -4,13 +4,7 @@ type DetectionJson = Record; const detectionData: DetectionJson = detectionJson; -function normalizeString (input: string): string { - return input.toLowerCase().replace(/[\s-]+/g, ""); -} - function detectCategory (input: string): string | null { - const normalizedInput = normalizeString(input); - for (const [category, patterns] of Object.entries(detectionData)) { if (patterns.some(pattern => new RegExp(pattern, "i").test(input))) { return category; @@ -20,4 +14,4 @@ function detectCategory (input: string): string | null { return null; } -export default detectCategory; \ No newline at end of file +export default detectCategory; diff --git a/src/views/account/Attendance/Attendance.tsx b/src/views/account/Attendance/Attendance.tsx index bd82ccca0..cc368846f 100644 --- a/src/views/account/Attendance/Attendance.tsx +++ b/src/views/account/Attendance/Attendance.tsx @@ -1,5 +1,5 @@ import { useTheme } from "@react-navigation/native"; -import { useEffect, useMemo, useState } from "react"; +import React, { useEffect, useMemo, useState } from "react"; import { View, ActivityIndicator, Platform, RefreshControl } from "react-native"; import type { Screen } from "@/router/helpers/types"; @@ -13,13 +13,11 @@ import { ChevronDown, Eye, Scale, Timer, UserX } from "lucide-react-native"; import PapillonHeader from "@/components/Global/PapillonHeader"; import { animPapillon } from "@/utils/ui/animations"; import AttendanceItem from "./Atoms/AttendanceItem"; -import { getAbsenceTime } from "@/utils/format/attendance_time"; import TotalMissed from "./Atoms/TotalMissed"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; import { protectScreenComponent } from "@/router/helpers/protected-screen"; import { Observation } from "@/services/shared/Observation"; import MissingItem from "@/components/Global/MissingItem"; -import React from "react"; const Attendance: Screen<"Attendance"> = ({ route, navigation }) => { const theme = useTheme(); @@ -100,8 +98,6 @@ const Attendance: Screen<"Attendance"> = ({ route, navigation }) => { let totalDelayMinutes = 0; attendances[selectedPeriod]?.absences.forEach(absence => { - const missed = getAbsenceTime(absence.fromTimestamp, absence.toTimestamp); - if (!absence.justified) { totalUnJustifiedHours += parseInt(absence.hours.split("h")[0]); totalUnJustifiedMinutes += parseInt(absence.hours.split("h")[1]); diff --git a/src/views/account/Evaluation/Document.tsx b/src/views/account/Evaluation/Document.tsx index 710d16951..a430c587e 100644 --- a/src/views/account/Evaluation/Document.tsx +++ b/src/views/account/Evaluation/Document.tsx @@ -14,11 +14,11 @@ import { } from "lucide-react-native"; import { Screen } from "@/router/helpers/types"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; -import {Skill} from "@/services/shared/Evaluation"; -import {SkillLevelBadge} from "@/views/account/Evaluation/Atoms/SkillLevelBadge"; +import { Skill } from "@/services/shared/Evaluation"; +import { SkillLevelBadge } from "@/views/account/Evaluation/Atoms/SkillLevelBadge"; const EvaluationDocument: Screen<"EvaluationDocument"> = ({ route, navigation }) => { - const { evaluation, allEvaluations = [] } = route.params; + const { evaluation } = route.params; const theme = useTheme(); const [subjectData, setSubjectData] = useState({ diff --git a/src/views/account/Grades/Graph/GradesAverage.tsx b/src/views/account/Grades/Graph/GradesAverage.tsx index f6e96a7d0..680b886c4 100644 --- a/src/views/account/Grades/Graph/GradesAverage.tsx +++ b/src/views/account/Grades/Graph/GradesAverage.tsx @@ -26,13 +26,13 @@ import { animPapillon } from "@/utils/ui/animations"; import * as Haptics from "expo-haptics"; import { PressableScale } from "react-native-pressable-scale"; import { ReanimatedGraphProps, ReanimatedGraphPublicMethods } from "@birdwingo/react-native-reanimated-graph/src/core/dto/graphDTO"; -// Using require to set custom types bc module types are broken -const ReanimatedGraph: React.ForwardRefExoticComponent> = require("@birdwingo/react-native-reanimated-graph").default; import { useCurrentAccount } from "@/stores/account"; import AnimatedNumber from "@/components/Global/AnimatedNumber"; import type { Grade } from "@/services/shared/Grade"; import { AlertTriangle, Check, ExternalLink, PieChart, TrendingUp } from "lucide-react-native"; import { useAlert } from "@/providers/AlertProvider"; +// Using require to set custom types bc module types are broken +const ReanimatedGraph: React.ForwardRefExoticComponent> = require("@birdwingo/react-native-reanimated-graph").default; interface GradesAverageGraphProps { grades: Grade[]; @@ -85,8 +85,6 @@ const GradesAverageGraph: React.FC = ({ let maxAvg = getPronoteAverage(grades, "max"); let minAvg = getPronoteAverage(grades, "min"); - const finalAvg = getPronoteAverage(grades, "student"); - setGradesHistory(hst); setHLength(hst.length); diff --git a/src/views/account/Home/Home.tsx b/src/views/account/Home/Home.tsx index 7371485af..3c3d4625d 100644 --- a/src/views/account/Home/Home.tsx +++ b/src/views/account/Home/Home.tsx @@ -31,12 +31,12 @@ // | | // +——————————————————————————————————————————————————————————+ -import {protectScreenComponent} from "@/router/helpers/protected-screen"; -import type {Screen} from "@/router/helpers/types"; -import {useCurrentAccount} from "@/stores/account"; +import { protectScreenComponent } from "@/router/helpers/protected-screen"; +import type { Screen } from "@/router/helpers/types"; +import { useCurrentAccount } from "@/stores/account"; import getCorners from "@/utils/ui/corner-radius"; -import {useIsFocused, useTheme} from "@react-navigation/native"; -import React, {useCallback, useMemo, useState} from "react"; +import { useIsFocused, useTheme } from "@react-navigation/native"; +import React, { useMemo, useState } from "react"; import { Dimensions, Platform, @@ -52,14 +52,14 @@ import Animated, { useAnimatedStyle, useScrollViewOffset, } from "react-native-reanimated"; -import {useSafeAreaInsets} from "react-native-safe-area-context"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; import AccountSwitcher from "@/components/Home/AccountSwitcher"; import ContextMenu from "@/components/Home/AccountSwitcherContextMenu"; import Header from "@/components/Home/Header"; -import {useBottomTabBarHeight} from "@react-navigation/bottom-tabs"; +import { useBottomTabBarHeight } from "@react-navigation/bottom-tabs"; import * as Haptics from "expo-haptics"; import ModalContent from "@/views/account/Home/ModalContent"; -import {AnimatedScrollView} from "react-native-reanimated/lib/typescript/reanimated2/component/ScrollView"; +import { AnimatedScrollView } from "react-native-reanimated/lib/typescript/reanimated2/component/ScrollView"; import useScreenDimensions from "@/hooks/useScreenDimensions"; const Home: Screen<"HomeScreen"> = ({ navigation }) => { @@ -75,21 +75,12 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { let account = useCurrentAccount(store => store.account!); - const [shouldOpenContextMenu, setShouldOpenContextMenu] = useState(false); - const [modalOpen, setModalOpen] = useState(false); const [modalFull, setModalFull] = useState(false); const [canHaptics, setCanHaptics] = useState(true); const [refreshing, setRefreshing] = useState(false); - const openAccSwitcher = useCallback(() => { - setShouldOpenContextMenu(false); - setTimeout(() => { - setShouldOpenContextMenu(true); - }, 150); - }, []); - const windowHeight = Dimensions.get("window").height; const tabbarHeight = useBottomTabBarHeight(); @@ -151,23 +142,6 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { ], })); - const navigationBarAnimatedStyle = useAnimatedStyle(() => ({ - position: "absolute", - top: scrollOffset.value - 270 - insets.top, - left: 0, - right: 0, - height: interpolate( - scrollOffset.value, - [125, 265], - [0, insets.top + 60], - Extrapolation.CLAMP - ), - zIndex: 100, - backgroundColor: colors.background, - borderColor: colors.border, - borderBottomWidth: 0.5, - })); - const modalContentAnimatedStyle = useAnimatedStyle(() => ({ paddingHorizontal: 16, paddingBottom: 16 + insets.top + 56, @@ -230,7 +204,6 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { left: 16, zIndex: 1000, }]} - shouldOpenContextMenu={shouldOpenContextMenu} > = ({ route, navigation }) => { const theme = useTheme(); @@ -14,16 +14,6 @@ const PlaceholderScreen: Screen<"Discussions" | "Menu"> = ({ route, navigation } }); }, [navigation, route.params, theme.colors.text]); - const [isFocused, setIsFocused] = React.useState(false); - - useEffect(() => { - const unsubscribe = navigation.addListener("focus", () => { - setIsFocused(true); - }); - - return unsubscribe; - }, [navigation]); - return ( ; - account: Account; - updateHomeworks: () => Promise; - loading: boolean; - getDayName: (date: string | number | Date) => string; -}; - const formatDate = (date: string | number | Date): string => { return new Date(date).toLocaleDateString("fr-FR", { day: "numeric", @@ -96,8 +84,6 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const [data, setData] = useState(Array.from({ length: 100 }, (_, i) => currentWeek - 50 + i)); const [selectedWeek, setSelectedWeek] = useState(currentWeek); - const [direction, setDirection] = useState<"left" | "right">("right"); - const [oldSelectedWeek, setOldSelectedWeek] = useState(selectedWeek); const [hideDone, setHideDone] = useState(false); @@ -142,14 +128,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { // on page change, load the homeworks useEffect(() => { - if (selectedWeek > oldSelectedWeek) { - setDirection("right"); - } else if (selectedWeek < oldSelectedWeek) { - setDirection("left"); - } - setTimeout(() => { - setOldSelectedWeek(selectedWeek); updateHomeworks(false, false); }, 0); }, [selectedWeek]); diff --git a/src/views/account/Homeworks/HomeworksHeader.tsx b/src/views/account/Homeworks/HomeworksHeader.tsx index 8c0ce1ef0..043e78719 100644 --- a/src/views/account/Homeworks/HomeworksHeader.tsx +++ b/src/views/account/Homeworks/HomeworksHeader.tsx @@ -11,8 +11,6 @@ import Reanimated, { import { epochWMToCalendarWeekNumber } from "@/utils/epochWeekNumber"; const HeaderCalendar: React.FC<{ epochWeekNumber: number, oldPageIndex: number, showPicker: () => void, changeIndex: (index: number) => void }> = ({ epochWeekNumber, oldPageIndex, showPicker, changeIndex }) => { - const { colors } = useTheme(); - const dims = Dimensions.get("window"); const tablet = dims.width > 600; @@ -147,4 +145,4 @@ const HeaderWeekComponent: React.FC<{ epochWeekNumber: number, active: boolean, ); }; -export { HeaderCalendar }; \ No newline at end of file +export { HeaderCalendar }; diff --git a/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx b/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx index bc8e48d53..7552fd455 100644 --- a/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx +++ b/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx @@ -1,5 +1,5 @@ import React, { useState, useRef, useEffect, useCallback } from "react"; -import {View, Text, StyleSheet, TouchableOpacity, Dimensions, FlatList, ListRenderItem} from "react-native"; +import { View, Text, StyleSheet, TouchableOpacity, Dimensions, FlatList, ListRenderItem } from "react-native"; import { format, addDays, isSameDay } from "date-fns"; import { fr } from "date-fns/locale"; import { useTheme } from "@react-navigation/native"; @@ -14,9 +14,9 @@ import Animated, { runOnJS, SharedValue, } from "react-native-reanimated"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import {Theme} from "@react-navigation/native/src/types"; -import {NativeScrollEvent, ScrollViewProps} from "react-native/Libraries/Components/ScrollView/ScrollView"; -import {NativeSyntheticEvent} from "react-native/Libraries/Types/CoreEventTypes"; +import { Theme } from "@react-navigation/native/src/types"; +import { NativeScrollEvent, ScrollViewProps } from "react-native/Libraries/Components/ScrollView/ScrollView"; +import { NativeSyntheticEvent } from "react-native/Libraries/Types/CoreEventTypes"; const { width: SCREEN_WIDTH } = Dimensions.get("window"); const ITEM_WIDTH = 104; @@ -24,7 +24,6 @@ const ITEM_MARGIN = 10; const ITEM_TOTAL_WIDTH = ITEM_WIDTH + ITEM_MARGIN * 2; const DATE_RANGE = 30; const SCROLL_THRESHOLD = 7; -const SCROLL_VELOCITY = 100; const generateDateRange = (centerDate: Date) => { return Array.from({ length: DATE_RANGE }, (_, i) => addDays(centerDate, i - Math.floor(DATE_RANGE / 2))); diff --git a/src/views/account/Lessons/Options/LessonsImportIcal.tsx b/src/views/account/Lessons/Options/LessonsImportIcal.tsx index 86edf258e..cfd88d9a5 100644 --- a/src/views/account/Lessons/Options/LessonsImportIcal.tsx +++ b/src/views/account/Lessons/Options/LessonsImportIcal.tsx @@ -11,27 +11,22 @@ import { ScrollView } from "react-native-gesture-handler"; import * as Clipboard from "expo-clipboard"; import { CameraView } from "expo-camera"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { fetchIcalData } from "@/services/local/ical"; -import {Screen} from "@/router/helpers/types"; +import { Screen } from "@/router/helpers/types"; const ical = require("cal-parser"); const LessonsImportIcal: Screen<"LessonsImportIcal"> = ({ route, navigation }) => { const theme = useTheme(); - const insets = useSafeAreaInsets(); const defaultIcal = route.params?.ical || ""; - const defaultTitle = route.params?.title || ""; const autoAdd = route.params?.autoAdd || false; const account = useCurrentAccount(store => store.account!); - const timetables = useTimetableStore(store => store.timetables); const mutateProperty = useCurrentAccount(store => store.mutateProperty); const [url, setUrl] = React.useState(defaultIcal); - const [title, setTitle] = React.useState(defaultTitle); const [cameraVisible, setCameraVisible] = React.useState(false); @@ -74,7 +69,7 @@ const LessonsImportIcal: Screen<"LessonsImportIcal"> = ({ route, navigation }) = mutateProperty("personalization", { ...account.personalization, icalURLs: [...oldUrls, { - name: title.trim().length > 0 ? title : defaultTitle, + name: defaultTitle, url, }] }); diff --git a/src/views/account/News/Document.tsx b/src/views/account/News/Document.tsx index 46f2e42f5..37e5e03f0 100644 --- a/src/views/account/News/Document.tsx +++ b/src/views/account/News/Document.tsx @@ -16,7 +16,7 @@ import { MoreHorizontal, } from "lucide-react-native"; import React, { useEffect, useLayoutEffect, useState } from "react"; -import { View, Linking, TouchableOpacity, type GestureResponderEvent, StyleSheet } from "react-native"; +import { View, Linking, TouchableOpacity, StyleSheet } from "react-native"; import { ScrollView } from "react-native-gesture-handler"; import HTMLView from "react-native-htmlview"; import { PapillonModernHeader } from "@/components/Global/PapillonModernHeader"; @@ -65,26 +65,10 @@ const NewsItem: Screen<"NewsItem"> = ({ route, navigation }) => { })); }, [account.instance]); - const tagsStyles = { - body: { - color: theme.colors.text, - }, - a: { - color: theme.colors.primary, - textDecorationColor: theme.colors.primary, - }, - }; - - function onPress (event: GestureResponderEvent, href: string) { + function onPress (href: string) { Linking.openURL(href); } - const renderersProps = { - a: { - onPress: onPress, - }, - }; - return ( @@ -196,6 +180,7 @@ const NewsItem: Screen<"NewsItem"> = ({ route, navigation }) => { ${message.content.replaceAll("

", "").replaceAll("

", "")}`} stylesheet={stylesText} + onLinkPress={onPress} />
diff --git a/src/views/account/News/News.tsx b/src/views/account/News/News.tsx index bbe55936c..e5e29d234 100644 --- a/src/views/account/News/News.tsx +++ b/src/views/account/News/News.tsx @@ -16,8 +16,8 @@ import { categorizeMessages } from "@/utils/magic/categorizeMessages"; import TabAnimatedTitle from "@/components/Global/TabAnimatedTitle"; import { protectScreenComponent } from "@/router/helpers/protected-screen"; import MissingItem from "@/components/Global/MissingItem"; -import {Information} from "@/services/shared/Information"; -import {AccountService} from "@/stores/account/types"; +import { Information } from "@/services/shared/Information"; +import { AccountService } from "@/stores/account/types"; type NewsItem = Omit & { date: string, important: boolean }; @@ -29,7 +29,6 @@ const NewsScreen: Screen<"News"> = ({ route, navigation }) => { const [isLoading, setIsLoading] = useState(false); const [importantMessages, setImportantMessages] = useState([]); const [sortedMessages, setSortedMessages] = useState([]); - const [isED, setIsED] = useState(false); useLayoutEffect(() => { navigation.setOptions({ @@ -44,7 +43,6 @@ const NewsScreen: Screen<"News"> = ({ route, navigation }) => { }, [account]); useEffect(() => { - if (account.service === AccountService.EcoleDirecte) setIsED(true); if (sortedMessages.length === 0) { navigation.addListener("focus", () => fetchData(true)); fetchData(); diff --git a/src/views/login/IdentityProvider/BackgroundIdentityProvider.tsx b/src/views/login/IdentityProvider/BackgroundIdentityProvider.tsx index ce50a9b56..da241a964 100644 --- a/src/views/login/IdentityProvider/BackgroundIdentityProvider.tsx +++ b/src/views/login/IdentityProvider/BackgroundIdentityProvider.tsx @@ -13,7 +13,7 @@ const BackgroundIdentityProvider: Screen<"BackgroundIdentityProvider"> = ({ rout const identityProvider = account!.identityProvider; if(identityProvider) { - const { identifier, rawData } = identityProvider; + const { identifier } = identityProvider; if(identifier === "iut-lannion") { navigation.goBack(); @@ -31,4 +31,4 @@ const BackgroundIdentityProvider: Screen<"BackgroundIdentityProvider"> = ({ rout return null; }; -export default BackgroundIdentityProvider; \ No newline at end of file +export default BackgroundIdentityProvider; diff --git a/src/views/login/IdentityProvider/providers/UnivIUTLannion.tsx b/src/views/login/IdentityProvider/providers/UnivIUTLannion.tsx index 8affafc73..04aaae64d 100644 --- a/src/views/login/IdentityProvider/providers/UnivIUTLannion.tsx +++ b/src/views/login/IdentityProvider/providers/UnivIUTLannion.tsx @@ -4,7 +4,6 @@ import { View } from "react-native"; import type { Screen } from "@/router/helpers/types"; export const UnivIUTLannion_Login: Screen<"UnivIUTLannion_Login"> = ({ navigation }) => { - const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const login = async (username: string, password: string) => { @@ -17,8 +16,7 @@ export const UnivIUTLannion_Login: Screen<"UnivIUTLannion_Login"> = ({ navigatio } catch (e) { console.error(e); - // setLoading(false); - // @ts-ignore + // @ts-expect-error setError(e.toString()); } }; @@ -27,7 +25,6 @@ export const UnivIUTLannion_Login: Screen<"UnivIUTLannion_Login"> = ({ navigatio serviceName: "IUT de Lannion", serviceIcon: require("@/../assets/images/service_iutlan.png"), onLogin: login, - loading, error, }), [login]); diff --git a/src/views/login/IdentityProvider/providers/UnivSorbonneParisNord.tsx b/src/views/login/IdentityProvider/providers/UnivSorbonneParisNord.tsx index a1136e6a7..6d21f7fd2 100644 --- a/src/views/login/IdentityProvider/providers/UnivSorbonneParisNord.tsx +++ b/src/views/login/IdentityProvider/providers/UnivSorbonneParisNord.tsx @@ -61,6 +61,7 @@ const UnivSorbonneParisNord_login: Screen<"UnivSorbonneParisNord_login"> = ({ na const studentInfo = await fetchStudentInfo(studentId, token); // Exclude INE from rawData + // eslint-disable-next-line const { INE, ...safeStudentInfo } = studentInfo; const localAccount: LocalAccount = { @@ -139,4 +140,4 @@ const UnivSorbonneParisNord_login: Screen<"UnivSorbonneParisNord_login"> = ({ na ); }; -export default UnivSorbonneParisNord_login; \ No newline at end of file +export default UnivSorbonneParisNord_login; diff --git a/src/views/login/ServiceSelector.tsx b/src/views/login/ServiceSelector.tsx index beeeca622..905be61f5 100644 --- a/src/views/login/ServiceSelector.tsx +++ b/src/views/login/ServiceSelector.tsx @@ -3,13 +3,12 @@ import { Image, View, StyleSheet } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; import Reanimated, { LinearTransition, FlipInXDown } from "react-native-reanimated"; -import {RouteParameters, Screen} from "@/router/helpers/types"; +import { RouteParameters, Screen } from "@/router/helpers/types"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import MaskStars from "@/components/FirstInstallation/MaskStars"; -import { useAlert } from "@/providers/AlertProvider"; import { Audio } from "expo-av"; import { useTheme } from "@react-navigation/native"; import GetV6Data from "@/utils/login/GetV6Data"; @@ -21,8 +20,6 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { const { colors } = theme; const [sound, setSound] = useState(null); - const { showAlert } = useAlert(); - type Services = "pronote" | "ed" | "skolengo"; const [service, setService] = useState(null); @@ -85,13 +82,6 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { }, ]; - const UnsupportedAlert = () => { - showAlert({ - title: "Service non supporté", - message: "Désolé, ce service n'est pas encore supporté. Réessaye dans une prochaine version." - }); - }; - useEffect(() => { const loadSound = async () => { const { sound } = await Audio.Sound.createAsync( diff --git a/src/views/login/pronote/PronoteCredentials.tsx b/src/views/login/pronote/PronoteCredentials.tsx index 420d7566e..d2e94b071 100644 --- a/src/views/login/pronote/PronoteCredentials.tsx +++ b/src/views/login/pronote/PronoteCredentials.tsx @@ -1,6 +1,5 @@ import React, { useState } from "react"; import type { Screen } from "@/router/helpers/types"; -import { StyleSheet } from "react-native"; import pronote from "pawnote"; import uuid from "@/utils/uuid-v4"; @@ -110,57 +109,4 @@ const PronoteCredentials: Screen<"PronoteCredentials"> = ({ route, navigation }) ); }; -const styles = StyleSheet.create({ - container: { - flex: 1, - padding: 16, - alignItems: "center", - }, - - serviceContainer: { - alignItems: "center", - marginBottom: 20, - gap: 4, - }, - - serviceLogo: { - width: 48, - height: 48, - borderRadius: 12, - borderCurve: "continuous", - marginBottom: 10, - }, - - serviceName: { - fontSize: 15, - fontFamily: "medium", - opacity: 0.6, - textAlign: "center", - }, - - serviceSchool: { - fontSize: 18, - fontFamily: "semibold", - textAlign: "center", - }, - - textInputContainer: { - width: "100%", - paddingVertical: 12, - paddingHorizontal: 14, - borderRadius: 12, - borderCurve: "continuous", - marginBottom: 9, - flexDirection: "row", - alignItems: "center", - gap: 14, - }, - - textInput: { - fontFamily: "medium", - fontSize: 16, - flex: 1, - }, -}); - export default PronoteCredentials; diff --git a/src/views/login/pronote/PronoteManualLocation.tsx b/src/views/login/pronote/PronoteManualLocation.tsx index 9ae94c89b..3d73aea0a 100644 --- a/src/views/login/pronote/PronoteManualLocation.tsx +++ b/src/views/login/pronote/PronoteManualLocation.tsx @@ -37,16 +37,13 @@ const PronoteManualLocation: Screen<"PronoteManualLocation"> = ({ navigation }) const insets = useSafeAreaInsets(); const {colors} = useTheme(); - const [keyboardOpen, setKeyboardOpen] = useState(false); const [keyboardHeight, setKeyboardHeight] = useState(0); const keyboardDidShow = (event: KeyboardEvent) => { - setKeyboardOpen(true); setKeyboardHeight(event.endCoordinates.height); }; const keyboardDidHide = () => { - setKeyboardOpen(false); setKeyboardHeight(0); }; diff --git a/src/views/login/skolengo/SkolengoInstanceSelector.tsx b/src/views/login/skolengo/SkolengoInstanceSelector.tsx index db9124bcb..bba46c792 100644 --- a/src/views/login/skolengo/SkolengoInstanceSelector.tsx +++ b/src/views/login/skolengo/SkolengoInstanceSelector.tsx @@ -10,7 +10,6 @@ import { LinearGradient } from "expo-linear-gradient"; import { useTheme } from "@react-navigation/native"; import { Search, X, GraduationCap, } from "lucide-react-native"; -import { useAlert } from "@/providers/AlertProvider"; import { Audio } from "expo-av"; import type { School } from "scolengo-api/types/models/School"; import { Skolengo } from "scolengo-api"; @@ -23,13 +22,10 @@ const SkolengoInstanceSelector: Screen<"SkolengoInstanceSelector"> = ({ // `null` when loading, `[]` when no instances found. const [instances, setInstances] = useState([]); const [geoInstances, setGeoInstances] = useState(null); - const [isSearchLoading, setIsSearchLoading] = useState(false); const {colors} = useTheme(); const insets = useSafeAreaInsets(); - const { showAlert } = useAlert(); - const [search, setSearch] = useState(""); const searchInputRef = React.createRef(); const debouncedSearch = useDebounce(search, 1000); @@ -77,19 +73,15 @@ const SkolengoInstanceSelector: Screen<"SkolengoInstanceSelector"> = ({ }; }, []); - const playSound = () => sound?.replayAsync(); - useEffect(() => { if (params && params.pos && params.pos !== null) { void async function () { - setIsSearchLoading(true); const instances = await Skolengo.searchSchool({ lon: params.pos!.longitude, lat: params.pos!.latitude }, 20); // On limite à 20 instances. instances.splice(20); setInstances(instances); setGeoInstances(instances); - setIsSearchLoading(false); }(); } else { setInstances([]); @@ -107,11 +99,9 @@ const SkolengoInstanceSelector: Screen<"SkolengoInstanceSelector"> = ({ setInstances(newInstances); setHasSearched(true); } else { - setIsSearchLoading(true); const newInstances = await Skolengo.searchSchool({ text: search }, 20); // On limite à 20 instances. newInstances.splice(20); - setIsSearchLoading(false); if(_debSearch !== debouncedSearch) return; // if the search has changed, we don't update the instances. setInstances(newInstances); setHasSearched(true); diff --git a/src/views/login/skolengo/SkolengoWebview.tsx b/src/views/login/skolengo/SkolengoWebview.tsx index 6c8667761..7fd2e5da6 100644 --- a/src/views/login/skolengo/SkolengoWebview.tsx +++ b/src/views/login/skolengo/SkolengoWebview.tsx @@ -12,8 +12,7 @@ import { import { WebView } from "react-native-webview"; import type { Screen } from "@/router/helpers/types"; import { - SafeAreaView, - useSafeAreaInsets, + SafeAreaView } from "react-native-safe-area-context"; import { useTheme } from "@react-navigation/native"; import MaskStars from "@/components/FirstInstallation/MaskStars"; @@ -33,7 +32,6 @@ import { wait } from "@/services/skolengo/data/utils"; const SkolengoWebview: Screen<"SkolengoWebview"> = ({ route, navigation }) => { const { showAlert } = useAlert(); const theme = useTheme(); - const insets = useSafeAreaInsets(); const [showWebView, setShowWebView] = useState(false); @@ -307,6 +305,7 @@ const getSkolengoURL = async (school: School) => { }; //! This function is not used in the current codebase but will be used in the future +// eslint-disable-next-line const loginSkolengoWorkflow = async (school: School) => { const skolengoUrl = await getSkolengoURL(school); if(!skolengoUrl) return; @@ -324,4 +323,4 @@ const loginSkolengoWorkflow = async (school: School) => { skolengoUrl.discovery ); if (!token) return; -}; \ No newline at end of file +}; diff --git a/src/views/settings/ExternalAccount/PriceError.tsx b/src/views/settings/ExternalAccount/PriceError.tsx index 7896010c7..39664242e 100644 --- a/src/views/settings/ExternalAccount/PriceError.tsx +++ b/src/views/settings/ExternalAccount/PriceError.tsx @@ -2,23 +2,16 @@ import React from "react"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { CircleHelp } from "lucide-react-native"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; -import {View, StyleSheet, Text, Alert} from "react-native"; +import { View, StyleSheet, Text, Alert } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; import { useAccounts } from "@/stores/account"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; -import {ExternalAccount} from "@/stores/account/types"; -import {detectMealPrice as ARDPriceDetector} from "@/views/settings/ExternalAccount/ARD"; - -type Props = { - navigation: any; - route: { params: { accountID: string } }; -}; +import { ExternalAccount } from "@/stores/account/types"; +import { detectMealPrice as ARDPriceDetector } from "@/views/settings/ExternalAccount/ARD"; const PriceError: Screen<"PriceError"> = ({ navigation, route }) => { const theme = useTheme(); const { colors } = theme; - const insets = useSafeAreaInsets(); const update = useAccounts(store => store.update); const account = route.params?.account; const accountId = route.params?.accountId; diff --git a/src/views/settings/ExternalAccount/QrcodeScanner.tsx b/src/views/settings/ExternalAccount/QrcodeScanner.tsx index 6aec158e2..f8b59b345 100644 --- a/src/views/settings/ExternalAccount/QrcodeScanner.tsx +++ b/src/views/settings/ExternalAccount/QrcodeScanner.tsx @@ -1,26 +1,17 @@ import React, { useEffect } from "react"; import type { Screen } from "@/router/helpers/types"; -import { useTheme } from "@react-navigation/native"; import { QrCode } from "lucide-react-native"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { useSafeAreaInsets, SafeAreaView } from "react-native-safe-area-context"; import { View, StyleSheet, Text } from "react-native"; -import { SafeAreaView } from "react-native-safe-area-context"; - - + + import { ExternalAccount } from "@/stores/account/types"; import { useAccounts } from "@/stores/account"; import { BarCodeScanner } from "expo-barcode-scanner"; import MaskedView from "@react-native-masked-view/masked-view"; import * as Haptics from "expo-haptics"; -type Props = { - navigation: any; - route: { params: { accountID: string } }; -}; - const QrcodeScanner: Screen<"QrcodeScanner"> = ({ navigation, route }) => { - const theme = useTheme(); - const { colors } = theme; const insets = useSafeAreaInsets(); const update = useAccounts(store => store.update); const accountID = route.params?.accountID; diff --git a/src/views/settings/ExternalAccount/ServiceSelector.tsx b/src/views/settings/ExternalAccount/ServiceSelector.tsx index 6c148cfba..688934420 100644 --- a/src/views/settings/ExternalAccount/ServiceSelector.tsx +++ b/src/views/settings/ExternalAccount/ServiceSelector.tsx @@ -1,22 +1,14 @@ import React, { useState } from "react"; import type { Screen } from "@/router/helpers/types"; -import { useTheme } from "@react-navigation/native"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; import { Image, View, StyleSheet } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; import Reanimated, { LinearTransition, FlipInXDown } from "react-native-reanimated"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; import { AccountService } from "@/stores/account/types"; -import { useCurrentAccount } from "@/stores/account"; import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; - -const ExternalAccountSelector: Screen<"ExternalAccountSelector"> = ({ navigation, route }) => { - const theme = useTheme(); - const { colors } = theme; - const insets = useSafeAreaInsets(); - const account = useCurrentAccount(store => store.account!); +const ExternalAccountSelector: Screen<"ExternalAccountSelector"> = ({ navigation, route }) => { type Service = AccountService | "Other"; const [service, setService] = useState(null); diff --git a/src/views/settings/SettingsAbout.tsx b/src/views/settings/SettingsAbout.tsx index e1ba896c2..88cd67fe3 100644 --- a/src/views/settings/SettingsAbout.tsx +++ b/src/views/settings/SettingsAbout.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { ScrollView, Image, StyleSheet, View } from "react-native"; +import { ScrollView, Image, View } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { Euro, Github, MapPin, MessageCircle } from "lucide-react-native"; @@ -214,39 +214,4 @@ const SettingsAbout: Screen<"SettingsAbout"> = ({ navigation }) => { ); }; -// Styles -const styles = StyleSheet.create({ - title: { - color: "#222222", - fontSize: 15, - }, - time: { - color: "#3F3F3F", - opacity: 0.5, - textAlign: "right", - fontFamily: "sfmedium", - fontSize: 13, - marginRight: 10, - }, - message: { - color: "#3F3F3F", - fontFamily: "sfmedium", - fontSize: 14, - maxWidth: "85%", - minWidth: "85%", - lineHeight: 15, - letterSpacing: -0.4, - }, - - overlay: { - backgroundColor: "#EEF5F5", - borderWidth: 1, - borderColor: "#00000030", - borderRadius: 20, - height: 25, - padding: 9, - marginHorizontal: 20, - }, -}); - export default SettingsAbout; diff --git a/src/views/settings/SettingsFlags.tsx b/src/views/settings/SettingsFlags.tsx index 23de90910..0e27cd5ff 100644 --- a/src/views/settings/SettingsFlags.tsx +++ b/src/views/settings/SettingsFlags.tsx @@ -3,7 +3,6 @@ import { ScrollView, TextInput, Alert, KeyboardAvoidingView, StyleSheet } from " import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { Code } from "lucide-react-native"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; import { useFlagsStore } from "@/stores/flags"; import { useCurrentAccount } from "@/stores/account"; @@ -14,7 +13,6 @@ const SettingsFlags: Screen<"SettingsFlags"> = ({ navigation }) => { const account = useCurrentAccount(store => store.account!); const externals = useCurrentAccount(store => store.linkedAccounts); const { colors } = useTheme(); - const insets = useSafeAreaInsets(); const textInputRef = useRef(null); const isBase64Image = (str: string) => { @@ -153,4 +151,4 @@ const styles = StyleSheet.create({ }, }); -export default SettingsFlags; \ No newline at end of file +export default SettingsFlags; diff --git a/src/views/settings/SettingsMagic.tsx b/src/views/settings/SettingsMagic.tsx index e3daad22d..a9eda3b9e 100644 --- a/src/views/settings/SettingsMagic.tsx +++ b/src/views/settings/SettingsMagic.tsx @@ -2,7 +2,6 @@ import React from "react"; import { ScrollView, Switch } from "react-native"; import { useTheme } from "@react-navigation/native"; import type { Screen } from "@/router/helpers/types"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; // Ensure this file contains valid regex patterns import MagicContainerCard from "@/components/Settings/MagicContainerCard"; import { NativeIconGradient, NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; @@ -11,8 +10,6 @@ import { useCurrentAccount } from "@/stores/account"; const SettingsMagic: Screen<"SettingsMagic"> = ({ navigation }) => { const theme = useTheme(); - const { colors } = theme; - const insets = useSafeAreaInsets(); const account = useCurrentAccount(store => store.account); const mutateProperty = useCurrentAccount(store => store.mutateProperty); diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index c7630b773..14bbc66a0 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -17,8 +17,8 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { const account = useCurrentAccount(store => store.account!); const mutateProperty = useCurrentAccount(store => store.mutateProperty); - const [oldFirstName, setOldFirstName] = useState(account.studentName?.first ?? ""); - const [oldLastName, setOldLastName] = useState(account.studentName?.last ?? ""); + const oldFirstName = account.studentName?.first ?? ""; + const oldLastName = account.studentName?.last ?? ""; const firstNameRef = useRef(null); const lastNameRef = useRef(null); @@ -326,4 +326,4 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { ); }; -export default SettingsProfile; \ No newline at end of file +export default SettingsProfile; diff --git a/src/views/settings/SettingsReactions.tsx b/src/views/settings/SettingsReactions.tsx index 6b691d7f8..268696d57 100644 --- a/src/views/settings/SettingsReactions.tsx +++ b/src/views/settings/SettingsReactions.tsx @@ -1,13 +1,11 @@ import React from "react"; import { ScrollView, View } from "react-native"; import type { Screen } from "@/router/helpers/types"; -import { useTheme } from "@react-navigation/native"; import { useGradesStore } from "@/stores/grades"; import ReelGallery from "@/components/Settings/ReelGallery"; import MissingItem from "@/components/Global/MissingItem"; const SettingsReactions: Screen<"SettingsReactions"> = () => { - const theme = useTheme(); const reelsObject = useGradesStore((store) => store.reels); const reels = Object.values(reelsObject); @@ -39,4 +37,4 @@ const SettingsReactions: Screen<"SettingsReactions"> = () => { ); }; -export default SettingsReactions; \ No newline at end of file +export default SettingsReactions; diff --git a/src/views/settings/SettingsTabs.tsx b/src/views/settings/SettingsTabs.tsx index 45c9dc1fb..3486d13c5 100644 --- a/src/views/settings/SettingsTabs.tsx +++ b/src/views/settings/SettingsTabs.tsx @@ -44,16 +44,6 @@ interface Tab { icon: any; // Should be updated if Lottie icon types are more specific } -interface Personalization { - tabs: Array<{ name: string; enabled: boolean; installed: boolean }>; - hideTabTitles?: boolean; - showTabBackground?: boolean; -} - -interface Account { - personalization: Personalization; -} - const SettingsTabs = () => { const theme = useTheme(); const insets = useSafeAreaInsets(); @@ -562,4 +552,4 @@ const SettingsTabs = () => { ); }; -export default SettingsTabs; \ No newline at end of file +export default SettingsTabs; diff --git a/src/views/settings/SettingsTrophies.tsx b/src/views/settings/SettingsTrophies.tsx index a4da7f690..3a0232d76 100644 --- a/src/views/settings/SettingsTrophies.tsx +++ b/src/views/settings/SettingsTrophies.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { ScrollView, View, StyleSheet } from "react-native"; +import { ScrollView, View } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; @@ -9,7 +9,6 @@ const SettingsTrophies: Screen<"SettingsTrophies"> = ({ navigation }) => { const theme = useTheme(); const { colors } = theme; const insets = useSafeAreaInsets(); - const [enabled, setEnabled] = React.useState(false); return ( @@ -33,39 +32,4 @@ const SettingsTrophies: Screen<"SettingsTrophies"> = ({ navigation }) => { ); }; -// Styles -const styles = StyleSheet.create({ - title: { - color: "#222222", - fontSize: 15, - }, - time: { - color: "#3F3F3F", - opacity: 0.5, - textAlign: "right", - fontFamily: "sfmedium", - fontSize: 13, - marginRight: 10, - }, - message: { - color: "#3F3F3F", - fontFamily: "sfmedium", - fontSize: 14, - maxWidth: "85%", - minWidth: "85%", - lineHeight: 15, - letterSpacing: -0.4, - }, - - overlay: { - backgroundColor: "#EEF5F5", - borderWidth: 1, - borderColor: "#00000030", - borderRadius: 20, - height: 25, - padding: 9, - marginHorizontal: 20, - }, -}); - export default SettingsTrophies; diff --git a/src/views/welcome/ChangelogScreen.tsx b/src/views/welcome/ChangelogScreen.tsx index 444406d10..612e394e3 100644 --- a/src/views/welcome/ChangelogScreen.tsx +++ b/src/views/welcome/ChangelogScreen.tsx @@ -11,7 +11,6 @@ import { useTheme } from "@react-navigation/native"; import Reanimated, { FadeInUp, FadeOutUp, LinearTransition } from "react-native-reanimated"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { animPapillon } from "@/utils/ui/animations"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; import { TouchableOpacity } from "react-native-gesture-handler"; import { PressableScale } from "react-native-pressable-scale"; @@ -43,7 +42,6 @@ const changelogURL = datasets.changelog.replace("[version]", currentVersion); const ChangelogScreen: Screen<"ChangelogScreen"> = ({ route, navigation }) => { const theme = useTheme(); - const insets = useSafeAreaInsets(); const [changelog, setChangelog] = useState(null); const [loading, setLoading] = useState(true); diff --git a/src/views/welcome/FirstInstallation.tsx b/src/views/welcome/FirstInstallation.tsx index dcfa6d6f3..96a86bb5b 100644 --- a/src/views/welcome/FirstInstallation.tsx +++ b/src/views/welcome/FirstInstallation.tsx @@ -47,12 +47,6 @@ const FirstInstallation: Screen<"FirstInstallation"> = ({ navigation }) => { }; }, []); - const playSound = async () => { - if (sound) { - await sound.replayAsync(); - } - }; - return ( diff --git a/src/widgets/Components/RestaurantBalance.tsx b/src/widgets/Components/RestaurantBalance.tsx index a799a99d4..c7cf3a51c 100644 --- a/src/widgets/Components/RestaurantBalance.tsx +++ b/src/widgets/Components/RestaurantBalance.tsx @@ -18,7 +18,6 @@ const RestaurantBalanceWidget = forwardRef(({ const theme = useTheme(); const { colors } = theme; - const account = useCurrentAccount((store) => store.account); const linkedAccounts = useCurrentAccount(store => store.linkedAccounts); const [balances, setBalances] = useState(null); const [currentBalanceIndex, setCurrentBalanceIndex] = useState(0); diff --git a/src/widgets/Components/RestaurantQRCode.tsx b/src/widgets/Components/RestaurantQRCode.tsx index ecfea4e43..df26fc9c0 100644 --- a/src/widgets/Components/RestaurantQRCode.tsx +++ b/src/widgets/Components/RestaurantQRCode.tsx @@ -1,4 +1,4 @@ -import { useTheme } from "@react-navigation/native"; +import { useTheme, useNavigation } from "@react-navigation/native"; import { Pizza } from "lucide-react-native"; import React, { forwardRef, useEffect, useImperativeHandle, useState } from "react"; import { Text, View } from "react-native"; @@ -11,7 +11,6 @@ import { useCurrentAccount } from "@/stores/account"; import QRCode from "react-native-qrcode-svg"; import { AccountService } from "@/stores/account/types"; import { qrcodeFromExternal } from "@/services/qrcode"; -import { useNavigation } from "@react-navigation/native"; import { StackNavigationProp } from "@react-navigation/stack"; import { RouteParameters } from "./../../router/helpers/types"; @@ -25,7 +24,6 @@ const RestaurantQRCodeWidget = forwardRef(({ const theme = useTheme(); const { colors } = theme; - const account = useCurrentAccount((store) => store.account); const linkedAccounts = useCurrentAccount(store => store.linkedAccounts); const [qrcode, setQRCodes] = useState | null>(null); const navigation = useNavigation(); From b0e65a4e511c6e14a8ec9d8b5fb0da1455b5bada Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 25 Jan 2025 14:47:26 +0100 Subject: [PATCH 0353/1144] feat(lint): discouraging the use of var and encouraging the use of const or let instead --- eslint.config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/eslint.config.js b/eslint.config.js index a45815615..2fdfef68f 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -40,7 +40,8 @@ module.exports = [ "unused-imports/no-unused-imports": "error", "no-unused-vars": ["error", { "args": "none" - }] + }], + "no-var": "error" } } ]; From 285f122087d8c7d8d28e06e53ed49c809467d0a7 Mon Sep 17 00:00:00 2001 From: CodeurIII Date: Sat, 25 Jan 2025 18:14:10 +0400 Subject: [PATCH 0354/1144] Add header when you have no homeworks --- .../Home/Elements/HomeworksElement.tsx | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 7d306b21d..6e7e2de34 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -83,20 +83,27 @@ const HomeworksElement: React.FC = ({ navigation, onImpor if (hwSemaineActuelle.length === 0 && hwSemaineProchaine.length === 0) { return ( - - - - - + <> + + )} + /> + + + + + + ); } From 8c5fb993145da2e5ace901bf29b54486ad0dcac9 Mon Sep 17 00:00:00 2001 From: godetremy Date: Sat, 25 Jan 2025 15:24:30 +0100 Subject: [PATCH 0355/1144] fix: Gradient not visible when homework is truncated --- src/views/account/Homeworks/Atoms/Item.tsx | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/views/account/Homeworks/Atoms/Item.tsx b/src/views/account/Homeworks/Atoms/Item.tsx index bd080164e..d26aa6a4d 100644 --- a/src/views/account/Homeworks/Atoms/Item.tsx +++ b/src/views/account/Homeworks/Atoms/Item.tsx @@ -35,6 +35,7 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total } const theme = useTheme(); const [subjectData, setSubjectData] = useState(getSubjectData(homework.subject)); const [category, setCategory] = useState(null); + const [shouldShowMoreGradient, setShouldShowMoreGradient] = useState(false); const account = useCurrentAccount((store) => store.account!); const route = useRoute(); @@ -74,8 +75,6 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total } setMainLoaded(true); }, [homework.done]); - const contentLines = homework.content.split("\n"); - const renderCategoryOrReturnType = () => { if (category) { return ( @@ -189,17 +188,24 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total } exiting={FadeOut.duration(200).delay(50)} > - + { + const { height } = event.nativeEvent.layout; + if (height >= 22 * 5) { + setShouldShowMoreGradient(true); + } + }} + > ${parse_homeworks(homework.content).replace("\n", "")}`} stylesheet={stylesText} /> - {contentLines.length > 5 && ( + {shouldShowMoreGradient && ( From b6cca912b566e28bdc1aa3536a921856ce60f9f7 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 25 Jan 2025 15:49:15 +0100 Subject: [PATCH 0356/1144] lint: duplicated imports --- eslint.config.js | 4 ++- src/components/Global/InfiniteDatePager.tsx | 3 +-- src/components/Home/AccountSwitcher.tsx | 3 +-- .../Home/AccountSwitcherContextMenu.tsx | 3 +-- src/views/account/Grades/Document.tsx | 3 +-- src/views/account/Grades/Grades.tsx | 3 +-- .../Home/Elements/AttendanceElement.tsx | 3 +-- src/views/account/Home/Home.tsx | 5 ++-- src/views/account/Homeworks/Atoms/Item.tsx | 7 +++-- src/views/account/Lessons/Lessons.tsx | 3 +-- src/views/settings/SettingsAbout.tsx | 4 +-- src/views/settings/SettingsApparence.tsx | 7 +++-- src/views/welcome/AccountSelector.tsx | 27 +++++++++---------- 13 files changed, 32 insertions(+), 43 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 2fdfef68f..55a39ce27 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -41,7 +41,9 @@ module.exports = [ "no-unused-vars": ["error", { "args": "none" }], - "no-var": "error" + "no-var": "error", + "no-unneeded-ternary": "error", + "no-duplicate-imports": "error" } } ]; diff --git a/src/components/Global/InfiniteDatePager.tsx b/src/components/Global/InfiniteDatePager.tsx index 75b9265a8..50ebee9f9 100644 --- a/src/components/Global/InfiniteDatePager.tsx +++ b/src/components/Global/InfiniteDatePager.tsx @@ -1,7 +1,6 @@ import React, { useRef, useCallback, useEffect } from "react"; import { View, Text, StyleSheet } from "react-native"; -import InfinitePager from "react-native-infinite-pager"; -import {InfinitePagerImperativeApi, InfinitePagerPageComponent} from "react-native-infinite-pager"; +import InfinitePager, { InfinitePagerImperativeApi, InfinitePagerPageComponent } from "react-native-infinite-pager"; const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000; diff --git a/src/components/Home/AccountSwitcher.tsx b/src/components/Home/AccountSwitcher.tsx index f7ba4bed7..95d5d47ef 100644 --- a/src/components/Home/AccountSwitcher.tsx +++ b/src/components/Home/AccountSwitcher.tsx @@ -12,7 +12,6 @@ import { useCurrentAccount } from "@/stores/account"; import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; import PapillonSpinner from "../Global/PapillonSpinner"; import { animPapillon } from "@/utils/ui/animations"; -import Animated from "react-native-reanimated"; import { BlurView } from "expo-blur"; const ReanimatedBlurView = Reanimated.createAnimatedComponent(BlurView); @@ -32,7 +31,7 @@ const AccountSwitcher: React.FC<{ const shouldHideName = account.personalization.hideNameOnHomeScreen || false; const shouldHidePicture = account.personalization.hideProfilePicOnHomeScreen || false; - const AnimatedChevronDown = Animated.createAnimatedComponent(ChevronDown); + const AnimatedChevronDown = Reanimated.createAnimatedComponent(ChevronDown); return ( = ({ navigation }) => { loading={!account.instance} /> - = ({ navigation }) => { /> - + ); }; diff --git a/src/views/account/Homeworks/Atoms/Item.tsx b/src/views/account/Homeworks/Atoms/Item.tsx index bd080164e..e28b89728 100644 --- a/src/views/account/Homeworks/Atoms/Item.tsx +++ b/src/views/account/Homeworks/Atoms/Item.tsx @@ -1,11 +1,10 @@ import React, { useEffect, useState, useCallback } from "react"; -import {Clock, Paperclip, Sparkles} from "lucide-react-native"; +import { Clock, Paperclip, Sparkles } from "lucide-react-native"; import { getSubjectData } from "@/services/shared/Subject"; -import { useRoute, useTheme} from "@react-navigation/native"; +import { useRoute, useTheme } from "@react-navigation/native"; import { NativeItem, NativeText } from "@/components/Global/NativeComponents"; import PapillonCheckbox from "@/components/Global/PapillonCheckbox"; -import Reanimated, { LinearTransition } from "react-native-reanimated"; -import { FadeIn, FadeOut } from "react-native-reanimated"; +import Reanimated, { LinearTransition, FadeIn, FadeOut } from "react-native-reanimated"; import { animPapillon } from "@/utils/ui/animations"; import HTMLView from "react-native-htmlview"; import { NativeStackNavigationProp } from "@react-navigation/native-stack"; diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index 09134d1ba..99f9bedbe 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -1,6 +1,5 @@ import React, { useCallback, useEffect, useRef, useState } from "react"; -import { FlatList, View, Dimensions, ViewToken } from "react-native"; -import { StyleSheet } from "react-native"; +import { FlatList, View, Dimensions, ViewToken, StyleSheet } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useCurrentAccount } from "@/stores/account"; import { useTimetableStore } from "@/stores/timetable"; diff --git a/src/views/settings/SettingsAbout.tsx b/src/views/settings/SettingsAbout.tsx index 88cd67fe3..1fbfdffb7 100644 --- a/src/views/settings/SettingsAbout.tsx +++ b/src/views/settings/SettingsAbout.tsx @@ -4,9 +4,7 @@ import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { Euro, Github, MapPin, MessageCircle } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import { NativeList, NativeItem, NativeListHeader } from "@/components/Global/NativeComponents"; -import { NativeIcon } from "@/components/Global/NativeComponents"; -import { NativeText } from "@/components/Global/NativeComponents"; +import { NativeList, NativeItem, NativeListHeader, NativeIcon, NativeText } from "@/components/Global/NativeComponents"; import PackageJSON from "../../../package.json"; import AboutContainerCard from "@/components/Settings/AboutContainerCard"; import * as Linking from "expo-linking"; diff --git a/src/views/settings/SettingsApparence.tsx b/src/views/settings/SettingsApparence.tsx index 8e5f95d1d..03c8311a0 100644 --- a/src/views/settings/SettingsApparence.tsx +++ b/src/views/settings/SettingsApparence.tsx @@ -2,13 +2,12 @@ import React, { useEffect, useState } from "react"; import { ScrollView } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; -import {Moon, RefreshCw, Sun, SunMoon} from "lucide-react-native"; +import { Moon, RefreshCw, Sun, SunMoon } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import { NativeList, NativeItem, NativeListHeader } from "@/components/Global/NativeComponents"; -import { NativeText } from "@/components/Global/NativeComponents"; +import { NativeList, NativeItem, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; import PapillonCheckbox from "@/components/Global/PapillonCheckbox"; import AsyncStorage from "@react-native-async-storage/async-storage"; -import Animated, {FadeInDown, FadeOutDown} from "react-native-reanimated"; +import Animated, { FadeInDown, FadeOutDown } from "react-native-reanimated"; import ApparenceContainerCard from "@/components/Settings/ApparenceContainerCard"; import * as Brightness from "expo-brightness"; diff --git a/src/views/welcome/AccountSelector.tsx b/src/views/welcome/AccountSelector.tsx index 56da2988d..2f26efd34 100644 --- a/src/views/welcome/AccountSelector.tsx +++ b/src/views/welcome/AccountSelector.tsx @@ -1,9 +1,9 @@ -import {NativeItem, NativeList, NativeListHeader, NativeText} from "@/components/Global/NativeComponents"; -import {useAccounts, useCurrentAccount} from "@/stores/account"; -import {defaultProfilePicture} from "@/utils/ui/default-profile-picture"; -import {useIsFocused, useTheme} from "@react-navigation/native"; -import {PlusIcon} from "lucide-react-native"; -import {useEffect, useState} from "react"; +import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; +import { useAccounts, useCurrentAccount } from "@/stores/account"; +import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; +import { useIsFocused, useTheme } from "@react-navigation/native"; +import { PlusIcon } from "lucide-react-native"; +import { useEffect, useState } from "react"; import { Alert, Dimensions, @@ -14,7 +14,7 @@ import { TouchableHighlight, View } from "react-native"; -import {useSafeAreaInsets} from "react-native-safe-area-context"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; import * as SplashScreen from "expo-splash-screen"; import PapillonAvatar from "@/components/Global/PapillonAvatar"; @@ -31,15 +31,14 @@ import Reanimated, { useScrollViewOffset, ZoomIn, } from "react-native-reanimated"; -import {LinearGradient} from "expo-linear-gradient"; -import {animPapillon} from "@/utils/ui/animations"; -import {Screen} from "@/router/helpers/types"; +import { LinearGradient } from "expo-linear-gradient"; +import { animPapillon } from "@/utils/ui/animations"; +import { Screen } from "@/router/helpers/types"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; -import {PressableScale} from "react-native-pressable-scale"; +import { PressableScale } from "react-native-pressable-scale"; import datasets from "@/consts/datasets.json"; -import Animated from "react-native-reanimated"; -import {PrimaryAccount} from "@/stores/account/types"; +import { PrimaryAccount } from "@/stores/account/types"; // https://raw.githubusercontent.com/PapillonApp/datasets/refs/heads/main/illustrations/index.json @@ -66,7 +65,7 @@ const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { const [illustration, setIllustration] = useState(undefined); const [illustrationLoaded, setIllustrationLoaded] = useState(false); - const scrollRef = useAnimatedRef(); + const scrollRef = useAnimatedRef(); const scrollOffset = useScrollViewOffset(scrollRef); const headerRatioHeight = 250; let headerAnimatedStyle = useAnimatedStyle(() => ({ From c5f0d33c03ff2c090b41f00537b6c7ab3eab6f74 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 25 Jan 2025 15:53:25 +0100 Subject: [PATCH 0357/1144] lint: no-unneeded-ternary --- src/views/settings/SettingsAbout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/settings/SettingsAbout.tsx b/src/views/settings/SettingsAbout.tsx index 1fbfdffb7..f728a4da0 100644 --- a/src/views/settings/SettingsAbout.tsx +++ b/src/views/settings/SettingsAbout.tsx @@ -88,7 +88,7 @@ const SettingsAbout: Screen<"SettingsAbout"> = ({ navigation }) => { {teams.map((team, index) => ( Linking.openURL(team.link) : undefined} - chevron={team.link ? true : false} + chevron={!!team.link} key={index} leading={ Date: Sat, 25 Jan 2025 16:27:24 +0100 Subject: [PATCH 0358/1144] lint: remove extra semicolons --- eslint.config.js | 1 + src/components/Global/PapillonModernHeader.tsx | 2 +- src/services/shared/Grade.ts | 2 +- src/utils/external/download-as-base64.ts | 2 +- src/utils/magic/categorizeMessages.ts | 2 +- src/utils/uuid-v4.ts | 2 +- src/views/account/Home/Elements/AttendanceElement.tsx | 2 +- src/views/login/pronote/PronoteManualURL.tsx | 4 ++-- src/views/settings/ExternalAccount/Turboself.tsx | 2 +- 9 files changed, 10 insertions(+), 9 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 55a39ce27..82f513a4a 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -37,6 +37,7 @@ module.exports = [ "@stylistic/quotes": ["error", "double"], "@stylistic/semi": ["error", "always"], "@stylistic/space-before-function-paren": ["error", "always"], + "@stylistic/no-extra-semi": "error", "unused-imports/no-unused-imports": "error", "no-unused-vars": ["error", { "args": "none" diff --git a/src/components/Global/PapillonModernHeader.tsx b/src/components/Global/PapillonModernHeader.tsx index 3df4adf27..4c4760056 100644 --- a/src/components/Global/PapillonModernHeader.tsx +++ b/src/components/Global/PapillonModernHeader.tsx @@ -17,7 +17,7 @@ interface ModernHeaderProps { startLocation?: number, native? : boolean, tint?: string, -}; +} export const PapillonModernHeader: React.FC = (props) => { if (props.native) { diff --git a/src/services/shared/Grade.ts b/src/services/shared/Grade.ts index 8062d4606..18b63e941 100644 --- a/src/services/shared/Grade.ts +++ b/src/services/shared/Grade.ts @@ -29,7 +29,7 @@ export interface GradeValue { */ disabled?: boolean -}; +} export interface Grade { id: string; diff --git a/src/utils/external/download-as-base64.ts b/src/utils/external/download-as-base64.ts index 54a2caa55..f316dd386 100644 --- a/src/utils/external/download-as-base64.ts +++ b/src/utils/external/download-as-base64.ts @@ -15,4 +15,4 @@ export default async function downloadAsBase64 (url: string, headers?: Record = ({ onImportance }) = }, 0) + data.delays.reduce((sum, delay) => { const [hours, minutes] = [Math.floor(delay.duration / 60), delay.duration % 60]; return sum + hours + (minutes || 0) / 60; - }, 0);; + }, 0); const unJustifiedHours = data.absences.reduce((sum, absence) => { if (!absence.justified) { diff --git a/src/views/login/pronote/PronoteManualURL.tsx b/src/views/login/pronote/PronoteManualURL.tsx index b92c92fa7..39fe3ec1e 100644 --- a/src/views/login/pronote/PronoteManualURL.tsx +++ b/src/views/login/pronote/PronoteManualURL.tsx @@ -128,7 +128,7 @@ const PronoteManualURL: Screen<"PronoteManualURL"> = ({ route, navigation }) => onSubmitEditing={() => { if (instanceURL.length > 0) { checkForDemoInstance(instanceURL, navigation, showAlert); - }; + } }} /> @@ -159,7 +159,7 @@ const PronoteManualURL: Screen<"PronoteManualURL"> = ({ route, navigation }) => onPress={() => { if (instanceURL.length > 0) { checkForDemoInstance(instanceURL, navigation, showAlert); - }; + } }} /> {(route.params?.method) && ( diff --git a/src/views/settings/ExternalAccount/Turboself.tsx b/src/views/settings/ExternalAccount/Turboself.tsx index a09c29975..f0909cc30 100644 --- a/src/views/settings/ExternalAccount/Turboself.tsx +++ b/src/views/settings/ExternalAccount/Turboself.tsx @@ -44,7 +44,7 @@ const ExternalTurboselfLogin: Screen<"ExternalTurboselfLogin"> = ({ navigation } setError(error.message); if (error.message == "401: {\"statusCode\":401,\"message\":\"Accès interdit\"}") { setError("Nom d'utilisateur ou mot de passe incorrect"); - }; + } } else { setError("Une erreur est survenue lors de la connexion."); From c4e0832b5c2db7181bdce7a922c1df1ca988b069 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 25 Jan 2025 16:51:21 +0100 Subject: [PATCH 0359/1144] lint: no-irregular-whitespace --- eslint.config.js | 1 + src/router/helpers/types.ts | 12 ++++++------ src/services/pronote/chats.ts | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 82f513a4a..ef0a86a07 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -43,6 +43,7 @@ module.exports = [ "args": "none" }], "no-var": "error", + "no-irregular-whitespace": "error", "no-unneeded-ternary": "error", "no-duplicate-imports": "error" } diff --git a/src/router/helpers/types.ts b/src/router/helpers/types.ts index 6057d5217..673564a91 100644 --- a/src/router/helpers/types.ts +++ b/src/router/helpers/types.ts @@ -1,6 +1,6 @@ -import {AddonLogs as AddonLog, AddonPlacementManifest} from "@/addons/types"; +import { AddonLogs as AddonLog, AddonPlacementManifest } from "@/addons/types"; import type { Chat, ChatRecipient } from "@/services/shared/Chat"; -import type {Grade, GradesPerSubject} from "@/services/shared/Grade"; +import type { Grade, GradesPerSubject } from "@/services/shared/Grade"; import { Homework } from "@/services/shared/Homework"; import { ReservationHistory } from "@/services/shared/ReservationHistory"; import type { AccountService } from "@/stores/account/types"; @@ -8,11 +8,11 @@ import type { CurrentPosition } from "@/utils/native/location"; import type { NativeStackScreenProps } from "@react-navigation/native-stack"; import type pronote from "pawnote"; import type React from "react"; -import type { School as SkolengoSchool} from "scolengo-api/types/models/School"; +import type { School as SkolengoSchool } from "scolengo-api/types/models/School"; import { ImageSourcePropType } from "react-native"; -import {Client} from "pawrd"; +import { Client } from "pawrd"; import { Host } from "turboself-api"; -import {Evaluation} from "@/services/shared/Evaluation"; +import { Evaluation } from "@/services/shared/Evaluation"; import { ThemesMeta } from "@/utils/chat/themes/Themes.types"; export type RouteParameters = { @@ -137,7 +137,7 @@ export type RouteParameters = { Menu?: undefined; RestaurantQrCode: { - QrCodes: Array; + QrCodes: Array; }; RestaurantHistory: { histories: ReservationHistory[]; diff --git a/src/services/pronote/chats.ts b/src/services/pronote/chats.ts index 915e33e89..87853eca1 100644 --- a/src/services/pronote/chats.ts +++ b/src/services/pronote/chats.ts @@ -36,7 +36,7 @@ export const getChats = async (account: PronoteAccount): Promise> => })); }; -export const getChatRecipients = async (account: PronoteAccount, chat: Chat): Promise => { +export const getChatRecipients = async (account: PronoteAccount, chat: Chat): Promise => { if (!account.instance) throw new ErrorServiceUnauthenticated("pronote"); @@ -52,7 +52,7 @@ export const getChatRecipients = async (account: PronoteAccount, chat: Chat): Pr }); }; -export const sendMessageInChat = async (account: PronoteAccount, chat: Chat, content: string): Promise => { +export const sendMessageInChat = async (account: PronoteAccount, chat: Chat, content: string): Promise => { if (!account.instance) throw new ErrorServiceUnauthenticated("pronote"); From e0e212c3212ed6b8c64fff7310275378eca85779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 25 Jan 2025 17:03:50 +0100 Subject: [PATCH 0360/1144] delete verification Expo Go (not working on iOS) --- src/background/BackgroundTasks.ts | 27 +++++++------------- src/background/Notifications.ts | 3 --- src/views/settings/SettingsNotifications.tsx | 6 ----- 3 files changed, 9 insertions(+), 27 deletions(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 2dae32667..1f68fc157 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -2,8 +2,6 @@ import notifee, { EventType } from "@notifee/react-native"; import * as BackgroundFetch from "expo-background-fetch"; import * as TaskManager from "expo-task-manager"; import { BackgroundFetchResult } from "expo-background-fetch"; -import { isExpoGo } from "@/utils/native/expoGoAlert"; -import Constants from "expo-constants"; import { fetchNews } from "./data/News"; import { log, error, warn } from "@/utils/logger/logger"; @@ -96,22 +94,15 @@ const registerBackgroundTasks = async () => { await unsetBackgroundFetch(); } - if (!isExpoGo()) { - try { - await BackgroundFetch.registerTaskAsync("background-fetch", { - minimumInterval: 60 * 15, - stopOnTerminate: false, - startOnBoot: true, - }); - log("✅ Background task registered", "BackgroundEvent"); - } catch (err) { - error(`❌ Failed to register background task: ${err}`, "BackgroundEvent"); - } - } else { - error( - `🚨 Running in Expo Go (Constants => ${Constants.appOwnership}). Skipping background task registration...`, - "BackgroundEvent" - ); + try { + await BackgroundFetch.registerTaskAsync("background-fetch", { + minimumInterval: 60 * 15, + stopOnTerminate: false, + startOnBoot: true, + }); + log("✅ Background task registered", "BackgroundEvent"); + } catch (err) { + error(`❌ Failed to register background task: ${err}`, "BackgroundEvent"); } }; diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index d4bcd76c8..66a111f11 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -1,4 +1,3 @@ -import { isExpoGo } from "@/utils/native/expoGoAlert"; import notifee, { AuthorizationStatus, Notification, @@ -6,8 +5,6 @@ import notifee, { import { Platform } from "react-native"; const requestNotificationPermission = async () => { - if (isExpoGo()) return false; - const settings = await notifee.requestPermission(); if (Platform.OS === "ios") { if (settings.authorizationStatus >= AuthorizationStatus.AUTHORIZED) { diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 1ab6e8d39..2217543cf 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -21,7 +21,6 @@ import { } from "@/components/Global/NativeComponents"; import NotificationContainerCard from "@/components/Settings/NotificationContainerCard"; import { createChannelNotification, requestNotificationPermission } from "@/background/Notifications"; -import { alertExpoGo, isExpoGo } from "@/utils/native/expoGoAlert"; import { useCurrentAccount } from "@/stores/account"; import { PressableScale } from "react-native-pressable-scale"; import { useAlert } from "@/providers/AlertProvider"; @@ -81,11 +80,6 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ }, [enabled]); const askEnabled = async (newValue: boolean) => { - if (isExpoGo()) { - alertExpoGo(); - return; - } - setEnabled(newValue); }; From 5a4bb419e20dc42d2bd3c44087f2dfac25b1263d Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 25 Jan 2025 17:05:58 +0100 Subject: [PATCH 0361/1144] lint: no-mixed-spaces-and-tabs --- eslint.config.js | 1 + .../Evaluation/Subject/EvaluationItem.tsx | 6 +++--- .../account/Grades/Latest/LatestGradesItem.tsx | 16 ++++++++-------- src/views/account/Grades/Modals/Subject.tsx | 14 +++++++------- src/views/account/Grades/Subject/GradeItem.tsx | 6 +++--- 5 files changed, 22 insertions(+), 21 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index ef0a86a07..c6416da73 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -38,6 +38,7 @@ module.exports = [ "@stylistic/semi": ["error", "always"], "@stylistic/space-before-function-paren": ["error", "always"], "@stylistic/no-extra-semi": "error", + "@stylistic/no-mixed-spaces-and-tabs": "error", "unused-imports/no-unused-imports": "error", "no-unused-vars": ["error", { "args": "none" diff --git a/src/views/account/Evaluation/Subject/EvaluationItem.tsx b/src/views/account/Evaluation/Subject/EvaluationItem.tsx index cad03b101..a71076577 100644 --- a/src/views/account/Evaluation/Subject/EvaluationItem.tsx +++ b/src/views/account/Evaluation/Subject/EvaluationItem.tsx @@ -36,9 +36,9 @@ const EvaluationItem: React.FC = ({ }); const gradeValue = - typeof grade.student.value === "number" - ? grade.student.value.toFixed(2) - : "N. not"; + typeof grade.student.value === "number" + ? grade.student.value.toFixed(2) + : "N. not"; return ( = ({ }: undefined} > {grade.description || - `Note renseignée le ${new Date( - grade.timestamp - ).toLocaleDateString("fr-FR", { - weekday: "long", - month: "long", - day: "numeric", - year: "numeric", - })}`} + `Note renseignée le ${new Date( + grade.timestamp + ).toLocaleDateString("fr-FR", { + weekday: "long", + month: "long", + day: "numeric", + year: "numeric", + })}`} diff --git a/src/views/account/Grades/Modals/Subject.tsx b/src/views/account/Grades/Modals/Subject.tsx index 9da07d68b..38052698d 100644 --- a/src/views/account/Grades/Modals/Subject.tsx +++ b/src/views/account/Grades/Modals/Subject.tsx @@ -6,12 +6,12 @@ import { } from "@/components/Global/NativeComponents"; import { getSubjectData } from "@/services/shared/Subject"; import { getCourseSpeciality } from "@/utils/format/format_cours_name"; -import {AverageDiffGrade, getAverageDiffGrade} from "@/utils/grades/getAverages"; +import { AverageDiffGrade, getAverageDiffGrade } from "@/utils/grades/getAverages"; import { useTheme } from "@react-navigation/native"; import { Trophy, User, UserMinus, UserPlus, Users } from "lucide-react-native"; import React, { useEffect, useLayoutEffect, useState } from "react"; import { View, ScrollView } from "react-native"; -import {Screen} from "@/router/helpers/types"; +import { Screen } from "@/router/helpers/types"; const GradeSubjectScreen: Screen<"GradeSubject"> = ({ route, navigation }) => { const { subject, allGrades } = route.params; @@ -236,11 +236,11 @@ const GradeSubjectScreen: Screen<"GradeSubject"> = ({ route, navigation }) => { lineHeight: 18, fontFamily: "semibold", color: - (averageDiff.difference || 0) < 0 - ? "#4CAF50" - : (averageDiff.difference || 0) === 0 - ? theme.colors.text - : "#F44336", + (averageDiff.difference || 0) < 0 + ? "#4CAF50" + : (averageDiff.difference || 0) === 0 + ? theme.colors.text + : "#F44336", marginLeft: 12, marginRight: 6, }} diff --git a/src/views/account/Grades/Subject/GradeItem.tsx b/src/views/account/Grades/Subject/GradeItem.tsx index 8493eeb9a..82777cd03 100644 --- a/src/views/account/Grades/Subject/GradeItem.tsx +++ b/src/views/account/Grades/Subject/GradeItem.tsx @@ -36,9 +36,9 @@ const GradeItem: React.FC = ({ }); const gradeValue = - (typeof grade.student.value === "number" && !isNaN(grade.student.value)) - ? grade.student.value.toFixed(2) - : "N. not"; + (typeof grade.student.value === "number" && !isNaN(grade.student.value)) + ? grade.student.value.toFixed(2) + : "N. not"; return ( Date: Sat, 25 Jan 2025 17:31:17 +0100 Subject: [PATCH 0362/1144] lint: no-empty --- eslint.config.js | 3 ++- src/views/welcome/ChangelogScreen.tsx | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index c6416da73..8bf0979da 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -46,7 +46,8 @@ module.exports = [ "no-var": "error", "no-irregular-whitespace": "error", "no-unneeded-ternary": "error", - "no-duplicate-imports": "error" + "no-duplicate-imports": "error", + "no-empty": "error" } } ]; diff --git a/src/views/welcome/ChangelogScreen.tsx b/src/views/welcome/ChangelogScreen.tsx index 612e394e3..f01ec21d2 100644 --- a/src/views/welcome/ChangelogScreen.tsx +++ b/src/views/welcome/ChangelogScreen.tsx @@ -17,6 +17,7 @@ import { PressableScale } from "react-native-pressable-scale"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { Screen } from "@/router/helpers/types"; +import { error } from "@/utils/logger/logger"; interface Feature { title: string; @@ -314,7 +315,9 @@ const ChangelogFeature: React.FC<{ feature: Feature, navigation: any, theme: any navigation.goBack(); navigation.navigate(feature.navigation); } - catch {} + catch (err){ + error("Fail with `feature.navigation`", "ChangelogScreen"); + } } } : undefined} > From be68e53c7a00177fa42b46edd6b2583f803f073a Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 25 Jan 2025 17:33:59 +0100 Subject: [PATCH 0363/1144] lint: no-useless-escape --- eslint.config.js | 3 ++- src/utils/format/extract_pronote_name.ts | 2 +- src/views/login/IdentityProvider/providers/UnivRennes2.tsx | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 8bf0979da..bcd7d6107 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -47,7 +47,8 @@ module.exports = [ "no-irregular-whitespace": "error", "no-unneeded-ternary": "error", "no-duplicate-imports": "error", - "no-empty": "error" + "no-empty": "error", + "no-useless-escape": "error" } } ]; diff --git a/src/utils/format/extract_pronote_name.ts b/src/utils/format/extract_pronote_name.ts index f9e335ea3..b860af8e6 100644 --- a/src/utils/format/extract_pronote_name.ts +++ b/src/utils/format/extract_pronote_name.ts @@ -7,7 +7,7 @@ function upperName (name: string): string { } export default function extract_pronote_name (fullName: string) { - const regex = /^([\p{L} \-]+) ([\p{L}\-]+.*)$/u; + const regex = /^([\p{L} -]+) ([\p{L}-]+.*)$/u; const match = fullName.match(regex); if (match) { diff --git a/src/views/login/IdentityProvider/providers/UnivRennes2.tsx b/src/views/login/IdentityProvider/providers/UnivRennes2.tsx index a0014dfee..5d436f9da 100644 --- a/src/views/login/IdentityProvider/providers/UnivRennes2.tsx +++ b/src/views/login/IdentityProvider/providers/UnivRennes2.tsx @@ -20,7 +20,7 @@ function extractStudentDataFromHTML (htmlString: string) { // Helper function to extract section data function extractSectionData (sectionName: string, dataObject: any) { - const sectionRegex = new RegExp(`

${sectionName}[\\s\\S]*?
[\\s\\S]*?<\/dl>`); + const sectionRegex = new RegExp(`

${sectionName}[\\s\\S]*?
[\\s\\S]*?
`); const sectionMatch = htmlString.match(sectionRegex); if (sectionMatch) { const divRegex = /
[\s\S]*?]*>(.*?)<\/dt>[\s\S]*?]*>(.*?)<\/dd>[\s\S]*?<\/div>/g; From 7ba3381a673d8c465150ba9da1a13d2ae6eedc80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 25 Jan 2025 19:03:43 +0100 Subject: [PATCH 0364/1144] =?UTF-8?q?d=C3=A9sactivation=20des=20requ=C3=AA?= =?UTF-8?q?tes=20en=20arri=C3=A8re-plan=20si=20l'utilisateur=20a=20d=C3=A9?= =?UTF-8?q?sactiv=C3=A9=20les=20notifs=20+=20d=C3=A9sactivation=20des=20no?= =?UTF-8?q?tifications=20de=20l'emploi=20du=20temps=20pour=20le=20moment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/BackgroundTasks.ts | 19 +++++++++++-------- src/views/settings/SettingsNotifications.tsx | 14 +++++++------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 1f68fc157..4ba82da5e 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -8,7 +8,7 @@ import { log, error, warn } from "@/utils/logger/logger"; import { getAccounts, getSwitchToFunction } from "./utils/accounts"; import { fetchHomeworks } from "./data/Homeworks"; import { fetchGrade } from "./data/Grades"; -import { fetchLessons } from "./data/Lessons"; +// import { fetchLessons } from "./data/Lessons"; import { fetchAttendance } from "./data/Attendance"; import { fetchEvaluation } from "./data/Evaluation"; @@ -55,13 +55,16 @@ const backgroundFetch = async () => { for (const account of accounts) { await switchTo(account); - - await fetchNews(); - await fetchHomeworks(); - await fetchGrade(); - await fetchLessons(); - await fetchAttendance(); - await fetchEvaluation(); + const notificationsTypesPermissions = account.personalization.notifications; + + if (notificationsTypesPermissions?.enabled) { + await fetchNews(); + await fetchHomeworks(); + await fetchGrade(); + // await fetchLessons(); // Disabled for now + await fetchAttendance(); + await fetchEvaluation(); + } } log("✅ Finish background fetch", "BackgroundEvent"); diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 2217543cf..7ff5bc163 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -3,7 +3,7 @@ import { Alert, Platform, ScrollView, Switch, View } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { - CalendarCheck, + // CalendarCheck, BookCheck, TrendingUp, Newspaper, @@ -85,12 +85,12 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ // Schoolary notifications const notificationSchoolary = [ - { - icon: } color={colors.primary} />, - title: "Emploi du temps du jour modifié", - message: "Le cours de Musique (10:00-11:00) a été annulé", - personalizationValue: "timetable", - }, + // { + // icon: } color={colors.primary} />, + // title: "Emploi du temps du jour modifié", + // message: "Le cours de Musique (10:00-11:00) a été annulé", + // personalizationValue: "timetable", + // }, { icon: } color={colors.primary} />, title: "Nouveau devoir", From 8386e716a7bdbab806e9bf3f5fe2657a20b0d457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 25 Jan 2025 19:04:37 +0100 Subject: [PATCH 0365/1144] suppression d'une condition inutile --- src/background/data/Attendance.ts | 5 +---- src/background/data/Evaluation.ts | 5 +---- src/background/data/Grades.ts | 5 +---- src/background/data/Homeworks.ts | 5 +---- src/background/data/Lessons.ts | 1 - src/background/data/News.ts | 5 +---- 6 files changed, 5 insertions(+), 21 deletions(-) diff --git a/src/background/data/Attendance.ts b/src/background/data/Attendance.ts index eac97b02d..14890d42b 100644 --- a/src/background/data/Attendance.ts +++ b/src/background/data/Attendance.ts @@ -71,10 +71,7 @@ const fetchAttendance = async (): Promise => { differences.observations.length + differences.punishments.length; - if ( - notificationsTypesPermissions?.enabled && - notificationsTypesPermissions?.attendance - ) { + if (notificationsTypesPermissions?.attendance) { switch (LAdifference) { case 0: break; diff --git a/src/background/data/Evaluation.ts b/src/background/data/Evaluation.ts index d3a7cee69..a9c5c9207 100644 --- a/src/background/data/Evaluation.ts +++ b/src/background/data/Evaluation.ts @@ -32,10 +32,7 @@ const fetchEvaluation = async (): Promise => { updatedEvaluation ?? [] ); - if ( - notificationsTypesPermissions?.enabled && - notificationsTypesPermissions?.evaluation - ) { + if (notificationsTypesPermissions?.evaluation) { switch (differences.length) { case 0: break; diff --git a/src/background/data/Grades.ts b/src/background/data/Grades.ts index b9226fbaa..c09bcf3f5 100644 --- a/src/background/data/Grades.ts +++ b/src/background/data/Grades.ts @@ -32,10 +32,7 @@ const fetchGrade = async (): Promise => { updatedGrade ?? [] ); - if ( - notificationsTypesPermissions?.enabled && - notificationsTypesPermissions?.grades - ) { + if (notificationsTypesPermissions?.grades) { switch (differences.length) { case 0: break; diff --git a/src/background/data/Homeworks.ts b/src/background/data/Homeworks.ts index a43c479f2..cb65a50a0 100644 --- a/src/background/data/Homeworks.ts +++ b/src/background/data/Homeworks.ts @@ -52,10 +52,7 @@ const fetchHomeworks = async (): Promise => { const differences = differencesHwSemaineActuelle.length + differencesHwSemaineProchaine.length; - if ( - notificationsTypesPermissions?.enabled && - notificationsTypesPermissions?.homeworks - ) { + if (notificationsTypesPermissions?.homeworks) { switch (differences) { case 0: break; diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index 5ae4f466b..da193d175 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -39,7 +39,6 @@ const fetchLessons = async (): Promise => { const oneHourBefore = lessonsDay[0]?.startTimestamp - 15 * 60 * 1000; if ( - notificationsTypesPermissions?.enabled && notificationsTypesPermissions?.timetable && now >= oneHourBefore && now < lessonsDay[0]?.startTimestamp diff --git a/src/background/data/News.ts b/src/background/data/News.ts index 7d7c420a1..4fffc900a 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -30,10 +30,7 @@ const fetchNews = async (): Promise => { const differences = getDifferences(currentNews, updatedNews); - if ( - notificationsTypesPermissions?.enabled && - notificationsTypesPermissions?.news - ) { + if (notificationsTypesPermissions?.news) { switch (differences.length) { case 0: break; From 99d955fb1ce61adc125040e721e42a9cae05308c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 25 Jan 2025 19:25:32 +0100 Subject: [PATCH 0366/1144] =?UTF-8?q?feat:=20int=C3=A9gration=20d'un=20bou?= =?UTF-8?q?ton=20pour=20pouvoir=20tester=20l'envoi=20de=20notifications?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/Notifications.ts | 8 ++++ src/views/settings/SettingsNotifications.tsx | 43 +++++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index 66a111f11..854ebbef7 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -20,6 +20,13 @@ const requestNotificationPermission = async () => { }; const createChannelNotification = async () => { + await notifee.createChannel({ + id: "Test", + name: "Test", + description: "Permet de tester les notifications", + sound: "default", + }); + await notifee.createChannelGroup({ id: "Papillon", name: "Notifications Scolaires", @@ -79,6 +86,7 @@ const createChannelNotification = async () => { const papillonNotify = async ( props: Notification, channelId: + | "Test" | "News" | "Homeworks" | "Grades" diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 7ff5bc163..8e7182a29 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -20,10 +20,12 @@ import { NativeText } from "@/components/Global/NativeComponents"; import NotificationContainerCard from "@/components/Settings/NotificationContainerCard"; -import { createChannelNotification, requestNotificationPermission } from "@/background/Notifications"; +import { createChannelNotification, papillonNotify, requestNotificationPermission } from "@/background/Notifications"; import { useCurrentAccount } from "@/stores/account"; import { PressableScale } from "react-native-pressable-scale"; import { useAlert } from "@/providers/AlertProvider"; +import ButtonCta from "@/components/FirstInstallation/ButtonCta"; +import PapillonSpinner from "@/components/Global/PapillonSpinner"; const SettingsNotifications: Screen<"SettingsNotifications"> = ({ navigation @@ -40,6 +42,7 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ const [enabled, setEnabled] = useState( notifications?.enabled || false ); + const [loading, setLoading] = useState(false); useEffect(() => { const handleNotificationPermission = async () => { const statut = await requestNotificationPermission(); @@ -151,7 +154,43 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ - + + + + + + ) : undefined + } + primary={!loading} + onPress={async () => { + setLoading(true); + await papillonNotify( + { + id: `${account.name}-test`, + title: `[${account.name}] Coucou, c'est Papillon 👋`, + subtitle: "Test", + body: "Si tu me vois, c'est que tout fonctionne correctement !", + ios: { + categoryId: account.name, + }, + }, + "Test" + ); + setTimeout(() => { + setLoading(false); + }, 500); + }} + /> + + {notificationSchoolary.map((notification, index) => ( Date: Sat, 25 Jan 2025 20:03:32 +0100 Subject: [PATCH 0367/1144] add width --- src/views/settings/SettingsNotifications.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 8e7182a29..d077ca352 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -170,6 +170,12 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ ) : undefined } primary={!loading} + style={{ + minWidth: null, + maxWidth: null, + width: "75%", + alignSelf: "center", + }} onPress={async () => { setLoading(true); await papillonNotify( From ac404f664751dd841198c1cf29deba608597685c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 25 Jan 2025 20:06:24 +0100 Subject: [PATCH 0368/1144] delete `` --- src/views/settings/SettingsNotifications.tsx | 79 ++++++++++---------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index d077ca352..66df5d9e3 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -155,47 +155,46 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ - - - - - ) : undefined - } - primary={!loading} - style={{ - minWidth: null, - maxWidth: null, - width: "75%", - alignSelf: "center", - }} - onPress={async () => { - setLoading(true); - await papillonNotify( - { - id: `${account.name}-test`, - title: `[${account.name}] Coucou, c'est Papillon 👋`, - subtitle: "Test", - body: "Si tu me vois, c'est que tout fonctionne correctement !", - ios: { - categoryId: account.name, - }, + + + + ) : undefined + } + primary={!loading} + style={{ + marginTop: 14, + minWidth: null, + maxWidth: null, + width: "75%", + alignSelf: "center", + }} + onPress={async () => { + setLoading(true); + await papillonNotify( + { + id: `${account.name}-test`, + title: `[${account.name}] Coucou, c'est Papillon 👋`, + subtitle: "Test", + body: "Si tu me vois, c'est que tout fonctionne correctement !", + ios: { + categoryId: account.name, }, - "Test" - ); - setTimeout(() => { - setLoading(false); - }, 500); - }} - /> - + }, + "Test" + ); + setTimeout(() => { + setLoading(false); + }, 500); + }} + /> {notificationSchoolary.map((notification, index) => ( From 6b04c16c52da462c8149c9b213ddab21cd55ada8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 25 Jan 2025 20:26:31 +0100 Subject: [PATCH 0369/1144] =?UTF-8?q?feat:=20modification=20de=20la=20page?= =?UTF-8?q?=20des=20param=C3=A8tres=20des=20notifications=20pour=20pouvoir?= =?UTF-8?q?=20g=C3=A9rer=20le=20background?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/SettingsNotifications.tsx | 99 +++++++++++++++++++- 1 file changed, 94 insertions(+), 5 deletions(-) diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 66df5d9e3..3ab2c5390 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -9,7 +9,9 @@ import { Newspaper, Info, NotepadText, - BookPlus + BookPlus, + CheckCircle2, + CircleAlert } from "lucide-react-native"; import { useSharedValue, withTiming } from "react-native-reanimated"; import { @@ -26,6 +28,9 @@ import { PressableScale } from "react-native-pressable-scale"; import { useAlert } from "@/providers/AlertProvider"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; +import * as TaskManager from "expo-task-manager"; +import { registerBackgroundTasks, unsetBackgroundFetch } from "@/background/BackgroundTasks"; +import { error } from "@/utils/logger/logger"; const SettingsNotifications: Screen<"SettingsNotifications"> = ({ navigation @@ -43,6 +48,8 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ notifications?.enabled || false ); const [loading, setLoading] = useState(false); + const [isBackgroundActive, setIsBackgroundActive] = useState(null); + useEffect(() => { const handleNotificationPermission = async () => { const statut = await requestNotificationPermission(); @@ -66,6 +73,24 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ handleNotificationPermission(); }, [enabled]); + useEffect(() => { + const checkBackgroundTaskStatus = async () => { + try { + const isRegistered = await TaskManager.isTaskRegisteredAsync("background-fetch"); + setTimeout(() => { + setIsBackgroundActive(isRegistered); + }, 500); + } catch (err) { + error(`❌ Failed to register background task: ${err}`, "BackgroundEvent"); + setIsBackgroundActive(false); + } + }; + + if (!loading) { + checkBackgroundTaskStatus(); + } + }, [isBackgroundActive, loading]); + // Animation states const opacity = useSharedValue(0); const invertedOpacity = useSharedValue(1); @@ -146,11 +171,74 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ <> - }> + + ) : isBackgroundActive === false ? ( + + ) : ( + + ) + } + onPress={() => { + if (Platform.OS === "ios") { + Alert.alert( + "Information", + "Le background permet à Papillon de te connecter à ton compte toutes les 15 minutes et te notifie en fonction des paramètres ci-dessous", + [ + { + text: "OK", + } + ] + ); + } else { + showAlert({ + title: "Information", + message: "Le background permet à Papillon de te connecter à ton compte toutes les 15 minutes et te notifie en fonction des paramètres ci-dessous", + actions: [ + { + title: "OK", + onPress: () => {}, + primary: true, + }, + ], + }); + } + }} + > - Toutes les 15 minutes, Papillon va se connecter à ton compte - et te notifie en fonction des paramètres ci-dessous + {isBackgroundActive === true + ? "Le background est actuellement actif." + : isBackgroundActive === false + ? "Le background n'est pas actif." + : "Vérification du background..."} + {isBackgroundActive !== null && ( + { + setLoading(true); + setIsBackgroundActive(null); + if (isBackgroundActive) { + await unsetBackgroundFetch(); + } + + await registerBackgroundTasks(); + setTimeout(() => { + setLoading(false); + }, 500); + }} + /> + )} @@ -158,7 +246,7 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ = ({ ) : undefined } + disabled={loading} primary={!loading} style={{ marginTop: 14, From 90568ed4c3db1f386379b2e7bf2681ca60fceee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Sat, 25 Jan 2025 21:51:46 +0100 Subject: [PATCH 0370/1144] Fix: faute de frappe --- src/utils/logger/logger.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/logger/logger.ts b/src/utils/logger/logger.ts index 48f9ec307..90c74bc8d 100644 --- a/src/utils/logger/logger.ts +++ b/src/utils/logger/logger.ts @@ -33,7 +33,7 @@ function get_file_from_stacktrace (stack: string): string .split(/\/\/localhost:\d\d\d\d\//g)[1] .split("//&")[0]; } catch (e) { - res = "UNKOWN"; + res = "UNKNOWN"; } return (res); } From 93a6f2f7dc6365eae10d09e7e9add826aa63024a Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 25 Jan 2025 22:16:02 +0100 Subject: [PATCH 0371/1144] feat(lint): add custom rules Ptn j'en ai chier pour avoir un truc propre pour les fix automatiques avec `eslint_rules/no-redundant-ternary.js` --- eslint.config.js | 9 +++- eslint_rules/no-redundant-ternary.js | 78 ++++++++++++++++++++++++++++ eslint_rules/plugin.js | 10 ++++ eslint_rules/redundant-logical.js | 63 ++++++++++++++++++++++ 4 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 eslint_rules/no-redundant-ternary.js create mode 100644 eslint_rules/plugin.js create mode 100644 eslint_rules/redundant-logical.js diff --git a/eslint.config.js b/eslint.config.js index bcd7d6107..cebdc1ca8 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -2,6 +2,8 @@ const stylistic = require("@stylistic/eslint-plugin"); const unusedImports = require("eslint-plugin-unused-imports"); const typescript = require("@typescript-eslint/parser"); +const custom_rules = require("./eslint_rules/plugin.js"); + module.exports = [ { // Ignored directory ignores: [ @@ -29,7 +31,8 @@ module.exports = [ }, plugins: { "@stylistic": stylistic, - "unused-imports": unusedImports + "unused-imports": unusedImports, + "custom": custom_rules }, rules: { "@stylistic/indent": ["error", 2], @@ -48,7 +51,9 @@ module.exports = [ "no-unneeded-ternary": "error", "no-duplicate-imports": "error", "no-empty": "error", - "no-useless-escape": "error" + "no-useless-escape": "error", + "custom/redundant-logical": "error", + "custom/no-redundant-ternary": "error" } } ]; diff --git a/eslint_rules/no-redundant-ternary.js b/eslint_rules/no-redundant-ternary.js new file mode 100644 index 000000000..8e00ce201 --- /dev/null +++ b/eslint_rules/no-redundant-ternary.js @@ -0,0 +1,78 @@ +module.exports = { + meta: { + type: "suggestion", + docs: { + description: "Detect and transform nested ternary operators into nullish coalescing chains", + category: "Best Practices", + recommended: false + }, + schema: [], + messages: { + redundantNestedTernary: "The nested ternary can be simplified using nullish coalescing operators" + }, + fixable: "code", + }, + create (context) { + function processConditionalExpression (node) { + const sourceCode = context.getSourceCode(); + + // Fonction récursive pour construire la chaîne de nullish coalescing + function buildNullishChain (node) { + if (node.type !== "ConditionalExpression") { + return sourceCode.getText(node); + } + + const test = sourceCode.getText(node.test); + + // Si c'est un ternaire imbriqué dans l'alternate + if (node.alternate.type === "ConditionalExpression") { + // Récupérer l'expression complète du consequent + const nextValueNode = node.alternate.consequent; + + // Si c'est un accès à une propriété imbriquée, ajouter l'optional chaining + if (nextValueNode.type === "MemberExpression") { + const object = sourceCode.getText(nextValueNode.object); + const property = sourceCode.getText(nextValueNode.property); + return `${test} ?? ${object}?.${property} ?? ${sourceCode.getText(node.alternate.alternate)}`; + } + + const nextValue = sourceCode.getText(nextValueNode); + return `${test} ?? ${nextValue} ?? ${sourceCode.getText(node.alternate.alternate)}`; + } + + return `${test} ?? ${sourceCode.getText(node.alternate)}`; + } + + // Vérifie si c'est un cas valide à transformer + function isValidTransform (node) { + if (!node || node.type !== "ConditionalExpression") return false; + + const test = sourceCode.getText(node.test); + const consequent = sourceCode.getText(node.consequent); + + // Soit test et consequent sont identiques + if (test === consequent) return true; + + // Soit c'est dans un alternate d'un ternaire valide + return !!(node.parent?.type === "ConditionalExpression" + && node.parent.alternate === node + && isValidTransform(node.parent)); + } + + // Vérifie si c'est un cas de ternaire imbriqué à transformer + if (isValidTransform(node)) { + context.report({ + node, + messageId: "redundantNestedTernary", + fix (fixer) { + return fixer.replaceText(node, buildNullishChain(node)); + } + }); + } + } + + return { + ConditionalExpression: processConditionalExpression + }; + } +}; diff --git a/eslint_rules/plugin.js b/eslint_rules/plugin.js new file mode 100644 index 000000000..5dd89aa5e --- /dev/null +++ b/eslint_rules/plugin.js @@ -0,0 +1,10 @@ +const redundant_logical = require("./redundant-logical.js"); +const no_redundant_ternary = require("./no-redundant-ternary.js"); + +const plugin = { + rules: { + "redundant-logical": redundant_logical, + "no-redundant-ternary": no_redundant_ternary + } +}; +module.exports = plugin; diff --git a/eslint_rules/redundant-logical.js b/eslint_rules/redundant-logical.js new file mode 100644 index 000000000..e286e882a --- /dev/null +++ b/eslint_rules/redundant-logical.js @@ -0,0 +1,63 @@ +const ts = require("typescript"); + +module.exports = { + meta: { + type: "problem", + docs: { + description: "Detect redundant `x || x.y` or `x && x` patterns", + category: "Best Practices", + recommended: false + }, + schema: [], + messages: { + redundantLogical: "The logical expression `{{ expression }}` is redundant. Consider simplifying it." + } + }, + create (context) { + const parserServices = context.parserServices; + if (!parserServices?.program) { + return {}; // Ignorer si TypeScript n'est pas activé + } + const typeChecker = parserServices.program.getTypeChecker(); + + return { + LogicalExpression (node) { + if (node.operator === "&&" || node.operator === "||") { + const left = context.getSourceCode().getText(node.left); + const right = context.getSourceCode().getText(node.right); + + if (right.startsWith(left)) { + const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node.left); + const type = typeChecker.getTypeAtLocation(tsNode); + const isNullable = (type.getFlags() & ts.TypeFlags.Null) !== 0; + + if ((node.operator === "&&" && isNullable) || + (node.operator === "||" && !isNullable)) { + return; + } + + // Extraire la partie après le point (ex: pour "x.y", on récupère "y") + const suffix = right.slice(left.length); + + context.report({ + node, + messageId: "redundantLogical", + data: { + expression: `${left} ${node.operator} ${right}` + }, + fix (fixer) { + if (node.operator === "&&") { + // Pour &&, utiliser l'opérateur optionnel + return fixer.replaceText(node, `${left}?${suffix}`); + } else { + // Pour ||, utiliser l'accès direct + return fixer.replaceText(node, `${right}`); + } + } + }); + } + } + } + }; + } +}; From 8c3f1eef2f5387eba00d4944b9acfad5524b34cc Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 25 Jan 2025 22:16:44 +0100 Subject: [PATCH 0372/1144] lint: simplify ternary --- src/components/Global/MissingItem.tsx | 4 ++-- src/components/Global/PapillonHeader.tsx | 4 ++-- src/components/Global/PapillonPicker.tsx | 2 +- src/providers/AlertProvider.tsx | 4 ++-- src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx | 2 +- src/views/welcome/AccountSelector.tsx | 7 +------ 6 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/components/Global/MissingItem.tsx b/src/components/Global/MissingItem.tsx index c55b2ad25..f9a6f8772 100644 --- a/src/components/Global/MissingItem.tsx +++ b/src/components/Global/MissingItem.tsx @@ -31,8 +31,8 @@ const MissingItem: React.FC = ({ gap: 4, paddingHorizontal: 40, }, style]} - entering={entering ? entering : FadeInUp} - exiting={exiting ? exiting : FadeOutDown} + entering={entering ?? FadeInUp} + exiting={exiting ?? FadeOutDown} > {!animatedEmoji ? ( diff --git a/src/components/Global/PapillonHeader.tsx b/src/components/Global/PapillonHeader.tsx index 7a31e1cc8..e0620eb80 100644 --- a/src/components/Global/PapillonHeader.tsx +++ b/src/components/Global/PapillonHeader.tsx @@ -28,7 +28,7 @@ const PapillonHeader: React.FC = ({ return ( = ({ alignItems: "center", paddingHorizontal: 16, zIndex: 10000, - paddingTop: topPadding ?( topPadding ) : 0, + paddingTop: topPadding ?? 0, }} > {route.params?.outsideNav && Platform.OS !== "ios" && ( diff --git a/src/components/Global/PapillonPicker.tsx b/src/components/Global/PapillonPicker.tsx index 0144790ca..8f33f582d 100644 --- a/src/components/Global/PapillonPicker.tsx +++ b/src/components/Global/PapillonPicker.tsx @@ -98,7 +98,7 @@ const PapillonPicker: React.FC = ({ const isNotString = typeof item !== "string"; const label = isNotString ? item.label : item; - const icon: null | React.ReactNode = isNotString ? (item.icon ? item.icon: null) : null; + const icon: null | React.ReactNode = isNotString ? (item.icon ?? null) : null; const onPressItem = isNotString ? item.onPress : null; diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index 810608d82..0a51bd7ac 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -194,7 +194,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { styles.button, primary && styles.primaryButton, primary && { - backgroundColor: backgroundColor ? backgroundColor : colors.primary, + backgroundColor: backgroundColor ?? colors.primary, }, danger && { backgroundColor: "#b62000", @@ -204,7 +204,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { } ]} > - {icon ? icon : null} + {icon ?? null} {title} diff --git a/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx b/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx index cb9495c7f..5b71aee8c 100644 --- a/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx +++ b/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx @@ -57,7 +57,7 @@ const EcoleDirecteCredentials: Screen<"EcoleDirecteCredentials"> = ({ navigation setCachedPassword(password); } - const accounts = await login(currentSession, password ? password : cachedPassword); + const accounts = await login(currentSession, password ?? cachedPassword); const account = accounts[0]; // NOTE: We only support single accounts for now. //TODO: Support multiple accounts in ED setAccessToken(currentSession, account); diff --git a/src/views/welcome/AccountSelector.tsx b/src/views/welcome/AccountSelector.tsx index 2f26efd34..f5c542124 100644 --- a/src/views/welcome/AccountSelector.tsx +++ b/src/views/welcome/AccountSelector.tsx @@ -447,12 +447,7 @@ const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { {account.studentName.first} {account.studentName.last} - {account.schoolName ? - account.schoolName : - account.identityProvider ? - account.identityProvider.name : - "Compte local" - } + {account.schoolName ?? account.identityProvider?.name ?? "Compte local"} From 11e139a6bf846695594abb2006a12a62c163bdbb Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 25 Jan 2025 22:53:29 +0100 Subject: [PATCH 0373/1144] lint --- src/components/Grades/GradeModal.tsx | 31 ++++++++++++++----------- src/components/Settings/ReelGallery.tsx | 2 +- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/components/Grades/GradeModal.tsx b/src/components/Grades/GradeModal.tsx index be1e21ab3..11c80c948 100644 --- a/src/components/Grades/GradeModal.tsx +++ b/src/components/Grades/GradeModal.tsx @@ -4,19 +4,24 @@ import { View, Image, Text, - Alert, ScrollView, StyleSheet, Dimensions + Alert, + ScrollView, + StyleSheet, + Dimensions, + Share } from "react-native"; -import {Download, Trash2, Ellipsis} from "lucide-react-native"; +import { Download, Trash2, Ellipsis } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { BlurView } from "expo-blur"; -import * as Sharing from "expo-sharing"; import * as FileSystem from "expo-file-system"; import * as MediaLibrary from "expo-media-library"; -import {PressableScale} from "react-native-pressable-scale"; -import {NativeText} from "@/components/Global/NativeComponents"; -import {Reel} from "@/services/shared/Reel"; -import {captureRef} from "react-native-view-shot"; +import { PressableScale } from "react-native-pressable-scale"; +import { NativeText } from "@/components/Global/NativeComponents"; +import { Reel } from "@/services/shared/Reel"; +import { captureRef } from "react-native-view-shot"; import Constants from "expo-constants"; +import { Social, type ShareSingleOptions } from "react-native-share"; + interface GradeModalProps { isVisible: boolean; reel: Reel; @@ -44,7 +49,7 @@ const GradeModal: React.FC = ({ const insets = useSafeAreaInsets(); const stickersRef = React.useRef(null); - const shareToSocial = async (option) => { + const shareToSocial = async (option: ShareSingleOptions) => { const isExpoGo = Constants.appOwnership === "expo"; if (isExpoGo) { Alert.alert("Fonctionnalité indisponible", "Cette fonctionnalité n'est pas disponible dans Expo Go. Pour l'utiliser, tu peux tester l'application sur ton propre appareil."); @@ -167,7 +172,7 @@ const GradeModal: React.FC = ({ appId: "497734022878553", stickerImage: `data:image/png;base64,${stickers}`, backgroundImage: `data:image/png;base64,${reel.imagewithouteffect}`, - social: "instagramstories", + social: Social.InstagramStories, }); }} > @@ -188,10 +193,10 @@ const GradeModal: React.FC = ({ gap: 8, }} onPress={async () => { - await Share.open({ - url: `data:image/png;base64,${reel.image}`, - type: "image/png", + await Share.share({ message: "Voici ma note de " + reel.grade.value + "/" + reel.grade.outOf + " en " + reel.subjectdata.pretty + " ! Qu'en penses-tu ?", + url: `data:image/png;base64,${reel.image}`, + title: "Partager ma note", }); }} > @@ -381,4 +386,4 @@ const styles = StyleSheet.create({ }, }); -export default GradeModal; \ No newline at end of file +export default GradeModal; diff --git a/src/components/Settings/ReelGallery.tsx b/src/components/Settings/ReelGallery.tsx index c8c815e83..309c3b622 100644 --- a/src/components/Settings/ReelGallery.tsx +++ b/src/components/Settings/ReelGallery.tsx @@ -133,7 +133,7 @@ const ReelGallery = ({ reels }: ReelGalleryProps) => { {selectedReel && ( setSelectedReel(null)} DeleteGrade={() => selectedReel.id && deleteReel(selectedReel.id)} /> From 4ad231e487a38bc6e36b06794815c096ab29abae Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 25 Jan 2025 23:01:03 +0100 Subject: [PATCH 0374/1144] chore: update `Pawote` to v1.4.1 --- package-lock.json | 22 ++++++++++++++++------ package.json | 2 +- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index d0c1005e7..43830c73a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.7.2", + "version": "7.8.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.7.2", + "version": "7.8.0", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.3", "@candlefinance/app-icon": "^0.4.5", @@ -71,7 +71,7 @@ "openid-client": "^5.7.0", "pawdirecte": "^1.7.1", "pawnilim": "^0.2.0", - "pawnote": "^1.4.0", + "pawnote": "^1.4.1", "pawrd": "^0.6.1", "react": "18.2.0", "react-native": "^0.74.3", @@ -88,6 +88,7 @@ "react-native-reanimated": "~3.10.1", "react-native-safe-area-context": "4.10.5", "react-native-screens": "3.31.1", + "react-native-share": "^12.0.3", "react-native-svg": "^15.2.0", "react-native-url-polyfill": "^2.0.0", "react-native-view-shot": "3.8.0", @@ -13938,9 +13939,9 @@ } }, "node_modules/pawnote": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/pawnote/-/pawnote-1.4.0.tgz", - "integrity": "sha512-AXf7jzf48oeTSwjo+7e2xBaAFitlZE3m4uc8Hpr3zEKiP+F7sZmLzZZozaV2fmvSVKE4PMXjAnYtfKPmE2TWQg==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/pawnote/-/pawnote-1.4.1.tgz", + "integrity": "sha512-jdFHfyZ7tIRJ03etegs/ExgW2F2KZDPbOq6atjs6Fi1DINl893QLfVIxLwADb+tGQbYifYwwOurv4QZQ1OJZ8Q==", "license": "GPL-3.0-or-later", "dependencies": { "@literate.ink/utilities": "1.0.0-10641118381.1", @@ -14850,6 +14851,15 @@ "react-native": "*" } }, + "node_modules/react-native-share": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/react-native-share/-/react-native-share-12.0.3.tgz", + "integrity": "sha512-Bg96AjouSbcpdlI/AzWXMBwpjyWcm4NvGr49mSF1cz8aw8E1sMXwpsHa6I841SJML8Im6sRN3aXnK+/QManrtQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, "node_modules/react-native-svg": { "version": "15.8.0", "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.8.0.tgz", diff --git a/package.json b/package.json index 0ce3a4bd0..dfae337c6 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "openid-client": "^5.7.0", "pawdirecte": "^1.7.1", "pawnilim": "^0.2.0", - "pawnote": "^1.4.0", + "pawnote": "^1.4.1", "pawrd": "^0.6.1", "react": "18.2.0", "react-native": "^0.74.3", From 77222562e420605e77ee3615c8aeedbc054bbabe Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 25 Jan 2025 22:53:29 +0100 Subject: [PATCH 0375/1144] lint --- src/components/Grades/GradeModal.tsx | 29 +++++++++++++++---------- src/components/Settings/ReelGallery.tsx | 2 +- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/components/Grades/GradeModal.tsx b/src/components/Grades/GradeModal.tsx index b718e6de2..11c80c948 100644 --- a/src/components/Grades/GradeModal.tsx +++ b/src/components/Grades/GradeModal.tsx @@ -4,19 +4,24 @@ import { View, Image, Text, - Alert, ScrollView, StyleSheet, Dimensions + Alert, + ScrollView, + StyleSheet, + Dimensions, + Share } from "react-native"; -import {Download, Trash2, Ellipsis} from "lucide-react-native"; +import { Download, Trash2, Ellipsis } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { BlurView } from "expo-blur"; -import * as Sharing from "expo-sharing"; import * as FileSystem from "expo-file-system"; import * as MediaLibrary from "expo-media-library"; -import {PressableScale} from "react-native-pressable-scale"; -import {NativeText} from "@/components/Global/NativeComponents"; -import {Reel} from "@/services/shared/Reel"; -import {captureRef} from "react-native-view-shot"; +import { PressableScale } from "react-native-pressable-scale"; +import { NativeText } from "@/components/Global/NativeComponents"; +import { Reel } from "@/services/shared/Reel"; +import { captureRef } from "react-native-view-shot"; import Constants from "expo-constants"; +import { Social, type ShareSingleOptions } from "react-native-share"; + interface GradeModalProps { isVisible: boolean; reel: Reel; @@ -44,7 +49,7 @@ const GradeModal: React.FC = ({ const insets = useSafeAreaInsets(); const stickersRef = React.useRef(null); - const shareToSocial = async (option) => { + const shareToSocial = async (option: ShareSingleOptions) => { const isExpoGo = Constants.appOwnership === "expo"; if (isExpoGo) { Alert.alert("Fonctionnalité indisponible", "Cette fonctionnalité n'est pas disponible dans Expo Go. Pour l'utiliser, tu peux tester l'application sur ton propre appareil."); @@ -167,7 +172,7 @@ const GradeModal: React.FC = ({ appId: "497734022878553", stickerImage: `data:image/png;base64,${stickers}`, backgroundImage: `data:image/png;base64,${reel.imagewithouteffect}`, - social: "instagramstories", + social: Social.InstagramStories, }); }} > @@ -188,10 +193,10 @@ const GradeModal: React.FC = ({ gap: 8, }} onPress={async () => { - await Share.open({ - url: `data:image/png;base64,${reel.image}`, - type: "image/png", + await Share.share({ message: "Voici ma note de " + reel.grade.value + "/" + reel.grade.outOf + " en " + reel.subjectdata.pretty + " ! Qu'en penses-tu ?", + url: `data:image/png;base64,${reel.image}`, + title: "Partager ma note", }); }} > diff --git a/src/components/Settings/ReelGallery.tsx b/src/components/Settings/ReelGallery.tsx index deeceff85..db67ca95b 100644 --- a/src/components/Settings/ReelGallery.tsx +++ b/src/components/Settings/ReelGallery.tsx @@ -139,7 +139,7 @@ const ReelGallery = ({ reels }: ReelGalleryProps) => { {selectedReel && ( setSelectedReel(null)} DeleteGrade={() => selectedReel.id && deleteReel(selectedReel.id)} /> From f22cfc13e1d37767f23ee1646a20acef790717d4 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 25 Jan 2025 23:04:24 +0100 Subject: [PATCH 0376/1144] lint: missing `Maximize2` --- src/views/account/Grades/Document.tsx | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/views/account/Grades/Document.tsx b/src/views/account/Grades/Document.tsx index bca3468e5..2bd25c1ab 100644 --- a/src/views/account/Grades/Document.tsx +++ b/src/views/account/Grades/Document.tsx @@ -1,8 +1,8 @@ -import {NativeItem, NativeList, NativeListHeader, NativeText,} from "@/components/Global/NativeComponents"; -import {getSubjectData} from "@/services/shared/Subject"; -import {useTheme} from "@react-navigation/native"; -import React, {useCallback, useEffect, useLayoutEffect, useState} from "react"; -import {Image, Platform, ScrollView, Text, TouchableOpacity, View} from "react-native"; +import { NativeItem, NativeList, NativeListHeader, NativeText, } from "@/components/Global/NativeComponents"; +import { getSubjectData } from "@/services/shared/Subject"; +import { useTheme } from "@react-navigation/native"; +import { useCallback, useEffect, useLayoutEffect, useState } from "react"; +import { Image, Platform, ScrollView, Text, TouchableOpacity, View } from "react-native"; import * as StoreReview from "expo-store-review"; import { Asterisk, @@ -11,15 +11,16 @@ import { School, UserMinus, UserPlus, - Users + Users, + Maximize2 } from "lucide-react-native"; import { getAverageDiffGrade } from "@/utils/grades/getAverages"; import type { AverageDiffGrade } from "@/utils/grades/getAverages"; import { Screen } from "@/router/helpers/types"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; import AsyncStorage from "@react-native-async-storage/async-storage"; -import {useGradesStore} from "@/stores/grades"; -import {LinearGradient} from "expo-linear-gradient"; +import { useGradesStore } from "@/stores/grades"; +import { LinearGradient } from "expo-linear-gradient"; import AnimatedEmoji from "@/components/Grades/AnimatedEmoji"; import GradeModal from "@/components/Grades/GradeModal"; @@ -525,4 +526,4 @@ const GradeDocument: Screen<"GradeDocument"> = ({ route, navigation }) => { ); }; -export default GradeDocument; \ No newline at end of file +export default GradeDocument; From c31a89e11aa6b2ed62427c6fb0a19eb4e3fe91b3 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 25 Jan 2025 23:23:35 +0100 Subject: [PATCH 0377/1144] lint --- src/components/Global/PapillonPicker.tsx | 12 ++++---- src/components/Grades/GradeModal.tsx | 31 ++++++++++++-------- src/components/Settings/ReelGallery.tsx | 2 +- src/views/account/Grades/Subject/Subject.tsx | 8 +++-- 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/components/Global/PapillonPicker.tsx b/src/components/Global/PapillonPicker.tsx index 93777c422..0144790ca 100644 --- a/src/components/Global/PapillonPicker.tsx +++ b/src/components/Global/PapillonPicker.tsx @@ -10,17 +10,19 @@ import { NativeText } from "./NativeComponents"; import { BlurView } from "expo-blur"; import { Check } from "lucide-react-native"; -type PickerData = string[] | { label: string, icon?: JSX.Element, onPress: () => unknown, checked?: boolean }[]; +export type PickerDataItem = string | { label: string, icon?: JSX.Element, onPress?: () => unknown, checked?: boolean }; + +type PickerData = PickerDataItem[]; interface PapillonPickerProps { children: React.ReactNode data: PickerData - selected?: string + selected?: PickerDataItem contentContainerStyle?: StyleProp>> delay?: number, direction?: "left" | "right", animated?: boolean, - onSelectionChange?: (item: string) => unknown + onSelectionChange?: any } const PapillonPicker: React.FC = ({ @@ -37,7 +39,7 @@ const PapillonPicker: React.FC = ({ const [contentHeight, setContentHeight] = useState(0); const [opened, setOpened] = useState(false); - const handleSelectionChange = (item: string) => { + const handleSelectionChange = (item: PickerDataItem) => { if (onSelectionChange) { setTimeout(() => { onSelectionChange(item); @@ -115,7 +117,7 @@ const PapillonPicker: React.FC = ({ onPressItem(); } : () => { setOpened(false); - handleSelectionChange(item as string); + handleSelectionChange(item); }} style={[ styles.item diff --git a/src/components/Grades/GradeModal.tsx b/src/components/Grades/GradeModal.tsx index be1e21ab3..11c80c948 100644 --- a/src/components/Grades/GradeModal.tsx +++ b/src/components/Grades/GradeModal.tsx @@ -4,19 +4,24 @@ import { View, Image, Text, - Alert, ScrollView, StyleSheet, Dimensions + Alert, + ScrollView, + StyleSheet, + Dimensions, + Share } from "react-native"; -import {Download, Trash2, Ellipsis} from "lucide-react-native"; +import { Download, Trash2, Ellipsis } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { BlurView } from "expo-blur"; -import * as Sharing from "expo-sharing"; import * as FileSystem from "expo-file-system"; import * as MediaLibrary from "expo-media-library"; -import {PressableScale} from "react-native-pressable-scale"; -import {NativeText} from "@/components/Global/NativeComponents"; -import {Reel} from "@/services/shared/Reel"; -import {captureRef} from "react-native-view-shot"; +import { PressableScale } from "react-native-pressable-scale"; +import { NativeText } from "@/components/Global/NativeComponents"; +import { Reel } from "@/services/shared/Reel"; +import { captureRef } from "react-native-view-shot"; import Constants from "expo-constants"; +import { Social, type ShareSingleOptions } from "react-native-share"; + interface GradeModalProps { isVisible: boolean; reel: Reel; @@ -44,7 +49,7 @@ const GradeModal: React.FC = ({ const insets = useSafeAreaInsets(); const stickersRef = React.useRef(null); - const shareToSocial = async (option) => { + const shareToSocial = async (option: ShareSingleOptions) => { const isExpoGo = Constants.appOwnership === "expo"; if (isExpoGo) { Alert.alert("Fonctionnalité indisponible", "Cette fonctionnalité n'est pas disponible dans Expo Go. Pour l'utiliser, tu peux tester l'application sur ton propre appareil."); @@ -167,7 +172,7 @@ const GradeModal: React.FC = ({ appId: "497734022878553", stickerImage: `data:image/png;base64,${stickers}`, backgroundImage: `data:image/png;base64,${reel.imagewithouteffect}`, - social: "instagramstories", + social: Social.InstagramStories, }); }} > @@ -188,10 +193,10 @@ const GradeModal: React.FC = ({ gap: 8, }} onPress={async () => { - await Share.open({ - url: `data:image/png;base64,${reel.image}`, - type: "image/png", + await Share.share({ message: "Voici ma note de " + reel.grade.value + "/" + reel.grade.outOf + " en " + reel.subjectdata.pretty + " ! Qu'en penses-tu ?", + url: `data:image/png;base64,${reel.image}`, + title: "Partager ma note", }); }} > @@ -381,4 +386,4 @@ const styles = StyleSheet.create({ }, }); -export default GradeModal; \ No newline at end of file +export default GradeModal; diff --git a/src/components/Settings/ReelGallery.tsx b/src/components/Settings/ReelGallery.tsx index deeceff85..db67ca95b 100644 --- a/src/components/Settings/ReelGallery.tsx +++ b/src/components/Settings/ReelGallery.tsx @@ -139,7 +139,7 @@ const ReelGallery = ({ reels }: ReelGalleryProps) => { {selectedReel && ( setSelectedReel(null)} DeleteGrade={() => selectedReel.id && deleteReel(selectedReel.id)} /> diff --git a/src/views/account/Grades/Subject/Subject.tsx b/src/views/account/Grades/Subject/Subject.tsx index a7b97dd21..0a2173a00 100644 --- a/src/views/account/Grades/Subject/Subject.tsx +++ b/src/views/account/Grades/Subject/Subject.tsx @@ -109,7 +109,11 @@ const Subject: React.FC = ({ textTransform: "uppercase", }} > - {sortings[sorting].label} + { + typeof sortings[sorting] === "string" + ? sortings[sorting] + : sortings[sorting].label + } {isLoading && ( = ({ ); }; -export default Subject; \ No newline at end of file +export default Subject; From 2b92b78200697654efaf8e248e2ef093ee541f73 Mon Sep 17 00:00:00 2001 From: godetremy Date: Sun, 26 Jan 2025 13:49:35 +0100 Subject: [PATCH 0378/1144] feat(PapiReal): Show warning when no authorization --- .../account/Grades/Modals/GradeReaction.tsx | 163 +++++++++++------- 1 file changed, 101 insertions(+), 62 deletions(-) diff --git a/src/views/account/Grades/Modals/GradeReaction.tsx b/src/views/account/Grades/Modals/GradeReaction.tsx index 956bc4675..d41b415f1 100644 --- a/src/views/account/Grades/Modals/GradeReaction.tsx +++ b/src/views/account/Grades/Modals/GradeReaction.tsx @@ -1,15 +1,18 @@ -import React, { useEffect, useLayoutEffect, useRef, useState } from "react"; -import { Text, View, StyleSheet, TouchableOpacity, Image, Alert } from "react-native"; -import { CameraView, useCameraPermissions, PermissionStatus } from "expo-camera"; +import React, {useEffect, useLayoutEffect, useRef, useState} from "react"; +import {Alert, Image, Linking, StyleSheet, Text, TouchableOpacity, View} from "react-native"; +import {CameraView, PermissionStatus, useCameraPermissions} from "expo-camera"; import * as MediaLibrary from "expo-media-library"; -import { X } from "lucide-react-native"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; -import { captureRef } from "react-native-view-shot"; -import { Screen } from "@/router/helpers/types"; -import { getSubjectData } from "@/services/shared/Subject"; -import { useGradesStore } from "@/stores/grades"; -import { Reel } from "@/services/shared/Reel"; +import {Check, X} from "lucide-react-native"; +import {useSafeAreaInsets} from "react-native-safe-area-context"; +import {captureRef} from "react-native-view-shot"; +import {Screen} from "@/router/helpers/types"; +import {getSubjectData} from "@/services/shared/Subject"; +import {useGradesStore} from "@/stores/grades"; +import {Reel} from "@/services/shared/Reel"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; +import {NativeText} from "@/components/Global/NativeComponents"; +import ButtonCta from "@/components/FirstInstallation/ButtonCta"; +import Constants from "expo-constants"; // Types interface SubjectData { @@ -77,25 +80,36 @@ const GradeReaction: Screen<"GradeReaction"> = ({ navigation, route }) => { pretty: "Matière inconnue", emoji: "❓", }); + const [isMediaLibraryPermissionGranted, setIsMediaLibraryPermissionGranted] = useState(PermissionStatus.UNDETERMINED); + const [isCameraPermissionGranted, setIsCameraPermissionGranted] = useState(PermissionStatus.UNDETERMINED); + + const setupPermissions = async () => { + setIsMediaLibraryPermissionGranted(mediaLibraryPermission?.status); + setIsCameraPermissionGranted(cameraPermission?.status); + if (isMediaLibraryPermissionGranted !== PermissionStatus.GRANTED) { + await requestMediaLibraryPermission(); + } + if (isCameraPermissionGranted !== PermissionStatus.GRANTED) { + await requestCameraPermission(); + } + }; // Setup permissions useEffect(() => { - const setupPermissions = async () => { - if (mediaLibraryPermission?.status !== PermissionStatus.GRANTED) { - await requestMediaLibraryPermission(); - } - if (cameraPermission?.status !== PermissionStatus.GRANTED) { - await requestCameraPermission(); - } - }; setupPermissions(); - }, [mediaLibraryPermission, cameraPermission, requestMediaLibraryPermission, requestCameraPermission]); + }, [mediaLibraryPermission?.status, cameraPermission?.status]); // Fetch subject data useEffect(() => { setSubjectData(getSubjectData(grade.subjectName)); }, [grade.subjectName]); + // Volume button to take picture + useEffect(() => { + if (Constants.appOwnership === "expo") return; + + }, []); + useLayoutEffect(() => { navigation.setOptions({ headerRight: () => ( @@ -151,55 +165,80 @@ const GradeReaction: Screen<"GradeReaction"> = ({ navigation, route }) => { } }; - return ( - - - + 🫣 + On ne te voit pas… + Pour réagir à tes notes, Papillon a besoin d'un accès à ta caméra et à ta librairie photo. + + + : null} + onPress={() => {isCameraPermissionGranted != PermissionStatus.GRANTED && Linking.openSettings();}} /> - {capturedImage ? ( - - ) : ( - - )} - - - - {subjectData.emoji} - - - - {subjectData.pretty} - - - {new Date(grade.timestamp).toLocaleDateString()} - - - - {grade.student.value} - /{grade.outOf.value} + : null} + onPress={() => {isMediaLibraryPermissionGranted != PermissionStatus.GRANTED && Linking.openSettings();}} + /> + + + ) + : + ( + + + + {capturedImage ? ( + + ) : ( + + )} + + + + {subjectData.emoji} + + + + {subjectData.pretty} + + + {new Date(grade.timestamp).toLocaleDateString()} + + + + {grade.student.value} + /{grade.outOf.value} + - - {isLoading && ( - - - Enregistrement en cours... - - )} + {isLoading && ( + + + Enregistrement en cours... + + )} - {!capturedImage && !isLoading && ( - - - - )} - - ); + {!capturedImage && !isLoading && ( + + + + )} + + ); }; const styles = StyleSheet.create({ From 4d2df5e05a4c104e61dc32ed86732f9f6f80dc56 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sun, 26 Jan 2025 15:53:58 +0100 Subject: [PATCH 0379/1144] chore: package lock file --- package-lock.json | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index ef8f26b28..9072a22c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.7.2", + "version": "7.8.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.7.2", + "version": "7.8.0", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.3", "@candlefinance/app-icon": "^0.4.5", @@ -88,6 +88,7 @@ "react-native-reanimated": "~3.10.1", "react-native-safe-area-context": "4.10.5", "react-native-screens": "3.31.1", + "react-native-share": "^12.0.3", "react-native-svg": "^15.2.0", "react-native-url-polyfill": "^2.0.0", "react-native-view-shot": "3.8.0", @@ -14850,6 +14851,15 @@ "react-native": "*" } }, + "node_modules/react-native-share": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/react-native-share/-/react-native-share-12.0.3.tgz", + "integrity": "sha512-Bg96AjouSbcpdlI/AzWXMBwpjyWcm4NvGr49mSF1cz8aw8E1sMXwpsHa6I841SJML8Im6sRN3aXnK+/QManrtQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, "node_modules/react-native-svg": { "version": "15.8.0", "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.8.0.tgz", From 106b6d88170c305e71e19d81a91758af4914a89e Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sun, 26 Jan 2025 15:55:52 +0100 Subject: [PATCH 0380/1144] fix: don't import anything from `react-native-share` --- src/components/Grades/GradeModal.tsx | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/components/Grades/GradeModal.tsx b/src/components/Grades/GradeModal.tsx index 11c80c948..9641e93ad 100644 --- a/src/components/Grades/GradeModal.tsx +++ b/src/components/Grades/GradeModal.tsx @@ -20,7 +20,6 @@ import { NativeText } from "@/components/Global/NativeComponents"; import { Reel } from "@/services/shared/Reel"; import { captureRef } from "react-native-view-shot"; import Constants from "expo-constants"; -import { Social, type ShareSingleOptions } from "react-native-share"; interface GradeModalProps { isVisible: boolean; @@ -29,6 +28,26 @@ interface GradeModalProps { DeleteGrade: () => void; } +interface ShareOptions { + appId: string; + urls?: string[]; + url?: string; + type?: string; + filename?: string; + message?: string; + title?: string; + subject?: string; + email?: string; + recipient?: string; + social: "facebook"|"facebookstories"|"pagesmanager"|"twitter"|"whatsapp" + |"whatsappbusiness" |"instagram"|"instagramstories"|"googleplus" + |"email"|"pinterest"|"linkedin"|"sms" |"telegram"|"snapchat" + |"messenger"|"viber"|"discord"; + forceDialog?: boolean; + stickerImage?: string; + backgroundImage?: string; +} + const convertToBase64 = async (uri: string): Promise => { const response = await fetch(uri); const blob = await response.blob(); @@ -49,7 +68,7 @@ const GradeModal: React.FC = ({ const insets = useSafeAreaInsets(); const stickersRef = React.useRef(null); - const shareToSocial = async (option: ShareSingleOptions) => { + const shareToSocial = async (option: ShareOptions) => { const isExpoGo = Constants.appOwnership === "expo"; if (isExpoGo) { Alert.alert("Fonctionnalité indisponible", "Cette fonctionnalité n'est pas disponible dans Expo Go. Pour l'utiliser, tu peux tester l'application sur ton propre appareil."); @@ -172,7 +191,7 @@ const GradeModal: React.FC = ({ appId: "497734022878553", stickerImage: `data:image/png;base64,${stickers}`, backgroundImage: `data:image/png;base64,${reel.imagewithouteffect}`, - social: Social.InstagramStories, + social: "instagramstories", }); }} > From 47529e4b409d62295bb01f7e999235bfc1927060 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sun, 26 Jan 2025 15:55:52 +0100 Subject: [PATCH 0381/1144] fix: don't import anything from `react-native-share` --- src/components/Grades/GradeModal.tsx | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/components/Grades/GradeModal.tsx b/src/components/Grades/GradeModal.tsx index 11c80c948..9641e93ad 100644 --- a/src/components/Grades/GradeModal.tsx +++ b/src/components/Grades/GradeModal.tsx @@ -20,7 +20,6 @@ import { NativeText } from "@/components/Global/NativeComponents"; import { Reel } from "@/services/shared/Reel"; import { captureRef } from "react-native-view-shot"; import Constants from "expo-constants"; -import { Social, type ShareSingleOptions } from "react-native-share"; interface GradeModalProps { isVisible: boolean; @@ -29,6 +28,26 @@ interface GradeModalProps { DeleteGrade: () => void; } +interface ShareOptions { + appId: string; + urls?: string[]; + url?: string; + type?: string; + filename?: string; + message?: string; + title?: string; + subject?: string; + email?: string; + recipient?: string; + social: "facebook"|"facebookstories"|"pagesmanager"|"twitter"|"whatsapp" + |"whatsappbusiness" |"instagram"|"instagramstories"|"googleplus" + |"email"|"pinterest"|"linkedin"|"sms" |"telegram"|"snapchat" + |"messenger"|"viber"|"discord"; + forceDialog?: boolean; + stickerImage?: string; + backgroundImage?: string; +} + const convertToBase64 = async (uri: string): Promise => { const response = await fetch(uri); const blob = await response.blob(); @@ -49,7 +68,7 @@ const GradeModal: React.FC = ({ const insets = useSafeAreaInsets(); const stickersRef = React.useRef(null); - const shareToSocial = async (option: ShareSingleOptions) => { + const shareToSocial = async (option: ShareOptions) => { const isExpoGo = Constants.appOwnership === "expo"; if (isExpoGo) { Alert.alert("Fonctionnalité indisponible", "Cette fonctionnalité n'est pas disponible dans Expo Go. Pour l'utiliser, tu peux tester l'application sur ton propre appareil."); @@ -172,7 +191,7 @@ const GradeModal: React.FC = ({ appId: "497734022878553", stickerImage: `data:image/png;base64,${stickers}`, backgroundImage: `data:image/png;base64,${reel.imagewithouteffect}`, - social: Social.InstagramStories, + social: "instagramstories", }); }} > From 0f8bd1257c6151e7362a83eb4dab5da2424f44e6 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sun, 26 Jan 2025 16:32:24 +0100 Subject: [PATCH 0382/1144] feat(logs): better logs reading --- src/utils/logger/logger.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/utils/logger/logger.ts b/src/utils/logger/logger.ts index 460ce3f82..161f9de40 100644 --- a/src/utils/logger/logger.ts +++ b/src/utils/logger/logger.ts @@ -122,12 +122,12 @@ async function get_logs (): Promise { if (res) value = JSON.parse(res); value.forEach((item) => { - let arr = item.split("]"); + const matchs = /\[([A-Z\s]+)\]\[(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d+Z)]\[(\S+)\] (.+)/gm.exec(item); returned.push({ - type: arr[0].replace("[", ""), - date: arr[1].replace("[", ""), - from: arr[2].replace("[", ""), - message: arr[3].trim() + type: matchs?.[1]! ?? "Unkown type", // The index 0 is used for the global match + date: matchs?.[2]! ?? "Unkown date", + from: matchs?.[3]! ?? "Unkown from", + message: matchs?.[4]! ?? "Unkown content" }); }); From c7e94f17fd7308585f22d8b032d8230448e04fbc Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sun, 26 Jan 2025 16:33:31 +0100 Subject: [PATCH 0383/1144] fix(logs): now, we need to use `startsWith` for `updateClasses` due to new log reading --- src/views/settings/SettingsDevLogs.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/settings/SettingsDevLogs.tsx b/src/views/settings/SettingsDevLogs.tsx index d9122843a..4091a05f1 100644 --- a/src/views/settings/SettingsDevLogs.tsx +++ b/src/views/settings/SettingsDevLogs.tsx @@ -186,7 +186,7 @@ const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { ) : log.message.toLowerCase().includes("read") ? ( - ) : log.message === "[timetable:updateClasses" ? ( + ) : log.message.startsWith("[timetable:updateClasses") ? ( ) : log.message.toLowerCase().includes("folder") ? ( @@ -207,7 +207,7 @@ const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { ? "#1F618D" : log.message.toLowerCase().includes("read") ? "#D4AC02" - : log.message === "[timetable:updateClasses" + : log.message.startsWith("[timetable:updateClasses") ? "#884EA0" : log.message.toLowerCase().includes("folder") ? "#CA6F1E" From d2470fe88dd15d2add3a6ea55eea0465dc1fdf7b Mon Sep 17 00:00:00 2001 From: godetremy Date: Sun, 26 Jan 2025 17:09:35 +0100 Subject: [PATCH 0384/1144] feat(PapiReal): Add delete warning alert --- src/components/Grades/GradeModal.tsx | 280 +++++++++++++++++++-------- 1 file changed, 203 insertions(+), 77 deletions(-) diff --git a/src/components/Grades/GradeModal.tsx b/src/components/Grades/GradeModal.tsx index be1e21ab3..dc57993fb 100644 --- a/src/components/Grades/GradeModal.tsx +++ b/src/components/Grades/GradeModal.tsx @@ -9,7 +9,6 @@ import { import {Download, Trash2, Ellipsis} from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { BlurView } from "expo-blur"; -import * as Sharing from "expo-sharing"; import * as FileSystem from "expo-file-system"; import * as MediaLibrary from "expo-media-library"; import {PressableScale} from "react-native-pressable-scale"; @@ -17,6 +16,9 @@ import {NativeText} from "@/components/Global/NativeComponents"; import {Reel} from "@/services/shared/Reel"; import {captureRef} from "react-native-view-shot"; import Constants from "expo-constants"; +import Animated, {Easing, FadeInRight, ZoomIn} from "react-native-reanimated"; +import PapillonBottomSheet from "@/components/Modals/PapillonBottomSheet"; +import {useTheme} from "@react-navigation/native"; interface GradeModalProps { isVisible: boolean; reel: Reel; @@ -43,6 +45,7 @@ const GradeModal: React.FC = ({ }) => { const insets = useSafeAreaInsets(); const stickersRef = React.useRef(null); + const [showDeleteWarning, setShowDeleteWarning] = React.useState(false); const shareToSocial = async (option) => { const isExpoGo = Constants.appOwnership === "expo"; @@ -72,6 +75,89 @@ const GradeModal: React.FC = ({ animationType="fade" onRequestClose={onClose} > + + + + + + + Veux-tu vraiment supprimer cette réaction ? + Cette action est irréversible. + + setShowDeleteWarning(false)} + > + + ANNULER + + + + + SUPPRIMER + + + + + = ({ gap: 20, }} > - + + + = ({ gap: 16, }} > - - - - - Enregistrer - - { - const compositeUri = await captureRef(stickersRef, { - format: "png", - quality: 1, - }); - const stickers = await convertToBase64(compositeUri); - await shareToSocial({ - appId: "497734022878553", - stickerImage: `data:image/png;base64,${stickers}`, - backgroundImage: `data:image/png;base64,${reel.imagewithouteffect}`, - social: "instagramstories", - }); - }} + + + + Enregistrer + + + - - Instagram - - { - await Share.open({ - url: `data:image/png;base64,${reel.image}`, - type: "image/png", - message: "Voici ma note de " + reel.grade.value + "/" + reel.grade.outOf + " en " + reel.subjectdata.pretty + " ! Qu'en penses-tu ?", - }); - }} + onPress={async () => { + const compositeUri = await captureRef(stickersRef, { + format: "png", + quality: 1, + }); + const stickers = await convertToBase64(compositeUri); + await shareToSocial({ + appId: "497734022878553", + stickerImage: `data:image/png;base64,${stickers}`, + backgroundImage: `data:image/png;base64,${reel.imagewithouteffect}`, + social: "instagramstories", + }); + }} + > + + Instagram + + + - { + if (Constants.appOwnership === "expo") { + Alert.alert("Fonctionnalité indisponible", "Cette fonctionnalité n'est pas disponible dans Expo Go. Pour l'utiliser, tu peux tester l'application sur ton propre appareil."); + return; + } + await require("react-native-share").default.open({ + url: `data:image/png;base64,${reel.image}`, + type: "image/png", + message: "Voici ma note de " + reel.grade.value + "/" + reel.grade.outOf + " en " + reel.subjectdata.pretty + " ! Qu'en penses-tu ?", + }); }} > - - - Autre - + + + + Autre + + = ({ alignItems: "center", borderRadius: 100, }} - onPress={DeleteGrade} + onPress={() => setShowDeleteWarning(true)} > From bb74c74dc336d572ac4eabb84a14dab10233d09f Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sun, 26 Jan 2025 17:52:52 +0100 Subject: [PATCH 0385/1144] Merge remote-tracking branch 'origin/main' into better-logs --- src/components/Grades/GradeModal.tsx | 283 +++++++++++++----- .../account/Grades/Modals/GradeReaction.tsx | 151 ++++++---- 2 files changed, 299 insertions(+), 135 deletions(-) diff --git a/src/components/Grades/GradeModal.tsx b/src/components/Grades/GradeModal.tsx index 9641e93ad..fceb209b2 100644 --- a/src/components/Grades/GradeModal.tsx +++ b/src/components/Grades/GradeModal.tsx @@ -7,8 +7,7 @@ import { Alert, ScrollView, StyleSheet, - Dimensions, - Share + Dimensions } from "react-native"; import { Download, Trash2, Ellipsis } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; @@ -20,7 +19,9 @@ import { NativeText } from "@/components/Global/NativeComponents"; import { Reel } from "@/services/shared/Reel"; import { captureRef } from "react-native-view-shot"; import Constants from "expo-constants"; - +import Animated, { Easing, FadeInRight, ZoomIn } from "react-native-reanimated"; +import PapillonBottomSheet from "@/components/Modals/PapillonBottomSheet"; +import { useTheme } from "@react-navigation/native"; interface GradeModalProps { isVisible: boolean; reel: Reel; @@ -67,6 +68,7 @@ const GradeModal: React.FC = ({ }) => { const insets = useSafeAreaInsets(); const stickersRef = React.useRef(null); + const [showDeleteWarning, setShowDeleteWarning] = React.useState(false); const shareToSocial = async (option: ShareOptions) => { const isExpoGo = Constants.appOwnership === "expo"; @@ -96,6 +98,89 @@ const GradeModal: React.FC = ({ animationType="fade" onRequestClose={onClose} > + + + + + + + Veux-tu vraiment supprimer cette réaction ? + Cette action est irréversible. + + setShowDeleteWarning(false)} + > + + ANNULER + + + + + SUPPRIMER + + + + + = ({ gap: 20, }} > - + + + = ({ gap: 16, }} > - - - - - Enregistrer - - { - const compositeUri = await captureRef(stickersRef, { - format: "png", - quality: 1, - }); - const stickers = await convertToBase64(compositeUri); - await shareToSocial({ - appId: "497734022878553", - stickerImage: `data:image/png;base64,${stickers}`, - backgroundImage: `data:image/png;base64,${reel.imagewithouteffect}`, - social: "instagramstories", - }); - }} + + + + Enregistrer + + + - - Instagram - - { - await Share.share({ - message: "Voici ma note de " + reel.grade.value + "/" + reel.grade.outOf + " en " + reel.subjectdata.pretty + " ! Qu'en penses-tu ?", - url: `data:image/png;base64,${reel.image}`, - title: "Partager ma note", - }); - }} + onPress={async () => { + const compositeUri = await captureRef(stickersRef, { + format: "png", + quality: 1, + }); + const stickers = await convertToBase64(compositeUri); + await shareToSocial({ + appId: "497734022878553", + stickerImage: `data:image/png;base64,${stickers}`, + backgroundImage: `data:image/png;base64,${reel.imagewithouteffect}`, + social: "instagramstories", + }); + }} + > + + Instagram + + + - { + if (Constants.appOwnership === "expo") { + Alert.alert("Fonctionnalité indisponible", "Cette fonctionnalité n'est pas disponible dans Expo Go. Pour l'utiliser, tu peux tester l'application sur ton propre appareil."); + return; + } + await require("react-native-share").default.open({ + url: `data:image/png;base64,${reel.image}`, + type: "image/png", + message: "Voici ma note de " + reel.grade.value + "/" + reel.grade.outOf + " en " + reel.subjectdata.pretty + " ! Qu'en penses-tu ?", + }); }} > - - - Autre - + + + + Autre + + = ({ alignItems: "center", borderRadius: 100, }} - onPress={DeleteGrade} + onPress={() => setShowDeleteWarning(true)} > diff --git a/src/views/account/Grades/Modals/GradeReaction.tsx b/src/views/account/Grades/Modals/GradeReaction.tsx index 956bc4675..3b1c60de0 100644 --- a/src/views/account/Grades/Modals/GradeReaction.tsx +++ b/src/views/account/Grades/Modals/GradeReaction.tsx @@ -1,8 +1,8 @@ import React, { useEffect, useLayoutEffect, useRef, useState } from "react"; -import { Text, View, StyleSheet, TouchableOpacity, Image, Alert } from "react-native"; -import { CameraView, useCameraPermissions, PermissionStatus } from "expo-camera"; +import { Alert, Image, Linking, StyleSheet, Text, TouchableOpacity, View } from "react-native"; +import { CameraView, PermissionStatus, useCameraPermissions } from "expo-camera"; import * as MediaLibrary from "expo-media-library"; -import { X } from "lucide-react-native"; +import { Check, X } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { captureRef } from "react-native-view-shot"; import { Screen } from "@/router/helpers/types"; @@ -10,6 +10,9 @@ import { getSubjectData } from "@/services/shared/Subject"; import { useGradesStore } from "@/stores/grades"; import { Reel } from "@/services/shared/Reel"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; +import { NativeText } from "@/components/Global/NativeComponents"; +import ButtonCta from "@/components/FirstInstallation/ButtonCta"; +import Constants from "expo-constants"; // Types interface SubjectData { @@ -77,25 +80,36 @@ const GradeReaction: Screen<"GradeReaction"> = ({ navigation, route }) => { pretty: "Matière inconnue", emoji: "❓", }); + const [isMediaLibraryPermissionGranted, setIsMediaLibraryPermissionGranted] = useState(PermissionStatus.UNDETERMINED); + const [isCameraPermissionGranted, setIsCameraPermissionGranted] = useState(PermissionStatus.UNDETERMINED); + + const setupPermissions = async () => { + setIsMediaLibraryPermissionGranted(mediaLibraryPermission?.status ?? PermissionStatus.UNDETERMINED); + setIsCameraPermissionGranted(cameraPermission?.status ?? PermissionStatus.UNDETERMINED); + if (isMediaLibraryPermissionGranted !== PermissionStatus.GRANTED) { + await requestMediaLibraryPermission(); + } + if (isCameraPermissionGranted !== PermissionStatus.GRANTED) { + await requestCameraPermission(); + } + }; // Setup permissions useEffect(() => { - const setupPermissions = async () => { - if (mediaLibraryPermission?.status !== PermissionStatus.GRANTED) { - await requestMediaLibraryPermission(); - } - if (cameraPermission?.status !== PermissionStatus.GRANTED) { - await requestCameraPermission(); - } - }; setupPermissions(); - }, [mediaLibraryPermission, cameraPermission, requestMediaLibraryPermission, requestCameraPermission]); + }, [mediaLibraryPermission?.status, cameraPermission?.status]); // Fetch subject data useEffect(() => { setSubjectData(getSubjectData(grade.subjectName)); }, [grade.subjectName]); + // Volume button to take picture + useEffect(() => { + if (Constants.appOwnership === "expo") return; + + }, []); + useLayoutEffect(() => { navigation.setOptions({ headerRight: () => ( @@ -151,55 +165,80 @@ const GradeReaction: Screen<"GradeReaction"> = ({ navigation, route }) => { } }; - return ( - - - + 🫣 + On ne te voit pas… + Pour réagir à tes notes, Papillon a besoin d'un accès à ta caméra et à ta librairie photo. + + + : undefined} + onPress={() => {isCameraPermissionGranted != PermissionStatus.GRANTED && Linking.openSettings();}} /> - {capturedImage ? ( - - ) : ( - - )} - - - - {subjectData.emoji} - - - - {subjectData.pretty} - - - {new Date(grade.timestamp).toLocaleDateString()} - - - - {grade.student.value} - /{grade.outOf.value} + : undefined} + onPress={() => {isMediaLibraryPermissionGranted != PermissionStatus.GRANTED && Linking.openSettings();}} + /> + + + ) + : + ( + + + + {capturedImage ? ( + + ) : ( + + )} + + + + {subjectData.emoji} + + + + {subjectData.pretty} + + + {new Date(grade.timestamp).toLocaleDateString()} + + + + {grade.student.value} + /{grade.outOf.value} + - - {isLoading && ( - - - Enregistrement en cours... - - )} + {isLoading && ( + + + Enregistrement en cours... + + )} - {!capturedImage && !isLoading && ( - - - - )} - - ); + {!capturedImage && !isLoading && ( + + + + )} + + ); }; const styles = StyleSheet.create({ @@ -320,4 +359,4 @@ const styles = StyleSheet.create({ }, }); -export default GradeReaction; \ No newline at end of file +export default GradeReaction; From 5db139fe0f367c5c9c07b91e276e5aa2981d64d5 Mon Sep 17 00:00:00 2001 From: godetremy Date: Sun, 26 Jan 2025 13:49:35 +0100 Subject: [PATCH 0386/1144] feat(PapiReal): Show warning when no authorization --- src/views/account/Grades/Modals/GradeReaction.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/account/Grades/Modals/GradeReaction.tsx b/src/views/account/Grades/Modals/GradeReaction.tsx index 3b1c60de0..24103a474 100644 --- a/src/views/account/Grades/Modals/GradeReaction.tsx +++ b/src/views/account/Grades/Modals/GradeReaction.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useLayoutEffect, useRef, useState } from "react"; -import { Alert, Image, Linking, StyleSheet, Text, TouchableOpacity, View } from "react-native"; -import { CameraView, PermissionStatus, useCameraPermissions } from "expo-camera"; +import { Text, View, Linking, StyleSheet, TouchableOpacity, Image, Alert } from "react-native"; +import { CameraView, useCameraPermissions, PermissionStatus } from "expo-camera"; import * as MediaLibrary from "expo-media-library"; import { Check, X } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; From b562a5ffe21ad4771bf864f7f2e8c89861724189 Mon Sep 17 00:00:00 2001 From: godetremy Date: Sun, 26 Jan 2025 18:59:58 +0100 Subject: [PATCH 0387/1144] feat(Settings): Add a link to suport --- src/views/settings/Settings.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index 9ca1a0ee3..e83523db5 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -33,7 +33,7 @@ import { Smile, SwatchBook, WandSparkles, - X + X, HelpCircle } from "lucide-react-native"; import { NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; @@ -216,6 +216,12 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { label: "Quoi de neuf ?", onPress: () => navigation.navigate("ChangelogScreen"), }, + { + icon: , + color: "#0E7CCB", + label: "Besoin d'aide ?", + onPress: () => openUrl("https://support.papillon.bzh/"), + }, { icon: , color: "#888888", From 9be4af2ed30b681be51d5d418863c5400422dc3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 26 Jan 2025 19:02:06 +0100 Subject: [PATCH 0388/1144] =?UTF-8?q?fix(SettingsTabs):=20am=C3=A9liorer?= =?UTF-8?q?=20le=20chargement=20des=20onglets=20personnalis=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/SettingsTabs.tsx | 37 ++++++++++++++++------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/views/settings/SettingsTabs.tsx b/src/views/settings/SettingsTabs.tsx index 45c9dc1fb..177c30d5b 100644 --- a/src/views/settings/SettingsTabs.tsx +++ b/src/views/settings/SettingsTabs.tsx @@ -117,22 +117,27 @@ const SettingsTabs = () => { const loadTabs = async () => { if (account.personalization.tabs) { const storedTabs = account.personalization.tabs; - const updatedTabs = defaultTabs - .filter((defaultTab) => - storedTabs.some((storedTab) => storedTab.name === defaultTab.tab) - ) - .map((defaultTab) => { - const storedTab = storedTabs.find((t) => t.name === defaultTab.tab); - return { - ...defaultTab, - enabled: storedTab ? storedTab.enabled : false, - installed: true, - }; - }); - - const newTabsFound: Tab[] = defaultTabs.filter((defaultTab) => !storedTabs.some((storedTab) => storedTab.name === defaultTab.tab)).map((tab) => ({ ...tab, installed: true })); - - setTabs(updatedTabs); + + const updatedTabs: Tab[] = storedTabs.map((storedTab) => { + const defaultTab = defaultTabs.find((defaultTab) => defaultTab.tab === storedTab.name); + return { + tab: storedTab.name, + label: defaultTab?.label || "Default Label", + icon: defaultTab?.icon || null, + enabled: storedTab.enabled, + installed: true, + }; + }); + + const newTabsFound: Tab[] = defaultTabs + .filter((defaultTab) => !storedTabs.some((storedTab) => storedTab.name === defaultTab.tab)) + .map((tab) => ({ + ...tab, + installed: true, + enabled: false, + })); + + setTabs([...updatedTabs, ...newTabsFound]); setNewTabs(newTabsFound); setShowNewTabsNotification(newTabsFound.length > 0); } else { From 55773d4401e924a97ab87cc9aa78ba7ae2a5243c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 26 Jan 2025 19:05:56 +0100 Subject: [PATCH 0389/1144] =?UTF-8?q?fix(SettingsTabs):=20suppression=20de?= =?UTF-8?q?=20l'animation=20+=20ajout=20de=20cl=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/SettingsTabs.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/views/settings/SettingsTabs.tsx b/src/views/settings/SettingsTabs.tsx index 177c30d5b..67ada766e 100644 --- a/src/views/settings/SettingsTabs.tsx +++ b/src/views/settings/SettingsTabs.tsx @@ -378,9 +378,7 @@ const SettingsTabs = () => { - + {showNewTabsNotification && ( { )} tab.tab).join(",")} initialNumToRender={tabs.length} scrollEnabled={false} data={tabs} From 8abbdf2601485be6cb77362b6f50428cc19c3891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 26 Jan 2025 19:07:11 +0100 Subject: [PATCH 0390/1144] refractor: optimisation du code --- src/views/settings/SettingsTabs.tsx | 66 +++++++++++++++-------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/src/views/settings/SettingsTabs.tsx b/src/views/settings/SettingsTabs.tsx index 67ada766e..74955e65d 100644 --- a/src/views/settings/SettingsTabs.tsx +++ b/src/views/settings/SettingsTabs.tsx @@ -418,10 +418,12 @@ const SettingsTabs = () => { leading={ } @@ -435,40 +437,38 @@ const SettingsTabs = () => { width: 70, }} > - {!safeTabs.includes(item.tab) && ( + {!safeTabs.includes(item.tab) && !loading && ( - {!loading && ( - { - if (!item.enabled && tabs.filter(t => t.enabled).length === 5) { - if (Platform.OS === "ios") { - Alert.alert("Information", "Tu ne peux pas ajouter plus de 5 onglets sur la page d'accueil.", [ + { + if (!item.enabled && tabs.filter(t => t.enabled).length === 5) { + if (Platform.OS === "ios") { + Alert.alert("Information", "Tu ne peux pas ajouter plus de 5 onglets sur la page d'accueil.", [ + { + text: "OK", + }, + ]); + } else { + showAlert({ + title: "Information", + message: "Tu ne peux pas ajouter plus de 5 onglets sur la page d'accueil.", + actions: [ { - text: "OK", + title: "OK", + onPress: () => {}, + backgroundColor: theme.colors.card, }, - ]); - } else { - showAlert({ - title: "Information", - message: "Tu ne peux pas ajouter plus de 5 onglets sur la page d'accueil.", - actions: [ - { - title: "OK", - onPress: () => {}, - backgroundColor: theme.colors.card, - }, - ], - }); - } + ], + }); } - toggleTab(item.tab); - }} - /> - )} + } + toggleTab(item.tab); + }} + /> )} @@ -480,7 +480,9 @@ const SettingsTabs = () => { } > - {item.label} + + {item.label} + From cfdff1128e93065784a1564392ff8650d57eec95 Mon Sep 17 00:00:00 2001 From: godetremy Date: Sun, 26 Jan 2025 19:48:25 +0100 Subject: [PATCH 0391/1144] feat(Login): Update for support link --- .../login/pronote/PronoteInstanceSelector.tsx | 31 ++++++++++++++++++- src/views/welcome/FirstInstallation.tsx | 6 ++-- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/views/login/pronote/PronoteInstanceSelector.tsx b/src/views/login/pronote/PronoteInstanceSelector.tsx index 62158b4d4..263031aa8 100644 --- a/src/views/login/pronote/PronoteInstanceSelector.tsx +++ b/src/views/login/pronote/PronoteInstanceSelector.tsx @@ -28,10 +28,12 @@ import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import { LinearGradient } from "expo-linear-gradient"; import { useTheme } from "@react-navigation/native"; -import { Search, X, GraduationCap } from "lucide-react-native"; +import {Search, X, GraduationCap, SearchX} from "lucide-react-native"; import { useAlert } from "@/providers/AlertProvider"; import { Audio } from "expo-av"; import getInstancesFromDataset from "@/services/pronote/dataset_geolocation"; +import {openURL} from "expo-linking"; +import * as WebBrowser from "expo-web-browser"; const PronoteInstanceSelector: Screen<"PronoteInstanceSelector"> = ({ route: { params }, @@ -336,6 +338,33 @@ const PronoteInstanceSelector: Screen<"PronoteInstanceSelector"> = ({ /> ))} + + + } + onPress={async () => { + await WebBrowser.openBrowserAsync("https://support.papillon.bzh//articles/351104-frequency-asked-questions#etab-not-found", { + controlsColor: "#0E7CCB", + presentationStyle: WebBrowser.WebBrowserPresentationStyle.PAGE_SHEET, + }); + }} + text={"Je ne trouve pas mon établissement..."} + /> + diff --git a/src/views/welcome/FirstInstallation.tsx b/src/views/welcome/FirstInstallation.tsx index dcfa6d6f3..b3d85a259 100644 --- a/src/views/welcome/FirstInstallation.tsx +++ b/src/views/welcome/FirstInstallation.tsx @@ -14,8 +14,8 @@ import * as WebBrowser from "expo-web-browser"; import { Audio } from "expo-av"; import * as SplashScreen from "expo-splash-screen"; -const PRIVACY_POLICY_URL = "https://docs.papillon.bzh/legal/privacy"; -const TERMS_OF_SERVICE_URL = "https://docs.papillon.bzh/legal/terms"; +const PRIVACY_POLICY_URL = "https://support.papillon.bzh/articles/352402-privacy-policy"; +const TERMS_OF_SERVICE_URL = "https://support.papillon.bzh/articles/352401-terms-of-service"; const FirstInstallation: Screen<"FirstInstallation"> = ({ navigation }) => { const theme = useTheme(); @@ -91,7 +91,7 @@ const FirstInstallation: Screen<"FirstInstallation"> = ({ navigation }) => { openUrl("https://support.getpapillon.xyz/")} + onPress={() => openUrl("https://support.papillon.bzh/")} /> Date: Sun, 26 Jan 2025 19:51:59 +0100 Subject: [PATCH 0392/1144] feat(Login): Update error message for bad credential --- src/views/login/pronote/PronoteCredentials.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/views/login/pronote/PronoteCredentials.tsx b/src/views/login/pronote/PronoteCredentials.tsx index 420d7566e..6eea4d0bf 100644 --- a/src/views/login/pronote/PronoteCredentials.tsx +++ b/src/views/login/pronote/PronoteCredentials.tsx @@ -91,7 +91,10 @@ const PronoteCredentials: Screen<"PronoteCredentials"> = ({ route, navigation }) setLoading(false); if (error instanceof Error) { - setError(error.message); + if (error.name === "BadCredentialsError") + setError("Nom d'utilisateur ou mot de passe incorrect"); + else + setError(error.message); } else { setError("Erreur inconnue"); From 7071292370e359ebd96803c562c607ae7f55ca93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 26 Jan 2025 21:24:45 +0100 Subject: [PATCH 0393/1144] update `package-lock.json` --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index ef8f26b28..959245218 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.7.2", + "version": "7.8.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.7.2", + "version": "7.8.0", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.3", "@candlefinance/app-icon": "^0.4.5", From d32982bd415c89fa1d3924f5b512be6fb77796f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 26 Jan 2025 22:28:10 +0100 Subject: [PATCH 0394/1144] feat(Background): Replace log with info for background tasks and events --- src/background/BackgroundTasks.ts | 3 +-- src/background/data/Attendance.ts | 4 ++-- src/background/data/Evaluation.ts | 4 ++-- src/background/data/Grades.ts | 4 ++-- src/background/data/Homeworks.ts | 4 ++-- src/background/data/Lessons.ts | 4 ++-- src/background/data/News.ts | 4 ++-- 7 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 4ba82da5e..72b67bdbb 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -42,12 +42,11 @@ let isBackgroundFetchRunning = false; const backgroundFetch = async () => { if (isBackgroundFetchRunning) { - log("⚠️ Background fetch already running. Skipping...", "BackgroundEvent"); + warn("⚠️ Background fetch already running. Skipping...", "BackgroundEvent"); return BackgroundFetchResult.NoData; } isBackgroundFetchRunning = true; - log("Running background fetch", "BackgroundEvent"); try { const accounts = getAccounts(); diff --git a/src/background/data/Attendance.ts b/src/background/data/Attendance.ts index 14890d42b..e7240aeef 100644 --- a/src/background/data/Attendance.ts +++ b/src/background/data/Attendance.ts @@ -2,7 +2,7 @@ import { getCurrentAccount } from "../utils/accounts"; import { papillonNotify } from "../Notifications"; import { Attendance } from "@/services/shared/Attendance"; import { getAttendance, updateAttendanceState } from "../utils/attendance"; -import { log } from "@/utils/logger/logger"; +import { info } from "@/utils/logger/logger"; const getDifferences = ( currentAttendance: Attendance, @@ -53,7 +53,7 @@ const getDifferences = ( }; const fetchAttendance = async (): Promise => { - log("Running background Attendance", "BackgroundEvent"); + info("▶️ Running background Attendance", "BackgroundEvent"); const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; diff --git a/src/background/data/Evaluation.ts b/src/background/data/Evaluation.ts index a9c5c9207..2d4638df9 100644 --- a/src/background/data/Evaluation.ts +++ b/src/background/data/Evaluation.ts @@ -2,7 +2,7 @@ import { getCurrentAccount } from "../utils/accounts"; import { papillonNotify } from "../Notifications"; import { Evaluation } from "@/services/shared/Evaluation"; import { getEvaluation, updateEvaluationState } from "../utils/evaluation"; -import { log } from "@/utils/logger/logger"; +import { info } from "@/utils/logger/logger"; const getDifferences = ( currentEvaluation: Evaluation[], @@ -19,7 +19,7 @@ const getDifferences = ( }; const fetchEvaluation = async (): Promise => { - log("Running background Evaluation", "BackgroundEvent"); + info("▶️ Running background Evaluation", "BackgroundEvent"); const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; diff --git a/src/background/data/Grades.ts b/src/background/data/Grades.ts index c09bcf3f5..da85b1e39 100644 --- a/src/background/data/Grades.ts +++ b/src/background/data/Grades.ts @@ -2,7 +2,7 @@ import { getCurrentAccount } from "../utils/accounts"; import { papillonNotify } from "../Notifications"; import { Grade } from "@/services/shared/Grade"; import { getGrades, updateGradeState } from "../utils/grades"; -import { log } from "@/utils/logger/logger"; +import { info } from "@/utils/logger/logger"; const getDifferences = ( currentGrade: Grade[], @@ -19,7 +19,7 @@ const getDifferences = ( }; const fetchGrade = async (): Promise => { - log("Running background Grade", "BackgroundEvent"); + info("▶️ Running background Grades", "BackgroundEvent"); const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; diff --git a/src/background/data/Homeworks.ts b/src/background/data/Homeworks.ts index cb65a50a0..9392eda84 100644 --- a/src/background/data/Homeworks.ts +++ b/src/background/data/Homeworks.ts @@ -3,7 +3,7 @@ import { papillonNotify } from "../Notifications"; import { getHomeworks, updateHomeworksState } from "../utils/homeworks"; import { Homework } from "@/services/shared/Homework"; import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; -import { log } from "@/utils/logger/logger"; +import { info } from "@/utils/logger/logger"; const getDifferences = ( currentHomeworks: Homework[], @@ -19,7 +19,7 @@ const getDifferences = ( }; const fetchHomeworks = async (): Promise => { - log("Running background Homeworks", "BackgroundEvent"); + info("▶️ Running background Homeworks", "BackgroundEvent"); const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index da193d175..e41b07a40 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -3,7 +3,7 @@ import { papillonNotify } from "../Notifications"; import { getLessons, updateLessonsState } from "../utils/lessons"; import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; import { Timetable, TimetableClassStatus } from "@/services/shared/Timetable"; -import { log } from "@/utils/logger/logger"; +import { info } from "@/utils/logger/logger"; const getAllLessonsForDay = (lessons: Record) => { const date = new Date(); @@ -22,7 +22,7 @@ const getAllLessonsForDay = (lessons: Record) => { }; const fetchLessons = async (): Promise => { - log("Running background Lessons", "BackgroundEvent"); + info("▶️ Running background Lessons", "BackgroundEvent"); const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; diff --git a/src/background/data/News.ts b/src/background/data/News.ts index 4fffc900a..db8fb9f6f 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -3,7 +3,7 @@ import { getCurrentAccount } from "../utils/accounts"; import { papillonNotify } from "../Notifications"; import parse_news_resume from "@/utils/format/format_pronote_news"; import { Information } from "@/services/shared/Information"; -import { log } from "@/utils/logger/logger"; +import { info } from "@/utils/logger/logger"; const getDifferences = ( currentNews: Information[], @@ -20,7 +20,7 @@ const getDifferences = ( }; const fetchNews = async (): Promise => { - log("Running background News", "BackgroundEvent"); + info("▶️ Running background News", "BackgroundEvent"); const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; From b08e1b70b5fab98954167f134a755d09f2ee1652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 26 Jan 2025 22:40:17 +0100 Subject: [PATCH 0395/1144] fix: remplacer console.log par console.error, console.warn et console.info dans le logger --- src/utils/logger/logger.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/logger/logger.ts b/src/utils/logger/logger.ts index 90c74bc8d..54b20a430 100644 --- a/src/utils/logger/logger.ts +++ b/src/utils/logger/logger.ts @@ -74,19 +74,19 @@ function log (message: string, from: string): void { function error (message: string, from: string): void { let log = get_message(1, get_iso_date(), get_file_from_stacktrace(from), message); save_logs_to_memory(log); - console.log(log); + console.error(log); } function warn (message: string, from: string): void { let log = get_message(2, get_iso_date(), get_file_from_stacktrace(from), message); save_logs_to_memory(log); - console.log(log); + console.warn(log); } function info (message: string, from: string): void { let log = get_message(3, get_iso_date(), get_file_from_stacktrace(from), message); save_logs_to_memory(log); - console.log(log); + console.info(log); } function navigate (to: string): void { From 77cb58bd4d1db3751284308aad41d769b1209976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 26 Jan 2025 23:16:41 +0100 Subject: [PATCH 0396/1144] =?UTF-8?q?feat:=20affichage=20d'une=20notificat?= =?UTF-8?q?ion=20uniquement=20pendant=20l'ex=C3=A9cution=20du=20background?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/BackgroundTasks.ts | 9 +++++++++ src/background/Notifications.ts | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 72b67bdbb..89fe4a4cf 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -11,6 +11,7 @@ import { fetchGrade } from "./data/Grades"; // import { fetchLessons } from "./data/Lessons"; import { fetchAttendance } from "./data/Attendance"; import { fetchEvaluation } from "./data/Evaluation"; +import { papillonNotify } from "./Notifications"; // Gestion des notifs quand app en arrière-plan notifee.onBackgroundEvent(async ({ type, detail }) => { @@ -47,6 +48,13 @@ const backgroundFetch = async () => { } isBackgroundFetchRunning = true; + papillonNotify( + { + id: "statusBackground", + body: "Récupération des données des comptes les plus récentes en arrière-plan...", + }, + "Status" + ); try { const accounts = getAccounts(); @@ -73,6 +81,7 @@ const backgroundFetch = async () => { return BackgroundFetchResult.Failed; } finally { isBackgroundFetchRunning = false; + notifee.cancelNotification("statusBackground"); } }; diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index 854ebbef7..022fd7fc2 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -27,6 +27,15 @@ const createChannelNotification = async () => { sound: "default", }); + await notifee.createChannel({ + id: "Status", + name: "Statut du background", + description: "Affiche quand le background est actuellement actif", + vibration: false, + importance: AndroidImportance.MIN, + badge: false, + }); + await notifee.createChannelGroup({ id: "Papillon", name: "Notifications Scolaires", @@ -87,6 +96,7 @@ const papillonNotify = async ( props: Notification, channelId: | "Test" + | "Status" | "News" | "Homeworks" | "Grades" From 718f7ca196682f92ff6adefaae596571d7aea575 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 26 Jan 2025 23:19:41 +0100 Subject: [PATCH 0397/1144] =?UTF-8?q?feat:=20nouveaut=C3=A9s=20pour=20iOS?= =?UTF-8?q?=20+=20adaptation=20du=20comportement=20des=20notifs=20en=20fon?= =?UTF-8?q?ction=20de=20leur=20id?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/Notifications.ts | 21 ++++++++++++++++----- src/background/data/Attendance.ts | 6 ------ src/background/data/Evaluation.ts | 6 ------ src/background/data/Grades.ts | 6 ------ src/background/data/Homeworks.ts | 9 --------- src/background/data/Lessons.ts | 6 ------ src/background/data/News.ts | 6 ------ 7 files changed, 16 insertions(+), 44 deletions(-) diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index 022fd7fc2..487af2f9d 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -1,4 +1,5 @@ import notifee, { + AndroidImportance, AuthorizationStatus, Notification, } from "@notifee/react-native"; @@ -113,15 +114,25 @@ const papillonNotify = async ( android: { channelId, timestamp, - showTimestamp: true, + showTimestamp: channelId !== "Status" ? true : false, + showChronometer: channelId === "Status" ? true : false, smallIcon: "@mipmap/ic_launcher_foreground", color: "#32AB8E", - pressAction: { - id: "default", - launchActivity: "xyz.getpapillon.app.MainActivity", - } + pressAction: + channelId !== "Test" && channelId !== "Status" + ? { + id: "default", + launchActivity: "xyz.getpapillon.app.MainActivity", + } + : undefined, // à intégrer => `actions` }, + ios: { + threadId: channelId, + badgeCount: channelId !== "Status" ? 1 : 0, + sound: channelId !== "Status" ? "default" : undefined, + // à intégrer => `categoryId` + } }); }; diff --git a/src/background/data/Attendance.ts b/src/background/data/Attendance.ts index e7240aeef..48b27e314 100644 --- a/src/background/data/Attendance.ts +++ b/src/background/data/Attendance.ts @@ -142,9 +142,6 @@ const fetchAttendance = async (): Promise => { title: `[${account.name}] ${thenewevent}`, subtitle: defaultPeriod, body: explication, - ios: { - categoryId: account.name, - }, }, "Attendance" ); @@ -198,9 +195,6 @@ const fetchAttendance = async (): Promise => { title: `[${account.name}] Vie Scolaire`, subtitle: defaultPeriod, body: `De nouveaux événements ont été publiés, consulte la vie scolaire pour plus de détails : ${LesExplication.join(", ")}.`, - ios: { - categoryId: account.name, - }, }, "Attendance" ); diff --git a/src/background/data/Evaluation.ts b/src/background/data/Evaluation.ts index 2d4638df9..b8c747c5b 100644 --- a/src/background/data/Evaluation.ts +++ b/src/background/data/Evaluation.ts @@ -43,9 +43,6 @@ const fetchEvaluation = async (): Promise => { title: `[${account.name}] Nouvelle compétence`, subtitle: defaultPeriod, body: `Une nouvelle compétence en ${differences[0].subjectName} a été publiée`, - ios: { - categoryId: account.name, - }, }, "Evaluation" ); @@ -64,9 +61,6 @@ const fetchEvaluation = async (): Promise => { }) .join("
")} `, - ios: { - categoryId: account.name, - }, }, "Evaluation" ); diff --git a/src/background/data/Grades.ts b/src/background/data/Grades.ts index da85b1e39..c58b72f57 100644 --- a/src/background/data/Grades.ts +++ b/src/background/data/Grades.ts @@ -43,9 +43,6 @@ const fetchGrade = async (): Promise => { title: `[${account.name}] Nouvelle note`, subtitle: defaultPeriod, body: `Une nouvelle note en ${differences[0].subjectName} a été publiée`, - ios: { - categoryId: account.name, - }, }, "Grades" ); @@ -64,9 +61,6 @@ const fetchGrade = async (): Promise => { }) .join("
")} `, - ios: { - categoryId: account.name, - }, }, "Grades" ); diff --git a/src/background/data/Homeworks.ts b/src/background/data/Homeworks.ts index 9392eda84..8a77e997b 100644 --- a/src/background/data/Homeworks.ts +++ b/src/background/data/Homeworks.ts @@ -67,9 +67,6 @@ const fetchHomeworks = async (): Promise => { 1 ).toString()}`, body: `Un nouveau devoir en ${differencesHwSemaineActuelle[0].subject} a été publié`, - ios: { - categoryId: account.name, - }, }, "Homeworks" ); @@ -83,9 +80,6 @@ const fetchHomeworks = async (): Promise => { 2 ).toString()}`, body: `Un nouveau devoir en ${differencesHwSemaineProchaine[0].subject} a été publié`, - ios: { - categoryId: account.name, - }, }, "Homeworks" ); @@ -113,9 +107,6 @@ const fetchHomeworks = async (): Promise => { }) .join("
")} `, - ios: { - categoryId: account.name, - }, }, "Homeworks" ); diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index e41b07a40..18b86b667 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -105,9 +105,6 @@ const fetchLessons = async (): Promise => { month: "long", }), body: `Le cours de ${lessonsEvent[0].title} (${dateLessonsDebut}-${dateLessonsFin}) ${statut}`, - ios: { - categoryId: account.name, - }, }, "Lessons" ); @@ -130,9 +127,6 @@ const fetchLessons = async (): Promise => { return `- ${element.title}`; }) .join("
")}`, - ios: { - categoryId: account.name, - }, }, "Lessons" ); diff --git a/src/background/data/News.ts b/src/background/data/News.ts index db8fb9f6f..fe9732b3e 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -47,9 +47,6 @@ const fetchNews = async (): Promise => { 100 )}...` : "Aucun résumé disponible.", - ios: { - categoryId: account.name, - }, }, "News" ); @@ -67,9 +64,6 @@ const fetchNews = async (): Promise => { }) .join("
")} `, - ios: { - categoryId: account.name, - }, }, "News" ); From fcdda1c5adf07b9a03247acaff1ec14daafa56c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 26 Jan 2025 23:22:17 +0100 Subject: [PATCH 0398/1144] feat: ajout d'un `await` sur `papillonNotify` pour garantir l'affichage des notifications et un code synchro --- src/background/BackgroundTasks.ts | 5 +++-- src/background/data/Attendance.ts | 8 +++++--- src/background/data/Evaluation.ts | 4 ++-- src/background/data/Grades.ts | 4 ++-- src/background/data/Homeworks.ts | 6 +++--- src/background/data/Lessons.ts | 4 ++-- src/background/data/News.ts | 8 +++++--- 7 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 89fe4a4cf..23cb80b4a 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -48,7 +48,7 @@ const backgroundFetch = async () => { } isBackgroundFetchRunning = true; - papillonNotify( + await papillonNotify( { id: "statusBackground", body: "Récupération des données des comptes les plus récentes en arrière-plan...", @@ -62,7 +62,8 @@ const backgroundFetch = async () => { for (const account of accounts) { await switchTo(account); - const notificationsTypesPermissions = account.personalization.notifications; + const notificationsTypesPermissions = + account.personalization.notifications; if (notificationsTypesPermissions?.enabled) { await fetchNews(); diff --git a/src/background/data/Attendance.ts b/src/background/data/Attendance.ts index 48b27e314..70469d8fb 100644 --- a/src/background/data/Attendance.ts +++ b/src/background/data/Attendance.ts @@ -136,7 +136,7 @@ const fetchAttendance = async (): Promise => { `; } - papillonNotify( + await papillonNotify( { id: `${account.name}-attendance`, title: `[${account.name}] ${thenewevent}`, @@ -189,12 +189,14 @@ const fetchAttendance = async (): Promise => { } } - papillonNotify( + await papillonNotify( { id: `${account.name}-attendance`, title: `[${account.name}] Vie Scolaire`, subtitle: defaultPeriod, - body: `De nouveaux événements ont été publiés, consulte la vie scolaire pour plus de détails : ${LesExplication.join(", ")}.`, + body: `De nouveaux événements ont été publiés, consulte la vie scolaire pour plus de détails : ${LesExplication.join( + ", " + )}.`, }, "Attendance" ); diff --git a/src/background/data/Evaluation.ts b/src/background/data/Evaluation.ts index b8c747c5b..62cc4f715 100644 --- a/src/background/data/Evaluation.ts +++ b/src/background/data/Evaluation.ts @@ -37,7 +37,7 @@ const fetchEvaluation = async (): Promise => { case 0: break; case 1: - papillonNotify( + await papillonNotify( { id: `${account.name}-evaluation`, title: `[${account.name}] Nouvelle compétence`, @@ -48,7 +48,7 @@ const fetchEvaluation = async (): Promise => { ); break; default: - papillonNotify( + await papillonNotify( { id: `${account.name}-evaluation`, subtitle: defaultPeriod, diff --git a/src/background/data/Grades.ts b/src/background/data/Grades.ts index c58b72f57..c40dc25eb 100644 --- a/src/background/data/Grades.ts +++ b/src/background/data/Grades.ts @@ -37,7 +37,7 @@ const fetchGrade = async (): Promise => { case 0: break; case 1: - papillonNotify( + await papillonNotify( { id: `${account.name}-grades`, title: `[${account.name}] Nouvelle note`, @@ -48,7 +48,7 @@ const fetchGrade = async (): Promise => { ); break; default: - papillonNotify( + await papillonNotify( { id: `${account.name}-grades`, subtitle: defaultPeriod, diff --git a/src/background/data/Homeworks.ts b/src/background/data/Homeworks.ts index 8a77e997b..d174705a0 100644 --- a/src/background/data/Homeworks.ts +++ b/src/background/data/Homeworks.ts @@ -58,7 +58,7 @@ const fetchHomeworks = async (): Promise => { break; case 1: if (differencesHwSemaineActuelle.length === 1) { - papillonNotify( + await papillonNotify( { id: `${account.name}-homeworks`, title: `[${account.name}] Nouveau devoir`, @@ -71,7 +71,7 @@ const fetchHomeworks = async (): Promise => { "Homeworks" ); } else { - papillonNotify( + await papillonNotify( { id: `${account.name}-homeworks`, title: `[${account.name}] Nouveau devoir`, @@ -86,7 +86,7 @@ const fetchHomeworks = async (): Promise => { } break; default: - papillonNotify( + await papillonNotify( { id: `${account.name}-homeworks`, title: `[${account.name}] Nouveaux devoirs`, diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index 18b86b667..60958168c 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -93,7 +93,7 @@ const fetchLessons = async (): Promise => { break; } - papillonNotify( + await papillonNotify( { id: `${account.name}-lessons`, title: `[${account.name}] Emploi du temps du jour modifié`, @@ -110,7 +110,7 @@ const fetchLessons = async (): Promise => { ); break; default: - papillonNotify( + await papillonNotify( { id: `${account.name}-lessons`, title: `[${account.name}] Emploi du temps du jour`, diff --git a/src/background/data/News.ts b/src/background/data/News.ts index fe9732b3e..49e4fe3d8 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -35,7 +35,7 @@ const fetchNews = async (): Promise => { case 0: break; case 1: - papillonNotify( + await papillonNotify( { id: `${account.name}-news`, title: `[${account.name}] Nouvelle actualité`, @@ -52,12 +52,14 @@ const fetchNews = async (): Promise => { ); break; default: - papillonNotify( + await papillonNotify( { id: `${account.name}-news`, title: `[${account.name}] Nouvelles actualités`, body: ` - ${differences.length} nouvelles actualités ont été publiées par :
+ ${ + differences.length + } nouvelles actualités ont été publiées par :
${differences .flatMap((element) => { return `- ${element.author}`; From cd751a495f003ab993e5c6cf284de378904512f4 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 27 Jan 2025 12:12:58 +0100 Subject: [PATCH 0399/1144] fix: Update presentation style for various screens to formSheet with improved gesture and animation settings --- src/router/screens/views/index.ts | 32 +++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/router/screens/views/index.ts b/src/router/screens/views/index.ts index d2fe1374e..ffb39c703 100644 --- a/src/router/screens/views/index.ts +++ b/src/router/screens/views/index.ts @@ -60,21 +60,41 @@ export default [ }), createScreen("LessonDocument", LessonDocument, { headerTitle: "Cours", - presentation: "modal", + presentation: "formSheet", + gestureDirection: "vertical", + animation: "slide_from_bottom", + sheetGrabberVisible: false, + sheetInitialDetentIndex: 0, + sheetAllowedDetents: [0.5, 1.0], headerShown: false, }), createScreen("HomeworksDocument", HomeworksDocument, { headerTitle: "Devoir", - presentation: "modal", + presentation: "formSheet", + gestureDirection: "vertical", + animation: "slide_from_bottom", + sheetGrabberVisible: false, + sheetInitialDetentIndex: 0, + sheetAllowedDetents: [0.5, 1.0], headerShown: false, }), createScreen("GradeSubject", GradeSubjectScreen, { headerTitle: "Détail de la matière", - presentation: "modal", + presentation: "formSheet", + gestureDirection: "vertical", + animation: "slide_from_bottom", + sheetGrabberVisible: true, + sheetInitialDetentIndex: 0, + sheetAllowedDetents: [0.5, 1.0], }), createScreen("GradeDocument", GradeDocument, { headerTitle: "Détail de la note", - presentation: "modal", + presentation: "formSheet", + gestureDirection: "vertical", + animation: "slide_from_bottom", + sheetGrabberVisible: false, + sheetInitialDetentIndex: 0, + sheetAllowedDetents: [0.5, 1.0], headerShown: Platform.OS !== "ios", }), createScreen("ChatCreate", ChatCreate, { @@ -90,12 +110,12 @@ export default [ createScreen("ChatDetails", ChatDetails, { headerTitle: "Discussions", headerShown: false, - presentation: "modal" + presentation: "modal", }), createScreen("ChatThemes", ChatThemes, { headerTitle: "Thèmes", headerShown: true, - presentation: "modal" + presentation: "modal", }), createScreen("BackgroundIUTLannion", BackgroundIUTLannion, { headerTitle: "IUT de Lannion", From bafb2a8fe50739df7d7dc5bd3db9f9579bcd1eb7 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 27 Jan 2025 12:13:05 +0100 Subject: [PATCH 0400/1144] feat: Update AndroidManifest and Info.plist for social media integration and version bump to 7.8.0 --- android/app/src/main/AndroidManifest.xml | 4 ++ .../AppIcon.appiconset/Contents.json | 42 ++++--------------- ios/Papillon/Info.plist | 11 ++++- 3 files changed, 22 insertions(+), 35 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 4f859e36e..e8f9fa9d2 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -14,6 +14,10 @@ + + + + diff --git a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json index e91cbd33d..90d8d4c2a 100644 --- a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,38 +1,14 @@ { - "images" : [ + "images": [ { - "filename" : "Icon-Light-1024x1024.png", - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "Icon-Dark-1024x1024.png", - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "tinted" - } - ], - "filename" : "Icon-Tinted-1024x1024.png", - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" + "filename": "App-Icon-1024x1024@1x.png", + "idiom": "universal", + "platform": "ios", + "size": "1024x1024" } ], - "info" : { - "author" : "xcode", - "version" : 1 + "info": { + "version": 1, + "author": "expo" } -} +} \ No newline at end of file diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index 2b3629ee4..bc9188054 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 7.7.2 + 7.8.0 CFBundleSignature ???? CFBundleURLTypes @@ -36,6 +36,13 @@ 1 ITSAppUsesNonExemptEncryption + LSApplicationQueriesSchemes + + fb + instagram + twitter + tiktoksharesdk + LSRequiresIPhoneOS NSAppTransportSecurity @@ -46,7 +53,7 @@ NSCameraUsageDescription - Papillon utilise ta caméra pour scanner des QR-codes pour te connecter, pour capturer des documents, ou pour des fonctionnalités amusantes telles que les réaction. + Papillon utilise ta caméra pour scanner des QR-codes pour te connecter, pour capturer des documents, ou pour des fonctionnalités amusantes telles que les réactions. NSLocationAlwaysAndWhenInUseUsageDescription Autoriser $(PRODUCT_NAME) à utiliser ta localisation. NSLocationAlwaysUsageDescription From c60267d2edbe9d3300c0df4cb993d9be700eb30f Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 27 Jan 2025 12:13:16 +0100 Subject: [PATCH 0401/1144] chore: Bump version to 7.9.0 in package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 262993ed3..aed7885e3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "papillonvex", - "version": "7.8.0", + "version": "7.9.0", "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", From c9ba8be27ae0bdab5cb09843cfdb668aa1d7914b Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 27 Jan 2025 12:14:04 +0100 Subject: [PATCH 0402/1144] =?UTF-8?q?feat:=20Ajoute=20des=20animations=20?= =?UTF-8?q?=C3=A0=20l'en-t=C3=AAte=20de=20la=20liste=20et=20=C3=A0=20la=20?= =?UTF-8?q?liste=20des=20unit=C3=A9s=20d'enseignement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Grades/Atoms/GradesScodocUE.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/account/Grades/Atoms/GradesScodocUE.tsx b/src/views/account/Grades/Atoms/GradesScodocUE.tsx index 41de3e697..14b5bb852 100644 --- a/src/views/account/Grades/Atoms/GradesScodocUE.tsx +++ b/src/views/account/Grades/Atoms/GradesScodocUE.tsx @@ -40,6 +40,7 @@ const GradesScodocUE = ({ account, navigation }: { account: PrimaryAccount, navi return ( <> - + {finalUes.map((ue) => { interface ueGrade { key: string, From d28705283e5fabc98e4c7371f0132ec26bea9768 Mon Sep 17 00:00:00 2001 From: Bulgus <88266443+Bulgus@users.noreply.github.com> Date: Mon, 27 Jan 2025 12:14:20 +0100 Subject: [PATCH 0403/1144] feat(account): reset profile picture button --- ios/Papillon/Info.plist | 202 +++++++++--------- .../pronote/default-personalization.ts | 6 +- src/stores/account/types.ts | 1 + src/views/settings/SettingsProfile.tsx | 24 +++ 4 files changed, 130 insertions(+), 103 deletions(-) diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index 2b3629ee4..91fe3e911 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -1,104 +1,104 @@ - - CADisableMinimumFrameDurationOnPhone - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - Papillon - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 7.7.2 - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleURLSchemes - - papillon - izly - - - - CFBundleVersion - 1 - ITSAppUsesNonExemptEncryption - - LSRequiresIPhoneOS - - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - NSAllowsLocalNetworking - - - NSCameraUsageDescription - Papillon utilise ta caméra pour scanner des QR-codes pour te connecter, pour capturer des documents, ou pour des fonctionnalités amusantes telles que les réaction. - NSLocationAlwaysAndWhenInUseUsageDescription - Autoriser $(PRODUCT_NAME) à utiliser ta localisation. - NSLocationAlwaysUsageDescription - Autoriser $(PRODUCT_NAME) à utiliser ta localisation. - NSLocationWhenInUseUsageDescription - Papillon utilise ton emplacement pour trouver les établissements autour de toi. - NSMicrophoneUsageDescription - Papillon utilise ton micro pour enregistrer des travaux audio ou des cours. - NSMotionUsageDescription - Papillon utilise les capteurs de mouvement de ton appareil pour de nombreuses fonctionnalités interactives. - NSPhotoLibraryAddUsageDescription - Allow $(PRODUCT_NAME) to save photos - NSPhotoLibraryUsageDescription - Papillon utilise tes photos et vidéos pour personnaliser ton profil, ta gestion des cours et bien plus. - UIAppFonts - - FixelText-Bold.ttf - FixelText-Light.ttf - FixelText-Medium.ttf - FixelText-Regular.ttf - FixelText-SemiBold.ttf - - UIBackgroundModes - - fetch - remote-notification - - UILaunchStoryboardName - SplashScreen - UIRequiredDeviceCapabilities - - arm64 - - UIRequiresFullScreen - - UIStatusBarStyle - UIStatusBarStyleDefault - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIUserInterfaceStyle - Automatic - UIViewControllerBasedStatusBarAppearance - - - \ No newline at end of file + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Papillon + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 7.7.2 + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleURLSchemes + + papillon + izly + + + + CFBundleVersion + 1 + ITSAppUsesNonExemptEncryption + + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSAllowsLocalNetworking + + + NSCameraUsageDescription + Papillon utilise ta caméra pour scanner des QR-codes pour te connecter, pour capturer des documents, ou pour des fonctionnalités amusantes telles que les réaction. + NSLocationAlwaysAndWhenInUseUsageDescription + Autoriser $(PRODUCT_NAME) à utiliser ta localisation. + NSLocationAlwaysUsageDescription + Autoriser $(PRODUCT_NAME) à utiliser ta localisation. + NSLocationWhenInUseUsageDescription + Papillon utilise ton emplacement pour trouver les établissements autour de toi. + NSMicrophoneUsageDescription + Papillon utilise ton micro pour enregistrer des travaux audio ou des cours. + NSMotionUsageDescription + Papillon utilise les capteurs de mouvement de ton appareil pour de nombreuses fonctionnalités interactives. + NSPhotoLibraryAddUsageDescription + Allow $(PRODUCT_NAME) to save photos + NSPhotoLibraryUsageDescription + Papillon utilise tes photos et vidéos pour personnaliser ton profil, ta gestion des cours et bien plus. + UIAppFonts + + FixelText-Bold.ttf + FixelText-Light.ttf + FixelText-Medium.ttf + FixelText-Regular.ttf + FixelText-SemiBold.ttf + + UIBackgroundModes + + fetch + remote-notification + + UILaunchStoryboardName + SplashScreen + UIRequiredDeviceCapabilities + + arm64 + + UIRequiresFullScreen + + UIStatusBarStyle + UIStatusBarStyleDefault + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIUserInterfaceStyle + Automatic + UIViewControllerBasedStatusBarAppearance + + + diff --git a/src/services/pronote/default-personalization.ts b/src/services/pronote/default-personalization.ts index 70ce3c1e3..f86ab2eee 100644 --- a/src/services/pronote/default-personalization.ts +++ b/src/services/pronote/default-personalization.ts @@ -25,12 +25,14 @@ const defaultPersonalization = async (instance: pronote.SessionHandle): Promise< profilePictureB64: user.profilePicture ? await downloadAsBase64(user.profilePicture.url) : void 0, - + initialProfilePictureB64: user.profilePicture + ? await downloadAsBase64(user.profilePicture.url) + : void 0, tabs: defaultTabs.filter(current => defaultPronoteTabs.includes(current.tab)).map((tab, index) => ({ name: tab.tab, enabled: index <= 4 })) - }; + }; }; export default defaultPersonalization; diff --git a/src/stores/account/types.ts b/src/stores/account/types.ts index dde651879..52db58be5 100644 --- a/src/stores/account/types.ts +++ b/src/stores/account/types.ts @@ -36,6 +36,7 @@ export interface PapillonIcalURL { export interface Personalization { color: PersonalizationColor profilePictureB64?: string, + initialProfilePictureB64?: string, hideNameOnHomeScreen: boolean, hideProfilePicOnHomeScreen: boolean, hideTabTitles: boolean, diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index c7630b773..321b4f963 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -26,6 +26,15 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { const [firstName, setFirstName] = useState(account.studentName?.first ?? ""); const [lastName, setLastName] = useState(account.studentName?.last ?? ""); + const resetProfilePic = () => { + const initialPic = account.personalization.initialProfilePictureB64; + setProfilePic(initialPic); + mutateProperty("personalization", { + ...account.personalization, + profilePictureB64: initialPic, + }); + }; + // on name change, update the account name useEffect(() => { let newLastName = lastName; @@ -176,6 +185,21 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { )} + + {profilePic && ( + } + > + + Réinitialiser la photo de profil + + + Supprime la photo actuelle et rétablit l'image par défaut. + + + )} Date: Mon, 27 Jan 2025 13:48:55 +0100 Subject: [PATCH 0404/1144] add other services --- src/services/ecoledirecte/default-personalization.ts | 6 ++++++ src/services/local/default-personalization.ts | 1 + src/services/pronote/default-personalization.ts | 2 +- src/services/skolengo/default-personalization.ts | 1 + 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/services/ecoledirecte/default-personalization.ts b/src/services/ecoledirecte/default-personalization.ts index d2878085f..ccf11aef6 100644 --- a/src/services/ecoledirecte/default-personalization.ts +++ b/src/services/ecoledirecte/default-personalization.ts @@ -26,6 +26,12 @@ export default async function defaultPersonalization (account: Account): Promise Pragma: "no-cache" }) : void 0, + initialProfilePictureB64: account.profilePictureURL + ? await downloadAsBase64(account.profilePictureURL, { + Referer: ".ecoledirecte.com/", + Pragma: "no-cache" + }) + : void 0, tabs: defaultTabs.filter(current => defaultEDTabs.includes(current.tab)).map((tab, index) => ({ name: tab.tab, enabled: index <= 4 diff --git a/src/services/local/default-personalization.ts b/src/services/local/default-personalization.ts index 16beab36a..44e79658d 100644 --- a/src/services/local/default-personalization.ts +++ b/src/services/local/default-personalization.ts @@ -20,6 +20,7 @@ export default async function defaultPersonalization (customDefaults?: Partial

defaultLocalTabs.includes(current.tab)).map((tab, index) => ({ name: tab.tab, enabled: index <= 4 diff --git a/src/services/pronote/default-personalization.ts b/src/services/pronote/default-personalization.ts index f86ab2eee..06ba09f87 100644 --- a/src/services/pronote/default-personalization.ts +++ b/src/services/pronote/default-personalization.ts @@ -32,7 +32,7 @@ const defaultPersonalization = async (instance: pronote.SessionHandle): Promise< name: tab.tab, enabled: index <= 4 })) - }; + }; }; export default defaultPersonalization; diff --git a/src/services/skolengo/default-personalization.ts b/src/services/skolengo/default-personalization.ts index 9f35930ab..91b93d63f 100644 --- a/src/services/skolengo/default-personalization.ts +++ b/src/services/skolengo/default-personalization.ts @@ -13,6 +13,7 @@ const defaultSkolengoPersonalization = async (instance: SkolengoAccount["instanc MagicHomeworks: true, MagicNews: true, profilePictureB64: void 0, + initialProfilePictureB64: void 0, tabs: defaultTabs.filter(current => skoTabs.includes(current.tab)).map((tab, index) => ({ name: tab.tab, From 7c6313d56c161e9f1d2fa575fb475fa4a2090ceb Mon Sep 17 00:00:00 2001 From: Bulgus <88266443+Bulgus@users.noreply.github.com> Date: Mon, 27 Jan 2025 15:07:05 +0100 Subject: [PATCH 0405/1144] remove Info.plist --- ios/Papillon/Info.plist | 202 ++++++++++++++++++++-------------------- 1 file changed, 101 insertions(+), 101 deletions(-) diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index 91fe3e911..2b3629ee4 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -1,104 +1,104 @@ - - CADisableMinimumFrameDurationOnPhone - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - Papillon - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 7.7.2 - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleURLSchemes - - papillon - izly - - - - CFBundleVersion - 1 - ITSAppUsesNonExemptEncryption - - LSRequiresIPhoneOS - - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - NSAllowsLocalNetworking - - - NSCameraUsageDescription - Papillon utilise ta caméra pour scanner des QR-codes pour te connecter, pour capturer des documents, ou pour des fonctionnalités amusantes telles que les réaction. - NSLocationAlwaysAndWhenInUseUsageDescription - Autoriser $(PRODUCT_NAME) à utiliser ta localisation. - NSLocationAlwaysUsageDescription - Autoriser $(PRODUCT_NAME) à utiliser ta localisation. - NSLocationWhenInUseUsageDescription - Papillon utilise ton emplacement pour trouver les établissements autour de toi. - NSMicrophoneUsageDescription - Papillon utilise ton micro pour enregistrer des travaux audio ou des cours. - NSMotionUsageDescription - Papillon utilise les capteurs de mouvement de ton appareil pour de nombreuses fonctionnalités interactives. - NSPhotoLibraryAddUsageDescription - Allow $(PRODUCT_NAME) to save photos - NSPhotoLibraryUsageDescription - Papillon utilise tes photos et vidéos pour personnaliser ton profil, ta gestion des cours et bien plus. - UIAppFonts - - FixelText-Bold.ttf - FixelText-Light.ttf - FixelText-Medium.ttf - FixelText-Regular.ttf - FixelText-SemiBold.ttf - - UIBackgroundModes - - fetch - remote-notification - - UILaunchStoryboardName - SplashScreen - UIRequiredDeviceCapabilities - - arm64 - - UIRequiresFullScreen - - UIStatusBarStyle - UIStatusBarStyleDefault - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIUserInterfaceStyle - Automatic - UIViewControllerBasedStatusBarAppearance - - - + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Papillon + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 7.7.2 + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleURLSchemes + + papillon + izly + + + + CFBundleVersion + 1 + ITSAppUsesNonExemptEncryption + + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSAllowsLocalNetworking + + + NSCameraUsageDescription + Papillon utilise ta caméra pour scanner des QR-codes pour te connecter, pour capturer des documents, ou pour des fonctionnalités amusantes telles que les réaction. + NSLocationAlwaysAndWhenInUseUsageDescription + Autoriser $(PRODUCT_NAME) à utiliser ta localisation. + NSLocationAlwaysUsageDescription + Autoriser $(PRODUCT_NAME) à utiliser ta localisation. + NSLocationWhenInUseUsageDescription + Papillon utilise ton emplacement pour trouver les établissements autour de toi. + NSMicrophoneUsageDescription + Papillon utilise ton micro pour enregistrer des travaux audio ou des cours. + NSMotionUsageDescription + Papillon utilise les capteurs de mouvement de ton appareil pour de nombreuses fonctionnalités interactives. + NSPhotoLibraryAddUsageDescription + Allow $(PRODUCT_NAME) to save photos + NSPhotoLibraryUsageDescription + Papillon utilise tes photos et vidéos pour personnaliser ton profil, ta gestion des cours et bien plus. + UIAppFonts + + FixelText-Bold.ttf + FixelText-Light.ttf + FixelText-Medium.ttf + FixelText-Regular.ttf + FixelText-SemiBold.ttf + + UIBackgroundModes + + fetch + remote-notification + + UILaunchStoryboardName + SplashScreen + UIRequiredDeviceCapabilities + + arm64 + + UIRequiresFullScreen + + UIStatusBarStyle + UIStatusBarStyleDefault + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIUserInterfaceStyle + Automatic + UIViewControllerBasedStatusBarAppearance + + + \ No newline at end of file From 926ab804c7c6ed1e41e2b2e09f65f6cfb13a504f Mon Sep 17 00:00:00 2001 From: godetremy Date: Mon, 27 Jan 2025 19:40:37 +0100 Subject: [PATCH 0406/1144] feat(Login): Update error message for all error from Pawnote --- .../login/pronote/PronoteCredentials.tsx | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/views/login/pronote/PronoteCredentials.tsx b/src/views/login/pronote/PronoteCredentials.tsx index 6eea4d0bf..399072a84 100644 --- a/src/views/login/pronote/PronoteCredentials.tsx +++ b/src/views/login/pronote/PronoteCredentials.tsx @@ -91,10 +91,23 @@ const PronoteCredentials: Screen<"PronoteCredentials"> = ({ route, navigation }) setLoading(false); if (error instanceof Error) { - if (error.name === "BadCredentialsError") - setError("Nom d'utilisateur ou mot de passe incorrect"); - else - setError(error.message); + switch (error.name) { + case "BadCredentialsError": + setError("Nom d'utilisateur ou mot de passe incorrect"); + break; + case "AuthenticateError": + setError("Impossible de s'authentifier : " + error.message); + break; + case "AccessDeniedError": + setError("Vous n'êtes pas autorisé à vous connecter à cet établissement"); + break; + case "AccountDisabledError": + setError("Votre compte a été désactivé. Contactez votre établissement."); + break; + default: + setError(error.message); + break; + } } else { setError("Erreur inconnue"); From f4374b90e3afb155aacb89695d5fbfce6347f1ef Mon Sep 17 00:00:00 2001 From: godetremy Date: Mon, 27 Jan 2025 19:41:09 +0100 Subject: [PATCH 0407/1144] feat(Login): Update error message for all error from Pawnote --- .../login/pronote/PronoteCredentials.tsx | 53 ------------------- 1 file changed, 53 deletions(-) diff --git a/src/views/login/pronote/PronoteCredentials.tsx b/src/views/login/pronote/PronoteCredentials.tsx index 399072a84..6c0e55c37 100644 --- a/src/views/login/pronote/PronoteCredentials.tsx +++ b/src/views/login/pronote/PronoteCredentials.tsx @@ -126,57 +126,4 @@ const PronoteCredentials: Screen<"PronoteCredentials"> = ({ route, navigation }) ); }; -const styles = StyleSheet.create({ - container: { - flex: 1, - padding: 16, - alignItems: "center", - }, - - serviceContainer: { - alignItems: "center", - marginBottom: 20, - gap: 4, - }, - - serviceLogo: { - width: 48, - height: 48, - borderRadius: 12, - borderCurve: "continuous", - marginBottom: 10, - }, - - serviceName: { - fontSize: 15, - fontFamily: "medium", - opacity: 0.6, - textAlign: "center", - }, - - serviceSchool: { - fontSize: 18, - fontFamily: "semibold", - textAlign: "center", - }, - - textInputContainer: { - width: "100%", - paddingVertical: 12, - paddingHorizontal: 14, - borderRadius: 12, - borderCurve: "continuous", - marginBottom: 9, - flexDirection: "row", - alignItems: "center", - gap: 14, - }, - - textInput: { - fontFamily: "medium", - fontSize: 16, - flex: 1, - }, -}); - export default PronoteCredentials; From 7330c4bd1c86c7b77bb2e57913e91dcad2868a6d Mon Sep 17 00:00:00 2001 From: Cleboost Date: Mon, 27 Jan 2025 21:09:25 +0100 Subject: [PATCH 0408/1144] =?UTF-8?q?ui=F0=9F=92=84:=20add=20background=20?= =?UTF-8?q?color=20to=20NewsItem=20component?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/News/Document.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/account/News/Document.tsx b/src/views/account/News/Document.tsx index 56d198237..33c4a68fe 100644 --- a/src/views/account/News/Document.tsx +++ b/src/views/account/News/Document.tsx @@ -146,6 +146,7 @@ const NewsItem: Screen<"NewsItem"> = ({ route, navigation }) => { style={{ flex: 1, paddingTop: 106 - 16, + backgroundColor: theme.colors.background, }} > Date: Mon, 27 Jan 2025 21:27:28 +0100 Subject: [PATCH 0409/1144] =?UTF-8?q?ui=F0=9F=92=84:=20Update=20checkbox?= =?UTF-8?q?=20color=20to=20use=20primary=20theme=20color?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/News/Document.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/News/Document.tsx b/src/views/account/News/Document.tsx index 46f2e42f5..f14dc9f4c 100644 --- a/src/views/account/News/Document.tsx +++ b/src/views/account/News/Document.tsx @@ -178,7 +178,7 @@ const NewsItem: Screen<"NewsItem"> = ({ route, navigation }) => { })); } }} - color="green" + color={theme.colors.primary} /> } > From fc255ec677f2b2bdeaf0f9d154806b4afdbc42e8 Mon Sep 17 00:00:00 2001 From: Bulgus <88266443+Bulgus@users.noreply.github.com> Date: Tue, 28 Jan 2025 13:55:43 +0100 Subject: [PATCH 0410/1144] remove all edit --- App.tsx | 8 +-- android/app/src/main/AndroidManifest.xml | 4 -- app.config.ts | 3 - .../AppIcon.appiconset/Contents.json | 42 ++++++++++--- ios/Papillon/Info.plist | 11 +--- package.json | 2 +- src/background/BackgroundTasks.ts | 6 +- src/background/Notifications.ts | 23 +++---- src/components/Templates/LoginView.tsx | 2 +- src/router/screens/views/index.ts | 32 ++-------- .../ecoledirecte/default-personalization.ts | 6 -- src/services/local/default-personalization.ts | 1 - .../pronote/default-personalization.ts | 2 +- .../skolengo/default-personalization.ts | 1 - src/utils/native/expoGoAlert.ts | 17 ++++- .../account/Grades/Atoms/GradesScodocUE.tsx | 3 +- .../Home/Elements/AttendanceElement.tsx | 43 ++++++------- .../Home/Elements/HomeworksElement.tsx | 35 +++++------ src/views/account/Homeworks/Atoms/Item.tsx | 20 +++--- src/views/account/Restaurant/Menu.tsx | 62 +------------------ .../login/pronote/PronoteCredentials.tsx | 5 +- .../login/pronote/PronoteInstanceSelector.tsx | 31 +--------- src/views/settings/Settings.tsx | 8 +-- src/views/settings/SettingsIcons.tsx | 18 +++--- src/views/welcome/ColorSelector.tsx | 6 +- src/views/welcome/FirstInstallation.tsx | 6 +- 26 files changed, 134 insertions(+), 263 deletions(-) diff --git a/App.tsx b/App.tsx index ff0fe4eae..80503e092 100644 --- a/App.tsx +++ b/App.tsx @@ -7,9 +7,8 @@ import AsyncStorage from "@react-native-async-storage/async-storage"; import { useAccounts, useCurrentAccount } from "@/stores/account"; import {AccountService, PrimaryAccount} from "@/stores/account/types"; import { log } from "@/utils/logger/logger"; -import { isExpoGo } from "@/utils/native/expoGoAlert"; +import { expoGoWrapper } from "@/utils/native/expoGoAlert"; import { atobPolyfill, btoaPolyfill } from "js-base64"; -import { registerBackgroundTasks } from "@/background/BackgroundTasks"; SplashScreen.preventAutoHideAsync(); @@ -106,9 +105,10 @@ export default function App () { "[Reanimated] Property ", ]); - if (!isExpoGo()) { + expoGoWrapper(async () => { + const { registerBackgroundTasks } = await import("@/background/BackgroundTasks"); registerBackgroundTasks(); - }; + }); }, []); const applyGlobalPolyfills = useCallback(() => { diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index e8f9fa9d2..4f859e36e 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -14,10 +14,6 @@ - - - - diff --git a/app.config.ts b/app.config.ts index 0dfe3ca3d..b859fae2c 100644 --- a/app.config.ts +++ b/app.config.ts @@ -76,9 +76,6 @@ export default (): ExpoConfig => ({ "android.permission.ACCESS_FINE_LOCATION", ], }, - extra: { - EXPO_ENV: "expo", - }, plugins: [ [ "expo-font", diff --git a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json index 90d8d4c2a..e91cbd33d 100644 --- a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,14 +1,38 @@ { - "images": [ + "images" : [ { - "filename": "App-Icon-1024x1024@1x.png", - "idiom": "universal", - "platform": "ios", - "size": "1024x1024" + "filename" : "Icon-Light-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Dark-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "Icon-Tinted-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" } ], - "info": { - "version": 1, - "author": "expo" + "info" : { + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index bc9188054..2b3629ee4 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 7.8.0 + 7.7.2 CFBundleSignature ???? CFBundleURLTypes @@ -36,13 +36,6 @@ 1 ITSAppUsesNonExemptEncryption - LSApplicationQueriesSchemes - - fb - instagram - twitter - tiktoksharesdk - LSRequiresIPhoneOS NSAppTransportSecurity @@ -53,7 +46,7 @@ NSCameraUsageDescription - Papillon utilise ta caméra pour scanner des QR-codes pour te connecter, pour capturer des documents, ou pour des fonctionnalités amusantes telles que les réactions. + Papillon utilise ta caméra pour scanner des QR-codes pour te connecter, pour capturer des documents, ou pour des fonctionnalités amusantes telles que les réaction. NSLocationAlwaysAndWhenInUseUsageDescription Autoriser $(PRODUCT_NAME) à utiliser ta localisation. NSLocationAlwaysUsageDescription diff --git a/package.json b/package.json index aed7885e3..262993ed3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "papillonvex", - "version": "7.9.0", + "version": "7.8.0", "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index bfac560d8..aaf05ef3e 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -1,7 +1,7 @@ import * as BackgroundFetch from "expo-background-fetch"; import * as TaskManager from "expo-task-manager"; import { BackgroundFetchResult } from "expo-background-fetch"; -import { isExpoGo } from "@/utils/native/expoGoAlert"; +import { expoGoWrapper } from "@/utils/native/expoGoAlert"; import { useAccounts, useCurrentAccount } from "@/stores/account"; import { fetchNews } from "./data/News"; @@ -26,7 +26,7 @@ const backgroundFetch = async () => { }; const registerBackgroundTasks = async () => { - if (!isExpoGo()) { + expoGoWrapper(async () => { TaskManager.defineTask("background-fetch", () => backgroundFetch()); BackgroundFetch?.registerTaskAsync("background-fetch", { @@ -38,7 +38,7 @@ const registerBackgroundTasks = async () => { backgroundFetch(); console.log("[background fetch] Registered background fetch"); - }; + }); }; const unsetBackgroundFetch = async () => { diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index 5018f0e64..61921d120 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -1,26 +1,21 @@ -import { alertExpoGo, isExpoGo } from "@/utils/native/expoGoAlert"; -import { Notification } from "@notifee/react-native"; +import { expoGoWrapper } from "@/utils/native/expoGoAlert"; +import {Notification} from "@notifee/react-native"; -const requestNotificationPermission = () => { - return async () => { - if (!isExpoGo()) { - const notifee = (await import("@notifee/react-native")).default; - await notifee.requestPermission(); - } else { - alertExpoGo(); - return false; - } - }; +const requestNotificationPermission = async () => { + return expoGoWrapper(async () => { + const notifee = (await import("@notifee/react-native")).default; + await notifee.requestPermission(); + }, true); }; const papillonNotify = async (props: Notification) => { - if (!isExpoGo()) { + expoGoWrapper(async () => { const notifee = (await import("@notifee/react-native")).default; await notifee.displayNotification({ ...props, title: props.title || "Coucou, c'est Papillon 👋", }); - } + }); }; export { diff --git a/src/components/Templates/LoginView.tsx b/src/components/Templates/LoginView.tsx index 09b22b7e1..ca4746957 100644 --- a/src/components/Templates/LoginView.tsx +++ b/src/components/Templates/LoginView.tsx @@ -165,7 +165,7 @@ const LoginView: React.FC<{ }} > }> - Impossible de se connecter, vérifie tes identifiants ou utilise le portail de ton ENT pour te connecter. + {error} )} diff --git a/src/router/screens/views/index.ts b/src/router/screens/views/index.ts index ffb39c703..d2fe1374e 100644 --- a/src/router/screens/views/index.ts +++ b/src/router/screens/views/index.ts @@ -60,41 +60,21 @@ export default [ }), createScreen("LessonDocument", LessonDocument, { headerTitle: "Cours", - presentation: "formSheet", - gestureDirection: "vertical", - animation: "slide_from_bottom", - sheetGrabberVisible: false, - sheetInitialDetentIndex: 0, - sheetAllowedDetents: [0.5, 1.0], + presentation: "modal", headerShown: false, }), createScreen("HomeworksDocument", HomeworksDocument, { headerTitle: "Devoir", - presentation: "formSheet", - gestureDirection: "vertical", - animation: "slide_from_bottom", - sheetGrabberVisible: false, - sheetInitialDetentIndex: 0, - sheetAllowedDetents: [0.5, 1.0], + presentation: "modal", headerShown: false, }), createScreen("GradeSubject", GradeSubjectScreen, { headerTitle: "Détail de la matière", - presentation: "formSheet", - gestureDirection: "vertical", - animation: "slide_from_bottom", - sheetGrabberVisible: true, - sheetInitialDetentIndex: 0, - sheetAllowedDetents: [0.5, 1.0], + presentation: "modal", }), createScreen("GradeDocument", GradeDocument, { headerTitle: "Détail de la note", - presentation: "formSheet", - gestureDirection: "vertical", - animation: "slide_from_bottom", - sheetGrabberVisible: false, - sheetInitialDetentIndex: 0, - sheetAllowedDetents: [0.5, 1.0], + presentation: "modal", headerShown: Platform.OS !== "ios", }), createScreen("ChatCreate", ChatCreate, { @@ -110,12 +90,12 @@ export default [ createScreen("ChatDetails", ChatDetails, { headerTitle: "Discussions", headerShown: false, - presentation: "modal", + presentation: "modal" }), createScreen("ChatThemes", ChatThemes, { headerTitle: "Thèmes", headerShown: true, - presentation: "modal", + presentation: "modal" }), createScreen("BackgroundIUTLannion", BackgroundIUTLannion, { headerTitle: "IUT de Lannion", diff --git a/src/services/ecoledirecte/default-personalization.ts b/src/services/ecoledirecte/default-personalization.ts index ccf11aef6..d2878085f 100644 --- a/src/services/ecoledirecte/default-personalization.ts +++ b/src/services/ecoledirecte/default-personalization.ts @@ -26,12 +26,6 @@ export default async function defaultPersonalization (account: Account): Promise Pragma: "no-cache" }) : void 0, - initialProfilePictureB64: account.profilePictureURL - ? await downloadAsBase64(account.profilePictureURL, { - Referer: ".ecoledirecte.com/", - Pragma: "no-cache" - }) - : void 0, tabs: defaultTabs.filter(current => defaultEDTabs.includes(current.tab)).map((tab, index) => ({ name: tab.tab, enabled: index <= 4 diff --git a/src/services/local/default-personalization.ts b/src/services/local/default-personalization.ts index 44e79658d..16beab36a 100644 --- a/src/services/local/default-personalization.ts +++ b/src/services/local/default-personalization.ts @@ -20,7 +20,6 @@ export default async function defaultPersonalization (customDefaults?: Partial

defaultLocalTabs.includes(current.tab)).map((tab, index) => ({ name: tab.tab, enabled: index <= 4 diff --git a/src/services/pronote/default-personalization.ts b/src/services/pronote/default-personalization.ts index 06ba09f87..f86ab2eee 100644 --- a/src/services/pronote/default-personalization.ts +++ b/src/services/pronote/default-personalization.ts @@ -32,7 +32,7 @@ const defaultPersonalization = async (instance: pronote.SessionHandle): Promise< name: tab.tab, enabled: index <= 4 })) - }; + }; }; export default defaultPersonalization; diff --git a/src/services/skolengo/default-personalization.ts b/src/services/skolengo/default-personalization.ts index 91b93d63f..9f35930ab 100644 --- a/src/services/skolengo/default-personalization.ts +++ b/src/services/skolengo/default-personalization.ts @@ -13,7 +13,6 @@ const defaultSkolengoPersonalization = async (instance: SkolengoAccount["instanc MagicHomeworks: true, MagicNews: true, profilePictureB64: void 0, - initialProfilePictureB64: void 0, tabs: defaultTabs.filter(current => skoTabs.includes(current.tab)).map((tab, index) => ({ name: tab.tab, diff --git a/src/utils/native/expoGoAlert.ts b/src/utils/native/expoGoAlert.ts index 50c22ef84..09967880e 100644 --- a/src/utils/native/expoGoAlert.ts +++ b/src/utils/native/expoGoAlert.ts @@ -2,7 +2,7 @@ import { Alert } from "react-native"; import Constants from "expo-constants"; export const isExpoGo = () => { - return Constants.expoConfig?.extra?.EXPO_ENV === "expo"; + return Constants.appOwnership === "expo"; }; export const alertExpoGo = async () => { @@ -11,3 +11,18 @@ export const alertExpoGo = async () => { "Sous Expo Go, les appels aux API natives sont indisponibles. Utilise un build de développement pour accéder à toutes les fonctionnalités.", ); }; +/** + * Function wrapper that only calls the function if the app is not Expo Go + * @param fn Founciton to call if not Expo Go + * @param alert Show an alert if the app is Expo Go + * @returns Execute the function if not Expo Go + */ +export const expoGoWrapper = (fn: () => void, alert?: boolean) => { + if (!isExpoGo()) { + return fn(); + } + else if (alert) { + alertExpoGo(); + return false; + } +}; \ No newline at end of file diff --git a/src/views/account/Grades/Atoms/GradesScodocUE.tsx b/src/views/account/Grades/Atoms/GradesScodocUE.tsx index 14b5bb852..41de3e697 100644 --- a/src/views/account/Grades/Atoms/GradesScodocUE.tsx +++ b/src/views/account/Grades/Atoms/GradesScodocUE.tsx @@ -40,7 +40,6 @@ const GradesScodocUE = ({ account, navigation }: { account: PrimaryAccount, navi return ( <> - + {finalUes.map((ue) => { interface ueGrade { key: string, diff --git a/src/views/account/Home/Elements/AttendanceElement.tsx b/src/views/account/Home/Elements/AttendanceElement.tsx index f9c86e928..1e1834264 100644 --- a/src/views/account/Home/Elements/AttendanceElement.tsx +++ b/src/views/account/Home/Elements/AttendanceElement.tsx @@ -92,31 +92,24 @@ const AttendanceElement: React.FC = ({ onImportance }) = if (!totalMissed || totalMissed.absences.length === 0) { return ( - <> - - )} - /> - - - - - - + + + + + ); } diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 6e7e2de34..7d306b21d 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -83,27 +83,20 @@ const HomeworksElement: React.FC = ({ navigation, onImpor if (hwSemaineActuelle.length === 0 && hwSemaineProchaine.length === 0) { return ( - <> - - )} - /> - - - - - - + + + + + ); } diff --git a/src/views/account/Homeworks/Atoms/Item.tsx b/src/views/account/Homeworks/Atoms/Item.tsx index d26aa6a4d..bd080164e 100644 --- a/src/views/account/Homeworks/Atoms/Item.tsx +++ b/src/views/account/Homeworks/Atoms/Item.tsx @@ -35,7 +35,6 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total } const theme = useTheme(); const [subjectData, setSubjectData] = useState(getSubjectData(homework.subject)); const [category, setCategory] = useState(null); - const [shouldShowMoreGradient, setShouldShowMoreGradient] = useState(false); const account = useCurrentAccount((store) => store.account!); const route = useRoute(); @@ -75,6 +74,8 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total } setMainLoaded(true); }, [homework.done]); + const contentLines = homework.content.split("\n"); + const renderCategoryOrReturnType = () => { if (category) { return ( @@ -188,24 +189,17 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total } exiting={FadeOut.duration(200).delay(50)} > - { - const { height } = event.nativeEvent.layout; - if (height >= 22 * 5) { - setShouldShowMoreGradient(true); - } - }} - > + ${parse_homeworks(homework.content).replace("\n", "")}`} stylesheet={stylesText} /> - {shouldShowMoreGradient && ( + {contentLines.length > 5 && ( diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 54632e3e8..ef822a4aa 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -36,7 +36,7 @@ import { Balance } from "@/services/shared/Balance"; import { balanceFromExternal } from "@/services/balance"; import MissingItem from "@/components/Global/MissingItem"; import { animPapillon } from "@/utils/ui/animations"; -import Reanimated, { FadeIn, FadeInDown, FadeInUp, FadeOut, FadeOutDown, LinearTransition, ZoomIn, ZoomOut } from "react-native-reanimated"; +import Reanimated, { FadeIn, FadeInDown, FadeInUp, FadeOut, FadeOutDown, LinearTransition } from "react-native-reanimated"; import { reservationHistoryFromExternal } from "@/services/reservation-history"; import { qrcodeFromExternal } from "@/services/qrcode"; import { ReservationHistory } from "@/services/shared/ReservationHistory"; @@ -50,9 +50,6 @@ import { bookDayFromExternal, getBookingsAvailableFromExternal } from "@/service import AccountButton from "@/components/Restaurant/AccountButton"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; import PapillonHeader from "@/components/Global/PapillonHeader"; -import { PressableScale } from "react-native-pressable-scale"; -import { BlurView } from "expo-blur"; -import { ChevronLeft, ChevronRight} from "lucide-react-native"; const Menu: Screen<"Menu"> = ({ route, navigation }) => { const theme = useTheme(); @@ -370,36 +367,8 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { /> - {(currentMenu || (allBookings && allBookings?.some((terminal) => terminal.days.some((day) => day.date?.toDateString() === pickerDate.toDateString())))) && + {(currentMenu || (allBookings && allBookings.some((terminal) => terminal.days.some((day) => day.date?.toDateString() === pickerDate.toDateString())))) && - - { - onDatePickerSelect(new Date(pickerDate.setDate(pickerDate.getDate() - 1))); - setRefreshCount(refreshCount + 1); - }} - activeScale={0.8} - > - - - - - setShowDatePicker(true)}> = ({ route, navigation }) => { {pickerDate.toLocaleDateString("fr-FR", { month: "long" })} - - { - onDatePickerSelect(new Date(pickerDate.setDate(pickerDate.getDate() + 1))); - setRefreshCount(refreshCount + 1); - }} - activeScale={0.8} - > - - - - - } diff --git a/src/views/login/pronote/PronoteCredentials.tsx b/src/views/login/pronote/PronoteCredentials.tsx index 6eea4d0bf..420d7566e 100644 --- a/src/views/login/pronote/PronoteCredentials.tsx +++ b/src/views/login/pronote/PronoteCredentials.tsx @@ -91,10 +91,7 @@ const PronoteCredentials: Screen<"PronoteCredentials"> = ({ route, navigation }) setLoading(false); if (error instanceof Error) { - if (error.name === "BadCredentialsError") - setError("Nom d'utilisateur ou mot de passe incorrect"); - else - setError(error.message); + setError(error.message); } else { setError("Erreur inconnue"); diff --git a/src/views/login/pronote/PronoteInstanceSelector.tsx b/src/views/login/pronote/PronoteInstanceSelector.tsx index 263031aa8..62158b4d4 100644 --- a/src/views/login/pronote/PronoteInstanceSelector.tsx +++ b/src/views/login/pronote/PronoteInstanceSelector.tsx @@ -28,12 +28,10 @@ import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import { LinearGradient } from "expo-linear-gradient"; import { useTheme } from "@react-navigation/native"; -import {Search, X, GraduationCap, SearchX} from "lucide-react-native"; +import { Search, X, GraduationCap } from "lucide-react-native"; import { useAlert } from "@/providers/AlertProvider"; import { Audio } from "expo-av"; import getInstancesFromDataset from "@/services/pronote/dataset_geolocation"; -import {openURL} from "expo-linking"; -import * as WebBrowser from "expo-web-browser"; const PronoteInstanceSelector: Screen<"PronoteInstanceSelector"> = ({ route: { params }, @@ -338,33 +336,6 @@ const PronoteInstanceSelector: Screen<"PronoteInstanceSelector"> = ({ /> ))} - - - } - onPress={async () => { - await WebBrowser.openBrowserAsync("https://support.papillon.bzh//articles/351104-frequency-asked-questions#etab-not-found", { - controlsColor: "#0E7CCB", - presentationStyle: WebBrowser.WebBrowserPresentationStyle.PAGE_SHEET, - }); - }} - text={"Je ne trouve pas mon établissement..."} - /> - diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index e83523db5..9ca1a0ee3 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -33,7 +33,7 @@ import { Smile, SwatchBook, WandSparkles, - X, HelpCircle + X } from "lucide-react-native"; import { NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; @@ -216,12 +216,6 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { label: "Quoi de neuf ?", onPress: () => navigation.navigate("ChangelogScreen"), }, - { - icon: , - color: "#0E7CCB", - label: "Besoin d'aide ?", - onPress: () => openUrl("https://support.papillon.bzh/"), - }, { icon: , color: "#888888", diff --git a/src/views/settings/SettingsIcons.tsx b/src/views/settings/SettingsIcons.tsx index e52000994..9591e7e07 100644 --- a/src/views/settings/SettingsIcons.tsx +++ b/src/views/settings/SettingsIcons.tsx @@ -12,7 +12,7 @@ import colorsList from "@/utils/data/colors.json"; import { getIconName, setIconName } from "@candlefinance/app-icon"; import PapillonCheckbox from "@/components/Global/PapillonCheckbox"; -import { alertExpoGo, isExpoGo } from "@/utils/native/expoGoAlert"; +import { expoGoWrapper } from "@/utils/native/expoGoAlert"; import { useAlert } from "@/providers/AlertProvider"; type Icon = { @@ -47,11 +47,11 @@ const SettingsIcons: Screen<"SettingsIcons"> = ({ navigation }) => { const [currentIcon, setIcon] = React.useState("default"); useEffect(() => { - if (!isExpoGo()) { + expoGoWrapper(() => { getIconName().then((icon) => { setIcon(icon); }); - }; + }); }, []); const setNewIcon = (icon: Icon) => { @@ -61,20 +61,16 @@ const SettingsIcons: Screen<"SettingsIcons"> = ({ navigation }) => { const iconConstructName = icon.id + (colorItem ? "_" + colorItem.id : ""); - if (!isExpoGo()) { + expoGoWrapper(() => { setIconName(iconConstructName); setIcon(iconConstructName); - } else { - alertExpoGo(); - }; + }, true); } else { - if (!isExpoGo()) { + expoGoWrapper(() => { setIconName(icon.id); setIcon(icon.id); - } else { - alertExpoGo(); - }; + }, true); } }; diff --git a/src/views/welcome/ColorSelector.tsx b/src/views/welcome/ColorSelector.tsx index 30f01bdf3..a8d7a50e1 100644 --- a/src/views/welcome/ColorSelector.tsx +++ b/src/views/welcome/ColorSelector.tsx @@ -15,7 +15,7 @@ import { getIconName, setIconName } from "@candlefinance/app-icon"; import colorsList from "@/utils/data/colors.json"; import { removeColor } from "../settings/SettingsIcons"; -import { isExpoGo } from "@/utils/native/expoGoAlert"; +import { expoGoWrapper } from "@/utils/native/expoGoAlert"; type Color = typeof colorsList[number]; @@ -84,7 +84,7 @@ const ColorSelector: Screen<"ColorSelector"> = ({ route, navigation }) => { Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); playSound2(); - if (!isExpoGo()) { + expoGoWrapper(() => { getIconName().then((currentIcon) => { if (currentIcon.includes("_Dynamic_")) { const mainColor = color.hex.primary; @@ -96,7 +96,7 @@ const ColorSelector: Screen<"ColorSelector"> = ({ route, navigation }) => { setIconName(iconConstructName); } }); - }; + }); }; const ColorButton: React.FC<{ color: Color }> = ({ color }) => ( diff --git a/src/views/welcome/FirstInstallation.tsx b/src/views/welcome/FirstInstallation.tsx index b3d85a259..dcfa6d6f3 100644 --- a/src/views/welcome/FirstInstallation.tsx +++ b/src/views/welcome/FirstInstallation.tsx @@ -14,8 +14,8 @@ import * as WebBrowser from "expo-web-browser"; import { Audio } from "expo-av"; import * as SplashScreen from "expo-splash-screen"; -const PRIVACY_POLICY_URL = "https://support.papillon.bzh/articles/352402-privacy-policy"; -const TERMS_OF_SERVICE_URL = "https://support.papillon.bzh/articles/352401-terms-of-service"; +const PRIVACY_POLICY_URL = "https://docs.papillon.bzh/legal/privacy"; +const TERMS_OF_SERVICE_URL = "https://docs.papillon.bzh/legal/terms"; const FirstInstallation: Screen<"FirstInstallation"> = ({ navigation }) => { const theme = useTheme(); @@ -91,7 +91,7 @@ const FirstInstallation: Screen<"FirstInstallation"> = ({ navigation }) => { openUrl("https://support.papillon.bzh/")} + onPress={() => openUrl("https://support.getpapillon.xyz/")} /> Date: Tue, 28 Jan 2025 13:56:41 +0100 Subject: [PATCH 0411/1144] remove all edit 2 --- App.tsx | 8 +-- android/app/src/main/AndroidManifest.xml | 4 ++ app.config.ts | 3 + .../AppIcon.appiconset/Contents.json | 42 +++---------- ios/Papillon/Info.plist | 11 +++- package.json | 2 +- src/background/BackgroundTasks.ts | 6 +- src/background/Notifications.ts | 23 ++++--- src/components/Templates/LoginView.tsx | 2 +- src/router/screens/views/index.ts | 32 ++++++++-- .../pronote/default-personalization.ts | 6 +- src/stores/account/types.ts | 1 - src/utils/native/expoGoAlert.ts | 17 +---- .../account/Grades/Atoms/GradesScodocUE.tsx | 3 +- .../Home/Elements/AttendanceElement.tsx | 43 +++++++------ .../Home/Elements/HomeworksElement.tsx | 35 ++++++----- src/views/account/Homeworks/Atoms/Item.tsx | 20 +++--- src/views/account/Restaurant/Menu.tsx | 62 ++++++++++++++++++- .../login/pronote/PronoteCredentials.tsx | 5 +- .../login/pronote/PronoteInstanceSelector.tsx | 31 +++++++++- src/views/settings/Settings.tsx | 8 ++- src/views/settings/SettingsIcons.tsx | 18 +++--- src/views/settings/SettingsProfile.tsx | 24 ------- src/views/welcome/ColorSelector.tsx | 6 +- src/views/welcome/FirstInstallation.tsx | 6 +- 25 files changed, 256 insertions(+), 162 deletions(-) diff --git a/App.tsx b/App.tsx index 80503e092..ff0fe4eae 100644 --- a/App.tsx +++ b/App.tsx @@ -7,8 +7,9 @@ import AsyncStorage from "@react-native-async-storage/async-storage"; import { useAccounts, useCurrentAccount } from "@/stores/account"; import {AccountService, PrimaryAccount} from "@/stores/account/types"; import { log } from "@/utils/logger/logger"; -import { expoGoWrapper } from "@/utils/native/expoGoAlert"; +import { isExpoGo } from "@/utils/native/expoGoAlert"; import { atobPolyfill, btoaPolyfill } from "js-base64"; +import { registerBackgroundTasks } from "@/background/BackgroundTasks"; SplashScreen.preventAutoHideAsync(); @@ -105,10 +106,9 @@ export default function App () { "[Reanimated] Property ", ]); - expoGoWrapper(async () => { - const { registerBackgroundTasks } = await import("@/background/BackgroundTasks"); + if (!isExpoGo()) { registerBackgroundTasks(); - }); + }; }, []); const applyGlobalPolyfills = useCallback(() => { diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 4f859e36e..e8f9fa9d2 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -14,6 +14,10 @@ + + + + diff --git a/app.config.ts b/app.config.ts index b859fae2c..0dfe3ca3d 100644 --- a/app.config.ts +++ b/app.config.ts @@ -76,6 +76,9 @@ export default (): ExpoConfig => ({ "android.permission.ACCESS_FINE_LOCATION", ], }, + extra: { + EXPO_ENV: "expo", + }, plugins: [ [ "expo-font", diff --git a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json index e91cbd33d..90d8d4c2a 100644 --- a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,38 +1,14 @@ { - "images" : [ + "images": [ { - "filename" : "Icon-Light-1024x1024.png", - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "Icon-Dark-1024x1024.png", - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "tinted" - } - ], - "filename" : "Icon-Tinted-1024x1024.png", - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" + "filename": "App-Icon-1024x1024@1x.png", + "idiom": "universal", + "platform": "ios", + "size": "1024x1024" } ], - "info" : { - "author" : "xcode", - "version" : 1 + "info": { + "version": 1, + "author": "expo" } -} +} \ No newline at end of file diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index 2b3629ee4..bc9188054 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 7.7.2 + 7.8.0 CFBundleSignature ???? CFBundleURLTypes @@ -36,6 +36,13 @@ 1 ITSAppUsesNonExemptEncryption + LSApplicationQueriesSchemes + + fb + instagram + twitter + tiktoksharesdk + LSRequiresIPhoneOS NSAppTransportSecurity @@ -46,7 +53,7 @@ NSCameraUsageDescription - Papillon utilise ta caméra pour scanner des QR-codes pour te connecter, pour capturer des documents, ou pour des fonctionnalités amusantes telles que les réaction. + Papillon utilise ta caméra pour scanner des QR-codes pour te connecter, pour capturer des documents, ou pour des fonctionnalités amusantes telles que les réactions. NSLocationAlwaysAndWhenInUseUsageDescription Autoriser $(PRODUCT_NAME) à utiliser ta localisation. NSLocationAlwaysUsageDescription diff --git a/package.json b/package.json index 262993ed3..aed7885e3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "papillonvex", - "version": "7.8.0", + "version": "7.9.0", "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index aaf05ef3e..bfac560d8 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -1,7 +1,7 @@ import * as BackgroundFetch from "expo-background-fetch"; import * as TaskManager from "expo-task-manager"; import { BackgroundFetchResult } from "expo-background-fetch"; -import { expoGoWrapper } from "@/utils/native/expoGoAlert"; +import { isExpoGo } from "@/utils/native/expoGoAlert"; import { useAccounts, useCurrentAccount } from "@/stores/account"; import { fetchNews } from "./data/News"; @@ -26,7 +26,7 @@ const backgroundFetch = async () => { }; const registerBackgroundTasks = async () => { - expoGoWrapper(async () => { + if (!isExpoGo()) { TaskManager.defineTask("background-fetch", () => backgroundFetch()); BackgroundFetch?.registerTaskAsync("background-fetch", { @@ -38,7 +38,7 @@ const registerBackgroundTasks = async () => { backgroundFetch(); console.log("[background fetch] Registered background fetch"); - }); + }; }; const unsetBackgroundFetch = async () => { diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index 61921d120..5018f0e64 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -1,21 +1,26 @@ -import { expoGoWrapper } from "@/utils/native/expoGoAlert"; -import {Notification} from "@notifee/react-native"; +import { alertExpoGo, isExpoGo } from "@/utils/native/expoGoAlert"; +import { Notification } from "@notifee/react-native"; -const requestNotificationPermission = async () => { - return expoGoWrapper(async () => { - const notifee = (await import("@notifee/react-native")).default; - await notifee.requestPermission(); - }, true); +const requestNotificationPermission = () => { + return async () => { + if (!isExpoGo()) { + const notifee = (await import("@notifee/react-native")).default; + await notifee.requestPermission(); + } else { + alertExpoGo(); + return false; + } + }; }; const papillonNotify = async (props: Notification) => { - expoGoWrapper(async () => { + if (!isExpoGo()) { const notifee = (await import("@notifee/react-native")).default; await notifee.displayNotification({ ...props, title: props.title || "Coucou, c'est Papillon 👋", }); - }); + } }; export { diff --git a/src/components/Templates/LoginView.tsx b/src/components/Templates/LoginView.tsx index ca4746957..09b22b7e1 100644 --- a/src/components/Templates/LoginView.tsx +++ b/src/components/Templates/LoginView.tsx @@ -165,7 +165,7 @@ const LoginView: React.FC<{ }} > }> - {error} + Impossible de se connecter, vérifie tes identifiants ou utilise le portail de ton ENT pour te connecter. )} diff --git a/src/router/screens/views/index.ts b/src/router/screens/views/index.ts index d2fe1374e..ffb39c703 100644 --- a/src/router/screens/views/index.ts +++ b/src/router/screens/views/index.ts @@ -60,21 +60,41 @@ export default [ }), createScreen("LessonDocument", LessonDocument, { headerTitle: "Cours", - presentation: "modal", + presentation: "formSheet", + gestureDirection: "vertical", + animation: "slide_from_bottom", + sheetGrabberVisible: false, + sheetInitialDetentIndex: 0, + sheetAllowedDetents: [0.5, 1.0], headerShown: false, }), createScreen("HomeworksDocument", HomeworksDocument, { headerTitle: "Devoir", - presentation: "modal", + presentation: "formSheet", + gestureDirection: "vertical", + animation: "slide_from_bottom", + sheetGrabberVisible: false, + sheetInitialDetentIndex: 0, + sheetAllowedDetents: [0.5, 1.0], headerShown: false, }), createScreen("GradeSubject", GradeSubjectScreen, { headerTitle: "Détail de la matière", - presentation: "modal", + presentation: "formSheet", + gestureDirection: "vertical", + animation: "slide_from_bottom", + sheetGrabberVisible: true, + sheetInitialDetentIndex: 0, + sheetAllowedDetents: [0.5, 1.0], }), createScreen("GradeDocument", GradeDocument, { headerTitle: "Détail de la note", - presentation: "modal", + presentation: "formSheet", + gestureDirection: "vertical", + animation: "slide_from_bottom", + sheetGrabberVisible: false, + sheetInitialDetentIndex: 0, + sheetAllowedDetents: [0.5, 1.0], headerShown: Platform.OS !== "ios", }), createScreen("ChatCreate", ChatCreate, { @@ -90,12 +110,12 @@ export default [ createScreen("ChatDetails", ChatDetails, { headerTitle: "Discussions", headerShown: false, - presentation: "modal" + presentation: "modal", }), createScreen("ChatThemes", ChatThemes, { headerTitle: "Thèmes", headerShown: true, - presentation: "modal" + presentation: "modal", }), createScreen("BackgroundIUTLannion", BackgroundIUTLannion, { headerTitle: "IUT de Lannion", diff --git a/src/services/pronote/default-personalization.ts b/src/services/pronote/default-personalization.ts index f86ab2eee..70ce3c1e3 100644 --- a/src/services/pronote/default-personalization.ts +++ b/src/services/pronote/default-personalization.ts @@ -25,14 +25,12 @@ const defaultPersonalization = async (instance: pronote.SessionHandle): Promise< profilePictureB64: user.profilePicture ? await downloadAsBase64(user.profilePicture.url) : void 0, - initialProfilePictureB64: user.profilePicture - ? await downloadAsBase64(user.profilePicture.url) - : void 0, + tabs: defaultTabs.filter(current => defaultPronoteTabs.includes(current.tab)).map((tab, index) => ({ name: tab.tab, enabled: index <= 4 })) - }; + }; }; export default defaultPersonalization; diff --git a/src/stores/account/types.ts b/src/stores/account/types.ts index 52db58be5..dde651879 100644 --- a/src/stores/account/types.ts +++ b/src/stores/account/types.ts @@ -36,7 +36,6 @@ export interface PapillonIcalURL { export interface Personalization { color: PersonalizationColor profilePictureB64?: string, - initialProfilePictureB64?: string, hideNameOnHomeScreen: boolean, hideProfilePicOnHomeScreen: boolean, hideTabTitles: boolean, diff --git a/src/utils/native/expoGoAlert.ts b/src/utils/native/expoGoAlert.ts index 09967880e..50c22ef84 100644 --- a/src/utils/native/expoGoAlert.ts +++ b/src/utils/native/expoGoAlert.ts @@ -2,7 +2,7 @@ import { Alert } from "react-native"; import Constants from "expo-constants"; export const isExpoGo = () => { - return Constants.appOwnership === "expo"; + return Constants.expoConfig?.extra?.EXPO_ENV === "expo"; }; export const alertExpoGo = async () => { @@ -11,18 +11,3 @@ export const alertExpoGo = async () => { "Sous Expo Go, les appels aux API natives sont indisponibles. Utilise un build de développement pour accéder à toutes les fonctionnalités.", ); }; -/** - * Function wrapper that only calls the function if the app is not Expo Go - * @param fn Founciton to call if not Expo Go - * @param alert Show an alert if the app is Expo Go - * @returns Execute the function if not Expo Go - */ -export const expoGoWrapper = (fn: () => void, alert?: boolean) => { - if (!isExpoGo()) { - return fn(); - } - else if (alert) { - alertExpoGo(); - return false; - } -}; \ No newline at end of file diff --git a/src/views/account/Grades/Atoms/GradesScodocUE.tsx b/src/views/account/Grades/Atoms/GradesScodocUE.tsx index 41de3e697..14b5bb852 100644 --- a/src/views/account/Grades/Atoms/GradesScodocUE.tsx +++ b/src/views/account/Grades/Atoms/GradesScodocUE.tsx @@ -40,6 +40,7 @@ const GradesScodocUE = ({ account, navigation }: { account: PrimaryAccount, navi return ( <> - + {finalUes.map((ue) => { interface ueGrade { key: string, diff --git a/src/views/account/Home/Elements/AttendanceElement.tsx b/src/views/account/Home/Elements/AttendanceElement.tsx index 1e1834264..f9c86e928 100644 --- a/src/views/account/Home/Elements/AttendanceElement.tsx +++ b/src/views/account/Home/Elements/AttendanceElement.tsx @@ -92,24 +92,31 @@ const AttendanceElement: React.FC = ({ onImportance }) = if (!totalMissed || totalMissed.absences.length === 0) { return ( - - - - - + <> + + )} + /> + + + + + + ); } diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 7d306b21d..6e7e2de34 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -83,20 +83,27 @@ const HomeworksElement: React.FC = ({ navigation, onImpor if (hwSemaineActuelle.length === 0 && hwSemaineProchaine.length === 0) { return ( - - - - - + <> + + )} + /> + + + + + + ); } diff --git a/src/views/account/Homeworks/Atoms/Item.tsx b/src/views/account/Homeworks/Atoms/Item.tsx index bd080164e..d26aa6a4d 100644 --- a/src/views/account/Homeworks/Atoms/Item.tsx +++ b/src/views/account/Homeworks/Atoms/Item.tsx @@ -35,6 +35,7 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total } const theme = useTheme(); const [subjectData, setSubjectData] = useState(getSubjectData(homework.subject)); const [category, setCategory] = useState(null); + const [shouldShowMoreGradient, setShouldShowMoreGradient] = useState(false); const account = useCurrentAccount((store) => store.account!); const route = useRoute(); @@ -74,8 +75,6 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total } setMainLoaded(true); }, [homework.done]); - const contentLines = homework.content.split("\n"); - const renderCategoryOrReturnType = () => { if (category) { return ( @@ -189,17 +188,24 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total } exiting={FadeOut.duration(200).delay(50)} > - + { + const { height } = event.nativeEvent.layout; + if (height >= 22 * 5) { + setShouldShowMoreGradient(true); + } + }} + > ${parse_homeworks(homework.content).replace("\n", "")}`} stylesheet={stylesText} /> - {contentLines.length > 5 && ( + {shouldShowMoreGradient && ( diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index ef822a4aa..54632e3e8 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -36,7 +36,7 @@ import { Balance } from "@/services/shared/Balance"; import { balanceFromExternal } from "@/services/balance"; import MissingItem from "@/components/Global/MissingItem"; import { animPapillon } from "@/utils/ui/animations"; -import Reanimated, { FadeIn, FadeInDown, FadeInUp, FadeOut, FadeOutDown, LinearTransition } from "react-native-reanimated"; +import Reanimated, { FadeIn, FadeInDown, FadeInUp, FadeOut, FadeOutDown, LinearTransition, ZoomIn, ZoomOut } from "react-native-reanimated"; import { reservationHistoryFromExternal } from "@/services/reservation-history"; import { qrcodeFromExternal } from "@/services/qrcode"; import { ReservationHistory } from "@/services/shared/ReservationHistory"; @@ -50,6 +50,9 @@ import { bookDayFromExternal, getBookingsAvailableFromExternal } from "@/service import AccountButton from "@/components/Restaurant/AccountButton"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; import PapillonHeader from "@/components/Global/PapillonHeader"; +import { PressableScale } from "react-native-pressable-scale"; +import { BlurView } from "expo-blur"; +import { ChevronLeft, ChevronRight} from "lucide-react-native"; const Menu: Screen<"Menu"> = ({ route, navigation }) => { const theme = useTheme(); @@ -367,8 +370,36 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { /> - {(currentMenu || (allBookings && allBookings.some((terminal) => terminal.days.some((day) => day.date?.toDateString() === pickerDate.toDateString())))) && + {(currentMenu || (allBookings && allBookings?.some((terminal) => terminal.days.some((day) => day.date?.toDateString() === pickerDate.toDateString())))) && + + { + onDatePickerSelect(new Date(pickerDate.setDate(pickerDate.getDate() - 1))); + setRefreshCount(refreshCount + 1); + }} + activeScale={0.8} + > + + + + + setShowDatePicker(true)}> = ({ route, navigation }) => { {pickerDate.toLocaleDateString("fr-FR", { month: "long" })} + + { + onDatePickerSelect(new Date(pickerDate.setDate(pickerDate.getDate() + 1))); + setRefreshCount(refreshCount + 1); + }} + activeScale={0.8} + > + + + + + } diff --git a/src/views/login/pronote/PronoteCredentials.tsx b/src/views/login/pronote/PronoteCredentials.tsx index 420d7566e..6eea4d0bf 100644 --- a/src/views/login/pronote/PronoteCredentials.tsx +++ b/src/views/login/pronote/PronoteCredentials.tsx @@ -91,7 +91,10 @@ const PronoteCredentials: Screen<"PronoteCredentials"> = ({ route, navigation }) setLoading(false); if (error instanceof Error) { - setError(error.message); + if (error.name === "BadCredentialsError") + setError("Nom d'utilisateur ou mot de passe incorrect"); + else + setError(error.message); } else { setError("Erreur inconnue"); diff --git a/src/views/login/pronote/PronoteInstanceSelector.tsx b/src/views/login/pronote/PronoteInstanceSelector.tsx index 62158b4d4..263031aa8 100644 --- a/src/views/login/pronote/PronoteInstanceSelector.tsx +++ b/src/views/login/pronote/PronoteInstanceSelector.tsx @@ -28,10 +28,12 @@ import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import { LinearGradient } from "expo-linear-gradient"; import { useTheme } from "@react-navigation/native"; -import { Search, X, GraduationCap } from "lucide-react-native"; +import {Search, X, GraduationCap, SearchX} from "lucide-react-native"; import { useAlert } from "@/providers/AlertProvider"; import { Audio } from "expo-av"; import getInstancesFromDataset from "@/services/pronote/dataset_geolocation"; +import {openURL} from "expo-linking"; +import * as WebBrowser from "expo-web-browser"; const PronoteInstanceSelector: Screen<"PronoteInstanceSelector"> = ({ route: { params }, @@ -336,6 +338,33 @@ const PronoteInstanceSelector: Screen<"PronoteInstanceSelector"> = ({ /> ))} + + + } + onPress={async () => { + await WebBrowser.openBrowserAsync("https://support.papillon.bzh//articles/351104-frequency-asked-questions#etab-not-found", { + controlsColor: "#0E7CCB", + presentationStyle: WebBrowser.WebBrowserPresentationStyle.PAGE_SHEET, + }); + }} + text={"Je ne trouve pas mon établissement..."} + /> + diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index 9ca1a0ee3..e83523db5 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -33,7 +33,7 @@ import { Smile, SwatchBook, WandSparkles, - X + X, HelpCircle } from "lucide-react-native"; import { NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; @@ -216,6 +216,12 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { label: "Quoi de neuf ?", onPress: () => navigation.navigate("ChangelogScreen"), }, + { + icon: , + color: "#0E7CCB", + label: "Besoin d'aide ?", + onPress: () => openUrl("https://support.papillon.bzh/"), + }, { icon: , color: "#888888", diff --git a/src/views/settings/SettingsIcons.tsx b/src/views/settings/SettingsIcons.tsx index 9591e7e07..e52000994 100644 --- a/src/views/settings/SettingsIcons.tsx +++ b/src/views/settings/SettingsIcons.tsx @@ -12,7 +12,7 @@ import colorsList from "@/utils/data/colors.json"; import { getIconName, setIconName } from "@candlefinance/app-icon"; import PapillonCheckbox from "@/components/Global/PapillonCheckbox"; -import { expoGoWrapper } from "@/utils/native/expoGoAlert"; +import { alertExpoGo, isExpoGo } from "@/utils/native/expoGoAlert"; import { useAlert } from "@/providers/AlertProvider"; type Icon = { @@ -47,11 +47,11 @@ const SettingsIcons: Screen<"SettingsIcons"> = ({ navigation }) => { const [currentIcon, setIcon] = React.useState("default"); useEffect(() => { - expoGoWrapper(() => { + if (!isExpoGo()) { getIconName().then((icon) => { setIcon(icon); }); - }); + }; }, []); const setNewIcon = (icon: Icon) => { @@ -61,16 +61,20 @@ const SettingsIcons: Screen<"SettingsIcons"> = ({ navigation }) => { const iconConstructName = icon.id + (colorItem ? "_" + colorItem.id : ""); - expoGoWrapper(() => { + if (!isExpoGo()) { setIconName(iconConstructName); setIcon(iconConstructName); - }, true); + } else { + alertExpoGo(); + }; } else { - expoGoWrapper(() => { + if (!isExpoGo()) { setIconName(icon.id); setIcon(icon.id); - }, true); + } else { + alertExpoGo(); + }; } }; diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index 321b4f963..c7630b773 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -26,15 +26,6 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { const [firstName, setFirstName] = useState(account.studentName?.first ?? ""); const [lastName, setLastName] = useState(account.studentName?.last ?? ""); - const resetProfilePic = () => { - const initialPic = account.personalization.initialProfilePictureB64; - setProfilePic(initialPic); - mutateProperty("personalization", { - ...account.personalization, - profilePictureB64: initialPic, - }); - }; - // on name change, update the account name useEffect(() => { let newLastName = lastName; @@ -185,21 +176,6 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { )} - - {profilePic && ( - } - > - - Réinitialiser la photo de profil - - - Supprime la photo actuelle et rétablit l'image par défaut. - - - )} = ({ route, navigation }) => { Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); playSound2(); - expoGoWrapper(() => { + if (!isExpoGo()) { getIconName().then((currentIcon) => { if (currentIcon.includes("_Dynamic_")) { const mainColor = color.hex.primary; @@ -96,7 +96,7 @@ const ColorSelector: Screen<"ColorSelector"> = ({ route, navigation }) => { setIconName(iconConstructName); } }); - }); + }; }; const ColorButton: React.FC<{ color: Color }> = ({ color }) => ( diff --git a/src/views/welcome/FirstInstallation.tsx b/src/views/welcome/FirstInstallation.tsx index dcfa6d6f3..b3d85a259 100644 --- a/src/views/welcome/FirstInstallation.tsx +++ b/src/views/welcome/FirstInstallation.tsx @@ -14,8 +14,8 @@ import * as WebBrowser from "expo-web-browser"; import { Audio } from "expo-av"; import * as SplashScreen from "expo-splash-screen"; -const PRIVACY_POLICY_URL = "https://docs.papillon.bzh/legal/privacy"; -const TERMS_OF_SERVICE_URL = "https://docs.papillon.bzh/legal/terms"; +const PRIVACY_POLICY_URL = "https://support.papillon.bzh/articles/352402-privacy-policy"; +const TERMS_OF_SERVICE_URL = "https://support.papillon.bzh/articles/352401-terms-of-service"; const FirstInstallation: Screen<"FirstInstallation"> = ({ navigation }) => { const theme = useTheme(); @@ -91,7 +91,7 @@ const FirstInstallation: Screen<"FirstInstallation"> = ({ navigation }) => { openUrl("https://support.getpapillon.xyz/")} + onPress={() => openUrl("https://support.papillon.bzh/")} /> Date: Tue, 28 Jan 2025 18:19:46 +0100 Subject: [PATCH 0412/1144] =?UTF-8?q?ajout=20de=20la=20v=C3=A9rification?= =?UTF-8?q?=20de=20Expo=20Go?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/BackgroundTasks.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 6a99e8151..17c29d7bf 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -87,7 +87,9 @@ const backgroundFetch = async () => { } }; -TaskManager.defineTask("background-fetch", backgroundFetch); +if (!isExpoGo()) { + TaskManager.defineTask("background-fetch", backgroundFetch); +} const unsetBackgroundFetch = async () => { await BackgroundFetch.unregisterTaskAsync("background-fetch"); From 385a423ad3a9309d0da708ed54a6c93420a85fcc Mon Sep 17 00:00:00 2001 From: Bulgus <88266443+Bulgus@users.noreply.github.com> Date: Tue, 28 Jan 2025 18:45:22 +0100 Subject: [PATCH 0413/1144] create a GetProfilePicture function in utils --- src/views/settings/SettingsProfile.tsx | 28 ++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index c7630b773..6b99a2b1e 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -10,6 +10,7 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import * as Clipboard from "expo-clipboard"; import { TouchableOpacity } from "react-native-gesture-handler"; +//import { getDefaultProfilePicture } from "@/utils/GetProfilePicture"; const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { const theme = useTheme(); @@ -26,6 +27,18 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { const [firstName, setFirstName] = useState(account.studentName?.first ?? ""); const [lastName, setLastName] = useState(account.studentName?.last ?? ""); + console.warn(account) + + async function resetProfilePic(account: Account) { + const initialPic = await getDefaultProfilePicture(account); + + setProfilePic(initialPic); + mutateProperty("personalization", { + ...account.personalization, + profilePictureB64: initialPic, + }); + } + // on name change, update the account name useEffect(() => { let newLastName = lastName; @@ -176,6 +189,21 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { )} + + {profilePic && ( + } + > + + Réinitialiser la photo de profil + + + Supprime la photo actuelle et rétablit l'image par défaut. + + + )} Date: Tue, 28 Jan 2025 18:49:29 +0100 Subject: [PATCH 0414/1144] fix(grades): if no coeffecient is returned, set to 1 --- src/services/pronote/grades.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/pronote/grades.ts b/src/services/pronote/grades.ts index 0ad206e2d..61d1c998b 100644 --- a/src/services/pronote/grades.ts +++ b/src/services/pronote/grades.ts @@ -105,7 +105,7 @@ export const getGradesAndAverages = async ( isOptional: g.isOptional, outOf: decodeGradeValue(g.outOf), - coefficient: g.coefficient, + coefficient: g.coefficient ?? 1, student: decodeGradeValue(g.value), average: decodeGradeValue(g.average), From 6f01fa848a38c26748905a500ef81718af2dc1bb Mon Sep 17 00:00:00 2001 From: Bulgus <88266443+Bulgus@users.noreply.github.com> Date: Tue, 28 Jan 2025 19:00:49 +0100 Subject: [PATCH 0415/1144] Apply the same thing to Ecole Direct --- src/services/ecoledirecte/grades.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/ecoledirecte/grades.ts b/src/services/ecoledirecte/grades.ts index 2754abc0f..2bb145ad1 100644 --- a/src/services/ecoledirecte/grades.ts +++ b/src/services/ecoledirecte/grades.ts @@ -122,7 +122,7 @@ export const getGradesAndAverages = async ( isOptional: g.isOptional, outOf: getGradeValue(g.outOf), - coefficient: g.coefficient, + coefficient: g.coefficient ?? 1, student: decodeGradeValue(g.value), average: decodeGradeValue(g.average), From ed24c75555c3b1cb547db1c9812142e17813b240 Mon Sep 17 00:00:00 2001 From: Bulgus <88266443+Bulgus@users.noreply.github.com> Date: Tue, 28 Jan 2025 19:02:22 +0100 Subject: [PATCH 0416/1144] If the returned coefficient is 0 in Skolengo, take it into account rather than setting it to 1 --- src/services/skolengo/data/grades.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/skolengo/data/grades.ts b/src/services/skolengo/data/grades.ts index 805fe7d6a..941c788ff 100644 --- a/src/services/skolengo/data/grades.ts +++ b/src/services/skolengo/data/grades.ts @@ -65,7 +65,7 @@ export const getGradesAndAverages = async (account: SkolengoAccount, periodName: isOptional: false, outOf: decodeGradeNumber(g.scale), - coefficient: g.coefficient || 1, + coefficient: g.coefficient ?? 1, student: decodeGradeNumber(g.evaluationResult.mark), average: decodeGradeNumber(g.average), From eca5b00f62cb63a3eb4572a5be414f11c8818f91 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 28 Jan 2025 19:21:24 +0100 Subject: [PATCH 0417/1144] fix: ajout semestres --- src/services/grades.ts | 30 ++++++--- src/services/iutlan/grades.ts | 29 ++++++++- .../actions/BackgroundIUTLannion.tsx | 65 +++++++++++++++---- 3 files changed, 100 insertions(+), 24 deletions(-) diff --git a/src/services/grades.ts b/src/services/grades.ts index 52add827b..830f7b507 100644 --- a/src/services/grades.ts +++ b/src/services/grades.ts @@ -32,15 +32,25 @@ export async function updateGradesPeriodsInCache (account: T break; } case AccountService.Local: { - periods = [ - { - name: "Toutes", - startTimestamp: 1609459200, - endTimestamp: 1622505600 - }, - ]; - defaultPeriod = "Toutes"; - break; + if (account.identityProvider.identifier == "iut-lannion") { + const { saveIUTLanPeriods } = await import("./iutlan/grades"); + const data = await saveIUTLanPeriods(account); + + periods = data.periods; + defaultPeriod = data.defaultPeriod; + break; + } + else { + periods = [ + { + name: "Toutes", + startTimestamp: 1609459200, + endTimestamp: 1622505600 + }, + ]; + defaultPeriod = "Toutes"; + break; + } } case AccountService.Skolengo: { if(!checkIfSkoSupported(account, "Grades")) { @@ -91,7 +101,7 @@ export async function updateGradesAndAveragesInCache (accoun case AccountService.Local: { if (account.identityProvider.identifier == "iut-lannion") { const { saveIUTLanGrades } = await import("./iutlan/grades"); - const data = await saveIUTLanGrades(account); + const data = await saveIUTLanGrades(account, periodName); grades = data.grades; averages = data.averages; diff --git a/src/services/iutlan/grades.ts b/src/services/iutlan/grades.ts index 3ab4c0225..6bd113c61 100644 --- a/src/services/iutlan/grades.ts +++ b/src/services/iutlan/grades.ts @@ -5,11 +5,13 @@ import { } from "@/services/shared/Grade"; import uuid from "@/utils/uuid-v4"; -export const saveIUTLanGrades = async (account: LocalAccount): Promise<{ +export const saveIUTLanGrades = async (account: LocalAccount, periodName: string): Promise<{ grades: Grade[]; averages: AverageOverview; }> => { try { + // console.log(periodName); + // Il faudrait peut-être penser à typer cette partie, tous les types sont any :( const scodocData = account.identityProvider.rawData; @@ -168,3 +170,28 @@ export const saveIUTLanGrades = async (account: LocalAccount): Promise<{ }; } }; + +export const saveIUTLanPeriods = async (account: LocalAccount): Promise => { + const scodocData = account.identityProvider.rawData; + + const semestres = (scodocData["semestres"] as any).map((semestre: any) => { + return { + name: "Semestre " + semestre.semestre_id, + startTimestamp: 1609459200, + endTimestamp: 1622505600 + }; + }); + + const finalData = { + periods: semestres.length > 0 ? semestres : [ + { + name: "Toutes", + startTimestamp: 1609459200, + endTimestamp: 1622505600 + }, + ], + defaultPeriod: semestres.length > 0 ? semestres[semestres.length - 1].name : "Toutes" + }; + + return finalData; +}; diff --git a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx index 33736ff8b..5a1baca09 100644 --- a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx +++ b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx @@ -4,13 +4,9 @@ import { AccountService, Identity, LocalAccount } from "@/stores/account/types"; import uuid from "@/utils/uuid-v4"; import { useTheme } from "@react-navigation/native"; import React from "react"; -import { Alert, View } from "react-native"; +import { Alert } from "react-native"; import { WebView } from "react-native-webview"; import type { Screen } from "@/router/helpers/types"; -import PapillonSpinner from "@/components/Global/PapillonSpinner"; -import { NativeText } from "@/components/Global/NativeComponents"; -import { animPapillon } from "@/utils/ui/animations"; -import { FadeInDown, FadeOutUp } from "react-native-reanimated"; const providers = ["scodoc", "moodle", "ical"]; @@ -87,10 +83,39 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio mutateProperty("providers", providers); mutateProperty("identity", buildIdentity(data)); - navigation.goBack(); + retreiveGrades(data); + + // navigation.goBack(); } }; + const [semestresToRetrieve, setSemestresToRetrieve] = React.useState([]); + const [currentSemestre, setCurrentSemestre] = React.useState(0); + + const retreiveGrades = async (data: any) => { + const scodocData = data; + const semestres = (scodocData["semestres"] as any); + + setSemestresToRetrieve(semestres); + await retreiveNextSemestre(semestres); + }; + + const retreiveNextSemestre = async (semestres: any[] = semestresToRetrieve) => { + console.log("Retreive next semestre"); + const sem = semestres[currentSemestre]; + console.log(sem); + wbref.current?.injectJavaScript(` + window.location.href = "https://notes9.iutlan.univ-rennes1.fr/services/data.php?q=relev%C3%A9Etudiant&semestre=" + ${sem.formsemestre_id}; + `); + }; + + const processSemestre = async (data: any) => { + // ajouter le semestre ici + + // passer au prochain semestre + // #TODO + }; + const actionFirstLogin = async (data: any) => { const local_account: LocalAccount = { authentication: undefined, @@ -183,7 +208,7 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio return ( <> - = ({ route, navigatio Cela peut prendre quelques secondes... - + */} = ({ route, navigatio onLoad={(data) => { const url = data.nativeEvent.url; + console.log(url); if(url.startsWith("https://sso-cas.univ-rennes.fr//login?")) { injectPassword(); @@ -231,10 +257,15 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio setCanExtractJSON(false); } - if(url.startsWith("https://notes9.iutlan.univ-rennes1.fr/services/data.php")) { + if(url.startsWith("https://notes9.iutlan.univ-rennes1.fr/services/data.php?q=relev%C3%A9Etudiant&semestre=")) { wbref.current?.injectJavaScript(` - window.ReactNativeWebView.postMessage(document.body.innerText); - `); + window.ReactNativeWebView.postMessage("semestre:"+document.body.innerText); + `); + } + else if(url.startsWith("https://notes9.iutlan.univ-rennes1.fr/services/data.php")) { + wbref.current?.injectJavaScript(` + window.ReactNativeWebView.postMessage("firstLogin:"+document.body.innerText); + `); } }} @@ -250,8 +281,16 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio onMessage={(event) => { try { - const parsedData = JSON.parse(event.nativeEvent.data); - useData(parsedData); + if(event.nativeEvent.data.startsWith("firstLogin:")) { + const data = event.nativeEvent.data.replace("firstLogin:", ""); + const parsedData = JSON.parse(data); + useData(parsedData); + } + else if(event.nativeEvent.data.startsWith("semestre:")) { + const data = event.nativeEvent.data.replace("semestre:", ""); + const parsedData = JSON.parse(data); + processSemestre(parsedData); + } } catch (e) { console.error(e); From e7cb43f12978334c0c26cb8e00e7c2c337227635 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 28 Jan 2025 20:25:09 +0100 Subject: [PATCH 0418/1144] feat: Added semesters to IUT de Lannion --- src/components/Global/PapillonPicker.tsx | 2 +- src/services/iutlan/grades.ts | 21 ++++- src/stores/account/types.ts | 61 ++++++++------ .../actions/BackgroundIUTLannion.tsx | 83 ++++++++++++++----- 4 files changed, 117 insertions(+), 50 deletions(-) diff --git a/src/components/Global/PapillonPicker.tsx b/src/components/Global/PapillonPicker.tsx index 93777c422..0fcee8680 100644 --- a/src/components/Global/PapillonPicker.tsx +++ b/src/components/Global/PapillonPicker.tsx @@ -136,7 +136,7 @@ const PapillonPicker: React.FC = ({ - {item === selected || (isNotString && item.checked) && ( + {isNotString ? item.checked : item === selected && ( ; } export interface EcoleDirecteAccount extends BaseAccount { - service: AccountService.EcoleDirecte - instance: {} + service: AccountService.EcoleDirecte; + instance: {}; authentication: { - session: PawdirecteSession - account: PawdirecteAccount - } - identityProvider?: undefined + session: PawdirecteSession; + account: PawdirecteAccount; + }; + identityProvider?: undefined; + providers: string[]; + serviceData: Record; } export interface SkolengoAccount extends BaseAccount { - service: AccountService.Skolengo - instance?: ScolengoAPI.Skolengo - authentication: SkolengoAuthConfig - userInfo: ScolengoAPIUser - identityProvider?: undefined + service: AccountService.Skolengo; + instance?: ScolengoAPI.Skolengo; + authentication: SkolengoAuthConfig; + userInfo: ScolengoAPIUser; + identityProvider?: undefined; + providers: string[]; + serviceData: Record; } export interface MultiAccount extends BaseAccount { @@ -174,27 +180,30 @@ export interface MultiAccount extends BaseAccount { refreshAuthToken: string } identityProvider?: undefined + providers: string[] + serviceData: Record } export interface LocalAccount extends BaseAccount { - service: AccountService.Local + service: AccountService.Local; // Both are useless for local accounts. - instance: undefined | Record - authentication: undefined | boolean + instance: undefined | Record; + authentication: undefined | boolean; identityProvider: { - identifier: string - name: string, - rawData: Record - } + identifier: string; + name: string; + rawData: Record; + }; credentials?: { - username: string - password: string - } + username: string; + password: string; + }; - providers?: string[] + providers?: string[]; + serviceData: Record; } export interface TurboselfAccount extends BaseExternalAccount { diff --git a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx index 5a1baca09..259f81191 100644 --- a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx +++ b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx @@ -4,9 +4,13 @@ import { AccountService, Identity, LocalAccount } from "@/stores/account/types"; import uuid from "@/utils/uuid-v4"; import { useTheme } from "@react-navigation/native"; import React from "react"; -import { Alert } from "react-native"; +import { Alert, View } from "react-native"; import { WebView } from "react-native-webview"; import type { Screen } from "@/router/helpers/types"; +import PapillonSpinner from "@/components/Global/PapillonSpinner"; +import { NativeText } from "@/components/Global/NativeComponents"; +import { animPapillon } from "@/utils/ui/animations"; +import { FadeInDown, FadeOutUp } from "react-native-reanimated"; const providers = ["scodoc", "moodle", "ical"]; @@ -84,8 +88,6 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio mutateProperty("identity", buildIdentity(data)); retreiveGrades(data); - - // navigation.goBack(); } }; @@ -93,17 +95,30 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio const [currentSemestre, setCurrentSemestre] = React.useState(0); const retreiveGrades = async (data: any) => { - const scodocData = data; - const semestres = (scodocData["semestres"] as any); + setStep("Récupération des notes"); + + try { + const scodocData = data; + const semestres = (scodocData["semestres"] as any); - setSemestresToRetrieve(semestres); - await retreiveNextSemestre(semestres); + setSemestresToRetrieve(semestres); + await retreiveNextSemestre(currentSemestre, semestres); + } + catch (e) { + console.error(e); + Alert.alert( + "Erreur", + "Impossible de récupérer les notes de l'IUT de Lannion. Vérifie ta connexion internet et réessaye.", + [{ text: "OK", onPress: () => navigation.goBack() }] + ); + navigation.goBack(); + } }; - const retreiveNextSemestre = async (semestres: any[] = semestresToRetrieve) => { - console.log("Retreive next semestre"); - const sem = semestres[currentSemestre]; + const retreiveNextSemestre = async (cs: number, semestres: any[] = semestresToRetrieve) => { + const sem = semestres[cs]; console.log(sem); + setStep("Récupération du semestre " + sem.semestre_id); wbref.current?.injectJavaScript(` window.location.href = "https://notes9.iutlan.univ-rennes1.fr/services/data.php?q=relev%C3%A9Etudiant&semestre=" + ${sem.formsemestre_id}; `); @@ -111,9 +126,41 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio const processSemestre = async (data: any) => { // ajouter le semestre ici + const newServiceData = account?.serviceData || {}; + + if (!newServiceData["semestres"]) { + newServiceData["semestres"] = {}; + } + + const semesterName = "Semestre " + semestresToRetrieve[currentSemestre].semestre_id; + console.log(semesterName); + + newServiceData["semestres"][semesterName] = data; + mutateProperty("serviceData", newServiceData); // passer au prochain semestre - // #TODO + const newCurrentSemestre = currentSemestre + 1; + setCurrentSemestre(newCurrentSemestre); + + if (newCurrentSemestre < semestresToRetrieve.length) { + await retreiveNextSemestre(newCurrentSemestre, semestresToRetrieve); + } + else { + if(firstLogin) { + queueMicrotask(() => { + // Reset the navigation stack to the "Home" screen. + // Prevents the user from going back to the login screen. + navigation.goBack(); + navigation.reset({ + index: 0, + routes: [{ name: "AccountCreated" }], + }); + }); + } + else { + navigation.goBack(); + } + } }; const actionFirstLogin = async (data: any) => { @@ -156,15 +203,7 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio createStoredAccount(local_account); switchTo(local_account); - queueMicrotask(() => { - // Reset the navigation stack to the "Home" screen. - // Prevents the user from going back to the login screen. - navigation.goBack(); - navigation.reset({ - index: 0, - routes: [{ name: "AccountCreated" }], - }); - }); + retreiveGrades(data); }; const wbref = React.useRef(null); @@ -208,7 +247,7 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio return ( <> - {/* = ({ route, navigatio Cela peut prendre quelques secondes... - */} + Date: Tue, 28 Jan 2025 20:25:14 +0100 Subject: [PATCH 0419/1144] chore: Bump version to 7.9.0 and add react-native-share dependency --- package-lock.json | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index ef8f26b28..a18f60629 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.7.2", + "version": "7.9.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.7.2", + "version": "7.9.0", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.3", "@candlefinance/app-icon": "^0.4.5", @@ -88,6 +88,7 @@ "react-native-reanimated": "~3.10.1", "react-native-safe-area-context": "4.10.5", "react-native-screens": "3.31.1", + "react-native-share": "^12.0.3", "react-native-svg": "^15.2.0", "react-native-url-polyfill": "^2.0.0", "react-native-view-shot": "3.8.0", @@ -14850,6 +14851,14 @@ "react-native": "*" } }, + "node_modules/react-native-share": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/react-native-share/-/react-native-share-12.0.3.tgz", + "integrity": "sha512-Bg96AjouSbcpdlI/AzWXMBwpjyWcm4NvGr49mSF1cz8aw8E1sMXwpsHa6I841SJML8Im6sRN3aXnK+/QManrtQ==", + "engines": { + "node": ">=16" + } + }, "node_modules/react-native-svg": { "version": "15.8.0", "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.8.0.tgz", From 106a8acd82d159ea3794bb1ec885abde40f9d8e8 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 28 Jan 2025 20:28:05 +0100 Subject: [PATCH 0420/1144] =?UTF-8?q?chore:=20Met=20=C3=A0=20jour=20la=20v?= =?UTF-8?q?ersion=20=C3=A0=207.9.0=20dans=20Info.plist=20et=20build.gradle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/build.gradle | 4 +- ios/Papillon.xcodeproj/project.pbxproj | 108 +++++++------- ios/Papillon/Info.plist | 2 +- ios/Podfile.lock | 195 ++++++++++++++----------- 4 files changed, 167 insertions(+), 142 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 8689ba11c..059efc776 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -88,8 +88,8 @@ android { applicationId 'xyz.getpapillon.app' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 7800 - versionName "7.8.0" + versionCode 7900 + versionName "7.9.0" } signingConfigs { debug { diff --git a/ios/Papillon.xcodeproj/project.pbxproj b/ios/Papillon.xcodeproj/project.pbxproj index 2377b367d..01967ab06 100644 --- a/ios/Papillon.xcodeproj/project.pbxproj +++ b/ios/Papillon.xcodeproj/project.pbxproj @@ -10,8 +10,8 @@ 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 1DCAF8F7437CAA80FED004D0 /* libPods-Papillon.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F4D5C3B3ADAE08E4CE97F28A /* libPods-Papillon.a */; }; 2D6C6538E5F346C0964ED2DA /* FixelText-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2CA436545607494ABF55D4A0 /* FixelText-Light.ttf */; }; - 3884CE273E4A98CB47EAA284 /* libPods-Papillon.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FA9574CF2D4D94FAA137392F /* libPods-Papillon.a */; }; 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; }; 472EB3522C45E0ED00503877 /* Stickers.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 472EB3512C45E0ED00503877 /* Stickers.xcassets */; }; 63920DB919D54244A4D0D651 /* FixelText-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 222162480574474F81E8B151 /* FixelText-Regular.ttf */; }; @@ -34,7 +34,7 @@ 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Papillon/main.m; sourceTree = ""; }; 222162480574474F81E8B151 /* FixelText-Regular.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "FixelText-Regular.ttf"; path = "../assets/fonts/FixelText-Regular.ttf"; sourceTree = ""; }; 2CA436545607494ABF55D4A0 /* FixelText-Light.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "FixelText-Light.ttf"; path = "../assets/fonts/FixelText-Light.ttf"; sourceTree = ""; }; - 34052B33E0B6329F11EBC220 /* Pods-Papillon.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Papillon.release.xcconfig"; path = "Target Support Files/Pods-Papillon/Pods-Papillon.release.xcconfig"; sourceTree = ""; }; + 3A8E4D54C16E8EFC3D40700F /* Pods-Papillon.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Papillon.debug.xcconfig"; path = "Target Support Files/Pods-Papillon/Pods-Papillon.debug.xcconfig"; sourceTree = ""; }; 472EB33B2C45E06600503877 /* Messages.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Messages.framework; path = System/Library/Frameworks/Messages.framework; sourceTree = SDKROOT; }; 472EB34F2C45E0EC00503877 /* Autocollants Papillon.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Autocollants Papillon.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; 472EB3512C45E0ED00503877 /* Stickers.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Stickers.xcassets; sourceTree = ""; }; @@ -42,13 +42,13 @@ 523BD2EA68B24DC59000E016 /* FixelText-Medium.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "FixelText-Medium.ttf"; path = "../assets/fonts/FixelText-Medium.ttf"; sourceTree = ""; }; 52806640827D4D76AC6BD305 /* FixelText-Bold.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "FixelText-Bold.ttf"; path = "../assets/fonts/FixelText-Bold.ttf"; sourceTree = ""; }; 54DA555BF32B482E8C5E646E /* FixelText-SemiBold.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "FixelText-SemiBold.ttf"; path = "../assets/fonts/FixelText-SemiBold.ttf"; sourceTree = ""; }; - 8609E53447C1AEB25F30038A /* Pods-Papillon.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Papillon.debug.xcconfig"; path = "Target Support Files/Pods-Papillon/Pods-Papillon.debug.xcconfig"; sourceTree = ""; }; + 5980E394169B8298F2271582 /* Pods-Papillon.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Papillon.release.xcconfig"; path = "Target Support Files/Pods-Papillon/Pods-Papillon.release.xcconfig"; sourceTree = ""; }; AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = Papillon/SplashScreen.storyboard; sourceTree = ""; }; BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = ""; }; BBF321108A9C475FB9616C65 /* Papillon-Bridging-Header.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = "Papillon-Bridging-Header.h"; path = "Papillon/Papillon-Bridging-Header.h"; sourceTree = ""; }; E3597223C6B74832B9814997 /* noop-file.swift */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.swift; name = "noop-file.swift"; path = "Papillon/noop-file.swift"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; - FA9574CF2D4D94FAA137392F /* libPods-Papillon.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Papillon.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + F4D5C3B3ADAE08E4CE97F28A /* libPods-Papillon.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Papillon.a"; sourceTree = BUILT_PRODUCTS_DIR; }; FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-Papillon/ExpoModulesProvider.swift"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -57,7 +57,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 3884CE273E4A98CB47EAA284 /* libPods-Papillon.a in Frameworks */, + 1DCAF8F7437CAA80FED004D0 /* libPods-Papillon.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -85,8 +85,8 @@ isa = PBXGroup; children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - FA9574CF2D4D94FAA137392F /* libPods-Papillon.a */, 472EB33B2C45E06600503877 /* Messages.framework */, + F4D5C3B3ADAE08E4CE97F28A /* libPods-Papillon.a */, ); name = Frameworks; sourceTree = ""; @@ -165,8 +165,8 @@ D65327D7A22EEC0BE12398D9 /* Pods */ = { isa = PBXGroup; children = ( - 8609E53447C1AEB25F30038A /* Pods-Papillon.debug.xcconfig */, - 34052B33E0B6329F11EBC220 /* Pods-Papillon.release.xcconfig */, + 3A8E4D54C16E8EFC3D40700F /* Pods-Papillon.debug.xcconfig */, + 5980E394169B8298F2271582 /* Pods-Papillon.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -186,14 +186,14 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Papillon" */; buildPhases = ( - 01A8F125CC3F9A1D7DF41248 /* [CP] Check Pods Manifest.lock */, + 01FFFC051C3C080886C1579E /* [CP] Check Pods Manifest.lock */, 1F5E2074DBF04DEF0D455912 /* [Expo] Configure project */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - B2EC4DD867A80C27774450D8 /* [CP] Embed Pods Frameworks */, - 1F2697A1CD991C612DEAF825 /* [CP] Copy Pods Resources */, + 76C612767CA1E0C77C9DBC0D /* [CP] Embed Pods Frameworks */, + CF8CCF044100B2E613895955 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -299,7 +299,7 @@ shellPath = /bin/sh; shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" \"$PROJECT_ROOT\" ios absolute | tail -n 1)\"\nfi\n\nif [[ -z \"$CLI_PATH\" ]]; then\n # Use Expo CLI\n export CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })\")\"\nfi\nif [[ -z \"$BUNDLE_COMMAND\" ]]; then\n # Default Expo CLI command for bundling\n export BUNDLE_COMMAND=\"export:embed\"\nfi\n\n# Source .xcode.env.updates if it exists to allow\n# SKIP_BUNDLING to be unset if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.updates\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.updates\"\nfi\n# Source local changes to allow overrides\n# if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n`\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n\n"; }; - 01A8F125CC3F9A1D7DF41248 /* [CP] Check Pods Manifest.lock */ = { + 01FFFC051C3C080886C1579E /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -321,7 +321,44 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 1F2697A1CD991C612DEAF825 /* [CP] Copy Pods Resources */ = { + 1F5E2074DBF04DEF0D455912 /* [Expo] Configure project */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "[Expo] Configure project"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-Papillon/expo-configure-project.sh\"\n"; + }; + 76C612767CA1E0C77C9DBC0D /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Papillon/Pods-Papillon-frameworks.sh", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Papillon/Pods-Papillon-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + CF8CCF044100B2E613895955 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -363,43 +400,6 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Papillon/Pods-Papillon-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 1F5E2074DBF04DEF0D455912 /* [Expo] Configure project */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "[Expo] Configure project"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-Papillon/expo-configure-project.sh\"\n"; - }; - B2EC4DD867A80C27774450D8 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Papillon/Pods-Papillon-frameworks.sh", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Papillon/Pods-Papillon-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -426,7 +426,7 @@ /* Begin XCBuildConfiguration section */ 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 8609E53447C1AEB25F30038A /* Pods-Papillon.debug.xcconfig */; + baseConfigurationReference = 3A8E4D54C16E8EFC3D40700F /* Pods-Papillon.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; @@ -453,7 +453,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = "Papillon"; + PRODUCT_NAME = Papillon; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -464,7 +464,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 34052B33E0B6329F11EBC220 /* Pods-Papillon.release.xcconfig */; + baseConfigurationReference = 5980E394169B8298F2271582 /* Pods-Papillon.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; @@ -486,7 +486,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = "Papillon"; + PRODUCT_NAME = Papillon; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index bc9188054..853e2457f 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 7.8.0 + 7.9.0 CFBundleSignature ???? CFBundleURLTypes diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 06f856173..7f747c2a7 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1542,6 +1542,27 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga + - RNShare (12.0.3): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Codegen + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga - RNSVG (15.8.0): - React-Core - SocketRocket (0.7.0) @@ -1658,6 +1679,7 @@ DEPENDENCIES: - "RNNotifee (from `../node_modules/@notifee/react-native`)" - RNReanimated (from `../node_modules/react-native-reanimated`) - RNScreens (from `../node_modules/react-native-screens`) + - RNShare (from `../node_modules/react-native-share`) - RNSVG (from `../node_modules/react-native-svg`) - UMAppLoader (from `../node_modules/unimodules-app-loader/ios`) - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) @@ -1874,6 +1896,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-reanimated" RNScreens: :path: "../node_modules/react-native-screens" + RNShare: + :path: "../node_modules/react-native-share" RNSVG: :path: "../node_modules/react-native-svg" UMAppLoader: @@ -1883,109 +1907,110 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: d3f49c53809116a5d38da093a8aa78bf551aed09 - candlefinance-app-icon: 91095f99969a105b86ff97aab7dd60e2bfd4152c + candlefinance-app-icon: 9d7562e14a7a087c4ce87c262f59300a4f18106a DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5 - EXApplication: ec862905fdab3a15bf6bd8ca1a99df7fc02d7762 - EXAV: 64e72329d2f8c2ba13608fed4a713af4e793242d - EXBarCodeScanner: 6415603150dd5989a139570bb5af19b7f169fe49 - EXConstants: 89d35611505a8ce02550e64e43cd05565da35f9a - EXImageLoader: 1fe96c70cdc78bedc985ec4b1fab5dd8e67dc38b + EXApplication: c08200c34daca7af7fd76ac4b9d606077410e8ad + EXAV: afa491e598334bbbb92a92a2f4dd33d7149ad37f + EXBarCodeScanner: e2dd9b42c1b522a2adc9202b1dfbc64cb34456d1 + EXConstants: 409690fbfd5afea964e5e9d6c4eb2c2b59222c59 + EXImageLoader: ab589d67d6c5f2c33572afea9917304418566334 EXJSONUtils: 30c17fd9cc364d722c0946a550dfbf1be92ef6a4 - EXLocation: c52e800875ce2a5974b3b796ff2f3a8b978e0b28 - EXManifests: ebb7f551c340c0d06f3ecd9ae662e418bf68417c - Expo: ed0a748eb6be0efd2c3df7f6de3f3158a14464c9 - expo-dev-menu: 8b0ea59392e2dd975390ea6f0472ce350d94866a - expo-dev-menu-interface: 5c6b79875bf0ab1251ea9962f60968fe39ed2637 - ExpoAsset: 286fee7ba711ce66bf20b315e68106b13b8629fc - ExpoBackgroundFetch: 2988b27bc95f322e856dbd44060b6951fdfe8950 - ExpoBlur: d6023b4ccd20035624b60ae0bda541e65b9ece5c - ExpoBrightness: 174c89604618f907ac2a22dfaa2e71d49db283fb - ExpoCamera: cf49d2d121a9f883be0f98dde15a2185a1dd42be - ExpoClipboard: 243e22ff4161bbffcd3d2db469ae860ddc1156be - ExpoCrypto: c5c052d5f9f668c21975cb4caf072cec23c823fa - ExpoDevice: 84b3ed79df1234c17edfbf335f6ecf3c636f74de - ExpoFileSystem: 2988caaf68b7cb706e36d382829d99811d9d76a5 - ExpoFont: 38dddf823e32740c2a9f37c926a33aeca736b5c4 - ExpoHaptics: 9f47be324f691b6291c17c216189ab832d1a4d69 - ExpoImagePicker: 517a47896adf5d55d0a1c159e5d1e312af12e57c - ExpoKeepAwake: dd02e65d49f1cfd9194640028ae2857e536eb1c9 - ExpoLinearGradient: 4c44b3803b441724874b232e6520b51ca6a50db1 - ExpoMediaLibrary: b30366bf8f938166b1fe2d019e5b15337349d91e - ExpoModulesCore: 9ac73e2f60e0ea1d30137ca96cfc8c2aa34ef2b2 - ExpoSensors: 805b2d91b629a08da78dcc86032a0833cb0e98c0 - ExpoSharing: 5e6b6cbc0c232084b79ffa7243459f7dcdc5b1cb - ExpoStoreReview: 5ce23b11d7cdcba23fa26b8cd9dd83765e2ac7bf - ExpoSystemUI: 2072307375696c398a5d75633bdd5143fadc3d26 - ExpoWebBrowser: cf10afe886891ab495877dada977fe6c269614a4 - EXSplashScreen: 4d8b63645949a45202b853e9daec9f2291f539e6 - EXTaskManager: b515b853fd97286a25cb643978ff7fa456f3139a + EXLocation: 43e9b582ca63a23c6f0a18d8cbe2145b3a388b55 + EXManifests: c1fab4c3237675e7b0299ea8df0bcb14baca4f42 + Expo: 8c995afb875c15bf8439af0b20bcb9ed8f90d0bd + expo-dev-menu: 12b319a9bc73d76a1ed47ce52055b5356edb971d + expo-dev-menu-interface: 5764ad537419c1a5e8f66f668e29c81e8aca290c + ExpoAsset: 323700f291684f110fb55f0d4022a3362ea9f875 + ExpoBackgroundFetch: a06c553ecaf0bade0acd691042d996b9ce926327 + ExpoBlur: fa53f874e7b208bc3756d1bf07903c12e790beb1 + ExpoBrightness: c184f0ef116a51d7f5c1dd4c253d4d9806a5e022 + ExpoCamera: 929be541d1c1319fcf32f9f5d9df8b97804346b5 + ExpoClipboard: 23d203f5d4843699fbc45be1cc4fe1fbd811a6fa + ExpoCrypto: 156078f266bf28f80ecf5e2a9c3a0d6ffce07a1c + ExpoDevice: fc94f0e42ecdfd897e7590f2874fc64dfa7e9b1c + ExpoFileSystem: 80bfe850b1f9922c16905822ecbf97acd711dc51 + ExpoFont: 00756e6c796d8f7ee8d211e29c8b619e75cbf238 + ExpoHaptics: 5a3a88971af384255baf2504f38b41189cec6984 + ExpoImagePicker: 12a420923383ae38dccb069847218f27a3b87816 + ExpoKeepAwake: 3b8815d9dd1d419ee474df004021c69fdd316d08 + ExpoLinearGradient: 8cec4a09426d8934c433e83cb36262d72c667fce + ExpoMediaLibrary: 81573bcbd50cbd0a3ef57216c93593157d32b558 + ExpoModulesCore: 831ece8311a489418746925820bbffdda587d6f4 + ExpoSensors: 3bc12e5186b94703464d1554ce5ebfff05f91093 + ExpoSharing: 8db05dd85081219f75989a3db2c92fe5e9741033 + ExpoStoreReview: 15f9a636b62ff00bb21cbe9a9fe22f0239da4481 + ExpoSystemUI: d4f065a016cae6721b324eb659cdee4d4cf0cb26 + ExpoWebBrowser: 7595ccac6938eb65b076385fd23d035db9ecdc8e + EXSplashScreen: 17a656c08a0095be15b620c52e61dfdb665863d2 + EXTaskManager: 9c3520305c3aa1b4a12a7c6d1e3f85f2779c06e9 FBLazyVector: 4b1589d37c9ff4dba11a63083fe7515fad3ac111 fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120 glog: fdfdfe5479092de0c4bdbebedd9056951f092c4f hermes-engine: 2102c92e54a031a270fd1fe84169ec8a0901b7bd lottie-ios: e047b1d2e6239b787cc5e9755b988869cf190494 - lottie-react-native: 45707364bd70cffa7602fa1a1abb40dee5f3c0e0 - RCT-Folly: 5dc73daec3476616d19e8a53f0156176f7b55461 + lottie-react-native: 4279da8b681e89c29a2adb9f99985d6cf372d49d + RCT-Folly: 02617c592a293bd6d418e0a88ff4ee1f88329b47 RCTDeprecation: 5f1d7e1f8ef6c53f0207e3ac0d0ca23575e8a6ab RCTRequired: dcfd24ece09940bbf24b7c2974f4eb68a9baee55 RCTTypeSafety: 3d65944055cc73f3bb28c3f05c7eaff2bb7ceb83 React: c5e9f3c07a890a7e2a1ec9b79faa5c53dd7aee01 React-callinvoker: 9ac986dbbd0e1b3463cb740b12c0b37dbcd15fed - React-Codegen: d7f2c1914e3284a6c6a44fe576a1b6e86bb1d524 - React-Core: d5c879b114e7bb7cc9ecc77a09379001bde9be0c - React-CoreModules: c537c3f970d7b14af54783d7f66a9a878a3bcbf9 - React-cxxreact: 10e3a4e690dd6460bc3e412491abc658fee70bb0 + React-Codegen: f5bd8446ba2b7d4a7bb4b25e137f44f286eb98f7 + React-Core: e8de3613460de4f02cdf6a0d726526fe273766b4 + React-CoreModules: 704fd52f83780e1870a946d5c3ea6ea1175aa523 + React-cxxreact: 75572783e7feae5a9f67157ea13968bf5ab274a3 React-debug: 180e1bf4a97fa4404ee7fb68952cace122aa9d73 - React-Fabric: 99898fcb2c7db8e290e4b3c6bfc172aa3fbb9896 - React-FabricImage: cd8fb0bccc6c7dd39ac6dbf6b5d798648bd342cf + React-Fabric: 1118712bb2dfae21b7d03d5daf419018e867f8e9 + React-FabricImage: 3878f51fa0ac860fb733f0a3c958442a7b5ec587 React-featureflags: 05fabc5e165fa3864c879556e83a455b8a0573fa - React-graphics: a3392f38f12f3bf5f97fdced159d168f77e31c8a - React-hermes: fd5ac04a0481c2fcc351e69a999615c0338dc45d - React-ImageManager: 1e56d48d4d9db1be58f43c8915784ad46deb8cca - React-jserrorhandler: 31b842ab3ecac9f7483469f1bbe29d576ebf76c5 - React-jsi: 59781f13936f4b5930e9fdbbacf28923f066bab4 - React-jsiexecutor: d22d8f5b421656a1627fd0eb2c876c77e8b8991c - React-jsinspector: d09c1db99fb83ba4171a6b99a6a5efcd0f271c0d - React-jsitracing: 257f7a96aed4216f0f611f1f7a34fd288227c924 - React-logger: 76e7467052f51022eca5bcfe0c700004ad88954d - React-Mapbuffer: 023e56d8228ebc662ba021b0c662e5d17ee6922b - react-native-cookies: d648ab7025833b977c0b19e142503034f5f29411 - react-native-netinfo: 2e3c27627db7d49ba412bfab25834e679db41e21 - react-native-pager-view: 0f50eef500ef15dfae1f95a1c945f3d2a5ec5ade - react-native-safe-area-context: df9763c5de6fa38883028e243a0b60123acb8858 - react-native-view-shot: d1a701eb0719c6dccbd20b4bb43b1069f304cb70 - react-native-webview: a4483a25c71098e407df1c1d9056ab907647d7c7 + React-graphics: 77bb68d9d913682a23c621df01ab61b2e58a5c70 + React-hermes: e4c48dab6830b282e1b41023cdae9b5c1f7ae075 + React-ImageManager: 6dbe9e5578c03d48b25e646ee65faa63b10e9544 + React-jserrorhandler: 16f7ef986fd20a2d342e5430c9dd0502a4136320 + React-jsi: b34c85593159261ce19f9c5fd0a627b3dc1483c6 + React-jsiexecutor: dcf7df38a296d104b196c193a0111d3f8a46da0b + React-jsinspector: 7dfaff7a0f57d23eeab023fe6a243f7707a14f6d + React-jsitracing: df84cc252a1f4bb0970f7fe13c470451b18c2cbb + React-logger: de9b65c8c7b71a663e6e99d347b1c445f5190c39 + React-Mapbuffer: 766bb4d8f655d816913325b353d800debbde7209 + react-native-cookies: f54fcded06bb0cda05c11d86788020b43528a26c + react-native-netinfo: bdb108d340cdb41875c9ced535977cac6d2ff321 + react-native-pager-view: c1e29e1a6105a02807392ba822ad322447a72f55 + react-native-safe-area-context: a240ad4b683349e48b1d51fed1611138d1bdad97 + react-native-view-shot: 6b7ed61d77d88580fed10954d45fad0eb2d47688 + react-native-webview: 05bae3a03a1e4f59568dfc05286c0ebf8954106c React-nativeconfig: c36a079fa219a9911070cc0058b746407e1ef47d - React-NativeModulesApple: 057d7428de4fc072da9cfa390549f4d98d3fb827 + React-NativeModulesApple: eab84dd7bda0650f3ce41c53f76ffd49d689763f React-perflogger: 9f21c9e3d8d220833e649a141fed8e5ca08977d9 React-RCTActionSheet: 4c1f0dc56952f21a904e9f3bf74253eebee1d1d9 - React-RCTAnimation: b49334467a2c2c567af1e6f18417f608802c4e20 - React-RCTAppDelegate: 72c9263f5bc6d2c2538b2df9305bd5c662a7e6e4 - React-RCTBlob: fda186e70632bcd2eb7db51a063e8dc827823d3e - React-RCTFabric: 0d63e24554e0bb4ff385c637466f0b1770ce09f6 - React-RCTImage: cf8086f64eabadfd7298a833290ea5dc7f8991cc - React-RCTLinking: 9e87f27bc7110cdd528ce7c92dd0960227e654d1 - React-RCTNetwork: baf8c9a3c9879945069e7c06cb07ea33953039c8 - React-RCTSettings: 5aa1bc9c600819134a24ed7ed905678655bdc5bb - React-RCTText: 14fbdef8c1a58b5f352c36cda2539fec7bd3be00 - React-RCTVibration: 79d960e1d2539195ff87906b09ec7560440901ff - React-rendererdebug: 5431c0940e87d171e2e31d7176f37e325c896be7 + React-RCTAnimation: 2c0b963d4fd978ce35daa330986a8bc442c7517a + React-RCTAppDelegate: bfb8293aa467aae8a28050e4095b0ceff284cbd5 + React-RCTBlob: a440574d805536c58c0c409cb5058334c8d2886c + React-RCTFabric: 6ea72ddf222ea1e373d0cbac88a1c62355701995 + React-RCTImage: e63bc8abbad2c5a4eda53ff35282d83bc9df7559 + React-RCTLinking: 12c6962253fd2f2494231eb8ae2fecae71e54e2f + React-RCTNetwork: 46df47440bd2bf63b0ca0a3c640471243ed2922a + React-RCTSettings: 5e1dfa02ae2d6cf54b3fdfebaa80837540c50847 + React-RCTText: 1c045a74e4fda674523c932f53bdd15b2a3ba085 + React-RCTVibration: 2ba9de92ae71526b3e02b8b8b2fce5cbf47c393f + React-rendererdebug: acb324f4975412bb14d55b29dd5ca6961b5fa06a React-rncore: 63db76511a92db6cf9649c9d6567e014b7eeb6f5 - React-RuntimeApple: 746c0bb58c7b2475cdeb6fc06f61f5cb6a42d19d - React-RuntimeCore: 7784c06b618e1e5ffe12f63f47109163472b1d9e + React-RuntimeApple: 6ecb0a470d1ef989895a4e5d31980004378ebf71 + React-RuntimeCore: 2794fdb42f7d37f3c877f614e12a0a240b594815 React-runtimeexecutor: bf091a7f5f5130daab6d8216aaa290374b214cb8 - React-RuntimeHermes: 3a6bfd73b07bebb4f99fc583fb3f8c53c7198c2e - React-runtimescheduler: 58f254c234fcd21826759cc85b0c9b9d3b1679f7 - React-utils: 61a4c1ae313725ae97f72aeca2b7f73c316ee5e7 - ReactCommon: 2d6cc4daa19b1ec85b9ae72bfa7958cdd50a60ea - RNCAsyncStorage: aa75595c1aefa18f868452091fa0c411a516ce11 - RNCMaskedView: de80352547bd4f0d607bf6bab363d826822bd126 - RNDateTimePicker: dde7ca9005d716f3efa9a63004b441679bca9a41 - RNGestureHandler: 8d857f8c5e6697585f6a246d1acbf0533c438ab1 - RNNotifee: 271cfeb505183d2cd1b858c14c3968b6ca30a642 - RNReanimated: def444e044c354f38bb0a5926a8583ba19d944c1 - RNScreens: a2d8a2555b4653d7a19706eb172f855657ac30d7 - RNSVG: 8542aa11770b27563714bbd8494a8436385fc85f + React-RuntimeHermes: 73249fcc108708a137119de18c3d40ac5ab90160 + React-runtimescheduler: b63ebebd3e000e0ba4ac19ca69bdac071559ad57 + React-utils: 2955bdc1b2ed495f14dc7d3bfbbb7e3624cfc0fc + ReactCommon: 5c504a77030c7ab89eee75b1725b80d8cee7f5d7 + RNCAsyncStorage: 826b603ae9c0f88b5ac4e956801f755109fa4d5c + RNCMaskedView: 090213d32d8b3bb83a4dcb7d12c18f0152591906 + RNDateTimePicker: b6a9b35a785ecbe12b4e7d6de5439d0aa4614146 + RNGestureHandler: 6fee3422fd8c81c5ee756fa72e3d1780e9943d9d + RNNotifee: 935f3ea8c134c88cbf8b13ea0c97c72c09ad2116 + RNReanimated: 35f9ac9c3ac42d0497ebd1cce5c39d7687a8493e + RNScreens: b32a9ff15bea7fcdbe5dff6477bc503f792b1208 + RNShare: 22717e910836a66cb7255b3b8c4ab06cbe346e27 + RNSVG: 8b1a777d54096b8c2a0fd38fc9d5a454332bbb4d SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d UMAppLoader: f17a5ee8e85b536ace0fc254b447a37ed198d57e Yoga: 4f4f07a17818e76d1b04edc01b68b6d49a682100 @@ -1993,4 +2018,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 7f0387b1ee3dbf846a6b743dd3d86d401c33d8c2 -COCOAPODS: 1.16.2 +COCOAPODS: 1.15.2 From 09a6ff121b3f2ab1ed5ea381c23269de7d2745ed Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 28 Jan 2025 20:36:39 +0100 Subject: [PATCH 0421/1144] =?UTF-8?q?feat:=20Ajout=20d'animations=20=C3=A0?= =?UTF-8?q?=20la=20vue=20GradesScodocUE=20et=20am=C3=A9lioration=20de=20la?= =?UTF-8?q?=20condition=20d'affichage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../account/Grades/Atoms/GradesScodocUE.tsx | 16 ++++++++++------ src/views/account/Grades/Grades.tsx | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/views/account/Grades/Atoms/GradesScodocUE.tsx b/src/views/account/Grades/Atoms/GradesScodocUE.tsx index 14b5bb852..58033bcd1 100644 --- a/src/views/account/Grades/Atoms/GradesScodocUE.tsx +++ b/src/views/account/Grades/Atoms/GradesScodocUE.tsx @@ -2,6 +2,7 @@ import AnimatedNumber from "@/components/Global/AnimatedNumber"; import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; import { useAlert } from "@/providers/AlertProvider"; import { PrimaryAccount } from "@/stores/account/types"; +import { animPapillon } from "@/utils/ui/animations"; import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; import { useTheme } from "@react-navigation/native"; @@ -10,7 +11,7 @@ import { useState } from "react"; import { Image, View } from "react-native"; import { TouchableOpacity } from "react-native-gesture-handler"; -import Reanimated, { FadeIn, FadeOut } from "react-native-reanimated"; +import Reanimated, { FadeIn, FadeOut, LinearTransition } from "react-native-reanimated"; const GradesScodocUE = ({ account, navigation }: { account: PrimaryAccount, navigation: any }) => { try { @@ -38,7 +39,11 @@ const GradesScodocUE = ({ account, navigation }: { account: PrimaryAccount, navi }); return ( - <> + + ))} - + ); })} - + ); } catch (e) { diff --git a/src/views/account/Grades/Grades.tsx b/src/views/account/Grades/Grades.tsx index 75a3baadf..f7bdb3b32 100644 --- a/src/views/account/Grades/Grades.tsx +++ b/src/views/account/Grades/Grades.tsx @@ -237,7 +237,7 @@ const Grades: Screen<"Grades"> = ({ route, navigation }) => { /> )} - {"providers" in account && account.providers && account.providers.includes("scodoc") && ( + {gradesPerSubject.length > 0 && "providers" in account && account.providers && account.providers.includes("scodoc") && ( )} From 8c96b42553389ec831fe387a727a098349d4b7bf Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 28 Jan 2025 23:59:28 +0100 Subject: [PATCH 0422/1144] =?UTF-8?q?refactor:=20Remplace=20animPapillon?= =?UTF-8?q?=20par=20anim2Papillon=20et=20am=C3=A9liore=20les=20animations?= =?UTF-8?q?=20dans=20les=20composants=20Grades?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Global/AnimatedNumber.tsx | 41 +++--- .../account/Grades/Atoms/GradesScodocUE.tsx | 25 ++-- src/views/account/Grades/Grades.tsx | 137 +++++++++--------- .../account/Grades/Latest/LatestGrades.tsx | 4 +- .../Grades/Latest/LatestGradesItem.tsx | 4 + src/views/account/Grades/Subject/Subject.tsx | 8 +- .../account/Grades/Subject/SubjectList.tsx | 16 +- .../account/Grades/Subject/SubjectTitle.tsx | 7 +- 8 files changed, 119 insertions(+), 123 deletions(-) diff --git a/src/components/Global/AnimatedNumber.tsx b/src/components/Global/AnimatedNumber.tsx index 5780058ea..84bcc14a3 100644 --- a/src/components/Global/AnimatedNumber.tsx +++ b/src/components/Global/AnimatedNumber.tsx @@ -1,42 +1,35 @@ import type { StyleProp, TextStyle, ViewStyle } from "react-native"; import { NativeText } from "@/components/Global/NativeComponents"; import { animPapillon } from "@/utils/ui/animations"; - import Reanimated, { AnimatedStyle, FadeInDown, FadeOutUp, LinearTransition } from "react-native-reanimated"; +import { useRef, useEffect } from "react"; interface AnimatedNumberProps { - /** - * Nombre en tant que string pour permettre - * d'animer chaque chiffre et d'avoir un - * flottant fixé, par exemple. - */ value: string | any; - - /** - * Style du texte du nombre. - */ style?: StyleProp - - /** - * Style du conteneur du texte qui contient chaque chiffre. - */ contentContainerStyle?: StyleProp>> } -/** - * Composant qui permet d'animer un nombre - * lors de son apparition et modification. - */ const AnimatedNumber: React.FC = ({ value, style, contentContainerStyle }) => { + const isFirstRender = useRef(true); + + useEffect(() => { + if (isFirstRender.current) { + isFirstRender.current = false; + } + }, []); + + const shouldAnimate = !isFirstRender.current; + return ( = ({ paddingVertical: 2, marginVertical: -2, }, contentContainerStyle]} - layout={animPapillon(LinearTransition)} + layout={shouldAnimate ? animPapillon(LinearTransition) : undefined} > - {value.toString().split("").map((n:number, i:number) => ( + {value.toString().split("").map((n: number, i: number) => ( {n} diff --git a/src/views/account/Grades/Atoms/GradesScodocUE.tsx b/src/views/account/Grades/Atoms/GradesScodocUE.tsx index 58033bcd1..8bfab4aad 100644 --- a/src/views/account/Grades/Atoms/GradesScodocUE.tsx +++ b/src/views/account/Grades/Atoms/GradesScodocUE.tsx @@ -2,24 +2,26 @@ import AnimatedNumber from "@/components/Global/AnimatedNumber"; import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; import { useAlert } from "@/providers/AlertProvider"; import { PrimaryAccount } from "@/stores/account/types"; -import { animPapillon } from "@/utils/ui/animations"; +import { anim2Papillon } from "@/utils/ui/animations"; import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; import { useTheme } from "@react-navigation/native"; import { ChevronDown, ChevronUp, Info } from "lucide-react-native"; -import { useState } from "react"; +import { memo, useState } from "react"; import { Image, View } from "react-native"; import { TouchableOpacity } from "react-native-gesture-handler"; -import Reanimated, { FadeIn, FadeOut, LinearTransition } from "react-native-reanimated"; +import Reanimated, { FadeIn, FadeInDown, FadeOut, FadeOutUp, LinearTransition } from "react-native-reanimated"; -const GradesScodocUE = ({ account, navigation }: { account: PrimaryAccount, navigation: any }) => { +const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: PrimaryAccount, navigation: any, selectedPeriod: string }) => { try { const { colors } = useTheme(); const { showAlert } = useAlert(); + const grades = account.serviceData.semestres[selectedPeriod]; + // @ts-expect-error - const ues = account.identityProvider?.rawData["relevé"]["ues"]; + const ues = grades["relevé"]["ues"]; const uekeys = Object.keys(ues); if (uekeys.length === 0) { @@ -27,9 +29,9 @@ const GradesScodocUE = ({ account, navigation }: { account: PrimaryAccount, navi } // @ts-expect-error - const ressources = account.identityProvider?.rawData["relevé"]["ressources"]; + const ressources = grades["relevé"]["ressources"]; // @ts-expect-error - const saes = account.identityProvider?.rawData["relevé"]["saes"]; + const saes = grades["relevé"]["saes"]; const finalUes = uekeys.map((ue) => { return { @@ -40,9 +42,9 @@ const GradesScodocUE = ({ account, navigation }: { account: PrimaryAccount, navi return ( = ({ route, navigation }) => { - {!isLoading && ( - setIsRefreshing(true)} - colors={Platform.OS === "android" ? [theme.colors.primary] : void 0} - progressViewOffset={outsideNav ? 72 : insets.top + 56} - /> - } - contentContainerStyle={{ - paddingTop: outsideNav ? 64 : insets.top + 42, - }} - scrollIndicatorInsets={{ top: outsideNav ? 64 : insets.top + 16 }} - > - }> - - {(!grades[selectedPeriod] || grades[selectedPeriod].length === 0) && + + setIsRefreshing(true)} + colors={Platform.OS === "android" ? [theme.colors.primary] : void 0} + progressViewOffset={outsideNav ? 72 : insets.top + 56} + /> + } + contentContainerStyle={{ + paddingTop: outsideNav ? 64 : insets.top + 42, + }} + scrollIndicatorInsets={{ top: outsideNav ? 64 : insets.top + 16 }} + > + }> + + {(!grades[selectedPeriod] || grades[selectedPeriod].length === 0) && !isLoading && !isRefreshing && ( - - )} + + )} - {!isLoading && - grades[selectedPeriod] && + {grades[selectedPeriod] && grades[selectedPeriod].length > 1 && ( - - - - )} - - {latestGradesRef.current.length > 2 && ( - + - )} + + )} - {gradesPerSubject.length > 0 && "providers" in account && account.providers && account.providers.includes("scodoc") && ( - - )} + {latestGradesRef.current.length > 2 && ( + + )} - {gradesPerSubject.length > 0 && ( - - )} - - - - )} + {gradesPerSubject.length > 0 && "providers" in account && account.providers && account.providers.includes("scodoc") && ( + + )} + + {gradesPerSubject.length > 0 && ( + + )} + + + ); }; diff --git a/src/views/account/Grades/Latest/LatestGrades.tsx b/src/views/account/Grades/Latest/LatestGrades.tsx index e75083bb9..bf2611828 100644 --- a/src/views/account/Grades/Latest/LatestGrades.tsx +++ b/src/views/account/Grades/Latest/LatestGrades.tsx @@ -1,7 +1,7 @@ import React from "react"; import { FlatList } from "react-native"; import { NativeListHeader } from "@/components/Global/NativeComponents"; -import { animPapillon } from "@/utils/ui/animations"; +import { anim2Papillon } from "@/utils/ui/animations"; import Reanimated, { LinearTransition } from "react-native-reanimated"; import GradesLatestItem from "./LatestGradesItem"; import { Grade } from "@/services/shared/Grade"; @@ -30,7 +30,7 @@ const GradesLatestList = (props: GradesLatestListProps) => { return ( diff --git a/src/views/account/Grades/Latest/LatestGradesItem.tsx b/src/views/account/Grades/Latest/LatestGradesItem.tsx index 6691be7ce..b253084e0 100644 --- a/src/views/account/Grades/Latest/LatestGradesItem.tsx +++ b/src/views/account/Grades/Latest/LatestGradesItem.tsx @@ -4,6 +4,8 @@ import React, { useEffect, useState } from "react"; import { View } from "react-native"; import { PressableScale } from "react-native-pressable-scale"; import type { Grade } from "@/services/shared/Grade"; +import { FadeInRight, FadeOutLeft } from "react-native-reanimated"; +import { anim2Papillon } from "@/utils/ui/animations"; type GradeLatestItemProps = { grade: Grade; @@ -43,6 +45,8 @@ const GradesLatestItem: React.FC = ({ style={{ width: 230, }} + entering={i < 3 && anim2Papillon(FadeInRight).duration(300).delay(i * 50)} + exiting={i < 3 && anim2Papillon(FadeOutLeft).duration(100).delay(i * 50)} > + navigation: NativeStackNavigationProp, + currentPeriod?: string } type SortingFunction = (a: GradesPerSubject, b: GradesPerSubject) => number; @@ -44,7 +45,8 @@ const sortings: PickerDataItem[] = [ const Subject: React.FC = ({ gradesPerSubject, navigation, - allGrades + allGrades, + currentPeriod }) => { const [sorting, setSorting] = useState(0); const [isLoading, setIsLoading] = useState(false); @@ -67,7 +69,7 @@ const Subject: React.FC = ({ const renderItem = useCallback(({ item, index }: { item: GradesPerSubject; index: number }) => ( = ({ return ( = ({ subject, grade, index, onPress }) => { return ( - + >{typeof subject.average.average?.value === "number" ? subject.average.average.value.toFixed(2) : calculatedAverage !== -1 ? calculatedAverage.toFixed(2) : "N/A"} Date: Wed, 29 Jan 2025 00:00:38 +0100 Subject: [PATCH 0423/1144] =?UTF-8?q?feat:=20Mise=20=C3=A0=20jour=20de=20l?= =?UTF-8?q?'animation=20dans=20NativeList=20avec=20anim2Papillon=20pour=20?= =?UTF-8?q?une=20transition=20am=C3=A9lior=C3=A9e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Grades/Atoms/GradesScodocUE.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Grades/Atoms/GradesScodocUE.tsx b/src/views/account/Grades/Atoms/GradesScodocUE.tsx index 8bfab4aad..897c546b0 100644 --- a/src/views/account/Grades/Atoms/GradesScodocUE.tsx +++ b/src/views/account/Grades/Atoms/GradesScodocUE.tsx @@ -72,7 +72,7 @@ const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: Prim } /> - + {finalUes.map((ue) => { interface ueGrade { key: string, From 5752e5b8c0a3825daae3ad36c24ea407eddf390d Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 29 Jan 2025 00:12:33 +0100 Subject: [PATCH 0424/1144] fix(GradesAverageGraph): filter out NaN values from grades history --- src/views/account/Grades/Graph/GradesAverage.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/views/account/Grades/Graph/GradesAverage.tsx b/src/views/account/Grades/Graph/GradesAverage.tsx index 7af1e585f..c8f2d70dc 100644 --- a/src/views/account/Grades/Graph/GradesAverage.tsx +++ b/src/views/account/Grades/Graph/GradesAverage.tsx @@ -102,6 +102,9 @@ const GradesAverageGraph: React.FC = ({ setMaxAvg(maxAvg); setMinAvg(minAvg); + hst = hst.filter((p) => isNaN(p.value) === false); + console.log(hst.map((p) => p.value)); + graphRef.current?.updateData({ xAxis: hst.map((p, i) => new Date(p.date).getTime()), yAxis: hst.map((p) => p.value), @@ -229,7 +232,7 @@ const GradesAverageGraph: React.FC = ({ xAxis={gradesHistory.map((p, i) => new Date(p.date).getTime() )} - yAxis={gradesHistory.map((p) => p.value)} + yAxis={gradesHistory.map((p) => !isNaN(p.value) ? p.value : (currentAvg ?? 10))} color={theme.colors.primary} showXAxisLegend={false} showYAxisLegend={false} From 6eb589c84d702b36d3cd09db9f17f1d1376e8851 Mon Sep 17 00:00:00 2001 From: Armand Camponovo Date: Wed, 29 Jan 2025 21:44:54 +0100 Subject: [PATCH 0425/1144] fix: imports --- src/services/evaluation.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/services/evaluation.ts b/src/services/evaluation.ts index c8949d99a..669a48907 100644 --- a/src/services/evaluation.ts +++ b/src/services/evaluation.ts @@ -1,8 +1,10 @@ import { type Account, AccountService } from "@/stores/account/types"; import type { Period } from "./shared/Period"; +import {getFeatureAccount} from "@/utils/multiservice"; +import {MultiServiceFeature} from "@/stores/multiService/types"; import { useEvaluationStore } from "@/stores/evaluation"; import { Evaluation } from "@/services/shared/Evaluation"; -import { error } from "@/utils/logger/logger"; +import { error, log } from "@/utils/logger/logger"; const getDefaultPeriod = (periods: Period[]): string => { const now = Date.now(); From 118d6acca2d1328d653071d240ce368eb119f5eb Mon Sep 17 00:00:00 2001 From: Armand Camponovo Date: Wed, 29 Jan 2025 21:45:25 +0100 Subject: [PATCH 0426/1144] fix: imports --- src/services/grades.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/grades.ts b/src/services/grades.ts index 80539564c..602bcb677 100644 --- a/src/services/grades.ts +++ b/src/services/grades.ts @@ -2,7 +2,7 @@ import { type Account, AccountService } from "@/stores/account/types"; import { useGradesStore } from "@/stores/grades"; import type { Period } from "./shared/Period"; import type { AverageOverview, Grade } from "./shared/Grade"; -import { error } from "@/utils/logger/logger"; +import { error, log } from "@/utils/logger/logger"; import { checkIfSkoSupported } from "./skolengo/default-personalization"; import {MultiServiceFeature} from "@/stores/multiService/types"; import {getFeatureAccount} from "@/utils/multiservice"; From a86ef9c468be2d594fe96637c5c167c935226b00 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Thu, 30 Jan 2025 13:13:08 +0100 Subject: [PATCH 0427/1144] fix: downgrade version to 7.8.2 and update dependencies --- android/app/build.gradle | 4 +- .../AppIcon.appiconset/Contents.json | 42 +++++++++++++++---- ios/Papillon/Info.plist | 2 +- ios/Podfile.lock | 4 +- package-lock.json | 22 +++++----- package.json | 4 +- 6 files changed, 51 insertions(+), 27 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 059efc776..1ef08a4ea 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -88,8 +88,8 @@ android { applicationId 'xyz.getpapillon.app' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 7900 - versionName "7.9.0" + versionCode 7820 + versionName "7.8.2" } signingConfigs { debug { diff --git a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json index 90d8d4c2a..e91cbd33d 100644 --- a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,14 +1,38 @@ { - "images": [ + "images" : [ { - "filename": "App-Icon-1024x1024@1x.png", - "idiom": "universal", - "platform": "ios", - "size": "1024x1024" + "filename" : "Icon-Light-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Dark-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "Icon-Tinted-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" } ], - "info": { - "version": 1, - "author": "expo" + "info" : { + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index 853e2457f..c30522e5f 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 7.9.0 + 7.8.2 CFBundleSignature ???? CFBundleURLTypes diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 7f747c2a7..030f884b8 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1473,7 +1473,7 @@ PODS: - React-Core - RNDateTimePicker (8.0.1): - React-Core - - RNGestureHandler (2.21.2): + - RNGestureHandler (2.16.2): - DoubleConversion - glog - hermes-engine @@ -2005,7 +2005,7 @@ SPEC CHECKSUMS: RNCAsyncStorage: 826b603ae9c0f88b5ac4e956801f755109fa4d5c RNCMaskedView: 090213d32d8b3bb83a4dcb7d12c18f0152591906 RNDateTimePicker: b6a9b35a785ecbe12b4e7d6de5439d0aa4614146 - RNGestureHandler: 6fee3422fd8c81c5ee756fa72e3d1780e9943d9d + RNGestureHandler: 2282cfbcf86c360d29f44ace393203afd5c6cff7 RNNotifee: 935f3ea8c134c88cbf8b13ea0c97c72c09ad2116 RNReanimated: 35f9ac9c3ac42d0497ebd1cce5c39d7687a8493e RNScreens: b32a9ff15bea7fcdbe5dff6477bc503f792b1208 diff --git a/package-lock.json b/package-lock.json index a18f60629..0999a01e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.9.0", + "version": "7.8.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.9.0", + "version": "7.8.2", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.3", "@candlefinance/app-icon": "^0.4.5", @@ -71,14 +71,14 @@ "openid-client": "^5.7.0", "pawdirecte": "^1.7.1", "pawnilim": "^0.2.0", - "pawnote": "^1.3.4", + "pawnote": "^1.4.1", "pawrd": "^0.6.1", "react": "18.2.0", "react-native": "^0.74.3", "react-native-barcode-svg": "^0.0.15", "react-native-draggable-flatlist": "^4.0.1", "react-native-draglist": "^3.6.1", - "react-native-gesture-handler": "^2.21.2", + "react-native-gesture-handler": "~2.16.1", "react-native-htmlview": "^0.17.0", "react-native-infinite-pager": "^0.3.16", "react-native-pager-view": "6.3.0", @@ -13939,10 +13939,9 @@ } }, "node_modules/pawnote": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/pawnote/-/pawnote-1.3.4.tgz", - "integrity": "sha512-8VMb3vA09z9kM9V2oMCJDNMBOK71gn9p5r2QqoGy/IE5g5Pn2vYR9gaVBmpEgahETz7mGw1mUMzXZeSm212s9w==", - "license": "GPL-3.0-or-later", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/pawnote/-/pawnote-1.4.1.tgz", + "integrity": "sha512-jdFHfyZ7tIRJ03etegs/ExgW2F2KZDPbOq6atjs6Fi1DINl893QLfVIxLwADb+tGQbYifYwwOurv4QZQ1OJZ8Q==", "dependencies": { "@literate.ink/utilities": "1.0.0-10641118381.1", "html-entities": "^2.5.2", @@ -14721,13 +14720,14 @@ } }, "node_modules/react-native-gesture-handler": { - "version": "2.21.2", - "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.21.2.tgz", - "integrity": "sha512-HcwB225K9aeZ8e/B8nFzEh+2T4EPWTeamO1l/y3PcQ9cyCDYO2zja/G31ITpYRIqkip7XzGs6wI/gnHOQn1LDQ==", + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.16.2.tgz", + "integrity": "sha512-vGFlrDKlmyI+BT+FemqVxmvO7nqxU33cgXVsn6IKAFishvlG3oV2Ds67D5nPkHMea8T+s1IcuMm0bF8ntZtAyg==", "dependencies": { "@egjs/hammerjs": "^2.0.17", "hoist-non-react-statics": "^3.3.0", "invariant": "^2.2.4", + "lodash": "^4.17.21", "prop-types": "^15.7.2" }, "peerDependencies": { diff --git a/package.json b/package.json index 68c198693..2b2295f29 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "papillonvex", - "version": "7.9.0", + "version": "7.8.2", "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", @@ -80,7 +80,7 @@ "react-native-barcode-svg": "^0.0.15", "react-native-draggable-flatlist": "^4.0.1", "react-native-draglist": "^3.6.1", - "react-native-gesture-handler": "^2.21.2", + "react-native-gesture-handler": "~2.16.1", "react-native-htmlview": "^0.17.0", "react-native-infinite-pager": "^0.3.16", "react-native-pager-view": "6.3.0", From e461e7068a69eb81b9eb6af7fcb02f5c69cef2f4 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 30 Jan 2025 19:06:10 +0100 Subject: [PATCH 0428/1144] feat: moving logs --- src/background/BackgroundTasks.ts | 12 ++++++++++-- src/background/data/Attendance.ts | 2 -- src/background/data/Evaluation.ts | 2 -- src/background/data/Grades.ts | 2 -- src/background/data/Homeworks.ts | 2 -- src/background/data/Lessons.ts | 2 -- src/background/data/News.ts | 2 -- 7 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 17c29d7bf..66d07450f 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -5,7 +5,7 @@ import { BackgroundFetchResult } from "expo-background-fetch"; import { isExpoGo } from "@/utils/native/expoGoAlert"; import { fetchNews } from "./data/News"; -import { log, error, warn } from "@/utils/logger/logger"; +import { log, error, warn, info } from "@/utils/logger/logger"; import { getAccounts, getSwitchToFunction } from "./utils/accounts"; import { fetchHomeworks } from "./data/Homeworks"; import { fetchGrade } from "./data/Grades"; @@ -67,11 +67,19 @@ const backgroundFetch = async () => { account.personalization.notifications; if (notificationsTypesPermissions?.enabled) { + info("▶️ Running background News", "BackgroundEvent"); await fetchNews(); + info("▶️ Running background Homeworks", "BackgroundEvent"); await fetchHomeworks(); + info("▶️ Running background Grades", "BackgroundEvent"); await fetchGrade(); - // await fetchLessons(); // Disabled for now + /* Disabled for now + info("▶️ Running background Lessons", "BackgroundEvent"); + await fetchLessons(); + */ + info("▶️ Running background Attendance", "BackgroundEvent"); await fetchAttendance(); + info("▶️ Running background Evaluation", "BackgroundEvent"); await fetchEvaluation(); } } diff --git a/src/background/data/Attendance.ts b/src/background/data/Attendance.ts index 70469d8fb..7c7e313f3 100644 --- a/src/background/data/Attendance.ts +++ b/src/background/data/Attendance.ts @@ -2,7 +2,6 @@ import { getCurrentAccount } from "../utils/accounts"; import { papillonNotify } from "../Notifications"; import { Attendance } from "@/services/shared/Attendance"; import { getAttendance, updateAttendanceState } from "../utils/attendance"; -import { info } from "@/utils/logger/logger"; const getDifferences = ( currentAttendance: Attendance, @@ -53,7 +52,6 @@ const getDifferences = ( }; const fetchAttendance = async (): Promise => { - info("▶️ Running background Attendance", "BackgroundEvent"); const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; diff --git a/src/background/data/Evaluation.ts b/src/background/data/Evaluation.ts index 62cc4f715..0a1b5ff0a 100644 --- a/src/background/data/Evaluation.ts +++ b/src/background/data/Evaluation.ts @@ -2,7 +2,6 @@ import { getCurrentAccount } from "../utils/accounts"; import { papillonNotify } from "../Notifications"; import { Evaluation } from "@/services/shared/Evaluation"; import { getEvaluation, updateEvaluationState } from "../utils/evaluation"; -import { info } from "@/utils/logger/logger"; const getDifferences = ( currentEvaluation: Evaluation[], @@ -19,7 +18,6 @@ const getDifferences = ( }; const fetchEvaluation = async (): Promise => { - info("▶️ Running background Evaluation", "BackgroundEvent"); const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; diff --git a/src/background/data/Grades.ts b/src/background/data/Grades.ts index c40dc25eb..226bbda8e 100644 --- a/src/background/data/Grades.ts +++ b/src/background/data/Grades.ts @@ -2,7 +2,6 @@ import { getCurrentAccount } from "../utils/accounts"; import { papillonNotify } from "../Notifications"; import { Grade } from "@/services/shared/Grade"; import { getGrades, updateGradeState } from "../utils/grades"; -import { info } from "@/utils/logger/logger"; const getDifferences = ( currentGrade: Grade[], @@ -19,7 +18,6 @@ const getDifferences = ( }; const fetchGrade = async (): Promise => { - info("▶️ Running background Grades", "BackgroundEvent"); const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; diff --git a/src/background/data/Homeworks.ts b/src/background/data/Homeworks.ts index d174705a0..cba9c78ce 100644 --- a/src/background/data/Homeworks.ts +++ b/src/background/data/Homeworks.ts @@ -3,7 +3,6 @@ import { papillonNotify } from "../Notifications"; import { getHomeworks, updateHomeworksState } from "../utils/homeworks"; import { Homework } from "@/services/shared/Homework"; import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; -import { info } from "@/utils/logger/logger"; const getDifferences = ( currentHomeworks: Homework[], @@ -19,7 +18,6 @@ const getDifferences = ( }; const fetchHomeworks = async (): Promise => { - info("▶️ Running background Homeworks", "BackgroundEvent"); const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index 60958168c..046d020d5 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -3,7 +3,6 @@ import { papillonNotify } from "../Notifications"; import { getLessons, updateLessonsState } from "../utils/lessons"; import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; import { Timetable, TimetableClassStatus } from "@/services/shared/Timetable"; -import { info } from "@/utils/logger/logger"; const getAllLessonsForDay = (lessons: Record) => { const date = new Date(); @@ -22,7 +21,6 @@ const getAllLessonsForDay = (lessons: Record) => { }; const fetchLessons = async (): Promise => { - info("▶️ Running background Lessons", "BackgroundEvent"); const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; diff --git a/src/background/data/News.ts b/src/background/data/News.ts index 49e4fe3d8..094bb3090 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -3,7 +3,6 @@ import { getCurrentAccount } from "../utils/accounts"; import { papillonNotify } from "../Notifications"; import parse_news_resume from "@/utils/format/format_pronote_news"; import { Information } from "@/services/shared/Information"; -import { info } from "@/utils/logger/logger"; const getDifferences = ( currentNews: Information[], @@ -20,7 +19,6 @@ const getDifferences = ( }; const fetchNews = async (): Promise => { - info("▶️ Running background News", "BackgroundEvent"); const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; From 0c341a0b0f54ca39d939b51db6a3bf220554fff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Thu, 30 Jan 2025 19:55:38 +0100 Subject: [PATCH 0429/1144] fix: test erasing background task --- src/background/BackgroundTasks.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 66d07450f..a70471aa1 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -110,11 +110,16 @@ const registerBackgroundTasks = async () => { ); if (isRegistered) { - warn( + /* warn( "⚠️ Background task already registered. Unregister background task...", "BackgroundEvent" ); await unsetBackgroundFetch(); + */ + warn( + "⚠️ Background task already registered. Erasing previous background task...", + "BackgroundEvent" + ); } try { From 806a9e9a89a0757a9b46b5c73c43a73541acaa8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Thu, 30 Jan 2025 20:10:49 +0100 Subject: [PATCH 0430/1144] feat: add badge count management for notifications --- App.tsx | 1 + src/background/Notifications.ts | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/App.tsx b/App.tsx index a94bf071f..38fa8f2b1 100644 --- a/App.tsx +++ b/App.tsx @@ -117,6 +117,7 @@ export default function App () { if (nextAppState === "active") { log("🔄 App is active", "AppState"); + notifee.setBadgeCount(0); await handleBackgroundState(); backgroundStartTime.current = null; } else if (nextAppState.match(/inactive|background/)) { diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index c968f4365..0fdf8f14b 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -114,12 +114,19 @@ const papillonNotify = async ( // Add timestamp for Android const timestamp = new Date().getTime(); + // Get badge count + let badgeCount = await notifee.getBadgeCount(); + if (channelId !== "Status") { + badgeCount++; + } + // Display a notification await notifee.displayNotification({ ...props, android: { channelId, timestamp, + badgeCount, showTimestamp: channelId !== "Status" ? true : false, showChronometer: channelId === "Status" ? true : false, smallIcon: "@mipmap/ic_launcher_foreground", @@ -135,7 +142,7 @@ const papillonNotify = async ( }, ios: { threadId: channelId, - badgeCount: channelId !== "Status" ? 1 : 0, + badgeCount, sound: channelId !== "Status" ? "default" : undefined, // à intégrer => `categoryId` } From 52787a1f2aa33a70e064748e4c89151ae9fe4688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Thu, 30 Jan 2025 20:29:41 +0100 Subject: [PATCH 0431/1144] =?UTF-8?q?feat:=20notification=20du=20statut=20?= =?UTF-8?q?en=20arri=C3=A8re-plan=20+=20progression=20sur=20Android?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/BackgroundTasks.ts | 5 +++++ src/background/data/Attendance.ts | 16 ++++++++++++++++ src/background/data/Evaluation.ts | 16 ++++++++++++++++ src/background/data/Grades.ts | 16 ++++++++++++++++ src/background/data/Homeworks.ts | 16 ++++++++++++++++ src/background/data/Lessons.ts | 16 ++++++++++++++++ src/background/data/News.ts | 16 ++++++++++++++++ 7 files changed, 101 insertions(+) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index a70471aa1..1f883616a 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -53,6 +53,11 @@ const backgroundFetch = async () => { { id: "statusBackground", body: "Récupération des données des comptes les plus récentes en arrière-plan...", + android: { + progress: { + indeterminate: true, + }, + }, }, "Status" ); diff --git a/src/background/data/Attendance.ts b/src/background/data/Attendance.ts index 7c7e313f3..8d171fd05 100644 --- a/src/background/data/Attendance.ts +++ b/src/background/data/Attendance.ts @@ -55,6 +55,22 @@ const fetchAttendance = async (): Promise => { const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; + await papillonNotify( + { + id: "statusBackground", + title: account.name, + body: "Récupération des dernières événement de la vie scolaire...", + android: { + progress: { + max: 100, + current: 100 / 6 * 5, + indeterminate: false, + }, + }, + }, + "Status" + ); + const { defaultPeriod, attendances } = getAttendance(); await updateAttendanceState(account, defaultPeriod); const updatedAttendance = getAttendance().attendances[defaultPeriod]; diff --git a/src/background/data/Evaluation.ts b/src/background/data/Evaluation.ts index 0a1b5ff0a..4755f910b 100644 --- a/src/background/data/Evaluation.ts +++ b/src/background/data/Evaluation.ts @@ -21,6 +21,22 @@ const fetchEvaluation = async (): Promise => { const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; + await papillonNotify( + { + id: "statusBackground", + title: account.name, + body: "Récupération des dernières compétences...", + android: { + progress: { + max: 100, + current: 100 / 6 * 6, + indeterminate: false, + }, + }, + }, + "Status" + ); + const { defaultPeriod, evaluation } = getEvaluation(); await updateEvaluationState(account, defaultPeriod); const updatedEvaluation = getEvaluation().evaluation[defaultPeriod]; diff --git a/src/background/data/Grades.ts b/src/background/data/Grades.ts index 226bbda8e..53560d83c 100644 --- a/src/background/data/Grades.ts +++ b/src/background/data/Grades.ts @@ -21,6 +21,22 @@ const fetchGrade = async (): Promise => { const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; + await papillonNotify( + { + id: "statusBackground", + title: account.name, + body: "Récupération des dernières notes...", + android: { + progress: { + max: 100, + current: 100 / 6 * 3, + indeterminate: false, + }, + }, + }, + "Status" + ); + const { defaultPeriod, grades } = getGrades(); await updateGradeState(account, defaultPeriod); const updatedGrade = getGrades().grades[defaultPeriod]; diff --git a/src/background/data/Homeworks.ts b/src/background/data/Homeworks.ts index cba9c78ce..6f8bbb34f 100644 --- a/src/background/data/Homeworks.ts +++ b/src/background/data/Homeworks.ts @@ -21,6 +21,22 @@ const fetchHomeworks = async (): Promise => { const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; + await papillonNotify( + { + id: "statusBackground", + title: account.name, + body: "Récupération des derniers devoirs...", + android: { + progress: { + max: 100, + current: 100 / 6 * 2, + indeterminate: false, + }, + }, + }, + "Status" + ); + // @ts-expect-error let firstDate = account.instance?.instance?.firstDate || null; if (!firstDate) { diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index 046d020d5..a4ef587b5 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -24,6 +24,22 @@ const fetchLessons = async (): Promise => { const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; + await papillonNotify( + { + id: "statusBackground", + title: account.name, + body: "Récupération des dernières actualités...", + android: { + progress: { + max: 100, + current: 100 / 6 * 4, + indeterminate: false, + }, + }, + }, + "Status" + ); + const weekNumber = dateToEpochWeekNumber(new Date()); await updateLessonsState(account, weekNumber); const updatedLessons = getLessons(); diff --git a/src/background/data/News.ts b/src/background/data/News.ts index 094bb3090..40f5d143b 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -22,6 +22,22 @@ const fetchNews = async (): Promise => { const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; + await papillonNotify( + { + id: "statusBackground", + title: account.name, + body: "Récupération des dernières actualités...", + android: { + progress: { + max: 100, + current: 100 / 6 * 1, + indeterminate: false, + }, + }, + }, + "Status" + ); + const currentNews = getNews(); await updateNewsState(account); const updatedNews = getNews(); From e8a3dd297d464196e3759ee5a6ca7720e53edc33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 31 Jan 2025 13:36:17 +0100 Subject: [PATCH 0432/1144] feat: add Notifee plugin configuration for Android build --- app.config.ts | 1 + plugins/notifee-mod.js | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 plugins/notifee-mod.js diff --git a/app.config.ts b/app.config.ts index 0dfe3ca3d..9d715d4a1 100644 --- a/app.config.ts +++ b/app.config.ts @@ -80,6 +80,7 @@ export default (): ExpoConfig => ({ EXPO_ENV: "expo", }, plugins: [ + "./plugins/notifee-mod.js", [ "expo-font", { diff --git a/plugins/notifee-mod.js b/plugins/notifee-mod.js new file mode 100644 index 000000000..0c957eafb --- /dev/null +++ b/plugins/notifee-mod.js @@ -0,0 +1,17 @@ +const { withProjectBuildGradle } = require("expo/config-plugins"); + +module.exports = function withNotifeeRepo (config) { + return withProjectBuildGradle(config, async config => { + const contents = config.modResults.contents; + + if (!contents.includes("@notifee/react-native")) { + const replacement = `maven { url 'https://www.jitpack.io' } + maven { + url "$rootDir/../node_modules/@notifee/react-native/android/libs" + }`; + config.modResults.contents = contents.replace("maven { url 'https://www.jitpack.io' }", replacement); + } + + return config; + }); +}; \ No newline at end of file From 22bf6aad63a27fc1b7b52dd6f753237bc8d8a89e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 31 Jan 2025 13:37:34 +0100 Subject: [PATCH 0433/1144] run prebuild --- android/build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/android/build.gradle b/android/build.gradle index 9ae2aca2b..031f875ea 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -37,6 +37,9 @@ allprojects { google() mavenCentral() maven { url 'https://www.jitpack.io' } + maven { + url "$rootDir/../node_modules/@notifee/react-native/android/libs" + } } } // @generated begin expo-camera-import - expo prebuild (DO NOT MODIFY) sync-f244f4f3d8bf7229102e8f992b525b8602c74770 From 3fce05b0f119c148fd44c1161634f19ef84d689d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 31 Jan 2025 19:08:35 +0100 Subject: [PATCH 0434/1144] fix: update isExpoGo function to check for execution environment --- src/utils/native/expoGoAlert.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/native/expoGoAlert.ts b/src/utils/native/expoGoAlert.ts index 50c22ef84..1fa9db09b 100644 --- a/src/utils/native/expoGoAlert.ts +++ b/src/utils/native/expoGoAlert.ts @@ -1,8 +1,8 @@ import { Alert } from "react-native"; -import Constants from "expo-constants"; +import Constants, { ExecutionEnvironment } from "expo-constants"; export const isExpoGo = () => { - return Constants.expoConfig?.extra?.EXPO_ENV === "expo"; + return Constants.executionEnvironment !== ExecutionEnvironment.Bare; }; export const alertExpoGo = async () => { From c6ce75c27691a4d7c0998faaf91ded27ce6773ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 31 Jan 2025 19:09:06 +0100 Subject: [PATCH 0435/1144] fix: remove EXPO_ENV configuration from environment and app config --- .env | 1 - app.config.ts | 3 --- 2 files changed, 4 deletions(-) delete mode 100644 .env diff --git a/.env b/.env deleted file mode 100644 index d31f8d1ad..000000000 --- a/.env +++ /dev/null @@ -1 +0,0 @@ -EXPO_ENV=expo diff --git a/app.config.ts b/app.config.ts index 9d715d4a1..2519d418c 100644 --- a/app.config.ts +++ b/app.config.ts @@ -76,9 +76,6 @@ export default (): ExpoConfig => ({ "android.permission.ACCESS_FINE_LOCATION", ], }, - extra: { - EXPO_ENV: "expo", - }, plugins: [ "./plugins/notifee-mod.js", [ From b91f3232f10a4f95eddc52073b3a2484c73cd266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 31 Jan 2025 23:26:00 +0100 Subject: [PATCH 0436/1144] feat: update homework cache for the upcoming week --- src/background/utils/homeworks.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/background/utils/homeworks.ts b/src/background/utils/homeworks.ts index 1550ef138..ba67c7695 100644 --- a/src/background/utils/homeworks.ts +++ b/src/background/utils/homeworks.ts @@ -22,4 +22,8 @@ export const updateHomeworksState = async (account: PrimaryAccount) => { account, epochWNToDate(getCurrentWeekNumber()) ); + await updateHomeworkForWeekInCache( + account, + epochWNToDate(getCurrentWeekNumber() + 1) + ); }; From b405dc05b8640210d9666ede74e671de7df857a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 31 Jan 2025 23:36:06 +0100 Subject: [PATCH 0437/1144] refactor: update attendance, evaluation, and grades to avoid period errors on first use --- src/background/utils/attendance.ts | 10 +++++++--- src/background/utils/evaluation.ts | 10 +++++++--- src/background/utils/grades.ts | 10 +++++++--- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/background/utils/attendance.ts b/src/background/utils/attendance.ts index 3c61c7ca1..9d61c35f0 100644 --- a/src/background/utils/attendance.ts +++ b/src/background/utils/attendance.ts @@ -1,4 +1,7 @@ -import { updateAttendanceInCache, updateAttendancePeriodsInCache } from "@/services/attendance"; +import { + updateAttendanceInCache, + updateAttendancePeriodsInCache, +} from "@/services/attendance"; import { PrimaryAccount } from "@/stores/account/types"; import { useAttendanceStore } from "@/stores/attendance"; @@ -13,6 +16,7 @@ export const updateAttendanceState = async ( account: PrimaryAccount, period: string ) => { - await updateAttendancePeriodsInCache(account); - await updateAttendanceInCache(account, period); + await updateAttendancePeriodsInCache(account).then( + async () => await updateAttendanceInCache(account, period) + ); }; diff --git a/src/background/utils/evaluation.ts b/src/background/utils/evaluation.ts index 54ee4053e..63441fc3c 100644 --- a/src/background/utils/evaluation.ts +++ b/src/background/utils/evaluation.ts @@ -1,4 +1,7 @@ -import { updateEvaluationPeriodsInCache, updateEvaluationsInCache } from "@/services/evaluation"; +import { + updateEvaluationPeriodsInCache, + updateEvaluationsInCache, +} from "@/services/evaluation"; import { PrimaryAccount } from "@/stores/account/types"; import { useEvaluationStore } from "@/stores/evaluation"; @@ -13,6 +16,7 @@ export const updateEvaluationState = async ( account: PrimaryAccount, period: string ) => { - await updateEvaluationPeriodsInCache(account); - await updateEvaluationsInCache(account, period); + await updateEvaluationPeriodsInCache(account).then( + async () => await updateEvaluationsInCache(account, period) + ); }; diff --git a/src/background/utils/grades.ts b/src/background/utils/grades.ts index 5c89eb61c..aacebbce9 100644 --- a/src/background/utils/grades.ts +++ b/src/background/utils/grades.ts @@ -1,4 +1,7 @@ -import { updateGradesAndAveragesInCache, updateGradesPeriodsInCache } from "@/services/grades"; +import { + updateGradesAndAveragesInCache, + updateGradesPeriodsInCache, +} from "@/services/grades"; import { PrimaryAccount } from "@/stores/account/types"; import { useGradesStore } from "@/stores/grades"; @@ -13,6 +16,7 @@ export const updateGradeState = async ( account: PrimaryAccount, period: string ) => { - await updateGradesPeriodsInCache(account); - await updateGradesAndAveragesInCache(account, period); + await updateGradesPeriodsInCache(account).then( + async () => await updateGradesAndAveragesInCache(account, period) + ); }; From e9a37b3564ed4a6c188412a3f128c3947765aa75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 1 Feb 2025 00:50:21 +0100 Subject: [PATCH 0438/1144] fix: update notification dismissal handling to adjust badge count --- App.tsx | 9 ++++----- src/background/BackgroundTasks.ts | 7 +++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/App.tsx b/App.tsx index 38fa8f2b1..20599f216 100644 --- a/App.tsx +++ b/App.tsx @@ -26,7 +26,7 @@ const BACKGROUND_LIMITS: Partial> = { export default function App () { useEffect(() => { // Gestion des notifs quand app en premier plan - const unsubscribe = notifee.onForegroundEvent(({ type, detail }) => { + const unsubscribe = notifee.onForegroundEvent(async ({ type, detail }) => { const { notification, pressAction } = detail; switch (type) { @@ -43,11 +43,10 @@ export default function App () { break; case EventType.DISMISSED: - console.log(`[Notifee] Notification dismissed: ${notification?.id}`); + let badgeCount = await notifee.getBadgeCount(); + badgeCount--; + await notifee.setBadgeCount(badgeCount); break; - - default: - console.log(`[Notifee] Foreground event type: ${type}`); } }); diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 1f883616a..8a624bf4e 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -32,11 +32,10 @@ notifee.onBackgroundEvent(async ({ type, detail }) => { break; case EventType.DISMISSED: - console.log(`[Notifee] Notification dismissed: ${notification?.id}`); + let badgeCount = await notifee.getBadgeCount(); + badgeCount--; + await notifee.setBadgeCount(badgeCount); break; - - default: - console.log(`[Notifee] Background event type: ${type}`); } }); From 0ac83227f6f62ceaccb992a6a69625d9f8236431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 1 Feb 2025 00:51:11 +0100 Subject: [PATCH 0439/1144] fix: reset badge count and cancel all notifications on app activation --- App.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/App.tsx b/App.tsx index 20599f216..7f12cf6cd 100644 --- a/App.tsx +++ b/App.tsx @@ -116,7 +116,9 @@ export default function App () { if (nextAppState === "active") { log("🔄 App is active", "AppState"); - notifee.setBadgeCount(0); + await notifee.setBadgeCount(0); + await notifee.cancelAllNotifications(); + await handleBackgroundState(); backgroundStartTime.current = null; } else if (nextAppState.match(/inactive|background/)) { From 2ac9ffcdcdd5d900b559807d7ee3001a3c2e0246 Mon Sep 17 00:00:00 2001 From: Bulgus <88266443+Bulgus@users.noreply.github.com> Date: Sat, 1 Feb 2025 09:54:58 +0100 Subject: [PATCH 0440/1144] fix(gradesAvarage): change null condition to NaN --- src/views/account/Grades/Graph/GradesAverage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/account/Grades/Graph/GradesAverage.tsx b/src/views/account/Grades/Graph/GradesAverage.tsx index c8f2d70dc..73c7468f5 100644 --- a/src/views/account/Grades/Graph/GradesAverage.tsx +++ b/src/views/account/Grades/Graph/GradesAverage.tsx @@ -97,7 +97,7 @@ const GradesAverageGraph: React.FC = ({ originalCurrentAvgRef.current = hst[hst.length - 1].value; - setClassAvg(cla.length > 0 ? cla[cla.length - 1].value : null); + setClassAvg(cla[cla.length - 1].value); setMaxAvg(maxAvg); setMinAvg(minAvg); @@ -337,7 +337,7 @@ const GradesAverageGraph: React.FC = ({ style={[styles.gradeValue]} layout={animPapillon(LinearTransition)} > - {classAvg !== null ? ( + { !Number.isNaN(classAvg) ? ( <> Date: Sat, 1 Feb 2025 09:55:39 +0100 Subject: [PATCH 0441/1144] fix(gradesAvarage): update classAvg state to disable null --- src/views/account/Grades/Graph/GradesAverage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Grades/Graph/GradesAverage.tsx b/src/views/account/Grades/Graph/GradesAverage.tsx index 73c7468f5..ad447d53f 100644 --- a/src/views/account/Grades/Graph/GradesAverage.tsx +++ b/src/views/account/Grades/Graph/GradesAverage.tsx @@ -54,7 +54,7 @@ const GradesAverageGraph: React.FC = ({ const [currentAvg, setCurrentAvg] = useState(0); const [originalCurrentAvg, setOriginalCurrentAvg] = useState(0); - const [classAvg, setClassAvg] = useState(0); + const [classAvg, setClassAvg] = useState(0); const [maxAvg, setMaxAvg] = useState(0); const [minAvg, setMinAvg] = useState(0); From f1e48a3bdbc51a4b51779586715014c9c6a53020 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 1 Feb 2025 10:54:11 +0100 Subject: [PATCH 0442/1144] fix(ts): added ts ignore comments for weird options in `views/index.ts` --- src/router/screens/views/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/router/screens/views/index.ts b/src/router/screens/views/index.ts index ffb39c703..d902a4f5a 100644 --- a/src/router/screens/views/index.ts +++ b/src/router/screens/views/index.ts @@ -65,6 +65,7 @@ export default [ animation: "slide_from_bottom", sheetGrabberVisible: false, sheetInitialDetentIndex: 0, + // @ts-expect-error IDK why it is a list of number, it should be SheetDetentTypes sheetAllowedDetents: [0.5, 1.0], headerShown: false, }), @@ -75,6 +76,7 @@ export default [ animation: "slide_from_bottom", sheetGrabberVisible: false, sheetInitialDetentIndex: 0, + // @ts-expect-error IDK why it is a list of number, it should be SheetDetentTypes sheetAllowedDetents: [0.5, 1.0], headerShown: false, }), @@ -85,6 +87,7 @@ export default [ animation: "slide_from_bottom", sheetGrabberVisible: true, sheetInitialDetentIndex: 0, + // @ts-expect-error IDK why it is a list of number, it should be SheetDetentTypes sheetAllowedDetents: [0.5, 1.0], }), createScreen("GradeDocument", GradeDocument, { @@ -94,6 +97,7 @@ export default [ animation: "slide_from_bottom", sheetGrabberVisible: false, sheetInitialDetentIndex: 0, + // @ts-expect-error IDK why it is a list of number, it should be SheetDetentTypes sheetAllowedDetents: [0.5, 1.0], headerShown: Platform.OS !== "ios", }), From 28beadf0609e09a3e98f976eee649b117c73fc62 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 1 Feb 2025 10:56:32 +0100 Subject: [PATCH 0443/1144] fix(ts): any type cast in `iutlan/grades.ts` --- src/services/iutlan/grades.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/iutlan/grades.ts b/src/services/iutlan/grades.ts index 0a26590e3..9987e6c4b 100644 --- a/src/services/iutlan/grades.ts +++ b/src/services/iutlan/grades.ts @@ -13,7 +13,8 @@ export const saveIUTLanGrades = async (account: LocalAccount, periodName: string // console.log(periodName); // Il faudrait peut-être penser à typer cette partie, tous les types sont any :( - const scodocData = account.serviceData.semestres[periodName]; + const data = account.serviceData.semestres as any; + const scodocData = data[periodName] as any; if (!scodocData) { return { From 7e535d2a326d09dce104809824d14117f764cd81 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 1 Feb 2025 10:58:07 +0100 Subject: [PATCH 0444/1144] fix(ts): missing members for account in `skolengo-account.ts` --- src/services/skolengo/skolengo-account.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/services/skolengo/skolengo-account.ts b/src/services/skolengo/skolengo-account.ts index 339469a85..6d73da973 100644 --- a/src/services/skolengo/skolengo-account.ts +++ b/src/services/skolengo/skolengo-account.ts @@ -107,7 +107,9 @@ export const getSkolengoAccount = async (authConfig: SkolengoAuthConfig, userInf className: userInfo?.className, personalization: await defaultSkolengoPersonalization(skolengoAccount), userInfo, - identity: {} + identity: {}, + providers: [], + serviceData: {} }; return account; }; From b813b9938c778d841c49491544da27212058ec2c Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 1 Feb 2025 11:01:34 +0100 Subject: [PATCH 0445/1144] fix(ts): missing members for PapillonMultiServiceSpace account type --- src/stores/account/types.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/stores/account/types.ts b/src/stores/account/types.ts index 22f9457ba..9d85e494e 100644 --- a/src/stores/account/types.ts +++ b/src/stores/account/types.ts @@ -223,6 +223,8 @@ export interface PapillonMultiServiceSpace extends BaseAccount { rawData: undefined }, associatedAccountsLocalIDs: string[] + providers: string[] + serviceData: Record } From a25df469c049d0c7e4e1b85840853dd8c9dfbc6b Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 1 Feb 2025 11:01:42 +0100 Subject: [PATCH 0446/1144] fix(ts): any type cast --- src/views/account/Grades/Atoms/GradesScodocUE.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/views/account/Grades/Atoms/GradesScodocUE.tsx b/src/views/account/Grades/Atoms/GradesScodocUE.tsx index 897c546b0..2ad4f5381 100644 --- a/src/views/account/Grades/Atoms/GradesScodocUE.tsx +++ b/src/views/account/Grades/Atoms/GradesScodocUE.tsx @@ -18,9 +18,9 @@ const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: Prim const { colors } = useTheme(); const { showAlert } = useAlert(); - const grades = account.serviceData.semestres[selectedPeriod]; + const data = account.serviceData.semestres as any; + const grades = data[selectedPeriod]; - // @ts-expect-error const ues = grades["relevé"]["ues"]; const uekeys = Object.keys(ues); @@ -28,9 +28,7 @@ const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: Prim return null; } - // @ts-expect-error const ressources = grades["relevé"]["ressources"]; - // @ts-expect-error const saes = grades["relevé"]["saes"]; const finalUes = uekeys.map((ue) => { @@ -305,4 +303,4 @@ const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: Prim } }; -export default memo(GradesScodocUE); \ No newline at end of file +export default memo(GradesScodocUE); From e0a132280a99ab217b257ae58627451a265ce29f Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 1 Feb 2025 11:02:44 +0100 Subject: [PATCH 0447/1144] fix(ts): missing react import --- src/views/account/Grades/Document.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/account/Grades/Document.tsx b/src/views/account/Grades/Document.tsx index 2bd25c1ab..845d01c45 100644 --- a/src/views/account/Grades/Document.tsx +++ b/src/views/account/Grades/Document.tsx @@ -1,3 +1,4 @@ +import React from 'react'; import { NativeItem, NativeList, NativeListHeader, NativeText, } from "@/components/Global/NativeComponents"; import { getSubjectData } from "@/services/shared/Subject"; import { useTheme } from "@react-navigation/native"; From a82754470a2bc805f2c5c073f839356e16f523b1 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 1 Feb 2025 11:04:29 +0100 Subject: [PATCH 0448/1144] fix(ts): `SubjectList.tsx` --- src/views/account/Grades/Subject/SubjectList.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/views/account/Grades/Subject/SubjectList.tsx b/src/views/account/Grades/Subject/SubjectList.tsx index 6e2cb5e69..aba7add94 100644 --- a/src/views/account/Grades/Subject/SubjectList.tsx +++ b/src/views/account/Grades/Subject/SubjectList.tsx @@ -45,6 +45,8 @@ const SubjectItem: React.FC = ({ return null; } + index = index || 0; + return ( Date: Sat, 1 Feb 2025 11:06:22 +0100 Subject: [PATCH 0449/1144] fix(ts): types in `BackgroundIUTLannion.tsx` --- .../login/IdentityProvider/actions/BackgroundIUTLannion.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx index 259f81191..b41782a66 100644 --- a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx +++ b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx @@ -126,7 +126,7 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio const processSemestre = async (data: any) => { // ajouter le semestre ici - const newServiceData = account?.serviceData || {}; + const newServiceData = account?.serviceData || {} as any; if (!newServiceData["semestres"]) { newServiceData["semestres"] = {}; @@ -197,7 +197,8 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio className: data["relevé"].etudiant.dept_acronym, schoolName: "IUT de Lannion - Université de Rennes", - personalization: await defaultPersonalization() + personalization: await defaultPersonalization(), + serviceData: {} }; createStoredAccount(local_account); From 087ddff9c36e45f855dd220d2d95a239bafe37bb Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 1 Feb 2025 11:08:05 +0100 Subject: [PATCH 0450/1144] fix(ts): account types in `Multi.tsx`, `UnivLimoges.tsx`, `UnivRennes1.tsx`, `UnivRennes2.tsx` & `UnivSorbonneParisNord.tsx` --- src/views/login/IdentityProvider/providers/Multi.tsx | 2 ++ src/views/login/IdentityProvider/providers/UnivLimoges.tsx | 4 +++- src/views/login/IdentityProvider/providers/UnivRennes1.tsx | 6 ++++-- src/views/login/IdentityProvider/providers/UnivRennes2.tsx | 4 +++- .../IdentityProvider/providers/UnivSorbonneParisNord.tsx | 6 ++++-- 5 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/views/login/IdentityProvider/providers/Multi.tsx b/src/views/login/IdentityProvider/providers/Multi.tsx index 87ba88016..5ed4953e0 100644 --- a/src/views/login/IdentityProvider/providers/Multi.tsx +++ b/src/views/login/IdentityProvider/providers/Multi.tsx @@ -53,6 +53,8 @@ const Muli_Login: Screen<"Multi_Login"> = ({ route, navigation }) => { refreshAuthToken: account.userData.refreshAuthToken || "", }, personalization: await defaultPersonalization(account), + serviceData: {}, + providers: [] }; createStoredAccount(local_account); diff --git a/src/views/login/IdentityProvider/providers/UnivLimoges.tsx b/src/views/login/IdentityProvider/providers/UnivLimoges.tsx index fdf85edb7..da79aacc1 100644 --- a/src/views/login/IdentityProvider/providers/UnivLimoges.tsx +++ b/src/views/login/IdentityProvider/providers/UnivLimoges.tsx @@ -65,7 +65,9 @@ const UnivLimoges_Login: Screen<"UnivLimoges_Login"> = ({ navigation }) => { personalization: await defaultPersonalization({ profilePictureB64: user.avatar }), - identity: {} + identity: {}, + serviceData: {}, + providers: [] }; createStoredAccount(local_account); diff --git a/src/views/login/IdentityProvider/providers/UnivRennes1.tsx b/src/views/login/IdentityProvider/providers/UnivRennes1.tsx index 85d7f2a69..3dd0b620c 100644 --- a/src/views/login/IdentityProvider/providers/UnivRennes1.tsx +++ b/src/views/login/IdentityProvider/providers/UnivRennes1.tsx @@ -52,7 +52,9 @@ const UnivRennes1_Login: Screen<"UnivRennes1_Login"> = ({ navigation }) => { schoolName: data.caccount.data.attachmentDpt.name.replace("Institut Universitaire de Technologie", "IUT") + " - Université de Rennes", personalization: await defaultPersonalization(), - identity: {} + identity: {}, + serviceData: {}, + providers: [] }; createStoredAccount(local_account); @@ -154,4 +156,4 @@ const UnivRennes1_Login: Screen<"UnivRennes1_Login"> = ({ navigation }) => { ); }; -export default UnivRennes1_Login; \ No newline at end of file +export default UnivRennes1_Login; diff --git a/src/views/login/IdentityProvider/providers/UnivRennes2.tsx b/src/views/login/IdentityProvider/providers/UnivRennes2.tsx index a0014dfee..7ca6bc9af 100644 --- a/src/views/login/IdentityProvider/providers/UnivRennes2.tsx +++ b/src/views/login/IdentityProvider/providers/UnivRennes2.tsx @@ -94,7 +94,9 @@ const UnivRennes2_Login: Screen<"UnivRennes2_Login"> = ({ navigation }) => { personalization: await defaultPersonalization(), - identity: {} + identity: {}, + serviceData: {}, + providers: [] }; createStoredAccount(local_account); diff --git a/src/views/login/IdentityProvider/providers/UnivSorbonneParisNord.tsx b/src/views/login/IdentityProvider/providers/UnivSorbonneParisNord.tsx index a1136e6a7..604b633eb 100644 --- a/src/views/login/IdentityProvider/providers/UnivSorbonneParisNord.tsx +++ b/src/views/login/IdentityProvider/providers/UnivSorbonneParisNord.tsx @@ -83,7 +83,9 @@ const UnivSorbonneParisNord_login: Screen<"UnivSorbonneParisNord_login"> = ({ na className: "", schoolName: "Université Sorbonne Paris Nord", personalization: await defaultPersonalization(), - identity: {} + identity: {}, + serviceData: {}, + providers: [] }; createStoredAccount(localAccount); @@ -139,4 +141,4 @@ const UnivSorbonneParisNord_login: Screen<"UnivSorbonneParisNord_login"> = ({ na ); }; -export default UnivSorbonneParisNord_login; \ No newline at end of file +export default UnivSorbonneParisNord_login; From cda92253dc5172c40bd73e41382f2a22611f9a79 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 1 Feb 2025 11:09:43 +0100 Subject: [PATCH 0451/1144] fix(ts): account types in all files --- src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx | 3 +++ src/views/login/pronote/Pronote2FA_Auth.tsx | 4 +++- src/views/login/pronote/PronoteCredentials.tsx | 4 +++- src/views/login/pronote/PronoteQRCode.tsx | 4 +++- src/views/login/pronote/PronoteV6Import.tsx | 4 +++- src/views/login/pronote/PronoteWebview.tsx | 4 +++- 6 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx b/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx index cb9495c7f..105b3bc9a 100644 --- a/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx +++ b/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx @@ -91,6 +91,9 @@ const EcoleDirecteCredentials: Screen<"EcoleDirecteCredentials"> = ({ navigation account }, personalization: await defaultPersonalization(account), + + serviceData: {}, + providers: [] }; diff --git a/src/views/login/pronote/Pronote2FA_Auth.tsx b/src/views/login/pronote/Pronote2FA_Auth.tsx index 83bff4fef..c38449178 100644 --- a/src/views/login/pronote/Pronote2FA_Auth.tsx +++ b/src/views/login/pronote/Pronote2FA_Auth.tsx @@ -152,7 +152,9 @@ export const Pronote2FA_Auth: Screen<"Pronote2FA_Auth"> = ({ authentication: { ...refresh, deviceUUID: accountID }, personalization: await defaultPersonalization(session), - identity: {} + identity: {}, + serviceData: {}, + providers: [] }; pronote.startPresenceInterval(session); diff --git a/src/views/login/pronote/PronoteCredentials.tsx b/src/views/login/pronote/PronoteCredentials.tsx index 6c0e55c37..f7d37fc2f 100644 --- a/src/views/login/pronote/PronoteCredentials.tsx +++ b/src/views/login/pronote/PronoteCredentials.tsx @@ -69,7 +69,9 @@ const PronoteCredentials: Screen<"PronoteCredentials"> = ({ route, navigation }) authentication: { ...refresh, deviceUUID: accountID }, personalization: await defaultPersonalization(session), - identity: {} + identity: {}, + serviceData: {}, + providers: [] }; pronote.startPresenceInterval(session); diff --git a/src/views/login/pronote/PronoteQRCode.tsx b/src/views/login/pronote/PronoteQRCode.tsx index 8ec03c4ce..37ef05fd2 100644 --- a/src/views/login/pronote/PronoteQRCode.tsx +++ b/src/views/login/pronote/PronoteQRCode.tsx @@ -117,7 +117,9 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { authentication: { ...refresh, deviceUUID: accountID }, personalization: await defaultPersonalization(session), - identity: {} + identity: {}, + serviceData: {}, + providers: [] }; pronote.startPresenceInterval(session); diff --git a/src/views/login/pronote/PronoteV6Import.tsx b/src/views/login/pronote/PronoteV6Import.tsx index 477419976..95f5b01b4 100644 --- a/src/views/login/pronote/PronoteV6Import.tsx +++ b/src/views/login/pronote/PronoteV6Import.tsx @@ -72,7 +72,9 @@ const PronoteV6Import: Screen<"PronoteV6Import"> = ({ route, navigation }) => { authentication: { ...refresh, deviceUUID: data.deviceUUID }, personalization: await defaultPersonalization(session), - identity: {} + identity: {}, + serviceData: {}, + providers: [] }; pronote.startPresenceInterval(session); diff --git a/src/views/login/pronote/PronoteWebview.tsx b/src/views/login/pronote/PronoteWebview.tsx index bfc3b5091..738b1c4fa 100644 --- a/src/views/login/pronote/PronoteWebview.tsx +++ b/src/views/login/pronote/PronoteWebview.tsx @@ -345,7 +345,9 @@ const PronoteWebview: Screen<"PronoteWebview"> = ({ route, navigation }) => { authentication: { ...refresh, deviceUUID }, personalization: await defaultPersonalization(session), - identity: {} + identity: {}, + serviceData: {}, + providers: [] }; pronote.startPresenceInterval(session); From b0c076e8888d45c24ce78ded20c808d8960479c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 1 Feb 2025 15:19:16 +0100 Subject: [PATCH 0452/1144] fix(MenuItem): import Pressable from react-native instead of react-native-gesture-handler --- src/router/navigator/atoms/MenuItem.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/router/navigator/atoms/MenuItem.tsx b/src/router/navigator/atoms/MenuItem.tsx index f53a74ad5..28959c711 100644 --- a/src/router/navigator/atoms/MenuItem.tsx +++ b/src/router/navigator/atoms/MenuItem.tsx @@ -1,9 +1,8 @@ import * as React from "react"; import { useTheme } from "@react-navigation/native"; -import { StyleSheet, Platform } from "react-native"; +import { StyleSheet, Platform, Pressable } from "react-native"; import LottieView from "lottie-react-native"; import colorsList from "@/utils/data/colors.json"; -import { Pressable } from "react-native-gesture-handler"; import * as Haptics from "expo-haptics"; import Reanimated, { FadeIn, FadeOut, LinearTransition } from "react-native-reanimated"; import { anim2Papillon } from "@/utils/ui/animations"; From f86cfff7eea0afe1478110beacd14d14a21c7128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 1 Feb 2025 15:46:55 +0100 Subject: [PATCH 0453/1144] fix(AccountSwitcher): affichage illisible en mode clair --- src/components/Home/AccountSwitcher.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Home/AccountSwitcher.tsx b/src/components/Home/AccountSwitcher.tsx index ab3ba3a9c..b69e15876 100644 --- a/src/components/Home/AccountSwitcher.tsx +++ b/src/components/Home/AccountSwitcher.tsx @@ -89,6 +89,7 @@ const AccountSwitcher: React.FC<{ tint={theme.dark ? "dark" : "light"} experimentalBlurMethod="dimezisBlurView" style={{ + backgroundColor: colors.primary + "85", paddingHorizontal: 2, paddingVertical: 0, alignSelf: "flex-start", From b6403bd28d74d5c027c4ed7b685e2907f1f8a9b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 1 Feb 2025 15:53:02 +0100 Subject: [PATCH 0454/1144] =?UTF-8?q?fix(AccountSwitcherContextMenu):=20aj?= =?UTF-8?q?uster=20le=20th=C3=A8me=20et=20l'intensit=C3=A9=20du=20flou?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Home/AccountSwitcherContextMenu.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/Home/AccountSwitcherContextMenu.tsx b/src/components/Home/AccountSwitcherContextMenu.tsx index a9b791274..eb1cfe101 100644 --- a/src/components/Home/AccountSwitcherContextMenu.tsx +++ b/src/components/Home/AccountSwitcherContextMenu.tsx @@ -360,15 +360,14 @@ const ContextMenu: React.FC<{ bottom: 0, width: "100%", height: "100%", - backgroundColor: "#00000050" }, ]} entering={FadeIn.duration(200)} exiting={FadeOut.duration(200)} > Date: Sat, 1 Feb 2025 18:14:04 +0100 Subject: [PATCH 0455/1144] fix(ts): account types in `SettingsMultiService.tsx` --- src/views/settings/SettingsMultiService.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index 115553149..20fb96827 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -98,7 +98,10 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => studentName: { first: currentAccount.account?.studentName.first || "", last: currentAccount.account?.studentName.last || "" - } + }, + + serviceData: {}, + providers: [] }; const space: MultiServiceSpace = { From 7ccb495cfb859a13bc8ec62c7997b8e721d5ed1e Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 1 Feb 2025 18:29:41 +0100 Subject: [PATCH 0456/1144] fix(lint): linter fixes --- src/components/Settings/MultiServiceContainerCard.tsx | 5 ++--- src/stores/multiService/index.ts | 1 - src/views/account/Grades/Document.tsx | 2 +- src/views/login/pronote/PronoteCredentials.tsx | 1 - src/views/login/pronote/PronoteInstanceSelector.tsx | 1 - src/views/settings/SettingsMultiService.tsx | 2 +- 6 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/components/Settings/MultiServiceContainerCard.tsx b/src/components/Settings/MultiServiceContainerCard.tsx index 38ac18c0d..1fb04e5b4 100644 --- a/src/components/Settings/MultiServiceContainerCard.tsx +++ b/src/components/Settings/MultiServiceContainerCard.tsx @@ -1,7 +1,6 @@ -import React, { useState, useEffect } from "react"; -import type { Screen } from "@/router/helpers/types"; +import React, { useEffect } from "react"; -import { ScrollView, Image, Text, View } from "react-native"; +import { View } from "react-native"; import LottieView from "lottie-react-native"; import { NativeItem, NativeList, NativeText } from "../Global/NativeComponents"; import { LinearGradient } from "expo-linear-gradient"; diff --git a/src/stores/multiService/index.ts b/src/stores/multiService/index.ts index 5e32018b5..95792e78e 100644 --- a/src/stores/multiService/index.ts +++ b/src/stores/multiService/index.ts @@ -3,7 +3,6 @@ import { createJSONStorage, persist } from "zustand/middleware"; import { create } from "zustand"; import { log } from "@/utils/logger/logger"; import {MultiServiceSpace, MultiServiceStore} from "@/stores/multiService/types"; -import {useAccounts} from "@/stores/account"; /** diff --git a/src/views/account/Grades/Document.tsx b/src/views/account/Grades/Document.tsx index 845d01c45..2a6eaec4f 100644 --- a/src/views/account/Grades/Document.tsx +++ b/src/views/account/Grades/Document.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React from "react"; import { NativeItem, NativeList, NativeListHeader, NativeText, } from "@/components/Global/NativeComponents"; import { getSubjectData } from "@/services/shared/Subject"; import { useTheme } from "@react-navigation/native"; diff --git a/src/views/login/pronote/PronoteCredentials.tsx b/src/views/login/pronote/PronoteCredentials.tsx index f7d37fc2f..da635022f 100644 --- a/src/views/login/pronote/PronoteCredentials.tsx +++ b/src/views/login/pronote/PronoteCredentials.tsx @@ -1,6 +1,5 @@ import React, { useState } from "react"; import type { Screen } from "@/router/helpers/types"; -import { StyleSheet } from "react-native"; import pronote from "pawnote"; import uuid from "@/utils/uuid-v4"; diff --git a/src/views/login/pronote/PronoteInstanceSelector.tsx b/src/views/login/pronote/PronoteInstanceSelector.tsx index 263031aa8..3f5af6d1f 100644 --- a/src/views/login/pronote/PronoteInstanceSelector.tsx +++ b/src/views/login/pronote/PronoteInstanceSelector.tsx @@ -32,7 +32,6 @@ import {Search, X, GraduationCap, SearchX} from "lucide-react-native"; import { useAlert } from "@/providers/AlertProvider"; import { Audio } from "expo-av"; import getInstancesFromDataset from "@/services/pronote/dataset_geolocation"; -import {openURL} from "expo-linking"; import * as WebBrowser from "expo-web-browser"; const PronoteInstanceSelector: Screen<"PronoteInstanceSelector"> = ({ diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index 20fb96827..fbb58c8b5 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -4,7 +4,7 @@ import {useTheme} from "@react-navigation/native"; import type {Screen} from "@/router/helpers/types"; import MultiServiceContainerCard from "@/components/Settings/MultiServiceContainerCard"; import {NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText} from "@/components/Global/NativeComponents"; -import {ImageIcon, PlugZap, Plus, Type, Trash2} from "lucide-react-native"; +import {ImageIcon, PlugZap, Plus, Type} from "lucide-react-native"; import {useAccounts, useCurrentAccount} from "@/stores/account"; import {useMultiService} from "@/stores/multiService"; import BottomSheet from "@/components/Modals/PapillonBottomSheet"; From a9ee8bd92a8c4c2449743b571424e5456001ddca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 1 Feb 2025 19:53:19 +0100 Subject: [PATCH 0457/1144] fix: suppression code inutile Co-authored-by: Gabriel29306 --- src/background/BackgroundTasks.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 8a624bf4e..d9bd0e7ac 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -114,16 +114,11 @@ const registerBackgroundTasks = async () => { ); if (isRegistered) { - /* warn( + warn( "⚠️ Background task already registered. Unregister background task...", "BackgroundEvent" ); await unsetBackgroundFetch(); - */ - warn( - "⚠️ Background task already registered. Erasing previous background task...", - "BackgroundEvent" - ); } try { From 7e825ada6bede383a908c78790167058fe1ffd83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 1 Feb 2025 20:10:50 +0100 Subject: [PATCH 0458/1144] =?UTF-8?q?feat:=20d=C3=A9placement=20du=20`if`?= =?UTF-8?q?=20pour=20=C3=A9viter=20de=20faire=20des=20requ=C3=AAtes=20pour?= =?UTF-8?q?=20rien?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gabriel29306 --- src/background/data/Attendance.ts | 48 +++---- src/background/data/Evaluation.ts | 45 +++---- src/background/data/Grades.ts | 45 +++---- src/background/data/Homeworks.ts | 63 ++++----- src/background/data/Lessons.ts | 211 +++++++++++++++--------------- src/background/data/News.ts | 39 +++--- 6 files changed, 230 insertions(+), 221 deletions(-) diff --git a/src/background/data/Attendance.ts b/src/background/data/Attendance.ts index 8d171fd05..12d659806 100644 --- a/src/background/data/Attendance.ts +++ b/src/background/data/Attendance.ts @@ -55,37 +55,37 @@ const fetchAttendance = async (): Promise => { const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; - await papillonNotify( - { - id: "statusBackground", - title: account.name, - body: "Récupération des dernières événement de la vie scolaire...", - android: { - progress: { - max: 100, - current: 100 / 6 * 5, - indeterminate: false, + const { defaultPeriod, attendances } = getAttendance(); + if (notificationsTypesPermissions?.attendance) { + await papillonNotify( + { + id: "statusBackground", + title: account.name, + body: "Récupération des dernières événement de la vie scolaire...", + android: { + progress: { + max: 100, + current: 100 / 6 * 5, + indeterminate: false, + }, }, }, - }, - "Status" - ); + "Status" + ); - const { defaultPeriod, attendances } = getAttendance(); - await updateAttendanceState(account, defaultPeriod); - const updatedAttendance = getAttendance().attendances[defaultPeriod]; + await updateAttendanceState(account, defaultPeriod); + const updatedAttendance = getAttendance().attendances[defaultPeriod]; - const differences = getDifferences( - attendances[defaultPeriod], - updatedAttendance - ); - const LAdifference = + const differences = getDifferences( + attendances[defaultPeriod], + updatedAttendance + ); + const LAdifference = differences.absences.length + differences.delays.length + differences.observations.length + differences.punishments.length; - if (notificationsTypesPermissions?.attendance) { switch (LAdifference) { case 0: break; @@ -216,9 +216,11 @@ const fetchAttendance = async (): Promise => { ); break; } + + return updatedAttendance; } - return updatedAttendance; + return attendances[defaultPeriod]; }; export { fetchAttendance }; diff --git a/src/background/data/Evaluation.ts b/src/background/data/Evaluation.ts index 4755f910b..8ab360626 100644 --- a/src/background/data/Evaluation.ts +++ b/src/background/data/Evaluation.ts @@ -21,32 +21,32 @@ const fetchEvaluation = async (): Promise => { const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; - await papillonNotify( - { - id: "statusBackground", - title: account.name, - body: "Récupération des dernières compétences...", - android: { - progress: { - max: 100, - current: 100 / 6 * 6, - indeterminate: false, + const { defaultPeriod, evaluation } = getEvaluation(); + if (notificationsTypesPermissions?.evaluation) { + await papillonNotify( + { + id: "statusBackground", + title: account.name, + body: "Récupération des dernières compétences...", + android: { + progress: { + max: 100, + current: 100 / 6 * 6, + indeterminate: false, + }, }, }, - }, - "Status" - ); + "Status" + ); - const { defaultPeriod, evaluation } = getEvaluation(); - await updateEvaluationState(account, defaultPeriod); - const updatedEvaluation = getEvaluation().evaluation[defaultPeriod]; + await updateEvaluationState(account, defaultPeriod); + const updatedEvaluation = getEvaluation().evaluation[defaultPeriod]; - const differences = getDifferences( - evaluation[defaultPeriod] ?? [], - updatedEvaluation ?? [] - ); + const differences = getDifferences( + evaluation[defaultPeriod] ?? [], + updatedEvaluation ?? [] + ); - if (notificationsTypesPermissions?.evaluation) { switch (differences.length) { case 0: break; @@ -80,9 +80,10 @@ const fetchEvaluation = async (): Promise => { ); break; } + return updatedEvaluation; } - return updatedEvaluation; + return evaluation[defaultPeriod]; }; export { fetchEvaluation }; diff --git a/src/background/data/Grades.ts b/src/background/data/Grades.ts index 53560d83c..f325efa5f 100644 --- a/src/background/data/Grades.ts +++ b/src/background/data/Grades.ts @@ -21,32 +21,32 @@ const fetchGrade = async (): Promise => { const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; - await papillonNotify( - { - id: "statusBackground", - title: account.name, - body: "Récupération des dernières notes...", - android: { - progress: { - max: 100, - current: 100 / 6 * 3, - indeterminate: false, + const { defaultPeriod, grades } = getGrades(); + if (notificationsTypesPermissions?.grades) { + await papillonNotify( + { + id: "statusBackground", + title: account.name, + body: "Récupération des dernières notes...", + android: { + progress: { + max: 100, + current: 100 / 6 * 3, + indeterminate: false, + }, }, }, - }, - "Status" - ); + "Status" + ); - const { defaultPeriod, grades } = getGrades(); - await updateGradeState(account, defaultPeriod); - const updatedGrade = getGrades().grades[defaultPeriod]; + await updateGradeState(account, defaultPeriod); + const updatedGrade = getGrades().grades[defaultPeriod]; - const differences = getDifferences( - grades[defaultPeriod] ?? [], - updatedGrade ?? [] - ); + const differences = getDifferences( + grades[defaultPeriod] ?? [], + updatedGrade ?? [] + ); - if (notificationsTypesPermissions?.grades) { switch (differences.length) { case 0: break; @@ -80,9 +80,10 @@ const fetchGrade = async (): Promise => { ); break; } + return updatedGrade; } - return updatedGrade; + return grades[defaultPeriod]; }; export { fetchGrade }; diff --git a/src/background/data/Homeworks.ts b/src/background/data/Homeworks.ts index 6f8bbb34f..225168073 100644 --- a/src/background/data/Homeworks.ts +++ b/src/background/data/Homeworks.ts @@ -21,22 +21,6 @@ const fetchHomeworks = async (): Promise => { const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; - await papillonNotify( - { - id: "statusBackground", - title: account.name, - body: "Récupération des derniers devoirs...", - android: { - progress: { - max: 100, - current: 100 / 6 * 2, - indeterminate: false, - }, - }, - }, - "Status" - ); - // @ts-expect-error let firstDate = account.instance?.instance?.firstDate || null; if (!firstDate) { @@ -48,25 +32,41 @@ const fetchHomeworks = async (): Promise => { const SemaineAct = dateToEpochWeekNumber(new Date()); const currentHwSemaineActuelle = getHomeworks()[SemaineAct] ?? []; - const currentHwSemaineProchaine = getHomeworks()[SemaineAct + 1] ?? []; + if (notificationsTypesPermissions?.homeworks) { + await papillonNotify( + { + id: "statusBackground", + title: account.name, + body: "Récupération des derniers devoirs...", + android: { + progress: { + max: 100, + current: 100 / 6 * 2, + indeterminate: false, + }, + }, + }, + "Status" + ); - await updateHomeworksState(account); + const currentHwSemaineProchaine = getHomeworks()[SemaineAct + 1] ?? []; - const updatedHwSemaineActuelle = getHomeworks()[SemaineAct] ?? []; - const updatedHwSemaineProchaine = getHomeworks()[SemaineAct + 1] ?? []; + await updateHomeworksState(account); - const differencesHwSemaineActuelle = getDifferences( - currentHwSemaineActuelle, - updatedHwSemaineActuelle - ); - const differencesHwSemaineProchaine = getDifferences( - currentHwSemaineProchaine, - updatedHwSemaineProchaine - ); - const differences = + const updatedHwSemaineActuelle = getHomeworks()[SemaineAct] ?? []; + const updatedHwSemaineProchaine = getHomeworks()[SemaineAct + 1] ?? []; + + const differencesHwSemaineActuelle = getDifferences( + currentHwSemaineActuelle, + updatedHwSemaineActuelle + ); + const differencesHwSemaineProchaine = getDifferences( + currentHwSemaineProchaine, + updatedHwSemaineProchaine + ); + const differences = differencesHwSemaineActuelle.length + differencesHwSemaineProchaine.length; - if (notificationsTypesPermissions?.homeworks) { switch (differences) { case 0: break; @@ -126,9 +126,10 @@ const fetchHomeworks = async (): Promise => { ); break; } + return updatedHwSemaineActuelle ?? updatedHwSemaineProchaine; } - return updatedHwSemaineActuelle ?? updatedHwSemaineProchaine; + return currentHwSemaineActuelle; }; export { fetchHomeworks }; diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index a4ef587b5..fb39ac61f 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -24,131 +24,134 @@ const fetchLessons = async (): Promise => { const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; - await papillonNotify( - { - id: "statusBackground", - title: account.name, - body: "Récupération des dernières actualités...", - android: { - progress: { - max: 100, - current: 100 / 6 * 4, - indeterminate: false, + const currentLessons = getLessons(); + if (notificationsTypesPermissions?.timetable){ + await papillonNotify( + { + id: "statusBackground", + title: account.name, + body: "Récupération des dernières actualités...", + android: { + progress: { + max: 100, + current: 100 / 6 * 4, + indeterminate: false, + }, }, }, - }, - "Status" - ); + "Status" + ); - const weekNumber = dateToEpochWeekNumber(new Date()); - await updateLessonsState(account, weekNumber); - const updatedLessons = getLessons(); - const lessonsDay = getAllLessonsForDay(updatedLessons); - const lessonsEvent = lessonsDay.filter((element) => element.statusText); + const weekNumber = dateToEpochWeekNumber(new Date()); + await updateLessonsState(account, weekNumber); + const updatedLessons = getLessons(); + const lessonsDay = getAllLessonsForDay(updatedLessons); + const lessonsEvent = lessonsDay.filter((element) => element.statusText); - // Notifie 15 minutes avant le début du 1er cours - // De base, je voulais faire 1 heure avant, mais la notif sera modifiée à chaque fois - // comme la fréquence du background est de 15 minutes - const now = new Date().getTime(); - const oneHourBefore = lessonsDay[0]?.startTimestamp - 15 * 60 * 1000; + // Notifie 15 minutes avant le début du 1er cours + // De base, je voulais faire 1 heure avant, mais la notif sera modifiée à chaque fois + // comme la fréquence du background est de 15 minutes + const now = new Date().getTime(); + const oneHourBefore = lessonsDay[0]?.startTimestamp - 15 * 60 * 1000; - if ( - notificationsTypesPermissions?.timetable && - now >= oneHourBefore && - now < lessonsDay[0]?.startTimestamp - ) { - switch (lessonsEvent.length) { - case 0: - break; - case 1: - const dateLessonsDebut = `${new Date(lessonsEvent[0].startTimestamp) - .getHours() - .toString() - .padStart(2, "0")}:${new Date(lessonsEvent[0].startTimestamp) - .getMinutes() - .toString() - .padStart(2, "0")}`; + if ( + now >= oneHourBefore && + now < lessonsDay[0]?.startTimestamp + ) { + switch (lessonsEvent.length) { + case 0: + break; + case 1: + const dateLessonsDebut = `${new Date(lessonsEvent[0].startTimestamp) + .getHours() + .toString() + .padStart(2, "0")}:${new Date(lessonsEvent[0].startTimestamp) + .getMinutes() + .toString() + .padStart(2, "0")}`; - const dateLessonsFin = `${new Date(lessonsEvent[0].endTimestamp) - .getHours() - .toString() - .padStart(2, "0")}:${new Date(lessonsEvent[0].endTimestamp) - .getMinutes() - .toString() - .padStart(2, "0")}`; + const dateLessonsFin = `${new Date(lessonsEvent[0].endTimestamp) + .getHours() + .toString() + .padStart(2, "0")}:${new Date(lessonsEvent[0].endTimestamp) + .getMinutes() + .toString() + .padStart(2, "0")}`; - let statut: string = ""; + let statut: string = ""; - switch (lessonsEvent[0].status) { - case TimetableClassStatus.CANCELED: - statut = "a été annulé"; - break; - case TimetableClassStatus.TEST: - statut = "est un devoir surveillé"; - case TimetableClassStatus.MODIFIED: - statut = + switch (lessonsEvent[0].status) { + case TimetableClassStatus.CANCELED: + statut = "a été annulé"; + break; + case TimetableClassStatus.TEST: + statut = "est un devoir surveillé"; + case TimetableClassStatus.MODIFIED: + statut = "est un cours modifié, consulte l'emploi du temps pour plus de détails"; - default: - if (lessonsEvent[0].statusText === "Changement de salle") { - statut = "a un changement de salle ! "; - if (lessonsEvent[0].room) { - if (lessonsEvent[0].room.includes(",")) { - statut += + default: + if (lessonsEvent[0].statusText === "Changement de salle") { + statut = "a un changement de salle ! "; + if (lessonsEvent[0].room) { + if (lessonsEvent[0].room.includes(",")) { + statut += "Consulte l'emploi du temps pour regarder les salles de cours"; - } else { - statut += `Tu dois aller en salle ${lessonsEvent[0].room}`; + } else { + statut += `Tu dois aller en salle ${lessonsEvent[0].room}`; + } } + } else if (lessonsEvent[0].statusText === "Devoir Surveillé") { + statut = "est un devoir surveillé"; + } else { + statut = `a un statut : ${lessonsEvent[0].statusText}`; } - } else if (lessonsEvent[0].statusText === "Devoir Surveillé") { - statut = "est un devoir surveillé"; - } else { - statut = `a un statut : ${lessonsEvent[0].statusText}`; - } - break; - } + break; + } - await papillonNotify( - { - id: `${account.name}-lessons`, - title: `[${account.name}] Emploi du temps du jour modifié`, - subtitle: new Date( - lessonsEvent[0].startTimestamp - ).toLocaleDateString("fr-FR", { - weekday: "long", - day: "numeric", - month: "long", - }), - body: `Le cours de ${lessonsEvent[0].title} (${dateLessonsDebut}-${dateLessonsFin}) ${statut}`, - }, - "Lessons" - ); - break; - default: - await papillonNotify( - { - id: `${account.name}-lessons`, - title: `[${account.name}] Emploi du temps du jour`, - subtitle: new Date( - lessonsEvent[0].startTimestamp - ).toLocaleDateString("fr-FR", { - weekday: "long", - day: "numeric", - month: "long", - }), - body: `Les cours suivants ont été modifiés, consulte l'emploi du temps pour plus de détails.
+ await papillonNotify( + { + id: `${account.name}-lessons`, + title: `[${account.name}] Emploi du temps du jour modifié`, + subtitle: new Date( + lessonsEvent[0].startTimestamp + ).toLocaleDateString("fr-FR", { + weekday: "long", + day: "numeric", + month: "long", + }), + body: `Le cours de ${lessonsEvent[0].title} (${dateLessonsDebut}-${dateLessonsFin}) ${statut}`, + }, + "Lessons" + ); + break; + default: + await papillonNotify( + { + id: `${account.name}-lessons`, + title: `[${account.name}] Emploi du temps du jour`, + subtitle: new Date( + lessonsEvent[0].startTimestamp + ).toLocaleDateString("fr-FR", { + weekday: "long", + day: "numeric", + month: "long", + }), + body: `Les cours suivants ont été modifiés, consulte l'emploi du temps pour plus de détails.
${lessonsEvent .flatMap((element) => { return `- ${element.title}`; }) .join("
")}`, - }, - "Lessons" - ); - break; + }, + "Lessons" + ); + break; + } } + return lessonsEvent; } - return lessonsEvent; + return getAllLessonsForDay(currentLessons); }; export { fetchLessons }; diff --git a/src/background/data/News.ts b/src/background/data/News.ts index 40f5d143b..ad35e39e1 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -22,29 +22,29 @@ const fetchNews = async (): Promise => { const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; - await papillonNotify( - { - id: "statusBackground", - title: account.name, - body: "Récupération des dernières actualités...", - android: { - progress: { - max: 100, - current: 100 / 6 * 1, - indeterminate: false, + const currentNews = getNews(); + if (notificationsTypesPermissions?.news) { + await papillonNotify( + { + id: "statusBackground", + title: account.name, + body: "Récupération des dernières actualités...", + android: { + progress: { + max: 100, + current: 100 / 6 * 1, + indeterminate: false, + }, }, }, - }, - "Status" - ); + "Status" + ); - const currentNews = getNews(); - await updateNewsState(account); - const updatedNews = getNews(); + await updateNewsState(account); + const updatedNews = getNews(); - const differences = getDifferences(currentNews, updatedNews); + const differences = getDifferences(currentNews, updatedNews); - if (notificationsTypesPermissions?.news) { switch (differences.length) { case 0: break; @@ -85,9 +85,10 @@ const fetchNews = async (): Promise => { ); break; } + return updatedNews; } - return updatedNews; + return currentNews; }; export { fetchNews }; From 5363d1db5c55421093c18cccc9f92945fc408b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 1 Feb 2025 21:17:00 +0100 Subject: [PATCH 0459/1144] feat: ajout d'une fonction pour formater les heures et les minutes Co-authored-by: Gabriel29306 --- src/background/data/Attendance.ts | 58 ++++++++++--------------------- 1 file changed, 18 insertions(+), 40 deletions(-) diff --git a/src/background/data/Attendance.ts b/src/background/data/Attendance.ts index 12d659806..f58954d72 100644 --- a/src/background/data/Attendance.ts +++ b/src/background/data/Attendance.ts @@ -3,6 +3,14 @@ import { papillonNotify } from "../Notifications"; import { Attendance } from "@/services/shared/Attendance"; import { getAttendance, updateAttendanceState } from "../utils/attendance"; +const formatHoursMinutes = (timestamp: number) => { + const LAdate = new Date(timestamp); + const heures = LAdate.getHours().toString().padStart(2, "0"); + const minutes = LAdate.getMinutes().toString().padStart(2, "0"); + + return `${heures}:${minutes}`; +}; + const getDifferences = ( currentAttendance: Attendance, updatedAttendance: Attendance @@ -65,7 +73,7 @@ const fetchAttendance = async (): Promise => { android: { progress: { max: 100, - current: 100 / 6 * 5, + current: (100 / 6) * 5, indeterminate: false, }, }, @@ -81,10 +89,10 @@ const fetchAttendance = async (): Promise => { updatedAttendance ); const LAdifference = - differences.absences.length + - differences.delays.length + - differences.observations.length + - differences.punishments.length; + differences.absences.length + + differences.delays.length + + differences.observations.length + + differences.punishments.length; switch (LAdifference) { case 0: @@ -94,49 +102,19 @@ const fetchAttendance = async (): Promise => { let explication = ""; if (differences.absences.length === 1) { - const dateAbsencesDebut = `${new Date( - differences.absences[0].fromTimestamp - ) - .getHours() - .toString() - .padStart(2, "0")}:${new Date(differences.absences[0].fromTimestamp) - .getMinutes() - .toString() - .padStart(2, "0")}`; - - const dateAbsencesFin = `${new Date( - differences.absences[0].toTimestamp - ) - .getHours() - .toString() - .padStart(2, "0")}:${new Date(differences.absences[0].toTimestamp) - .getMinutes() - .toString() - .padStart(2, "0")}`; + const dateAbsencesDebut = formatHoursMinutes(differences.absences[0].fromTimestamp); + + const dateAbsencesFin = formatHoursMinutes(differences.absences[0].toTimestamp); thenewevent = "Nouvelle absence"; explication = `Tu as été absent de ${dateAbsencesDebut} à ${dateAbsencesFin}.`; } else if (differences.delays.length === 1) { - const dateRetard = `${new Date(differences.delays[0].timestamp) - .getHours() - .toString() - .padStart(2, "0")}:${new Date(differences.delays[0].timestamp) - .getMinutes() - .toString() - .padStart(2, "0")}`; + const dateRetard = formatHoursMinutes(differences.delays[0].timestamp); thenewevent = "Nouveau retard"; explication = `Tu as été en retard de ${differences.delays[0].duration} min à ${dateRetard}.`; } else if (differences.observations.length === 1) { - const dateObservations = `${new Date( - differences.observations[0].timestamp - ) - .getHours() - .toString() - .padStart(2, "0")}:${new Date(differences.observations[0].timestamp) - .getMinutes() - .toString() - .padStart(2, "0")}`; + const dateObservations = formatHoursMinutes(differences.observations[0].timestamp); thenewevent = "Nouvelle observation"; explication = `Tu as eu une observation en ${ From e38f4ad88bf9a23f2dc3e68f8f9ee2b4ea5d3f3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 1 Feb 2025 21:51:03 +0100 Subject: [PATCH 0460/1144] =?UTF-8?q?feat:=20am=C3=A9lioration=20et=20simp?= =?UTF-8?q?lification=20de=20la=20notification=20des=20devoirs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Homeworks.ts | 59 +++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/src/background/data/Homeworks.ts b/src/background/data/Homeworks.ts index 225168073..74b63cd17 100644 --- a/src/background/data/Homeworks.ts +++ b/src/background/data/Homeworks.ts @@ -41,7 +41,7 @@ const fetchHomeworks = async (): Promise => { android: { progress: { max: 100, - current: 100 / 6 * 2, + current: (100 / 6) * 2, indeterminate: false, }, }, @@ -65,7 +65,8 @@ const fetchHomeworks = async (): Promise => { updatedHwSemaineProchaine ); const differences = - differencesHwSemaineActuelle.length + differencesHwSemaineProchaine.length; + differencesHwSemaineActuelle.length + + differencesHwSemaineProchaine.length; switch (differences) { case 0: @@ -100,26 +101,50 @@ const fetchHomeworks = async (): Promise => { } break; default: + const subjectCounts: Record = {}; + + [ + ...differencesHwSemaineActuelle, + ...differencesHwSemaineProchaine, + ].forEach((hw) => { + subjectCounts[hw.subject] = (subjectCounts[hw.subject] || 0) + 1; + }); + + const subjectPreview = Object.entries(subjectCounts) + .map(([subject, count]) => + count > 1 ? `${count}x ${subject}` : subject + ) + .join(", "); + + let subtitle = "Semaine "; + if (differencesHwSemaineActuelle.length > 0) { + subtitle += ( + ((SemaineAct - (firstDateEpoch % 52)) % 52) + + 1 + ).toString(); + } + if (differencesHwSemaineProchaine.length > 0) { + if (differencesHwSemaineActuelle.length > 0) { + subtitle += `et ${( + ((SemaineAct - (firstDateEpoch % 52)) % 52) + + 2 + ).toString()}`; + } else { + subtitle += ( + ((SemaineAct - (firstDateEpoch % 52)) % 52) + + 2 + ).toString(); + } + } + await papillonNotify( { id: `${account.name}-homeworks`, title: `[${account.name}] Nouveaux devoirs`, - subtitle: `Semaine ${( - ((SemaineAct - (firstDateEpoch % 52)) % 52) + - 1 - ).toString()}`, + subtitle, body: ` - ${differences} nouveaux devoirs ont été publiés :
- ${differencesHwSemaineActuelle - .flatMap((element) => { - return `- ${element.subject}`; - }) - .join("
")} - ${differencesHwSemaineProchaine - .flatMap((element) => { - return `- ${element.subject}`; - }) - .join("
")} + ${differences} nouveaux devoirs :
+ ${subjectPreview} `, }, "Homeworks" From 94b11798681abc3f6ed6607d269dfbebd1c90ea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 1 Feb 2025 22:00:50 +0100 Subject: [PATCH 0461/1144] =?UTF-8?q?feat:=20am=C3=A9lioration=20des=20not?= =?UTF-8?q?ifications=20de=20devoirs=20avec=20un=20formatage=20du=20conten?= =?UTF-8?q?u=20(lorsque=20un=20nouveau=20devoir)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Homeworks.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/background/data/Homeworks.ts b/src/background/data/Homeworks.ts index 74b63cd17..f07b89064 100644 --- a/src/background/data/Homeworks.ts +++ b/src/background/data/Homeworks.ts @@ -3,6 +3,7 @@ import { papillonNotify } from "../Notifications"; import { getHomeworks, updateHomeworksState } from "../utils/homeworks"; import { Homework } from "@/services/shared/Homework"; import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; +import parse_homeworks from "@/utils/format/format_pronote_homeworks"; const getDifferences = ( currentHomeworks: Homework[], @@ -76,12 +77,12 @@ const fetchHomeworks = async (): Promise => { await papillonNotify( { id: `${account.name}-homeworks`, - title: `[${account.name}] Nouveau devoir`, + title: `[${account.name}] Nouveau devoir en ${differencesHwSemaineActuelle[0].subject}`, subtitle: `Semaine ${( ((SemaineAct - (firstDateEpoch % 52)) % 52) + 1 ).toString()}`, - body: `Un nouveau devoir en ${differencesHwSemaineActuelle[0].subject} a été publié`, + body: parse_homeworks(differencesHwSemaineActuelle[0].content), }, "Homeworks" ); @@ -89,12 +90,12 @@ const fetchHomeworks = async (): Promise => { await papillonNotify( { id: `${account.name}-homeworks`, - title: `[${account.name}] Nouveau devoir`, + title: `[${account.name}] Nouveau devoir en ${differencesHwSemaineProchaine[0].subject}`, subtitle: `Semaine ${( ((SemaineAct - (firstDateEpoch % 52)) % 52) + 2 ).toString()}`, - body: `Un nouveau devoir en ${differencesHwSemaineProchaine[0].subject} a été publié`, + body: parse_homeworks(differencesHwSemaineProchaine[0].content), }, "Homeworks" ); From 40d2d44d1a45cc7243069e27152d9ae6c9f8efd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 1 Feb 2025 22:08:41 +0100 Subject: [PATCH 0462/1144] =?UTF-8?q?feat:=20am=C3=A9lioration=20et=20simp?= =?UTF-8?q?lification=20de=20la=20notification=20des=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Grades.ts | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/background/data/Grades.ts b/src/background/data/Grades.ts index f325efa5f..df8122513 100644 --- a/src/background/data/Grades.ts +++ b/src/background/data/Grades.ts @@ -31,7 +31,7 @@ const fetchGrade = async (): Promise => { android: { progress: { max: 100, - current: 100 / 6 * 3, + current: (100 / 6) * 3, indeterminate: false, }, }, @@ -54,26 +54,37 @@ const fetchGrade = async (): Promise => { await papillonNotify( { id: `${account.name}-grades`, - title: `[${account.name}] Nouvelle note`, + title: `[${account.name}] Nouvelle note en ${differences[0].subjectName}`, subtitle: defaultPeriod, - body: `Une nouvelle note en ${differences[0].subjectName} a été publiée`, + body: `Titre : ${ + differences[0].description || "Note sans titre" + }, Coefficient : ${differences[0].coefficient}`, }, "Grades" ); break; default: + const gradeCounts: Record = {}; + + differences.forEach((grade) => { + gradeCounts[grade.subjectName] = + (gradeCounts[grade.subjectName] || 0) + 1; + }); + + const gradePreview = Object.entries(gradeCounts) + .map(([subject, count]) => + count > 1 ? `${count}x ${subject}` : subject + ) + .join(", "); + await papillonNotify( { id: `${account.name}-grades`, subtitle: defaultPeriod, title: `[${account.name}] Nouvelles notes`, body: ` - ${differences.length} nouvelles notes ont été publiées :
- ${differences - .flatMap((element) => { - return `- ${element.subjectName}`; - }) - .join("
")} + ${differences.length} nouvelles notes :
+ ${gradePreview} `, }, "Grades" From 2ca7dd8b638b4983ffb28ab2aa1c823acf07151b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 1 Feb 2025 22:12:25 +0100 Subject: [PATCH 0463/1144] =?UTF-8?q?feat:=20am=C3=A9lioration=20et=20simp?= =?UTF-8?q?lification=20de=20la=20notification=20des=20comp=C3=A9tences?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Evaluation.ts | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/background/data/Evaluation.ts b/src/background/data/Evaluation.ts index 8ab360626..f1d35de30 100644 --- a/src/background/data/Evaluation.ts +++ b/src/background/data/Evaluation.ts @@ -31,7 +31,7 @@ const fetchEvaluation = async (): Promise => { android: { progress: { max: 100, - current: 100 / 6 * 6, + current: (100 / 6) * 6, indeterminate: false, }, }, @@ -54,26 +54,37 @@ const fetchEvaluation = async (): Promise => { await papillonNotify( { id: `${account.name}-evaluation`, - title: `[${account.name}] Nouvelle compétence`, + title: `[${account.name}] Nouvelle compétence en ${differences[0].subjectName}`, subtitle: defaultPeriod, - body: `Une nouvelle compétence en ${differences[0].subjectName} a été publiée`, + body: `Titre : ${ + differences[0].description || "Compétence sans titre" + }, Coefficient : ${differences[0].coefficient}`, }, "Evaluation" ); break; default: + const evaluationCounts: Record = {}; + + differences.forEach((grade) => { + evaluationCounts[grade.subjectName] = + (evaluationCounts[grade.subjectName] || 0) + 1; + }); + + const evaluationPreview = Object.entries(evaluationCounts) + .map(([subject, count]) => + count > 1 ? `${count}x ${subject}` : subject + ) + .join(", "); + await papillonNotify( { id: `${account.name}-evaluation`, subtitle: defaultPeriod, title: `[${account.name}] Nouvelles compétences`, body: ` - ${differences.length} nouvelles compétences ont été publiées :
- ${differences - .flatMap((element) => { - return `- ${element.subjectName}`; - }) - .join("
")} + ${differences.length} nouvelles compétences :
+ ${evaluationPreview} `, }, "Evaluation" From 2246b4814b95e36ee0cc277d21a0c6a41c2c4bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 1 Feb 2025 22:15:25 +0100 Subject: [PATCH 0464/1144] =?UTF-8?q?feat:=20am=C3=A9lioration=20et=20simp?= =?UTF-8?q?lification=20de=20la=20notification=20des=20actualit=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/News.ts | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/background/data/News.ts b/src/background/data/News.ts index ad35e39e1..06f60e2c3 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -32,7 +32,7 @@ const fetchNews = async (): Promise => { android: { progress: { max: 100, - current: 100 / 6 * 1, + current: (100 / 6) * 1, indeterminate: false, }, }, @@ -52,7 +52,7 @@ const fetchNews = async (): Promise => { await papillonNotify( { id: `${account.name}-news`, - title: `[${account.name}] Nouvelle actualité`, + title: `[${account.name}] Nouvelle actualité par ${differences[0].author}`, subtitle: differences[0].title, body: differences[0].content && !differences[0].content.includes(" => { ); break; default: + const newsCounts: Record = {}; + + differences.forEach((the_news) => { + newsCounts[the_news.author] = (newsCounts[the_news.author] || 0) + 1; + }); + + const newsPreview = Object.entries(newsCounts) + .map(([subject, count]) => + count > 1 ? `${count}x ${subject}` : subject + ) + .join(", "); + await papillonNotify( { id: `${account.name}-news`, title: `[${account.name}] Nouvelles actualités`, body: ` - ${ - differences.length - } nouvelles actualités ont été publiées par :
- ${differences - .flatMap((element) => { - return `- ${element.author}`; - }) - .join("
")} + ${differences.length} nouvelles actualités par :
+ ${newsPreview} `, }, "News" From f9ae1be540dbb3e506982543690fafbba9f594ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 1 Feb 2025 22:25:18 +0100 Subject: [PATCH 0465/1144] =?UTF-8?q?feat:=20ouverture=20de=20la=20page=20?= =?UTF-8?q?directe=20des=20param=C3=A8tres=20des=20notifications=20sur=20A?= =?UTF-8?q?ndroid=20(impossible=20sur=20iOS)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Settings/NotificationContainerCard.tsx | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/src/components/Settings/NotificationContainerCard.tsx b/src/components/Settings/NotificationContainerCard.tsx index 828479974..67eb653cc 100644 --- a/src/components/Settings/NotificationContainerCard.tsx +++ b/src/components/Settings/NotificationContainerCard.tsx @@ -8,6 +8,7 @@ import { Platform, Alert, Linking, + NativeModules, } from "react-native"; import LottieView from "lottie-react-native"; import Reanimated, { @@ -36,7 +37,15 @@ const openNotificationSettings = () => { if (Platform.OS === "ios") { Linking.openURL("app-settings:"); } else { + const { getConstants } = NativeModules.RNDeviceInfo || {}; + const packageName = getConstants?.().bundleId || "xyz.getpapillon.app"; + Linking.openSettings(); + setTimeout(() => { + Linking.openURL( + `android.settings.APP_NOTIFICATION_SETTINGS?package=${packageName}` + ); + }, 500); } }; @@ -44,7 +53,7 @@ const NotificationContainerCard = ({ theme, isEnable, setEnabled, - navigation + navigation, }: NotificationContainerCardProps) => { const { colors } = theme; @@ -88,10 +97,19 @@ const NotificationContainerCard = ({ return ( - + - - + + - Papillon - il y a 22 min + + Papillon + + + il y a 22 min + - Le cours de géographie (16:00-17:00) a un changement de salle ! Tu dois aller en salle B106 + style={[styles.message, textAnimatedStyle]} + > + Le cours de géographie (16:00-17:00) a un changement de salle + ! Tu dois aller en salle B106 From 1a29555a7f11cdfa3b43e0d86a25614ba4b79afd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 1 Feb 2025 22:39:14 +0100 Subject: [PATCH 0466/1144] =?UTF-8?q?feat:=20d=C3=A9placement=20de=20la=20?= =?UTF-8?q?gestion=20des=20t=C3=A2ches=20en=20arri=C3=A8re-plan=20dans=20l?= =?UTF-8?q?e=20menu=20d=C3=A9veloppeur?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/SettingsNotifications.tsx | 147 +------------------ src/views/welcome/DevMenu.tsx | 117 ++++++++++++++- 2 files changed, 118 insertions(+), 146 deletions(-) diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 3ab2c5390..35c6bded4 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { Alert, Platform, ScrollView, Switch, View } from "react-native"; +import { Alert, Platform, ScrollView, Switch } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { @@ -9,9 +9,7 @@ import { Newspaper, Info, NotepadText, - BookPlus, - CheckCircle2, - CircleAlert + BookPlus } from "lucide-react-native"; import { useSharedValue, withTiming } from "react-native-reanimated"; import { @@ -22,15 +20,10 @@ import { NativeText } from "@/components/Global/NativeComponents"; import NotificationContainerCard from "@/components/Settings/NotificationContainerCard"; -import { createChannelNotification, papillonNotify, requestNotificationPermission } from "@/background/Notifications"; +import { createChannelNotification, requestNotificationPermission } from "@/background/Notifications"; import { useCurrentAccount } from "@/stores/account"; import { PressableScale } from "react-native-pressable-scale"; import { useAlert } from "@/providers/AlertProvider"; -import ButtonCta from "@/components/FirstInstallation/ButtonCta"; -import PapillonSpinner from "@/components/Global/PapillonSpinner"; -import * as TaskManager from "expo-task-manager"; -import { registerBackgroundTasks, unsetBackgroundFetch } from "@/background/BackgroundTasks"; -import { error } from "@/utils/logger/logger"; const SettingsNotifications: Screen<"SettingsNotifications"> = ({ navigation @@ -48,7 +41,6 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ notifications?.enabled || false ); const [loading, setLoading] = useState(false); - const [isBackgroundActive, setIsBackgroundActive] = useState(null); useEffect(() => { const handleNotificationPermission = async () => { @@ -73,24 +65,6 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ handleNotificationPermission(); }, [enabled]); - useEffect(() => { - const checkBackgroundTaskStatus = async () => { - try { - const isRegistered = await TaskManager.isTaskRegisteredAsync("background-fetch"); - setTimeout(() => { - setIsBackgroundActive(isRegistered); - }, 500); - } catch (err) { - error(`❌ Failed to register background task: ${err}`, "BackgroundEvent"); - setIsBackgroundActive(false); - } - }; - - if (!loading) { - checkBackgroundTaskStatus(); - } - }, [isBackgroundActive, loading]); - // Animation states const opacity = useSharedValue(0); const invertedOpacity = useSharedValue(1); @@ -169,121 +143,6 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ {enabled && ( <> - - - - ) : isBackgroundActive === false ? ( - - ) : ( - - ) - } - onPress={() => { - if (Platform.OS === "ios") { - Alert.alert( - "Information", - "Le background permet à Papillon de te connecter à ton compte toutes les 15 minutes et te notifie en fonction des paramètres ci-dessous", - [ - { - text: "OK", - } - ] - ); - } else { - showAlert({ - title: "Information", - message: "Le background permet à Papillon de te connecter à ton compte toutes les 15 minutes et te notifie en fonction des paramètres ci-dessous", - actions: [ - { - title: "OK", - onPress: () => {}, - primary: true, - }, - ], - }); - } - }} - > - - {isBackgroundActive === true - ? "Le background est actuellement actif." - : isBackgroundActive === false - ? "Le background n'est pas actif." - : "Vérification du background..."} - - {isBackgroundActive !== null && ( - { - setLoading(true); - setIsBackgroundActive(null); - if (isBackgroundActive) { - await unsetBackgroundFetch(); - } - - await registerBackgroundTasks(); - setTimeout(() => { - setLoading(false); - }, 500); - }} - /> - )} - - - - - - - - ) : undefined - } - disabled={loading} - primary={!loading} - style={{ - marginTop: 14, - minWidth: null, - maxWidth: null, - width: "75%", - alignSelf: "center", - }} - onPress={async () => { - setLoading(true); - await papillonNotify( - { - id: `${account.name}-test`, - title: `[${account.name}] Coucou, c'est Papillon 👋`, - subtitle: "Test", - body: "Si tu me vois, c'est que tout fonctionne correctement !", - ios: { - categoryId: account.name, - }, - }, - "Test" - ); - setTimeout(() => { - setLoading(false); - }, 500); - }} - /> {notificationSchoolary.map((notification, index) => ( diff --git a/src/views/welcome/DevMenu.tsx b/src/views/welcome/DevMenu.tsx index ba4881e95..57ca0aac1 100644 --- a/src/views/welcome/DevMenu.tsx +++ b/src/views/welcome/DevMenu.tsx @@ -1,6 +1,6 @@ import { useTheme } from "@react-navigation/native"; -import { ChevronRight } from "lucide-react-native"; -import React, { useLayoutEffect } from "react"; +import { CheckCircle2, ChevronRight, CircleAlert } from "lucide-react-native"; +import React, { useEffect, useLayoutEffect, useState } from "react"; import { View, Text, TouchableOpacity, ScrollView, Alert } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { @@ -10,10 +10,37 @@ import { NativeText } from "@/components/Global/NativeComponents"; import AsyncStorage from "@react-native-async-storage/async-storage"; +import PapillonSpinner from "@/components/Global/PapillonSpinner"; +import * as TaskManager from "expo-task-manager"; +import { error } from "@/utils/logger/logger"; +import ButtonCta from "@/components/FirstInstallation/ButtonCta"; +import { registerBackgroundTasks, unsetBackgroundFetch } from "@/background/BackgroundTasks"; +import { isExpoGo } from "@/utils/native/expoGoAlert"; +import { papillonNotify } from "@/background/Notifications"; const DevMenu: Screen<"DevMenu"> = ({ navigation }) => { const theme = useTheme(); const { colors } = theme; + const [loading, setLoading] = useState(false); + const [isBackgroundActive, setIsBackgroundActive] = useState(null); + + useEffect(() => { + const checkBackgroundTaskStatus = async () => { + try { + const isRegistered = await TaskManager.isTaskRegisteredAsync("background-fetch"); + setTimeout(() => { + setIsBackgroundActive(isRegistered); + }, 500); + } catch (err) { + error(`❌ Failed to register background task: ${err}`, "BackgroundEvent"); + setIsBackgroundActive(false); + } + }; + + if (!isExpoGo() && !loading) { + checkBackgroundTaskStatus(); + } + }, [isBackgroundActive, loading]); // add button to header useLayoutEffect(() => { @@ -174,6 +201,92 @@ const DevMenu: Screen<"DevMenu"> = ({ navigation }) => { Logs de l'application
+ {!isExpoGo() && ( + <> + + ) : isBackgroundActive === false ? ( + + ) : ( + + ) + } + > + + {isBackgroundActive === true + ? "Le background est actuellement actif." + : isBackgroundActive === false + ? "Le background n'est pas actif." + : "Vérification du background..."} + + {isBackgroundActive !== null && ( + { + setLoading(true); + setIsBackgroundActive(null); + if (isBackgroundActive) { + await unsetBackgroundFetch(); + } + + await registerBackgroundTasks(); + setTimeout(() => { + setLoading(false); + }, 500); + }} + /> + )} + + + + + ) : undefined + } + disabled={loading} + primary={!loading} + style={{ + marginTop: 14, + minWidth: null, + maxWidth: null, + width: "75%", + alignSelf: "center", + }} + onPress={async () => { + setLoading(true); + await papillonNotify( + { + id: "test", + title: "Coucou, c'est Papillon 👋", + subtitle: "Test", + body: "Si tu me vois, c'est que tout fonctionne correctement !", + }, + "Test" + ); + setTimeout(() => { + setLoading(false); + }, 500); + }} + /> + + )} From eb99d91c799574596a66c1dff5c79f6d85b5e717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 1 Feb 2025 22:53:51 +0100 Subject: [PATCH 0467/1144] =?UTF-8?q?feat:=20simplification=20du=20`if`=20?= =?UTF-8?q?sur=20la=20v=C3=A9rification=20de=20l'activation=20des=20notifi?= =?UTF-8?q?cations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Attendance.ts | 226 +++++++++++++++--------------- src/background/data/Evaluation.ts | 120 ++++++++-------- src/background/data/Grades.ts | 120 ++++++++-------- src/background/data/Homeworks.ts | 198 +++++++++++++------------- src/background/data/Lessons.ts | 212 ++++++++++++++-------------- src/background/data/News.ts | 108 +++++++------- 6 files changed, 492 insertions(+), 492 deletions(-) diff --git a/src/background/data/Attendance.ts b/src/background/data/Attendance.ts index f58954d72..16926e58f 100644 --- a/src/background/data/Attendance.ts +++ b/src/background/data/Attendance.ts @@ -64,141 +64,141 @@ const fetchAttendance = async (): Promise => { const notificationsTypesPermissions = account.personalization.notifications; const { defaultPeriod, attendances } = getAttendance(); - if (notificationsTypesPermissions?.attendance) { - await papillonNotify( - { - id: "statusBackground", - title: account.name, - body: "Récupération des dernières événement de la vie scolaire...", - android: { - progress: { - max: 100, - current: (100 / 6) * 5, - indeterminate: false, - }, + if (!notificationsTypesPermissions?.attendance) { + return attendances[defaultPeriod]; + } + + await papillonNotify( + { + id: "statusBackground", + title: account.name, + body: "Récupération des dernières événement de la vie scolaire...", + android: { + progress: { + max: 100, + current: (100 / 6) * 5, + indeterminate: false, }, }, - "Status" - ); + }, + "Status" + ); - await updateAttendanceState(account, defaultPeriod); - const updatedAttendance = getAttendance().attendances[defaultPeriod]; + await updateAttendanceState(account, defaultPeriod); + const updatedAttendance = getAttendance().attendances[defaultPeriod]; - const differences = getDifferences( - attendances[defaultPeriod], - updatedAttendance - ); - const LAdifference = + const differences = getDifferences( + attendances[defaultPeriod], + updatedAttendance + ); + const LAdifference = differences.absences.length + differences.delays.length + differences.observations.length + differences.punishments.length; - switch (LAdifference) { - case 0: - break; - case 1: - let thenewevent = ""; - let explication = ""; - - if (differences.absences.length === 1) { - const dateAbsencesDebut = formatHoursMinutes(differences.absences[0].fromTimestamp); - - const dateAbsencesFin = formatHoursMinutes(differences.absences[0].toTimestamp); - - thenewevent = "Nouvelle absence"; - explication = `Tu as été absent de ${dateAbsencesDebut} à ${dateAbsencesFin}.`; - } else if (differences.delays.length === 1) { - const dateRetard = formatHoursMinutes(differences.delays[0].timestamp); - - thenewevent = "Nouveau retard"; - explication = `Tu as été en retard de ${differences.delays[0].duration} min à ${dateRetard}.`; - } else if (differences.observations.length === 1) { - const dateObservations = formatHoursMinutes(differences.observations[0].timestamp); - - thenewevent = "Nouvelle observation"; - explication = `Tu as eu une observation en ${ - differences.observations[0].subjectName ?? "Matière inconnue" - } à ${dateObservations}.`; - } else { - thenewevent = "Nouvelle punition"; - explication = ` + switch (LAdifference) { + case 0: + break; + case 1: + let thenewevent = ""; + let explication = ""; + + if (differences.absences.length === 1) { + const dateAbsencesDebut = formatHoursMinutes(differences.absences[0].fromTimestamp); + + const dateAbsencesFin = formatHoursMinutes(differences.absences[0].toTimestamp); + + thenewevent = "Nouvelle absence"; + explication = `Tu as été absent de ${dateAbsencesDebut} à ${dateAbsencesFin}.`; + } else if (differences.delays.length === 1) { + const dateRetard = formatHoursMinutes(differences.delays[0].timestamp); + + thenewevent = "Nouveau retard"; + explication = `Tu as été en retard de ${differences.delays[0].duration} min à ${dateRetard}.`; + } else if (differences.observations.length === 1) { + const dateObservations = formatHoursMinutes(differences.observations[0].timestamp); + + thenewevent = "Nouvelle observation"; + explication = `Tu as eu une observation en ${ + differences.observations[0].subjectName ?? "Matière inconnue" + } à ${dateObservations}.`; + } else { + thenewevent = "Nouvelle punition"; + explication = ` Tu as eu une punition de ${differences.punishments[0].givenBy}.
Raison : ${differences.punishments[0].reason.circumstances} `; - } + } + + await papillonNotify( + { + id: `${account.name}-attendance`, + title: `[${account.name}] ${thenewevent}`, + subtitle: defaultPeriod, + body: explication, + }, + "Attendance" + ); + break; + default: + let LesExplication: string[] = []; - await papillonNotify( - { - id: `${account.name}-attendance`, - title: `[${account.name}] ${thenewevent}`, - subtitle: defaultPeriod, - body: explication, - }, - "Attendance" - ); - break; - default: - let LesExplication: string[] = []; - - if (differences.absences.length > 0) { - if (differences.absences.length === 1) { - LesExplication.push("1 nouvelle absence"); - } else { - LesExplication.push( - `${differences.absences.length} nouvelles absences` - ); - } + if (differences.absences.length > 0) { + if (differences.absences.length === 1) { + LesExplication.push("1 nouvelle absence"); + } else { + LesExplication.push( + `${differences.absences.length} nouvelles absences` + ); } + } - if (differences.delays.length > 0) { - if (differences.delays.length === 1) { - LesExplication.push("1 nouveau retard"); - } else { - LesExplication.push( - `${differences.delays.length} nouveaux retards` - ); - } + if (differences.delays.length > 0) { + if (differences.delays.length === 1) { + LesExplication.push("1 nouveau retard"); + } else { + LesExplication.push( + `${differences.delays.length} nouveaux retards` + ); } + } - if (differences.observations.length > 0) { - if (differences.observations.length === 1) { - LesExplication.push("1 nouvelle observation"); - } else { - LesExplication.push( - `${differences.observations.length} nouvelles observations` - ); - } + if (differences.observations.length > 0) { + if (differences.observations.length === 1) { + LesExplication.push("1 nouvelle observation"); + } else { + LesExplication.push( + `${differences.observations.length} nouvelles observations` + ); } + } - if (differences.punishments.length > 0) { - if (differences.absences.length === 1) { - LesExplication.push("1 nouvelle punition"); - } else { - LesExplication.push( - `${differences.punishments.length} nouvelles punitions` - ); - } + if (differences.punishments.length > 0) { + if (differences.absences.length === 1) { + LesExplication.push("1 nouvelle punition"); + } else { + LesExplication.push( + `${differences.punishments.length} nouvelles punitions` + ); } - - await papillonNotify( - { - id: `${account.name}-attendance`, - title: `[${account.name}] Vie Scolaire`, - subtitle: defaultPeriod, - body: `De nouveaux événements ont été publiés, consulte la vie scolaire pour plus de détails : ${LesExplication.join( - ", " - )}.`, - }, - "Attendance" - ); - break; - } - - return updatedAttendance; + } + + await papillonNotify( + { + id: `${account.name}-attendance`, + title: `[${account.name}] Vie Scolaire`, + subtitle: defaultPeriod, + body: `De nouveaux événements ont été publiés, consulte la vie scolaire pour plus de détails : ${LesExplication.join( + ", " + )}.`, + }, + "Attendance" + ); + break; } - return attendances[defaultPeriod]; + return updatedAttendance; }; export { fetchAttendance }; diff --git a/src/background/data/Evaluation.ts b/src/background/data/Evaluation.ts index f1d35de30..9bd961900 100644 --- a/src/background/data/Evaluation.ts +++ b/src/background/data/Evaluation.ts @@ -22,79 +22,79 @@ const fetchEvaluation = async (): Promise => { const notificationsTypesPermissions = account.personalization.notifications; const { defaultPeriod, evaluation } = getEvaluation(); - if (notificationsTypesPermissions?.evaluation) { - await papillonNotify( - { - id: "statusBackground", - title: account.name, - body: "Récupération des dernières compétences...", - android: { - progress: { - max: 100, - current: (100 / 6) * 6, - indeterminate: false, - }, + if (!notificationsTypesPermissions?.evaluation) { + return evaluation[defaultPeriod]; + } + + await papillonNotify( + { + id: "statusBackground", + title: account.name, + body: "Récupération des dernières compétences...", + android: { + progress: { + max: 100, + current: (100 / 6) * 6, + indeterminate: false, }, }, - "Status" - ); + }, + "Status" + ); - await updateEvaluationState(account, defaultPeriod); - const updatedEvaluation = getEvaluation().evaluation[defaultPeriod]; + await updateEvaluationState(account, defaultPeriod); + const updatedEvaluation = getEvaluation().evaluation[defaultPeriod]; - const differences = getDifferences( - evaluation[defaultPeriod] ?? [], - updatedEvaluation ?? [] - ); + const differences = getDifferences( + evaluation[defaultPeriod] ?? [], + updatedEvaluation ?? [] + ); - switch (differences.length) { - case 0: - break; - case 1: - await papillonNotify( - { - id: `${account.name}-evaluation`, - title: `[${account.name}] Nouvelle compétence en ${differences[0].subjectName}`, - subtitle: defaultPeriod, - body: `Titre : ${ - differences[0].description || "Compétence sans titre" - }, Coefficient : ${differences[0].coefficient}`, - }, - "Evaluation" - ); - break; - default: - const evaluationCounts: Record = {}; + switch (differences.length) { + case 0: + break; + case 1: + await papillonNotify( + { + id: `${account.name}-evaluation`, + title: `[${account.name}] Nouvelle compétence en ${differences[0].subjectName}`, + subtitle: defaultPeriod, + body: `Titre : ${ + differences[0].description || "Compétence sans titre" + }, Coefficient : ${differences[0].coefficient}`, + }, + "Evaluation" + ); + break; + default: + const evaluationCounts: Record = {}; - differences.forEach((grade) => { - evaluationCounts[grade.subjectName] = + differences.forEach((grade) => { + evaluationCounts[grade.subjectName] = (evaluationCounts[grade.subjectName] || 0) + 1; - }); + }); - const evaluationPreview = Object.entries(evaluationCounts) - .map(([subject, count]) => - count > 1 ? `${count}x ${subject}` : subject - ) - .join(", "); + const evaluationPreview = Object.entries(evaluationCounts) + .map(([subject, count]) => + count > 1 ? `${count}x ${subject}` : subject + ) + .join(", "); - await papillonNotify( - { - id: `${account.name}-evaluation`, - subtitle: defaultPeriod, - title: `[${account.name}] Nouvelles compétences`, - body: ` + await papillonNotify( + { + id: `${account.name}-evaluation`, + subtitle: defaultPeriod, + title: `[${account.name}] Nouvelles compétences`, + body: ` ${differences.length} nouvelles compétences :
${evaluationPreview} `, - }, - "Evaluation" - ); - break; - } - return updatedEvaluation; + }, + "Evaluation" + ); + break; } - - return evaluation[defaultPeriod]; + return updatedEvaluation; }; export { fetchEvaluation }; diff --git a/src/background/data/Grades.ts b/src/background/data/Grades.ts index df8122513..dd99cd588 100644 --- a/src/background/data/Grades.ts +++ b/src/background/data/Grades.ts @@ -22,79 +22,79 @@ const fetchGrade = async (): Promise => { const notificationsTypesPermissions = account.personalization.notifications; const { defaultPeriod, grades } = getGrades(); - if (notificationsTypesPermissions?.grades) { - await papillonNotify( - { - id: "statusBackground", - title: account.name, - body: "Récupération des dernières notes...", - android: { - progress: { - max: 100, - current: (100 / 6) * 3, - indeterminate: false, - }, + if (!notificationsTypesPermissions?.grades) { + return grades[defaultPeriod]; + } + + await papillonNotify( + { + id: "statusBackground", + title: account.name, + body: "Récupération des dernières notes...", + android: { + progress: { + max: 100, + current: (100 / 6) * 3, + indeterminate: false, }, }, - "Status" - ); + }, + "Status" + ); - await updateGradeState(account, defaultPeriod); - const updatedGrade = getGrades().grades[defaultPeriod]; + await updateGradeState(account, defaultPeriod); + const updatedGrade = getGrades().grades[defaultPeriod]; - const differences = getDifferences( - grades[defaultPeriod] ?? [], - updatedGrade ?? [] - ); + const differences = getDifferences( + grades[defaultPeriod] ?? [], + updatedGrade ?? [] + ); - switch (differences.length) { - case 0: - break; - case 1: - await papillonNotify( - { - id: `${account.name}-grades`, - title: `[${account.name}] Nouvelle note en ${differences[0].subjectName}`, - subtitle: defaultPeriod, - body: `Titre : ${ - differences[0].description || "Note sans titre" - }, Coefficient : ${differences[0].coefficient}`, - }, - "Grades" - ); - break; - default: - const gradeCounts: Record = {}; + switch (differences.length) { + case 0: + break; + case 1: + await papillonNotify( + { + id: `${account.name}-grades`, + title: `[${account.name}] Nouvelle note en ${differences[0].subjectName}`, + subtitle: defaultPeriod, + body: `Titre : ${ + differences[0].description || "Note sans titre" + }, Coefficient : ${differences[0].coefficient}`, + }, + "Grades" + ); + break; + default: + const gradeCounts: Record = {}; - differences.forEach((grade) => { - gradeCounts[grade.subjectName] = + differences.forEach((grade) => { + gradeCounts[grade.subjectName] = (gradeCounts[grade.subjectName] || 0) + 1; - }); + }); - const gradePreview = Object.entries(gradeCounts) - .map(([subject, count]) => - count > 1 ? `${count}x ${subject}` : subject - ) - .join(", "); + const gradePreview = Object.entries(gradeCounts) + .map(([subject, count]) => + count > 1 ? `${count}x ${subject}` : subject + ) + .join(", "); - await papillonNotify( - { - id: `${account.name}-grades`, - subtitle: defaultPeriod, - title: `[${account.name}] Nouvelles notes`, - body: ` + await papillonNotify( + { + id: `${account.name}-grades`, + subtitle: defaultPeriod, + title: `[${account.name}] Nouvelles notes`, + body: ` ${differences.length} nouvelles notes :
${gradePreview} `, - }, - "Grades" - ); - break; - } - return updatedGrade; + }, + "Grades" + ); + break; } - - return grades[defaultPeriod]; + return updatedGrade; }; export { fetchGrade }; diff --git a/src/background/data/Homeworks.ts b/src/background/data/Homeworks.ts index f07b89064..09eafb074 100644 --- a/src/background/data/Homeworks.ts +++ b/src/background/data/Homeworks.ts @@ -33,129 +33,129 @@ const fetchHomeworks = async (): Promise => { const SemaineAct = dateToEpochWeekNumber(new Date()); const currentHwSemaineActuelle = getHomeworks()[SemaineAct] ?? []; - if (notificationsTypesPermissions?.homeworks) { - await papillonNotify( - { - id: "statusBackground", - title: account.name, - body: "Récupération des derniers devoirs...", - android: { - progress: { - max: 100, - current: (100 / 6) * 2, - indeterminate: false, - }, + if (!notificationsTypesPermissions?.homeworks) { + return currentHwSemaineActuelle; + } + + await papillonNotify( + { + id: "statusBackground", + title: account.name, + body: "Récupération des derniers devoirs...", + android: { + progress: { + max: 100, + current: (100 / 6) * 2, + indeterminate: false, }, }, - "Status" - ); + }, + "Status" + ); - const currentHwSemaineProchaine = getHomeworks()[SemaineAct + 1] ?? []; + const currentHwSemaineProchaine = getHomeworks()[SemaineAct + 1] ?? []; - await updateHomeworksState(account); + await updateHomeworksState(account); - const updatedHwSemaineActuelle = getHomeworks()[SemaineAct] ?? []; - const updatedHwSemaineProchaine = getHomeworks()[SemaineAct + 1] ?? []; + const updatedHwSemaineActuelle = getHomeworks()[SemaineAct] ?? []; + const updatedHwSemaineProchaine = getHomeworks()[SemaineAct + 1] ?? []; - const differencesHwSemaineActuelle = getDifferences( - currentHwSemaineActuelle, - updatedHwSemaineActuelle - ); - const differencesHwSemaineProchaine = getDifferences( - currentHwSemaineProchaine, - updatedHwSemaineProchaine - ); - const differences = + const differencesHwSemaineActuelle = getDifferences( + currentHwSemaineActuelle, + updatedHwSemaineActuelle + ); + const differencesHwSemaineProchaine = getDifferences( + currentHwSemaineProchaine, + updatedHwSemaineProchaine + ); + const differences = differencesHwSemaineActuelle.length + differencesHwSemaineProchaine.length; - switch (differences) { - case 0: - break; - case 1: - if (differencesHwSemaineActuelle.length === 1) { - await papillonNotify( - { - id: `${account.name}-homeworks`, - title: `[${account.name}] Nouveau devoir en ${differencesHwSemaineActuelle[0].subject}`, - subtitle: `Semaine ${( - ((SemaineAct - (firstDateEpoch % 52)) % 52) + + switch (differences) { + case 0: + break; + case 1: + if (differencesHwSemaineActuelle.length === 1) { + await papillonNotify( + { + id: `${account.name}-homeworks`, + title: `[${account.name}] Nouveau devoir en ${differencesHwSemaineActuelle[0].subject}`, + subtitle: `Semaine ${( + ((SemaineAct - (firstDateEpoch % 52)) % 52) + 1 - ).toString()}`, - body: parse_homeworks(differencesHwSemaineActuelle[0].content), - }, - "Homeworks" - ); - } else { - await papillonNotify( - { - id: `${account.name}-homeworks`, - title: `[${account.name}] Nouveau devoir en ${differencesHwSemaineProchaine[0].subject}`, - subtitle: `Semaine ${( - ((SemaineAct - (firstDateEpoch % 52)) % 52) + + ).toString()}`, + body: parse_homeworks(differencesHwSemaineActuelle[0].content), + }, + "Homeworks" + ); + } else { + await papillonNotify( + { + id: `${account.name}-homeworks`, + title: `[${account.name}] Nouveau devoir en ${differencesHwSemaineProchaine[0].subject}`, + subtitle: `Semaine ${( + ((SemaineAct - (firstDateEpoch % 52)) % 52) + 2 - ).toString()}`, - body: parse_homeworks(differencesHwSemaineProchaine[0].content), - }, - "Homeworks" - ); - } - break; - default: - const subjectCounts: Record = {}; + ).toString()}`, + body: parse_homeworks(differencesHwSemaineProchaine[0].content), + }, + "Homeworks" + ); + } + break; + default: + const subjectCounts: Record = {}; - [ - ...differencesHwSemaineActuelle, - ...differencesHwSemaineProchaine, - ].forEach((hw) => { - subjectCounts[hw.subject] = (subjectCounts[hw.subject] || 0) + 1; - }); + [ + ...differencesHwSemaineActuelle, + ...differencesHwSemaineProchaine, + ].forEach((hw) => { + subjectCounts[hw.subject] = (subjectCounts[hw.subject] || 0) + 1; + }); - const subjectPreview = Object.entries(subjectCounts) - .map(([subject, count]) => - count > 1 ? `${count}x ${subject}` : subject - ) - .join(", "); + const subjectPreview = Object.entries(subjectCounts) + .map(([subject, count]) => + count > 1 ? `${count}x ${subject}` : subject + ) + .join(", "); - let subtitle = "Semaine "; + let subtitle = "Semaine "; + if (differencesHwSemaineActuelle.length > 0) { + subtitle += ( + ((SemaineAct - (firstDateEpoch % 52)) % 52) + + 1 + ).toString(); + } + if (differencesHwSemaineProchaine.length > 0) { if (differencesHwSemaineActuelle.length > 0) { - subtitle += ( + subtitle += `et ${( ((SemaineAct - (firstDateEpoch % 52)) % 52) + - 1 - ).toString(); - } - if (differencesHwSemaineProchaine.length > 0) { - if (differencesHwSemaineActuelle.length > 0) { - subtitle += `et ${( - ((SemaineAct - (firstDateEpoch % 52)) % 52) + 2 - ).toString()}`; - } else { - subtitle += ( - ((SemaineAct - (firstDateEpoch % 52)) % 52) + + ).toString()}`; + } else { + subtitle += ( + ((SemaineAct - (firstDateEpoch % 52)) % 52) + 2 - ).toString(); - } + ).toString(); } + } - await papillonNotify( - { - id: `${account.name}-homeworks`, - title: `[${account.name}] Nouveaux devoirs`, - subtitle, - body: ` + await papillonNotify( + { + id: `${account.name}-homeworks`, + title: `[${account.name}] Nouveaux devoirs`, + subtitle, + body: ` ${differences} nouveaux devoirs :
${subjectPreview} `, - }, - "Homeworks" - ); - break; - } - return updatedHwSemaineActuelle ?? updatedHwSemaineProchaine; + }, + "Homeworks" + ); + break; } - - return currentHwSemaineActuelle; + return updatedHwSemaineActuelle ?? updatedHwSemaineProchaine; }; export { fetchHomeworks }; diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index fb39ac61f..5d76c527c 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -25,133 +25,133 @@ const fetchLessons = async (): Promise => { const notificationsTypesPermissions = account.personalization.notifications; const currentLessons = getLessons(); - if (notificationsTypesPermissions?.timetable){ - await papillonNotify( - { - id: "statusBackground", - title: account.name, - body: "Récupération des dernières actualités...", - android: { - progress: { - max: 100, - current: 100 / 6 * 4, - indeterminate: false, - }, + if (!notificationsTypesPermissions?.timetable) { + return getAllLessonsForDay(currentLessons); + } + + await papillonNotify( + { + id: "statusBackground", + title: account.name, + body: "Récupération des dernières actualités...", + android: { + progress: { + max: 100, + current: 100 / 6 * 4, + indeterminate: false, }, }, - "Status" - ); + }, + "Status" + ); - const weekNumber = dateToEpochWeekNumber(new Date()); - await updateLessonsState(account, weekNumber); - const updatedLessons = getLessons(); - const lessonsDay = getAllLessonsForDay(updatedLessons); - const lessonsEvent = lessonsDay.filter((element) => element.statusText); + const weekNumber = dateToEpochWeekNumber(new Date()); + await updateLessonsState(account, weekNumber); + const updatedLessons = getLessons(); + const lessonsDay = getAllLessonsForDay(updatedLessons); + const lessonsEvent = lessonsDay.filter((element) => element.statusText); - // Notifie 15 minutes avant le début du 1er cours - // De base, je voulais faire 1 heure avant, mais la notif sera modifiée à chaque fois - // comme la fréquence du background est de 15 minutes - const now = new Date().getTime(); - const oneHourBefore = lessonsDay[0]?.startTimestamp - 15 * 60 * 1000; + // Notifie 15 minutes avant le début du 1er cours + // De base, je voulais faire 1 heure avant, mais la notif sera modifiée à chaque fois + // comme la fréquence du background est de 15 minutes + const now = new Date().getTime(); + const oneHourBefore = lessonsDay[0]?.startTimestamp - 15 * 60 * 1000; - if ( - now >= oneHourBefore && + if ( + now >= oneHourBefore && now < lessonsDay[0]?.startTimestamp - ) { - switch (lessonsEvent.length) { - case 0: - break; - case 1: - const dateLessonsDebut = `${new Date(lessonsEvent[0].startTimestamp) - .getHours() - .toString() - .padStart(2, "0")}:${new Date(lessonsEvent[0].startTimestamp) - .getMinutes() - .toString() - .padStart(2, "0")}`; + ) { + switch (lessonsEvent.length) { + case 0: + break; + case 1: + const dateLessonsDebut = `${new Date(lessonsEvent[0].startTimestamp) + .getHours() + .toString() + .padStart(2, "0")}:${new Date(lessonsEvent[0].startTimestamp) + .getMinutes() + .toString() + .padStart(2, "0")}`; - const dateLessonsFin = `${new Date(lessonsEvent[0].endTimestamp) - .getHours() - .toString() - .padStart(2, "0")}:${new Date(lessonsEvent[0].endTimestamp) - .getMinutes() - .toString() - .padStart(2, "0")}`; + const dateLessonsFin = `${new Date(lessonsEvent[0].endTimestamp) + .getHours() + .toString() + .padStart(2, "0")}:${new Date(lessonsEvent[0].endTimestamp) + .getMinutes() + .toString() + .padStart(2, "0")}`; - let statut: string = ""; + let statut: string = ""; - switch (lessonsEvent[0].status) { - case TimetableClassStatus.CANCELED: - statut = "a été annulé"; - break; - case TimetableClassStatus.TEST: - statut = "est un devoir surveillé"; - case TimetableClassStatus.MODIFIED: - statut = + switch (lessonsEvent[0].status) { + case TimetableClassStatus.CANCELED: + statut = "a été annulé"; + break; + case TimetableClassStatus.TEST: + statut = "est un devoir surveillé"; + case TimetableClassStatus.MODIFIED: + statut = "est un cours modifié, consulte l'emploi du temps pour plus de détails"; - default: - if (lessonsEvent[0].statusText === "Changement de salle") { - statut = "a un changement de salle ! "; - if (lessonsEvent[0].room) { - if (lessonsEvent[0].room.includes(",")) { - statut += + default: + if (lessonsEvent[0].statusText === "Changement de salle") { + statut = "a un changement de salle ! "; + if (lessonsEvent[0].room) { + if (lessonsEvent[0].room.includes(",")) { + statut += "Consulte l'emploi du temps pour regarder les salles de cours"; - } else { - statut += `Tu dois aller en salle ${lessonsEvent[0].room}`; - } + } else { + statut += `Tu dois aller en salle ${lessonsEvent[0].room}`; } - } else if (lessonsEvent[0].statusText === "Devoir Surveillé") { - statut = "est un devoir surveillé"; - } else { - statut = `a un statut : ${lessonsEvent[0].statusText}`; } - break; - } + } else if (lessonsEvent[0].statusText === "Devoir Surveillé") { + statut = "est un devoir surveillé"; + } else { + statut = `a un statut : ${lessonsEvent[0].statusText}`; + } + break; + } - await papillonNotify( - { - id: `${account.name}-lessons`, - title: `[${account.name}] Emploi du temps du jour modifié`, - subtitle: new Date( - lessonsEvent[0].startTimestamp - ).toLocaleDateString("fr-FR", { - weekday: "long", - day: "numeric", - month: "long", - }), - body: `Le cours de ${lessonsEvent[0].title} (${dateLessonsDebut}-${dateLessonsFin}) ${statut}`, - }, - "Lessons" - ); - break; - default: - await papillonNotify( - { - id: `${account.name}-lessons`, - title: `[${account.name}] Emploi du temps du jour`, - subtitle: new Date( - lessonsEvent[0].startTimestamp - ).toLocaleDateString("fr-FR", { - weekday: "long", - day: "numeric", - month: "long", - }), - body: `Les cours suivants ont été modifiés, consulte l'emploi du temps pour plus de détails.
+ await papillonNotify( + { + id: `${account.name}-lessons`, + title: `[${account.name}] Emploi du temps du jour modifié`, + subtitle: new Date( + lessonsEvent[0].startTimestamp + ).toLocaleDateString("fr-FR", { + weekday: "long", + day: "numeric", + month: "long", + }), + body: `Le cours de ${lessonsEvent[0].title} (${dateLessonsDebut}-${dateLessonsFin}) ${statut}`, + }, + "Lessons" + ); + break; + default: + await papillonNotify( + { + id: `${account.name}-lessons`, + title: `[${account.name}] Emploi du temps du jour`, + subtitle: new Date( + lessonsEvent[0].startTimestamp + ).toLocaleDateString("fr-FR", { + weekday: "long", + day: "numeric", + month: "long", + }), + body: `Les cours suivants ont été modifiés, consulte l'emploi du temps pour plus de détails.
${lessonsEvent .flatMap((element) => { return `- ${element.title}`; }) .join("
")}`, - }, - "Lessons" - ); - break; - } + }, + "Lessons" + ); + break; } - return lessonsEvent; } - - return getAllLessonsForDay(currentLessons); + return lessonsEvent; }; export { fetchLessons }; diff --git a/src/background/data/News.ts b/src/background/data/News.ts index 06f60e2c3..08d840452 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -23,78 +23,78 @@ const fetchNews = async (): Promise => { const notificationsTypesPermissions = account.personalization.notifications; const currentNews = getNews(); - if (notificationsTypesPermissions?.news) { - await papillonNotify( - { - id: "statusBackground", - title: account.name, - body: "Récupération des dernières actualités...", - android: { - progress: { - max: 100, - current: (100 / 6) * 1, - indeterminate: false, - }, + if (!notificationsTypesPermissions?.news) { + return currentNews; + } + + await papillonNotify( + { + id: "statusBackground", + title: account.name, + body: "Récupération des dernières actualités...", + android: { + progress: { + max: 100, + current: (100 / 6) * 1, + indeterminate: false, }, }, - "Status" - ); + }, + "Status" + ); - await updateNewsState(account); - const updatedNews = getNews(); + await updateNewsState(account); + const updatedNews = getNews(); - const differences = getDifferences(currentNews, updatedNews); + const differences = getDifferences(currentNews, updatedNews); - switch (differences.length) { - case 0: - break; - case 1: - await papillonNotify( - { - id: `${account.name}-news`, - title: `[${account.name}] Nouvelle actualité par ${differences[0].author}`, - subtitle: differences[0].title, - body: + switch (differences.length) { + case 0: + break; + case 1: + await papillonNotify( + { + id: `${account.name}-news`, + title: `[${account.name}] Nouvelle actualité par ${differences[0].author}`, + subtitle: differences[0].title, + body: differences[0].content && !differences[0].content.includes(" = {}; + }, + "News" + ); + break; + default: + const newsCounts: Record = {}; - differences.forEach((the_news) => { - newsCounts[the_news.author] = (newsCounts[the_news.author] || 0) + 1; - }); + differences.forEach((the_news) => { + newsCounts[the_news.author] = (newsCounts[the_news.author] || 0) + 1; + }); - const newsPreview = Object.entries(newsCounts) - .map(([subject, count]) => - count > 1 ? `${count}x ${subject}` : subject - ) - .join(", "); + const newsPreview = Object.entries(newsCounts) + .map(([subject, count]) => + count > 1 ? `${count}x ${subject}` : subject + ) + .join(", "); - await papillonNotify( - { - id: `${account.name}-news`, - title: `[${account.name}] Nouvelles actualités`, - body: ` + await papillonNotify( + { + id: `${account.name}-news`, + title: `[${account.name}] Nouvelles actualités`, + body: ` ${differences.length} nouvelles actualités par :
${newsPreview} `, - }, - "News" - ); - break; - } - return updatedNews; + }, + "News" + ); + break; } - - return currentNews; + return updatedNews; }; export { fetchNews }; From 2c1927e1c1f7de3ebebdf57ddfc9234a6fe2f6b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 2 Feb 2025 00:08:13 +0100 Subject: [PATCH 0468/1144] =?UTF-8?q?feat:=20r=C3=A9int=C3=A9gration=20des?= =?UTF-8?q?=20notifications=20de=20l'emploi=20du=20temps=20!=20Et=20ajout?= =?UTF-8?q?=20d'un=20composant=20pour=20formatter=20les=20heures=20et=20ma?= =?UTF-8?q?j=20des=20param=C3=A8tres?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/BackgroundTasks.ts | 4 +- src/background/data/Attendance.ts | 9 +- src/background/data/Lessons.ts | 200 +++++++++++------- src/background/utils/format.ts | 7 + .../Settings/NotificationContainerCard.tsx | 3 +- src/views/settings/SettingsNotifications.tsx | 56 +---- 6 files changed, 137 insertions(+), 142 deletions(-) create mode 100644 src/background/utils/format.ts diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index d9bd0e7ac..a6c96c326 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -9,7 +9,7 @@ import { log, error, warn, info } from "@/utils/logger/logger"; import { getAccounts, getSwitchToFunction } from "./utils/accounts"; import { fetchHomeworks } from "./data/Homeworks"; import { fetchGrade } from "./data/Grades"; -// import { fetchLessons } from "./data/Lessons"; +import { fetchLessons } from "./data/Lessons"; import { fetchAttendance } from "./data/Attendance"; import { fetchEvaluation } from "./data/Evaluation"; import { papillonNotify } from "./Notifications"; @@ -77,10 +77,8 @@ const backgroundFetch = async () => { await fetchHomeworks(); info("▶️ Running background Grades", "BackgroundEvent"); await fetchGrade(); - /* Disabled for now info("▶️ Running background Lessons", "BackgroundEvent"); await fetchLessons(); - */ info("▶️ Running background Attendance", "BackgroundEvent"); await fetchAttendance(); info("▶️ Running background Evaluation", "BackgroundEvent"); diff --git a/src/background/data/Attendance.ts b/src/background/data/Attendance.ts index 16926e58f..72dc64bf7 100644 --- a/src/background/data/Attendance.ts +++ b/src/background/data/Attendance.ts @@ -2,14 +2,7 @@ import { getCurrentAccount } from "../utils/accounts"; import { papillonNotify } from "../Notifications"; import { Attendance } from "@/services/shared/Attendance"; import { getAttendance, updateAttendanceState } from "../utils/attendance"; - -const formatHoursMinutes = (timestamp: number) => { - const LAdate = new Date(timestamp); - const heures = LAdate.getHours().toString().padStart(2, "0"); - const minutes = LAdate.getMinutes().toString().padStart(2, "0"); - - return `${heures}:${minutes}`; -}; +import { formatHoursMinutes } from "../utils/format"; const getDifferences = ( currentAttendance: Attendance, diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index 5d76c527c..0f462aadc 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -2,7 +2,12 @@ import { getCurrentAccount } from "../utils/accounts"; import { papillonNotify } from "../Notifications"; import { getLessons, updateLessonsState } from "../utils/lessons"; import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; -import { Timetable, TimetableClassStatus } from "@/services/shared/Timetable"; +import { + Timetable, + TimetableClass, + TimetableClassStatus, +} from "@/services/shared/Timetable"; +import { formatHoursMinutes } from "../utils/format"; const getAllLessonsForDay = (lessons: Record) => { const date = new Date(); @@ -10,14 +15,25 @@ const getAllLessonsForDay = (lessons: Record) => { const week = dateToEpochWeekNumber(date); const timetable = lessons[week] || []; - const day = timetable.filter((lesson) => { + const lessonsOfDay = timetable.filter((lesson) => { const lessonDate = new Date(lesson.startTimestamp); lessonDate.setHours(0, 0, 0, 0); return lessonDate.getTime() === date.getTime(); }); - return day; + return lessonsOfDay; +}; + +const getDifferences = ( + currentLessons: TimetableClass[], + updatedLessons: TimetableClass[], + compareFn: (a: TimetableClass, b: TimetableClass) => boolean +): TimetableClass[] => { + return updatedLessons.filter( + (updatedItem) => + !currentLessons.some((item) => compareFn(item, updatedItem)) + ); }; const fetchLessons = async (): Promise => { @@ -33,11 +49,11 @@ const fetchLessons = async (): Promise => { { id: "statusBackground", title: account.name, - body: "Récupération des dernières actualités...", + body: "Récupération de l'emploi du temps...", android: { progress: { max: 100, - current: 100 / 6 * 4, + current: (100 / 6) * 4, indeterminate: false, }, }, @@ -48,110 +64,132 @@ const fetchLessons = async (): Promise => { const weekNumber = dateToEpochWeekNumber(new Date()); await updateLessonsState(account, weekNumber); const updatedLessons = getLessons(); - const lessonsDay = getAllLessonsForDay(updatedLessons); - const lessonsEvent = lessonsDay.filter((element) => element.statusText); - - // Notifie 15 minutes avant le début du 1er cours - // De base, je voulais faire 1 heure avant, mais la notif sera modifiée à chaque fois - // comme la fréquence du background est de 15 minutes - const now = new Date().getTime(); - const oneHourBefore = lessonsDay[0]?.startTimestamp - 15 * 60 * 1000; - - if ( - now >= oneHourBefore && - now < lessonsDay[0]?.startTimestamp - ) { - switch (lessonsEvent.length) { - case 0: - break; - case 1: - const dateLessonsDebut = `${new Date(lessonsEvent[0].startTimestamp) - .getHours() - .toString() - .padStart(2, "0")}:${new Date(lessonsEvent[0].startTimestamp) - .getMinutes() - .toString() - .padStart(2, "0")}`; - - const dateLessonsFin = `${new Date(lessonsEvent[0].endTimestamp) - .getHours() - .toString() - .padStart(2, "0")}:${new Date(lessonsEvent[0].endTimestamp) - .getMinutes() - .toString() - .padStart(2, "0")}`; - let statut: string = ""; + const differencesStatus = getDifferences( + getAllLessonsForDay(currentLessons) ?? [], + getAllLessonsForDay(updatedLessons) ?? [], + (a, b) => a.status === b.status && a.statusText === b.statusText + ); + const differencesTimestamp = getDifferences( + getAllLessonsForDay(currentLessons) ?? [], + getAllLessonsForDay(updatedLessons) ?? [], + (a, b) => + a.startTimestamp === b.startTimestamp && a.endTimestamp === b.endTimestamp + ); + const totalDifference = + differencesStatus.length + differencesTimestamp.length; - switch (lessonsEvent[0].status) { - case TimetableClassStatus.CANCELED: - statut = "a été annulé"; - break; - case TimetableClassStatus.TEST: - statut = "est un devoir surveillé"; - case TimetableClassStatus.MODIFIED: - statut = - "est un cours modifié, consulte l'emploi du temps pour plus de détails"; - default: - if (lessonsEvent[0].statusText === "Changement de salle") { - statut = "a un changement de salle ! "; - if (lessonsEvent[0].room) { - if (lessonsEvent[0].room.includes(",")) { - statut += - "Consulte l'emploi du temps pour regarder les salles de cours"; - } else { - statut += `Tu dois aller en salle ${lessonsEvent[0].room}`; - } - } - } else if (lessonsEvent[0].statusText === "Devoir Surveillé") { - statut = "est un devoir surveillé"; - } else { - statut = `a un statut : ${lessonsEvent[0].statusText}`; - } - break; - } + switch (totalDifference) { + case 0: + break; + case 1: + if (differencesTimestamp.length === 1) { + const dateLessonsDebut = formatHoursMinutes( + differencesTimestamp[0].startTimestamp + ); + const dateLessonsFin = formatHoursMinutes( + differencesTimestamp[0].endTimestamp + ); await papillonNotify( { id: `${account.name}-lessons`, - title: `[${account.name}] Emploi du temps du jour modifié`, + title: `[${account.name}] EdT modifié`, subtitle: new Date( - lessonsEvent[0].startTimestamp + differencesTimestamp[0].startTimestamp ).toLocaleDateString("fr-FR", { weekday: "long", day: "numeric", month: "long", }), - body: `Le cours de ${lessonsEvent[0].title} (${dateLessonsDebut}-${dateLessonsFin}) ${statut}`, + body: `${differencesStatus[0].subject} (${dateLessonsDebut}-${dateLessonsFin}) : Horaire du cours modifié`, }, "Lessons" ); - break; - default: + } else { + const dateLessonsDebut = formatHoursMinutes( + differencesStatus[0].startTimestamp + ); + const dateLessonsFin = formatHoursMinutes( + differencesStatus[0].endTimestamp + ); + + let statut: string = ""; + + switch (differencesTimestamp[0].status) { + case TimetableClassStatus.TEST: + statut = "Devoir surveillé"; + break; + case TimetableClassStatus.MODIFIED: + statut = "Cours modifié, ouvrir pour plus de détails"; + break; + default: + if (differencesStatus[0].statusText === "Changement de salle") { + statut = "Changement de salle"; + if (differencesStatus[0].room) { + if (differencesStatus[0].room.includes(",")) { + statut += ", ouvrir pour plus de détails"; + } else { + statut += ` ➡️ ${differencesStatus[0].room}`; + } + } + } else if (differencesStatus[0].statusText === "Devoir Surveillé") { + statut = "Devoir surveillé"; + } else { + statut = differencesStatus[0].statusText ?? ""; + } + break; + } + await papillonNotify( { id: `${account.name}-lessons`, - title: `[${account.name}] Emploi du temps du jour`, + title: `[${account.name}] EdT modifié`, subtitle: new Date( - lessonsEvent[0].startTimestamp + differencesStatus[0].startTimestamp ).toLocaleDateString("fr-FR", { weekday: "long", day: "numeric", month: "long", }), - body: `Les cours suivants ont été modifiés, consulte l'emploi du temps pour plus de détails.
- ${lessonsEvent - .flatMap((element) => { - return `- ${element.title}`; - }) - .join("
")}`, + body: `${differencesStatus[0].subject} (${dateLessonsDebut}-${dateLessonsFin}) : ${statut}`, }, "Lessons" ); - break; - } + } + break; + default: + const lessonsCounts: Record = {}; + + [...differencesStatus, ...differencesTimestamp].forEach((hw) => { + lessonsCounts[hw.title] = (lessonsCounts[hw.title] || 0) + 1; + }); + + const lessonsPreview = Object.entries(lessonsCounts) + .map(([subject, count]) => + count > 1 ? `${count}x ${subject}` : subject + ) + .join(", "); + + await papillonNotify( + { + id: `${account.name}-lessons`, + title: `[${account.name}] EdT modifié`, + subtitle: new Date( + differencesStatus[0].startTimestamp + ).toLocaleDateString("fr-FR", { + weekday: "long", + day: "numeric", + month: "long", + }), + body: `${totalDifference} cours modifiés :
+ ${lessonsPreview}`, + }, + "Lessons" + ); + break; } - return lessonsEvent; + return differencesStatus ?? differencesTimestamp; }; export { fetchLessons }; diff --git a/src/background/utils/format.ts b/src/background/utils/format.ts new file mode 100644 index 000000000..ec7880882 --- /dev/null +++ b/src/background/utils/format.ts @@ -0,0 +1,7 @@ +export const formatHoursMinutes = (timestamp: number) => { + const LAdate = new Date(timestamp); + const heures = LAdate.getHours().toString().padStart(2, "0"); + const minutes = LAdate.getMinutes().toString().padStart(2, "0"); + + return `${heures}:${minutes}`; +}; diff --git a/src/components/Settings/NotificationContainerCard.tsx b/src/components/Settings/NotificationContainerCard.tsx index 67eb653cc..16ec1367e 100644 --- a/src/components/Settings/NotificationContainerCard.tsx +++ b/src/components/Settings/NotificationContainerCard.tsx @@ -145,8 +145,7 @@ const NotificationContainerCard = ({ numberOfLines={2} style={[styles.message, textAnimatedStyle]} > - Le cours de géographie (16:00-17:00) a un changement de salle - ! Tu dois aller en salle B106 + Géographie (16:00-17:00) : Changement de salle ➡️ B106 diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 35c6bded4..0495854d0 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -1,13 +1,12 @@ import React, { useEffect, useState } from "react"; -import { Alert, Platform, ScrollView, Switch } from "react-native"; +import { ScrollView, Switch } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { - // CalendarCheck, + CalendarCheck, BookCheck, TrendingUp, Newspaper, - Info, NotepadText, BookPlus } from "lucide-react-native"; @@ -22,7 +21,6 @@ import { import NotificationContainerCard from "@/components/Settings/NotificationContainerCard"; import { createChannelNotification, requestNotificationPermission } from "@/background/Notifications"; import { useCurrentAccount } from "@/stores/account"; -import { PressableScale } from "react-native-pressable-scale"; import { useAlert } from "@/providers/AlertProvider"; const SettingsNotifications: Screen<"SettingsNotifications"> = ({ @@ -87,12 +85,12 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ // Schoolary notifications const notificationSchoolary = [ - // { - // icon: } color={colors.primary} />, - // title: "Emploi du temps du jour modifié", - // message: "Le cours de Musique (10:00-11:00) a été annulé", - // personalizationValue: "timetable", - // }, + { + icon: } color={colors.primary} />, + title: "EdT modifié", + message: "Musique (10:00-11:00) : Prof. absent", + personalizationValue: "timetable", + }, { icon: } color={colors.primary} />, title: "Nouveau devoir", @@ -176,44 +174,6 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ > {notification.title} - {notification.personalizationValue === "timetable" && ( - { - if (Platform.OS === "ios") { - Alert.alert( - "Information", - "Pour le moment, tu es prévenu de ton emploi du temps modifié uniquement 15 minutes avant le 1er cours de la journée.", - [ - { - text: "OK", - } - ] - ); - } else { - showAlert({ - title: "Information", - message: "Pour le moment, tu es prévenu de ton emploi du temps modifié uniquement 15 minutes avant le 1er cours de la journée.", - actions: [ - { - title: "OK", - onPress: () => {}, - primary: true, - }, - ], - }); - } - }} - > - - - )} Date: Sun, 2 Feb 2025 18:24:50 +0100 Subject: [PATCH 0469/1144] fix(ts): fixed types in account store --- src/stores/account/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index 11eb6482c..b63f7f366 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -9,7 +9,7 @@ import { Account, AccountService, ExternalAccount, - PrimaryAccount + PrimaryAccount, PapillonMultiServiceSpace } from "@/stores/account/types"; import { reload } from "@/services/reload-account"; import { useTimetableStore } from "../timetable"; @@ -216,7 +216,7 @@ export const useAccounts = create()( // 2. If a multi-service has no more associated accounts, it must be deleted (because a space is like a "group" of accounts, and without any associated accounts it does not work anymore⁾ // Fetching the accounts corresponding to spaces - const spacesAccounts = get().accounts.filter(account => account.service === AccountService.PapillonMultiService); + const spacesAccounts: PapillonMultiServiceSpace[] = get().accounts.filter(account => account.service === AccountService.PapillonMultiService) as PapillonMultiServiceSpace[]; for (const spaceAccount of spacesAccounts) { // The account deleted above is associated to this space From 24445343557217aabeba0a84dcfc67ab61acaa60 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sun, 2 Feb 2025 18:33:08 +0100 Subject: [PATCH 0470/1144] fix: updates package-lock.json --- package-lock.json | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index a18f60629..09a1c1801 100644 --- a/package-lock.json +++ b/package-lock.json @@ -71,7 +71,7 @@ "openid-client": "^5.7.0", "pawdirecte": "^1.7.1", "pawnilim": "^0.2.0", - "pawnote": "^1.3.4", + "pawnote": "^1.4.1", "pawrd": "^0.6.1", "react": "18.2.0", "react-native": "^0.74.3", @@ -13939,10 +13939,9 @@ } }, "node_modules/pawnote": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/pawnote/-/pawnote-1.3.4.tgz", - "integrity": "sha512-8VMb3vA09z9kM9V2oMCJDNMBOK71gn9p5r2QqoGy/IE5g5Pn2vYR9gaVBmpEgahETz7mGw1mUMzXZeSm212s9w==", - "license": "GPL-3.0-or-later", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/pawnote/-/pawnote-1.4.1.tgz", + "integrity": "sha512-jdFHfyZ7tIRJ03etegs/ExgW2F2KZDPbOq6atjs6Fi1DINl893QLfVIxLwADb+tGQbYifYwwOurv4QZQ1OJZ8Q==", "dependencies": { "@literate.ink/utilities": "1.0.0-10641118381.1", "html-entities": "^2.5.2", From c5412d270e1a2546acead131715df9cab2fc1b70 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sun, 2 Feb 2025 18:46:23 +0100 Subject: [PATCH 0471/1144] fix: switching the typecheck workflow to npm (which is the project's package manager) --- .github/workflows/checks.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 30d27a45c..34849576d 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -18,13 +18,13 @@ jobs: - name: Checkout code uses: actions/checkout@v3 - - name: Pnpm setup - uses: pnpm/action-setup@v2 + - name: NPM setup + uses: actions/setup-node@v2 with: - version: latest + version: 20 - name: Environnement setup - run: pnpm install + run: npm install - name: Typescript and Eslint scanning - run: pnpm lint + run: npm run lint From da3823c77458614932e98a2394f3823cd13a28b6 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sun, 2 Feb 2025 18:55:21 +0100 Subject: [PATCH 0472/1144] fix(deps): updated `react-native-gesture-handler` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 68c198693..a40545cc7 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "react-native-barcode-svg": "^0.0.15", "react-native-draggable-flatlist": "^4.0.1", "react-native-draglist": "^3.6.1", - "react-native-gesture-handler": "^2.21.2", + "react-native-gesture-handler": "^2.22.1", "react-native-htmlview": "^0.17.0", "react-native-infinite-pager": "^0.3.16", "react-native-pager-view": "6.3.0", From d4c7e08d517a6fdded2b286a8ed85237ab803bca Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sun, 2 Feb 2025 19:15:13 +0100 Subject: [PATCH 0473/1144] fix(deps): updated `react-native-gesture-handler` maybe it will resolve ts --- package-lock.json | 16 +++++++--------- package.json | 4 ++-- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0999a01e8..b7d569d74 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.8.2", + "version": "7.9.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.8.2", + "version": "7.9.0", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.3", "@candlefinance/app-icon": "^0.4.5", @@ -78,7 +78,7 @@ "react-native-barcode-svg": "^0.0.15", "react-native-draggable-flatlist": "^4.0.1", "react-native-draglist": "^3.6.1", - "react-native-gesture-handler": "~2.16.1", + "react-native-gesture-handler": "^2.22.1", "react-native-htmlview": "^0.17.0", "react-native-infinite-pager": "^0.3.16", "react-native-pager-view": "6.3.0", @@ -14720,15 +14720,13 @@ } }, "node_modules/react-native-gesture-handler": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.16.2.tgz", - "integrity": "sha512-vGFlrDKlmyI+BT+FemqVxmvO7nqxU33cgXVsn6IKAFishvlG3oV2Ds67D5nPkHMea8T+s1IcuMm0bF8ntZtAyg==", + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.22.1.tgz", + "integrity": "sha512-E0C9D+Ia2UZYevoSV9rTKjhFWEVdR/3l4Z3TUoQrI/wewgzDlmJOrYvGW5aMlPUuQF2vHQOdFfAWhVEqFu4tWw==", "dependencies": { "@egjs/hammerjs": "^2.0.17", "hoist-non-react-statics": "^3.3.0", - "invariant": "^2.2.4", - "lodash": "^4.17.21", - "prop-types": "^15.7.2" + "invariant": "^2.2.4" }, "peerDependencies": { "react": "*", diff --git a/package.json b/package.json index 2b2295f29..a40545cc7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "papillonvex", - "version": "7.8.2", + "version": "7.9.0", "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", @@ -80,7 +80,7 @@ "react-native-barcode-svg": "^0.0.15", "react-native-draggable-flatlist": "^4.0.1", "react-native-draglist": "^3.6.1", - "react-native-gesture-handler": "~2.16.1", + "react-native-gesture-handler": "^2.22.1", "react-native-htmlview": "^0.17.0", "react-native-infinite-pager": "^0.3.16", "react-native-pager-view": "6.3.0", From 13d1b63987e155e9cc539cc0d4ff2a98a841ee60 Mon Sep 17 00:00:00 2001 From: Bulgus <88266443+Bulgus@users.noreply.github.com> Date: Mon, 3 Feb 2025 15:53:44 +0100 Subject: [PATCH 0474/1144] recreate function call and GetDefaultProfilePicture.tsx --- src/views/settings/SettingsProfile.tsx | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index 6b99a2b1e..eb0d3d41d 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -10,7 +10,7 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import * as Clipboard from "expo-clipboard"; import { TouchableOpacity } from "react-native-gesture-handler"; -//import { getDefaultProfilePicture } from "@/utils/GetProfilePicture"; +import { getDefaultProfilePicture } from "@/utils/GetRessources/GetDefaultProfilePicture"; const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { const theme = useTheme(); @@ -27,17 +27,6 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { const [firstName, setFirstName] = useState(account.studentName?.first ?? ""); const [lastName, setLastName] = useState(account.studentName?.last ?? ""); - console.warn(account) - - async function resetProfilePic(account: Account) { - const initialPic = await getDefaultProfilePicture(account); - - setProfilePic(initialPic); - mutateProperty("personalization", { - ...account.personalization, - profilePictureB64: initialPic, - }); - } // on name change, update the account name useEffect(() => { @@ -61,6 +50,16 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { const [profilePic, setProfilePic] = useState(account.personalization.profilePictureB64); const [loadingPic, setLoadingPic] = useState(false); + const resetProfilePic = async (setProfilePic: (pic: string | undefined) => void, setLoadingPic: (loading: boolean) => void) => { + const defaultPic = await getDefaultProfilePicture(account); + setProfilePic(defaultPic); + + mutateProperty("personalization", { + ...account.personalization, + profilePictureB64: defaultPic, + }); + }; + const updateProfilePic = async () => { setLoadingPic(true); From 3dc12753cf5eeee994d9574c209de2304b01ebc3 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 3 Feb 2025 22:41:11 +0100 Subject: [PATCH 0475/1144] =?UTF-8?q?feat:=20mise=20=C3=A0=20jour=20de=20l?= =?UTF-8?q?a=20d=C3=A9pendance=20expo-blur=20vers=20la=20version=2014.0.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 13 ++++++++----- package.json | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0999a01e8..a1a2ae2fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,7 @@ "expo-av": "~14.0.6", "expo-background-fetch": "~12.0.1", "expo-barcode-scanner": "~13.0.1", - "expo-blur": "~13.0.2", + "expo-blur": "~14.0.3", "expo-brightness": "~12.0.1", "expo-camera": "~15.0.13", "expo-clipboard": "~6.0.3", @@ -9101,11 +9101,14 @@ } }, "node_modules/expo-blur": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/expo-blur/-/expo-blur-13.0.2.tgz", - "integrity": "sha512-t2p7BChO3Reykued++QJRMZ/og6J3aXtSQ+bU31YcBeXhZLkHwjWEhiPKPnJka7J2/yTs4+jOCNDY0kCZmcE3w==", + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/expo-blur/-/expo-blur-14.0.3.tgz", + "integrity": "sha512-BL3xnqBJbYm3Hg9t/HjNjdeY7N/q8eK5tsLYxswWG1yElISWZmMvrXYekl7XaVCPfyFyz8vQeaxd7q74ZY3Wrw==", + "license": "MIT", "peerDependencies": { - "expo": "*" + "expo": "*", + "react": "*", + "react-native": "*" } }, "node_modules/expo-brightness": { diff --git a/package.json b/package.json index 2b2295f29..93181d71d 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "expo-av": "~14.0.6", "expo-background-fetch": "~12.0.1", "expo-barcode-scanner": "~13.0.1", - "expo-blur": "~13.0.2", + "expo-blur": "~14.0.3", "expo-brightness": "~12.0.1", "expo-camera": "~15.0.13", "expo-clipboard": "~6.0.3", From 4a7c6a44676a90c5d7b4ec4526f87aa3c724d118 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 3 Feb 2025 23:04:00 +0100 Subject: [PATCH 0476/1144] =?UTF-8?q?feat:=20ajout=20de=20la=20gestion=20d?= =?UTF-8?q?e=20l'enregistrement=20des=20t=C3=A2ches=20en=20arri=C3=A8re-pl?= =?UTF-8?q?an=20avec=20gestion=20des=20erreurs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/BackgroundTasks.ts | 32 ++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index a6c96c326..75ef2a732 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -106,6 +106,15 @@ const unsetBackgroundFetch = async () => { log("✅ Background task unregistered", "BackgroundEvent"); }; +const setBackgroundFetch = async () => { + await BackgroundFetch.registerTaskAsync("background-fetch", { + minimumInterval: 60 * 15, + stopOnTerminate: false, + startOnBoot: true, + }); + log("✅ Background task registered", "BackgroundEvent"); +}; + const registerBackgroundTasks = async () => { const isRegistered = await TaskManager.isTaskRegisteredAsync( "background-fetch" @@ -116,19 +125,20 @@ const registerBackgroundTasks = async () => { "⚠️ Background task already registered. Unregister background task...", "BackgroundEvent" ); - await unsetBackgroundFetch(); - } - - try { - await BackgroundFetch.registerTaskAsync("background-fetch", { - minimumInterval: 60 * 15, - stopOnTerminate: false, - startOnBoot: true, + await unsetBackgroundFetch().catch((ERRfatal) => { + error( + `❌ Failed to unregister background task: ${ERRfatal}`, + "BackgroundEvent" + ); }); - log("✅ Background task registered", "BackgroundEvent"); - } catch (err) { - error(`❌ Failed to register background task: ${err}`, "BackgroundEvent"); } + + await setBackgroundFetch().catch((ERRfatal) => { + error( + `❌ Failed to register background task: ${ERRfatal}`, + "BackgroundEvent" + ); + }); }; export { registerBackgroundTasks, unsetBackgroundFetch }; From 71f5601100e075d85ccab75de035456226825ad1 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 3 Feb 2025 23:35:20 +0100 Subject: [PATCH 0477/1144] =?UTF-8?q?fix:=20ajout=20d'une=20condition=20po?= =?UTF-8?q?ur=20ne=20pas=20notifier=20si=20les=20anciennes=20donn=C3=A9es?= =?UTF-8?q?=20sont=20vides?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Lessons.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index 0f462aadc..607fb0e33 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -65,6 +65,13 @@ const fetchLessons = async (): Promise => { await updateLessonsState(account, weekNumber); const updatedLessons = getLessons(); + if ( + getAllLessonsForDay(currentLessons).length === 0 && + getAllLessonsForDay(currentLessons).length !== 0 + ) { + return getAllLessonsForDay(updatedLessons); + } + const differencesStatus = getDifferences( getAllLessonsForDay(currentLessons) ?? [], getAllLessonsForDay(updatedLessons) ?? [], From 8e118a2647dfb59db6d977d6c42f77d57201416e Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 4 Feb 2025 14:11:02 +0100 Subject: [PATCH 0478/1144] =?UTF-8?q?Revert=20"feat:=20mise=20=C3=A0=20jou?= =?UTF-8?q?r=20de=20la=20d=C3=A9pendance=20expo-blur=20vers=20la=20version?= =?UTF-8?q?=2014.0.3"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 3dc12753cf5eeee994d9574c209de2304b01ebc3. --- package-lock.json | 13 +++++-------- package.json | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index a1a2ae2fc..0999a01e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,7 @@ "expo-av": "~14.0.6", "expo-background-fetch": "~12.0.1", "expo-barcode-scanner": "~13.0.1", - "expo-blur": "~14.0.3", + "expo-blur": "~13.0.2", "expo-brightness": "~12.0.1", "expo-camera": "~15.0.13", "expo-clipboard": "~6.0.3", @@ -9101,14 +9101,11 @@ } }, "node_modules/expo-blur": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/expo-blur/-/expo-blur-14.0.3.tgz", - "integrity": "sha512-BL3xnqBJbYm3Hg9t/HjNjdeY7N/q8eK5tsLYxswWG1yElISWZmMvrXYekl7XaVCPfyFyz8vQeaxd7q74ZY3Wrw==", - "license": "MIT", + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/expo-blur/-/expo-blur-13.0.2.tgz", + "integrity": "sha512-t2p7BChO3Reykued++QJRMZ/og6J3aXtSQ+bU31YcBeXhZLkHwjWEhiPKPnJka7J2/yTs4+jOCNDY0kCZmcE3w==", "peerDependencies": { - "expo": "*", - "react": "*", - "react-native": "*" + "expo": "*" } }, "node_modules/expo-brightness": { diff --git a/package.json b/package.json index 93181d71d..2b2295f29 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "expo-av": "~14.0.6", "expo-background-fetch": "~12.0.1", "expo-barcode-scanner": "~13.0.1", - "expo-blur": "~14.0.3", + "expo-blur": "~13.0.2", "expo-brightness": "~12.0.1", "expo-camera": "~15.0.13", "expo-clipboard": "~6.0.3", From d4c3efdda45891fca507b1403c1ae08f86bfcd36 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 4 Feb 2025 15:10:22 +0100 Subject: [PATCH 0479/1144] =?UTF-8?q?Revert=20"feat:=20ouverture=20de=20la?= =?UTF-8?q?=20page=20directe=20des=20param=C3=A8tres=20des=20notifications?= =?UTF-8?q?=20sur=20Android=20(impossible=20sur=20iOS)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit f9ae1be540dbb3e506982543690fafbba9f594ab. --- .../Settings/NotificationContainerCard.tsx | 34 ++++--------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/src/components/Settings/NotificationContainerCard.tsx b/src/components/Settings/NotificationContainerCard.tsx index 16ec1367e..00c95573d 100644 --- a/src/components/Settings/NotificationContainerCard.tsx +++ b/src/components/Settings/NotificationContainerCard.tsx @@ -8,7 +8,6 @@ import { Platform, Alert, Linking, - NativeModules, } from "react-native"; import LottieView from "lottie-react-native"; import Reanimated, { @@ -37,15 +36,7 @@ const openNotificationSettings = () => { if (Platform.OS === "ios") { Linking.openURL("app-settings:"); } else { - const { getConstants } = NativeModules.RNDeviceInfo || {}; - const packageName = getConstants?.().bundleId || "xyz.getpapillon.app"; - Linking.openSettings(); - setTimeout(() => { - Linking.openURL( - `android.settings.APP_NOTIFICATION_SETTINGS?package=${packageName}` - ); - }, 500); } }; @@ -53,7 +44,7 @@ const NotificationContainerCard = ({ theme, isEnable, setEnabled, - navigation, + navigation }: NotificationContainerCardProps) => { const { colors } = theme; @@ -97,19 +88,10 @@ const NotificationContainerCard = ({ return ( - + - - + + - - Papillon - - - il y a 22 min - + Papillon + il y a 22 min Date: Tue, 4 Feb 2025 15:11:49 +0100 Subject: [PATCH 0480/1144] Suppression `pressAction` pour Android uniquement --- src/background/Notifications.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index 0fdf8f14b..0e5d51ad5 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -131,13 +131,6 @@ const papillonNotify = async ( showChronometer: channelId === "Status" ? true : false, smallIcon: "@mipmap/ic_launcher_foreground", color: "#32AB8E", - pressAction: - channelId !== "Test" && channelId !== "Status" - ? { - id: "default", - launchActivity: "xyz.getpapillon.app.MainActivity", - } - : undefined, // à intégrer => `actions` }, ios: { From e65b8a33375c72a351f14be85fbe7a54ab57e33e Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 4 Feb 2025 15:41:56 +0100 Subject: [PATCH 0481/1144] =?UTF-8?q?feat:=20mise=20=C3=A0=20jour=20du=20t?= =?UTF-8?q?ypage=20de=20`PapillonNavigation`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Home/RedirectButton.tsx | 7 +++---- src/router/helpers/types.ts | 3 ++- src/router/refs.ts | 5 ++++- src/views/account/Grades/Subject/GradeItem.tsx | 6 +++--- src/views/account/Home/Elements/AttendanceElement.tsx | 2 +- src/views/account/Home/Elements/PopupRestauration.tsx | 2 +- src/views/account/Lessons/Atoms/Item.tsx | 2 +- 7 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/components/Home/RedirectButton.tsx b/src/components/Home/RedirectButton.tsx index 7a1915a8b..793929f94 100644 --- a/src/components/Home/RedirectButton.tsx +++ b/src/components/Home/RedirectButton.tsx @@ -1,13 +1,12 @@ import type React from "react"; import { View, Text } from "react-native"; import { ArrowUpRight } from "lucide-react-native"; -import { useTheme } from "@react-navigation/native"; +import { NavigationContainerRef, useTheme } from "@react-navigation/native"; import { TouchableOpacity } from "react-native-gesture-handler"; import type { RouteParameters } from "@/router/helpers/types"; -import type { NativeStackNavigationProp } from "@react-navigation/native-stack"; interface RedirectButtonProps { - navigation: NativeStackNavigationProp + navigation: NavigationContainerRef | null, redirect: keyof RouteParameters } @@ -18,7 +17,7 @@ const RedirectButton: React.FC = ({ navigation, redirect }) return ( navigation.navigate(redirect)} + onPress={() => navigation?.navigate(redirect)} > (); +import { RouteParameters } from "./helpers/types"; +export const PapillonNavigation = + createRef>(); diff --git a/src/views/account/Grades/Subject/GradeItem.tsx b/src/views/account/Grades/Subject/GradeItem.tsx index 8493eeb9a..9cb2b91ee 100644 --- a/src/views/account/Grades/Subject/GradeItem.tsx +++ b/src/views/account/Grades/Subject/GradeItem.tsx @@ -3,13 +3,13 @@ import { View, Text, StyleSheet } from "react-native"; import { NativeItem, NativeText } from "@/components/Global/NativeComponents"; import { getSubjectData } from "@/services/shared/Subject"; import type { Grade } from "@/services/shared/Grade"; -import type { NativeStackNavigationProp } from "@react-navigation/native-stack"; import type { RouteParameters } from "@/router/helpers/types"; +import { NavigationContainerRef } from "@react-navigation/native"; interface GradeItemProps { subject: { average: { subjectName: string }; grades: any[] }; grade: Grade; - navigation: NativeStackNavigationProp; + navigation: NavigationContainerRef | null; index: number; totalItems: number; allGrades: Grade[]; @@ -43,7 +43,7 @@ const GradeItem: React.FC = ({ return ( navigation.navigate("GradeDocument", { grade, allGrades })} + onPress={() => navigation?.navigate("GradeDocument", { grade, allGrades })} chevron={false} animated leading={ diff --git a/src/views/account/Home/Elements/AttendanceElement.tsx b/src/views/account/Home/Elements/AttendanceElement.tsx index f9c86e928..56efb467d 100644 --- a/src/views/account/Home/Elements/AttendanceElement.tsx +++ b/src/views/account/Home/Elements/AttendanceElement.tsx @@ -128,7 +128,7 @@ const AttendanceElement: React.FC = ({ onImportance }) = )} /> PapillonNavigation.current.navigate("Attendance")} + onPress={() => PapillonNavigation.current?.navigate("Attendance")} > {totalMissed && } diff --git a/src/views/account/Home/Elements/PopupRestauration.tsx b/src/views/account/Home/Elements/PopupRestauration.tsx index ab1b0cbe0..f3b778627 100644 --- a/src/views/account/Home/Elements/PopupRestauration.tsx +++ b/src/views/account/Home/Elements/PopupRestauration.tsx @@ -63,7 +63,7 @@ const PopupRestauration: React.FC = ({ onImportance }) = PapillonNavigation.current.navigate("SettingStack")} + onPress={() => PapillonNavigation.current?.navigate("SettingStack")} > Configurer diff --git a/src/views/account/Lessons/Atoms/Item.tsx b/src/views/account/Lessons/Atoms/Item.tsx index 7341922b6..1c9c6daf8 100644 --- a/src/views/account/Lessons/Atoms/Item.tsx +++ b/src/views/account/Lessons/Atoms/Item.tsx @@ -50,7 +50,7 @@ export const TimetableItem: React.FC<{ style={[styles.detailsContainer, { backgroundColor: colors.card, borderColor: colors.text + "33" }]} underlayColor={colors.text + "11"} onPress={() => { - PapillonNavigation.current.navigate("LessonDocument", { lesson: item }); + PapillonNavigation.current?.navigate("LessonDocument", { lesson: item }); }} > From d8dc9d28f0cae75cb9e0b08c4a233121f37e238f Mon Sep 17 00:00:00 2001 From: Bulgus <88266443+Bulgus@users.noreply.github.com> Date: Tue, 4 Feb 2025 21:03:25 +0100 Subject: [PATCH 0482/1144] wip --- src/views/settings/SettingsProfile.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index 4eb2093ea..aaf940124 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -50,15 +50,16 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { const [profilePic, setProfilePic] = useState(account.personalization.profilePictureB64); const [loadingPic, setLoadingPic] = useState(false); - const resetProfilePic = async (setProfilePic: (pic: string | undefined) => void, setLoadingPic: (loading: boolean) => void) => { + const resetProfilePic = async (setProfilePic: (pic: string | undefined) => void, setLoadingPic: (loading: boolean) => void) => { + console.warn("HERE") const defaultPic = await getDefaultProfilePicture(account); setProfilePic(defaultPic); - + console.warn("HERE2") mutateProperty("personalization", { ...account.personalization, profilePictureB64: defaultPic, }); - }; + }; const updateProfilePic = async () => { setLoadingPic(true); From 39d282768d4b4f70af526e9e40ef3dc2273c5dc2 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 5 Feb 2025 15:20:12 +0100 Subject: [PATCH 0483/1144] =?UTF-8?q?feat:=20redirection=20de=20la=20notif?= =?UTF-8?q?ication=20sur=20le=20compte=20et=20la=20page=20sp=C3=A9cifique?= =?UTF-8?q?=20!=20(News=20uniquement=20pour=20le=20moment)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.tsx | 35 +++++++++++++++++++++++++++++++- src/background/Notifications.ts | 5 ++++- src/background/data/News.ts | 8 ++++++++ src/background/utils/accounts.ts | 5 +++++ 4 files changed, 51 insertions(+), 2 deletions(-) diff --git a/App.tsx b/App.tsx index 7f12cf6cd..efd799982 100644 --- a/App.tsx +++ b/App.tsx @@ -1,5 +1,5 @@ import "@/background/BackgroundTasks"; -import notifee, { EventType } from "@notifee/react-native"; +import notifee, { EventType, Notification } from "@notifee/react-native"; import Router from "@/router"; import { useFonts } from "expo-font"; import * as SplashScreen from "expo-splash-screen"; @@ -12,6 +12,9 @@ import { log } from "@/utils/logger/logger"; import { isExpoGo } from "@/utils/native/expoGoAlert"; import { atobPolyfill, btoaPolyfill } from "js-base64"; import { registerBackgroundTasks } from "@/background/BackgroundTasks"; +import { PapillonNavigation } from "@/router/refs"; +import { RouteParameters } from "@/router/helpers/types"; +import { findAccountByID, getSwitchToFunction } from "@/background/utils/accounts"; SplashScreen.preventAutoHideAsync(); @@ -24,6 +27,36 @@ const BACKGROUND_LIMITS: Partial> = { }; export default function App () { + const handleNotificationPress = async (notification: Notification) => { + if (notification?.data) { + const switchTo = getSwitchToFunction(); + const accountID = notification.data.accountID as string; + const account = findAccountByID(accountID); + if (account) { + await switchTo(account); + PapillonNavigation.current?.reset({ + index: 0, + routes: [{ name: "AccountStack" }], + }); + setTimeout(() => { + // @ts-expect-error : on ne prend pas le state des routes en compte ici. + PapillonNavigation.current?.navigate(notification.data.page as keyof RouteParameters); + }, 500); + } + } + }; + + const checkInitialNotification = async () => { + const initialNotification = await notifee.getInitialNotification(); + if (initialNotification) { + await handleNotificationPress(initialNotification.notification); + } + }; + + useEffect(() => { + checkInitialNotification(); + }, []); + useEffect(() => { // Gestion des notifs quand app en premier plan const unsubscribe = notifee.onForegroundEvent(async ({ type, detail }) => { diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index 0e5d51ad5..031a8ba17 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -131,7 +131,10 @@ const papillonNotify = async ( showChronometer: channelId === "Status" ? true : false, smallIcon: "@mipmap/ic_launcher_foreground", color: "#32AB8E", - // à intégrer => `actions` + pressAction: { + id: "default", + launchActivity: "default", + } }, ios: { threadId: channelId, diff --git a/src/background/data/News.ts b/src/background/data/News.ts index 08d840452..d87747d7b 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -64,6 +64,10 @@ const fetchNews = async (): Promise => { 100 )}...` : "Aucun résumé disponible.", + data: { + accountID: account.localID, + page: "News", + } }, "News" ); @@ -89,6 +93,10 @@ const fetchNews = async (): Promise => { ${differences.length} nouvelles actualités par :
${newsPreview} `, + data: { + accountID: account.localID, + page: "News", + } }, "News" ); diff --git a/src/background/utils/accounts.ts b/src/background/utils/accounts.ts index e3d6945ea..bd0596b67 100644 --- a/src/background/utils/accounts.ts +++ b/src/background/utils/accounts.ts @@ -14,3 +14,8 @@ export const getSwitchToFunction = () => { export const getCurrentAccount = (): PrimaryAccount => { return useCurrentAccount.getState().account!; }; + +export const findAccountByID = (accountID: string): PrimaryAccount | null => { + const account = getAccounts().find((acc) => acc.localID === accountID); + return account ?? null; +}; From d0cc47f6425776147a0790fe65490ff2fc3fed49 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 5 Feb 2025 16:14:42 +0100 Subject: [PATCH 0484/1144] feat: ajout d'un calendrier calendriable --- package-lock.json | 46 +- package.json | 1 + src/router/screens/account/index.tsx | 2 +- .../{Lessons => Lessons.old}/Atoms/Item.tsx | 0 .../Atoms/LessonsDatePicker.tsx | 0 .../Atoms/Loading.tsx | 0 .../Atoms/NoCourse.tsx | 0 .../{Lessons => Lessons.old}/Atoms/Page.tsx | 0 .../{Lessons => Lessons.old}/Document.tsx | 0 src/views/account/Lessons.old/Lessons.tsx | 485 +++++++++++++++++ .../LessonsHeader.tsx | 0 .../Options/LessonsImportIcal.tsx | 0 src/views/account/Lessons/Lessons.tsx | 501 ++++-------------- 13 files changed, 636 insertions(+), 399 deletions(-) rename src/views/account/{Lessons => Lessons.old}/Atoms/Item.tsx (100%) rename src/views/account/{Lessons => Lessons.old}/Atoms/LessonsDatePicker.tsx (100%) rename src/views/account/{Lessons => Lessons.old}/Atoms/Loading.tsx (100%) rename src/views/account/{Lessons => Lessons.old}/Atoms/NoCourse.tsx (100%) rename src/views/account/{Lessons => Lessons.old}/Atoms/Page.tsx (100%) rename src/views/account/{Lessons => Lessons.old}/Document.tsx (100%) create mode 100644 src/views/account/Lessons.old/Lessons.tsx rename src/views/account/{Lessons => Lessons.old}/LessonsHeader.tsx (100%) rename src/views/account/{Lessons => Lessons.old}/Options/LessonsImportIcal.tsx (100%) diff --git a/package-lock.json b/package-lock.json index 0999a01e8..32bc88c36 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.3", "@candlefinance/app-icon": "^0.4.5", + "@howljs/calendar-kit": "^2.2.1", "@notifee/react-native": "^7.8.2", "@react-native-async-storage/async-storage": "1.23.1", "@react-native-community/datetimepicker": "8.0.1", @@ -3266,6 +3267,34 @@ "@hapi/hoek": "^9.0.0" } }, + "node_modules/@howljs/calendar-kit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@howljs/calendar-kit/-/calendar-kit-2.2.1.tgz", + "integrity": "sha512-3ZgPS46/14pfZ/gBbMsdrSpCy/jTfVpgeHcvOHW7itflUnFBOSCRhGc4cWRd27vD9gIAu63PdWngQ34p/5EwFw==", + "dependencies": { + "lodash.debounce": "^4.0.8", + "lodash.isequal": "^4.5.0", + "lodash.merge": "^4.6.2", + "luxon": "^3.4.4", + "rrule": "^2.8.1" + }, + "peerDependencies": { + "expo-haptics": "*", + "react": "*", + "react-native": "*", + "react-native-gesture-handler": "*", + "react-native-haptic-feedback": "*", + "react-native-reanimated": "*" + }, + "peerDependenciesMeta": { + "expo-haptics": { + "optional": true + }, + "react-native-haptic-feedback": { + "optional": true + } + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -12139,11 +12168,16 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead." + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, "node_modules/lodash.throttle": { "version": "4.1.1", @@ -12423,6 +12457,14 @@ "react-native-svg": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0" } }, + "node_modules/luxon": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", + "engines": { + "node": ">=12" + } + }, "node_modules/make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", diff --git a/package.json b/package.json index 2b2295f29..1e1035bde 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.3", "@candlefinance/app-icon": "^0.4.5", + "@howljs/calendar-kit": "^2.2.1", "@notifee/react-native": "^7.8.2", "@react-native-async-storage/async-storage": "1.23.1", "@react-native-community/datetimepicker": "8.0.1", diff --git a/src/router/screens/account/index.tsx b/src/router/screens/account/index.tsx index afa9e5167..25fb0d51a 100644 --- a/src/router/screens/account/index.tsx +++ b/src/router/screens/account/index.tsx @@ -17,7 +17,7 @@ export const screens = [ }), createScreen("Lessons", Lessons, { headerTitle: "Cours", - headerShown: false, + headerShown: true, tabBarLabel: "Cours", tabBarLottie: require("@/../assets/lottie/tab_calendar.json"), }), diff --git a/src/views/account/Lessons/Atoms/Item.tsx b/src/views/account/Lessons.old/Atoms/Item.tsx similarity index 100% rename from src/views/account/Lessons/Atoms/Item.tsx rename to src/views/account/Lessons.old/Atoms/Item.tsx diff --git a/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx b/src/views/account/Lessons.old/Atoms/LessonsDatePicker.tsx similarity index 100% rename from src/views/account/Lessons/Atoms/LessonsDatePicker.tsx rename to src/views/account/Lessons.old/Atoms/LessonsDatePicker.tsx diff --git a/src/views/account/Lessons/Atoms/Loading.tsx b/src/views/account/Lessons.old/Atoms/Loading.tsx similarity index 100% rename from src/views/account/Lessons/Atoms/Loading.tsx rename to src/views/account/Lessons.old/Atoms/Loading.tsx diff --git a/src/views/account/Lessons/Atoms/NoCourse.tsx b/src/views/account/Lessons.old/Atoms/NoCourse.tsx similarity index 100% rename from src/views/account/Lessons/Atoms/NoCourse.tsx rename to src/views/account/Lessons.old/Atoms/NoCourse.tsx diff --git a/src/views/account/Lessons/Atoms/Page.tsx b/src/views/account/Lessons.old/Atoms/Page.tsx similarity index 100% rename from src/views/account/Lessons/Atoms/Page.tsx rename to src/views/account/Lessons.old/Atoms/Page.tsx diff --git a/src/views/account/Lessons/Document.tsx b/src/views/account/Lessons.old/Document.tsx similarity index 100% rename from src/views/account/Lessons/Document.tsx rename to src/views/account/Lessons.old/Document.tsx diff --git a/src/views/account/Lessons.old/Lessons.tsx b/src/views/account/Lessons.old/Lessons.tsx new file mode 100644 index 000000000..09134d1ba --- /dev/null +++ b/src/views/account/Lessons.old/Lessons.tsx @@ -0,0 +1,485 @@ +import React, { useCallback, useEffect, useRef, useState } from "react"; +import { FlatList, View, Dimensions, ViewToken } from "react-native"; +import { StyleSheet } from "react-native"; +import type { Screen } from "@/router/helpers/types"; +import { useCurrentAccount } from "@/stores/account"; +import { useTimetableStore } from "@/stores/timetable"; +import { getWeekFrequency, updateTimetableForWeekInCache } from "@/services/timetable"; +import { Page } from "./Atoms/Page"; +import { LessonsDateModal } from "./LessonsHeader"; +import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; + +import * as StoreReview from "expo-store-review"; + + +import Reanimated, { + FadeIn, + FadeOut, + LinearTransition, + ZoomIn, +} from "react-native-reanimated"; +import { animPapillon } from "@/utils/ui/animations"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { useTheme } from "@react-navigation/native"; +import AnimatedNumber from "@/components/Global/AnimatedNumber"; +import { CalendarPlus, Eye, MoreVertical } from "lucide-react-native"; +import { + PapillonHeaderAction, + PapillonHeaderSelector, + PapillonHeaderSeparator, + PapillonModernHeader, +} from "@/components/Global/PapillonModernHeader"; +import PapillonPicker from "@/components/Global/PapillonPicker"; +import AsyncStorage from "@react-native-async-storage/async-storage"; +import { WeekFrequency } from "@/services/shared/Timetable"; + +const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { + const account = useCurrentAccount((store) => store.account!); + const mutateProperty = useCurrentAccount((store) => store.mutateProperty); + + const timetables = useTimetableStore((store) => store.timetables); + + const outsideNav = route.params?.outsideNav; + const insets = useSafeAreaInsets(); + const theme = useTheme(); + + const loadedWeeks = useRef>(new Set()); + const currentlyLoadingWeeks = useRef>(new Set()); + + const [shouldShowWeekFrequency, setShouldShowWeekFrequency] = useState(account.personalization.showWeekFrequency); + const [weekFrequency, setWeekFrequency] = useState(null); + + useEffect(() => { + // add all week numbers in timetables to loadedWeeks + for (const week in timetables) { + loadedWeeks.current.add(parseInt(week)); + } + }, [timetables]); + + const today = new Date(); + today.setHours(0, 0, 0, 0); + + const [pickerDate, setPickerDate] = useState(new Date(today)); + + const getWeekFromDate = (date: Date) => { + const epochWeekNumber = dateToEpochWeekNumber(date); + return epochWeekNumber; + }; + + const [updatedWeeks, setUpdatedWeeks] = useState(new Set()); + + useEffect(() => { + void (async () => { + const weekNumber = getWeekFromDate(pickerDate); + await loadTimetableWeek(weekNumber, false); + setWeekFrequency((await getWeekFrequency(account, weekNumber))); + })(); + }, [pickerDate, account.instance]); + + useEffect(() => { + void (async () => { + mutateProperty("personalization", { + ...account.personalization, + showWeekFrequency: shouldShowWeekFrequency + }); + })(); + }, [shouldShowWeekFrequency]); + + useEffect(() => { + loadTimetableWeek(getWeekFromDate(new Date()), true); + }, [account.personalization.icalURLs]); + + const [loadingWeeks, setLoadingWeeks] = useState([]); + const [loading, setLoading] = useState(false); + + const [showDatePicker, setShowDatePicker] = useState(false); + + const loadTimetableWeek = async (weekNumber: number, force = false) => { + if ( + (currentlyLoadingWeeks.current.has(weekNumber) || + loadedWeeks.current.has(weekNumber)) && + !force + ) { + return; + } + + setLoading(true); + + if (force) { + setLoadingWeeks([...loadingWeeks, weekNumber]); + } + + try { + await updateTimetableForWeekInCache(account, weekNumber, force); + currentlyLoadingWeeks.current.add(weekNumber); + } finally { + currentlyLoadingWeeks.current.delete(weekNumber); + loadedWeeks.current.add(weekNumber); + setUpdatedWeeks(new Set(updatedWeeks).add(weekNumber)); + setLoadingWeeks(loadingWeeks.filter((w) => w !== weekNumber)); + setLoading(false); + } + }; + + const getAllLessonsForDay = (date: Date) => { + const week = getWeekFromDate(date); + const timetable = timetables[week] || []; + + const newDate = new Date(date); + newDate.setHours(0, 0, 0, 0); + + const day = timetable.filter((lesson) => { + const lessonDate = new Date(lesson.startTimestamp); + lessonDate.setHours(0, 0, 0, 0); + + return lessonDate.getTime() === newDate.getTime(); + }); + + return day; + }; + + const flatListRef = useRef(null); + const [data, setData] = useState(() => { + const today = new Date(); + return Array.from({ length: 100 }, (_, i) => { + const date = new Date(today); + date.setDate(today.getDate() - 50 + i); + date.setHours(0, 0, 0, 0); + return date; + }); + }); + const renderItem = useCallback(({ item: date }: { item: Date }) => { + const weekNumber = getWeekFromDate(date); + return ( + + 0 + } + refreshAction={() => loadTimetableWeek(weekNumber, true)} + loading={loadingWeeks.includes(weekNumber)} + /> + + ); + }, + [ + pickerDate, + timetables, + loadingWeeks, + outsideNav, + insets, + getAllLessonsForDay, + loadTimetableWeek, + ], + ); + + const onViewableItemsChanged = useCallback(({ viewableItems }: { viewableItems: ViewToken[] }) => { + if (viewableItems.length > 0) { + const newDate = viewableItems[0].item; + setPickerDate(newDate); + loadTimetableWeek(getWeekFromDate(newDate), false); + } + }, + [loadTimetableWeek], + ); + + const getItemLayout = useCallback((_: any, index: number) => ({ + length: Dimensions.get("window").width, + offset: Dimensions.get("window").width * index, + index, + }), + [], + ); + + const askForReview = async () => { + StoreReview.isAvailableAsync().then((available) => { + if (available) { + StoreReview.requestReview(); + } + }); + }; + + useEffect(() => { + // on focus + const unsubscribe = navigation.addListener("focus", () => { + AsyncStorage.getItem("review_coursesOpen").then((value) => { + if (value) { + if (parseInt(value) >= 7) { + AsyncStorage.setItem("review_coursesOpen", "0"); + + setTimeout(() => { + AsyncStorage.getItem("review_given").then((value) => { + if(!value) { + askForReview(); + AsyncStorage.setItem("review_given", "true"); + } + }); + }, 1000); + } + else { + AsyncStorage.setItem("review_coursesOpen", (parseInt(value) + 1).toString()); + } + } else { + AsyncStorage.setItem("review_coursesOpen", "1"); + } + }); + }); + + return unsubscribe; + }, []); + + const onDateSelect = (date: Date | undefined) => { + const newDate = new Date(date || 0); + newDate.setHours(0, 0, 0, 0); + setPickerDate(newDate); + + const firstDate = data[0]; + const lastDate = data[data.length - 1]; + + let updatedData = [...data]; + const uniqueDates = new Set(updatedData.map(d => d.getTime())); + + if (newDate < firstDate) { + const dates = []; + for (let d = new Date(firstDate); d >= newDate; d.setDate(d.getDate() - 1)) { + if (!uniqueDates.has(d.getTime())) { + dates.unshift(new Date(d)); + uniqueDates.add(d.getTime()); + } + } + updatedData = [...dates, ...data]; + } else if (newDate > lastDate) { + const dates = []; + for (let d = new Date(lastDate); d <= newDate; d.setDate(d.getDate() + 1)) { + if (!uniqueDates.has(d.getTime())) { + dates.push(new Date(d)); + uniqueDates.add(d.getTime()); + } + } + updatedData = [...data, ...dates]; + } + + setData(updatedData); + + setTimeout(() => { + const index = updatedData.findIndex((d) => d.getTime() === newDate.getTime()); + if (index !== -1) { + flatListRef.current?.scrollToIndex({ index, animated: false }); + } + }, 0); + }; + + return ( + + + setShowDatePicker(true)} + onLongPress={() => { + const today = new Date(); + today.setHours(0, 0, 0, 0); + onDateSelect(today); + }} + > + + + + {pickerDate.toLocaleDateString("fr-FR", { weekday: "long" })} + + + + + + + + {pickerDate.toLocaleDateString("fr-FR", { month: "long" })} + + + {weekFrequency && shouldShowWeekFrequency && ( + + + + {weekFrequency.freqLabel} + + + + ) } + + + + + , + label: "Importer un iCal", + onPress: () => { + navigation.navigate("LessonsImportIcal", {}); + } + }, + ...(weekFrequency != null) ? [{ + icon: , + label: "Afficher type sem.", + onPress: () => { + setShouldShowWeekFrequency(!shouldShowWeekFrequency); + }, + checked: shouldShowWeekFrequency, + }] : [] + ]} + > + } + entering={animPapillon(ZoomIn)} + exiting={FadeOut.duration(130)} + /> + + + + item.toISOString()} + horizontal + pagingEnabled + showsHorizontalScrollIndicator={false} + onViewableItemsChanged={onViewableItemsChanged} + viewabilityConfig={{ itemVisiblePercentThreshold: 50 }} + getItemLayout={getItemLayout} + initialScrollIndex={50} + onEndReached={() => { + // Charger plus de dates si nécessaire + const lastDate = data[data.length - 1]; + const newDates = Array.from({ length: 30 }, (_, i) => { + const date = new Date(lastDate); + date.setDate(lastDate.getDate() + i + 1); + return date; + }); + setData((prevData) => [...prevData, ...newDates]); + }} + onEndReachedThreshold={0.5} + /> + + { + onDateSelect(date); + }} + /> + + ); +}; + +const styles = StyleSheet.create({ + header: { + paddingHorizontal: 16, + paddingVertical: 8, + position: "absolute", + top: 0, + left: 0, + }, + + weekPicker: { + flexDirection: "row", + justifyContent: "center", + alignItems: "center", + paddingHorizontal: 20, + height: 40, + borderRadius: 80, + gap: 6, + backgroundColor: "rgba(0, 0, 0, 0.05)", + alignSelf: "flex-start", + overflow: "hidden", + }, + + weekPickerText: { + zIndex: 10000, + }, + + weekPickerTextIntl: { + fontSize: 14.5, + fontFamily: "medium", + opacity: 0.7, + }, + + weekPickerTextNbr: { + fontSize: 16.5, + fontFamily: "semibold", + marginTop: -1.5, + }, + + weekButton: { + overflow: "hidden", + borderRadius: 80, + height: 38, + width: 38, + justifyContent: "center", + alignItems: "center", + }, +}); + +export default Lessons; diff --git a/src/views/account/Lessons/LessonsHeader.tsx b/src/views/account/Lessons.old/LessonsHeader.tsx similarity index 100% rename from src/views/account/Lessons/LessonsHeader.tsx rename to src/views/account/Lessons.old/LessonsHeader.tsx diff --git a/src/views/account/Lessons/Options/LessonsImportIcal.tsx b/src/views/account/Lessons.old/Options/LessonsImportIcal.tsx similarity index 100% rename from src/views/account/Lessons/Options/LessonsImportIcal.tsx rename to src/views/account/Lessons.old/Options/LessonsImportIcal.tsx diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index 09134d1ba..ae3d4c2c4 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -1,53 +1,55 @@ -import React, { useCallback, useEffect, useRef, useState } from "react"; -import { FlatList, View, Dimensions, ViewToken } from "react-native"; -import { StyleSheet } from "react-native"; -import type { Screen } from "@/router/helpers/types"; +import * as React from "react"; +import { useEffect, useState } from "react"; +import { View } from "react-native"; + +import CalendarKit, { CalendarBody, CalendarHeader, DeepPartial, ThemeConfigs } from "@howljs/calendar-kit"; import { useCurrentAccount } from "@/stores/account"; import { useTimetableStore } from "@/stores/timetable"; -import { getWeekFrequency, updateTimetableForWeekInCache } from "@/services/timetable"; -import { Page } from "./Atoms/Page"; -import { LessonsDateModal } from "./LessonsHeader"; import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; +import { updateTimetableForWeekInCache } from "@/services/timetable"; +import { useTheme } from "@react-navigation/native"; +import { NativeText } from "@/components/Global/NativeComponents"; -import * as StoreReview from "expo-store-review"; +const initialLocales: Record> = { + en: { + weekDayShort: "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"), // Text in day header (Sun, Mon, etc.) + meridiem: { ante: "am", post: "pm" }, // Hour format (hh:mm a) + more: "more", // Text for "more" button (All day events) + }, + fr: { + weekDayShort: "Dim_Lun_Mar_Mer_Jeu_Ven_Sam".split("_"), // Text in day header (Sun, Mon, etc.) + meridiem: { ante: "am", post: "pm" }, // Hour format (hh:mm a) + more: "plus", // Text for "more" button (All day events) + }, +}; +const Lessons = () => { + const theme = useTheme(); + + const [events, setEvents] = React.useState([]); + const [isLoading, setIsLoading] = React.useState(false); -import Reanimated, { - FadeIn, - FadeOut, - LinearTransition, - ZoomIn, -} from "react-native-reanimated"; -import { animPapillon } from "@/utils/ui/animations"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; -import { useTheme } from "@react-navigation/native"; -import AnimatedNumber from "@/components/Global/AnimatedNumber"; -import { CalendarPlus, Eye, MoreVertical } from "lucide-react-native"; -import { - PapillonHeaderAction, - PapillonHeaderSelector, - PapillonHeaderSeparator, - PapillonModernHeader, -} from "@/components/Global/PapillonModernHeader"; -import PapillonPicker from "@/components/Global/PapillonPicker"; -import AsyncStorage from "@react-native-async-storage/async-storage"; -import { WeekFrequency } from "@/services/shared/Timetable"; - -const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const account = useCurrentAccount((store) => store.account!); const mutateProperty = useCurrentAccount((store) => store.mutateProperty); const timetables = useTimetableStore((store) => store.timetables); - const outsideNav = route.params?.outsideNav; - const insets = useSafeAreaInsets(); - const theme = useTheme(); + const loadedWeeks = React.useRef>(new Set()); + const currentlyLoadingWeeks = React.useRef>(new Set()); - const loadedWeeks = useRef>(new Set()); - const currentlyLoadingWeeks = useRef>(new Set()); - const [shouldShowWeekFrequency, setShouldShowWeekFrequency] = useState(account.personalization.showWeekFrequency); - const [weekFrequency, setWeekFrequency] = useState(null); + const customTheme: DeepPartial = { + colors: { + primary: theme.colors.primary, + background: theme.colors.background, + border: theme.colors.border, + text: theme.colors.text, + surface: theme.colors.card, + onPrimary: theme.colors.background, + onBackground: theme.colors.text, + onSurface: theme.colors.text, + } + }; useEffect(() => { // add all week numbers in timetables to loadedWeeks @@ -72,19 +74,10 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { void (async () => { const weekNumber = getWeekFromDate(pickerDate); await loadTimetableWeek(weekNumber, false); - setWeekFrequency((await getWeekFrequency(account, weekNumber))); + // setWeekFrequency((await getWeekFrequency(account, weekNumber))); })(); }, [pickerDate, account.instance]); - useEffect(() => { - void (async () => { - mutateProperty("personalization", { - ...account.personalization, - showWeekFrequency: shouldShowWeekFrequency - }); - })(); - }, [shouldShowWeekFrequency]); - useEffect(() => { loadTimetableWeek(getWeekFromDate(new Date()), true); }, [account.personalization.icalURLs]); @@ -97,8 +90,8 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const loadTimetableWeek = async (weekNumber: number, force = false) => { if ( (currentlyLoadingWeeks.current.has(weekNumber) || - loadedWeeks.current.has(weekNumber)) && - !force + loadedWeeks.current.has(weekNumber)) && + !force ) { return; } @@ -121,365 +114,81 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { } }; - const getAllLessonsForDay = (date: Date) => { - const week = getWeekFromDate(date); - const timetable = timetables[week] || []; - - const newDate = new Date(date); - newDate.setHours(0, 0, 0, 0); - - const day = timetable.filter((lesson) => { - const lessonDate = new Date(lesson.startTimestamp); - lessonDate.setHours(0, 0, 0, 0); - - return lessonDate.getTime() === newDate.getTime(); - }); - - return day; - }; - - const flatListRef = useRef(null); - const [data, setData] = useState(() => { - const today = new Date(); - return Array.from({ length: 100 }, (_, i) => { - const date = new Date(today); - date.setDate(today.getDate() - 50 + i); - date.setHours(0, 0, 0, 0); - return date; - }); - }); - const renderItem = useCallback(({ item: date }: { item: Date }) => { - const weekNumber = getWeekFromDate(date); - return ( - - 0 - } - refreshAction={() => loadTimetableWeek(weekNumber, true)} - loading={loadingWeeks.includes(weekNumber)} - /> - - ); - }, - [ - pickerDate, - timetables, - loadingWeeks, - outsideNav, - insets, - getAllLessonsForDay, - loadTimetableWeek, - ], - ); - - const onViewableItemsChanged = useCallback(({ viewableItems }: { viewableItems: ViewToken[] }) => { - if (viewableItems.length > 0) { - const newDate = viewableItems[0].item; - setPickerDate(newDate); - loadTimetableWeek(getWeekFromDate(newDate), false); - } - }, - [loadTimetableWeek], - ); - - const getItemLayout = useCallback((_: any, index: number) => ({ - length: Dimensions.get("window").width, - offset: Dimensions.get("window").width * index, - index, - }), - [], - ); - - const askForReview = async () => { - StoreReview.isAvailableAsync().then((available) => { - if (available) { - StoreReview.requestReview(); - } - }); - }; - useEffect(() => { - // on focus - const unsubscribe = navigation.addListener("focus", () => { - AsyncStorage.getItem("review_coursesOpen").then((value) => { - if (value) { - if (parseInt(value) >= 7) { - AsyncStorage.setItem("review_coursesOpen", "0"); - - setTimeout(() => { - AsyncStorage.getItem("review_given").then((value) => { - if(!value) { - askForReview(); - AsyncStorage.setItem("review_given", "true"); - } - }); - }, 1000); - } - else { - AsyncStorage.setItem("review_coursesOpen", (parseInt(value) + 1).toString()); - } - } else { - AsyncStorage.setItem("review_coursesOpen", "1"); - } + if(!events) { + const events = Object.values(timetables) + .map((timetable) => timetable) + .flat(); + + const finalEvents = events.map((event) => { + return { + id: event.id, + title: event.title, + start: { + dateTime: new Date(event.startTimestamp).toISOString().slice(0, 19) + "Z", + }, + end: { + dateTime: new Date(event.endTimestamp).toISOString().slice(0, 19) + "Z", + }, + color: "#ff0000", + }; }); - }); - return unsubscribe; - }, []); - - const onDateSelect = (date: Date | undefined) => { - const newDate = new Date(date || 0); - newDate.setHours(0, 0, 0, 0); - setPickerDate(newDate); - - const firstDate = data[0]; - const lastDate = data[data.length - 1]; - - let updatedData = [...data]; - const uniqueDates = new Set(updatedData.map(d => d.getTime())); - - if (newDate < firstDate) { - const dates = []; - for (let d = new Date(firstDate); d >= newDate; d.setDate(d.getDate() - 1)) { - if (!uniqueDates.has(d.getTime())) { - dates.unshift(new Date(d)); - uniqueDates.add(d.getTime()); - } - } - updatedData = [...dates, ...data]; - } else if (newDate > lastDate) { - const dates = []; - for (let d = new Date(lastDate); d <= newDate; d.setDate(d.getDate() + 1)) { - if (!uniqueDates.has(d.getTime())) { - dates.push(new Date(d)); - uniqueDates.add(d.getTime()); - } - } - updatedData = [...data, ...dates]; + setEvents(finalEvents); } + }, []); - setData(updatedData); - - setTimeout(() => { - const index = updatedData.findIndex((d) => d.getTime() === newDate.getTime()); - if (index !== -1) { - flatListRef.current?.scrollToIndex({ index, animated: false }); - } - }, 0); + const _onDateChanged = async (date: string) => { + console.log(date); + // setIsLoading(true); }; return ( - - - setShowDatePicker(true)} - onLongPress={() => { - const today = new Date(); - today.setHours(0, 0, 0, 0); - onDateSelect(today); - }} - > - - - - {pickerDate.toLocaleDateString("fr-FR", { weekday: "long" })} - - - - - - - - {pickerDate.toLocaleDateString("fr-FR", { month: "long" })} - - - {weekFrequency && shouldShowWeekFrequency && ( - + { + return ( + - - - {weekFrequency.freqLabel} - - - - ) } - - - - - , - label: "Importer un iCal", - onPress: () => { - navigation.navigate("LessonsImportIcal", {}); - } - }, - ...(weekFrequency != null) ? [{ - icon: , - label: "Afficher type sem.", - onPress: () => { - setShouldShowWeekFrequency(!shouldShowWeekFrequency); - }, - checked: shouldShowWeekFrequency, - }] : [] - ]} - > - } - entering={animPapillon(ZoomIn)} - exiting={FadeOut.duration(130)} - /> - - - - item.toISOString()} - horizontal - pagingEnabled - showsHorizontalScrollIndicator={false} - onViewableItemsChanged={onViewableItemsChanged} - viewabilityConfig={{ itemVisiblePercentThreshold: 50 }} - getItemLayout={getItemLayout} - initialScrollIndex={50} - onEndReached={() => { - // Charger plus de dates si nécessaire - const lastDate = data[data.length - 1]; - const newDates = Array.from({ length: 30 }, (_, i) => { - const date = new Date(lastDate); - date.setDate(lastDate.getDate() + i + 1); - return date; - }); - setData((prevData) => [...prevData, ...newDates]); + {event.title} +
+ + ); }} - onEndReachedThreshold={0.5} - /> - - { - onDateSelect(date); - }} - /> + > + + + ); }; -const styles = StyleSheet.create({ - header: { - paddingHorizontal: 16, - paddingVertical: 8, - position: "absolute", - top: 0, - left: 0, - }, - - weekPicker: { - flexDirection: "row", - justifyContent: "center", - alignItems: "center", - paddingHorizontal: 20, - height: 40, - borderRadius: 80, - gap: 6, - backgroundColor: "rgba(0, 0, 0, 0.05)", - alignSelf: "flex-start", - overflow: "hidden", - }, - - weekPickerText: { - zIndex: 10000, - }, - - weekPickerTextIntl: { - fontSize: 14.5, - fontFamily: "medium", - opacity: 0.7, - }, - - weekPickerTextNbr: { - fontSize: 16.5, - fontFamily: "semibold", - marginTop: -1.5, - }, - - weekButton: { - overflow: "hidden", - borderRadius: 80, - height: 38, - width: 38, - justifyContent: "center", - alignItems: "center", - }, -}); - -export default Lessons; +export default Lessons; \ No newline at end of file From 077adc8f950bf0fc3002bbe24096a5d454776772 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 5 Feb 2025 16:19:37 +0100 Subject: [PATCH 0485/1144] fix: update version to 7.8.3 and modify attendance handling for IUT Lannion --- android/app/build.gradle | 4 +- ios/Papillon.xcodeproj/project.pbxproj | 4 +- .../AppIcon.appiconset/Contents.json | 42 ++++--------------- ios/Papillon/Info.plist | 2 +- package.json | 2 +- src/services/attendance.ts | 29 ++++++++----- src/services/iutlan/attendance.ts | 9 ++-- 7 files changed, 39 insertions(+), 53 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 1ef08a4ea..5b3d5424d 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -88,8 +88,8 @@ android { applicationId 'xyz.getpapillon.app' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 7820 - versionName "7.8.2" + versionCode 7830 + versionName "7.8.3" } signingConfigs { debug { diff --git a/ios/Papillon.xcodeproj/project.pbxproj b/ios/Papillon.xcodeproj/project.pbxproj index 01967ab06..ecf2782fd 100644 --- a/ios/Papillon.xcodeproj/project.pbxproj +++ b/ios/Papillon.xcodeproj/project.pbxproj @@ -453,7 +453,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = Papillon; + PRODUCT_NAME = "Papillon"; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -486,7 +486,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = Papillon; + PRODUCT_NAME = "Papillon"; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json index e91cbd33d..90d8d4c2a 100644 --- a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,38 +1,14 @@ { - "images" : [ + "images": [ { - "filename" : "Icon-Light-1024x1024.png", - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "Icon-Dark-1024x1024.png", - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "tinted" - } - ], - "filename" : "Icon-Tinted-1024x1024.png", - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" + "filename": "App-Icon-1024x1024@1x.png", + "idiom": "universal", + "platform": "ios", + "size": "1024x1024" } ], - "info" : { - "author" : "xcode", - "version" : 1 + "info": { + "version": 1, + "author": "expo" } -} +} \ No newline at end of file diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index c30522e5f..04c938763 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 7.8.2 + 7.8.3 CFBundleSignature ???? CFBundleURLTypes diff --git a/package.json b/package.json index 2b2295f29..cb7c6c831 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "papillonvex", - "version": "7.8.2", + "version": "7.8.3", "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", diff --git a/src/services/attendance.ts b/src/services/attendance.ts index 0e9739273..6892d9bc5 100644 --- a/src/services/attendance.ts +++ b/src/services/attendance.ts @@ -33,17 +33,24 @@ export async function updateAttendancePeriodsInCache (accoun break; } case AccountService.Local: { - periods = [ - { - name: "Toutes", - startTimestamp: new Date("2021-09-01").getTime(), //not relevant to ED - endTimestamp: new Date("2022-06-30").getTime(), - }, - ]; - - defaultPeriod = "Toutes"; + if (account.identityProvider.identifier == "iut-lannion") { + const { saveIUTLanPeriods } = await import("./iutlan/grades"); + const data = await saveIUTLanPeriods(account); - break; + periods = data.periods; + defaultPeriod = data.defaultPeriod; + break; + } else { + periods = [ + { + name: "Toutes", + startTimestamp: 1609459200, + endTimestamp: 1622505600, + }, + ]; + defaultPeriod = "Toutes"; + break; + } } case AccountService.Skolengo: { const { getPeriod } = await import("./skolengo/data/period"); @@ -86,7 +93,7 @@ export async function updateAttendanceInCache (account: T, p case AccountService.Local: { if (account.identityProvider.identifier == "iut-lannion") { const { saveIUTLanAttendance } = await import("./iutlan/attendance"); - const data = await saveIUTLanAttendance(account); + const data = await saveIUTLanAttendance(account, periodName); attendance = { delays: data.delays, diff --git a/src/services/iutlan/attendance.ts b/src/services/iutlan/attendance.ts index 5eca2f79a..2a3d01358 100644 --- a/src/services/iutlan/attendance.ts +++ b/src/services/iutlan/attendance.ts @@ -14,9 +14,12 @@ interface scodocData { [key: string]: Array; } -export const saveIUTLanAttendance = async (account: LocalAccount): Promise => { +export const saveIUTLanAttendance = async ( + account: LocalAccount, + periodName: string +): Promise => { try { - const scodocData = account.identityProvider.rawData.absences as scodocData; + const scodocData = account.serviceData.semestres[periodName].absences as scodocData; const allAbsences: Array = []; if (scodocData && Object.keys(scodocData).length > 0) { @@ -35,7 +38,7 @@ export const saveIUTLanAttendance = async (account: LocalAccount): Promise Date: Wed, 5 Feb 2025 16:45:09 +0100 Subject: [PATCH 0486/1144] fix(AccountSwitcherContextMenu): improve account switching animation handling --- src/components/Home/AccountSwitcherContextMenu.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/Home/AccountSwitcherContextMenu.tsx b/src/components/Home/AccountSwitcherContextMenu.tsx index a9b791274..a18b94126 100644 --- a/src/components/Home/AccountSwitcherContextMenu.tsx +++ b/src/components/Home/AccountSwitcherContextMenu.tsx @@ -136,8 +136,10 @@ const ContextMenu: React.FC<{ key={index} onPress={() => { Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Soft); - switchTo(account).then(() => { - setOpened(false); + setOpened(false); + + requestAnimationFrame(() => { + switchTo(account); }); }} style={({ pressed }) => [ From 044f540333e061bf7d584b9f46fc52200dfb67cd Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 5 Feb 2025 17:44:07 +0100 Subject: [PATCH 0487/1144] =?UTF-8?q?fix:=20mise=20=C3=A0=20jour=20des=20c?= =?UTF-8?q?hemins=20d'importation=20pour=20les=20le=C3=A7ons?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/screens/views/index.ts | 4 +- .../Home/Elements/TimetableElement.tsx | 2 +- src/views/account/Lessons/Lessons.tsx | 337 ++++++++++-------- src/views/account/Restaurant/Menu.tsx | 2 +- 4 files changed, 191 insertions(+), 154 deletions(-) diff --git a/src/router/screens/views/index.ts b/src/router/screens/views/index.ts index ffb39c703..cf66a3d4e 100644 --- a/src/router/screens/views/index.ts +++ b/src/router/screens/views/index.ts @@ -11,8 +11,8 @@ import RestaurantHistory from "@/views/account/Restaurant/Modals/History"; import ChatCreate from "@/views/account/Chat/Modals/ChatCreate"; import Chat from "@/views/account/Chat/Modals/Chat"; import HomeworksDocument from "@/views/account/Homeworks/Document"; -import LessonsImportIcal from "@/views/account/Lessons/Options/LessonsImportIcal"; -import LessonDocument from "@/views/account/Lessons/Document"; +import LessonsImportIcal from "@/views/account/Lessons.old/Options/LessonsImportIcal"; +import LessonDocument from "@/views/account/Lessons.old/Document"; import BackgroundIUTLannion from "@/views/login/IdentityProvider/actions/BackgroundIUTLannion"; import { Platform } from "react-native"; import GradeReaction from "@/views/account/Grades/Modals/GradeReaction"; diff --git a/src/views/account/Home/Elements/TimetableElement.tsx b/src/views/account/Home/Elements/TimetableElement.tsx index cbca06ce8..0e4a33ad1 100644 --- a/src/views/account/Home/Elements/TimetableElement.tsx +++ b/src/views/account/Home/Elements/TimetableElement.tsx @@ -9,7 +9,7 @@ import { PapillonNavigation } from "@/router/refs"; import RedirectButton from "@/components/Home/RedirectButton"; import { updateTimetableForWeekInCache } from "@/services/timetable"; import MissingItem from "@/components/Global/MissingItem"; -import { TimetableItem } from "../../Lessons/Atoms/Item"; +import { TimetableItem } from "../../Lessons.old/Atoms/Item"; import { getHolidayEmoji } from "@/utils/format/holidayEmoji"; interface TimetableElementProps { diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index ae3d4c2c4..1245d3ff8 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -1,194 +1,231 @@ import * as React from "react"; -import { useEffect, useState } from "react"; -import { View } from "react-native"; - -import CalendarKit, { CalendarBody, CalendarHeader, DeepPartial, ThemeConfigs } from "@howljs/calendar-kit"; +import { memo, useCallback, useMemo } from "react"; +import { ActivityIndicator, Text, TouchableOpacity, View } from "react-native"; +import CalendarKit from "@howljs/calendar-kit"; import { useCurrentAccount } from "@/stores/account"; import { useTimetableStore } from "@/stores/timetable"; -import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; -import { updateTimetableForWeekInCache } from "@/services/timetable"; import { useTheme } from "@react-navigation/native"; -import { NativeText } from "@/components/Global/NativeComponents"; +import { updateTimetableForWeekInCache } from "@/services/timetable"; +import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; -const initialLocales: Record> = { +const LOCALES = { en: { - weekDayShort: "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"), // Text in day header (Sun, Mon, etc.) - meridiem: { ante: "am", post: "pm" }, // Hour format (hh:mm a) - more: "more", // Text for "more" button (All day events) + weekDayShort: "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"), + meridiem: { ante: "am", post: "pm" }, + more: "more", }, fr: { - weekDayShort: "Dim_Lun_Mar_Mer_Jeu_Ven_Sam".split("_"), // Text in day header (Sun, Mon, etc.) - meridiem: { ante: "am", post: "pm" }, // Hour format (hh:mm a) - more: "plus", // Text for "more" button (All day events) + weekDayShort: "Dim_Lun_Mar_Mer_Jeu_Ven_Sam".split("_"), + meridiem: { ante: "am", post: "pm" }, + more: "plus", }, -}; +} as const; + +const EventItem = memo(({ event }) => { + return ( + + + + {event.title} + + + {event.event.room} + + + + ); +}); -const Lessons = () => { +const HeaderItem = memo(({ header }) => { const theme = useTheme(); - const [events, setEvents] = React.useState([]); - const [isLoading, setIsLoading] = React.useState(false); + const cols = header.extra.columns; + const start = header.startUnix; - const account = useCurrentAccount((store) => store.account!); - const mutateProperty = useCurrentAccount((store) => store.mutateProperty); + const today = new Date(); + today.setHours(0, 0, 0, 0); - const timetables = useTimetableStore((store) => store.timetables); + const todayStamp = today.getTime(); - const loadedWeeks = React.useRef>(new Set()); - const currentlyLoadingWeeks = React.useRef>(new Set()); + return ( + + {Array.from({ length: cols }, (_, i) => ( + + + {new Date(start + + i * 24 * 60 * 60 * 1000 + ).toLocaleDateString("fr-FR", {weekday: "short"})} + + + {new Date(start + + i * 24 * 60 * 60 * 1000 + ).toLocaleDateString("fr-FR", {day: "numeric"})} + + + ))} + + ); +}); + +const Lessons = ({ navigation }) => { + const theme = useTheme(); + const [loadedWeeks, setLoadedWeeks] = React.useState(() => new Set()); + const [isLoading, setIsLoading] = React.useState(false); + const account = useCurrentAccount((store) => store.account); + const timetables = useTimetableStore((store) => store.timetables); - const customTheme: DeepPartial = { + const customTheme = useMemo(() => ({ colors: { primary: theme.colors.primary, background: theme.colors.background, - border: theme.colors.border, + border: theme.colors.border + "88", text: theme.colors.text, surface: theme.colors.card, onPrimary: theme.colors.background, onBackground: theme.colors.text, onSurface: theme.colors.text, } - }; - - useEffect(() => { - // add all week numbers in timetables to loadedWeeks - for (const week in timetables) { - loadedWeeks.current.add(parseInt(week)); - } - }, [timetables]); - - const today = new Date(); - today.setHours(0, 0, 0, 0); - - const [pickerDate, setPickerDate] = useState(new Date(today)); - - const getWeekFromDate = (date: Date) => { - const epochWeekNumber = dateToEpochWeekNumber(date); - return epochWeekNumber; - }; - - const [updatedWeeks, setUpdatedWeeks] = useState(new Set()); - - useEffect(() => { - void (async () => { - const weekNumber = getWeekFromDate(pickerDate); - await loadTimetableWeek(weekNumber, false); - // setWeekFrequency((await getWeekFrequency(account, weekNumber))); - })(); - }, [pickerDate, account.instance]); - - useEffect(() => { - loadTimetableWeek(getWeekFromDate(new Date()), true); - }, [account.personalization.icalURLs]); - - const [loadingWeeks, setLoadingWeeks] = useState([]); - const [loading, setLoading] = useState(false); - - const [showDatePicker, setShowDatePicker] = useState(false); - - const loadTimetableWeek = async (weekNumber: number, force = false) => { - if ( - (currentlyLoadingWeeks.current.has(weekNumber) || - loadedWeeks.current.has(weekNumber)) && - !force - ) { - return; - } - - setLoading(true); + }), [theme.colors]); + + const events = useMemo(() => + Object.values(timetables) + .flat() + .map(event => ({ + id: event.id, + title: event.title, + start: { dateTime: new Date(event.startTimestamp) }, + end: { dateTime: new Date(event.endTimestamp) }, + event: event, + })), + [timetables] + ); - if (force) { - setLoadingWeeks([...loadingWeeks, weekNumber]); - } + const loadTimetableWeek = useCallback(async (weekNumber, force = false) => { + if (!force && loadedWeeks.has(weekNumber)) return; + setIsLoading(true); try { await updateTimetableForWeekInCache(account, weekNumber, force); - currentlyLoadingWeeks.current.add(weekNumber); + setLoadedWeeks(prev => new Set([...prev, weekNumber])); } finally { - currentlyLoadingWeeks.current.delete(weekNumber); - loadedWeeks.current.add(weekNumber); - setUpdatedWeeks(new Set(updatedWeeks).add(weekNumber)); - setLoadingWeeks(loadingWeeks.filter((w) => w !== weekNumber)); - setLoading(false); - } - }; - - useEffect(() => { - if(!events) { - const events = Object.values(timetables) - .map((timetable) => timetable) - .flat(); - - const finalEvents = events.map((event) => { - return { - id: event.id, - title: event.title, - start: { - dateTime: new Date(event.startTimestamp).toISOString().slice(0, 19) + "Z", - }, - end: { - dateTime: new Date(event.endTimestamp).toISOString().slice(0, 19) + "Z", - }, - color: "#ff0000", - }; - }); - - setEvents(finalEvents); + setIsLoading(false); } - }, []); + }, [account, loadedWeeks]); - const _onDateChanged = async (date: string) => { - console.log(date); - // setIsLoading(true); - }; + const handleDateChange = useCallback(async (date) => { + const weekNumber = dateToEpochWeekNumber(new Date(date)); + await loadTimetableWeek(weekNumber); + }, [loadTimetableWeek]); + + React.useLayoutEffect(() => { + navigation.setOptions({ + headerRight: () => , + headerShadowVisible: false, + }); + }, [navigation, isLoading]); return ( - + { - return ( - - - {event.title} - - - ); - }} - > - - - + allowPinchToZoom + renderEvent={(event) => } + renderHeaderItem={(header) => } + dayBarHeight={50} + /> ); }; -export default Lessons; \ No newline at end of file +export default memo(Lessons); \ No newline at end of file diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 54632e3e8..7ae66691a 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -44,7 +44,7 @@ import { getMenu } from "@/services/menu"; import type { FoodAllergen, FoodLabel, Menu as PawnoteMenu } from "pawnote"; import { PapillonHeaderSelector } from "@/components/Global/PapillonModernHeader"; import AnimatedNumber from "@/components/Global/AnimatedNumber"; -import { LessonsDateModal } from "../Lessons/LessonsHeader"; +import { LessonsDateModal } from "../Lessons.old/LessonsHeader"; import { BookingTerminal, BookingDay } from "@/services/shared/Booking"; import { bookDayFromExternal, getBookingsAvailableFromExternal } from "@/services/booking"; import AccountButton from "@/components/Restaurant/AccountButton"; From 2f2c20207871d3b522d77c82832cdf7daa13ed3b Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 5 Feb 2025 18:35:26 +0100 Subject: [PATCH 0488/1144] =?UTF-8?q?feat:=20redirection=20de=20la=20notif?= =?UTF-8?q?ication=20sur=20le=20compte=20et=20la=20page=20sp=C3=A9cifique?= =?UTF-8?q?=20(sur=20toutes=20les=20pages)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Attendance.ts | 8 ++++++++ src/background/data/Evaluation.ts | 8 ++++++++ src/background/data/Grades.ts | 8 ++++++++ src/background/data/Homeworks.ts | 12 ++++++++++++ src/background/data/Lessons.ts | 12 ++++++++++++ 5 files changed, 48 insertions(+) diff --git a/src/background/data/Attendance.ts b/src/background/data/Attendance.ts index 72dc64bf7..ad674c7da 100644 --- a/src/background/data/Attendance.ts +++ b/src/background/data/Attendance.ts @@ -130,6 +130,10 @@ const fetchAttendance = async (): Promise => { title: `[${account.name}] ${thenewevent}`, subtitle: defaultPeriod, body: explication, + data: { + accountID: account.localID, + page: "Attendance" + } }, "Attendance" ); @@ -185,6 +189,10 @@ const fetchAttendance = async (): Promise => { body: `De nouveaux événements ont été publiés, consulte la vie scolaire pour plus de détails : ${LesExplication.join( ", " )}.`, + data: { + accountID: account.localID, + page: "Attendance" + } }, "Attendance" ); diff --git a/src/background/data/Evaluation.ts b/src/background/data/Evaluation.ts index 9bd961900..ab8270eca 100644 --- a/src/background/data/Evaluation.ts +++ b/src/background/data/Evaluation.ts @@ -62,6 +62,10 @@ const fetchEvaluation = async (): Promise => { body: `Titre : ${ differences[0].description || "Compétence sans titre" }, Coefficient : ${differences[0].coefficient}`, + data: { + accountID: account.localID, + page: "Evaluation" + } }, "Evaluation" ); @@ -89,6 +93,10 @@ const fetchEvaluation = async (): Promise => { ${differences.length} nouvelles compétences :
${evaluationPreview} `, + data: { + accountID: account.localID, + page: "Evaluation" + } }, "Evaluation" ); diff --git a/src/background/data/Grades.ts b/src/background/data/Grades.ts index dd99cd588..e07203c07 100644 --- a/src/background/data/Grades.ts +++ b/src/background/data/Grades.ts @@ -62,6 +62,10 @@ const fetchGrade = async (): Promise => { body: `Titre : ${ differences[0].description || "Note sans titre" }, Coefficient : ${differences[0].coefficient}`, + data: { + accountID: account.localID, + page: "Grades" + } }, "Grades" ); @@ -89,6 +93,10 @@ const fetchGrade = async (): Promise => { ${differences.length} nouvelles notes :
${gradePreview} `, + data: { + accountID: account.localID, + page: "Grades" + } }, "Grades" ); diff --git a/src/background/data/Homeworks.ts b/src/background/data/Homeworks.ts index 09eafb074..4e86e33ac 100644 --- a/src/background/data/Homeworks.ts +++ b/src/background/data/Homeworks.ts @@ -86,6 +86,10 @@ const fetchHomeworks = async (): Promise => { 1 ).toString()}`, body: parse_homeworks(differencesHwSemaineActuelle[0].content), + data: { + accountID: account.localID, + page: "Homeworks" + } }, "Homeworks" ); @@ -99,6 +103,10 @@ const fetchHomeworks = async (): Promise => { 2 ).toString()}`, body: parse_homeworks(differencesHwSemaineProchaine[0].content), + data: { + accountID: account.localID, + page: "Homeworks" + } }, "Homeworks" ); @@ -150,6 +158,10 @@ const fetchHomeworks = async (): Promise => { ${differences} nouveaux devoirs :
${subjectPreview} `, + data: { + accountID: account.localID, + page: "Homeworks" + } }, "Homeworks" ); diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index 607fb0e33..0e661916e 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -110,6 +110,10 @@ const fetchLessons = async (): Promise => { month: "long", }), body: `${differencesStatus[0].subject} (${dateLessonsDebut}-${dateLessonsFin}) : Horaire du cours modifié`, + data: { + accountID: account.localID, + page: "Lessons" + } }, "Lessons" ); @@ -160,6 +164,10 @@ const fetchLessons = async (): Promise => { month: "long", }), body: `${differencesStatus[0].subject} (${dateLessonsDebut}-${dateLessonsFin}) : ${statut}`, + data: { + accountID: account.localID, + page: "Lessons" + } }, "Lessons" ); @@ -191,6 +199,10 @@ const fetchLessons = async (): Promise => { }), body: `${totalDifference} cours modifiés :
${lessonsPreview}`, + data: { + accountID: account.localID, + page: "Lessons" + } }, "Lessons" ); From e56c7917679f2dd7b1e0c7d5d5034d3dd194b4b7 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 5 Feb 2025 19:05:21 +0100 Subject: [PATCH 0489/1144] =?UTF-8?q?refactor:=20simplification=20des=20?= =?UTF-8?q?=C3=A9v=C3=A9nements=20des=20notifications=20en=20premier=20et?= =?UTF-8?q?=20arri=C3=A8re=20plan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.tsx | 33 +------------------------------ src/background/BackgroundTasks.ts | 23 +++++++++++++-------- 2 files changed, 16 insertions(+), 40 deletions(-) diff --git a/App.tsx b/App.tsx index efd799982..f5731b656 100644 --- a/App.tsx +++ b/App.tsx @@ -1,5 +1,5 @@ import "@/background/BackgroundTasks"; -import notifee, { EventType, Notification } from "@notifee/react-native"; +import notifee, { Notification } from "@notifee/react-native"; import Router from "@/router"; import { useFonts } from "expo-font"; import * as SplashScreen from "expo-splash-screen"; @@ -57,37 +57,6 @@ export default function App () { checkInitialNotification(); }, []); - useEffect(() => { - // Gestion des notifs quand app en premier plan - const unsubscribe = notifee.onForegroundEvent(async ({ type, detail }) => { - const { notification, pressAction } = detail; - - switch (type) { - case EventType.ACTION_PRESS: - console.log(`[Notifee] Action press: ${pressAction?.id}`); - /* - Ici on va gérer les redirections vers une page de l'app - par exemple quand on clique sur une notification - - if (pressAction?.id === "open_lessons") { - console.log("Open lessons screen"); - } - */ - break; - - case EventType.DISMISSED: - let badgeCount = await notifee.getBadgeCount(); - badgeCount--; - await notifee.setBadgeCount(badgeCount); - break; - } - }); - - return () => { - unsubscribe(); - }; - }, []); - const [appState, setAppState] = useState(AppState.currentState); const backgroundStartTime = useRef(null); const switchTo = useCurrentAccount((store) => store.switchTo); diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 75ef2a732..9e18219d2 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -14,22 +14,29 @@ import { fetchAttendance } from "./data/Attendance"; import { fetchEvaluation } from "./data/Evaluation"; import { papillonNotify } from "./Notifications"; -// Gestion des notifs quand app en arrière-plan +// Gestion des badges quand app en arrière-plan notifee.onBackgroundEvent(async ({ type, detail }) => { const { notification, pressAction } = detail; switch (type) { case EventType.ACTION_PRESS: console.log(`[Notifee] Action press: ${pressAction?.id}`); - /* - Ici on va gérer les redirections vers une page de l'app - par exemple quand on clique sur une notification - if (pressAction?.id === "open_lessons") { - console.log("Open lessons screen"); - } - */ + case EventType.DISMISSED: + let badgeCount = await notifee.getBadgeCount(); + badgeCount--; + await notifee.setBadgeCount(badgeCount); break; + } +}); + +// Gestion des badges quand app en premier plan +notifee.onForegroundEvent(async ({ type, detail }) => { + const { notification, pressAction } = detail; + + switch (type) { + case EventType.ACTION_PRESS: + console.log(`[Notifee] Action press: ${pressAction?.id}`); case EventType.DISMISSED: let badgeCount = await notifee.getBadgeCount(); From 0c3bc1afe5aa70795a5014d4145a8d12d713b0ee Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 5 Feb 2025 19:13:13 +0100 Subject: [PATCH 0490/1144] =?UTF-8?q?feat:=20am=C3=A9lioration=20de=20l'af?= =?UTF-8?q?fichage=20des=20le=C3=A7ons=20avec=20un=20s=C3=A9lecteur=20de?= =?UTF-8?q?=20mode=20et=20ajustements=20de=20style?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/screens/account/index.tsx | 2 +- src/views/account/Lessons/Lessons.tsx | 86 ++++++++++++++++++++++----- 2 files changed, 72 insertions(+), 16 deletions(-) diff --git a/src/router/screens/account/index.tsx b/src/router/screens/account/index.tsx index 25fb0d51a..afa9e5167 100644 --- a/src/router/screens/account/index.tsx +++ b/src/router/screens/account/index.tsx @@ -17,7 +17,7 @@ export const screens = [ }), createScreen("Lessons", Lessons, { headerTitle: "Cours", - headerShown: true, + headerShown: false, tabBarLabel: "Cours", tabBarLottie: require("@/../assets/lottie/tab_calendar.json"), }), diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index 1245d3ff8..e22584225 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -1,12 +1,19 @@ import * as React from "react"; import { memo, useCallback, useMemo } from "react"; -import { ActivityIndicator, Text, TouchableOpacity, View } from "react-native"; +import { Text, TouchableOpacity, View } from "react-native"; import CalendarKit from "@howljs/calendar-kit"; import { useCurrentAccount } from "@/stores/account"; import { useTimetableStore } from "@/stores/timetable"; import { useTheme } from "@react-navigation/native"; import { updateTimetableForWeekInCache } from "@/services/timetable"; import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import PapillonPicker from "@/components/Global/PapillonPicker"; +import { CalendarDays } from "lucide-react-native"; +import { PapillonHeaderAction } from "@/components/Global/PapillonModernHeader"; +import { getSubjectData } from "@/services/shared/Subject"; +import { PapillonNavigation } from "@/router/refs"; +import PapillonSpinner from "@/components/Global/PapillonSpinner"; const LOCALES = { en: { @@ -22,6 +29,8 @@ const LOCALES = { } as const; const EventItem = memo(({ event }) => { + const subjectData = useMemo(() => getSubjectData(event.event.title), [event.event]); + return ( { overflow: "hidden", borderCurve: "continuous", }} + activeOpacity={0.7} + onPress={() => { + PapillonNavigation.current.navigate("LessonDocument", { lesson: event.event }); + }} > { height: 50, borderBottomWidth: 1, borderColor: theme.colors.border, - backgroundColor: theme.colors.card, + backgroundColor: theme.colors.background, }} > {Array.from({ length: cols }, (_, i) => ( @@ -111,6 +124,7 @@ const HeaderItem = memo(({ header }) => { fontFamily: "medium", opacity: 0.6, letterSpacing: 0.5, + color: theme.colors.text, }} > {new Date(start @@ -130,6 +144,7 @@ const HeaderItem = memo(({ header }) => { minWidth: 42, borderCurve: "continuous", overflow: "hidden", + color: theme.colors.text, }, start + i * 24 * 60 * 60 * 1000 === todayStamp && { @@ -148,14 +163,22 @@ const HeaderItem = memo(({ header }) => { ); }); -const Lessons = ({ navigation }) => { +const displayModes = ["Semaine", "3 jours", "Journée"]; + +const Lessons = ({ route, navigation }) => { const theme = useTheme(); + const insets = useSafeAreaInsets(); + + const outsideNav = route.params?.outsideNav; + const [loadedWeeks, setLoadedWeeks] = React.useState(() => new Set()); const [isLoading, setIsLoading] = React.useState(false); const account = useCurrentAccount((store) => store.account); const timetables = useTimetableStore((store) => store.timetables); + const [displayMode, setDisplayMode] = React.useState("Semaine"); + const customTheme = useMemo(() => ({ colors: { primary: theme.colors.primary, @@ -199,27 +222,60 @@ const Lessons = ({ navigation }) => { await loadTimetableWeek(weekNumber); }, [loadTimetableWeek]); - React.useLayoutEffect(() => { - navigation.setOptions({ - headerRight: () => , - headerShadowVisible: false, - }); - }, [navigation, isLoading]); - return ( + {!outsideNav && ( + + )} + + + setDisplayMode(mode)} + data={displayModes} + > + + ) : ( + + ) + } + /> + + + } renderHeaderItem={(header) => } dayBarHeight={50} From 387390b8e1698bc3da8725b5975df13b9e9c2ffb Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 5 Feb 2025 19:40:41 +0100 Subject: [PATCH 0491/1144] =?UTF-8?q?feat:=20ajout=20de=20la=20vue=20Semai?= =?UTF-8?q?ne=20et=20composants=20associ=C3=A9s=20pour=20le=20chargement?= =?UTF-8?q?=20et=20l'absence=20de=20cours?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/consts/DefaultTabs.ts | 9 +- src/router/helpers/types.ts | 1 + src/router/screens/account/index.tsx | 7 + src/router/screens/views/index.ts | 4 +- .../Home/Elements/TimetableElement.tsx | 2 +- src/views/account/Lessons.old/Lessons.tsx | 485 ------------- .../{Lessons.old => Lessons}/Atoms/Item.tsx | 0 .../Atoms/LessonsDatePicker.tsx | 0 .../Atoms/Loading.tsx | 0 .../Atoms/NoCourse.tsx | 0 .../{Lessons.old => Lessons}/Atoms/Page.tsx | 0 .../{Lessons.old => Lessons}/Document.tsx | 0 src/views/account/Lessons/Lessons.tsx | 680 +++++++++++------- .../LessonsHeader.tsx | 0 .../Options/LessonsImportIcal.tsx | 0 src/views/account/Restaurant/Menu.tsx | 2 +- src/views/account/Week/Week.tsx | 335 +++++++++ 17 files changed, 794 insertions(+), 731 deletions(-) delete mode 100644 src/views/account/Lessons.old/Lessons.tsx rename src/views/account/{Lessons.old => Lessons}/Atoms/Item.tsx (100%) rename src/views/account/{Lessons.old => Lessons}/Atoms/LessonsDatePicker.tsx (100%) rename src/views/account/{Lessons.old => Lessons}/Atoms/Loading.tsx (100%) rename src/views/account/{Lessons.old => Lessons}/Atoms/NoCourse.tsx (100%) rename src/views/account/{Lessons.old => Lessons}/Atoms/Page.tsx (100%) rename src/views/account/{Lessons.old => Lessons}/Document.tsx (100%) rename src/views/account/{Lessons.old => Lessons}/LessonsHeader.tsx (100%) rename src/views/account/{Lessons.old => Lessons}/Options/LessonsImportIcal.tsx (100%) create mode 100644 src/views/account/Week/Week.tsx diff --git a/src/consts/DefaultTabs.ts b/src/consts/DefaultTabs.ts index b8939a914..d83bf88fe 100644 --- a/src/consts/DefaultTabs.ts +++ b/src/consts/DefaultTabs.ts @@ -63,5 +63,12 @@ export const defaultTabs = [ description: "Tes compétences et évaluations", icon: require("@/../assets/lottie/tab_evaluations.json"), enabled: true, - } + }, + { + tab: "Week", + label: "Semaine", + description: "Vue avancée de la semaine", + icon: require("@/../assets/lottie/tab_calendar.json"), + enabled: true, + }, ] as const; \ No newline at end of file diff --git a/src/router/helpers/types.ts b/src/router/helpers/types.ts index 6057d5217..3a9e3f2cf 100644 --- a/src/router/helpers/types.ts +++ b/src/router/helpers/types.ts @@ -87,6 +87,7 @@ export type RouteParameters = { autoAdd?: boolean; }; LessonDocument: { lesson: Homework }; + Week: { outsideNav?: boolean }; Homeworks?: { outsideNav?: boolean }; HomeworksDocument: { homework: Homework }; diff --git a/src/router/screens/account/index.tsx b/src/router/screens/account/index.tsx index afa9e5167..d93adbc1b 100644 --- a/src/router/screens/account/index.tsx +++ b/src/router/screens/account/index.tsx @@ -1,5 +1,6 @@ import createScreen from "@/router/helpers/create-screen"; import Lessons from "@/views/account/Lessons/Lessons"; +import Week from "@/views/account/Week/Week"; import Homeworks from "@/views/account/Homeworks/Homeworks"; import HomeStackScreen from "./home"; import Menu from "@/views/account/Restaurant/Menu"; @@ -21,6 +22,12 @@ export const screens = [ tabBarLabel: "Cours", tabBarLottie: require("@/../assets/lottie/tab_calendar.json"), }), + createScreen("Week", Week, { + headerTitle: "Semaine", + headerShown: false, + tabBarLabel: "Semaine", + tabBarLottie: require("@/../assets/lottie/tab_calendar.json"), + }), createScreen("Homeworks", Homeworks, { headerTitle: "Devoirs", headerShown: false, diff --git a/src/router/screens/views/index.ts b/src/router/screens/views/index.ts index cf66a3d4e..ffb39c703 100644 --- a/src/router/screens/views/index.ts +++ b/src/router/screens/views/index.ts @@ -11,8 +11,8 @@ import RestaurantHistory from "@/views/account/Restaurant/Modals/History"; import ChatCreate from "@/views/account/Chat/Modals/ChatCreate"; import Chat from "@/views/account/Chat/Modals/Chat"; import HomeworksDocument from "@/views/account/Homeworks/Document"; -import LessonsImportIcal from "@/views/account/Lessons.old/Options/LessonsImportIcal"; -import LessonDocument from "@/views/account/Lessons.old/Document"; +import LessonsImportIcal from "@/views/account/Lessons/Options/LessonsImportIcal"; +import LessonDocument from "@/views/account/Lessons/Document"; import BackgroundIUTLannion from "@/views/login/IdentityProvider/actions/BackgroundIUTLannion"; import { Platform } from "react-native"; import GradeReaction from "@/views/account/Grades/Modals/GradeReaction"; diff --git a/src/views/account/Home/Elements/TimetableElement.tsx b/src/views/account/Home/Elements/TimetableElement.tsx index 0e4a33ad1..cbca06ce8 100644 --- a/src/views/account/Home/Elements/TimetableElement.tsx +++ b/src/views/account/Home/Elements/TimetableElement.tsx @@ -9,7 +9,7 @@ import { PapillonNavigation } from "@/router/refs"; import RedirectButton from "@/components/Home/RedirectButton"; import { updateTimetableForWeekInCache } from "@/services/timetable"; import MissingItem from "@/components/Global/MissingItem"; -import { TimetableItem } from "../../Lessons.old/Atoms/Item"; +import { TimetableItem } from "../../Lessons/Atoms/Item"; import { getHolidayEmoji } from "@/utils/format/holidayEmoji"; interface TimetableElementProps { diff --git a/src/views/account/Lessons.old/Lessons.tsx b/src/views/account/Lessons.old/Lessons.tsx deleted file mode 100644 index 09134d1ba..000000000 --- a/src/views/account/Lessons.old/Lessons.tsx +++ /dev/null @@ -1,485 +0,0 @@ -import React, { useCallback, useEffect, useRef, useState } from "react"; -import { FlatList, View, Dimensions, ViewToken } from "react-native"; -import { StyleSheet } from "react-native"; -import type { Screen } from "@/router/helpers/types"; -import { useCurrentAccount } from "@/stores/account"; -import { useTimetableStore } from "@/stores/timetable"; -import { getWeekFrequency, updateTimetableForWeekInCache } from "@/services/timetable"; -import { Page } from "./Atoms/Page"; -import { LessonsDateModal } from "./LessonsHeader"; -import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; - -import * as StoreReview from "expo-store-review"; - - -import Reanimated, { - FadeIn, - FadeOut, - LinearTransition, - ZoomIn, -} from "react-native-reanimated"; -import { animPapillon } from "@/utils/ui/animations"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; -import { useTheme } from "@react-navigation/native"; -import AnimatedNumber from "@/components/Global/AnimatedNumber"; -import { CalendarPlus, Eye, MoreVertical } from "lucide-react-native"; -import { - PapillonHeaderAction, - PapillonHeaderSelector, - PapillonHeaderSeparator, - PapillonModernHeader, -} from "@/components/Global/PapillonModernHeader"; -import PapillonPicker from "@/components/Global/PapillonPicker"; -import AsyncStorage from "@react-native-async-storage/async-storage"; -import { WeekFrequency } from "@/services/shared/Timetable"; - -const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { - const account = useCurrentAccount((store) => store.account!); - const mutateProperty = useCurrentAccount((store) => store.mutateProperty); - - const timetables = useTimetableStore((store) => store.timetables); - - const outsideNav = route.params?.outsideNav; - const insets = useSafeAreaInsets(); - const theme = useTheme(); - - const loadedWeeks = useRef>(new Set()); - const currentlyLoadingWeeks = useRef>(new Set()); - - const [shouldShowWeekFrequency, setShouldShowWeekFrequency] = useState(account.personalization.showWeekFrequency); - const [weekFrequency, setWeekFrequency] = useState(null); - - useEffect(() => { - // add all week numbers in timetables to loadedWeeks - for (const week in timetables) { - loadedWeeks.current.add(parseInt(week)); - } - }, [timetables]); - - const today = new Date(); - today.setHours(0, 0, 0, 0); - - const [pickerDate, setPickerDate] = useState(new Date(today)); - - const getWeekFromDate = (date: Date) => { - const epochWeekNumber = dateToEpochWeekNumber(date); - return epochWeekNumber; - }; - - const [updatedWeeks, setUpdatedWeeks] = useState(new Set()); - - useEffect(() => { - void (async () => { - const weekNumber = getWeekFromDate(pickerDate); - await loadTimetableWeek(weekNumber, false); - setWeekFrequency((await getWeekFrequency(account, weekNumber))); - })(); - }, [pickerDate, account.instance]); - - useEffect(() => { - void (async () => { - mutateProperty("personalization", { - ...account.personalization, - showWeekFrequency: shouldShowWeekFrequency - }); - })(); - }, [shouldShowWeekFrequency]); - - useEffect(() => { - loadTimetableWeek(getWeekFromDate(new Date()), true); - }, [account.personalization.icalURLs]); - - const [loadingWeeks, setLoadingWeeks] = useState([]); - const [loading, setLoading] = useState(false); - - const [showDatePicker, setShowDatePicker] = useState(false); - - const loadTimetableWeek = async (weekNumber: number, force = false) => { - if ( - (currentlyLoadingWeeks.current.has(weekNumber) || - loadedWeeks.current.has(weekNumber)) && - !force - ) { - return; - } - - setLoading(true); - - if (force) { - setLoadingWeeks([...loadingWeeks, weekNumber]); - } - - try { - await updateTimetableForWeekInCache(account, weekNumber, force); - currentlyLoadingWeeks.current.add(weekNumber); - } finally { - currentlyLoadingWeeks.current.delete(weekNumber); - loadedWeeks.current.add(weekNumber); - setUpdatedWeeks(new Set(updatedWeeks).add(weekNumber)); - setLoadingWeeks(loadingWeeks.filter((w) => w !== weekNumber)); - setLoading(false); - } - }; - - const getAllLessonsForDay = (date: Date) => { - const week = getWeekFromDate(date); - const timetable = timetables[week] || []; - - const newDate = new Date(date); - newDate.setHours(0, 0, 0, 0); - - const day = timetable.filter((lesson) => { - const lessonDate = new Date(lesson.startTimestamp); - lessonDate.setHours(0, 0, 0, 0); - - return lessonDate.getTime() === newDate.getTime(); - }); - - return day; - }; - - const flatListRef = useRef(null); - const [data, setData] = useState(() => { - const today = new Date(); - return Array.from({ length: 100 }, (_, i) => { - const date = new Date(today); - date.setDate(today.getDate() - 50 + i); - date.setHours(0, 0, 0, 0); - return date; - }); - }); - const renderItem = useCallback(({ item: date }: { item: Date }) => { - const weekNumber = getWeekFromDate(date); - return ( - - 0 - } - refreshAction={() => loadTimetableWeek(weekNumber, true)} - loading={loadingWeeks.includes(weekNumber)} - /> - - ); - }, - [ - pickerDate, - timetables, - loadingWeeks, - outsideNav, - insets, - getAllLessonsForDay, - loadTimetableWeek, - ], - ); - - const onViewableItemsChanged = useCallback(({ viewableItems }: { viewableItems: ViewToken[] }) => { - if (viewableItems.length > 0) { - const newDate = viewableItems[0].item; - setPickerDate(newDate); - loadTimetableWeek(getWeekFromDate(newDate), false); - } - }, - [loadTimetableWeek], - ); - - const getItemLayout = useCallback((_: any, index: number) => ({ - length: Dimensions.get("window").width, - offset: Dimensions.get("window").width * index, - index, - }), - [], - ); - - const askForReview = async () => { - StoreReview.isAvailableAsync().then((available) => { - if (available) { - StoreReview.requestReview(); - } - }); - }; - - useEffect(() => { - // on focus - const unsubscribe = navigation.addListener("focus", () => { - AsyncStorage.getItem("review_coursesOpen").then((value) => { - if (value) { - if (parseInt(value) >= 7) { - AsyncStorage.setItem("review_coursesOpen", "0"); - - setTimeout(() => { - AsyncStorage.getItem("review_given").then((value) => { - if(!value) { - askForReview(); - AsyncStorage.setItem("review_given", "true"); - } - }); - }, 1000); - } - else { - AsyncStorage.setItem("review_coursesOpen", (parseInt(value) + 1).toString()); - } - } else { - AsyncStorage.setItem("review_coursesOpen", "1"); - } - }); - }); - - return unsubscribe; - }, []); - - const onDateSelect = (date: Date | undefined) => { - const newDate = new Date(date || 0); - newDate.setHours(0, 0, 0, 0); - setPickerDate(newDate); - - const firstDate = data[0]; - const lastDate = data[data.length - 1]; - - let updatedData = [...data]; - const uniqueDates = new Set(updatedData.map(d => d.getTime())); - - if (newDate < firstDate) { - const dates = []; - for (let d = new Date(firstDate); d >= newDate; d.setDate(d.getDate() - 1)) { - if (!uniqueDates.has(d.getTime())) { - dates.unshift(new Date(d)); - uniqueDates.add(d.getTime()); - } - } - updatedData = [...dates, ...data]; - } else if (newDate > lastDate) { - const dates = []; - for (let d = new Date(lastDate); d <= newDate; d.setDate(d.getDate() + 1)) { - if (!uniqueDates.has(d.getTime())) { - dates.push(new Date(d)); - uniqueDates.add(d.getTime()); - } - } - updatedData = [...data, ...dates]; - } - - setData(updatedData); - - setTimeout(() => { - const index = updatedData.findIndex((d) => d.getTime() === newDate.getTime()); - if (index !== -1) { - flatListRef.current?.scrollToIndex({ index, animated: false }); - } - }, 0); - }; - - return ( - - - setShowDatePicker(true)} - onLongPress={() => { - const today = new Date(); - today.setHours(0, 0, 0, 0); - onDateSelect(today); - }} - > - - - - {pickerDate.toLocaleDateString("fr-FR", { weekday: "long" })} - - - - - - - - {pickerDate.toLocaleDateString("fr-FR", { month: "long" })} - - - {weekFrequency && shouldShowWeekFrequency && ( - - - - {weekFrequency.freqLabel} - - - - ) } - - - - - , - label: "Importer un iCal", - onPress: () => { - navigation.navigate("LessonsImportIcal", {}); - } - }, - ...(weekFrequency != null) ? [{ - icon: , - label: "Afficher type sem.", - onPress: () => { - setShouldShowWeekFrequency(!shouldShowWeekFrequency); - }, - checked: shouldShowWeekFrequency, - }] : [] - ]} - > - } - entering={animPapillon(ZoomIn)} - exiting={FadeOut.duration(130)} - /> - - - - item.toISOString()} - horizontal - pagingEnabled - showsHorizontalScrollIndicator={false} - onViewableItemsChanged={onViewableItemsChanged} - viewabilityConfig={{ itemVisiblePercentThreshold: 50 }} - getItemLayout={getItemLayout} - initialScrollIndex={50} - onEndReached={() => { - // Charger plus de dates si nécessaire - const lastDate = data[data.length - 1]; - const newDates = Array.from({ length: 30 }, (_, i) => { - const date = new Date(lastDate); - date.setDate(lastDate.getDate() + i + 1); - return date; - }); - setData((prevData) => [...prevData, ...newDates]); - }} - onEndReachedThreshold={0.5} - /> - - { - onDateSelect(date); - }} - /> - - ); -}; - -const styles = StyleSheet.create({ - header: { - paddingHorizontal: 16, - paddingVertical: 8, - position: "absolute", - top: 0, - left: 0, - }, - - weekPicker: { - flexDirection: "row", - justifyContent: "center", - alignItems: "center", - paddingHorizontal: 20, - height: 40, - borderRadius: 80, - gap: 6, - backgroundColor: "rgba(0, 0, 0, 0.05)", - alignSelf: "flex-start", - overflow: "hidden", - }, - - weekPickerText: { - zIndex: 10000, - }, - - weekPickerTextIntl: { - fontSize: 14.5, - fontFamily: "medium", - opacity: 0.7, - }, - - weekPickerTextNbr: { - fontSize: 16.5, - fontFamily: "semibold", - marginTop: -1.5, - }, - - weekButton: { - overflow: "hidden", - borderRadius: 80, - height: 38, - width: 38, - justifyContent: "center", - alignItems: "center", - }, -}); - -export default Lessons; diff --git a/src/views/account/Lessons.old/Atoms/Item.tsx b/src/views/account/Lessons/Atoms/Item.tsx similarity index 100% rename from src/views/account/Lessons.old/Atoms/Item.tsx rename to src/views/account/Lessons/Atoms/Item.tsx diff --git a/src/views/account/Lessons.old/Atoms/LessonsDatePicker.tsx b/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx similarity index 100% rename from src/views/account/Lessons.old/Atoms/LessonsDatePicker.tsx rename to src/views/account/Lessons/Atoms/LessonsDatePicker.tsx diff --git a/src/views/account/Lessons.old/Atoms/Loading.tsx b/src/views/account/Lessons/Atoms/Loading.tsx similarity index 100% rename from src/views/account/Lessons.old/Atoms/Loading.tsx rename to src/views/account/Lessons/Atoms/Loading.tsx diff --git a/src/views/account/Lessons.old/Atoms/NoCourse.tsx b/src/views/account/Lessons/Atoms/NoCourse.tsx similarity index 100% rename from src/views/account/Lessons.old/Atoms/NoCourse.tsx rename to src/views/account/Lessons/Atoms/NoCourse.tsx diff --git a/src/views/account/Lessons.old/Atoms/Page.tsx b/src/views/account/Lessons/Atoms/Page.tsx similarity index 100% rename from src/views/account/Lessons.old/Atoms/Page.tsx rename to src/views/account/Lessons/Atoms/Page.tsx diff --git a/src/views/account/Lessons.old/Document.tsx b/src/views/account/Lessons/Document.tsx similarity index 100% rename from src/views/account/Lessons.old/Document.tsx rename to src/views/account/Lessons/Document.tsx diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index e22584225..09134d1ba 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -1,287 +1,485 @@ -import * as React from "react"; -import { memo, useCallback, useMemo } from "react"; -import { Text, TouchableOpacity, View } from "react-native"; -import CalendarKit from "@howljs/calendar-kit"; +import React, { useCallback, useEffect, useRef, useState } from "react"; +import { FlatList, View, Dimensions, ViewToken } from "react-native"; +import { StyleSheet } from "react-native"; +import type { Screen } from "@/router/helpers/types"; import { useCurrentAccount } from "@/stores/account"; import { useTimetableStore } from "@/stores/timetable"; -import { useTheme } from "@react-navigation/native"; -import { updateTimetableForWeekInCache } from "@/services/timetable"; +import { getWeekFrequency, updateTimetableForWeekInCache } from "@/services/timetable"; +import { Page } from "./Atoms/Page"; +import { LessonsDateModal } from "./LessonsHeader"; import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; + +import * as StoreReview from "expo-store-review"; + + +import Reanimated, { + FadeIn, + FadeOut, + LinearTransition, + ZoomIn, +} from "react-native-reanimated"; +import { animPapillon } from "@/utils/ui/animations"; import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { useTheme } from "@react-navigation/native"; +import AnimatedNumber from "@/components/Global/AnimatedNumber"; +import { CalendarPlus, Eye, MoreVertical } from "lucide-react-native"; +import { + PapillonHeaderAction, + PapillonHeaderSelector, + PapillonHeaderSeparator, + PapillonModernHeader, +} from "@/components/Global/PapillonModernHeader"; import PapillonPicker from "@/components/Global/PapillonPicker"; -import { CalendarDays } from "lucide-react-native"; -import { PapillonHeaderAction } from "@/components/Global/PapillonModernHeader"; -import { getSubjectData } from "@/services/shared/Subject"; -import { PapillonNavigation } from "@/router/refs"; -import PapillonSpinner from "@/components/Global/PapillonSpinner"; - -const LOCALES = { - en: { - weekDayShort: "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"), - meridiem: { ante: "am", post: "pm" }, - more: "more", - }, - fr: { - weekDayShort: "Dim_Lun_Mar_Mer_Jeu_Ven_Sam".split("_"), - meridiem: { ante: "am", post: "pm" }, - more: "plus", - }, -} as const; +import AsyncStorage from "@react-native-async-storage/async-storage"; +import { WeekFrequency } from "@/services/shared/Timetable"; -const EventItem = memo(({ event }) => { - const subjectData = useMemo(() => getSubjectData(event.event.title), [event.event]); +const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { + const account = useCurrentAccount((store) => store.account!); + const mutateProperty = useCurrentAccount((store) => store.mutateProperty); - return ( - { - PapillonNavigation.current.navigate("LessonDocument", { lesson: event.event }); - }} - > - - - {event.title} - - - {event.event.room} - - - - ); -}); + const timetables = useTimetableStore((store) => store.timetables); -const HeaderItem = memo(({ header }) => { + const outsideNav = route.params?.outsideNav; + const insets = useSafeAreaInsets(); const theme = useTheme(); - const cols = header.extra.columns; - const start = header.startUnix; + const loadedWeeks = useRef>(new Set()); + const currentlyLoadingWeeks = useRef>(new Set()); + + const [shouldShowWeekFrequency, setShouldShowWeekFrequency] = useState(account.personalization.showWeekFrequency); + const [weekFrequency, setWeekFrequency] = useState(null); + + useEffect(() => { + // add all week numbers in timetables to loadedWeeks + for (const week in timetables) { + loadedWeeks.current.add(parseInt(week)); + } + }, [timetables]); const today = new Date(); today.setHours(0, 0, 0, 0); - const todayStamp = today.getTime(); + const [pickerDate, setPickerDate] = useState(new Date(today)); - return ( - - {Array.from({ length: cols }, (_, i) => ( - - - {new Date(start - + i * 24 * 60 * 60 * 1000 - ).toLocaleDateString("fr-FR", {weekday: "short"})} - - - {new Date(start - + i * 24 * 60 * 60 * 1000 - ).toLocaleDateString("fr-FR", {day: "numeric"})} - - - ))} - - ); -}); + const getWeekFromDate = (date: Date) => { + const epochWeekNumber = dateToEpochWeekNumber(date); + return epochWeekNumber; + }; -const displayModes = ["Semaine", "3 jours", "Journée"]; + const [updatedWeeks, setUpdatedWeeks] = useState(new Set()); -const Lessons = ({ route, navigation }) => { - const theme = useTheme(); - const insets = useSafeAreaInsets(); + useEffect(() => { + void (async () => { + const weekNumber = getWeekFromDate(pickerDate); + await loadTimetableWeek(weekNumber, false); + setWeekFrequency((await getWeekFrequency(account, weekNumber))); + })(); + }, [pickerDate, account.instance]); - const outsideNav = route.params?.outsideNav; + useEffect(() => { + void (async () => { + mutateProperty("personalization", { + ...account.personalization, + showWeekFrequency: shouldShowWeekFrequency + }); + })(); + }, [shouldShowWeekFrequency]); - const [loadedWeeks, setLoadedWeeks] = React.useState(() => new Set()); - const [isLoading, setIsLoading] = React.useState(false); + useEffect(() => { + loadTimetableWeek(getWeekFromDate(new Date()), true); + }, [account.personalization.icalURLs]); - const account = useCurrentAccount((store) => store.account); - const timetables = useTimetableStore((store) => store.timetables); + const [loadingWeeks, setLoadingWeeks] = useState([]); + const [loading, setLoading] = useState(false); + + const [showDatePicker, setShowDatePicker] = useState(false); - const [displayMode, setDisplayMode] = React.useState("Semaine"); - - const customTheme = useMemo(() => ({ - colors: { - primary: theme.colors.primary, - background: theme.colors.background, - border: theme.colors.border + "88", - text: theme.colors.text, - surface: theme.colors.card, - onPrimary: theme.colors.background, - onBackground: theme.colors.text, - onSurface: theme.colors.text, + const loadTimetableWeek = async (weekNumber: number, force = false) => { + if ( + (currentlyLoadingWeeks.current.has(weekNumber) || + loadedWeeks.current.has(weekNumber)) && + !force + ) { + return; } - }), [theme.colors]); - - const events = useMemo(() => - Object.values(timetables) - .flat() - .map(event => ({ - id: event.id, - title: event.title, - start: { dateTime: new Date(event.startTimestamp) }, - end: { dateTime: new Date(event.endTimestamp) }, - event: event, - })), - [timetables] - ); - const loadTimetableWeek = useCallback(async (weekNumber, force = false) => { - if (!force && loadedWeeks.has(weekNumber)) return; + setLoading(true); + + if (force) { + setLoadingWeeks([...loadingWeeks, weekNumber]); + } - setIsLoading(true); try { await updateTimetableForWeekInCache(account, weekNumber, force); - setLoadedWeeks(prev => new Set([...prev, weekNumber])); + currentlyLoadingWeeks.current.add(weekNumber); } finally { - setIsLoading(false); + currentlyLoadingWeeks.current.delete(weekNumber); + loadedWeeks.current.add(weekNumber); + setUpdatedWeeks(new Set(updatedWeeks).add(weekNumber)); + setLoadingWeeks(loadingWeeks.filter((w) => w !== weekNumber)); + setLoading(false); } - }, [account, loadedWeeks]); + }; + + const getAllLessonsForDay = (date: Date) => { + const week = getWeekFromDate(date); + const timetable = timetables[week] || []; + + const newDate = new Date(date); + newDate.setHours(0, 0, 0, 0); + + const day = timetable.filter((lesson) => { + const lessonDate = new Date(lesson.startTimestamp); + lessonDate.setHours(0, 0, 0, 0); + + return lessonDate.getTime() === newDate.getTime(); + }); + + return day; + }; - const handleDateChange = useCallback(async (date) => { - const weekNumber = dateToEpochWeekNumber(new Date(date)); - await loadTimetableWeek(weekNumber); - }, [loadTimetableWeek]); + const flatListRef = useRef(null); + const [data, setData] = useState(() => { + const today = new Date(); + return Array.from({ length: 100 }, (_, i) => { + const date = new Date(today); + date.setDate(today.getDate() - 50 + i); + date.setHours(0, 0, 0, 0); + return date; + }); + }); + const renderItem = useCallback(({ item: date }: { item: Date }) => { + const weekNumber = getWeekFromDate(date); + return ( + + 0 + } + refreshAction={() => loadTimetableWeek(weekNumber, true)} + loading={loadingWeeks.includes(weekNumber)} + /> + + ); + }, + [ + pickerDate, + timetables, + loadingWeeks, + outsideNav, + insets, + getAllLessonsForDay, + loadTimetableWeek, + ], + ); + + const onViewableItemsChanged = useCallback(({ viewableItems }: { viewableItems: ViewToken[] }) => { + if (viewableItems.length > 0) { + const newDate = viewableItems[0].item; + setPickerDate(newDate); + loadTimetableWeek(getWeekFromDate(newDate), false); + } + }, + [loadTimetableWeek], + ); + + const getItemLayout = useCallback((_: any, index: number) => ({ + length: Dimensions.get("window").width, + offset: Dimensions.get("window").width * index, + index, + }), + [], + ); + + const askForReview = async () => { + StoreReview.isAvailableAsync().then((available) => { + if (available) { + StoreReview.requestReview(); + } + }); + }; + + useEffect(() => { + // on focus + const unsubscribe = navigation.addListener("focus", () => { + AsyncStorage.getItem("review_coursesOpen").then((value) => { + if (value) { + if (parseInt(value) >= 7) { + AsyncStorage.setItem("review_coursesOpen", "0"); + + setTimeout(() => { + AsyncStorage.getItem("review_given").then((value) => { + if(!value) { + askForReview(); + AsyncStorage.setItem("review_given", "true"); + } + }); + }, 1000); + } + else { + AsyncStorage.setItem("review_coursesOpen", (parseInt(value) + 1).toString()); + } + } else { + AsyncStorage.setItem("review_coursesOpen", "1"); + } + }); + }); + + return unsubscribe; + }, []); + + const onDateSelect = (date: Date | undefined) => { + const newDate = new Date(date || 0); + newDate.setHours(0, 0, 0, 0); + setPickerDate(newDate); + + const firstDate = data[0]; + const lastDate = data[data.length - 1]; + + let updatedData = [...data]; + const uniqueDates = new Set(updatedData.map(d => d.getTime())); + + if (newDate < firstDate) { + const dates = []; + for (let d = new Date(firstDate); d >= newDate; d.setDate(d.getDate() - 1)) { + if (!uniqueDates.has(d.getTime())) { + dates.unshift(new Date(d)); + uniqueDates.add(d.getTime()); + } + } + updatedData = [...dates, ...data]; + } else if (newDate > lastDate) { + const dates = []; + for (let d = new Date(lastDate); d <= newDate; d.setDate(d.getDate() + 1)) { + if (!uniqueDates.has(d.getTime())) { + dates.push(new Date(d)); + uniqueDates.add(d.getTime()); + } + } + updatedData = [...data, ...dates]; + } + + setData(updatedData); + + setTimeout(() => { + const index = updatedData.findIndex((d) => d.getTime() === newDate.getTime()); + if (index !== -1) { + flatListRef.current?.scrollToIndex({ index, animated: false }); + } + }, 0); + }; return ( - {!outsideNav && ( - + setShowDatePicker(true)} + onLongPress={() => { + const today = new Date(); + today.setHours(0, 0, 0, 0); + onDateSelect(today); }} - /> - )} - - + > + + + + {pickerDate.toLocaleDateString("fr-FR", { weekday: "long" })} + + + + + + + + {pickerDate.toLocaleDateString("fr-FR", { month: "long" })} + + + {weekFrequency && shouldShowWeekFrequency && ( + + + + {weekFrequency.freqLabel} + + + + ) } + + + + setDisplayMode(mode)} - data={displayModes} + data={[ + { + icon: , + label: "Importer un iCal", + onPress: () => { + navigation.navigate("LessonsImportIcal", {}); + } + }, + ...(weekFrequency != null) ? [{ + icon: , + label: "Afficher type sem.", + onPress: () => { + setShouldShowWeekFrequency(!shouldShowWeekFrequency); + }, + checked: shouldShowWeekFrequency, + }] : [] + ]} > - ) : ( - - ) - } + icon={} + entering={animPapillon(ZoomIn)} + exiting={FadeOut.duration(130)} /> - + + + item.toISOString()} + horizontal + pagingEnabled + showsHorizontalScrollIndicator={false} + onViewableItemsChanged={onViewableItemsChanged} + viewabilityConfig={{ itemVisiblePercentThreshold: 50 }} + getItemLayout={getItemLayout} + initialScrollIndex={50} + onEndReached={() => { + // Charger plus de dates si nécessaire + const lastDate = data[data.length - 1]; + const newDates = Array.from({ length: 30 }, (_, i) => { + const date = new Date(lastDate); + date.setDate(lastDate.getDate() + i + 1); + return date; + }); + setData((prevData) => [...prevData, ...newDates]); + }} + onEndReachedThreshold={0.5} + /> - } - renderHeaderItem={(header) => } - dayBarHeight={50} + { + onDateSelect(date); + }} /> ); }; -export default memo(Lessons); \ No newline at end of file +const styles = StyleSheet.create({ + header: { + paddingHorizontal: 16, + paddingVertical: 8, + position: "absolute", + top: 0, + left: 0, + }, + + weekPicker: { + flexDirection: "row", + justifyContent: "center", + alignItems: "center", + paddingHorizontal: 20, + height: 40, + borderRadius: 80, + gap: 6, + backgroundColor: "rgba(0, 0, 0, 0.05)", + alignSelf: "flex-start", + overflow: "hidden", + }, + + weekPickerText: { + zIndex: 10000, + }, + + weekPickerTextIntl: { + fontSize: 14.5, + fontFamily: "medium", + opacity: 0.7, + }, + + weekPickerTextNbr: { + fontSize: 16.5, + fontFamily: "semibold", + marginTop: -1.5, + }, + + weekButton: { + overflow: "hidden", + borderRadius: 80, + height: 38, + width: 38, + justifyContent: "center", + alignItems: "center", + }, +}); + +export default Lessons; diff --git a/src/views/account/Lessons.old/LessonsHeader.tsx b/src/views/account/Lessons/LessonsHeader.tsx similarity index 100% rename from src/views/account/Lessons.old/LessonsHeader.tsx rename to src/views/account/Lessons/LessonsHeader.tsx diff --git a/src/views/account/Lessons.old/Options/LessonsImportIcal.tsx b/src/views/account/Lessons/Options/LessonsImportIcal.tsx similarity index 100% rename from src/views/account/Lessons.old/Options/LessonsImportIcal.tsx rename to src/views/account/Lessons/Options/LessonsImportIcal.tsx diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 7ae66691a..54632e3e8 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -44,7 +44,7 @@ import { getMenu } from "@/services/menu"; import type { FoodAllergen, FoodLabel, Menu as PawnoteMenu } from "pawnote"; import { PapillonHeaderSelector } from "@/components/Global/PapillonModernHeader"; import AnimatedNumber from "@/components/Global/AnimatedNumber"; -import { LessonsDateModal } from "../Lessons.old/LessonsHeader"; +import { LessonsDateModal } from "../Lessons/LessonsHeader"; import { BookingTerminal, BookingDay } from "@/services/shared/Booking"; import { bookDayFromExternal, getBookingsAvailableFromExternal } from "@/services/booking"; import AccountButton from "@/components/Restaurant/AccountButton"; diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx new file mode 100644 index 000000000..2bbd42c77 --- /dev/null +++ b/src/views/account/Week/Week.tsx @@ -0,0 +1,335 @@ +import * as React from "react"; +import { memo, useCallback, useMemo } from "react"; +import { Text, TouchableOpacity, View } from "react-native"; +import CalendarKit from "@howljs/calendar-kit"; +import { useCurrentAccount } from "@/stores/account"; +import { useTimetableStore } from "@/stores/timetable"; +import { useTheme } from "@react-navigation/native"; +import { updateTimetableForWeekInCache } from "@/services/timetable"; +import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import PapillonPicker from "@/components/Global/PapillonPicker"; +import { AlertTriangle, CalendarDays } from "lucide-react-native"; +import { PapillonHeaderAction } from "@/components/Global/PapillonModernHeader"; +import { getSubjectData } from "@/services/shared/Subject"; +import { PapillonNavigation } from "@/router/refs"; +import PapillonSpinner from "@/components/Global/PapillonSpinner"; +import { TimetableClassStatus } from "@/services/shared/Timetable"; + +const LOCALES = { + en: { + weekDayShort: "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"), + meridiem: { ante: "am", post: "pm" }, + more: "more", + }, + fr: { + weekDayShort: "Dim_Lun_Mar_Mer_Jeu_Ven_Sam".split("_"), + meridiem: { ante: "am", post: "pm" }, + more: "plus", + }, +} as const; + +const EventItem = memo(({ event }) => { + const subjectData = useMemo(() => getSubjectData(event.event.title), [event.event]); + const [layout, setLayout] = React.useState({ width: 0, height: 0 }); + + return ( + setLayout(e.nativeEvent.layout)} + style={[ + { + flex: 1, + borderRadius: 5, + overflow: "hidden", + borderCurve: "continuous", + }, + event.event.status == TimetableClassStatus.CANCELED && { + borderColor: "red", + borderWidth: 2, + } + ]} + activeOpacity={0.7} + onPress={() => { + PapillonNavigation.current.navigate("LessonDocument", { lesson: event.event }); + }} + > + {event.event.statusText && ( + + + + )} + + + 100 && { + fontSize: 15, + letterSpacing: 0.1, + textTransform: "none", + }, + ]} + > + {subjectData.pretty} + + + {event.event.room} + + + + ); +}); + +const HeaderItem = memo(({ header }) => { + const theme = useTheme(); + + const cols = header.extra.columns; + const start = header.startUnix; + + const today = new Date(); + today.setHours(0, 0, 0, 0); + + const todayStamp = today.getTime(); + + return ( + + {Array.from({ length: cols }, (_, i) => ( + + + {new Date(start + + i * 24 * 60 * 60 * 1000 + ).toLocaleDateString("fr-FR", {weekday: "short"})} + + + {new Date(start + + i * 24 * 60 * 60 * 1000 + ).toLocaleDateString("fr-FR", {day: "numeric"})} + + + ))} + + ); +}); + +const displayModes = ["Semaine", "3 jours", "Journée"]; + +const Week = ({ route, navigation }) => { + const theme = useTheme(); + const insets = useSafeAreaInsets(); + + const outsideNav = route.params?.outsideNav; + + const [loadedWeeks, setLoadedWeeks] = React.useState(() => new Set()); + const [isLoading, setIsLoading] = React.useState(false); + + const account = useCurrentAccount((store) => store.account); + const timetables = useTimetableStore((store) => store.timetables); + + const [displayMode, setDisplayMode] = React.useState("Semaine"); + + const customTheme = useMemo(() => ({ + colors: { + primary: theme.colors.primary, + background: theme.colors.background, + border: theme.colors.border + "88", + text: theme.colors.text, + surface: theme.colors.card, + onPrimary: theme.colors.background, + onBackground: theme.colors.text, + onSurface: theme.colors.text, + } + }), [theme.colors]); + + const events = useMemo(() => + Object.values(timetables) + .flat() + .map(event => ({ + id: event.id, + title: event.title, + start: { dateTime: new Date(event.startTimestamp) }, + end: { dateTime: new Date(event.endTimestamp) }, + event: event, + })), + [timetables] + ); + + const loadTimetableWeek = useCallback(async (weekNumber, force = false) => { + if (!force && loadedWeeks.has(weekNumber)) return; + + setIsLoading(true); + try { + await updateTimetableForWeekInCache(account, weekNumber, force); + setLoadedWeeks(prev => new Set([...prev, weekNumber])); + } finally { + setIsLoading(false); + } + }, [account, loadedWeeks]); + + const handleDateChange = useCallback(async (date) => { + const weekNumber = dateToEpochWeekNumber(new Date(date)); + await loadTimetableWeek(weekNumber); + }, [loadTimetableWeek]); + + return ( + + {!outsideNav && ( + + )} + + + setDisplayMode(mode)} + data={displayModes} + > + + ) : ( + + ) + } + /> + + + + } + renderHeaderItem={(header) => } + dayBarHeight={50} + /> + + ); +}; + +export default memo(Week); \ No newline at end of file From d5ff720ea6d774f630a057f59a12fbf2736c3c23 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 5 Feb 2025 19:55:45 +0100 Subject: [PATCH 0492/1144] =?UTF-8?q?feat:=20ajout=20d'une=20interface=20p?= =?UTF-8?q?our=20importer=20des=20cours=20depuis=20un=20calendrier=20exter?= =?UTF-8?q?ne=20et=20mise=20=C3=A0=20jour=20de=20la=20personnalisation=20d?= =?UTF-8?q?es=20onglets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Week/Week.tsx | 58 ++++++++++++++++++- .../actions/BackgroundIUTLannion.tsx | 12 +++- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index 2bbd42c77..16b6a9af5 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import { memo, useCallback, useMemo } from "react"; -import { Text, TouchableOpacity, View } from "react-native"; +import { Linking, Text, TouchableOpacity, View } from "react-native"; import CalendarKit from "@howljs/calendar-kit"; import { useCurrentAccount } from "@/stores/account"; import { useTimetableStore } from "@/stores/timetable"; @@ -15,6 +15,8 @@ import { getSubjectData } from "@/services/shared/Subject"; import { PapillonNavigation } from "@/router/refs"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { TimetableClassStatus } from "@/services/shared/Timetable"; +import { NativeText } from "@/components/Global/NativeComponents"; +import ButtonCta from "@/components/FirstInstallation/ButtonCta"; const LOCALES = { en: { @@ -280,6 +282,60 @@ const Week = ({ route, navigation }) => { /> )} + {account.providers?.includes("ical") && ( + + + + Aucun agenda externe + + + Importez un calendrier depuis une URL de votre agenda externe tel que ADE ou Moodle. + + + + + { + PapillonNavigation.current.navigate("LessonsImportIcal"); + }} + /> + { + Linking.openURL("https://support.papillon.bzh/articles/351142-import-ical"); + }} + /> + + )} + = ({ route, navigatio className: data["relevé"].etudiant.dept_acronym, schoolName: "IUT de Lannion - Université de Rennes", - personalization: await defaultPersonalization() + personalization: await defaultPersonalization({ + tabs: [ + { name: "Home", enabled: true }, + { name: "Week", enabled: true }, + { name: "Grades", enabled: true }, + { name: "Attendance", enabled: true }, + { name: "Menu", enabled: true } + ] + }) }; + // https://planning.univ-rennes1.fr/jsp/custom/modules/plannings/pn8d0kn8.shu + createStoredAccount(local_account); switchTo(local_account); From 5ee4bace200e9dfc702c88e96bb29ce4d2e8eb3e Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 5 Feb 2025 20:25:26 +0100 Subject: [PATCH 0493/1144] =?UTF-8?q?feat:=20ajout=20de=20la=20fonction=20?= =?UTF-8?q?injectClasses=20pour=20remplacer=20les=20cours=20et=20mise=20?= =?UTF-8?q?=C3=A0=20jour=20de=20la=20logique=20de=20r=C3=A9cup=C3=A9ration?= =?UTF-8?q?=20des=20donn=C3=A9es=20iCal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/local/ical.ts | 114 ++++++++++++++--------- src/stores/timetable/index.ts | 11 +++ src/views/account/Week/Week.tsx | 160 ++++++++++++++++++++------------ 3 files changed, 185 insertions(+), 100 deletions(-) diff --git a/src/services/local/ical.ts b/src/services/local/ical.ts index be04028ab..5ddbe2940 100644 --- a/src/services/local/ical.ts +++ b/src/services/local/ical.ts @@ -1,79 +1,107 @@ import { Account } from "@/stores/account/types"; import { Timetable } from "../shared/Timetable"; -import { useTimetableStore } from "@/stores/timetable"; import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; import { reduceIcalToCourse } from "./utils/reduceIcalToCourse"; -import {error} from "@/utils/logger/logger"; +import { error } from "@/utils/logger/logger"; +import { useTimetableStore } from "@/stores/timetable"; const icalParser = require("cal-parser"); - const MERGE_THRESHOLD = 20 * 60 * 1000; // 20 minutes in milliseconds interface CoursesByEpochWeekNumber { - epochWeekNumber: number - courses: Timetable + epochWeekNumber: number; + courses: Timetable; } -export const fetchIcalData = async (account: Account, force = false): Promise => { +export const fetchIcalData = async ( + account: Account, + force = false +): Promise => { if (account.isExternal) { - error(`Cannot fetch iCals for account type ${account.service}`, "fetchIcalData"); + error( + `Cannot fetch iCals for account type ${account.service}`, + "fetchIcalData" + ); return []; } + const identityProvider = account.identityProvider || ""; const courses: Timetable = []; - const icalURLs = account.personalization.icalURLs || []; for (const ical of icalURLs) { - await fetch(ical.url).then(res => res.text()).then(text => { - const parsed = icalParser.parseString(text).events; - for (const event of parsed) { - courses.push(reduceIcalToCourse(event, identityProvider, ical.url)); - } - }); + await fetch(ical.url) + .then((res) => res.text()) + .then((text) => { + const parsed = icalParser.parseString(text).events; + for (const event of parsed) { + courses.push(reduceIcalToCourse(event, identityProvider, ical.url)); + } + }); } - const coursesByEpochWeekNumber: CoursesByEpochWeekNumber[] = courses.reduce((acc: CoursesByEpochWeekNumber[], course) => { - const epochWeekNumber = dateToEpochWeekNumber(new Date(course.startTimestamp)); - const item = acc.find((c) => c.epochWeekNumber === epochWeekNumber); - if (item) { - item.courses.push(course); - } - else { - acc.push({ epochWeekNumber, courses: [course] }); - } - return acc; - }, []); + const coursesByEpochWeekNumber: CoursesByEpochWeekNumber[] = courses.reduce( + (acc: CoursesByEpochWeekNumber[], course) => { + const epochWeekNumber = dateToEpochWeekNumber( + new Date(course.startTimestamp) + ); + const item = acc.find((c) => c.epochWeekNumber === epochWeekNumber); + if (item) { + item.courses.push(course); + } else { + acc.push({ epochWeekNumber, courses: [course] }); + } + return acc; + }, + [] + ); // merge courses with same subject, room, title and itemType, and less than 20 minutes between them for (const { courses } of coursesByEpochWeekNumber) { courses.sort((a, b) => a.startTimestamp - b.startTimestamp); - - const mergedCourses: Timetable = courses.reduce((acc: Timetable, course) => { - const lastCourse = acc[acc.length - 1]; - if (lastCourse && + const mergedCourses: Timetable = courses.reduce( + (acc: Timetable, course) => { + const lastCourse = acc[acc.length - 1]; + if ( + lastCourse && lastCourse.subject === course.subject && lastCourse.room === course.room && lastCourse.title === course.title && lastCourse.itemType === course.itemType && - course.startTimestamp - lastCourse.endTimestamp <= MERGE_THRESHOLD) { - lastCourse.endTimestamp = Math.max(lastCourse.endTimestamp, course.endTimestamp); - } else { - acc.push(course); - } - return acc; - }, []); - + course.startTimestamp - lastCourse.endTimestamp <= MERGE_THRESHOLD + ) { + lastCourse.endTimestamp = Math.max( + lastCourse.endTimestamp, + course.endTimestamp + ); + } else { + acc.push(course); + } + return acc; + }, + [] + ); courses.splice(0, courses.length, ...mergedCourses); } - // for each week, sort by startTimestamp - for (const { courses } of coursesByEpochWeekNumber) { + // Filter out weeks with empty courses before sorting and updating store + const nonEmptyWeeks = coursesByEpochWeekNumber.filter( + (week) => week.courses.length > 0 + ); + + // for each non-empty week, sort by startTimestamp + for (const { courses } of nonEmptyWeeks) { courses.sort((a, b) => a.startTimestamp - b.startTimestamp); } - for (const { epochWeekNumber, courses } of coursesByEpochWeekNumber) { - useTimetableStore.getState().updateClasses(epochWeekNumber, courses); - } + const newData = nonEmptyWeeks.reduce( + (acc, { epochWeekNumber, courses }) => { + acc[epochWeekNumber] = courses; + return acc; + }, + {} + ); + + useTimetableStore.getState().injectClasses(newData); - return coursesByEpochWeekNumber; + return nonEmptyWeeks; }; diff --git a/src/stores/timetable/index.ts b/src/stores/timetable/index.ts index 3b83244a5..c416929e6 100644 --- a/src/stores/timetable/index.ts +++ b/src/stores/timetable/index.ts @@ -23,6 +23,17 @@ export const useTimetableStore = create()( log(`[timetable:updateClasses]: updated classes for week ${weekNumber}`, "timetable:updateClasses"); }, + injectClasses: (data) => { + log("replacing classes", "timetable:replaceClasses"); + + set((state) => { + return { + timetables: data, + }; + }); + + log(`[timetable:replaceClasses]: replaced classes for week ${data.weekNumber}`, "timetable:replaceClasses"); + }, removeClasses: (weekNumber) => { log(`removing classes for week ${weekNumber}`, "timetable:removeClasses"); diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index 16b6a9af5..83c7dcf00 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -167,20 +167,22 @@ const HeaderItem = memo(({ header }) => { } ]} > - - {new Date(start + {cols > 1 && ( + + {new Date(start + i * 24 * 60 * 60 * 1000 - ).toLocaleDateString("fr-FR", {weekday: "short"})} - + ).toLocaleDateString("fr-FR", {weekday: "short"})} + + )} { > {new Date(start + i * 24 * 60 * 60 * 1000 - ).toLocaleDateString("fr-FR", {day: "numeric"})} + ).toLocaleDateString("fr-FR", + cols > 1 ? {day: "numeric"} : { + weekday: "long", + day: "numeric", + month: "long", + })} ))} @@ -221,7 +228,6 @@ const Week = ({ route, navigation }) => { const outsideNav = route.params?.outsideNav; - const [loadedWeeks, setLoadedWeeks] = React.useState(() => new Set()); const [isLoading, setIsLoading] = React.useState(false); const account = useCurrentAccount((store) => store.account); @@ -256,22 +262,44 @@ const Week = ({ route, navigation }) => { ); const loadTimetableWeek = useCallback(async (weekNumber, force = false) => { - if (!force && loadedWeeks.has(weekNumber)) return; + if (!force) { + if (timetables[weekNumber]) return; + } setIsLoading(true); - try { - await updateTimetableForWeekInCache(account, weekNumber, force); - setLoadedWeeks(prev => new Set([...prev, weekNumber])); - } finally { - setIsLoading(false); - } - }, [account, loadedWeeks]); + requestAnimationFrame(async () => { + try { + await updateTimetableForWeekInCache(account, weekNumber, force); + } finally { + setIsLoading(false); + } + }); + }, [account, timetables]); const handleDateChange = useCallback(async (date) => { const weekNumber = dateToEpochWeekNumber(new Date(date)); await loadTimetableWeek(weekNumber); }, [loadTimetableWeek]); + const [openedIcalModal, setOpenedIcalModal] = React.useState(false); + + React.useEffect(() => { + navigation.addListener("focus", async () => { + if(openedIcalModal) { + setIsLoading(true); + requestAnimationFrame(async () => { + const weekNumber = dateToEpochWeekNumber(new Date()); + await loadTimetableWeek(weekNumber, true); + setOpenedIcalModal(false); + }); + } + }); + + return () => { + navigation.removeListener("focus"); + }; + }, [openedIcalModal]); + return ( {!outsideNav && ( @@ -282,7 +310,7 @@ const Week = ({ route, navigation }) => { /> )} - {account.providers?.includes("ical") && ( + {account.providers?.includes("ical") && Object.values(timetables).flat().length === 0 && ( { padding: 24, }} > - - - Aucun agenda externe - - - Importez un calendrier depuis une URL de votre agenda externe tel que ADE ou Moodle. - + {isLoading ? ( + + ) : ( + <> + + + Aucun agenda externe + + + Importez un calendrier depuis une URL de votre agenda externe tel que ADE ou Moodle. + - + - { - PapillonNavigation.current.navigate("LessonsImportIcal"); - }} - /> - { - Linking.openURL("https://support.papillon.bzh/articles/351142-import-ical"); - }} - /> + { + setOpenedIcalModal(true); + setTimeout(() => { + PapillonNavigation.current.navigate("LessonsImportIcal"); + }, 100); + }} + /> + { + Linking.openURL("https://support.papillon.bzh/articles/351142-import-ical"); + }} + /> + + )} )} @@ -350,7 +389,14 @@ const Week = ({ route, navigation }) => { direction="left" delay={0} selected={displayMode} - onSelectionChange={(mode) => setDisplayMode(mode)} + onSelectionChange={(mode) => { + setIsLoading(true); + requestAnimationFrame(() => { + + setDisplayMode(mode); + setIsLoading(false); + }); + }} data={displayModes} > Date: Wed, 5 Feb 2025 20:30:23 +0100 Subject: [PATCH 0494/1144] =?UTF-8?q?feat:=20ajout=20de=20la=20propri?= =?UTF-8?q?=C3=A9t=C3=A9=20zIndex=20pour=20am=C3=A9liorer=20l'affichage=20?= =?UTF-8?q?des=20=C3=A9l=C3=A9ments=20d'=C3=A9v=C3=A9nement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Week/Week.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index 83c7dcf00..886b78329 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -105,6 +105,7 @@ const EventItem = memo(({ event }) => { letterSpacing: 0.2, fontFamily: "semibold", textTransform: "uppercase", + zIndex: 100, }, layout.width > 100 && { fontSize: 15, @@ -123,6 +124,7 @@ const EventItem = memo(({ event }) => { letterSpacing: 0.2, fontFamily: "medium", opacity: 0.6, + zIndex: 100, }} > {event.event.room} From 96dec3c394948f1ec2be5697069f6a5395e5eb5c Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 5 Feb 2025 20:33:50 +0100 Subject: [PATCH 0495/1144] =?UTF-8?q?feat:=20optimisation=20de=20la=20gest?= =?UTF-8?q?ion=20des=20=C3=A9v=C3=A9nements=20avec=20des=20styles=20am?= =?UTF-8?q?=C3=A9lior=C3=A9s=20et=20une=20logique=20de=20rendu=20simplifi?= =?UTF-8?q?=C3=A9e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Week/Week.tsx | 182 ++++++++++++++++++-------------- 1 file changed, 101 insertions(+), 81 deletions(-) diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index 886b78329..0ca3f04c2 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import { memo, useCallback, useMemo } from "react"; -import { Linking, Text, TouchableOpacity, View } from "react-native"; +import { Linking, StyleSheet, Text, TouchableOpacity, View } from "react-native"; import CalendarKit from "@howljs/calendar-kit"; import { useCurrentAccount } from "@/stores/account"; import { useTimetableStore } from "@/stores/timetable"; @@ -32,101 +32,58 @@ const LOCALES = { } as const; const EventItem = memo(({ event }) => { - const subjectData = useMemo(() => getSubjectData(event.event.title), [event.event]); + const subjectData = useMemo( + () => getSubjectData(event.event.title), + [event.event.title] // Optimized dependency array + ); + const [layout, setLayout] = React.useState({ width: 0, height: 0 }); + const isCanceled = event.event.status === TimetableClassStatus.CANCELED; + const isWide = layout.width > 100; + + const handlePress = () => { + PapillonNavigation.current.navigate("LessonDocument", { lesson: event.event }); + }; + + const containerStyle = [ + styles.container, + isCanceled && styles.canceledContainer + ]; + + const contentStyle = [ + styles.contentContainer, + { backgroundColor: subjectData.color }, + isCanceled && styles.canceledContent + ]; + + const titleStyle = [ + styles.title, + isWide && styles.wideTitleVariant + ]; + return ( setLayout(e.nativeEvent.layout)} - style={[ - { - flex: 1, - borderRadius: 5, - overflow: "hidden", - borderCurve: "continuous", - }, - event.event.status == TimetableClassStatus.CANCELED && { - borderColor: "red", - borderWidth: 2, - } - ]} + style={containerStyle} activeOpacity={0.7} - onPress={() => { - PapillonNavigation.current.navigate("LessonDocument", { lesson: event.event }); - }} + onPress={handlePress} > {event.event.statusText && ( - + )} - - - 100 && { - fontSize: 15, - letterSpacing: 0.1, - textTransform: "none", - }, - ]} - > + + {subjectData.pretty} - + {event.event.room} @@ -436,4 +393,67 @@ const Week = ({ route, navigation }) => { ); }; -export default memo(Week); \ No newline at end of file + + +const styles = StyleSheet.create({ + container: { + flex: 1, + borderRadius: 5, + overflow: "hidden", + borderCurve: "continuous", + }, + canceledContainer: { + borderColor: "red", + borderWidth: 2, + }, + alertBadge: { + position: "absolute", + top: -8, + right: -8, + backgroundColor: "#00000099", + zIndex: 1000, + borderRadius: 30, + borderColor: "#ffffff99", + borderWidth: 2, + padding: 4, + paddingBottom: 2, + paddingLeft: 2, + }, + alertIcon: { + margin: 4, + }, + contentContainer: { + flex: 1, + borderRadius: 0, + padding: 4, + flexDirection: "column", + gap: 2, + }, + canceledContent: { + opacity: 0.3, + backgroundColor: "grey", + }, + title: { + color: "white", + fontSize: 13, + letterSpacing: 0.2, + fontFamily: "semibold", + textTransform: "uppercase", + zIndex: 100, + }, + wideTitleVariant: { + fontSize: 15, + letterSpacing: 0.1, + textTransform: "none", + }, + room: { + color: "white", + fontSize: 13, + letterSpacing: 0.2, + fontFamily: "medium", + opacity: 0.6, + zIndex: 100, + }, +}); + +export default memo(Week); From 4097f4e26ef43980901fa8c2eee04e43bcbf0ab3 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 5 Feb 2025 20:37:38 +0100 Subject: [PATCH 0496/1144] feat: ajout des fournisseurs "ical" et "moodle" pour les connexions UnivRennes1 et UnivRennes2 --- src/views/login/IdentityProvider/providers/UnivRennes1.tsx | 2 ++ src/views/login/IdentityProvider/providers/UnivRennes2.tsx | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/views/login/IdentityProvider/providers/UnivRennes1.tsx b/src/views/login/IdentityProvider/providers/UnivRennes1.tsx index 85d7f2a69..009840c8b 100644 --- a/src/views/login/IdentityProvider/providers/UnivRennes1.tsx +++ b/src/views/login/IdentityProvider/providers/UnivRennes1.tsx @@ -37,6 +37,8 @@ const UnivRennes1_Login: Screen<"UnivRennes1_Login"> = ({ navigation }) => { rawData: data }, + providers: ["ical", "moodle"], + localID: uuid(), service: AccountService.Local, diff --git a/src/views/login/IdentityProvider/providers/UnivRennes2.tsx b/src/views/login/IdentityProvider/providers/UnivRennes2.tsx index a0014dfee..7d618bb43 100644 --- a/src/views/login/IdentityProvider/providers/UnivRennes2.tsx +++ b/src/views/login/IdentityProvider/providers/UnivRennes2.tsx @@ -78,6 +78,8 @@ const UnivRennes2_Login: Screen<"UnivRennes2_Login"> = ({ navigation }) => { rawData: data }, + providers: ["ical", "moodle"], + localID: uuid(), service: AccountService.Local, From dec2f3f074968bf551fcff426c2f68bef2a405b7 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 5 Feb 2025 20:38:31 +0100 Subject: [PATCH 0497/1144] =?UTF-8?q?feat:=20suppression=20des=20logs=20de?= =?UTF-8?q?=20d=C3=A9bogage=20dans=20les=20composants=20de=20cr=C3=A9ation?= =?UTF-8?q?=20de=20chat=20et=20d'=C3=A9valuation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Chat/Modals/ChatCreate.tsx | 1 - src/views/account/Evaluation/Evaluation.tsx | 1 - .../login/IdentityProvider/actions/BackgroundIUTLannion.tsx | 3 --- 3 files changed, 5 deletions(-) diff --git a/src/views/account/Chat/Modals/ChatCreate.tsx b/src/views/account/Chat/Modals/ChatCreate.tsx index ca80295b1..9e35c3059 100644 --- a/src/views/account/Chat/Modals/ChatCreate.tsx +++ b/src/views/account/Chat/Modals/ChatCreate.tsx @@ -173,7 +173,6 @@ const ChatCreate: Screen<"ChatCreate"> = ({ navigation }) => { 0)} onPress={() => { - console.log("onPress"); if (!subject) { Alert.alert( "Veux-tu continuer sans objet ?", diff --git a/src/views/account/Evaluation/Evaluation.tsx b/src/views/account/Evaluation/Evaluation.tsx index f3a47d0e6..8f9ab62bc 100644 --- a/src/views/account/Evaluation/Evaluation.tsx +++ b/src/views/account/Evaluation/Evaluation.tsx @@ -91,7 +91,6 @@ const Evaluation: Screen<"Evaluation"> = ({ route, navigation }) => { subjectName: evaluation.subjectName, evaluations: [evaluation], }); - console.log(evaluation.name); } } diff --git a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx index 2b71954b6..42fb67113 100644 --- a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx +++ b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx @@ -117,7 +117,6 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio const retreiveNextSemestre = async (cs: number, semestres: any[] = semestresToRetrieve) => { const sem = semestres[cs]; - console.log(sem); setStep("Récupération du semestre " + sem.semestre_id); wbref.current?.injectJavaScript(` window.location.href = "https://notes9.iutlan.univ-rennes1.fr/services/data.php?q=relev%C3%A9Etudiant&semestre=" + ${sem.formsemestre_id}; @@ -133,7 +132,6 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio } const semesterName = "Semestre " + semestresToRetrieve[currentSemestre].semestre_id; - console.log(semesterName); newServiceData["semestres"][semesterName] = data; mutateProperty("serviceData", newServiceData); @@ -295,7 +293,6 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio onLoad={(data) => { const url = data.nativeEvent.url; - console.log(url); if(url.startsWith("https://sso-cas.univ-rennes.fr//login?")) { injectPassword(); From c5ee5af97775b4cc584705be376adc3e7c54e93e Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 5 Feb 2025 20:46:13 +0100 Subject: [PATCH 0498/1144] =?UTF-8?q?feat:=20ajout=20d'une=20v=C3=A9rifica?= =?UTF-8?q?tion=20pour=20=C3=A9viter=20les=20appels=20inutiles=20lorsque?= =?UTF-8?q?=20aucune=20URL=20iCal=20n'est=20fournie?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/local/ical.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/services/local/ical.ts b/src/services/local/ical.ts index 5ddbe2940..a4901ad3b 100644 --- a/src/services/local/ical.ts +++ b/src/services/local/ical.ts @@ -28,6 +28,10 @@ export const fetchIcalData = async ( const courses: Timetable = []; const icalURLs = account.personalization.icalURLs || []; + if(icalURLs.length === 0) { + return []; + } + for (const ical of icalURLs) { await fetch(ical.url) .then((res) => res.text()) From f67412593c55822914464cfcaba9e83b1e5976a5 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 5 Feb 2025 20:46:32 +0100 Subject: [PATCH 0499/1144] =?UTF-8?q?feat:=20ajout=20d'une=20v=C3=A9rifica?= =?UTF-8?q?tion=20pour=20retourner=20un=20tableau=20vide=20si=20aucun=20co?= =?UTF-8?q?urs=20n'est=20trouv=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/local/ical.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/services/local/ical.ts b/src/services/local/ical.ts index a4901ad3b..7eb491e23 100644 --- a/src/services/local/ical.ts +++ b/src/services/local/ical.ts @@ -43,6 +43,10 @@ export const fetchIcalData = async ( }); } + if (courses.length === 0) { + return []; + } + const coursesByEpochWeekNumber: CoursesByEpochWeekNumber[] = courses.reduce( (acc: CoursesByEpochWeekNumber[], course) => { const epochWeekNumber = dateToEpochWeekNumber( From bf8c1c367c1478b3b0f1a87fe68a13c747141357 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 5 Feb 2025 20:53:05 +0100 Subject: [PATCH 0500/1144] =?UTF-8?q?feat:=20remplacement=20de=20la=20vue?= =?UTF-8?q?=20HTML=20par=20MaskedView=20pour=20am=C3=A9liorer=20l'affichag?= =?UTF-8?q?e=20des=20devoirs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../account/Grades/Graph/GradesAverage.tsx | 1 - src/views/account/Homeworks/Atoms/Item.tsx | 32 +++++++++++-------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/views/account/Grades/Graph/GradesAverage.tsx b/src/views/account/Grades/Graph/GradesAverage.tsx index c8f2d70dc..a94ae46f4 100644 --- a/src/views/account/Grades/Graph/GradesAverage.tsx +++ b/src/views/account/Grades/Graph/GradesAverage.tsx @@ -103,7 +103,6 @@ const GradesAverageGraph: React.FC = ({ setMinAvg(minAvg); hst = hst.filter((p) => isNaN(p.value) === false); - console.log(hst.map((p) => p.value)); graphRef.current?.updateData({ xAxis: hst.map((p, i) => new Date(p.date).getTime()), diff --git a/src/views/account/Homeworks/Atoms/Item.tsx b/src/views/account/Homeworks/Atoms/Item.tsx index d26aa6a4d..47a768063 100644 --- a/src/views/account/Homeworks/Atoms/Item.tsx +++ b/src/views/account/Homeworks/Atoms/Item.tsx @@ -19,6 +19,7 @@ import LinkFavicon, { getURLDomain } from "@/components/Global/LinkFavicon"; import { AutoFileIcon } from "@/components/Global/FileIcon"; import { timestampToString } from "@/utils/format/DateHelper"; import parse_homeworks from "@/utils/format/format_pronote_homeworks"; +import MaskedView from "@react-native-masked-view/masked-view"; interface HomeworkItemProps { @@ -196,20 +197,23 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total } } }} > - ${parse_homeworks(homework.content).replace("\n", "")}`} stylesheet={stylesText} /> - {shouldShowMoreGradient && ( - - )} + + } + > + ${parse_homeworks(homework.content).replace("\n", "")}`} stylesheet={stylesText} /> + {route.name === "HomeScreen" && ( From 11a687edb6a5a062ba89edb91147647efe74627e Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 5 Feb 2025 21:19:29 +0100 Subject: [PATCH 0501/1144] =?UTF-8?q?feat:=20ajout=20d'images=20de=20masqu?= =?UTF-8?q?es=20et=20am=C3=A9lioration=20du=20style=20des=20=C3=A9v=C3=A9n?= =?UTF-8?q?ements=20dans=20la=20vue=20de=20la=20semaine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/images/mask_stripes_large.png | Bin 0 -> 8033 bytes assets/images/mask_stripes_long.png | Bin 0 -> 2101 bytes assets/images/mask_stripes_long_white.png | Bin 0 -> 1712 bytes assets/images/mask_stripes_repeat.png | Bin 0 -> 250 bytes assets/images/mask_stripes_texture.png | Bin 0 -> 392 bytes src/views/account/Week/Week.tsx | 32 ++++++++++++++++++---- 6 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 assets/images/mask_stripes_large.png create mode 100644 assets/images/mask_stripes_long.png create mode 100644 assets/images/mask_stripes_long_white.png create mode 100644 assets/images/mask_stripes_repeat.png create mode 100644 assets/images/mask_stripes_texture.png diff --git a/assets/images/mask_stripes_large.png b/assets/images/mask_stripes_large.png new file mode 100644 index 0000000000000000000000000000000000000000..8eb599f2559d8edb1251556b9f011156343b8195 GIT binary patch literal 8033 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 literal 0 HcmV?d00001 diff --git a/assets/images/mask_stripes_long.png b/assets/images/mask_stripes_long.png new file mode 100644 index 0000000000000000000000000000000000000000..f69786891b394adb3164adda499261a2b09d4a26 GIT binary patch literal 2101 zcmb7FdrT8|9B&a7WC*h|R1g(M7nyoZHDfHL3@I;Lz^KG+(X&lpEZfvcF}4L1-3HdE zwGzA*iN=U__*Tqj^-f?oC|(B|=C{*M7fii^5pq{%HHX^zQflem}31 zuggdYoV8#UjYbPhOXaPn(Pq8^b+F$I@J)~IDFZ+L-=%&orqKcz@I$9nRL+C{iPxvB zp;fB5t>zaNEu zIYPe`-|t)J7jQcG@tx5F`60_3={L^UZgWrNk0mv=)f{O}zFrs}e`>@vlrlWB=c}O# zVTN+>QgJLpzl6P)&q~im59@1#idM3*Bg11|(fZo_D*f;VlUg_Q*OuvmholnRFUJbh zdk^5dUO0R#bJS>EVGLq5j}RNxPWDMTz8y)~ADhHP`*PaI9%&lV-+%UA6=oBdb``|V z(Jy1$@{lr=+sc=mj ziKL@}8shocpVh+I$~2?^clt2V#s)HinxTIoF)aFH(JzjYqF87A-6-~odQ-vz#k3Zb zS`pO!H4-UP#bjUcvD_xCYZ2pNU-3bKtfOIbDHjp0R9dGCWb+l&+C|h3o*u-Z6vV~0 znz6B@ev^?ygO$B7L51F2!tpG z1qI#cuMgr^#(ayEP0Qd!RK@AXxdc$**2hz0bq0&0nn$2tpYXQ?tKE&db2tEmDZzdH zz-aez37s4Au3}n7ka*$Wc(ImuE|eub+Gs4U!J1E89>S|Im+KhR1w$&S-o{iVW}oyw zC)Zy9BfBUSMG=K3F{b_1`X>TbsgA=CFw~m>kbRD0sG*B58{{DACJ(zFo|webo+tvZ z#WMTMf_2iK4f=5 zLecD%86J%Q++`icv!RdxyjKKZXWH55=v6*Wi&+lrpbxC^g(w^HU%Dl#h}rTV8g+Um zXy9*KRUaN%=I(|A{k+u!O-L`l+XvI#@GT2_?@i^xnz`fF+aLw~SE2I^;1Iw(0J7G) z-D)*E@YP<+S=7P`9X?Ct)o>e0=&|1u#}eOBHftgW3|FJrQt=0dRF+0(O1OnJ(|d|= zPLc&)>m`Hf*)6`}1He7TDB@5Igx_jLnMC0tBvVPHgwXCCDb>y{LhCk1w!?$~@VnSuFXJPeY-%`Q$O-Cq!g%U7 zmjOXS34pA*PZ9!|9Lof{py-E$07FNi+-CA1dPhGe^jXPoP{)%rEg5_t0e5wqNAPjA zx(}AfF_**cMb~|FcNA)9C0Et2R!T!y)ZtV@Zf(>=%C^tj(RVk@-!fOtNp-PfpWzBj zqYH=Egz(oN=^-N@E+qdT=CTQZFBnUxWiPn=S^ zSS~<>z~+YhPSVuAmfFjktN#zK)(2#Nm^?tA7#4H!4OTd*#+`K?q$&N83&z~i`bsGN z*MaYiOZ&KlCa?x~_?LQKrwnwc7E43An#c{=M<$21{vy`3TORt;O;KTVB=D=EE33{_ z%ur;0_$Tx_c=vEgO?iuNJJ0e^2RaE%6XO)*s{182As|3fOqIx9hhMPUR}>5)wW0jp UJ<{!C;4M!}OUmF~UL)N950gnEJ^%m! literal 0 HcmV?d00001 diff --git a/assets/images/mask_stripes_long_white.png b/assets/images/mask_stripes_long_white.png new file mode 100644 index 0000000000000000000000000000000000000000..feef9f8121560c4f4a8f3855981aec98bf4bb921 GIT binary patch literal 1712 zcmbtVZA=?w96u~Dwo;05NjG31e%z8aTHQ)vCA>^v%UT7^GP54wLh7~>I$l~@-e#jX zos7k0UUWU0#%&kkhQbO9l$J6~{ZQ9-9topGSA+HXLU}8cRHpU0Qo{B@%i@Q7?w;S1 zd+z`Ly*&5nTl^B*uC!ec1kuV$i_0Ns$0B%6O-ck~+PzGm2Ys#D zL#{QHmlQ&MmJAP=#ES}!7eG*dH}y$%0t6)@1P0LpY2g*e^*k}Eg6LmwkN9cDtyziY|#RTCApU=0%C9DF9 zA*_46AZg#n9m_L@R#ON<6X?!W{b-ZJ?k8*bm4$H7VUz174wNg>VG*3U%vyQz)^E5FKA9UKwbU4*Rs`H(8G700cU7<-Yp#Oo@xK?r*xB^|SQP z?AlWjJE6SmYyE*)IWNg9B0>;R(t^Lsa`YnX%bLKbt#|tsZ}h~gWj)SxR=l|Jy5|8J z9Cpre)pCS)XL1w~8j{ifL5f1o`Y!WPJVPZNasI^Vv;+zPH|ultQ=yAO!_04a5dmI zpHU0vxmo>YBx>79b8y<#SHVixi^-MMfrr}!8rn$j^<>X`8RYHk-^!F4=$a# zDfPPE047w!CIwX^#?WGP6el)T_e$+pz0HM*6 zO>S@uWP~j?^I_~6$8^H;IiMd!v>yy%uhd&XY+Jmr;jc@MMZh_zIT}YGrf#N*_ddds zak@2u)(bdxElnrJV0iYq-J%{r^26hl>*teE;$uKYFqHjE(}?qR>9pZnS?IM!uj}*E z?mSkbeN$Lr)aZ}41GJ0X#2BZN$e1X)#aDm!ftqUR3w+g4DwKdDQEEU02lQKke=0Bk pM#t8}{4c5E4F)_c@kxz64@7S$C>uDL zF=}}wFom7uD4CL(d6S{r<t=|Bu*!NKbN2)O!5oZTtT4@1HJ1y`-9rc~cA->!f8JQmg-L?8{JcT { + const theme = useTheme(); + const subjectData = useMemo( () => getSubjectData(event.event.title), [event.event.title] // Optimized dependency array @@ -48,18 +50,25 @@ const EventItem = memo(({ event }) => { const containerStyle = [ styles.container, + { backgroundColor: theme.colors.card }, isCanceled && styles.canceledContainer ]; const contentStyle = [ styles.contentContainer, - { backgroundColor: subjectData.color }, + { backgroundColor: subjectData.color + "22", borderColor: theme.colors.border }, isCanceled && styles.canceledContent ]; const titleStyle = [ styles.title, - isWide && styles.wideTitleVariant + isWide && styles.wideTitleVariant, + { color: theme.colors.text } + ]; + + const roomStyle = [ + styles.room, + { color: theme.colors.text } ]; return ( @@ -79,11 +88,23 @@ const EventItem = memo(({ event }) => { /> )} + + + {subjectData.pretty} - + {event.event.room} @@ -428,13 +449,13 @@ const styles = StyleSheet.create({ padding: 4, flexDirection: "column", gap: 2, + borderWidth: 1, }, canceledContent: { opacity: 0.3, backgroundColor: "grey", }, title: { - color: "white", fontSize: 13, letterSpacing: 0.2, fontFamily: "semibold", @@ -447,7 +468,6 @@ const styles = StyleSheet.create({ textTransform: "none", }, room: { - color: "white", fontSize: 13, letterSpacing: 0.2, fontFamily: "medium", From 00e2fe4e8747cfc2f39bc3f6b1caf50a94db2efa Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 5 Feb 2025 21:31:28 +0100 Subject: [PATCH 0502/1144] =?UTF-8?q?feat:=20mise=20=C3=A0=20jour=20des=20?= =?UTF-8?q?styles=20des=20=C3=A9v=C3=A9nements=20avec=20des=20couleurs=20s?= =?UTF-8?q?p=C3=A9cifiques=20et=20remplacement=20de=20l'image=20de=20fond?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Week/Week.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index cb165041e..c6f9bfe7a 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -50,7 +50,7 @@ const EventItem = memo(({ event }) => { const containerStyle = [ styles.container, - { backgroundColor: theme.colors.card }, + { backgroundColor: subjectData.color }, isCanceled && styles.canceledContainer ]; @@ -63,12 +63,12 @@ const EventItem = memo(({ event }) => { const titleStyle = [ styles.title, isWide && styles.wideTitleVariant, - { color: theme.colors.text } + { color: "#ffffff" } ]; const roomStyle = [ styles.room, - { color: theme.colors.text } + { color: "#ffffff" } ]; return ( @@ -90,11 +90,11 @@ const EventItem = memo(({ event }) => { )} Date: Wed, 5 Feb 2025 21:36:25 +0100 Subject: [PATCH 0503/1144] refractor: update `package-lock.json` --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 32bc88c36..81cb778a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.8.2", + "version": "7.8.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.8.2", + "version": "7.8.3", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.3", "@candlefinance/app-icon": "^0.4.5", From 399eb557672a3eed672a48285cd2b68f21f924f4 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 5 Feb 2025 21:42:20 +0100 Subject: [PATCH 0504/1144] =?UTF-8?q?feat:=20mise=20=C3=A0=20jour=20de=20l?= =?UTF-8?q?a=20condition=20de=20chargement=20pour=20v=C3=A9rifier=20si=20l?= =?UTF-8?q?es=20emplois=20du=20temps=20sont=20vides?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Week/Week.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index c6f9bfe7a..b45dffd91 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -265,7 +265,7 @@ const Week = ({ route, navigation }) => { React.useEffect(() => { navigation.addListener("focus", async () => { - if(openedIcalModal) { + if(Object.values(timetables).flat().length === 0) { setIsLoading(true); requestAnimationFrame(async () => { const weekNumber = dateToEpochWeekNumber(new Date()); From 80a173b5da5e43e8fe6b6e64f462039f1402d147 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 5 Feb 2025 21:47:25 +0100 Subject: [PATCH 0505/1144] =?UTF-8?q?feat:=20ajout=20d'une=20v=C3=A9rifica?= =?UTF-8?q?tion=20pour=20charger=20le=20calendrier=20si=20les=20emplois=20?= =?UTF-8?q?du=20temps=20sont=20vides?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Week/Week.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index b45dffd91..c73c23e61 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -278,7 +278,18 @@ const Week = ({ route, navigation }) => { return () => { navigation.removeListener("focus"); }; - }, [openedIcalModal]); + }, [openedIcalModal, timetables]); + + React.useEffect(() => { + if(Object.values(timetables).flat().length === 0) { + setIsLoading(true); + requestAnimationFrame(async () => { + const weekNumber = dateToEpochWeekNumber(new Date()); + await loadTimetableWeek(weekNumber, true); + setOpenedIcalModal(false); + }); + } + }, [account?.personalization?.icalURLs]); return ( From 93528deb789cac06051759cfa805fb9a12f98eea Mon Sep 17 00:00:00 2001 From: Bulgus <88266443+Bulgus@users.noreply.github.com> Date: Wed, 5 Feb 2025 21:49:10 +0100 Subject: [PATCH 0506/1144] wip 2 --- ios/Papillon/Info.plist | 216 ++++++++++++------------- src/views/settings/SettingsProfile.tsx | 14 +- 2 files changed, 116 insertions(+), 114 deletions(-) diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index c30522e5f..cb1269f35 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -1,111 +1,111 @@ - - CADisableMinimumFrameDurationOnPhone - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - Papillon - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 7.8.2 - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleURLSchemes - - papillon - izly - - - - CFBundleVersion - 1 - ITSAppUsesNonExemptEncryption - - LSApplicationQueriesSchemes - - fb - instagram - twitter - tiktoksharesdk - - LSRequiresIPhoneOS - - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - NSAllowsLocalNetworking - - - NSCameraUsageDescription - Papillon utilise ta caméra pour scanner des QR-codes pour te connecter, pour capturer des documents, ou pour des fonctionnalités amusantes telles que les réactions. - NSLocationAlwaysAndWhenInUseUsageDescription - Autoriser $(PRODUCT_NAME) à utiliser ta localisation. - NSLocationAlwaysUsageDescription - Autoriser $(PRODUCT_NAME) à utiliser ta localisation. - NSLocationWhenInUseUsageDescription - Papillon utilise ton emplacement pour trouver les établissements autour de toi. - NSMicrophoneUsageDescription - Papillon utilise ton micro pour enregistrer des travaux audio ou des cours. - NSMotionUsageDescription - Papillon utilise les capteurs de mouvement de ton appareil pour de nombreuses fonctionnalités interactives. - NSPhotoLibraryAddUsageDescription - Allow $(PRODUCT_NAME) to save photos - NSPhotoLibraryUsageDescription - Papillon utilise tes photos et vidéos pour personnaliser ton profil, ta gestion des cours et bien plus. - UIAppFonts - - FixelText-Bold.ttf - FixelText-Light.ttf - FixelText-Medium.ttf - FixelText-Regular.ttf - FixelText-SemiBold.ttf - - UIBackgroundModes - - fetch - remote-notification - - UILaunchStoryboardName - SplashScreen - UIRequiredDeviceCapabilities - - arm64 - - UIRequiresFullScreen - - UIStatusBarStyle - UIStatusBarStyleDefault - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIUserInterfaceStyle - Automatic - UIViewControllerBasedStatusBarAppearance - - - \ No newline at end of file + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Papillon + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 7.8.2 + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleURLSchemes + + papillon + izly + + + + CFBundleVersion + 1 + ITSAppUsesNonExemptEncryption + + LSApplicationQueriesSchemes + + fb + instagram + twitter + tiktoksharesdk + + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSAllowsLocalNetworking + + + NSCameraUsageDescription + Papillon utilise ta caméra pour scanner des QR-codes pour te connecter, pour capturer des documents, ou pour des fonctionnalités amusantes telles que les réactions. + NSLocationAlwaysAndWhenInUseUsageDescription + Autoriser $(PRODUCT_NAME) à utiliser ta localisation. + NSLocationAlwaysUsageDescription + Autoriser $(PRODUCT_NAME) à utiliser ta localisation. + NSLocationWhenInUseUsageDescription + Papillon utilise ton emplacement pour trouver les établissements autour de toi. + NSMicrophoneUsageDescription + Papillon utilise ton micro pour enregistrer des travaux audio ou des cours. + NSMotionUsageDescription + Papillon utilise les capteurs de mouvement de ton appareil pour de nombreuses fonctionnalités interactives. + NSPhotoLibraryAddUsageDescription + Allow $(PRODUCT_NAME) to save photos + NSPhotoLibraryUsageDescription + Papillon utilise tes photos et vidéos pour personnaliser ton profil, ta gestion des cours et bien plus. + UIAppFonts + + FixelText-Bold.ttf + FixelText-Light.ttf + FixelText-Medium.ttf + FixelText-Regular.ttf + FixelText-SemiBold.ttf + + UIBackgroundModes + + fetch + remote-notification + + UILaunchStoryboardName + SplashScreen + UIRequiredDeviceCapabilities + + arm64 + + UIRequiresFullScreen + + UIStatusBarStyle + UIStatusBarStyleDefault + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIUserInterfaceStyle + Automatic + UIViewControllerBasedStatusBarAppearance + + + diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index aaf940124..f1c868f3a 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -50,15 +50,17 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { const [profilePic, setProfilePic] = useState(account.personalization.profilePictureB64); const [loadingPic, setLoadingPic] = useState(false); - const resetProfilePic = async (setProfilePic: (pic: string | undefined) => void, setLoadingPic: (loading: boolean) => void) => { - console.warn("HERE") - const defaultPic = await getDefaultProfilePicture(account); - setProfilePic(defaultPic); - console.warn("HERE2") + const resetProfilePic = async () => { + setLoadingPic(true); + + const img = await getDefaultProfilePicture(account); + setProfilePic(img); mutateProperty("personalization", { ...account.personalization, - profilePictureB64: defaultPic, + profilePictureB64: img, }); + + setLoadingPic(false); }; const updateProfilePic = async () => { From 684add15b1d14fb6364a75e706a43f27113ff335 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 5 Feb 2025 22:01:00 +0100 Subject: [PATCH 0507/1144] =?UTF-8?q?Revert=20"fix(SettingsTabs):=20am?= =?UTF-8?q?=C3=A9liorer=20le=20chargement=20des=20onglets=20personnalis?= =?UTF-8?q?=C3=A9s"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 9be4af2ed30b681be51d5d418863c5400422dc3b. --- src/views/settings/SettingsTabs.tsx | 37 +++++++++++++---------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/src/views/settings/SettingsTabs.tsx b/src/views/settings/SettingsTabs.tsx index 74955e65d..98693813c 100644 --- a/src/views/settings/SettingsTabs.tsx +++ b/src/views/settings/SettingsTabs.tsx @@ -117,27 +117,22 @@ const SettingsTabs = () => { const loadTabs = async () => { if (account.personalization.tabs) { const storedTabs = account.personalization.tabs; - - const updatedTabs: Tab[] = storedTabs.map((storedTab) => { - const defaultTab = defaultTabs.find((defaultTab) => defaultTab.tab === storedTab.name); - return { - tab: storedTab.name, - label: defaultTab?.label || "Default Label", - icon: defaultTab?.icon || null, - enabled: storedTab.enabled, - installed: true, - }; - }); - - const newTabsFound: Tab[] = defaultTabs - .filter((defaultTab) => !storedTabs.some((storedTab) => storedTab.name === defaultTab.tab)) - .map((tab) => ({ - ...tab, - installed: true, - enabled: false, - })); - - setTabs([...updatedTabs, ...newTabsFound]); + const updatedTabs = defaultTabs + .filter((defaultTab) => + storedTabs.some((storedTab) => storedTab.name === defaultTab.tab) + ) + .map((defaultTab) => { + const storedTab = storedTabs.find((t) => t.name === defaultTab.tab); + return { + ...defaultTab, + enabled: storedTab ? storedTab.enabled : false, + installed: true, + }; + }); + + const newTabsFound: Tab[] = defaultTabs.filter((defaultTab) => !storedTabs.some((storedTab) => storedTab.name === defaultTab.tab)).map((tab) => ({ ...tab, installed: true })); + + setTabs(updatedTabs); setNewTabs(newTabsFound); setShowNewTabsNotification(newTabsFound.length > 0); } else { From 833445a83d112e1a9ce6363675e23df0e9059fbf Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 5 Feb 2025 22:02:31 +0100 Subject: [PATCH 0508/1144] =?UTF-8?q?fix(SettingsTabs):=20chargement=20inc?= =?UTF-8?q?orrect=20des=20onglets=20personnalis=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/SettingsTabs.tsx | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/views/settings/SettingsTabs.tsx b/src/views/settings/SettingsTabs.tsx index 98693813c..a6fc9e8f2 100644 --- a/src/views/settings/SettingsTabs.tsx +++ b/src/views/settings/SettingsTabs.tsx @@ -117,18 +117,14 @@ const SettingsTabs = () => { const loadTabs = async () => { if (account.personalization.tabs) { const storedTabs = account.personalization.tabs; - const updatedTabs = defaultTabs - .filter((defaultTab) => - storedTabs.some((storedTab) => storedTab.name === defaultTab.tab) - ) - .map((defaultTab) => { - const storedTab = storedTabs.find((t) => t.name === defaultTab.tab); - return { - ...defaultTab, - enabled: storedTab ? storedTab.enabled : false, - installed: true, - }; - }); + const updatedTabs = storedTabs + .map((storedTab) => { + const defaultTab = defaultTabs.find((t) => t.tab === storedTab.name); + return defaultTab + ? { ...defaultTab, enabled: storedTab.enabled, installed: true } + : null; + }) + .filter((tab) => tab !== null); const newTabsFound: Tab[] = defaultTabs.filter((defaultTab) => !storedTabs.some((storedTab) => storedTab.name === defaultTab.tab)).map((tab) => ({ ...tab, installed: true })); From 23a65a092ec16a71b9dbce1df0af3838888e5789 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 5 Feb 2025 22:06:16 +0100 Subject: [PATCH 0509/1144] =?UTF-8?q?fix(SettingsTabs):=20Activation/D?= =?UTF-8?q?=C3=A9sactivation=20des=20onglets=20non=20fonctionnel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/SettingsTabs.tsx | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/views/settings/SettingsTabs.tsx b/src/views/settings/SettingsTabs.tsx index a6fc9e8f2..f0ebb16a7 100644 --- a/src/views/settings/SettingsTabs.tsx +++ b/src/views/settings/SettingsTabs.tsx @@ -81,19 +81,20 @@ const SettingsTabs = () => { const toggleTab = (tab: string) => { void (async () => { - if (tabs.filter((t) => t.enabled).length === 5 && !tabs.find((t) => t.tab === tab)?.enabled) { + const isTabEnabled = tabs.find((t) => t.tab === tab)?.enabled ?? false; + const enabledTabsCount = tabs.filter((t) => t.enabled).length; + + if (!isTabEnabled && enabledTabsCount === 5) { playFailAnimation(); return; } - const newTabs = [...tabs]; - const index = newTabs.findIndex((t) => t.tab === tab); + const updatedTabs = tabs.map((t) => + t.tab === tab ? { ...t, enabled: !t.enabled } : t + ); - if (index !== -1 && !safeTabs.includes(tab)) { - newTabs[index].enabled = !newTabs[index].enabled; - setTabs(newTabs); - updatePersonalizationTabs(newTabs); - } + setTabs(updatedTabs); + updatePersonalizationTabs(updatedTabs); })(); }; From 9d7e5115926c0be3d85c84cfd16ff67dbfeefbb9 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 5 Feb 2025 22:44:11 +0100 Subject: [PATCH 0510/1144] refractor: update `package-lock.json` --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 32bc88c36..81cb778a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.8.2", + "version": "7.8.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.8.2", + "version": "7.8.3", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.3", "@candlefinance/app-icon": "^0.4.5", From a6238fb64c0ba804bdc85f30745349ad90999c00 Mon Sep 17 00:00:00 2001 From: Bulgus <88266443+Bulgus@users.noreply.github.com> Date: Wed, 5 Feb 2025 23:12:30 +0100 Subject: [PATCH 0511/1144] wip 3 --- .../GetDefaultProfilePicture.tsx | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/utils/GetRessources/GetDefaultProfilePicture.tsx diff --git a/src/utils/GetRessources/GetDefaultProfilePicture.tsx b/src/utils/GetRessources/GetDefaultProfilePicture.tsx new file mode 100644 index 000000000..e02f873c1 --- /dev/null +++ b/src/utils/GetRessources/GetDefaultProfilePicture.tsx @@ -0,0 +1,34 @@ +import downloadAsBase64 from "@/utils/external/download-as-base64"; +import { AccountService, PronoteAccount, SkolengoAccount, EcoleDirecteAccount } from "@/stores/account/types"; + +// Depending on your account type, download the default profile photo +export async function getDefaultProfilePicture( + account: PronoteAccount | SkolengoAccount | EcoleDirecteAccount): Promise { + try { + switch (account.service) { + case AccountService.Pronote: { + const user = account.instance?.user?.resources?.[0]; + return user?.profilePicture + ? await downloadAsBase64(user.profilePicture.url) + : undefined; + } + case AccountService.Skolengo: + console.warn("Skolengo does not provide a profile picture."); + return undefined; // Skolengo does not provide profile pictures + + case AccountService.EcoleDirecte: + return account.profilePictureURL + ? await downloadAsBase64(account.profilePictureURL, { + Referer: ".ecoledirecte.com/", + Pragma: "no-cache", + }) + : undefined; + + default: + return undefined; + } + } catch (error) { + console.error("Error while retrieving default profile picture:", error); + return undefined; // Return undefined in case of error + } +} From a6126ad2d65828a54b76aaedc22637c23371b879 Mon Sep 17 00:00:00 2001 From: Bulgus <88266443+Bulgus@users.noreply.github.com> Date: Wed, 5 Feb 2025 23:27:30 +0100 Subject: [PATCH 0512/1144] wip 4 --- src/views/settings/SettingsProfile.tsx | 45 ++++++++++++++++++++------ 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index f1c868f3a..9634be585 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -5,9 +5,9 @@ import { useTheme } from "@react-navigation/native"; import * as ImagePicker from "expo-image-picker"; import { Camera, ChevronDown, ChevronUp, TextCursorInput, User2, UserCircle2, WholeWord } from "lucide-react-native"; import React, { useEffect, useRef, useState } from "react"; -import { ActivityIndicator, Alert, Image, KeyboardAvoidingView, ScrollView, Switch, TextInput } from "react-native"; +import { ActivityIndicator, Alert, Image, KeyboardAvoidingView, ScrollView, Switch, TextInput, Platform } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; - +import { useAlert } from "@/providers/AlertProvider"; import * as Clipboard from "expo-clipboard"; import { TouchableOpacity } from "react-native-gesture-handler"; import { getDefaultProfilePicture } from "@/utils/GetRessources/GetDefaultProfilePicture"; @@ -27,6 +27,7 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { const [firstName, setFirstName] = useState(account.studentName?.first ?? ""); const [lastName, setLastName] = useState(account.studentName?.last ?? ""); + const { showAlert } = useAlert(); // on name change, update the account name useEffect(() => { @@ -53,15 +54,41 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { const resetProfilePic = async () => { setLoadingPic(true); + // Call up the function to obtain the profile picture const img = await getDefaultProfilePicture(account); - setProfilePic(img); - mutateProperty("personalization", { - ...account.personalization, - profilePictureB64: img, - }); - + + // If the image is undefined, an alert is displayed with an error message + if (!img) { + if (Platform.OS === "ios") { + Alert.alert("Erreur", "Impossible de récupérer de la photo de profil", [ + { + text: "OK", + }, + ]); + } else { + showAlert({ + title: "Erreur", + message: "Impossible de récupérer de la photo de profil", + actions: [ + { + title: "OK", + onPress: () => {}, + backgroundColor: theme.colors.card, + }, + ], + }); + } + } else { + // If image available, update profile picture + setProfilePic(img); + mutateProperty("personalization", { + ...account.personalization, + profilePictureB64: img, + }); + } + setLoadingPic(false); - }; + }; const updateProfilePic = async () => { setLoadingPic(true); From f8d47f4432dc2699996678c2de5f6c05a9c9891a Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Thu, 6 Feb 2025 11:19:50 +0100 Subject: [PATCH 0513/1144] fix: Refresh week view on focus --- src/views/account/Week/Week.tsx | 34 ++++++++++----------------------- 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index c73c23e61..cdb653b11 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { memo, useCallback, useMemo } from "react"; +import { memo, useCallback, useMemo, useEffect } from "react"; import { Image, Linking, StyleSheet, Text, TouchableOpacity, View } from "react-native"; import CalendarKit from "@howljs/calendar-kit"; import { useCurrentAccount } from "@/stores/account"; @@ -228,8 +228,10 @@ const Week = ({ route, navigation }) => { } }), [theme.colors]); - const events = useMemo(() => - Object.values(timetables) + const [events, setEvents] = React.useState([]); + + useEffect(() => { + const nevts = Object.values(timetables) .flat() .map(event => ({ id: event.id, @@ -237,9 +239,10 @@ const Week = ({ route, navigation }) => { start: { dateTime: new Date(event.startTimestamp) }, end: { dateTime: new Date(event.endTimestamp) }, event: event, - })), - [timetables] - ); + })); + + setEvents(nevts); + }, [timetables]); const loadTimetableWeek = useCallback(async (weekNumber, force = false) => { if (!force) { @@ -264,24 +267,7 @@ const Week = ({ route, navigation }) => { const [openedIcalModal, setOpenedIcalModal] = React.useState(false); React.useEffect(() => { - navigation.addListener("focus", async () => { - if(Object.values(timetables).flat().length === 0) { - setIsLoading(true); - requestAnimationFrame(async () => { - const weekNumber = dateToEpochWeekNumber(new Date()); - await loadTimetableWeek(weekNumber, true); - setOpenedIcalModal(false); - }); - } - }); - - return () => { - navigation.removeListener("focus"); - }; - }, [openedIcalModal, timetables]); - - React.useEffect(() => { - if(Object.values(timetables).flat().length === 0) { + if(events.length === 0 && account?.personalization?.icalURLs?.length > 0) { setIsLoading(true); requestAnimationFrame(async () => { const weekNumber = dateToEpochWeekNumber(new Date()); From c06e238b8fbaef7819633ece601c6d4ab02432e9 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Thu, 6 Feb 2025 15:40:35 +0100 Subject: [PATCH 0514/1144] fix: App seeing itself as running in Expo --- src/utils/native/expoGoAlert.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/native/expoGoAlert.ts b/src/utils/native/expoGoAlert.ts index 50c22ef84..c6bee2ef3 100644 --- a/src/utils/native/expoGoAlert.ts +++ b/src/utils/native/expoGoAlert.ts @@ -2,12 +2,12 @@ import { Alert } from "react-native"; import Constants from "expo-constants"; export const isExpoGo = () => { - return Constants.expoConfig?.extra?.EXPO_ENV === "expo"; + return Constants.appOwnership === "expo"; }; export const alertExpoGo = async () => { Alert.alert( "Tu développes à l'aide d'Expo Go", - "Sous Expo Go, les appels aux API natives sont indisponibles. Utilise un build de développement pour accéder à toutes les fonctionnalités.", + "Sous Expo Go, les appels aux API natives sont indisponibles. Utilise un build de développement pour accéder à toutes les fonctionnalités." ); }; From 6a31e7cbd754fb3af6f7a5cf7a5c9899c4bb313b Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Thu, 6 Feb 2025 20:06:56 +0100 Subject: [PATCH 0515/1144] =?UTF-8?q?feat:=20Ajout=20de=20la=20biblioth?= =?UTF-8?q?=C3=A8que=20Notifee=20et=20mise=20=C3=A0=20jour=20des=20autoris?= =?UTF-8?q?ations=20de=20notification?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Papillon.xcodeproj/project.pbxproj | 4 ++-- package-lock.json | 7 +++++++ package.json | 1 + src/background/Notifications.ts | 13 +++++++++---- src/views/settings/SettingsNotifications.tsx | 9 ++++++--- 5 files changed, 25 insertions(+), 9 deletions(-) diff --git a/ios/Papillon.xcodeproj/project.pbxproj b/ios/Papillon.xcodeproj/project.pbxproj index ecf2782fd..01967ab06 100644 --- a/ios/Papillon.xcodeproj/project.pbxproj +++ b/ios/Papillon.xcodeproj/project.pbxproj @@ -453,7 +453,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = "Papillon"; + PRODUCT_NAME = Papillon; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -486,7 +486,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = "Papillon"; + PRODUCT_NAME = Papillon; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/package-lock.json b/package-lock.json index 81cb778a4..cee656e64 100644 --- a/package-lock.json +++ b/package-lock.json @@ -69,6 +69,7 @@ "lodash": "^4.17.21", "lottie-react-native": "^6.7.0", "lucide-react-native": "^0.378.0", + "notifee": "^0.0.1", "openid-client": "^5.7.0", "pawdirecte": "^1.7.1", "pawnilim": "^0.2.0", @@ -13312,6 +13313,12 @@ "node": ">=0.10.0" } }, + "node_modules/notifee": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/notifee/-/notifee-0.0.1.tgz", + "integrity": "sha512-R5DEyfQnhBR717u66Zut+Jx5MAOS4iLNSVEdsbiVcSzPH9S1gjxHEUJNnLnUmRTbvz8L8sUzhgkNMrtgm55UHg==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info." + }, "node_modules/npm-package-arg": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-7.0.0.tgz", diff --git a/package.json b/package.json index 56af1330a..e3834ad5f 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "lodash": "^4.17.21", "lottie-react-native": "^6.7.0", "lucide-react-native": "^0.378.0", + "notifee": "^0.0.1", "openid-client": "^5.7.0", "pawdirecte": "^1.7.1", "pawnilim": "^0.2.0", diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index 5018f0e64..de9ceb984 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -1,16 +1,21 @@ import { alertExpoGo, isExpoGo } from "@/utils/native/expoGoAlert"; import { Notification } from "@notifee/react-native"; -const requestNotificationPermission = () => { - return async () => { +const requestNotificationPermission = async () => { + try { if (!isExpoGo()) { + console.log("Requesting notification permission..."); const notifee = (await import("@notifee/react-native")).default; - await notifee.requestPermission(); + return notifee.requestPermission(); } else { alertExpoGo(); return false; } - }; + } + catch (error) { + console.error("Error requesting notification permission:", error); + return false; + } }; const papillonNotify = async (props: Notification) => { diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 5e595533d..e80706ecd 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -44,10 +44,13 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = () => { return; } - if (await requestNotificationPermission()) return; + requestNotificationPermission().then((result) => { + console.log("Notification permission requested:", result); + }); - await mutateProperty("personalization", { notifications: { ...notifications, enabled: newValue } }); - setEnabled(newValue); + + // await mutateProperty("personalization", { notifications: { ...notifications, enabled: newValue } }); + // setEnabled(newValue); }; // Schoolary notifications From 08dadd714fcc8dd1ea2d3a8dc53bb4327e2e59ba Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Thu, 6 Feb 2025 20:07:01 +0100 Subject: [PATCH 0516/1144] =?UTF-8?q?feat(Menu):=20Ajout=20de=20composants?= =?UTF-8?q?=20DrawableImportRestaurant=20et=20ButtonCta,=20am=C3=A9liorati?= =?UTF-8?q?on=20de=20l'affichage=20des=20=C3=A9l=C3=A9ments=20manquants?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Drawables/DrawableImportRestaurant.tsx | 41 ++++++++++ src/components/Global/MissingItem.tsx | 14 +++- src/views/account/Restaurant/Menu.tsx | 81 +++++++++++++------ 3 files changed, 110 insertions(+), 26 deletions(-) create mode 100644 src/components/Drawables/DrawableImportRestaurant.tsx diff --git a/src/components/Drawables/DrawableImportRestaurant.tsx b/src/components/Drawables/DrawableImportRestaurant.tsx new file mode 100644 index 000000000..81d697117 --- /dev/null +++ b/src/components/Drawables/DrawableImportRestaurant.tsx @@ -0,0 +1,41 @@ +import * as React from "react"; +import Svg, { Rect, Path } from "react-native-svg"; + +const DrawableImportRestaurant = (props: any) => ( + + + + + + +); + +export default DrawableImportRestaurant; \ No newline at end of file diff --git a/src/components/Global/MissingItem.tsx b/src/components/Global/MissingItem.tsx index c55b2ad25..1fbfdbc54 100644 --- a/src/components/Global/MissingItem.tsx +++ b/src/components/Global/MissingItem.tsx @@ -6,6 +6,8 @@ import AnimatedEmoji from "../Grades/AnimatedEmoji"; interface MissingItemProps { style?: StyleProp>>; emoji?: string; + leading?: React.ReactNode; + trailing?: React.ReactNode; animatedEmoji?: boolean; title: string; description: string; @@ -15,6 +17,8 @@ interface MissingItemProps { const MissingItem: React.FC = ({ style, + leading, + trailing, emoji, animatedEmoji, title, @@ -28,13 +32,15 @@ const MissingItem: React.FC = ({ style={[{ justifyContent: "center", alignItems: "center", - gap: 4, - paddingHorizontal: 40, + gap: 8, + paddingHorizontal: 20, }, style]} entering={entering ? entering : FadeInUp} exiting={exiting ? exiting : FadeOutDown} > - {!animatedEmoji ? ( + {leading && leading} + + {!animatedEmoji ? emoji && ( {emoji} @@ -49,6 +55,8 @@ const MissingItem: React.FC = ({ {description} + + {trailing && trailing}
); }; diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 54632e3e8..1d66474d0 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -53,6 +53,8 @@ import PapillonHeader from "@/components/Global/PapillonHeader"; import { PressableScale } from "react-native-pressable-scale"; import { BlurView } from "expo-blur"; import { ChevronLeft, ChevronRight} from "lucide-react-native"; +import DrawableImportRestaurant from "@/components/Drawables/DrawableImportRestaurant"; +import ButtonCta from "@/components/FirstInstallation/ButtonCta"; const Menu: Screen<"Menu"> = ({ route, navigation }) => { const theme = useTheme(); @@ -321,7 +323,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { ) : ( <> {allBalances?.length === 0 ? ( - + ) : ( <> @@ -355,20 +357,51 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { )} - - } - onPress={() => navigation.navigate("RestaurantHistory", { histories: allHistories ?? [] })} - enable={allHistories?.length !== 0} - /> - } - onPress={() => navigation.navigate("RestaurantQrCode", { QrCodes: allQRCodes ?? [] })} - enable={allQRCodes?.length !== 0} + {allBalances?.length === 0 && ( + + } + title="Commence par connecter un service externe de cantine" + description="Papillon te permet d’importer un compte depuis Turboself, ARD, Alize et Izly." + entering={animPapillon(FadeInDown)} + exiting={animPapillon(FadeOut)} + trailing={ + navigation.navigate("SettingStack", { view: "SettingsExternalServices" })} + style={{ marginTop: 16 }} + /> + } /> - + )} + + {allHistories?.length > 0 || allQRCodes?.length > 0 && ( + + } + onPress={() => navigation.navigate("RestaurantHistory", { histories: allHistories ?? [] })} + enable={allHistories?.length !== 0} + /> + } + onPress={() => navigation.navigate("RestaurantQrCode", { QrCodes: allQRCodes ?? [] })} + enable={allQRCodes?.length !== 0} + /> + + )} {(currentMenu || (allBookings && allBookings?.some((terminal) => terminal.days.some((day) => day.date?.toDateString() === pickerDate.toDateString())))) && @@ -568,14 +601,16 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { )} : <> - + {allBalances?.length > 0 && ( + + )} } = ({ route, navigation }) => { }; const styles = StyleSheet.create({ - scrollViewContent: { padding: 16, paddingTop: 0 }, + scrollViewContent: { padding: 16, paddingTop: 0, flexGrow: 1 }, accountButtonContainer: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", gap: 16, marginBottom: 16 }, horizontalList: { marginTop: 10 }, calendarContainer: { flexDirection: "row", justifyContent: "center", alignItems: "center", marginTop: 16, marginBottom: -10, gap: 10 }, From 4b43669476397922157bbc52071cf9eeb1987248 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Thu, 6 Feb 2025 20:11:10 +0100 Subject: [PATCH 0517/1144] =?UTF-8?q?fix(Menu):=20Correction=20de=20la=20c?= =?UTF-8?q?ondition=20d'affichage=20des=20=C3=A9l=C3=A9ments=20d'historiqu?= =?UTF-8?q?e=20et=20de=20QR=20codes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Restaurant/Menu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 1d66474d0..1e6dd9233 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -386,7 +386,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { /> )} - {allHistories?.length > 0 || allQRCodes?.length > 0 && ( + {((allHistories?.length !== 0) || (allQRCodes?.length !== 0)) && ( Date: Thu, 6 Feb 2025 20:12:51 +0100 Subject: [PATCH 0518/1144] =?UTF-8?q?fix(Menu):=20Am=C3=A9lioration=20de?= =?UTF-8?q?=20la=20condition=20d'affichage=20pour=20les=20=C3=A9l=C3=A9men?= =?UTF-8?q?ts=20manquants?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Restaurant/Menu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 1e6dd9233..39264772d 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -357,7 +357,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { )} - {allBalances?.length === 0 && ( + {allBalances?.length === 0 && !currentMenu && allHistories?.length === 0 && allQRCodes?.length === 0 && allBookings?.length === 0 && ( Date: Thu, 6 Feb 2025 22:30:32 +0100 Subject: [PATCH 0519/1144] feat: Ajout des cartes visuelles --- assets/images/cards/Carte_Cover_ARD.png | Bin 0 -> 16892 bytes assets/images/cards/Carte_Cover_Alise.png | Bin 0 -> 8558 bytes assets/images/cards/Carte_Cover_Izly.png | Bin 0 -> 22542 bytes assets/images/cards/Carte_Cover_Turboself.png | Bin 0 -> 21909 bytes .../cards/Turboself/Carte_Cover_Izly.png | Bin 0 -> 34668 bytes src/services/izly/balance.ts | 2 + src/utils/ui/default-profile-picture.ts | 4 +- src/views/account/Restaurant/Cards/Card.tsx | 153 ++++++++++++++++++ .../account/Restaurant/Cards/StoreThemes.ts | 23 +++ src/views/account/Restaurant/Menu.tsx | 124 ++++++-------- 10 files changed, 229 insertions(+), 77 deletions(-) create mode 100644 assets/images/cards/Carte_Cover_ARD.png create mode 100644 assets/images/cards/Carte_Cover_Alise.png create mode 100644 assets/images/cards/Carte_Cover_Izly.png create mode 100644 assets/images/cards/Carte_Cover_Turboself.png create mode 100644 assets/images/cards/Turboself/Carte_Cover_Izly.png create mode 100644 src/views/account/Restaurant/Cards/Card.tsx create mode 100644 src/views/account/Restaurant/Cards/StoreThemes.ts diff --git a/assets/images/cards/Carte_Cover_ARD.png b/assets/images/cards/Carte_Cover_ARD.png new file mode 100644 index 0000000000000000000000000000000000000000..771809ca2e81989108ae528605cede377fedd0da GIT binary patch literal 16892 zcmeIZ^T7JB0L(Bdfuc_ju0A(tIHPM^9S zp&|PV-+uMXeRaY(H9|8xXvLFHYH?&jPqZI)E9^X}C6`le{(UNsw5$$)*mfp7Bv2{L zlfp50D13I?db2@pjp`q`S%APrEYy#LME&J~Re@GMRiZCFG#IqNWj_E!0$k3;q3FH& zFpFaW7g4fI4B*0q59}HQCB?P`F70@jyuf7<>;H!RpGf{sOa7PPxM{9t)3O)8AP3BY zLZ_kqP3&`4r4uWSPtJCqiFY;=Z|w*+XJBNYq-P>8Bf&-SFg8*`5VHto_2=xPqUL>{ za~&$H@DwVNF%i0^Ck<6{A+cYuLD$b6zV^iN@k9u>k!552dzzOsG9RybdYt6dtVow; z-bp+En8*`Q6cGtrgDb#eQ$jYRdOvK=J)i=$Z{s5(3atwVFYNfFw~2g5K(7j|%VJaFPiTzG%jp=YU&9}P zpug}D_l8rwAAgVkJFy|U8PMWe?GB3CTGeP?4=yxqHAIDZ(S$uU<9^Ky0{xkMljn6Y ziPvbW?mKc~Lzic-g=M-ACF|!C=0JTz-ddt!aY1C6=^td3_{QZ$H#**mA**fHlbB1# ztw2lfsy^xsExnP+;CGS4W19zwL)~OQ)!re$8(2mm`_zBcbSJNJf&x|4Qf_9IUd9-8 zjjR(%@K+T}rY zA}y%%>r1x;A>qI>?i;vYYe?bus!#Rai0}?m81G_;rU!V8vP4rz-p5xN#H@^uNT}NU z)2f+KeQL@!<2~s1YE1#lZ6q2Z$d`ja8PjH;ctYbBK%GG#yPrN0W&La-%sRDW$y9Ty z`FN^|Q3?&(gJk)0jypG9og^*ah>lGrT=aCexI$#G~$KzR(%~cF$X>rdK=y9AVdz z1)uw)@vX>SWOOIYS@L=gHMH`R;7wnVETPE9ZMU58mEC(`I2i~ zGEHpD{(A04u<`M$y&h33;60{Fc2q8_2a;cL$)vF@zXfGoriJ2KiS(%I3#E!$F@FZm zz~%>8;>JXA%QT3L0(eB)T<4+hMS$J0xKakMV~M7R+X=3zhys#B1w@8D5K#MdJ7@cl zeVncIJyk@-Xtbl?c<0HcFmO6f1APvlG8S?D&hakxE?^fh+dV#;KvxMOvWK*=nnU`y zVvRycj7PN4etFx3udlU;Do1Y}eQ9x+BS zv3!pYoDDainy)LByR35`oeyXq_T1+)^0@c>W z&{kZlX`w~T8zGYIBMEE$N0Az!GG_5avYc%~HUCzYiC{<}b?q?aME=RMl~M!XlolUg zx)A&n$gAhg97C%EFx=$xn45&12qqvKAzV%hcb2w~(1 zs-Ph#ll}osFe*~hqpI3)lLDT2ufCKVyK#JM9SzpowBM!xRosWqKW*O-Y@*+d6`kcM zRbp`Y^=P~E-LvU0c1x1=`dQbjb6 z?3cu^XO^ykIHw*4Xa!e(l!VJ}){g#QkVkhEKhz61Kk+D~el_ zp5v}$0BV1Tz46%8C0!ptGsPL9jo3dPntT(%ANky~HsJPvWeMYAwBtT$KtI!@j$-IN zHPRr-QKN0ZAGO|ertDQ&xQIyyR&<&Y3*64U|7tw~L znnc7>QySGx166-4PDPNj-6`Rn%O}5$Au8CWNCs{UoW9;oJ~Da>9MfBCU>_z6Yq3L1XscN-hsz_DpQs+ttq9B5wgFq`{n5*dge_`Q#(xB(T_I3o8I z_boB@2IdGBXU;Zvf@a3C;~9lUe16L>zju3m>Zm@i&MGrrX&a$0_JlZ4NnqD+wIJ!B z2HH8c(o%*JqPobCb-}$LJh!3)Ra?NA22Jrngux&~W?=c!2sS%5ZAep=n@kIFSIjGF zIx^5R>@u;3@=wPtRsv4t3;rx_k`#t_a4>XM385E^z(?S8J``NhC{kSkD?~SHX_si8NLZKc)*tg#b=hri9)v zjO|V$DIMu;c%nDW>>mJGg2kajJf-;RzOpaB8&H+S8vm$OF?cSlu|$ds_%k0C@pYPb z@wl)MY=)(H>pf=q$jIB->WSu(EJ-r@%d0UXH&73rVxH&k4Z+EeE7Zb<1D(Q~vm2(9 zn$H`;DC94b5HcKdcIlWP@{V_pBa0FtiK08%FYqZG^oc z96C^z>x{F#x$r}K@mPkR-WrPU9fcg9t>M@vfIrH4#5{I6fo1q3I?xZRE!YeluZjC8TZe}7&G@g0?a{zK=nZAHHI)B&f8`L#QKqV10 zf5XT~#NXu~gm$sx6D4+5m94P~=OB*rCGGBGf-tMq24ZBW0UH_d>^HLL1Z?viF`yk$ zlF-utU}v9iQ3M*EjH>s(p5Rb&y#koec2Ff2We>EV0Uka2M&sa(lnGoH0j~V%ZmL%0A z?@=3m$qXxxeDiWb`&U)r@vtlok(E%i{q_#pk1VwhTM}z#+?V#M*t4GF!t%cCaual) zI8J#0`|z_bL6An3@#88#*#r9MKV->(i`HL! z%>T;zmun5jmI~1WsL5k-i%fn}Sy~Pp$2q`drPuAavI>YY~cxQACdRd)?INX*ZmblInP5P^aC@!7(CdaH0kGMC0f{kwS#Y%B~f z3cy&+%7QFf(hS>bhl2>2Igj^5`2OC5&d7)50GliOCu4U&#@c@c-pNo~HO1Itq0lnk z&Bhc7G*Vp4yf?AYk+e@-pHrC^p`Zp`76|+Pws-KeWBC6QsY_u{h=n!;tK1m1<2iDh zzya!hEZ!C}?CSX4rntdI2T2YKxX&w1`V?39f<)je=!OO8B3WH4fZ>RTF-@MX)1{)~*!lm9){u;v4u}=b z$TEY0m;GvoiE4Pdec44;AtKli}mDpkwO1 z8QG?poi;+8AQejvMD$d(eY>bv#S>vH`9j;mWovemECk~PwISXN;6oMYKL5;}R817M zM62FgkHTdwc|uVx6-uPUmko`HAu(o zWR7EhHLdvq`ochOuyiSTd{*2l2`y+Xw4gJqC3Pf6l;FVPUl92wtG-TQd(5&l{5_!8IwV9Iv53NT;UD^GnzW zgF?p=lQ(Dktu+92-k)#iy4086j8Id8+(zRUEs-S0ocWy=Z@YFtvA9mn7~xy|gOit?JW%l; zwuL!!_miOF3?Bd)OCl6ibhiCyCP%qyNAn11VN+my6V5_I0ui<8g&5pP*7q1GTRevV z&bievI-2sGH9s#j14P2~4>|Ju^(8?oU~UXOhwx|X=IkrcO2rL1^Wy-JVo{T}6u!;a zGpO&woqWul_Z&Fw!>+vjk{KKVIEYo!LfVFi8aj*p7`^|C-`4h^55M{x+B3wlEq9;D zwvxdxf&gIeF=WQ%)kj8=-pr7*3X`&Z>2jN{0uU(5L9iF_u;GIj=!063`VU}~kir{N z^t~yqbTQvP^HLsPr}1sYh=Ltq2=aA5pyo0hoNMX0{chmJJ=*uS>-sqcRPwpak0>Bo z#}W5Z#j~`V%e)K^MeNU34c|V+^m+O}62oX2^j(_B^COc#9+pq@ zie1~t>FQhx1O5T?vzoCRS+tlBaVb8>Vx)AjQv;lYV7@&%4v|^cQb29tcJhF1EWa`; z5-Z--27x}Te}YB~o$pHU0Cl+5Q~}34p|O<%A;azkbySx?ITwkb3!uKA?yvsA~^NukagJiQ__ox z*pADZ?`xS@APF*nopa-5<^<+=_uRFoGjD$YPzZ@!ozSTV;T4X5pABsYej+z!f_&t8 zxI`n406^ly((mZBE_Qi29f*6)uKP!p1l6eF{|uA+Dog#IDYK?LEQ~>Rbic%O3)fWP zr=g2c{8*VivoIDu3CXZjs03TtJ8}h(9eUZjj6~1@rrYBP}=rmLreN-pXb^S zDq|Q}m_f^|3n_zA0TbLt!vrSC#Ma9{wzdfaTm=@lVGiZhChYA0k7)PRX6BNraT0ai zSJp8o?Ez?Dd#!UHhGFs%P>)1itpIZ;MUPEud!5cIFb<-Y1kgie<=mfvG8~RES_J=6 zS#BWf|BqzTBXmj(=SaJN{*EoVcWLpQ7)1XK5njRJ z2{i`>0FF?gDy7N1i}x0cVS+8mv)>ql2jFVZXO0;hKH=>^3gFa1Su{PlD;RoL8TV~V z)^I`YWGMkZ<9XQucI+HLd=q{{^So~*_=DoRiYX7@r#-MO`?m)CR832yw?+)gH^{ub zAm%Bq-5hfTcC6@2_c~X>`>=kx$NyqYam4+UfNOT$z*6fna_mS^iS2L;-fZ$CW(2)jU&VN{@DDP9^r9L<;jDFukx5_n=M{t1AVV2}5MB%r*U ztcJE0ofsi+Ge<9MoHpkX?6rWA@xc0NpJRh;Ezn1Jyti{wwtqqd z@}_(tzCHLEU{(TV(5t?Xvc#R$tajcsa*oe_FCwI+bYR@au`i4emicEIdgs3gMR_z?l7Bw`oT`8^{%|B zW`0Ssga__hb>KcdA!8ZupuBa1$~mU z#O*B2#L(j^PUQJK!4AN;yjlj+RV|yc2`ewwk~RWVilTGue#>b!{Td$*a-`0p5f{WE z0uT@KEySW!xTykM3-Iv%`XIs(1hEMxaEnvbNv_R3^K1FHP!tfV4u)9)vw8wAT9oU+ ztUeX{<>#yaccdjt!?s{a*FrdPSc8;V4fpgI;L7|Yc|Z$<572AJMI#cRWVMHU1RcKEE0osf#}{%_`kxR0hy*?Er(jU1&&P&FP;Vrl(7sL7WT9pLysc_eK5W*HVF}hkPm!yMqNpj=D5J{r2l24;=?O^fYuGEdc7US zVFw4TZ)DRJ>d!HQqI#ndJBBKd4{jpR#a?|tUBc3VGiJsIrnil$12H74Y=_tZ(lU^> z#5E~y#sNTXN=64_GXuN=;6w*O22qKi!M_wuXnv}S2rr{ii=_G%^aw4Zl;+%dfXQmV z2RQziNrcsnguD)9q9jSy16MK(LELUeeh(p7o;d94wgJSk_FTdv`!U9(CLOn}&ruU!ef&zH6o@gkXG^{+Z zV;E^_(6#B$6ramO*6x2p&aGOKAOqpj~<(olmRvHq>V2Jh_}o zCo8%c;*{kGnd*iv+y%r;Go$!~({vHt#mua@s=RI)nJ{)qN%P(!P&X3QW%RQC(%cNcb!uk0xQ7_;Cy?vPUDD-%gXt%? zwD}yJao!IFnU{~uwpAqpmO5LMeg9(n%z!ho=mjA{&lh=XLGeX9_yL_@saX4_^==B~C_WsTCmUi; zm461R(9dmF7TrfeR!Y~oRIa?fcT8Vhi?!Ls%GFl)KeYfS(A}LfRAxG*Sj|zRM&j9e z)pq?1PXFvI*hK&3KtuMc#xi+c;cj#uObc`U1JRWV22}uRC;rH??U1QdME~}@h(DBZ_zYHoqQcv-7S@^zOGSKMXejVKyu*Dd&^mf}V>4 zu9z)L`wMX~q}KY2lfW~Lk-6?4E65zwmAQ0rSfv)@e;Lb>TbVt3 z%&|AW@RYyKKIXf=akLD?q6e?s=;yJG;WCxyLo|nDr*j{h($MKJp&V%#VASaX%v;zGQJz5j(FMTF zTkFvR?fxkxsve70?}FxvZK#3MZm;v77*BP5tw;LqyNN!6D>n;)e{el7gXcb8NjE7B z?w$J}x!x7SN6XS#dc;How!+st-mBYE8EUe)7Smyif4H9#?R;y+meSBLW!ZDS!tcj| z^|>KZXj}=!RDVgor|y${&bQ*563`*RV<$1e-x^`> zxtr&Ecn;y}4=4-T<1P{z==NrE&>eMB*Jt3{^Zy|osx4Mt8*V$>sAT)f9H;RkV+%ZZ zam0GJXbqsDv2b$Xc)mb#$4#D+wKiCk2=_l9R5wobz1Ha@O7sI~Z0j2}nKv|C!?=Ey zFO0;FM16l`x!{ZIDC6|x~&bLAEML@_ks^SUl}poZI!Q-k-%4vYrP)0 zq~9{?)b$**Cqwk^+A~XL&I7OaTIkGfV!0H-_s`FhHCFa+;bxWA7X;=unIxy~tSi@~ zep0~h{zc#`iAw*toeyLw(Fy=SUnGjPI1lWXJZQOTElMC-=k39T=DGKD zWJZj+&1bRD)TkWe?($ornt*U|VNLvDhmCgcVOMv5L90`7VOfOCFO;Vd-(8Cl)Ogja-2H;awTCi}H!mhtDa7oyywn3Z2Tg$<9hk1U|Yd+odyXR+c*$ZF|sy*0tIduBeDk^CHNwDe1|`M zhEuG`A!p|encCHB+9x%y{N`l(2WD*IwfX06Hio)YW_m3@-za!mMYg*1%+bLT$=8(= z#hP}78#YQza$sLtdb| z4FV90tv9P!YpeXEqNwy{&X@i=034@&_oqr9ezMTkqB8PysPOiGy0}9js<5zouP9|`PpdL@}u4t2AmEl z90i=#1Lt0)E3Ne$Yd95Izl>wg{HOaeZITMcwff(eA{Dp<3g-Er>fL`SGU1ux>_$;) z%XKJe-v}zr*z_L=NPCntP5rfk_g9XU$a1gG z&Ff)RmA-^rWGK-s-*|0RDtLPGfp6mjU+2*lx;&nFj#VT15x=dxa+P>Q>TPAH*TQHu zF+akp=HzC6q9nl4*rLwow8_19&)Kq#T17|Gf3<)#&XzD(d-!f^j;jB_=qvKA|BRIW z3S^S zFvOT*HjTVb+X?UcHwN$$Oy1!9{t7mclybMIeqG?{4?FlO}szqGyr5ioHrV}&f z-+k#f-T5kENh-KSu}owp<1$h|H>Y@FcG7eHkbH1YKwl?1!t(mUS1skNMqwJrC*rOF zQzR1xG*ZKr_haB+t8b$Vyyr((uls1&2Ko3a^G4H`pYhw}^k~l^lZVbM3K1~xEy-S4 zD%;tUo?(_rm$B&-p43;ZYW~iMS=))ZBd?0lAJf}7r!y_wt$joh4sW<1V)a9I^9#ei z18v*z?d%EBy&)Z;Rqr}G?o#41_9IpCTSwHC`sU5|A562iN!5EOG94?mbFl>vpfs0p z)lPYcBvKXGIuUGZ{8fC;*3vFqTB~04oRsRg+N9Og{R)HclXL*z9aWoJ&T%i5rsSwpfo!pS6M<=|)>k~;75{MQUvRAl+eTB+TCq4LAn z;OE$Zv+*c1*@If0Qww65%a_X{Qylr1m!~?B483lfQ%Vs&#OcM&<)%AEAL!C~{qwCX z-U;66m6O%)tm=^VMTjssj|A$ z|GRjCHrMfF{cYz4&bxfA7x^krmb>=@HwMofUO}S9R}^AZ;MMcO_0vti%UXVGSdUO4 zkN^CdX*O|RXMr=G>>XK5^wTXMIHzVOYPAIawpbY#j8Ax06`1+nl?t8p27H#&$*ghE zyDE!6y_&y8JaigQHhS8!Ta$6s5-I9%*=2azcdH7?*{tk>M9h}_K)qbJT$MURr7|7f z{&~Jiv8UJ%{Ds2k^ZuMnZ6)Oz_)b%FoNo{srB@Fx%yZO^786e&DSO}=$yv(&u z6g`ql%tMhfQd7t5-LBpDG+U6+2pCO0e??=Xsh3+aP|U~$zkE#_D0UD*WbDtx6*N>* z&PTWDaw(hNOMK}^2lG^DbsPQMT^*A)?UFX&Fd*#ESkOrWR|%WngZ9Zyd>bQ$84Rxy7sb&X>B(G^3c(P*rG^FK8|Sg!p7N#YTmzX)r>uAK(cv?+m8FKiZa4;x85?aq5E*;p)T8hrRBl z(c}?I#=kwRuk0@_;f%tsErA`Vu_@~eY&ql8mljzZT%S!=U;+%|*Ff)P4CSF8`L zjhFh~uHD%;O;e((g794dLFVnVx4H*j@~92)P`uro<@s>=ET_=vZ{?_Qz70VOgL0RR zJwtyp!Qs`?^^C`fB?d80g35x0q3mYmhQmpsd9uhdqvs1vrGvB5SBZPgZ~K}TT3Y== zS5b6Z@=#=I@AI{*R51b|054gr>ysc)$ ztu!R)3Hy{oBr$^HNa9<52iaX-KlZJy-Ak0Cb7MRbEPZBk!lyH>(R#A#YbCd1mc893 zm&*HhJ3}*9!E5_9iu0T41I44ji9vGtGco9{(4AD3>LKc zi4HtRPARtK>-OfRSyPQ?A!BWD>mP%cPE{&P9?NggIf!>KH#v#BaoeVZr{gk_Gxu<+ zE4UJOP^(|H=kma>$-XKXmD)Q!UXYYZ%`?2o&^D9gw*s3N5D+M_qr0E1op>cmgj#Ps zK+3IraToO(*q--ZCA{c4Syw1Cn(}`udbw(>;PC1!VJM&@M_&My;v4fAc^ zUGrE@+%p7!QlGYV6R8?Mx$}mTKM0LWrPHp#kz1GA?v4K5uOa)&KT9*?hA!->iig`$ zra@g%K$ieMi6oD$%T~7RMe3Q+<6-$rlOJ?zer^l0GIXR=CGRDxliAkqbHC~1%7`7%~o?9gw$S(-i0+JJC4>G9^TvA z>S9TqS&dk5h=%(v_LcbV=b1D10)=5^(|rk2!~qfu$&?1v^8eAd*o;{;uZ4z)FfuN3 z`puBl1LG^-S{4iA>xPd*ekoTgCbH*Rk(QPD4?RJSvlw4ZVO4tj8~C|QEFTW48L_Pw zTwJr$X5Q`(TJ*EPo8x}+NpPJRLnPMri(L$0rwuqR$jqX=6x=ULB~BOo5hhlI$VCz$k#s@D$Oc*blERv&!=^%T-|u_8^YyH zdAB@35}eqw^kk~p)Zq`^L%V!_MQbtFbgEZ;)mvX%haS%=*H@ag`kXB5^lI)M zhY=<5Yr7|Sxyw{dDnwd)YXVC&$dReq#;}Vph-!=^!pU@Vnq2iw*1Ly{iEnyXo0%JL zd#NI=btW&iCONWgT8dT_jz%-^El1934wH@;hWsb?;!@pCg;IMIo});FH#QnMBDsanq!cTiVw4?l_m>>0%*Ie^%u+ z6`A}M-gT<6WyGxG3FdwxeJ67AirZwN#e5a0({)S& z#6N{`(MXAoADU1%6Iex2aV3g1Y|)eF5=n5CvdPW)!p3vTym7}YJaS9X3OhaF>PF&ipv+3Az;xs>o~RPJgln} z5zbGiQrvwe(x}pU+Sb-z$aK-WZXA8ZIAg6H3QyJIa%fxOg^=fF6jHyYKJ;S}Yq=uO zUDFz^iC@RvyLx}OLR5JZ-wFQG;Qb4de_7hHa>@U0({Nb8YM#^cDz6}wPI|%bU{hU# zCcf=%2z9t8<}2QM__NMSYa3_Hy>Rqhbv#QK-e3N0m1?CSPy4tMzwqmNH*LkVTOGPF?fMkXo&c z@4kRT@5VN{@r2b~W9zo~WZCZD8YVV#o|Jh3jQo1boa!E-+$+<))_IDnsWan8{PTxj zXZLPIkG<(cc~2oexh2ZBQX{ld209Q2rLX^~K|y$)8Aw~oM)7(jwgbG910VlGdjM$pBOK;`$}xJGe+joIK-S(v)s@nl`Z_n# zJ#tm-4z}RzyY$nG9wXcMq7B>gT6t6T+-H6Mr@s-4$k~YKMY*vECzF5og;7#5p#K|# zz}B0(HRa{hrYn*o_^;RRTJ=@;i<*vFK)$ODgZYu?qjwkjZ>ZrdOnTl2VrOhRNvmv) zu7?h7uBpvYSuv#&`urHJA2u-|dE#ngygPmXPnLWEpJuG?Z2G&u+dmY8$KU$wtXhQY zg%Q+fNh@pb*w1*n7=~KYu`2{>4Mv zUI!^(Ug9bHTlS?T8%2p!v77dP#T_Am@5Jelx)gZME%nm#{F(F-@ZI`Z-e$Y1U|wt> zYSlB|e{FpJp?K#FV)g3g#QUa$Au=T)mJTd3r>TB-5Xr^QpMSHm%l643#b{^vI4Jib zXevL(P_gNhAx`b4`^DjjSevZ57n_{dqnNG~18!|kuz5xm)+|{PiKYU9&P&G06WphzU#Gy0KV;GM+(k@Doy-F5=k?Y?Mv%EeBQ9=N(ihF@q)c$w6@ zEwL}`>vgX_JlehGKp7~xsP&2fY#ge5ClS22{)6rDs!_Z_aXG{S*{?eYSsg4+==-?0 z9(_4-QUy!x7lM%|pzxh+Ga1BDF_Amu;9m)sD5u zqO?cZe?CgXoD2sVC+bItsP|4gb@K@wb+X~ujRu`nsv=1*`MkAsxZ(>ewcg3Ko6X=B z+!W2Y@AjWx`lg#2JC%mZDpQYgmw^Rtq?5&j#Fqb5Z(aA}!D1Zdn&*iuO?AFPviLK7 z_v+{dq`#@P+I-g?Q(yMIx%3{Px}jn9!c%4{JN4h*F3P5Sb=m!6^jYhYA)m+fMPm1L z+Vt8XI)k|$iArM^FSeUM+i!*6Xo@<|d9LNk6#w=*%5Sy&CN+DN{sa$iZ~;2Mq61{V zuWF=B+fLz=TDTn5Bna~|3;P@ZUxYYFX#)@F7fs-ju+aokJ4B5wxC|~M#@lbx$ z>L;jLP1B$<_wg=OzTH2SGvn^>o@u#TOrq2_bjvKYCmKHQQQ)&zOJdHEX@p7= zX&IXRRG@>W-eHVY5MtayQ(>pnG<&$nQ#nM0SkCJAa{1nL`k`)5M0j_fNUVOM`bhY6 zNA>9FrpaN!{?^b!PHg}8>!Ssq;f4Nu`ae_;XL671@1ng&yNICs#LD3WgEJ3C&}G!{ zw_X#g$?MEE^&_x(gCcm*G*N-W3An|=CfvhGN14%U%kkOBJBtEy8WyU(O&)%a6N}_e zPGM&+6D%&M6+fRF(!qGfRZG3vthByaeea3Kjer5Es;9(TeU56ol}w#vt~D3pQeH0G zH1la5RRR0PWZY~N|7F!Mv=L(brMbFbw3_w!c+HJZPwjEFfAdGo@rm1j0G56`=PZA^ z(PqRfuR{nYOh^hSTUo2(SF;JaaX;eP-Q8DJHR4<7mUc)R>uVlzLMmP_a>G$!AQbSe zB|1BTmKQht>qqYBK?ql{)tLZaYHv#}bY`Uv{w>A(PqX1KNHu$#yC9Yy&~O3zK(z%b zy-5a_tRc~zXmJm+Xh6`3;Md}3cA-jVxc;A~lqBkiw!(pq|FG`}Vvx937BK?oHX32! z#R75HBA)Ve@;LGW%^#rKpy%mNrv-D}A$5~zN)6q0@{9s~XPGK2DE~ti;3TkdXJlw| zScTP!@Zo~W6hLJd1ii-hfg%x^JTSxegAD&IDE3epl8x1ge8A-S&l4_Csbv8Iz10gK zK-|pd?ff^70LufdHR1M zEb8T@AkG#UQ-qe@ZK5EMKtfe0co_Kz_!O-RUFRLGmGHR7kKRwm(x;+t<&nwEADiS& z$AZ=sC}VM!fc_&8b5a^1;!87GHj%nPl0V&l#k3@ezI6_?AbNd{2g^G>5(Zix6$bmf zBm~HSv&E-k!{jY)_yD9lAkdPTFCd*?4}oGgD6`}qLh5I()r{jKbXlY*K~JIrJ)qqZ zXzN3NV_GXxyL%HxQeOP--_Y?^zTM}2TYi#3pvOFa)k{l+V+ z;sMiqscXW79{wyL2FH>_7$|iQuDZ_10ADoEZQ%wOq7NgJLh(T+-}y$kMN3w{8$g<= zS5+FB;Qbp_19YEhLw<3Qr_?>o^|t_;`vAR}y~DF&vR4bpk3IlC+y*Jgs7jYfngsrT D`UxU4 literal 0 HcmV?d00001 diff --git a/assets/images/cards/Carte_Cover_Alise.png b/assets/images/cards/Carte_Cover_Alise.png new file mode 100644 index 0000000000000000000000000000000000000000..7fb9c5c8a734225beb4cc203befb7343aba07975 GIT binary patch literal 8558 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
%G5x0%7 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 v1h&#pTgTOgdJ@6-PSA%0qi literal 0 HcmV?d00001 diff --git a/assets/images/cards/Turboself/Carte_Cover_Izly.png b/assets/images/cards/Turboself/Carte_Cover_Izly.png new file mode 100644 index 0000000000000000000000000000000000000000..29169a555421e624797f80f75c220401ac773013 GIT binary patch literal 34668 zcmeEuXCqwS*S16_YLpl~S`an5=)DF}M~^ysuTg>^dheo_=wGeR z;CFwA=f&R(j&aUDd#}C9wbr_}Pim@iI9MPoBqSsp1$k)=BqS68B&5e47-+yJtqI&; zfFGDn@_MdFNVLEoXaye0q;Q!4A3k!`kds8J`auB)enGL4P?11Fs*T0|XNro16!u6# zT0+z7(f&22SJ9T?miO(!mbI}W%NP2|&RMO^2D`+sG4;>p;~r%dKD8ty{B2?DRRUl2 zd$l+=?Zm6n%;-u`g}{{Z{8`vm(&rXNJ0?ZbjntTkACxUX@4WZyz}dja#+|K91F~GS z_4(>nzNA6}A~;enwv0EO$5K~V0CRCeEFT+xAOC=-`v{mF64D^&oBN+g+%bX3_gCgr zkI?Tg246qMy}yX_MxnUBpgG0>RtkKH|0Zy{zX<$Jw{m}hlrGo(Fd}#C{|34f$^T^Z z|EC^3MnVd#_@CC^TkZcW`+rXBP7(g+*#CctS<^Ik`w=MbKGN-c2n`Y1dj zJ|T<8o_)_6D+muM^TFi>;%kBI>=B`BCS|(JW)*c-qzdA)|NLEfg|Cky5U9lE9_1T-S z*1Bd2x4lTHw~Lamm_;Q=HE3>xU(Yx%qSrr5v!KF*#dcnGoWElvdamHe$ouVS5NVebA=POms(EC_N%gt%= z|5QKPh%XyvN*d&i3G>mUNnr3Z;-L){_l3h05Y*Pl|c|Cm=eJ zZc*FT4L)Iz{$So}=n_-dmNmq^8&{Z86Rt^zA!)G9%VboeaZ!gYY?+QrfGs+5Cv~+j zmzCUr-|)_eisc`}(H<2$PG$`DX0z~4jj(+$!vpl>|6GMoa&Q;{B&jqigVB4~j$t7@ zuCc(Hrj{wK%#`5?$ZfAFQG!=qJ33>#vwD}Sb!RJ@g!pd!Jcx5xiyt@hq6Z?QC&Y)7 znit1q2|BDWw0ss@6jz{s*4B5(?wMw<%ZpU2-5R6aRJ3gamX_1k;90Q7g6}qFXuI<0 z8><$YT&Xs&7rLeGhtsayjf?*rhu5LL58)(0d$o|fgTrP_G@XvZk;!&cF`eC3vmSDF zp2a^3*hY;_v;{BEj91@B36LG9BOQ2iIJ&eqx-M7i+w~K3whj5+#?Of^r(5y&PG4Mw zXQp`qcgXN&jCU8rUr-(JD`s%~TYT>Ke_YiZrT&B1mA@}K-CBd~GTlbn?L6aEQy1Uk z28V(Yi)o*!ehE_yiuT%Td>bD|ZUJ&pZYj9Py<#4W!0yTYm2eflzDNR1$Ns&u^6}wa zlQGTZi?`hY1v@5YY=-PeUR?p&F&XOKWAkKiipXZjn6qeegM4r;j8=)&LSNf&$#m{^CX=+NJaM zRQ}|H;nQ5ka`K^Dg|W|PqH;HGDO1#ITTvMs!r zh?BDNLsB1Gtya)-OoE`wx%N^^@j~7A%zMdAPHv{lmed}Uov$#NsP&K@Q|P@WUcUHD zt9^U0%yeSIfn#?3tIRPcO?#E`D`UKSe--EsUsYmdPRM^H$g_deo09UEl#(o?FV9U4 zk>lk{k7>*`&%aP7eQ(Mph6f$W95>38LtH|TFi50NQQ5GN^TK&nM>p-w@GR3Pwp(k8!wT}&W8*-khdt$ zryz<2H$;I|07h?5{ieumW-H(bSGG}9d-))Mg)+&iqbkBE=Uz3sGhmH98{swTsPdV| z#WFYD^GrX}9WW=J7VF%+A^n_{ni8=b`fw-j*-#SCNPgjvx)|j?_h6?CR{|sC%B!p> zSnPIqp82-QtrU?fHQjylp~<*&4gPLt=XoUBU8D%qikab3AYUxaca`9q%SY0DM=SF| za;h&nd01k=3`Pn~f|G_TPWnfh`J+)9p;pTD7q?_tM0^cXXSNE~VpkSUc;E%qNCl+quV3>MhjCKjZ zCre}nf(KXGEIWr5mC`Wd=5H|gS&12g1gd43)gLkTpjplRc8Vjvty~1Ek%q>|DAqCY zDmqM9j6Xz-5)j|Xpt&DrmKM7Oal`*bv{2aH z3T!S%q z+1Yu}Vj>A%BQ-CDW;i(RXhrW}rDBWkLi74TiL1EYf#^}md8)bIJsW(+h{JceSAoq= zQV0fb)=sFR$A@b_H{cmfkg;0qDx8mfPUno~{^~S7MnL*7`@clmiq_w{QHOrh z3Pm^0lg(N&*<$f)+qm_J=X@Wg6<&?mV^;pgmsc-f^op`4$9b$QdYzzJiH+sKH%tpy z3NhHjF1Re>f6UG}mJ|+9HuM!r3_K!rahoDbqc>V!&e3*Y27a60V|C}% zyLqADHS;Ivoi^>t-Rk@}{AVpr1#J2FlFPSL=Qk4qT^6tAERPdr3H2_-n+Zfj>+j?> zb&>{8gg)a{L7|`>bCR;Ml2);Dg(_V({;`u~70kz=b@TXs{`|?CnvkFD!0my&**x}N zzO2c=ok`FVQid}0kC`@MzpWEUl#&YHQc2D7SiU_)BfLBlT3&CUK@8o17oiWKs-}tu z1smLPSRw3`@4+?_EWTd2&jKEb*9wMFXu59O#n=SnE+*tVmPmYR?4+Tp_ym;VkZRZ~ z*Sva91_4k)zlqoG7j(I85JkUTfmdmm=H@I%0;$Q{H3%~poceu3Nr_mV*z0|M-G(Mm zv^iNffv^_YHPX>H;2FyW(~p-$4KndJUq!s}KY7E8A~Rj75kcMFll$W!sw6vn2~ z6cVo+NfB{$DAM!VJ&^rq~cMa&=P?h4tj89DiC6s zEZTwkV*SoF9E_a_BIsM`dcsCfHkko= z*Zovg6yce(hBt`zg^%`hc$n^*-Q+o!g-fIT!b7An=* z(?|rF?Om3xF_BFF3-%c_drqq#pFIlHRYym=lT~=uo1Ks0|Dh$-BFOJ=&mW;bmwf1SO9 z*fY5|A%)|990#>;>kKW;BeT0~h`E<7?%o*p7R<{90ToVAq9pPlV)DdApo;4?lz6?- zrw_h{Yn*Aj62V8W^7!j!-If1t(#GQ$f{6`8_8HMHSyn#Wxlc3;Gf(bciT?=a6{1cG zq3}0kF98B?sMonZXJ#HEFIM@{Jh)C={q}P*`=k6^dlsR{0u>T^OwuB~K=AhZw*K`9 zV!XC4@COCug?QblE-)?e7>X23?`|=sFqY=I<^*l`_IE;?kxRHNuknSl-#TSyNBNNq z$V@1Af5@;d&0CfA4zy+{UAbG3?a1twj87BXOz|=~v_Ds|o)b|q)Pq(jHl|nZHSSgD$kn_g%C>YF& z9JSgsiFg(SOy^KbQ$%By_KnNnhrgcQy^{ATPK1v7=w}FtLlQ)0Kuqs7W7|Fy$#gs- zVuW#Stb)RUQS{Dsa%g?7B5Ie;b#IHJr5q|Fvf4qvelgDbp z4@PbyyEmRd&});QG9ZT}ICwT-qf53;F{-h3-8&~6v+6njr-02(SHz#Hc}KT>xJkd2 zk~PCX1@lngl_y`-1^?9g(b3j7rUmh6+-ZTn(x`iNE=_Py^sWw2UDKd4xpeH13bH;K zsn~-~xOA&J_^&jz`ZUm%294Q#tO%tvLo9Q`MQ^CiuP5!dY;lE(m8J00wuQjC+ViPW z-+UgFL+kR(x2J^si$7etD3j!ryJ%j6>=x|g%Bk9C*|A{`q~=}N(mdOM1F}2{?0Uh7 z!6d6(aK+E2*z{c~^UBj+wwxz)abCGuH^csW|CD_qDv*D6v>r5_0FYIt_P{ zA-GBIwnV!@(FB5aOoc|EnD*(<$)uwsp1TPzVY6g(J(cRO&59;vp0Tn2`2#huNDl>p z!mUrSUh&~Ka%w+&dM|S=FPi?Tq)9ibFm318b{-fuCCOL^Cmaf%gA$XY9?Bj=QST@0 zM%=W5sY9MIu)M<0tdTUkk0-g3a*M>bg+PXlI11ZXC43}T$+IxFT4P`5O)ksUx}UYM zD_L6YeT{-rs;^Mt#?~YSU)E2x()`ZGAw6kRRhz5zVt(1y(8H)#{N9i?xA}xw6ea>? z=2>FWaV6aIaru|pb5L8R7k>io3F?x|NQv;(;$MJPA9sMMFv@TJGP7~OVf_nA9BIE< zSLISdPG6;W1PB6}7Z)exUd8!j1c4uVNIXvIA#eng8}L4R&eA?A%j@ZP_t3y?#jo#( zIMAv@%9AVULLACrnp;a7VSqr-&$#GEh2}YpPV~x}ZDn z#CMjTYZMbxgc<0<=vKcs4q`pX@o@)YhouhE)s{i20|0Cx)>S9TRjfMGl5ET}_WYW8 zRRtY1$(S0;NltSjBqW-TLhepkzPa#0Meg0IH#jY+KU}iQ#^$heeT?wz-zX&D ze)Jo6!>q|@EV4_q?t5zw$>b~2#G|HkS2uZf59l;<1)GP3*{>|PONp$P%*7YtASv}Q zmY;1>69RAGSXL3I1;q24xSsdOoLH_)_$zK}j$N+uynL^nl3pw>%_K?Mv~pWva#br% zL1N@`C?IuAm|}F(%!mZ0pDXd(16~xkJ9@GHes~sd@FOhIwIn)*$X_Gqq zQo>Ue-X}k*gFgwvWf96h422{@(~5%%-YxP`9;07=BxCnVtO~aO)G}$;{bP_J@kgLk zwuh--$G$m_BM|7mDwp%!kl!l5SnXGo+iPxL4xqbux;in-1i>UR;-U;1b}KP&aBNd) zR6&E+v3p1@l|VDprR&Jk*xng`utrXD6`4 z=q%`fGHu_Co2qU(6~fmgTzIT!62>eO?U^zEP36-bsb$+6+$$E>I848?#=5G0pqUU% zDLF{cxusEqxn3uXL{SigJXmpt(nM3#k+4lR!FGCI@Cg5)t^F^TGD!e3=v+=o9J1v+ zk?EB=%tgC^R9C^<6DUIZ3HF(Y_)xBcS zr#}+!iT8?GCAK(YjAnY2LM>HNH)l-*BoHOOtO- z(232s`C2B>Y3+gHw^RDve3%cbXn$RTTM<)7&5OBe<%KWIE}3iM#pFFw4p*!G&d@n2 z%_{GZr^Ubna(Mq^p2K-!mK^VGhkC=8q!27RW}}~&g`@3}ES(lU<+y_P3igy`N&n_}RTOfThh^<;(ai0DYLs1PW=Fe=FAw z{Be7;(mWN(uIArA4?^k4yaylQ;Xjt-6R$lq@T-Ao{(YEbiuLUXntP&T=ENYN zkccoTPIOYsl&zI8X`l3C@ZC3Xn&{dc0l(5s=S%%_ZU)QDwQ-*FE9$bj49+zxXe2IP z0Bl3ryd*oGXO^+P=YvUBNnqZ1aPRGJ$^`U(u17|`s=bA;_(ivRqjjFxn(hMCoKQ7> zCfy!4$vKqC@s<2hcKI;h?_k##zavN?BOj|svz^eF;ZF?us9j@wgc-{Lq;YWnCj9HZO#R2x8sZ(7*v!SA@vbx8Vo=mx?OX1( zwP4f4rJkxjcI#*IxxN*X0RK6^L zT!v6NK?12e7_MbSd5W-OvHW{*iT~k85*lXqTuIjA*{DLSmr@9t1xYLIH=y}F7idc_ia+(OZ z{rLDvE5iz)5=ify7#VgIBbD{FsCfNcatxw)HpZORZ4v&~DzgjPq^%C0oQ&e99IXTM zT?Jvk^RX!!oAHZ9LdE~JU!8At_(;_yj%c0J8!v8 z?z3ROOKt=rzAiO-wIBbHLdg2Gf~dsj9x2g0US}3rA9{{}9mZ~@QvT$~Rf{*fgP@5GAu6XL#nM!kBo*^0-90Dsa?ayq zx#Sgp1T3YF@D5swqnwN-_S$S0#bS5!@)SNZppyP#8ula9x<0ZeZkSsnL^WN5lfp%J zCh#rmbymM#hkCIV#eQAd4p!R)45VpfJvMH%Nn?P+$Ikw9Uiam8>&v@{9Eh>~bkdbw zdB(JaK_yfm=}{(DFK)->sN$wPl%rZnRZ=MeIi?Qt{19F_`Epo09dFJXyf>Hg3WvAY zaGQ@Y+GTbz0%Z39*`%e>+xFv5P=IoB{29|3(vf3FpO4*ncKj0)uwsu&aug1Wu_CJ? zGOxT<6>Rdt4%krX4Z(@zGJ6(N$0(#gFn!BtA#HR=F!9*%6CB~;NlG8Og=Uf*0$`fq z+lfC(m75BCnjvwifaexJ${*&1(r%fjPAIgt#hN594Gjee(Rjv2 za<=ZZUoG3BxUsSa?TRlR!=9HYXCh^e_oPv25rre&Z^zfqi4L=l#e}n^`IXGeMLx({ z%|BEJ34}X6N^5bim!>|n%I&4K+co05nMf(1Qyfq0p}@BbII03C;@6UBRqJS_}Vu% zKs1Ff0qCD}LK-pErdHQNJLAClEWCE0#dUPs=;H@NHkC7fJp)!BNg>AJGm5LCnMqSw z_2R9Bc{O1>Lv83@#(K|hkQcvEqh3sLQ9{v@-u0fdvuMMrsv8jb%^R`+YM`oI8pCQr zdwu@v0R53WZHUFt!Sp*tzw zf4_W7?1>8FTwFI!ptF>``8BV?D;Mpy)GtBm^i@G;kkHAbD6*K@TI*9^cpFD=^gjR= z0b>2ev;AU1Ve9BN5i1Gp`|}5?Q>D4-xc~B{bqA z*&R;`W3+9bfh^9;XJ^Dsd{8OED4Q zS~hP3ef~}~xSsK3{VW=DPOBYXvW9ko-V9nzj2rv%Zpe>~#S3se1;V9u<2bV-Rnkhe z-1hV;Vpl`LmH^V4C{PUTToBC|@16{cSKNb5 zmCgrh^3&0-cOcmHO8xk(2kmCqCxSl`SAil5N?BrL{0@(qYolsB; z&xBUK0w|fWrvchgwv7g4(c}A_f`iHy{Y=fSM$2SSYJr z)*}L5d-CyUCg<_1d@WS(`{qMns~gj61Gff}sW^un4M|(6H?NR9RD}24Y7XhyTUm6` zV;_6pgIXk_@3zH@-!u-XP>Yp=vj`u@G<>~_WA-eX&NPnq&{-{&z*AWB#^`RKSWK>!Qb+0B2 zpO9UKR7wq!Qe6Y0R3pV9mQ@xi6JV!rr=?edMyvzf2=DTUQ1WG-c@$eEm(Wo@@Vl@03JA z1%i#wu+p!ogC2daaIfZ-twX*LLUQi_Xagp+;gnohW!zVD+=U@?K!uMDlWGd8@|)gP z-WI4QF;wdI^>K6Q;*MPrnC^>U&uEt5seqt7kN$8iLOV` zS(aFz>n~4)E>wXEr1+v6?m(hZb^CnR3x&U-@Tb32`@*}pkX?eKh6se&8-POlv;|y| zqs}x2zbp>XDCN>6)@<3(Z;7bsa~W8xdA%kXBd5|;_`qtAJPc_oJLe-)ER2vU=l}d_ zd^F(&Ar6yU@i1R{^6p-U-4_$tuRuQTS}dd5;uP!=wxirar>9ZR>DK+yxO(j`m#^y5 zDxN||P~g{#BL>H|Lex7|9Yht;babD7-nMAJN&JNXlHdv(-Whuaz&_>+cq{;IHhK zFeZb%T)w*vo07!>#jjBEfbD%(XVU4tIK#P}38s3Oc3;gvD`J{w&{eaZxIbXm-_s%4 zTFUCWqk>#JLHZR7Aaa!Ki<@PRPO#Qwza_M6UGCcT2(MvIJinUnMS{8>7Paj2okKBr z72%E5fr*ot_*Ay?O4+%RT`TjGN}t#GQu8L>$#x0%a@~O}*Ps!w5g4xi8rz^|Cv*u> z=?e=uFywfXL4}!!OLQ&e$PsK7PtH`tHuWI~+>v+w>0NUlb$s*o!7lxwK47wi8LIEQ zrekj~6SZCOU8L7pmjeH_!A{+ci>8`x-xMJqr8TJpwtuQO44F!L2Sf`61CJRph5NRf zxD7lx*Yj^GJv*2d$Td0->@l@JKYv}1IR28Us)|Y*7H?UvJN`a~Y<*}s^Ro%f9=}0N zPdm;=@{hGN&*cMT0&4sQUIw^CT=-Ai^Lb_c*XuAEz6(mB_%SoyE?PiqSQZ`R-hEln zRbdtMUl60~+M>h{IVgl+J1FA0TrJe92FFwplj}e=jhp5T;w)RbQ_zV9%VD(9{jqxy zZQ2rCz6H8&zPbz02)ZvgB6r@F#vQ7%Au84)YLq>cr_<%@{e!Ga=xw3x^8Uz2?I015>Ti;zca!H%e~_hfqp z@3N&gYDHt=_lYYlFNJ?MLQF%Ws`s(;Oa3tjOtM@6QA78A4Jqygj*uJD7wvaEAadxR zhL(X6zP6f^Wy8`u(|(!rQkl{Z$q6%Quij)%?D)+K@yh77~$Efv%_8+2Uxc zoWJkpCSP+pjX4;fG9ht##&y z8*eZeRs52tO|9VDq9VBiRqaUFw#+%0@gn`R%3nl`W6Rj816hPc+t4Qz@H_2?6~{N8 zCDl=~dkH!wkY|M2jiqCRnM*nV(1A~w@ZUj$_1IwqJjqJM(<$@$Yh=Qrur&`-^XiJJ z(vU5qE<+LnbBMVC=xv*>0V{sj{;90!*DLjj9Bbc*hr!1?hCBibp@nA<%dgj;qkl^V;ga}$-Aye#d<%rM8qCMI6;?nk zLd@truYlULdXT(l3{NF_C{ z5?s(#SRUZG8VFLv*L2LuPQbe=32yk28A?9lvyi;!IUjlS6tV9ifBE~C1`M8@07-aN z(#n}7SF$^kIa8;j5tyQJZaUGyr;_fmSD;Fb1ZsK5PgDZ2^_{>_q|(wdQZZNd&_1MNaeWQZ?JipWFsKo@TxWx-&-h)Z6xBJe0VbzuA(E^pTHB6zTET=P;*G)g4B`+K<8qPpmxcr3+u4Mkm;VPusdi`*pGQ00uPO=>JOEf zfBU0k4~sbAn3nYQS9c3-eG^fmRxKwsdnQ41CVDLJkn=3hh{AcY>pG_WbG{H2vG57%P(dQhW` zvd`>1ZbLw6URJc?^|s_C3axa~UCSkFny?~8rjzHFKzR|#oNV%0pczBuumJnMk_aE! zw>LoYAdiFd6vj7pd}SGl#4_AM4r%=fZKDf~Q$UYh%Br*B{#7kuR|xov<9D56cb%)i zB<}^KpOZG(__t6viFi3P?!5CSeU0->ZGa8ur<@a;xnD|SoFMQI#!bL+??G6=F*zwL zF(n_Jkd+dikU2cXowJzVkNPSz{R0tqfxsJA%FpOk>mgu$xEth9n0E(ysLmTPa~&d` z&-!h`N>`fSnI89}e21IibGrK@U4TEK zbotL;w21pmK)wiI=5%iC$5EgEl+XH+sr|REZJ7s#zn+^NhwK5n_FLBq>)_rs@`N7% zbUoEJvm?j^W2uV%V~KgiN)!t5#otdc$P@fFUQpey4*WCC0nX>@k^>a=t(yAqf;xNA!=>HVXO}F3VCeYb1DYyOm z>?tm55E92Y7vT~s_AKOSLkKx5gt&Kp&!S~E#t;ILuz+GQj$fskTfqvy8GIi|#Z0Uk zK>p>(*ME@b-5Wh~*VG97<-2e)+{H}!$?i`n!1Z*ZvzL;=n^S0+?;`T?4bcv^woY)H zmr0=~;~WE@6pwJ>QB#$$z3}{P_C71;_Z5@Jedu`#ty^363JbJIgri<jUznM{mD8lnSLjQC@$sojL00M%&3CUkR62;QjGls2 z*!+I@*P@XhBc|i41^Otv!j?k8Y>Wz;?c084pU`lH=yjyJOFP~PEl8xTYfwRu^2C1b z*U_ktovWP6a$nprNz#k+z74ZPk(@*8zIA$IWOEg1qJzpI&FTl7<2G>R7;MCt*C*|8 z9FZNILEr0oLZ)o7zQFPFjgX@~nW!#y*k3J~2G}{Up~U`6N6J74AH6xsM>w zxwVTBEUZipB`|GFQ2zMuMoq-u<_`9(IX!XZpDL&%1=ZBln)&%S{RQgDq8m9W0{NffCWASoT`5JP1fWPAy*_2{HpFK~?NyX6WN} zlh7pP|6PpK24JoBlN#1YpZ3q4xcVhhVmoB|Ej5%$N#S?Ew~m3rLYN%jFaI z(8XvcIzvTdO8)nEAvUfwbKja}szJ4_Z?Pti?H!#@_utR(gg(TJcA4J{%9~EXQ%S?y zt4Hn=v{&Y3Rf;jk#af*q~_Rg;_3T++`+;SiRqpjeNu<0 zmh*^VxWr!>QUR5;{@ls(45?;q`}cjjuErykcAdIF5(D-lAi2IYr`-_eBTxrgp7eG~ z5sHlyC$)vyp%lh{&IZZm84nt^Y?cCFZQ{=2jd*x}CCC4z+El)MmMCTUT$iq`&+ntq zUc>TFP)Jr%<4T~PD#kHiJ~651+z26`UzgZUPHNYY2cZj%=6%%z)3zRtNetS{SMxT^ zcFbH_@o1tVj zLY8nRkJN@`l_a3i-Z$=$BnxRn(J1rzX$taT*h69rD?+t0)P-2+Scw8qkH*=Qd5k%31vg3jo;lochi@^_95NrMJQABD$SkOT1g z@eEve!GXvR#3uO<6UABy{fx*+PyR`#-1n0U1GV2|@!2O7niaoABAO53kze>Nkn)`V zJ=0xA7lQT#8&gDN!ihnpPxTQ!6C&{w+JJ)52Yy^R1bemi>*C!7A`v#eOp3C4NpQpWR zWOiyzhe_*(*W+W+IMG%Tw~}l<{u$laP)b7t&BSj>0USZu1*JG@(CfX1xA9fclRMEm zMN)D0ARCH?0++Afo@MX;>x2caS3r`PRNfSth6xJ&dBh4xLcX%k!yu+v|Cw9v_5@;u zTUtRNOWTukb|1GknwH{S!)Tx$)?b&zwln_{YQys39G|wQdP*2j0-a}FB^zkK$_DDh0$Q3>MwCRg8r>*5NU6r_h=njK0GoRi@+ zrh6j7&BSnCwM4Tq7=18AGV5R&q!VM0co$aH(0olP7TguHF31tnAb&0l5}v-yecs?(dlidN5GSN<6|JfcTh zZ8PZ1_f=y0-oVM+E#^N8Uo^4d(PeWh=}7aA4esnrBXcvK{|%O?QI94#cl7_diwAb9 zsg?40p5RolR|xSmmeI!B`sAS1G8CiG0T}m@NWDy)is{EYn*95o`KREA(hrh`gudF1 z?QPV8?iTG%RvYL-uBe0i``8Gtdt30QlrT93{)%=7HxBH1t)mT}^f(||yZx$JR4Di1 zSukO+Yg}|hm&(M{0$MZFK3DWliax}Uivr~aB=I2V=5JDx*b%BPUDL+ZoJ*^?rm4!V zPJNSSy4fa#`@E`tw{y>VRfy%PtOfEWNn5X_t*SKZOl5+jyN(-C*_hvpnP;MrJJ$CR z@=>bUEZo9Vl6|=Dx=nUv47%uo)C+F7z1v60huDY!u5E%-fVH+|*j($=FDr85cinWu z{5QWz>aHfC)Y$xoX?QBB95G_J*=5%Z&XH@KI|XV6B9`nJLT;i1!!>3da$S%xD zN<$l!LMR<7P67i|E!zoQ1ykJToGyQ}j+2BiPx*a>yd7e#+{OT_UJ!15*6x>?odY_c z!WONF4+VGIyTWqNNV-{hvzcpCK#1%ii?%>~p-(CoJRH+?d4foL9SSNV!ynblk7Pp^OvK@PY0|Q*sx&5(cEX~Rn9bB@Mt*p z%4(y2=@Bz`b|it0ui~tR*SGab|5wbI_Q&VR$2h8Hq;O1--E0)I|VYtiZ_=lXKhv`L%pjt5c5!N}|Xz^)=(q zgYo}tz_oe7pwpj<*5c5`#l~4ZL4Z0i_K2RgqM?Qy(irL9Bz%~Lj3nOE@$)kd?}9Ge z@TMqt6IFK~$Q0K+a-wfVCkm6qyT^%KzsYAnzV+LCK-cb$)ELfkymiUW#z)${V=3o$ z`_CA~>0hgVBBVvvo+yWTO4rypz1n1n$8F*Dnsn!bh^b ze=rxywrPK3@hLcz!m<8vffRPokAi2-Kb%ICoFm@N_0yT|S+Q?N2x$fUA1;d^e$(MI zOL$b~PE zR$f`~!jn2%+n`5bRD4aX2e+$1Mt`Z?bxhmLF8}7Z>vU~Jg4yfsbV5F4_*0&0Z?NFv z&?1^hH!TBrF!*@V!uLBD{osB}@0PR7P9^c(pd(LS=bgYX%KUrxIS<|M>K2<;Yi|-75=dR~TjanK$p|(yU{5vSR{tGtm)P$qe zroZru^AVFjug=4S08s|J-4 zXHjYW(&Ylh*qyWMbv#2Po+fsQ+rxi3xRrH<-svXH(;haM)8`pf9{T(8O`RpyKxPir zeHBG_auVbW2IDu&VmLDQk4y#WvS~#^e}dlJ!1fYq?jY6ON))s~_)bLg(4=>n$h8>` zrzZ7bgXG0BRrQJS&@$`h9uGW#K{z~jPT*IrPIzg-S2{I@75)++Ted74m+fH8=@`r$ zhQm`|rW51Pdkmv**e%iyXf)TpN;@DRA$f}oyyKeS^oMr27=r|Mf^#oaN#jRLut*A6 z{$zJ0>@|wpFCL(inYu!!2RNnU8|S~@dp$R={Ts{q_zEidsP*5=h}K^$=3X-@@x);S zVZQZ^`(n?0DHUt_uSzz+RK8=YRGZT$!p;^1f#e zQ)EF6S_()Af0P-UD8q1&tFIwN62^jM{}l4cRJO;4gTTqaf_cL_QPi`h%$+?#`@=TA zO!ocC-Gm}FnHqi=^xjxRTL$$GJ2XgZEFPUXtj zKE~T(+O%kxwyAy~8hI<0#~>=XX;bTl1jTA!$PmI=wVW6#L&vJGzLp;>f6zp_5oq^yEb2~D`e&%Dhd|iQC}dCBb2FT(OXjUH_@+v7Mo`ZMBV1;- zffF&xWR;p1-oCG=U-9ka*|UOf_Rbj%$7-2gO;Xr+2y&|2_nJ_-GV21(l33v%Nj!-M zCM<2$dOnOoWWuy9o+KNKhpN}}E0^!pClaDwlMT-`W*k*S=a0^oCi{N|==(?HMfw(T zs~yC5=|(P1%9NSU7i+B+le$xBm7JvWW;5fzHjPF@pA(DbQ9rv)DUVb;a+=lAtu0*< zOb}I=p{#lI8xAcoOvGzC11GZ+80Hd=G9x*N{HJ6apczt4!`pVamA||D?`0DA7J+qU zD$K4xKHAOM`{>;KeN%RV2)DaESwqKVfDtF#QLEeFt|o@&{_WY>5XZ$p(fkhuB1`3= z#Q`HrM;Zd`cK|1xKkT*DTD$Le!JND^BCy6zNxdMC-ED=Zcuglvd;lj0AJ8=nO2p83 zPxx?A`JugI=93IJQNd}Gbq8U3pj&U}8sJ#AGFkiIX}o-i3U{Cv^Q zB{3>YG*+q)N90M?$adD_jlQHSD*k>HWP`q3u~(psm*(bGFuCiAqht23c8U_GCR3Eo zq%RP4shbc-+v49YUq3<2fB-cJP!VA(nu<#0%kRQA!>*6H)yyy(j11f6jS}!K9Z~Yo zSqfZHZtAh0%St{8J@F%OpkY&$&&+y~*9*i;!}>1)mLIADuEPTq@joN8%ioTE+X_yh zHi*1CdyXYteX*Yifn&iH-?4q>U;Kgz9zGdf^Yc(U3)5W8KX}7x(Z5LR8#NUkfg&ZR z;>h{)DDc<0``Gz0)6&v+924nI__>)#GDQL6a6|9>LNN7%?Jv;T&8yoqpbboqVchl- ztxwL_txHL+^jZ6ZdKJto#e^>48AXe+?t4sU!(30*`@#W^XPs7W_9Jyda=SNaJUHB0 zAJXX{d6}xvf31^#hTv#p=XoGizB|bw;aKt-eu`3$7YH2(4o9Bxfb^Y{Q_;xh67scZ z^mIqcS3_~1b};lTzmfqce{Fk3CQ?MS1DXH#KYczPiF)RCO~%MCKz-I}9m5KT@aD03 zn+i20s~i0%$!Ru9$D=)zac7uk*(W40Chb$J{A1H(&l3`|Jk*>4bUO zCv0Nu5m(?Q{RWNz$DbJ*YU4bs4ylELZa*7@caW7R#ev&< zFp_EGqRj5D2e5`q;ZS2{r~N2SsTJ!CfMs!6{=y?34`w>%mZ^|L8>a1GT{L)X)Y@2V zJ?1o~D!&jjB74K41NvS`4G2$ZT0)&X|CTLT=j;X$ag?(*2#;1c60^1NPy|1^A|Ge z2n-d3#bbGZ^Y_{q*=-+Ayt@MUW>U1Jae&GUjZ(0jdp0}~Mt5+=F=UipVKWkeM*c>z z`u+5-!fC524q2;(YC_%Qk0I2Fmq>9Ib6Dd!YPwOQe!s*RI5OBi&R5(c>#F=o3OsM& zpf2jh^}1=*k-*58tp+b@xc`iiB&h>bo8*433EJRdhRGh;*(sF1Xr2Wzq+d!~4mDg1 zi^G{({zY!SHL{H_?sJXSnDiejp?{$D3aI&hYg018ojmj3a?9ikB-V|JNfwt+_{=~- zV@^HSl=9g2Dy9fozU{H`*k(>eeYMDY`XG^Fn6TPo1&9-^Y@1Jt zhiYTRa!cwvGZ4*^`M^SslGql&g7O%bQD9Qeq@n0)xd@^WcB2}2S}bS8 z2`9gxM7={NNHl1a>@(X>cDU@c97~9)Kps48eV5IU!hADg9A2tFKZo$3rRu*{Or?|80SIp23uR^JF%M6z>3GTkp6>5}Su;>0GH? zx$hO@?noSjaYOga)N{qZ`!inIJHGteYa`OY1y<7F!1db~h8)4YHOn?*83~4F(|vV8{Wa6%Y`lJETjxJ03u~hR&gp2I*#g2fy!n|AsYZ&75=Y`@XKd z_vfNQ7yJPYkD(%V<$4YHf+ZK>bvL<(jT-7KVEbEs_$SpDfuk@h_Tk5;Z0gnvpQjz- zXoZ!3Ph>ZL{MvuwMx_zvfGwrC;g2o7Oe5>cklCZJUDJe3PcwcwA+?iRsHel>&f~+I z9}#P8f^?1ZYJ;55zqBe}ky0t$y8BcYW>g?_KU`UmU2Sqb&#b<~@*4{Na|I7nRb&V0 zyv|vamQ}Vva&rlRP>WpDHGqNP@X7&3A!Mas*(d@8*snA>?md zyI##+sV#nXm15yLGwc-yRt*I7J)p*P0qWH*>3D(4guf-E+ez!kKmGI4q z0gCQ+b2!lV0)FN=ZI|EN5f&?-t5KO{mE9D9^Dry2t;WRP^%b;G!~J`>II-)R9VXFe zk&uqdKRv7au3;}xj^I%`aVMYU^s+8YWtqj$z~Koo%A2Q9Gu$jsr?od~+n?Gh7M417 z;`#3v&=vT5?ldBnPt)%PBrlyB(8BX*{v^e!znJY^*}`5@i=h297N<(n=CmuE^@en z!(tXO9FAv6Q0tnc+OCEvV6iPWreAyW3UDduuDkD{b$<`CmH|2AQ2d(V`fo#ZRg5^Br%{i`J zUD%rhmep+>Y9- zPFocwGQVZG#R9F@zlWmgMSDqU;}m~~nE&snVblj+9^kOou&O-t)GOyT3mKj>f{IJ^ z$2F&vGW}6{GBD>_gJ$)-v$5R0{s4Sb8sf1_8vwV+1|xI6ndQ-i#%?7WT-sBN{ zdv6FmZJzX(z}71{o?wn@n(b94USnzxAtw_PTFZ(rZW|ETp%%*(F8F+0m%L3=E4fj# zBy4xNK)LC6YMVMkq=*KSlyKGsX^F^RE23FoZ4xix?rv_rJ`< zr3buMl8rubchN*9$>U!4+rG*bNV67;3Hh8M?$rW577f%sh=70o6Ops;=r=~d^M$w`KzyYl_jq&I>0N;gv8}%^) zAGv(%2K7nrHltA`e1w?Rg3Whczl$hSUnx}`_l{_gxD9s3ebfgAs5j0E+Btj4;Ph z#kF=ofd(Ru-_re&DRjuS8}iyb4?#6LOHWAcQf$Hwkl;74uZb0}8i%JPcbq><8(xq- z?dTh5NGrzsOpn07Ow44SwgjcVI7&y_NCc7?a+NYwkby^JcvE&toq#qw$u~I`UVq=y z$QFNX&n3`AXfr2a2)hs>5(1qB;`e~+8{uYHh|XKMm+lTFF-|!H$(8QxnYWB@clXBLmT#r- zW-BWI3qbXR&%TRZq?-W(nynG6Rj%F2mHzhA+E2$|l#XY}2Nchl|XV2;Y6t z1=m`bw!YT48ktsTQP?ORgj7DWBf}!K9e?8i+vdQ>qr@$|DRzWUSG~U9Jl^O8SLPy$ z+i-8iAOp=fLR3n(!q-UTQhKNc<#KxH>>b+s7=L6FSVAvO?pEs-En;SR)0yp%WqLA& zC(kN87Q>DC_>;*-(`vys#(k zl4sjd(?L@i5sR72PNVKi-uBOvgiMpiH$kFvHO~8WnDDD2%ZF$YcBvdNa}bTg>-`8@ zAI&TbQyw3_twt;aYF7c`&Gto(I9%8?es3ENwSDe#M$MDoS#4t`h}hotV2GaB&CoeC z{7ealq~~~>;JA~(k(y2aRQ+{(=cU}jZE`@hV^a><)p2ZxN`O9!$o2h(ySp47(hCWa z6UN*Rei1O=!e^EfwQFbFO%0tKViGDortX)z-G&J#6}cY2m&MBShzwI@C#!z?E&iv( zm0q{qdsFru{`ZJD1K7kNuf-Q7E966xo)g+{-5V*-2U*nC{`^M8yztyHV;EGD5=<*E{^);rnctqL)iL?aDhcqWGG> z5`I4Zhgx|{axBE$6noXG-?;1gl-IdWL!2ow8456gBhpZN`+%njsulMdzdc5`Vyse;V%n!O7FlZ(UY_O!n+ zt|%5Imv7jjMWG4;btF-LA04KEMxLT12BmU~z%rD(wiGc*;SD^p)Y?MvVPYrfKZ}A2 zld=G!KCL(XCCrqMO&21N51@tH@c{Ae3qmI`YsoMQV6!J2U47;+eUYszY~p^)dSLF3 zRvJRC#u7?VS1Ja8-=0dkS3!cX@kCpdc^-F1VWiqW6U`VzL>RzzNWse|@B z4*eBd65X#C;HoAsi#7uJ2VJE!6VeRsHMC;yD@*-g7lQz1K&u_=xfXi`P$^tO#RQ@y8|x7ED`D|{Hj0hGzbG65JR!ou zM_seqfTYn1bR6x55 zLG9G#pgH8aT(7+rmze)WrKzpSxONXC>5vTcZMC{;09!?f_MjRz?^rDvkr;Zp$n=uNKgww`O*fc>MS2z7(q=jKgV@KcR^ zX-+V2c-#0lOgW$}!i-&(pGcwq(r|ojf}qgIe@;-g>4oKV@5_h7ed_VVB@M{m9c(Pt z+2SE+GSi3vA_=BmfGDO6r5;9$RT0@u7evHGqi@kQ@N$-O?GpS!)nj!eK=$=qCY z^J72N{9iD{2z(2Mkd*i~H1*dz3hq!{KCW5CABTNBd|7J-N_Z7Tp85eKua4C6>l4ezAxKP-Hpmw3 zYMT;!aTG?0`4`sIvXi&eq@R; zuI!1El1DJEh2cl@0WX-Mf?SBA+kQx-u`=IhIRG(6|CnYwa8@G-eQ71YyUE`E^H`vQ zoww;E+RsBB#ZtNj_M(B}^N$qi<~3tU7H;Jh=OEL(6s`eif6RfQ??lH+xP9wIKl0Su zN`=t2;|`M;JiZBw?vL-}-)JO%D=73Rkl>xJEbhaV%Ye@&Md1Kg%^auyqM z#Ji@6JLxW=RT3;>+MyKouvKVffG>EdJia=1E{UwV*-dY_U;mD-EFbJ{BjnwQwz@F5nC+X8mvbS*NYHP!u-&izjI3=!76 ztXm_}2O0R!k@mkEEhjQdQWx%*Jdfp0gNL43`q%F6f4+5$dtJAbo@fL`RRdyOuri9xTBJ z;kP=jXOOeR!pjdbs3NAwKot04Y=iUH!lDb1XX!k}R#4~Czm5DxOpjIPw>u}_h>oBM z=sE^qz5`akBaZ@Rkxh=itsHtprj#f@B3((^UQf5k6DPsj@v{?CFFmtPwp;Db2gOvt z1S=k#{O+^tnpldr20840BOt_jPm{-7Gc<8q7KDys%c-X;jVD&u)nmVqtt3CxwOtSK zju3s{lZj2EwDVy8pZ1-U0-)Hj zT;(f#eIwtOp(WHX**vX}-cy~3^^;vC0*dafBPfkYWcnY@N&wTjZP|3V1#o;o1dmv_y)8sZGrT4Tw9P46?SRfP@3QW zN^(gxwml}kYw~$N8~`%am)3@WgFP-dbRH|CbBk==FK*3b$^i za7EQ_7p4ZRPn!UtiC?->R2wQE38)$a{({Zx2cX>?nt`JT+T*o3wt)2`bnQK&@7hij zbp-f{L2PxtMreGK5(b*#HFC&v-V+Y7LfJsN1Q(+`8`_oRGF4xN<0D9qHTjyE?zFx* zN!#J!yf=C41>8_6$c}sWhB2altLQPlu8KOF<^aecxKBA-aQi(5>~|y|*Ao}W-qB{x z=W9W*sA-h6yK zLw1VJp8&7~j>%?n`ue;>fU(*bp7AM1i)cIRu7ArU$q2i4JUM#A*NIE;%G4u6phCj|vAeKq%p_mKwNQupb>iStnuHroJ ziMLhEGuR8j%qPSzquPcI{ufaZlko3^P&ddYptk?~KK`Vuq8@Zn7jA)LZ{cR&`nwj{ z+$X9tl*<I&qSb@i_QD*=;k`H&b$2i?(e$9BW372(8;JHG9&)R+#5#WKtt&s`s>rJG&e4OyAHoQlS6!O zgptAftfA4#hL+h4;LWV50G(=fr_Ow>gR_jp{ENk{WAcH(;u5}0PFS@ANNz~F)YN{4 z;Cyq2*4U(?QyR#eC0DNJTK)SBMaigD7aExqEh2qF{HyyP-Hvh%ZJjb`d}quRM-*PG zFLMDICNPrOY+^QH$IY-EPs_H{-dfOn*&%nmPm&2v8MwIXJR#%SAYAZIdP{(Q*q9@W zLSdz^Pjd~$^%L#XYhg+}Fr6x#sX_TzDV_f@>4<@rf90imp&Q3jA7(JVt1b6HqwC6D zw)iJa0SjZ;U3Oy-a}>qU=3yj92at)B#Vh{vO12!hU7-;|7qL5wdDPugNp)J;f7!s{a!v zomu)nDjpMn&0K9^qz*9HPi`<>>ep1gTDG?df>-2(RPfH2ZbCwI_KlUB+Tb;Vt6#X= z_UsE){Mov{Y@P;UnY|CCKphPRoT-z>X2=QJ;9ao^p~_$*kWe@6tm@0?RDi%C0xzq> zAawGI+1bbXXDk{5N%bXq5YRTqLFxy&X4sm63TQYC6nBgk!nuDxb8YhJ`Kae8U>Thn z2lEg}v8p|D6LM;!#^gVF8Od2#{{c|{g6|vc4OHgKnF$JMV(VKpUAAOa{@>S;{eglh zd7Pax%@eP)OoMfMMQmO4kReQC<|EyLF_l@9IW>7@jhu>M0`M#^&VSs-$Fn`4`ygL6 zBL%%Je@^wzE@uWq27E3MXwX}t0O(9~$Wn07|I z!uEnEcZNtH*uzJb5Jw;E!M3iQb&_K2)dd-3Jw8QS&ajbpKFLdyvG(yyzdf`{1w&R1z%2(6AmsP25$2O-iKb=J*wfA#sc{|MR@KZah7bA?5mrrn{ThNKu z@7wPEu0M%~ynW{Iefe#aWbM^T8P{Zc+4tu2JslX}O#)l^nFz3_A2DucOYYFL_4&ry z+kWrVHg!l|pgt^~{=qb;Gb;80xVlrkKOQL-oiyxvsY`(8c7#;rY0)8hB4Na5LYt8C zt&a@ajA~dYHqoO_0eQhcu~%$kqhdqW;xc4x9yl&k4)~|Wc^jQnkR7~uvZ|GG28abv zyQpkG)A(#jz!l566nK)H>!>L1$CUyVt6%l2)H1lt1_B(TQPcIAlz?-O)#LpQFU_NW z!Fm*R3NyMJWDW6hMiG$sf&t{1iGeTESyyUw`pUGH6Oij?WfG=A6*A%VJ4+skm{)Ba z8TJsQ45qYdq1+DM4PP*Kk`w|6UM1^h(b<(&0Z?@&u-D^6zr!2s^!mastV-@YSNr--sauF#yX zj?wKcFC|!nmF3ejT%$E4Nl8e2fmsFt9XEdB0zd{srFGR)>K}PQeY==4G3!(%*mw|# zs@Svz{SxHc9r*zq;AAx+Yr8-rsLBjkHfL-bJ?!*H1FkQi%L8UFj7g~-#|QbnD~bBT zgz924z{Wr1R;ch~wb!?1XRTYuD#gM(N#tm&FeLmW^*s(n${X!Pj0l;7CHJyw*7X%i zsYNW+vm}rQv8U@2-LuVd4qq76*%h!F$cr6D4^W)HvpA; z^&ESErBGvXf9G*C7DtPfoHw#83K_S>%X9SM)is}Aa{CB$BG_+bi8kQqDCX@yzUn|Q z4>6K0eYcq)l_qVOF_TZ96tee}*k&Ro#n$EO-*F<7q^4cWD3v5fe1x!<=~{6GfIZvC zrvSLGfRPq~!-lzdcNg8!t7=AiHLWJDq1(280D^Adj$oP9AoU=EF5Ps> z@Q)2dNBupO#AW0jLDjHd$p_c2QZ8!+Mz*&$)h)lRU z(jKoI_!Gc4(ZJ@fGb&geL+eiNch1xDT`=DX@)$ZpIOabMoE-v?bp(WUy8qClbYJvH z!sM62d%ik0k!&)F;#avc0C2R=3`#-1vlKA^GjA}0f!N`&ddhyx5d?uNnRJYfp_ui{ zO*O2as8AgNSHsUXP};Z(0TFJ+I}!?5_7@DZH8I{iX~Dr=l;~`!49(Q6t|2Ece#=Tm znJ~c^0HdK6U@Hr}!%VZj1zyYTCc(3vZz0vFNBI%&c}$K=#0O{u@r06$oUzgEb^(O1Qnmq zE#xVBuiDWuWI~EdlB!w`0N?`L0`>KeQ;gTL7cW8^ifn;^ig;+)ECC=Z*$=18>4wqd z`*E5QR1rKGZiF#o)BUX#5&A{Z&_gXa^a3pq8{@ zXU{lqzPYcQ(L`T87U6ZO?+K>Bn+)0m0P<{UMEq+a!}jH`Skb}pcL9bZk9(6e#Iier zkmZezv7a=4<$e~+PS8FUxL{T+E?{Ml-t9WO9{DPZo$|bg9>3x|v+zWk5orZ~{wkM- z1ZHAeEjebifybK)(|pukVMx9Jml-(VuAyP;h-EVw36QWVnd1ZMi8rZFmDE|nya@5j57tMLTf{R{K37h|K%EA?E zc7^&vfMBoRFl=F*MIAsU@%|v{+>db`RnoX~`A-E8y!gJ^vR1wg;OiTC$R4|yXgqS3 z)?NNln_|RiG0oc*U(i+6BT=?r-XEt2dd}Hw`is8MM{oXJl>z1J;gWuzf2b$ zq|2eYB%F@p3g%A7#UM$FFZFxl{cpT{g0dxoq7E$0r^2xTpNEn_peZem7WwFgLa7lX zfDJ`Od-QC5siku-u~UzD%eKBtB24E+L`8K7O$Ml7=G{SgP`S+O6N1FssJ+CK1n&Y9 z>un-I=9BYrYx8sYW_^J8L(87RCie`uD=~$zDMc9BE$QQ`^uhW)Cxj=Be|SOv=K37Y zn!f!(l>c4p4e#@JV3=z*Q95ze(Ws~;n5+?xu6^S5qr9BmW|>?(cucsrf*aLpA(WL< zK#C7w<50Hi3ypxxp%)ufAoRc<9iyt8bDlG?ph&=!b>qfL2H`pwZJBdZnYV!vKpur& z)9;4We4|r2QoN@XxH)c|sBr81Q&*5@IFn=6Z*=U{`}0W^*E?TIUjnOuI0v$QI?F9_ zhH}kU%D%d9zS-mPFeFvnWaykTByM2xU(U&MgjCF^PGYm?X^$mGiuD3~c}2y~9L!~?}SHac4RMXC@{N6%F~YxY34sk$r$fI94rn7810P9btcyc31nQaPJ%Y_oC|2~ zqyheso)*{|#iI+loH@GQ{6arBRt7 zGZg?&V?a6eS5us%!58ILo5BO(tmjqJK^8c38v3%5P?OOECE)#__dt%$TS&;43O5J+ zq929|qL+o8`7#AAqb#^$c#u}c$pTzJHyrr5W9OOcw`Vk^F zlg%2*R+}E50L#pOEgR^(Lp4C7_k~iW&@SXm9^3xTYbM>XZ+N27=ilUbh<$m~z>o z$edE3gxFuXX%(H`B4|f;JK~c!o-Ev;U_ACS12Tf{oX_F6d4T2QIxwNXGZWeb$RnkQ zji?wdrJeZYPI*BFmwuQ{rEFqqWuA!@#*0^zv9bNp`jO457d|UQzviwj6)GBv$}X&& zT7!>(?Nuxd^r4zNf1k}yBnb(Ns}B9dRkeKLwINc;pO?76Gis^?Y(HIO^K`rHGuJo7 zbfZAUpcUL2xGBy@e?=AtAU9v1g&J^py{VI?o>=4D*eEgZtXSVFznux135a%O7@V`eX`gS z7$g9%F4z=pQTZ@6!FK(N>SdrDA)mQ}Z<^*}Ci+#M&z}{iJx||$%M(=I@f{+2jY4G8 ze&dNHoJhitMz+PMxEFU3g|O7DG5((Caa%^G_yb8!%f_Ec#@EFYRr}|qgyh8e^l02? zJgN{{6AnSkN?v+VT^sHVpu`Jmrt-=b?+e(>iuGfAX;(XkLZ<)gEM<;3*mNKhIV9=1 zEUYM;A#Z8m#=3|x5!o&J)==E0L+XAa2>)qew;=ka*%Hd=@8UxFLbHH`w#G=UqJp8pa%Y||qNXI~Dn_sH zBzwIL(t#zcd;l2l7D7R$skxa8N&-&(C3i=Y`CpCf#qyaRuVv8jNjyLbRDK7V2@10S9<%A>({E zM{0atoVMd`XS4nt|J`=4#Th%Gg49^dKN@4@jq;5VR+~Mq6NWkB5{oE4Nuq_sipg|L zgM<}8^F@8A=-S@B&-mKqN(cAl9<^ZmW$XA}8Ab)0Y;A^XDEjNr1|uoq|JYU|%+)Z! zjW^N5tSkbi8l?mmg<5Q6))@&6*{-xIkT1K@vmdz(MOEPSaA{~opaXQZNDyX~Lu7ED zx5ht}wm|q#O# zBPr_EK$9o>0pSZv9VK043%G{dD6VWpcB;QBO&bgx#q6+wd4AcOF~DpK{H@ip?@vQ( z?`s)Vo%MgJD*!Zl@bT#_>v1*J0$^WSJuj(zn0DS|s^zW)x-)?B(OHU2yQiXnaXnh0 zlrgUGjU#auxS`n=4ACbtONXM_9p$vKzxED zBR*&Kxohb_%?b!eRrFXdLX8O>qWCbU0{Sm>M@(YH`lL$+ z+*r=_m#*m}M!%3|^z?0DRxc?x_^CG43`_aPTJ9aVX8qnP(DVmlq}nW7Sgn-6o{Nxq zv9!$T0vZQW=W59S#U5Ln`R}HQhcC-fn)6NOK2A{^*&C{r$*eS(BGjj$f7~7RUXsWF z=B>OCeBU@f!41EsAqLx4_y9WIYI=D!3MHBKR)M<3GxY2W%jBKHNsZj`ru@`$1Q!wo z&~cyP&Rwi<{1&va&I+Q?q&&|rFjWSRymp2R| zt4`ER<|~D7A3Ur@G0^z&I&t<(Zc-L6Ycmxj+Gqp@tZuB#CTA%5#sFO?)=QRLADwq> zMu<;OGKy>TVd$Af^&g=?Fmt3|g8)pB$&KkwD|M?sH(g_qKtGxM>IX`GJj!bUQOQz% z=2rsNN=#@{Y)#GH*6ZxFpkdu zPG!pVSx!EUnJ*5LuZ6BTM*Jk5C(#@YCAy{Zil12DcK+kO|5K4-PvTN=>)!C#L4c>s z%MzY1k%0c!$wnEc|CrjCz8{HB(LkWi{&P^AQ0DRZ{_A+DO>)Z~l3SR>89!xOhRsy7VC;#9*tH5EEl2 zl|1V`Otwia=fjT19ud)WsM-{;WBSfu4!F}K7|b3xE%(QUjIu2*`Nbt<*U+&;T3DljCB&FLMq1SHFx z9sYQN|2^?@?{r+~NL_7<|H2YR5StGrlD-D(G#M%Hp_?F}1&u9C^e-3-vK+CE8IhLk zulvYCvK?5y%AhHy@!TTTs^CWHbuel?!#}DDMNh8=M2&0O}T@p-jQA+x`qpV7}(5P$&} zg8~q7y1nh3*^oO=YM;liSFwRLZdsg)9uVPWzqe0Dcy7r7vS%cfEPIorOiIsh<{X`e z_=*)(xr)tcXDg0v6}>wB&2kUjtN9^pJ2HP;v|L^+fw~i6C2}8Jw({wwi6}6ML&Dwx zWQm_RX%wjz_snpjdRRD98|a51vj!cD$qjtvf__lXqMdHta9`#ccm*Pu4kaT&_%GyX z>;joaM(DbJ6sF@{)Nt^nfe$C=E_e@1u74&Y2dNa3L8%4j$!D47s2yOY_C(sR`pf$- z2lP;W=|{fJTc2Ax+z#)KOuV=}an8|GFLzO!f)*tK16U-Z@@;5eSgB}L{j}z+MEh#= zZxl)TWXvY#in}5NxxBTCEuoZpB3%#@+f3(Z)IhsO z)iI0be)%!3Z#pI6Rw&iRiSsILy(5jCOEdzRYu%P}ZMU(Y-nBaxrm!X~h*j zz^c~jC^i9QZ-%G7C}7=USS-ZP5xx;?2 z&9=0%&=#%{YCWY+2~kT_dUW8Q1xD(|DZBbSnkqN1V)Wl}(S5WAA%LiMXA>xCkhdd_ z3F4C?4c_r!y@T{bKOO4aEsJF-_%0lxxf3W%oTefnXe!~XRU*^$XS5A>(#HcF^I*Rv~WqiJAcV5YR6L8&L_-GDvdzj3cE}yC|s1cb*m~h=1o=4Wb>H! zrQRkom^DDfKz{dm9p#=XwZ>t?p`I~z`59Ju#KKbf_W%XKPZ$d^H)B2aHLFl?N6M4t{FPAXoIS-)tyw=yk>*q<`(O7JN{1j}G7QRti?Zl?^Z>PBB||-kt*{{Yp8<+c zu*8y1;1Kqpzm+sAz%rsztdGo_p>)(g%)Ku&eEeiA`2%3f$uw5m>c;fWWLLHxR2~h( z#|DmjjD9H}c#q`KjKg%tfi#RU$4^ZcOI4FkwRVtNd8(ID-!Oxl#YjSy-4NSPS>Le_)7p?Mx(lK8-XBfZ1@at#&G~xXWA9r9`m0UHHH>1pLStm z8Kj$XuZdx`e6=>hcW$c(FWbCJjOu=};?$`FqbMD3n$ZGrT6ZN>Q~(K(V;oUZ>f$=i zk(wC?VQ!xWrYn75;{PuUr+1sX&EooQV`tnN@EVr1Y^7 zjcYrfKF7At$h6U@+sTG(d_u#syn}D=Ww`!QRmif%=r;<7-1!ZOk*~}hI%F`5K*#rj zJJ@@*isZU;j@SI&rR{BhGKvk3)S;(ZPSiR%G)aF6fjnH!q}`8FW{xIkSRnri_DZNB z1ztmJ#%Ma!tQ)IuNJLO?W@&HzA_XEp%iUUKo*hCBtdEo)A@Wh~yM-*2f$ex$A|h*@ zG~=nq(-m|w;IK!a{mA_KI$Kx!w{^TWL*%aA0PasuokJVfrjMTtw1AQ@wfygDVdz~a zuYp^&oreu2r!3c2n9r^YktV)lng3&x4GWF_!z>ao?+;q_>KfWYO1T#9;N1(6O^Ir? zAM+|GXBclT-6ex4rrDQj0_Umu`HTF$*g5*;aeMWKrm(^lu;2j}9V@fZx{*?onn$## zte|PsUjty)Ted_p%zY{dKMUYOWe5JXb%MK0v7weYbm|Vjy7oWQZLf}dy1yVAnfc&U z@q0!-Qb^OjY!=+J5R)Hzx0ey%q*;>WED36Ifj(yVuUd8&VJgdh{Z-BRzD*e9a?xS$GXxs!CmVU|GiXGZ=^?YI&&&^Wy293_SL%c3u_t8h zrNl}<%r2&=|H9UGWt0f|5#-U0tT;89V|c2TA?QvF1DBoMjj%Cg_9Id^UKc`fA7zrD25u;wXC|GW|+lw@vP8`lwfm$6%)! zLOn1w(2Zq8Wst~kdBzjIKxgjxBYnO=Ccs3FWtT0{UXO(3QVc*nj8Ht;xGa9zer0GG zf8Dva5o4PSkD_nn => { const balance = await ezly.balance(account.instance); const currency = account.authentication.configuration.currency; + console.log(account.authentication); + return [{ amount: balance.value, currency: currency, diff --git a/src/utils/ui/default-profile-picture.ts b/src/utils/ui/default-profile-picture.ts index a661c384f..03891dfa5 100644 --- a/src/utils/ui/default-profile-picture.ts +++ b/src/utils/ui/default-profile-picture.ts @@ -1,6 +1,6 @@ import { AccountService } from "@/stores/account/types"; -export const defaultProfilePicture = (service: AccountService, accountProvider: string) => { +export const defaultProfilePicture = (service: AccountService, accountProvider?: string) => { switch (accountProvider) { case "Université de Rennes": return require("../../../assets/images/service_rennes1.png"); @@ -23,6 +23,8 @@ export const defaultProfilePicture = (service: AccountService, accountProvider: return require("../../../assets/images/service_skolengo.png"); case AccountService.Local: return require("../../../assets/images/service_unknown.png"); + case AccountService.Izly: + return require("../../../assets/images/service_izly.png"); } return require("../../../assets/images/service_unknown.png"); diff --git a/src/views/account/Restaurant/Cards/Card.tsx b/src/views/account/Restaurant/Cards/Card.tsx new file mode 100644 index 000000000..27938e50f --- /dev/null +++ b/src/views/account/Restaurant/Cards/Card.tsx @@ -0,0 +1,153 @@ +import { View, Text, StyleSheet, Image } from "react-native"; +import { ServiceCard } from "../Menu"; +import { useTheme } from "@react-navigation/native"; +import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; +import { PressableScale } from "react-native-pressable-scale"; + +const MenuCard = ({ card }: { card: ServiceCard }) => { + console.log(card); + const theme = useTheme(); + + return ( + + + + + + {card?.theme?.name} + + + + + Solde de cantine + + + {card.balance[0].amount.toFixed(2)} € + + + + + {card.identifier && ( + + •••• {card.identifier.slice(-4)} + + )} + + {card?.theme?.background && ( + + )} + + + ); +}; + +const styles = StyleSheet.create({ + card: { + width: "100%", + aspectRatio: 36 / 21, + + borderRadius: 12, + borderCurve: "continuous", + + shadowColor: "#000", + shadowOpacity: 0.2, + shadowRadius: 6, + shadowOffset: { + width: 0, + height: 4, + }, + }, + + image: { + position: "absolute", + top: 0, + left: 0, + width: "100%", + height: "100%", + zIndex: -1, + + borderRadius: 12, + borderCurve: "continuous", + }, + + cardHeader: { + width: "100%", + padding: 10, + flexDirection: "row", + alignItems: "center", + gap: 10, + }, + + cardHeaderName: { + fontSize: 16, + fontFamily: "semibold", + flex: 1, + }, + + cardHeaderIcon: { + width: 36, + height: 36, + borderRadius: 6, + borderCurve: "continuous", + overflow: "hidden", + }, + + cardBalance: { + alignItems: "flex-end", + gap: 2, + }, + + cardBalanceTitle: { + fontSize: 12, + fontFamily: "semibold", + letterSpacing: 0.5, + textTransform: "uppercase", + }, + + cardBalanceValue: { + fontSize: 18, + fontFamily: "semibold", + letterSpacing: 0.5, + }, + + cardFloatingIdentifier: { + position: "absolute", + bottom: 16, + left: 16, + fontSize: 15, + letterSpacing: 1.5, + fontFamily: "medium", + opacity: 0.5, + }, +}); + +export default MenuCard; \ No newline at end of file diff --git a/src/views/account/Restaurant/Cards/StoreThemes.ts b/src/views/account/Restaurant/Cards/StoreThemes.ts new file mode 100644 index 000000000..f0ac1fc08 --- /dev/null +++ b/src/views/account/Restaurant/Cards/StoreThemes.ts @@ -0,0 +1,23 @@ +export interface StoreTheme { + id: string; + name: string; + colors: { + text: string; + background: string; + accent: string; + }; + background: any; +} + +export const STORE_THEMES = [ + { + id: "izly", + name: "Izly by Crous", + colors: { + text: "#FFFFFF", + background: "#2E174F", + accent: "#DD1314", + }, + background: require("../../../../../assets/images/cards/Carte_Cover_Izly.png"), + }, +]; \ No newline at end of file diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 39264772d..bc36117c4 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -13,17 +13,13 @@ import { useTheme } from "@react-navigation/native"; import { AlertTriangle, ChefHat, - Clock2, CookingPot, MapPin, - QrCode, Sprout, Utensils, } from "lucide-react-native"; import type { Screen } from "@/router/helpers/types"; -import RestaurantCard from "@/components/Restaurant/RestaurantCard"; -import { HorizontalList, Item } from "@/components/Restaurant/ButtonList"; import { NativeItem, NativeList, @@ -32,14 +28,12 @@ import { } from "@/components/Global/NativeComponents"; import { useCurrentAccount } from "@/stores/account"; import TabAnimatedTitle from "@/components/Global/TabAnimatedTitle"; -import { Balance } from "@/services/shared/Balance"; import { balanceFromExternal } from "@/services/balance"; import MissingItem from "@/components/Global/MissingItem"; import { animPapillon } from "@/utils/ui/animations"; -import Reanimated, { FadeIn, FadeInDown, FadeInUp, FadeOut, FadeOutDown, LinearTransition, ZoomIn, ZoomOut } from "react-native-reanimated"; +import Reanimated, { FadeIn, FadeInDown, FadeOut, LinearTransition, ZoomIn, ZoomOut } from "react-native-reanimated"; import { reservationHistoryFromExternal } from "@/services/reservation-history"; import { qrcodeFromExternal } from "@/services/qrcode"; -import { ReservationHistory } from "@/services/shared/ReservationHistory"; import { getMenu } from "@/services/menu"; import type { FoodAllergen, FoodLabel, Menu as PawnoteMenu } from "pawnote"; import { PapillonHeaderSelector } from "@/components/Global/PapillonModernHeader"; @@ -47,7 +41,6 @@ import AnimatedNumber from "@/components/Global/AnimatedNumber"; import { LessonsDateModal } from "../Lessons/LessonsHeader"; import { BookingTerminal, BookingDay } from "@/services/shared/Booking"; import { bookDayFromExternal, getBookingsAvailableFromExternal } from "@/services/booking"; -import AccountButton from "@/components/Restaurant/AccountButton"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; import PapillonHeader from "@/components/Global/PapillonHeader"; import { PressableScale } from "react-native-pressable-scale"; @@ -55,6 +48,20 @@ import { BlurView } from "expo-blur"; import { ChevronLeft, ChevronRight} from "lucide-react-native"; import DrawableImportRestaurant from "@/components/Drawables/DrawableImportRestaurant"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; +import { AccountService } from "@/stores/account/types"; +import { Balance } from "@/services/shared/Balance"; +import { ReservationHistory } from "@/services/shared/ReservationHistory"; +import { STORE_THEMES, StoreTheme } from "./Cards/StoreThemes"; +import MenuCard from "./Cards/Card"; + +export interface ServiceCard { + service: string | AccountService; + identifier: string; + balance: never[] | Balance[]; + history: never[] | ReservationHistory[]; + cardnumber: string | Blob | null; + theme: StoreTheme; +} const Menu: Screen<"Menu"> = ({ route, navigation }) => { const theme = useTheme(); @@ -68,9 +75,6 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { const currentDate = new Date(); - const [allBalances, setAllBalances] = useState(null); - const [allHistories, setAllHistories] = useState(null); - const [allQRCodes, setAllQRCodes] = useState | null>(null); const [allBookings, setAllBookings] = useState(null); const [currentMenu, setCurrentMenu] = useState(null); const [currentWeek, setCurrentWeek] = useState(0); @@ -79,6 +83,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { const [isMenuLoading, setMenuLoading] = useState(false); const [isInitialised, setIsInitialised] = useState(false); const [selectedIndex, setSelectedIndex] = useState(0); + const [allCards, setAllCards] = useState | null>(null); const refreshData = async () => { setIsRefreshing(true); @@ -170,9 +175,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { useEffect(() => { (async () => { try { - const newBalances: Balance[] = []; - const newHistories: ReservationHistory[] = []; - const newQRCodes: Array = []; + const newCards: Array = []; const newBookings: BookingTerminal[] = []; const dailyMenu = account ? await getMenu(account, pickerDate).catch(() => null) : null; @@ -197,11 +200,18 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { }) ]); - newBalances.push(...balance); - newHistories.push(...history); newBookings.push(...booking); - if (cardnumber) newQRCodes.push(cardnumber); + const newCard: ServiceCard = { + service: account.service, + identifier: account.username, + balance: balance, + history: history, + cardnumber: cardnumber, + theme: STORE_THEMES.find((theme) => theme.id === account.service) ?? STORE_THEMES[0], + }; + + newCards.push(newCard); } catch (error) { setIsInitialised(true); console.warn(`An error occurred with account ${account}:`, error); @@ -209,9 +219,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { }); await Promise.all(accountPromises); - setAllBalances(newBalances.sort((a, b) => a.label.toLowerCase() === "cafetaria" ? 1 : -1)); - setAllHistories(newHistories); - setAllQRCodes(newQRCodes); + setAllCards(newCards); setAllBookings(newBookings); setCurrentMenu(dailyMenu); setIsInitialised(true); @@ -225,14 +233,14 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { const fetchQRCode = async () => { if (linkedAccounts) { const qrCodes = await Promise.all(linkedAccounts.map(qrcodeFromExternal)); - setAllQRCodes(qrCodes.filter((code) => code !== null) as string[]); + // setAllQRCodes(qrCodes.filter((code) => code !== null) as string[]); } }; useEffect(() => { // force la regénération du QR code à chaque fois que l'écran est affiché - const unsub = navigation.addListener("focus", fetchQRCode); - return unsub; + // const unsub = navigation.addListener("focus", fetchQRCode); + // return unsub; }, []); const getLabelIcon = (label: string) => { @@ -322,42 +330,8 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { ) : ( <> - {allBalances?.length === 0 ? ( - - ) : ( - <> - - {allBalances!.length > 1 && allBalances?.map((account, index) => ( - setSelectedIndex(index)} - colors={colors} - /> - ))} - - {selectedIndex !== null && allBalances?.[selectedIndex] && ( - - - - )} - - )} - - {allBalances?.length === 0 && !currentMenu && allHistories?.length === 0 && allQRCodes?.length === 0 && allBookings?.length === 0 && ( + {allCards?.length === 0 && !currentMenu && ( = ({ route, navigation }) => { /> )} - {((allHistories?.length !== 0) || (allQRCodes?.length !== 0)) && ( - - } - onPress={() => navigation.navigate("RestaurantHistory", { histories: allHistories ?? [] })} - enable={allHistories?.length !== 0} - /> - } - onPress={() => navigation.navigate("RestaurantQrCode", { QrCodes: allQRCodes ?? [] })} - enable={allQRCodes?.length !== 0} - /> - - )} + + + + {allCards?.map((card, index) => ( + + ))} + {(currentMenu || (allBookings && allBookings?.some((terminal) => terminal.days.some((day) => day.date?.toDateString() === pickerDate.toDateString())))) && @@ -496,7 +468,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { trailing={ balance.remaining === 0)} + disabled={!bookingDay.canBook} onValueChange={() => handleBookTogglePress(terminal, bookingDay)} /> } @@ -601,7 +573,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { )} : <> - {allBalances?.length > 0 && ( + {allCards?.length > 0 && ( Date: Thu, 6 Feb 2025 22:44:11 +0100 Subject: [PATCH 0520/1144] fix(Menu): Remplacer BlurView par View pour fix le rounded --- src/views/account/Restaurant/Menu.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index bc36117c4..4df0878af 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -44,7 +44,6 @@ import { bookDayFromExternal, getBookingsAvailableFromExternal } from "@/service import InsetsBottomView from "@/components/Global/InsetsBottomView"; import PapillonHeader from "@/components/Global/PapillonHeader"; import { PressableScale } from "react-native-pressable-scale"; -import { BlurView } from "expo-blur"; import { ChevronLeft, ChevronRight} from "lucide-react-native"; import DrawableImportRestaurant from "@/components/Drawables/DrawableImportRestaurant"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; @@ -389,20 +388,19 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { }} activeScale={0.8} > - - + setShowDatePicker(true)}> @@ -434,7 +432,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { }} activeScale={0.8} > - = ({ route, navigation }) => { color={theme.colors.text} strokeWidth={2.5} /> - + From ed36d65ff532ed78d431aa066f81c9faec2d4fa6 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Fri, 7 Feb 2025 00:27:28 +0100 Subject: [PATCH 0521/1144] =?UTF-8?q?feat(Restaurant):=20Ajout=20du=20th?= =?UTF-8?q?=C3=A8me=20TurboSelf=20et=20mise=20=C3=A0=20jour=20des=20images?= =?UTF-8?q?=20de=20profil=20pour=20plusieurs=20services?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/helpers/types.ts | 24 +- src/router/screens/views/index.ts | 12 + src/utils/ui/default-profile-picture.ts | 6 + src/views/account/Restaurant/Cards/Card.tsx | 21 +- .../account/Restaurant/Cards/StoreThemes.ts | 10 + src/views/account/Restaurant/Menu.tsx | 51 +++- .../account/Restaurant/Modals/CardDetail.tsx | 239 ++++++++++++++++++ 7 files changed, 341 insertions(+), 22 deletions(-) create mode 100644 src/views/account/Restaurant/Modals/CardDetail.tsx diff --git a/src/router/helpers/types.ts b/src/router/helpers/types.ts index 3a9e3f2cf..ef2091424 100644 --- a/src/router/helpers/types.ts +++ b/src/router/helpers/types.ts @@ -14,6 +14,7 @@ import {Client} from "pawrd"; import { Host } from "turboself-api"; import {Evaluation} from "@/services/shared/Evaluation"; import { ThemesMeta } from "@/utils/chat/themes/Themes.types"; +import { ServiceCard } from "@/views/account/Restaurant/Menu"; export type RouteParameters = { // welcome.index @@ -138,16 +139,27 @@ export type RouteParameters = { Menu?: undefined; RestaurantQrCode: { - QrCodes: Array; + QrCodes: Array; }; RestaurantHistory: { histories: ReservationHistory[]; }; + RestaurantCardDetail: { + card: ServiceCard; + }; Discussions: undefined; ChatCreate: undefined; - ChatDetails: { handle: Chat, recipients: ChatRecipient[], onThemeChange?: (selectedThemePath: ThemesMeta) => void }; - ChatThemes: { handle: Chat; themes: ThemesMeta[]; onGoBack?: (selectedThemePath: ThemesMeta) => void }; + ChatDetails: { + handle: Chat; + recipients: ChatRecipient[]; + onThemeChange?: (selectedThemePath: ThemesMeta) => void; + }; + ChatThemes: { + handle: Chat; + themes: ThemesMeta[]; + onGoBack?: (selectedThemePath: ThemesMeta) => void; + }; Chat: { handle: Chat }; AccountStack: { onboard: boolean }; @@ -163,7 +175,11 @@ export type RouteParameters = { PriceDetectionOnboarding: { accountID: string }; PriceBeforeScan: { accountID: string }; PriceAfterScan: { accountID: string }; - TurboselfAccountSelector: { accounts: Array, username: string, password: string}; + TurboselfAccountSelector: { + accounts: Array; + username: string; + password: string; + }; AddonSettingsPage: { addon: AddonPlacementManifest; diff --git a/src/router/screens/views/index.ts b/src/router/screens/views/index.ts index ffb39c703..5614a8944 100644 --- a/src/router/screens/views/index.ts +++ b/src/router/screens/views/index.ts @@ -20,6 +20,7 @@ import EvaluationDocument from "@/views/account/Evaluation/Document"; import BackgroundIdentityProvider from "@/views/login/IdentityProvider/BackgroundIdentityProvider"; import ChatDetails from "@/views/account/Chat/Modals/ChatDetails"; import ChatThemes from "@/views/account/Chat/Modals/ChatThemes"; +import RestaurantCardDetail from "@/views/account/Restaurant/Modals/CardDetail"; export default [ createScreen("GradeReaction", GradeReaction, { @@ -37,6 +38,17 @@ export default [ headerShown: true, presentation: "modal", }), + createScreen("RestaurantCardDetail", RestaurantCardDetail, { + headerTitle: "Détail de la carte", + presentation: "formSheet", + stackPresentation: "formSheet", + headerShown: false, + sheetCornerRadius: 16, + sheetGrabberVisible: true, + sheetExpandsWhenScrolledToEdge: true, + sheetInitialDetent: 0, + sheetAllowedDetents: "all" + }), createScreen("SettingsTabs", SettingsTabs, { headerTitle: "Onglets et navigation", }), diff --git a/src/utils/ui/default-profile-picture.ts b/src/utils/ui/default-profile-picture.ts index 03891dfa5..b4a640d4e 100644 --- a/src/utils/ui/default-profile-picture.ts +++ b/src/utils/ui/default-profile-picture.ts @@ -25,6 +25,12 @@ export const defaultProfilePicture = (service: AccountService, accountProvider?: return require("../../../assets/images/service_unknown.png"); case AccountService.Izly: return require("../../../assets/images/service_izly.png"); + case AccountService.Turboself: + return require("../../../assets/images/service_turboself.png"); + case AccountService.ARD: + return require("../../../assets/images/service_ard.png"); + case AccountService.Alise: + return require("../../../assets/images/service_alise.jpg"); } return require("../../../assets/images/service_unknown.png"); diff --git a/src/views/account/Restaurant/Cards/Card.tsx b/src/views/account/Restaurant/Cards/Card.tsx index 27938e50f..9d6f0be8e 100644 --- a/src/views/account/Restaurant/Cards/Card.tsx +++ b/src/views/account/Restaurant/Cards/Card.tsx @@ -4,7 +4,7 @@ import { useTheme } from "@react-navigation/native"; import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; import { PressableScale } from "react-native-pressable-scale"; -const MenuCard = ({ card }: { card: ServiceCard }) => { +const MenuCard = ({ card, onPress }: { card: ServiceCard, onPress?: () => void }) => { console.log(card); const theme = useTheme(); @@ -12,6 +12,7 @@ const MenuCard = ({ card }: { card: ServiceCard }) => { { {card?.theme?.name} - - - Solde de cantine - - - {card.balance[0].amount.toFixed(2)} € - - + {card.balance[0] && card.balance[0].amount && ( + + + Solde de cantine + + + {card.balance[0] ? card.balance[0].amount.toFixed(2) + " €" : "---"} + + + )} {card.identifier && ( diff --git a/src/views/account/Restaurant/Cards/StoreThemes.ts b/src/views/account/Restaurant/Cards/StoreThemes.ts index f0ac1fc08..16e178cb6 100644 --- a/src/views/account/Restaurant/Cards/StoreThemes.ts +++ b/src/views/account/Restaurant/Cards/StoreThemes.ts @@ -20,4 +20,14 @@ export const STORE_THEMES = [ }, background: require("../../../../../assets/images/cards/Carte_Cover_Izly.png"), }, + { + id: "turboself", + name: "TurboSelf", + colors: { + text: "#FFFFFF", + background: "#840016", + accent: "#DD1314", + }, + background: require("../../../../../assets/images/cards/Carte_Cover_Turboself.png"), + }, ]; \ No newline at end of file diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index bc36117c4..1f3b3d1b8 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -7,7 +7,8 @@ import { Alert, ActivityIndicator, RefreshControl, - Text + Text, + Dimensions } from "react-native"; import { useTheme } from "@react-navigation/native"; import { @@ -175,7 +176,18 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { useEffect(() => { (async () => { try { - const newCards: Array = []; + const newCards: Array = [ + { + service: 5, + identifier: "123456789", + balance: [{ + amount: 20.30 + }], + history: [], + cardnumber: null, + theme: STORE_THEMES[1], + } + ]; const newBookings: BookingTerminal[] = []; const dailyMenu = account ? await getMenu(account, pickerDate).catch(() => null) : null; @@ -362,18 +374,39 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { - {allCards?.map((card, index) => ( - + + { + navigation.navigate("RestaurantCardDetail", { card }); + }} + /> + ))} - + {(currentMenu || (allBookings && allBookings?.some((terminal) => terminal.days.some((day) => day.date?.toDateString() === pickerDate.toDateString())))) && diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx new file mode 100644 index 000000000..f08fc59cf --- /dev/null +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -0,0 +1,239 @@ +import { Image, ScrollView, Text, View } from "react-native"; +import MenuCard from "../Cards/Card"; +import Reanimated from "react-native-reanimated"; +import React from "react"; +import { LinearGradient } from "expo-linear-gradient"; +import { NativeItem, NativeList, NativeText } from "@/components/Global/NativeComponents"; + +import { formatDistance } from "date-fns"; +import { fr } from "date-fns/locale"; +import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; +import { useTheme } from "@react-navigation/native"; +import InsetsBottomView from "@/components/Global/InsetsBottomView"; +import { PressableScale } from "react-native-pressable-scale"; +import { useCurrentAccount } from "@/stores/account"; +import { AccountService } from "@/stores/account/types"; +import { QrCode } from "lucide-react-native"; + +const formatCardIdentifier = (identifier) => { + const visiblePart = identifier.slice(-6); + const maskedPart = identifier.slice(0, -6).replace(/./g, "•"); + return maskedPart + " " + visiblePart.match(/.{1,4}/g).join(" "); +}; + +const RestaurantCardDetail = ({ route, navigation }) => { + const { card } = route.params; + const theme = useTheme(); + + const account = useCurrentAccount((store) => store.account); + + return ( + <> + + + + navigation.goBack()} + weight="light" + activeScale={0.95} + > + + + + + + + + Carte {AccountService[card.service]} {account?.identity?.firstName ? "de " + account.identity.firstName : ""} + + + {formatCardIdentifier(card.identifier)} + + + + + {card.balance[0] && ( + + + + Solde de cantine + + 0 ? "#00C853" : "#FF1744", + }} + > + {card.balance[0].amount > 0 && "+"}{card.balance[0].amount.toFixed(2)} € + + + + )} + + {card.cardnumber && ( + navigation.navigate("RestaurantQrCode", { card: card.cardnumber })} + weight="light" + activeScale={0.95} + > + + + + + QR-code + + + + + )} + + + {card.history.length > 0 && ( + + {card.history.sort((a, b) => b.timestamp - a.timestamp).map((history, i) => ( + + } + trailing={ + + {history.amount > 0 ? ( + + +{history.amount.toFixed(2)} € + + ) : ( + + -{(-history.amount).toFixed(2)} € + + )} + + } + > + + {history.label} + + + il y a {formatDistance(new Date(history.timestamp), new Date(), { locale: fr })} + + + ))} + + )} + + + + + ); +}; + +export default RestaurantCardDetail; \ No newline at end of file From 77e0b4b2dfeac1baebcd34135598ef0d3ea58b2d Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Fri, 7 Feb 2025 00:33:35 +0100 Subject: [PATCH 0522/1144] =?UTF-8?q?fix(Restaurant):=20Simplification=20d?= =?UTF-8?q?es=20libell=C3=A9s=20de=20solde=20dans=20les=20cartes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Restaurant/RestaurantCard.tsx | 2 +- src/views/account/Restaurant/Cards/Card.tsx | 5 +++-- src/views/account/Restaurant/Modals/CardDetail.tsx | 3 +-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/Restaurant/RestaurantCard.tsx b/src/components/Restaurant/RestaurantCard.tsx index f842a6993..213e991db 100644 --- a/src/components/Restaurant/RestaurantCard.tsx +++ b/src/components/Restaurant/RestaurantCard.tsx @@ -35,7 +35,7 @@ const RestaurantCard: React.FC = ({ solde, repas }) => { opacity: 0.5, }} > - Solde de cantine + Solde void } borderColor: theme.colors.text + "33", borderWidth: 1, backgroundColor: card?.theme?.colors?.background, + shadowColor: card?.theme?.colors?.background } ]} > @@ -42,7 +43,7 @@ const MenuCard = ({ card, onPress }: { card: ServiceCard, onPress?: () => void } {card.balance[0] && card.balance[0].amount && ( - Solde de cantine + Solde {card.balance[0] ? card.balance[0].amount.toFixed(2) + " €" : "---"} @@ -82,7 +83,7 @@ const styles = StyleSheet.create({ borderCurve: "continuous", shadowColor: "#000", - shadowOpacity: 0.2, + shadowOpacity: 0.4, shadowRadius: 6, shadowOffset: { width: 0, diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index f08fc59cf..b5ba401f6 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -50,7 +50,6 @@ const RestaurantCardDetail = ({ route, navigation }) => { }} > navigation.goBack()} weight="light" activeScale={0.95} > @@ -117,7 +116,7 @@ const RestaurantCardDetail = ({ route, navigation }) => { textAlign: "center", }} > - Solde de cantine + Solde de la carte Date: Fri, 7 Feb 2025 00:36:40 +0100 Subject: [PATCH 0523/1144] =?UTF-8?q?fix(Restaurant):=20Ajustement=20de=20?= =?UTF-8?q?la=20hauteur=20et=20de=20l'opacit=C3=A9=20du=20d=C3=A9grad?= =?UTF-8?q?=C3=A9=20dans=20CardDetail?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Restaurant/Modals/CardDetail.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index b5ba401f6..d62415945 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -30,14 +30,14 @@ const RestaurantCardDetail = ({ route, navigation }) => { return ( <> From 3c3dfd430dfad6f8a5ab7b88eed36db8c5392c06 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Fri, 7 Feb 2025 00:53:11 +0100 Subject: [PATCH 0524/1144] =?UTF-8?q?fix(Router):=20Ajout=20d'un=20contene?= =?UTF-8?q?ur=20View=20pour=20g=C3=A9rer=20le=20fond=20et=20am=C3=A9liorer?= =?UTF-8?q?=20la=20structure=20du=20routage=20fix(Card):=20Suppression=20d?= =?UTF-8?q?u=20log=20console=20pour=20nettoyer=20le=20code=20fix(Menu):=20?= =?UTF-8?q?Activation=20du=20d=C3=A9filement=20horizontal=20conditionnel?= =?UTF-8?q?=20pour=20les=20cartes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/index.tsx | 16 +++++++++------- src/views/account/Restaurant/Cards/Card.tsx | 1 - src/views/account/Restaurant/Menu.tsx | 1 + 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/router/index.tsx b/src/router/index.tsx index 2253da5b7..8f0f55b65 100644 --- a/src/router/index.tsx +++ b/src/router/index.tsx @@ -117,14 +117,16 @@ const Router: React.FC = () => { navigate(str); }} > - - - {screens.map((screen) => ( + + + + {screens.map((screen) => ( // @ts-expect-error : type not compatible, but it works fine. - - ))} - - + + ))} + + + diff --git a/src/views/account/Restaurant/Cards/Card.tsx b/src/views/account/Restaurant/Cards/Card.tsx index a096204ec..0a482208b 100644 --- a/src/views/account/Restaurant/Cards/Card.tsx +++ b/src/views/account/Restaurant/Cards/Card.tsx @@ -5,7 +5,6 @@ import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; import { PressableScale } from "react-native-pressable-scale"; const MenuCard = ({ card, onPress }: { card: ServiceCard, onPress?: () => void }) => { - console.log(card); const theme = useTheme(); return ( diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 6b47be57c..6a870a2d9 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -385,6 +385,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { }} horizontal showsHorizontalScrollIndicator={false} + scrollEnabled={allCards?.length > 1} decelerationRate={"fast"} snapToInterval={(Dimensions.get("window").width - 32) + 6} > From a66343a8cb62c876cb71dc16db973c81472570fb Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Fri, 7 Feb 2025 02:17:54 +0100 Subject: [PATCH 0525/1144] =?UTF-8?q?feat(Restaurant):=20Ajout=20de=20l'?= =?UTF-8?q?=C3=A9cran=20de=20succ=C3=A8s=20de=20paiement=20et=20mise=20?= =?UTF-8?q?=C3=A0=20jour=20des=20types=20de=20routage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/helpers/types.ts | 3 + src/router/screens/views/index.ts | 12 +- src/services/izly/balance.ts | 2 - src/views/account/Restaurant/Menu.tsx | 24 +- .../account/Restaurant/Modals/CardDetail.tsx | 405 +++++++++------- .../Restaurant/Modals/PaymentSuccess.tsx | 175 +++++++ .../account/Restaurant/Modals/QrCode.old.tsx | 289 ++++++++++++ .../account/Restaurant/Modals/QrCode.tsx | 439 +++++++----------- 8 files changed, 885 insertions(+), 464 deletions(-) create mode 100644 src/views/account/Restaurant/Modals/PaymentSuccess.tsx create mode 100644 src/views/account/Restaurant/Modals/QrCode.old.tsx diff --git a/src/router/helpers/types.ts b/src/router/helpers/types.ts index ef2091424..4adfee299 100644 --- a/src/router/helpers/types.ts +++ b/src/router/helpers/types.ts @@ -147,6 +147,9 @@ export type RouteParameters = { RestaurantCardDetail: { card: ServiceCard; }; + RestaurantPaymentSuccess;: { + card: ServiceCard; + }; Discussions: undefined; ChatCreate: undefined; diff --git a/src/router/screens/views/index.ts b/src/router/screens/views/index.ts index 5614a8944..a8190737e 100644 --- a/src/router/screens/views/index.ts +++ b/src/router/screens/views/index.ts @@ -7,7 +7,6 @@ import AddonLogs from "@/views/addon/AddonLogs"; import AddonPage from "@/views/addon/AddonPage"; import GradeSubjectScreen from "@/views/account/Grades/Modals/Subject"; import GradeDocument from "@/views/account/Grades/Document"; -import RestaurantHistory from "@/views/account/Restaurant/Modals/History"; import ChatCreate from "@/views/account/Chat/Modals/ChatCreate"; import Chat from "@/views/account/Chat/Modals/Chat"; import HomeworksDocument from "@/views/account/Homeworks/Document"; @@ -21,6 +20,7 @@ import BackgroundIdentityProvider from "@/views/login/IdentityProvider/Backgroun import ChatDetails from "@/views/account/Chat/Modals/ChatDetails"; import ChatThemes from "@/views/account/Chat/Modals/ChatThemes"; import RestaurantCardDetail from "@/views/account/Restaurant/Modals/CardDetail"; +import RestaurantPaymentSuccess from "@/views/account/Restaurant/Modals/PaymentSuccess"; export default [ createScreen("GradeReaction", GradeReaction, { @@ -31,10 +31,12 @@ export default [ createScreen("RestaurantQrCode", RestaurantQrCode, { headerTitle: "", headerTransparent: true, - presentation: "fullScreenModal", + presentation: "transparentModal", + animation: "fade", + animationDuration: 100, }), - createScreen("RestaurantHistory", RestaurantHistory, { - headerTitle: "Historique des réservations", + createScreen("RestaurantPaymentSuccess", RestaurantPaymentSuccess, { + headerTitle: "Paiement effectué", headerShown: true, presentation: "modal", }), @@ -47,7 +49,7 @@ export default [ sheetGrabberVisible: true, sheetExpandsWhenScrolledToEdge: true, sheetInitialDetent: 0, - sheetAllowedDetents: "all" + sheetAllowedDetents: "all", }), createScreen("SettingsTabs", SettingsTabs, { headerTitle: "Onglets et navigation", diff --git a/src/services/izly/balance.ts b/src/services/izly/balance.ts index 8218367f2..3f4978262 100644 --- a/src/services/izly/balance.ts +++ b/src/services/izly/balance.ts @@ -8,8 +8,6 @@ export const balance = async (account: IzlyAccount): Promise => { const balance = await ezly.balance(account.instance); const currency = account.authentication.configuration.currency; - console.log(account.authentication); - return [{ amount: balance.value, currency: currency, diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 6a870a2d9..61e42a4af 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -48,7 +48,7 @@ import { PressableScale } from "react-native-pressable-scale"; import { ChevronLeft, ChevronRight} from "lucide-react-native"; import DrawableImportRestaurant from "@/components/Drawables/DrawableImportRestaurant"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; -import { AccountService } from "@/stores/account/types"; +import { Account, AccountService } from "@/stores/account/types"; import { Balance } from "@/services/shared/Balance"; import { ReservationHistory } from "@/services/shared/ReservationHistory"; import { STORE_THEMES, StoreTheme } from "./Cards/StoreThemes"; @@ -56,6 +56,7 @@ import MenuCard from "./Cards/Card"; export interface ServiceCard { service: string | AccountService; + account: Account | null; identifier: string; balance: never[] | Balance[]; history: never[] | ReservationHistory[]; @@ -172,12 +173,13 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { }); }, [navigation, route.params, theme.colors.text]); - useEffect(() => { + const fetchCardsData = async () => { (async () => { try { const newCards: Array = [ - { + /* { service: 5, + account: null, identifier: "123456789", balance: [{ amount: 20.30 @@ -185,7 +187,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { history: [], cardnumber: null, theme: STORE_THEMES[1], - } + } */ ]; const newBookings: BookingTerminal[] = []; @@ -216,6 +218,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { const newCard: ServiceCard = { service: account.service, identifier: account.username, + account: account, balance: balance, history: history, cardnumber: cardnumber, @@ -239,20 +242,11 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { console.warn("An error occurred while fetching data:", error); } })(); - }, [linkedAccounts, refreshCount]); - - const fetchQRCode = async () => { - if (linkedAccounts) { - const qrCodes = await Promise.all(linkedAccounts.map(qrcodeFromExternal)); - // setAllQRCodes(qrCodes.filter((code) => code !== null) as string[]); - } }; useEffect(() => { - // force la regénération du QR code à chaque fois que l'écran est affiché - // const unsub = navigation.addListener("focus", fetchQRCode); - // return unsub; - }, []); + fetchCardsData(); + }, [linkedAccounts, refreshCount]); const getLabelIcon = (label: string) => { switch (label) { diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index d62415945..0b0d5461c 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -1,7 +1,7 @@ import { Image, ScrollView, Text, View } from "react-native"; import MenuCard from "../Cards/Card"; import Reanimated from "react-native-reanimated"; -import React from "react"; +import React, { useState } from "react"; import { LinearGradient } from "expo-linear-gradient"; import { NativeItem, NativeList, NativeText } from "@/components/Global/NativeComponents"; @@ -14,6 +14,8 @@ import { PressableScale } from "react-native-pressable-scale"; import { useCurrentAccount } from "@/stores/account"; import { AccountService } from "@/stores/account/types"; import { QrCode } from "lucide-react-native"; +import { balanceFromExternal } from "@/services/balance"; +import { reservationHistoryFromExternal } from "@/services/reservation-history"; const formatCardIdentifier = (identifier) => { const visiblePart = identifier.slice(-6); @@ -22,217 +24,262 @@ const formatCardIdentifier = (identifier) => { }; const RestaurantCardDetail = ({ route, navigation }) => { - const { card } = route.params; - const theme = useTheme(); - - const account = useCurrentAccount((store) => store.account); - - return ( - <> - - - - - - - - + try { + const { card } = route.params; + const [cardData, setCardData] = useState(null); + + const theme = useTheme(); + + const account = useCurrentAccount((store) => store.account); + + const updateCardData = async () => { + const [balance, history] = await Promise.all([ + balanceFromExternal(route.params.card.account).catch(err => { + console.warn(`Error fetching balance for account ${account}:`, err); + return []; + }), + reservationHistoryFromExternal(route.params.card.account).catch(err => { + console.warn(`Error fetching history for account ${account}:`, err); + return []; + }) + ]); - { + // on focus + const unsubscribe = navigation.addListener("focus", () => { + updateCardData(); + }); + + return unsubscribe; + }, []); + + return ( + <> + + + - - Carte {AccountService[card.service]} {account?.identity?.firstName ? "de " + account.identity.firstName : ""} - - + + + + + - {formatCardIdentifier(card.identifier)} - - - - - {card.balance[0] && ( - - + - - Solde de la carte - - 0 ? "#00C853" : "#FF1744", - }} - > - {card.balance[0].amount > 0 && "+"}{card.balance[0].amount.toFixed(2)} € - - - - )} - - {card.cardnumber && ( - navigation.navigate("RestaurantQrCode", { card: card.cardnumber })} - weight="light" - activeScale={0.95} + Carte {AccountService[route.params.card.service]} {account?.identity?.firstName ? "de " + account.identity.firstName : ""} + + + - + {formatCardIdentifier(route.params.card.identifier)} + + + + + {card?.balance[0] && ( + - + + Solde de la carte + 0 ? "#00C853" : "#FF1744", }} > - QR-code + {card.balance[0].amount > 0 && "+"}{card.balance[0].amount.toFixed(2)} € - - )} - - - {card.history.length > 0 && ( - - {card.history.sort((a, b) => b.timestamp - a.timestamp).map((history, i) => ( - - } - trailing={ + )} + + {card?.cardnumber && ( + navigation.navigate("RestaurantQrCode", { card: card })} + weight="light" + activeScale={0.95} + > + - {history.amount > 0 ? ( - - +{history.amount.toFixed(2)} € - - ) : ( - - -{(-history.amount).toFixed(2)} € - - )} + + + QR-code + - } - > - - {history.label} - - - il y a {formatDistance(new Date(history.timestamp), new Date(), { locale: fr })} - - - ))} - - )} - - - - - ); + + + )} + + + {card?.history.length > 0 && ( + + {card.history.sort((a, b) => b.timestamp - a.timestamp).map((history, i) => ( + + } + trailing={ + + {history.amount > 0 ? ( + + +{history.amount.toFixed(2)} € + + ) : ( + + -{(-history.amount).toFixed(2)} € + + )} + + } + > + + {history.label} + + + il y a {formatDistance(new Date(history.timestamp), new Date(), { locale: fr })} + + + ))} + + )} + + + + + ); + } + catch (e) { + console.log(e); + return ; + } }; export default RestaurantCardDetail; \ No newline at end of file diff --git a/src/views/account/Restaurant/Modals/PaymentSuccess.tsx b/src/views/account/Restaurant/Modals/PaymentSuccess.tsx new file mode 100644 index 000000000..0a1fa5b10 --- /dev/null +++ b/src/views/account/Restaurant/Modals/PaymentSuccess.tsx @@ -0,0 +1,175 @@ +import { NativeItem, NativeList, NativeText } from "@/components/Global/NativeComponents"; +import PapillonSpinner from "@/components/Global/PapillonSpinner"; +import { reservationHistoryFromExternal } from "@/services/reservation-history"; +import { anim2Papillon } from "@/utils/ui/animations"; +import { useTheme } from "@react-navigation/native"; +import { AlertCircle, Check } from "lucide-react-native"; +import { useEffect, useState } from "react"; +import { View, Text } from "react-native"; +import { FadeInDown, FadeOutUp } from "react-native-reanimated"; + +const RestaurantPaymentSuccess = ({ route, navigation }) => { + const { card, diff } = route.params; + const theme = useTheme(); + + const [lastPayment, setLastPayment] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + reservationHistoryFromExternal(card.account).then((history) => { + setLastPayment(history[0]); + setLoading(false); + }); + }, []); + + return ( + + {loading && ( + + + } + > + + Vérification de la transaction de {(-diff).toFixed(2)} € avec votre historique de paiements + + + + )} + + {lastPayment && ( + <> + + + + -{(-lastPayment.amount).toFixed(2)} € + + + {new Date(lastPayment.timestamp).toLocaleString("fr-FR", { weekday: "long", day: "numeric", month: "long", year: "numeric", hour: "numeric", minute: "numeric" })} + + + + + {diff == lastPayment.amount ? ( + + + + + } + > + + Transaction valide + + + Comparaison de solde effectuée et validée par Papillon + + + + ) : ( + + + + + } + > + + Impossible de vérifier la transaction + + + Le paiement et le solde de votre compte ne correspondent pas + + + + )} + + )} + + ); +}; + +export default RestaurantPaymentSuccess; \ No newline at end of file diff --git a/src/views/account/Restaurant/Modals/QrCode.old.tsx b/src/views/account/Restaurant/Modals/QrCode.old.tsx new file mode 100644 index 000000000..cb01db582 --- /dev/null +++ b/src/views/account/Restaurant/Modals/QrCode.old.tsx @@ -0,0 +1,289 @@ +import React, { useEffect, useLayoutEffect, useState } from "react"; +import { + View, + Text, + StatusBar, + TouchableOpacity, + StyleSheet, + ScrollView, Platform, + AppState, + Image +} from "react-native"; +import { DeviceMotion } from "expo-sensors"; +import { SafeAreaView } from "react-native-safe-area-context"; +import QRCode from "react-native-qrcode-svg"; +import * as Brightness from "expo-brightness"; +import Animated, { + useSharedValue, + useAnimatedStyle, + withTiming, + Easing, +} from "react-native-reanimated"; +import { useTheme } from "@react-navigation/native"; +import { X } from "lucide-react-native"; +import ScanIcon from "@/components/Restaurant/ScanIcon"; +import type { Screen } from "@/router/helpers/types"; + +const BETA_THRESHOLD_LOW = -0.2; +const BETA_THRESHOLD_HIGH = -0.15; +const ANIMATION_DURATION = 500; + +const RestaurantQrCode: Screen<"RestaurantQrCode"> = ({ route, navigation }) => { + const [currentState, setCurrentState] = useState< + "neutral" | "tiltedUp" | "tiltedDown" + >("neutral"); + const opacity = useSharedValue(1); + const rotate = useSharedValue(0); + const theme = useTheme(); + const { colors } = theme; + + const qrcodes = route.params.QrCodes; + const [activeIndex, setActiveIndex] = useState(0); + const handleScroll = (event: { nativeEvent: { contentOffset: { x: any; }; }; }) => { + const contentOffsetX = event.nativeEvent.contentOffset.x; + const index = Math.floor(contentOffsetX / (100)); + setActiveIndex(Math.max(0, Math.min(index, qrcodes ? qrcodes.length - 1 : 0))); + }; + + useLayoutEffect(() => { + navigation.setOptions({ + headerRight: () => ( + + + + ), + }); + }, [navigation]); + + const [oldBrightness, setOldBrightness] = useState(0.5); + + useEffect(() => { + let isActive = true; + + const handleAppStateChange = async (nextAppState: string) => { + if (nextAppState === "background" || nextAppState === "inactive") { + if (isActive) { + isActive = false; + await Brightness.setBrightnessAsync(oldBrightness); + } + } else if (nextAppState === "active") { + isActive = true; + await Brightness.setBrightnessAsync(1); + } + }; + + const appStateSubscription = AppState.addEventListener( + "change", + handleAppStateChange + ); + + const navigationSubscription = navigation.addListener("beforeRemove", () => { + Brightness.setBrightnessAsync(oldBrightness); + }); + + (async () => { + if (Platform.OS === "android") { + const { status } = await Brightness.requestPermissionsAsync(); + if (status !== "granted") { + navigation.goBack(); + return; + } + } + try { + const brightness = await Brightness.getBrightnessAsync(); + setOldBrightness(brightness); + await Brightness.setBrightnessAsync(1); + } catch (e) { console.warn("Brightness error:", e); } + })(); + return () => { + appStateSubscription.remove(); + navigationSubscription(); + Brightness.setBrightnessAsync(oldBrightness); + }; + }, [navigation, oldBrightness]); + + + useEffect(() => { + const subscription = DeviceMotion.addListener(({ rotation }) => { + let newState: "neutral" | "tiltedUp" | "tiltedDown" = "neutral"; + + if (!rotation || typeof rotation.beta === "undefined") { + return; + } + + if (rotation.beta < BETA_THRESHOLD_LOW) { + newState = "tiltedDown"; + } else if (rotation.beta > BETA_THRESHOLD_HIGH) { + newState = "tiltedUp"; + } + + if (newState !== currentState) { + setCurrentState(newState); + const finalRotation = newState === "tiltedDown" ? 180 : 0; + + opacity.value = withTiming(0, { + duration: ANIMATION_DURATION / 2, + easing: Easing.out(Easing.ease), + }, () => { + rotate.value = withTiming(finalRotation, { + duration: ANIMATION_DURATION / 2, + easing: Easing.inOut(Easing.ease), + }, () => { + opacity.value = withTiming(1, { + duration: ANIMATION_DURATION / 2, + easing: Easing.in(Easing.ease), + }); + }); + }); + } + }); + + return () => { + subscription.remove(); + }; + }, [currentState, opacity, rotate]); + + const animatedStyle = useAnimatedStyle(() => ({ + opacity: opacity.value, + transform: [{ rotate: `${rotate.value}deg` }], + })); + + return ( + + + + + 1} + onScroll={handleScroll} + > + { qrcodes && qrcodes.map((code, index) => { + if (typeof code === "string") { + return ( + + + + ); + } else if (code instanceof Blob) { + const imageUrl = URL.createObjectURL(code); + + return ( + + + + ); + } + })} + + + { qrcodes && qrcodes.length > 1 && ( + + {qrcodes.map((_, index) => ( + + ))} + + )} + + + + Oriente le code QR vers le scanner de la borne + + + + + ); +}; + +const styles = StyleSheet.create({ + safeArea: { + flex: 1, + }, + headerButton: { + padding: 8, + borderRadius: 50, + margin: 5, + }, + qrCodeContainer: { + alignItems: "center", + justifyContent: "center", + alignContent: "center", + marginTop: 75 + }, + qrCodeInnerContainer: { + flex: 1, + justifyContent: "center", + alignItems: "center", + width: 300, + padding: 15, + borderRadius: 15, + alignSelf: "center", + backgroundColor: "#FFFFFF" + }, + instructionContainer: { + marginTop: 60, + justifyContent: "center", + alignItems: "center", + gap: 10, + }, + instructionText: { + color: "#FFFFFF", + fontSize: 15, + lineHeight: 20, + textAlign: "center", + maxWidth: 200, + fontFamily: "medium", + }, + dotsContainer: { + flexDirection: "row", + justifyContent: "center", + alignItems: "center", + marginTop: 16, + }, + dot: { + width: 7, + height: 7, + borderRadius: 5, + marginHorizontal: 4, + }, + activeDot: { + backgroundColor: "#ffffff", + }, + inactiveDot: { + backgroundColor: "#ffffff25", + }, + barcodeImage: { + width: "100%", + height: 50, + resizeMode: "cover", + }, +}); + +export default RestaurantQrCode; diff --git a/src/views/account/Restaurant/Modals/QrCode.tsx b/src/views/account/Restaurant/Modals/QrCode.tsx index cb01db582..3170831e0 100644 --- a/src/views/account/Restaurant/Modals/QrCode.tsx +++ b/src/views/account/Restaurant/Modals/QrCode.tsx @@ -1,289 +1,202 @@ -import React, { useEffect, useLayoutEffect, useState } from "react"; -import { - View, - Text, - StatusBar, - TouchableOpacity, - StyleSheet, - ScrollView, Platform, - AppState, - Image -} from "react-native"; -import { DeviceMotion } from "expo-sensors"; -import { SafeAreaView } from "react-native-safe-area-context"; -import QRCode from "react-native-qrcode-svg"; -import * as Brightness from "expo-brightness"; -import Animated, { - useSharedValue, - useAnimatedStyle, - withTiming, - Easing, -} from "react-native-reanimated"; +import { balanceFromExternal } from "@/services/balance"; +import { qrcodeFromExternal } from "@/services/qrcode"; import { useTheme } from "@react-navigation/native"; -import { X } from "lucide-react-native"; -import ScanIcon from "@/components/Restaurant/ScanIcon"; -import type { Screen } from "@/router/helpers/types"; - -const BETA_THRESHOLD_LOW = -0.2; -const BETA_THRESHOLD_HIGH = -0.15; -const ANIMATION_DURATION = 500; - -const RestaurantQrCode: Screen<"RestaurantQrCode"> = ({ route, navigation }) => { - const [currentState, setCurrentState] = useState< - "neutral" | "tiltedUp" | "tiltedDown" - >("neutral"); - const opacity = useSharedValue(1); - const rotate = useSharedValue(0); - const theme = useTheme(); - const { colors } = theme; - - const qrcodes = route.params.QrCodes; - const [activeIndex, setActiveIndex] = useState(0); - const handleScroll = (event: { nativeEvent: { contentOffset: { x: any; }; }; }) => { - const contentOffsetX = event.nativeEvent.contentOffset.x; - const index = Math.floor(contentOffsetX / (100)); - setActiveIndex(Math.max(0, Math.min(index, qrcodes ? qrcodes.length - 1 : 0))); - }; - - useLayoutEffect(() => { - navigation.setOptions({ - headerRight: () => ( - - - - ), - }); - }, [navigation]); +import { BlurView } from "expo-blur"; +import { QrCodeIcon, X } from "lucide-react-native"; +import { useEffect, useState } from "react"; +import { View, Text, Pressable } from "react-native"; +import { TouchableOpacity } from "react-native-gesture-handler"; +import { PressableScale } from "react-native-pressable-scale"; +import QRCode from "react-native-qrcode-svg"; +import * as Haptics from "expo-haptics"; - const [oldBrightness, setOldBrightness] = useState(0.5); - useEffect(() => { - let isActive = true; +const RestaurantQrCode = ({ route, navigation }) => { + const { card } = route.params; + const [qrCode, setQrCode] = useState(null); - const handleAppStateChange = async (nextAppState: string) => { - if (nextAppState === "background" || nextAppState === "inactive") { - if (isActive) { - isActive = false; - await Brightness.setBrightnessAsync(oldBrightness); - } - } else if (nextAppState === "active") { - isActive = true; - await Brightness.setBrightnessAsync(1); + const PollingBalance = async () => { + balanceFromExternal(card.account).then((newBalance) => { + if(card.balance[0].amount !== newBalance[0].amount) { + const diff = newBalance[0].amount - card.balance[0].amount; + openFeedback(); } - }; - - const appStateSubscription = AppState.addEventListener( - "change", - handleAppStateChange - ); - - const navigationSubscription = navigation.addListener("beforeRemove", () => { - Brightness.setBrightnessAsync(oldBrightness); }); + }; - (async () => { - if (Platform.OS === "android") { - const { status } = await Brightness.requestPermissionsAsync(); - if (status !== "granted") { - navigation.goBack(); - return; - } - } - try { - const brightness = await Brightness.getBrightnessAsync(); - setOldBrightness(brightness); - await Brightness.setBrightnessAsync(1); - } catch (e) { console.warn("Brightness error:", e); } - })(); - return () => { - appStateSubscription.remove(); - navigationSubscription(); - Brightness.setBrightnessAsync(oldBrightness); - }; - }, [navigation, oldBrightness]); - + const openFeedback = () => { + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); + navigation.goBack(); + setTimeout(() => { + navigation.navigate("RestaurantPaymentSuccess", { card, diff: 0 }); + }, 1000); + }; useEffect(() => { - const subscription = DeviceMotion.addListener(({ rotation }) => { - let newState: "neutral" | "tiltedUp" | "tiltedDown" = "neutral"; - - if (!rotation || typeof rotation.beta === "undefined") { - return; - } - - if (rotation.beta < BETA_THRESHOLD_LOW) { - newState = "tiltedDown"; - } else if (rotation.beta > BETA_THRESHOLD_HIGH) { - newState = "tiltedUp"; - } + // Si Izly + if(card.service === 10) { + const interval = setInterval(() => { + console.log("[CANTINE >> IZLY] Demande du solde"); + PollingBalance(); + }, 1000); + + return () => { + clearInterval(interval); + console.log("[CANTINE >> IZLY] Fin du polling"); + }; + } + }, []); - if (newState !== currentState) { - setCurrentState(newState); - const finalRotation = newState === "tiltedDown" ? 180 : 0; + const theme = useTheme(); - opacity.value = withTiming(0, { - duration: ANIMATION_DURATION / 2, - easing: Easing.out(Easing.ease), - }, () => { - rotate.value = withTiming(finalRotation, { - duration: ANIMATION_DURATION / 2, - easing: Easing.inOut(Easing.ease), - }, () => { - opacity.value = withTiming(1, { - duration: ANIMATION_DURATION / 2, - easing: Easing.in(Easing.ease), - }); - }); - }); - } + const GenerateQRCode = async () => { + qrcodeFromExternal(card.account).then((qrCode) => { + setQrCode(qrCode); }); + }; - return () => { - subscription.remove(); - }; - }, [currentState, opacity, rotate]); - - const animatedStyle = useAnimatedStyle(() => ({ - opacity: opacity.value, - transform: [{ rotate: `${rotate.value}deg` }], - })); + useEffect(() => { + GenerateQRCode(); + }, []); return ( - - + - - - 1} - onScroll={handleScroll} - > - { qrcodes && qrcodes.map((code, index) => { - if (typeof code === "string") { - return ( - - - - ); - } else if (code instanceof Blob) { - const imageUrl = URL.createObjectURL(code); - return ( - - - - ); - } - })} - - - { qrcodes && qrcodes.length > 1 && ( - - {qrcodes.map((_, index) => ( - - ))} + navigation.goBack()} + > + + + + + Approche le code QR du scanner de la borne afin de valider ta carte + + + + {qrCode && ( + { + GenerateQRCode(); + }} + weight="light" + activeScale={0.9} + > + + )} - - - - Oriente le code QR vers le scanner de la borne - - - + + navigation.goBack()} + > + navigation.goBack()} + > + + + + Fermer + + + + + ); }; -const styles = StyleSheet.create({ - safeArea: { - flex: 1, - }, - headerButton: { - padding: 8, - borderRadius: 50, - margin: 5, - }, - qrCodeContainer: { - alignItems: "center", - justifyContent: "center", - alignContent: "center", - marginTop: 75 - }, - qrCodeInnerContainer: { - flex: 1, - justifyContent: "center", - alignItems: "center", - width: 300, - padding: 15, - borderRadius: 15, - alignSelf: "center", - backgroundColor: "#FFFFFF" - }, - instructionContainer: { - marginTop: 60, - justifyContent: "center", - alignItems: "center", - gap: 10, - }, - instructionText: { - color: "#FFFFFF", - fontSize: 15, - lineHeight: 20, - textAlign: "center", - maxWidth: 200, - fontFamily: "medium", - }, - dotsContainer: { - flexDirection: "row", - justifyContent: "center", - alignItems: "center", - marginTop: 16, - }, - dot: { - width: 7, - height: 7, - borderRadius: 5, - marginHorizontal: 4, - }, - activeDot: { - backgroundColor: "#ffffff", - }, - inactiveDot: { - backgroundColor: "#ffffff25", - }, - barcodeImage: { - width: "100%", - height: 50, - resizeMode: "cover", - }, -}); - -export default RestaurantQrCode; +export default RestaurantQrCode; \ No newline at end of file From 58692a7bd75f562f81972bb0dff3412c3b219375 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Fri, 7 Feb 2025 02:22:18 +0100 Subject: [PATCH 0526/1144] feat(Menu): Ajout d'un bouton pour ajouter une carte lorsque aucun menu n'est disponible --- src/views/account/Restaurant/Menu.tsx | 39 +++++++++++++++---- .../account/Restaurant/Modals/CardDetail.tsx | 4 +- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 61e42a4af..1fef2f0bb 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -16,6 +16,7 @@ import { ChefHat, CookingPot, MapPin, + Plus, Sprout, Utensils, } from "lucide-react-native"; @@ -600,14 +601,36 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { : <> {allCards?.length > 0 && ( - + + { + navigation.navigate("SettingStack", { view: "SettingsExternalServices" }); + }} + style={{ + paddingHorizontal: 16, + paddingVertical: 10, + flexDirection: "row", + alignItems: "center", + justifyContent: "center", + gap: 8, + borderRadius: 100, + backgroundColor: theme.colors.text + "12", + borderColor: theme.colors.text + "40", + borderWidth: 0, + }} + > + + + Ajouter une carte + + + )} } { Date: Fri, 7 Feb 2025 10:33:28 +0100 Subject: [PATCH 0527/1144] =?UTF-8?q?fix:=20Ajout=20de=20guillemets=20auto?= =?UTF-8?q?ur=20du=20nom=20du=20produit=20et=20mise=20=C3=A0=20jour=20des?= =?UTF-8?q?=20ic=C3=B4nes=20d'application=20pour=20diff=C3=A9rents=20modes?= =?UTF-8?q?=20d'apparence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Papillon.xcodeproj/project.pbxproj | 4 +- .../AppIcon.appiconset/Contents.json | 42 +++++++++++++++---- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/ios/Papillon.xcodeproj/project.pbxproj b/ios/Papillon.xcodeproj/project.pbxproj index 01967ab06..ecf2782fd 100644 --- a/ios/Papillon.xcodeproj/project.pbxproj +++ b/ios/Papillon.xcodeproj/project.pbxproj @@ -453,7 +453,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = Papillon; + PRODUCT_NAME = "Papillon"; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -486,7 +486,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = Papillon; + PRODUCT_NAME = "Papillon"; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json index 90d8d4c2a..e91cbd33d 100644 --- a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,14 +1,38 @@ { - "images": [ + "images" : [ { - "filename": "App-Icon-1024x1024@1x.png", - "idiom": "universal", - "platform": "ios", - "size": "1024x1024" + "filename" : "Icon-Light-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Dark-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "Icon-Tinted-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" } ], - "info": { - "version": 1, - "author": "expo" + "info" : { + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} From 0a96a5183847faa8c3d8b9ac746b09e898a1957f Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Fri, 7 Feb 2025 19:21:07 +0100 Subject: [PATCH 0528/1144] fix: missing comma --- .../login/IdentityProvider/actions/BackgroundIUTLannion.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx index 963a1274e..9f748729c 100644 --- a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx +++ b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx @@ -195,7 +195,6 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio className: data["relevé"].etudiant.dept_acronym, schoolName: "IUT de Lannion - Université de Rennes", - serviceData: {} personalization: await defaultPersonalization({ tabs: [ { name: "Home", enabled: true }, @@ -204,7 +203,8 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio { name: "Attendance", enabled: true }, { name: "Menu", enabled: true } ] - }) + }), + serviceData: {} }; // https://planning.univ-rennes1.fr/jsp/custom/modules/plannings/pn8d0kn8.shu From fd629a8da71fda27d6654d12c7ff5b0fd9c0d632 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Fri, 7 Feb 2025 22:39:56 +0100 Subject: [PATCH 0529/1144] fix(ts): any type cast in --- src/services/iutlan/attendance.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/services/iutlan/attendance.ts b/src/services/iutlan/attendance.ts index 2a3d01358..c6cf91c6d 100644 --- a/src/services/iutlan/attendance.ts +++ b/src/services/iutlan/attendance.ts @@ -19,7 +19,8 @@ export const saveIUTLanAttendance = async ( periodName: string ): Promise => { try { - const scodocData = account.serviceData.semestres[periodName].absences as scodocData; + const data = account.serviceData.semestres as any; + const scodocData = data[periodName].absences as scodocData; const allAbsences: Array = []; if (scodocData && Object.keys(scodocData).length > 0) { @@ -58,4 +59,4 @@ export const saveIUTLanAttendance = async ( console.error(error); throw new Error("Failed to save attendance data"); } -}; \ No newline at end of file +}; From dbc24902ba25f5b4a5fb7dfe892c59bddadfc8d4 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Fri, 7 Feb 2025 22:50:06 +0100 Subject: [PATCH 0530/1144] fix(ts): types in --- src/services/local/ical.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/local/ical.ts b/src/services/local/ical.ts index 7eb491e23..032c2f3e2 100644 --- a/src/services/local/ical.ts +++ b/src/services/local/ical.ts @@ -102,7 +102,8 @@ export const fetchIcalData = async ( } const newData = nonEmptyWeeks.reduce( - (acc, { epochWeekNumber, courses }) => { + // Maybe add better typing, I added any type because didn't understand following lines :( + (acc: any, { epochWeekNumber, courses }) => { acc[epochWeekNumber] = courses; return acc; }, From 7c8eb78e5282388b9e69c585d383dfaaf3a7646b Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Fri, 7 Feb 2025 22:50:35 +0100 Subject: [PATCH 0531/1144] fix(ts): types in --- src/stores/timetable/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stores/timetable/index.ts b/src/stores/timetable/index.ts index c416929e6..b0adc2e13 100644 --- a/src/stores/timetable/index.ts +++ b/src/stores/timetable/index.ts @@ -23,7 +23,7 @@ export const useTimetableStore = create()( log(`[timetable:updateClasses]: updated classes for week ${weekNumber}`, "timetable:updateClasses"); }, - injectClasses: (data) => { + injectClasses: (data: any) => { log("replacing classes", "timetable:replaceClasses"); set((state) => { @@ -68,4 +68,4 @@ export const useTimetableStore = create()( storage: createJSONStorage(() => AsyncStorage) } ) -); \ No newline at end of file +); From 53d54b6dd1208f6587247cf47e32e04fb8161e9e Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Fri, 7 Feb 2025 22:55:23 +0100 Subject: [PATCH 0532/1144] feat: added header image --- assets/images/settings/multiservice.png | Bin 0 -> 1233263 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/images/settings/multiservice.png diff --git a/assets/images/settings/multiservice.png b/assets/images/settings/multiservice.png new file mode 100644 index 0000000000000000000000000000000000000000..6a8348fbb423dfbab80ebf61896949a8306f7e87 GIT binary patch literal 1233263 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�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 !p5 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

=F#gwe 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

8L{6Jgh&H|R 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

@iz*v*Z zo-kS5{~i+ae%?9x;DAm330GS?e*P1a!4!D8{0pr3^%yxD53OClDPQ+(qrH96@xRuu z{A?3WWDaJ4J!t_x{SmkhdUn;USFp!Bz3oi>`df?ToF%@z@@=zlIz=a z)J^`qZl|1UCXIPKJ|Xp`d%|D5CtWMP_%!izPc8DnJIv2N#dNVaJg)D(E`gyKB=-HE zb&!7|@G(4r8qh#lkR#Qv9+0p2rn=bBt;mcgRo6-j#KUUzS7Ba zI6flYPF%|}Dc0lU1Iz`1nvk?26{c*@@(3pra8!9(?HB{(DYtd;a_G8K2+h_UTVhxf z$K*aytVBNWVj}BZGV1XON@jQ*h#V$5>f&1fb~d&h;y<`M&{*n`*OgfIG=3_joSu~bBWFV8v?pH z=H9et`oVPv2My7vG7SmF5$`GRRdImAz*!{>a0+@ULz}$mJF;0xl?K&~=PAxXJNMkb zai8SDBY>4E{Q!?)s|-*?2pK33D>HJ5j5CRl4yZOTx|xPw=mhTxW;(v|I`K!po%xTy zn3Dqt`gQF4j{DGm$WOsnTl#@U<0(C-S5g0D-c3!^OI8hf%4h=UOP)#6XmRJFag1+& z!*I5xkCHf7`bK*^Qn>S`JAE8Zl!nY1yV3ffJBDSJvtmj(ZA$dj^}l{Jg;vxY~QN({#EDC zTeol1^Ups|uf6`7-Wc`4qzixKLkoTKiz~hIh||d;cRpS9s-o;O_xaddM_1_g8&##qD`tcvnu6R?|Ho#RJGq+756j|SJ+1gUXOkvp$t?^ z`ktX&j(3LVW_Ssk!?q{(#%cMlfBVvtz`y+x^Z_7&$z3#G7{F!b+Q?3lg`nPge{eKr zwvom@+tv{<`+bO_^YGpl8mA-H$w5~}x@n*%e?vtW)bAb{&oxN(ps4oCzz0(e6ct>< zb0f^9* z+jarBG9@`Z>$f746_-7parys?fBJ&%J=Sw@YhQcPmn-?1p?%46(H~sU#7ib}JijCv zZ<9lCA~8X#S58T`YxBDf`jWk8G(begj;&(N$t z3$JZ>OJvifGQ3}K1zxSlQrs_5N~$_i&Llu*_{b=eC`Xe7=ZNpDtvUBPDqQLs%QIEl zBTf#-Soa>T*k&gC=ko8g-%Ermy(Qou>0CH@Xk(8Sd)G9GNN7*A);&76bANyG(^pys zv=<_;2tbrycwn`rwbhuncd_Y-Z7PH1oBuu6DM7mMw?8 z@tDY00JE)FG#@n0iN!eD7SORZsU?k(M^+<&`mIY3P%Z}!er5m9-qR#)Yx7`R2^*~! zYm#hPO6X3dkM4M9t+sbbEIA|Oj+8@|Zg<8md~0UwbjG+whf;983q@Q_`C1c@Z7G-q zo}(WWM}q?vG-%>j+zwlU%FYw+h%OY~v)c>nF3Ukmc)2}hViQ4o-H|{c-TIZq93b(~ zQ(Hl+XZ!Z|GiGQQ>k3Lunjp@>*+%{d^cxR%_!fi5ga1l%ZMY|=+E-0f^?E$c({o$8 zmbMR*oRqP`*lSxma8@JXnbI+d!jswp{k&+~eIWs}3#|`<;y5$uD&WYe1N2`{apIWOFMHnlD-*_^r*8y zQYw0rZshu%xXR%U=xo+cs(T*}puYQdbfH8@$I7h(Ip%3|IGb=fEEXEKmw%6y? zEqJE1!2*ECyDGOGX0`-xh_9_M&W4$Va}Pif3#0lc_wHG{pZKT(3M7dG+B+pZ|u&L|(W}{PZoB z=Wm@7!?(E~gZTXIg)c5*B9x?0ph+|vM+AN(WAfG#$^nC98U^`)l=g(qyV6^e>~#JT z@79R?I~?10&&L0Kv4RQ}bhZFf8jmfIv%#dS;pciLm;DNF#^HrpNhc{g3ZTBokFg%N zhX{|;2Ish#;T0jsHvt~Hq8oRRpfV~m!z?J}Jw>5DQGcO-wiU+YdE;jCY(;X*Fn8YM zBL6|>N|epjfNhgK+8NBpb(MAu#&L~_jRUnJa1!EQ{D)E~eiIBt`*ne;C^Ax*2oB(* z3Y#BNdcuW>hvuSMj^|YcN=O`J(!JW8gTQ$|xsZnP`I`$mysKat~5~|<(vZ}-ny%;>?#+b)R2En&^qZm0KEBOpk zdnfoK3?Ici8uPC-&Z~5`yLQYQ7#wZg@?c%lASzI(^z(Y=g6ZDny6N}ep_pmJ?mgsQ z9XgoiHUrV^rne(G>nu63`}WeuiMe&D+aPU2Lh+Jq)#4qp*eHHwDl-eI^#Aw2c$l*= zZOM9ZIP7fy%ye_k{KInH6rVqexc~Qiy-U%XT;F^OJ^O9X(!G0c>Q>O$DdTsz{`|)l z`cFS|WS?ovw#q<#JN@2ORzLS_jRBiEpyUAuwt)f1R`paccRj~wU0l!Nq^VWF#;XrA1*O&v1*SpR>RAyc>=5^l zf5(A7^O6t41l&DOOmo3EDC9K7#0t%W`iMSnfQ0hOpCsq#!Bm4eLv0BgQGg$eg2dP{@8*3o$ou8ZDxbSo_wLomiRkXo&*1A z|59b00hZDjZwrlBPLq?se-=0^?~X8^yEpWfwOPx>ci!sNjS47&yQR-~&G7SjbamTS zRFL+lD0akuzfImr{*E{RJorA^ooEZ*m9f3y9V}DYrz-9=N&e=$70%i9cYgV*CxKl6 z{k`xdjA=J~4xNkNOQP87WH5RaJ}SCT@VQ{AisAdejfcAv-vX|^WbkYS@ZR;({k6z< z8>_EI2=wvP{E7!MJ@CX%Gqrotj-3);O{a%1O}vVpxm)@XP zFkz3CcKv%>g?P+_jnme*akkitCkVUvm<)MddI@`(v1f%f`#^@3qf9nY6j#=zH2@a? z&Z4#6wOFg$BJsX<-_`qBbJpqWGZ8V^A4}h2onl500I)V2O>E_7B%6q{KBcjBi+~;0 z@s;~smJEsHlM%}DK*|*Xljj`PIIoj!{g?H#H#;-BTST|VP-tfqaMlBy>_HB%9GKSmQ@FRQ+ahOC9{B|&U9z~PZikRXcF?E0w zcFu$sFIE0oWtun>$tK@yI?ViK=SkCXoy;5)Vz_K^?c1NT9pPkX`HEwg|BTlM8GXFY z(FI&N(0R%y7oeT=t;S&$d+?;QrmIA7FO@G{+nKq9E!eAnU*+FnM1M22NXZBB$Wtzx zBj zQ=kD&srNjQI%&u6m85Z+Euu}rSWIpcAJ2Lfk_3!^PjLk*J))*bJ(Od4`zmN4jNxh6STTb-;y|2jf>>cJ8o=H3jBnJ}h$QMX+r&Q1mR4qdiIBGQ~kw-b< zVa7;4Nx3ZWQQY@_WRP8TI*BM|-l-E6?{@P5GaJL}!kKT9o{P=iQ>Vp(?l$97aW>h; z*`xj;Ph5Z83QS~o&EbX%`c(<;a>kk z#{IernJO<#j)g}y36Q;e{r7vd8GG-2-}~sLufDW(cKr(7r$7JXiwoVoIMRbF8wlI> z%w0c)sq1c`dsV82#pc4Y*=;M<1~Gkr33i0Rz9~3Dk>vD2YJY#_@R0hKJ{V*hP38dK zg96nU*i2sNH%Lh%#dd7z*E=SRBt zc-`0|23q0dOrK>P4x^y+xP^c26az?+P?1f))xOL?=vayYarS-#oNHxL)S@ln7z$wk zglgaLq^rCx0#{;qZNnc#n_&N3H_H|V!uJhk$@=iKv^zrR{4m-EpjrFc&cyz^|MZf6 z;(vOpB7-lDpLLMcZi)B2JBh_<*C{o8L;9`^b(805vI)G_6nclKhuey3$d^>C(k1!2qF8A6Zk-U2B&9-lrsy~F- zV>ulK8Kkf!!r z$M5t>1)f-6^88c_UeTzKjgfjH&v`0%u}@Th_Pn`RJ#{=%$ z_JuaF7_q{biD%a(u1h84AGg$|^jmyR4Y?gf|3Q2cM)I*XC>`fHl_bkKhSg*he)P*I49%a<^z4^0>FnRLa+c2oTdvtW&dD4%>l7v0%KQj(HBmVD9egczG#W$Q^#H$7P)(2 zDq*#DoE`y)B1AmNUxXg>)ym^D zKB_)N-nZ|;A+nzkACuHMeW}ulvtA*fFD9wQ`8C^#T#&tKN-gURKE``guVQC>B0zI3 zj`&W>fAnF}grizcJpEd+yu*Yv19?OGsdlOS(%w9Z?W{ud`PYwpF;F~wA)+lWMlDeW z%WHQT0mO;Hrt!&*9VB%#u;(zl1vSAAh-IIlK$^Rm8a_lgBzX0sskNELY&Ezvc5^CH_<2x#9WJf0AZY zf|WVwMmF|L_!wyFzthPpiCe#y)0Ywkw~hwGCi#nHPsV7yGUv+`uCp^LkyBe2i*bXb zJ5a7opLn~gKUaQ?uVEM@6y0hjy7`3%pGlhxTw`&agQtFZp7g5w?xE|=z=%FMR>QMvwbliUVGs6 zl%xZaVYqKYs!CUZ_B=V^2Hy}3dP8zoc>-;@H!|GYQ75ge*!M9L}rha|cgp+MU1R^F z%g3|(3a1B!dDJNkxDN8Ba01LU@EQZ66TYO#;vw&9x%ZviwsuDH|M~xSsYzg|)u0R{ zPt;~9;Gx6~3h}^~9&{>f?`6k|C#X1#_#*Ex-%_=^mnIlJ7(XW-v%2tJoDux71O0{X zZHeI25hPMrpnQhhQ}SDJ*z4dClU+nrSBCbIfv_@SOQ9Pzm?xY5Ll`SOQ1z@X+CCra zG_;5Zz@v=Frhcr)@fs(2FjSuQV@v<3o~FvT@lK)l`Ac?w?}|SqzGEK}zOy64qevj{ zr1;P6Qnr=y?^urQzx<}qPygmI>}c3j2mwR~QYr`4(Fm-h>;reG-@(vdUN?<|@NsRS zLGp_!niMDc$a|K8(?|onN-})3+t(z3u`gbF(6`7nEI=*^Jt)&NlY1yy;Gv<>0}14SILqV@Wqd&wX{JXaNFSU3$!< zF_9(&*M62TO^BORaEke_q`Pj#8o?mJq>+yW7707S>HqfxlU3a}xi;6j@75NR@j>5v zVl?81u=3aFfPK;Z|6?RrXuL8Q9$l6z6)hp2!vZ@?nNlniaPc&d(MPzn<=gv8@ICa~ z%00QI2|M!bctM9)f@sKV^kg3CYF4N5!S*Nh^J5v?T6L-c>#Fa*g(>;Ad}P~Ki!X^F zPaFq4y)Sd6vC6b>rz?saIlNvYVk(xJ3Yk6?{)d_2yur9?{Ydsq?-R(4>QmeCaG50Z#S*?Y{`YDe9LFos9IUUm3yogUsgWrgn?6tr6hIwgirk)x$2jQRF~_>P_a(^eYaZb{?=qXH;l zgQ74g%NXwqVS-s|{D0&4`S0Y`n({$;jq*}TSOpTFVH+*FAW)l&alkji;_w3=E;z1C z1!#=84>>b0H)b?YPNuY=w8~Tx(SpQCy2d+04vMj^<0X%pGQ2n zV&7@V^6DuJ`dhCa`L#>IEuhKKRWz*u9_~|&?^4SEsW_Y`n6aeZS_rtH5;13AKkPYs za#TX)#va3Cnu`G6!8rUCUa?u31`4y0(tD4q#<&awj*DEGZ}mX|B#S9 zcg^0n`!Y)`&)S%k*{c^5!R~y%c3SgIRW&K&6P#o3uIUSj9oq`;ez|Krb2L}`cXmXkBGzXJFV(H5Qu&vVZ|PY-<+FrwpzeYp1{A71E_UpUf({PXRRxHa7xykiQld@#tmd z=mQa5iJ(q_IVB`I6P(%6 zl-Rp>>9im5N_fja6zVi!H96umzVRDok7_vueNVmP@7qUB?gH&GC_T{? zCE0Fp)%-ywz}sI<+L^MoW%9AdaOZCWT>pp&fDa?6|Gr2 zwTCI*GY_4TGqp@hY<&DXmb!)SNMlKS&t9P0wuwBrPe4d`Hr1Cj@8?a~0dADF-D}W^ z=IeEyfB%UdfA!zGMStrTF6sW2q!OJ-wgDWXR{Vr92RVA+tXVX$M!IRIm&;jOJpVj8(cTZaFY4_t-W`iAQ6o9 zZ4!uZzw{wKqpA2$8E^*s*dABKzqw|*8|(GYzxfM~Ny3)f6XP>xc*_$D4l;vJ!;Z5B z)EQdf2=G=Mk?BGK@CH|x=aHA19((3?#cTA5(AFT*qprWayrOd#mVWcG@hnkh+?hRY3Ig9(ski_)RUb@soq8Ri4|OXo@TU6!JU0V6$?y!`8n#| z_ny}Z<^ees!7&+!o$|Vk9$M8R9P6};m(3<2P7sQigm#VbzNM<7d&5qFHZu9a+68QN ziT|_nHHBSe%e5FiE>Cx_PKo2T%M(P&2`4yg7~yoaRmN>av2Q=!7AM>~*-Kk}%q@w0 zOJ~2(om;1P&WSD;2b#zKv451xHNBx7G4Mk?a6(FTY>@sjfXQ}bYzO3DD0Bkw64Vd( zFsFfV@bDY!7;%o3&_iN$e5W{Vvs?#zp|2cj70-~GUfD9P`0ao!QQdJ_AC>5GY zM5au-@)D4Fv-}qNDG#xns#tUsRmDymIhBVF)FIHN4jx;~V1}bFKZK(+z+!>0aIE-+Qk$*PLUH`y69lvg7GQ zP=DrjGC7(_YO15M@bwpo+=N*&;R@7QA|Vp7{xU3ra1yWm=9nE5IUViVQG{Ks?&S0Y zw~K#hyrh|g0~Fy;Rib4j-N6gmlzY)+%u;&!HQO;dzR!5%WH1ZjrR4FYdbrHdJ#Oph z{-uQS)}NQYZu;Tx%bSHEHo z;xT5_PhkU13WVhn=WbZdg7iUfY+18aimgS#35(AxY`9&bTsUOINnKg~!CJugin$!A z7{hr?bz@C^U&FuhAl9ns{iS)Z44#ZSuVNND2DlZ?@IXgyb%mW3!~;g>zHD1$d%tamm-5=f z9xYcwPGKv~qfK`~l`hhp!Jjz^R{ZujYT@mk$~van`43Lx&0D#9^> zPo8zpKX_h8BKUvt-OIq!0=cGSqS9|U_{}hh>{)JygWywsHrLbhYTXV6RB%jyQ$>WmS z-;q6CBJ2?><`VZ-HBCv5gkkhyiCCuyl7S9ZNI19qs3;!@d>ppyRd(dRs-CN%eD*?BtV7 zg?1~-$v3pcb}1C|*iN2Hx^@3FR%$w4hr}or^b`)n@q$SKkwCBIfKhkUv*vci6pKm% zasm?iJKB#exU=!a&y}^6<9`_!SG+=nsO#Fz~UJ;D}594GhoK5Patc+MB&&i zsY&PaLf%pl;0$cvG?^qi=n=FvXCAGkRwcW`%wGAbSpzQ? zXRa)Geu=n?1{ZFzl^Tm6>Fj6}>0<{3IU*(wUb#8*&x4c68#iusUM{iUU5*aN?bwQX zB!m4klE?%1Pux+zcjb%0wjDvYvyg*1LpKCRS|LnY@>t7Uao)k`cTFg$XG-}ZPT-1= zL&U%7U72%6k+RDC4#PUVuMJV!`on!FJvG5Fy(?VrgqO-8s_-T)Y)|v|oR^yK6YS^{ z<3hQQgzwZS=XBlulYagclb7Fe@j=XTlLtxw3%tHvekqf%nU{ewB=en$L3FpWbgGb_ zg|i(-PpK>?-=u8`-4&1~EJc8-ABjz9ZISNJlyj=WNn9pMM@YV%wz(w;qrGIB2-M zNY)-*heAR3FO`4T`$zAwkm4PFHVZhF(+4+myhXC#NZgur`=Ij0*RCv~V1|>jPI7FTqPFd;9 z;eh|aX~KCVat(v33`tAK5bzcbs;>a90ff)w+yvmcdsn^t7ZE5U0rcMW&Mtf7D`Kxb zFZTAU0ff_q$KW_w&8G&v-@K@AG%P{^PI$+b@&%u0KlG z```b5dEtc@_77&qQ~dXK7Vt;_<7}99fpg|Xw)2n9&vadsw-p;@u_9Qobq~gcYB(z_ zod*e5Z49hqpkW>`*j5)K0a?CbcrP?C!8UZRt5+CrKj$avk+nOw+w&`l$bmx|XsE$z z_Exu*8rRi#04=0$Giccn8}<(|3yBfV`2D3<+3(=mdT>)^dMuq*<)iFFq3*+1J*_?= z(kn_Ae%ypwz+LTV_RRuT4%u*hl5PGg-*+v)_WTySHsQcmxmy6Jj2J_+vuzf5QmiVk zzQz!ff(;X`1kSrozo#qdoS`g%qp9r08^*pBNf@1K4j47v7>Rx3dR6I{x8Kxu6!_eWCcpG~ z-wOIt(!{%IRngI>O4o0bj=T692uBi7dy*j&HuL);2MC>}R|%(FPLG&qD--b`=7D@igz{Ch zLXd*BD-|MbDTi$S0PaoqZ0AgW^3z))`0AVpzNXrDO;)=+*+2?!VhtS9L2=h?r^)mP zsZ>uCN*g00m2#p^Z&biQtGZp031(W#f)-*W(2 z7Fzjc@hNH3p^Ix(VCln1aAbTe;K$aR;%DAnZAnG*B$FpE;(X>f=htKUBDpnk;52u# z*`QjHq_eSga3p`n=Yo}ek<@@+;;U$_;%>Qvm`EnEhOzJQ3*$3dMQW#>mI&G^Icdty z_9vF@ZV4lw_?iB9zE6R3i<()q*`&;+0(S7m3z5C%BBmrl(@h?2&Z1iux7{ zn^DQ!(V0kWZHd~o;*RC>OD23ClXmICWACffU1~ zOf35IHTlP@(1Zc?Emug8B1s!099XBGqCLgx_`pNRTe%A*R-ULEL>T9EORQ3lP6iI5 z&7!(^{BP3ah^Zr4ns_gqU~);Gy_;juz@-xHDtQR~rldA2Hm-FBGw4|H(!hcD+SdFJs{>AxvOZ+b)S~!Y)18@y|B5@mMHk%U8 zo7u{pQ#o$U9VEVumGWyQe+0kFA_U(ECX8hLOEK2BvR5>*{*5D zpgCU?yj;FoNHOcmWW`uXD@e>1+_0&Lu^5g52$CuCA>g0Dr|1ooYz ztCFM9PJmmtNJX*R=7jZJOt=YI=rdms|poEZPEp67~Y%O>oR9ntg!TW|5 zK=$I+(7*VEKr_5C>jl=O85GHKntf8Tw3Yl7687o`%xknCHVgNT1yP$wQ>IwpP^zY( zOtGJEovxA63PV8u4a@VP{|}4y)|(2jW<=^d^_s6z{rns?S%M} zaOo_b$9!hRK)*GuRn41D?uVV!5_-k}657B||R6TD%iWdUY zF*cQ#VodJytYmqH3_+eoG@SVykS|~>i5XeL8xSccA^5BeSa=9wqEe9sSOC}=Rs)Ly z$34%uD40UYS5aidlbOmr>{EyXTH)m_{wa?1>)!#gO5$ELZ z>CN_>HC&&>^0 zbp)Hj^XKBDjRnA^cz|H%G!wtfp*@c^kKu3Z|R22v*f+Mm56|L5awR0Wkl7WmaN zUwl8g6L5$myu!vY>P6$_!_0N}_dF8o@O|pjH}Ypcyu=yV1RJve)PwFtvHOzMc)L&K zTRH3R*-;{R?ytyRuYyi;{K4LU3cR2v(8Vv(D>$p>5 z@>CqQf1dhg>IS_xvqIjyfb>{r)@*y~Tch@k3BGTS`RPFXE1t@HH{-K_YWnCA#J~6< zaQ`S5ql1(9_#I#TV=AWy?~Ta||L7)5H~T>SPv1*JUn@(-0g(6_$~edWK`hkoIMBG~ z&yuyPaRHT_4pu}zfBD#V*7?6ptr73p0e)n{>Gtn*I?2&>M4oTlsIh+mX~#CESSbga zrdU1cu!R3843owRyj(fAk|jsd4Y2q*zuvDnH`Durq%^ik%|2|t!RE_ed6;2)H@1e( z9l=JPP5fjMgi^45ta?Ol;UlUBSr#m#j3t<-3Y~7HYEgsVh@H)vu3Lo`1>l}_%LDA~ z?B1HW9mmJ2P5grge1&jO8m!>$NG2$+)OY?Fj2=ngNHSIsH1KGpyw&Qq!b{=tasI^SWg#!bxtA|M`T17;};flzg2F zKE{^3k*LUgr7k^rDCl3<@6nD9Jz?Ny;+N2%>f0>`;FxH@lPp%KZQj;7+^l z#jnA$rxQRC)Puz(uHxc6YL@?qG3Riz?nQSs`#|}SeAggzQvD{<1Y63OFbEX_R^70; zAOSg&oUV5ago(I-2Cgh*13=LDU5nYQ>i{QU@Ra13IuohU)m-JP-S=RBsyHzEQr0Og zW*7_;mf_IXgPjRSYV5|4tzJ3NyBK03R!KUsmX9KOcwLnUMqgmVb=9>0@u^(LKx3R& zoe6vqB-Y#6@nesc?`5+$Ugwocao+N$d;;Y97` zEC;{)b+fO$cG7P=NVzNBq*7|?d|h|6fCfl<6}UEa7)lRZQb8&y#UadG=nlSCk`X;* zMocI(ky*X>cKWJG;RJLFM5RpAFlRYwX1*!w`fVko!wXd*7-w2bdHZ2Bbq8-#h@>nK z&C1a0- z>Zly>CL($%QLNK@muJ9GGes}pxbtmP6*%PNE)w$oNjf$7v*=8L?tlsJ!eJ3Oso>m* zk*X;Hi)#=^4cjX;jmpT3N{Xa?<@U~(2oa|k{ z`deZzpA*05q_5rbicX($;oik-ODb<|y!A@d`MlHr!uwhGV^8O5;~`d$J>#p5w?bpp zaXgEa$fNup_BA8upXc?51;hUk`ud31SOxsn3!P3g!_n{W`wr1Byd2IvbmXSG_-w6o zi7^YG61!+)@qh=^*l0dhN-UR}567krid{wfb+WSm+ejFLL8-HPW3cGk`#Ina=Xx^+ zTUV;82SS(R)b#5FX8^l-4a3orXL7*%N}W+@_QJNAsx|_<&po-6U*i@wJR(M7)#P8& zntJWJcH=qp$vL+{XjwThE=t|k5B+S z%V@&Q3CN^e@OC~+!o!uEU>BlS0AvAlyhfBPy!Yg#WdWORG0UW5LVgQ5Xu=oGxDS~9 z{u}23+}}JW>3;4f?nouZ;RL47FBH@f8Vl^oB{_hMgKlLVZQLzcKF1O81GRA9>fH+z zD(~eJHB{#WZ+n-(`mv8K^1^FZ@(Z6imF^MZbvu4_Cc|D=+zvbV9Je^{!-4OV3A}n_ zXYh^{1(Dq>-lsMWP@JD+9PFUqt?8&|n}G9|{_flI=fC@EtOy?GPTRh|h6YMj$pp)e zY<7HaR~A7=+>1^tJxDObBA~F(hB(lue68%j_7U66ZmWiUt6EL>?CdkjNIhHb zm?sKQiFTBC*(rj^dwR61Aq?5gV8ET6xHt!-uR|0|NG;=E;qQ}AlUib#$o zN|Krw*9oy5C{)6mgnYmTX~{}%JG(4KK0&(A0)7ZOl88OeN8^6uzcV!7LFLHy``Tq zjB|hoOe!?qCSD3U1rzcAhJ)i3<~!0qCT_;hM36 zCI>39k0RY$(4~A?npiukeWJmlJkHKIuf%@h$vat(mVT@Fbgn`!2jW899qE3_(KXiO z!2T=i(pdi_WVmSSiyM>FUy_4KcdRugOVsm_ z8J%#J2{tVfN4E@_q!kl*`gzoj?Nv+9BaapBA^xuv|JO`f7c+|MX>3(5h{4yH?@&p3 zc6q6=SZk0#-xB|K`~VqXm!yznzCw81J$^qp^WM$5(EU?rh5NMYM)coOR+F;ZDq$&v3H$sD=gZ6jH z-zjI6@9t|R)RB$$cQ@RB?VOhT{3|#5)we__&6wlq%H}zom>hBK^;P20+n5qi5``i= zUI{^^v@WWsOjXuqwJNpYw;8)sP^t|cl_LuA%3W5xE<*>Q93LT=Wumc?b7G=}({eaQ z2al45QI}ZTG*P7Gkt1~tYP2yb{1)H}QyoG-dGq8PD)q!jpu!N+j0kgsT5&EUrH$y8 z#>@Y2v-h-C5f|+%Q7JEeP1?oD)zog5ymBN=`k`k`9=oIV#TU8|TohV-ulje=*XTpZ zRG@e6+>v*Dsl4-x*+d^w=F;zX)84zeHrl`Ss;poBkHud7 zY*zOuP0nSH?j1C@v$yZ${SDT(+W58ScXc1Oi^TB~D~Xr$_w&64$>MM2Hr35Q)4gx! zb%~$8)$RUawexMfwiUqFS66a#bJ{b^fvfSOA5!_d|BTO~3L6~*O48DyZ{utvoCgIP zvYyJvzqZ*Xo<`oK3Th{-+Y z&#P@0>$`C)nzJ_il{gA0H%~vL(2MKJ#f4si- za*Xe7%hO-}{7HWD`>sT(ez=x^NvKcN`RO<7Yok62;lYBdOo#{jRc@_i<2K3afdC38 zcP-p(lxzLswh2Z#(APinr+p6dFMNh}5{P=4|8YOqDc?^Jj&#$6qp9iMze!AZpsOm4 zf6OM-B&~!JYV>Zp6g8hvF7^-U!61K{iv|fV1hHB zE2~vK^X7Jbbxjdf?NlKtNIO@M$fN-1IscB&;tA_GqP8lb*7c{4g+phoeusJW2R?Wu zm+)kShgtX9bkC;buf)4Yd>YTPD-zJgSiX36=;iY;NIXe9L~zZX#!(_R&P>FL%aJD= zefD#GHCG<0WE?NIp>=;ZsfaUZw_i(?#&IiYdvzpnH)Lk=V~ILJ!Ye;+#ZAhpYb5Pd zebVEYST1q?DXo;v&G$0SV{~+uC-5wi4G8cDkvv(EyOsfexf`Oi7|hnh3FifG&mt+3 z3CjrZZ%454@vzIv7(cfAjcNjTbYpzM_li!5}9kQS{+>+g?N{R)%e}L358-d!1u7ec;!Th^@e5 zGJ%oXv1UwO{5U(;Yq|Q`fP0O!bnu)58^_-X?Q9Te!NdlQHV2&9*OWCNZ7Z{FNxf+4 zmrei0dFh$B_c1?W0oyy<&IUs7>5%`irE$nKCTYa3VTojqMTiNlrZofKxlA;^Og*=z z-iz@PlC8=-U#qb>t{*GKk!H-|iHt0#kTaSfkn%!9%$(e^dW@x*r+6!6P-=GvoAov& z=k_k>njDbNxbMymfJ!SNBf`gZ8TdHlH~oPn=@+saF?g>H0+YBE&anlBhfd^G$m*h* zCllqsxq!3aGf(ge%xLm_@EsV&2(t#BNdrGLaS?NQ0Fh-FZgYU<@Ksex4YQ-+3ptCnEp$WpI zLW|Gpt@MN%vt)UXb+6^*WOqgVO)*-Uy16h6_AbHL;4hdBnyub8OYc4txI1{#eR0@; z!>|CT6EN7TuXpBbg4Js83{BKb9uSP&469Q|4wONOoi=$%SfRFUlFNWMJgqSs*vRl^ zCDsli-@tt1rW_dO^O&J17Hh~vSUCcybTraISc$)egCGe%R?L}TU<%`(e}{{;eb14N z&`ho{l4-ybsNhVKXYb8x=R{_efa@Kv{(ZL>8-%0l=-KTY`_3RrRH9s&%Rq6~7anxb2$D zf4lhU538L^Z6CGqsJObN+>=i}C13mc*Q(t74yl`GA5;0>_lthvRj6p!yqFojW*sV_ zQ(N2JzgBmY*P1N~Yi(&tGRUWF{hPP5#_a5@AnXHzNItVzB=mCzGI>JN99$%z8FhN( zn$Yub2LpD#SADNRzQ`1}9rw0XXet#Lg|*XuBfsuokiw2O0`9-Jdg)CL;G*BwM|QIF zH#wk9FwL#b?{2H?KJi0~{O#Ym$re5NK$IvJknp)QhUELv@l^$8Eff6 zN*~Z_5mvSEWB@wxCDH^3?wZVSmHp?*bHO1YBUDxM;3Z|mVX&%JaNnehpgBK=o7Vd; zML&eCsl6JpmN5H}014@$KmC!J0}v%Pf(Op^7|kryIqBawcs}`=lYIXN7x}Jd7!IbM zBoC#IIzYW02v1AAw#rCH=wY{Z`m%z^+4JN*2|rLK`A>MH!)Ck8<=mNi_A@`UC4q0` zQ=dKw^r~EXCD^%1b|4SQ8}CaFe(G~4{)Fq)-v_L~?`ji+1VU%Uk;Pe#Y@&e*<4g~} zGwC1)`8&##G}yr5uYPfrpZURS`LT~Lt(<0?R&qGWVDId~mGB3UTy{zV?&?MNv?I0g zzcrK`HeX+~CtfFhHcl{HsHYN_yVncW2_GF-U2p9Ht|W8^-RosuV)@@`B4#6Q-ujo; z-rk$St~g^0Q4L4@Ck` z+?D@7h+wmUNh=Kz&+}+S;3Ii*%82$7Nmue_BNFTW2NFC%<+QP$P~UJE$kVKEJfP!d zL}d3pC!MPB(OwR}m5`%c1eP_Ez#^HZNM11{Q57`7>S;%lg`B_xjPI2E^@1yH@hrIl zF4fnVkZZ9D0=lMaDpr`Tf%Cy?zJ05sDEMt`D}Gg5YzH;5y>8SP>>q*mfEn^3 zk^CQc$atm+zaPSKu4Z+ZLPHL`#3bXQQ|M^Gi!h&A?c%r4`PJS(jYF4%4~wOPYM6!nJn!7vlY*Q<+LFo*nOE+jprPP}+Rau772vSbONRNMUCD-zC-K z`9@lV6Wh3nE!Z`8rS*Xi1xEZfO{8hpwvcM=@qddx@**j=GgFK|0z#R%PhHK)O)u%s zS=TQ#*+(+Zv3<8@Gcvg=CI1Khzw3dnyOK#^or5N!Ga@JBCPR2~iM1Tcxbx;Y`Fa1o z)mTPXG)z`BmEX9w6~uS1{Cwk$KC~6J-We;8eUyW~9oB%}* zho^*BDGryl%sI}9z+lD&2}RBAW=xPhPOgNT>p&KJBs3*-Qgbg8p^UJ^yVZ;ZPB2GZ zG(?KJ%URIU778XbqLxSxGo*2y2}|&A@Wjy}lkk_)YZqXyID~Kzk(C^LGwF&?xr2-u zNhQm)4r9w@VxI%NA+Nr6j zzZ(4>mG1D=@6Gg{?v;Wkg=boVMetmyTBE0=5{yiG=`R{BSZmAWXchPq@C7o+sV#|| z??HMSx%_@wywde4;gu(VoCFD{{>>^W&WIT+(!%j7MNQBS@FZtaxlx2cm7=*84ssxX zm3P~u$+!37Hoo0$9ksE>Hr4Vr635TWqg;A7 zRvTaMx2eu&^ETD*lppn=>^IBTx3zQWvPUfU&9=>bdDONpl{?q<)cei};IDn{;`3IA z`B}hUcr_j^aYm3p0f%Xk8sUt4NahVV+!K0$uq9yuL}hv}etX06n_q6=0fY7H zV35&aWaxX@QoYB)XW6yv*J>RmWiLrGp-!w>K@d@HhsslZKlQy==YFs9xfj-azsiyy zI(csChB(6At6)?G18LS$zNIzncCWLN%M?5pribNUzX3))5=>*$PhMugt<`$gkM`j4 zWIrd(tc(kcKj$)0vuV+Y@RlAEFt1X{H_n5-zxiL>$j|-wb#8+NI%lm&`asm9B6it29NKu*xJ$~Ur7>UBo9}X`jM~9( zAz1k0a{OrwL&3L8L{HzhvRe{(``pe=p6g69s&Fnpo6JBoS34zkp*$MvL>U$VoBCEf zPk5g3ap^blWz%bOJD2dw^_hSz8>|_Gq$t-*=Y-qe{KpUEvoBnY6~P~P5}lctsp-E9 z7c*B9LK@FgiYW=ADNntPu9bQ-vS-QmDf~TTu)#mQlzKpJ=@b3#FcMzpw&@!8Dnrw% z^w&AbyT`phQYpR9+T)gs`!*F;!h?$Lb(og?H?7}QPnihW_Zj8%;J#5C+w)KU_5*qG z^@7LR3p9?*dsWKF?&zVQg1!YLAM7t4?15@e8$(d(?~9bh{RrPDJhx?V!`MOrNLZY5UtDhq=CmyWF z%C^y&r3JYwyl2H)v<6-$Kgk>Z1Ri>|5kJs7n0{d5lTQ&7CJH;vyNhH+!S&T%!D z2XE~Z{0tfDa%Qy!GFCCJ(KqX!UsGd-aR#bs@w`-=5yu_%qzjQr5cHw8mu`tktw6p{ zST$d*o48vd@Q-AuI~I-~PDY$^)d?wdvlZ#iYQt zd#6mR72Cx4Y>W#R-vMfzV=PQ+Y>Vp2AkS$#R~etzn`cgItZLS7UtF2RYF5W>CM#W$ zLHi`+ZFGdm6<8%{k&q|bp1bRec4}+w0&)QFNgxd3Y%MxE-XdX-m9ID>HFOW`tK)KB zU7suQffFg(QclQxvNK@KnGwWut7U@Hz?Zc09r0oMP5yo0ACnnOayxO18Q3OiEVhB< zr<7$P9Wo$kot$qZx-7+mh^0#A<=dF_l=j!vF{qe3sO2*a`Xnx+{_1w{w)=;vACQP|wgLL1CKouV zu_M4ZKb>+_&6^S$rvhWZALYVqx@Q*gfAtAGs_coxgaIJYWky0bq>^bXOR> z=b1wnI6pzT=OhRdJIJBr#GabqBQ5zGH~`&9rqdfzz`R%vAiy*$<>}ga&P(NCR>}B3 ztc5D@>GH2&(dbJFJ*SyGd7SbuYzt#Wb@1MHHtx1n_3JlY!OO(GDP+Rx-WAm2SLbB# zSq0u%mRN19wyiMUO8BZ{$$#f(n%h+Kr9464h$h+SNc3)Se*J;k*WWzp8)sMP<@*l% z@uf8^Lp`7eF$R}UnXz#Oa5BD`Oi44LiX^$6A=e59FayW4O&}ui3tO6hl(sPt+c_{~ zj!pWo-9#)`APefO)wjv4R#s1QkR;q6hqLa!w;IUn~^V?0V3fju_Bx> z*;@%&_w8#KqJC+N`%;W5Mm&CMPMhJD3iq50$3z$4yYw9TVK07N4zEj$&9hWs1m6F| zCQ;IG@Yo&IANa87$Nz+^=OnOx4GU)k$Gmx`TL!=EmMZI z=NS_R{cGP-z^&j7R*BExHCW~n3o<}+KU^-|H5dcKv>5=J#D+0GVf+xBRQu;CBz$OAMD z$6f95D?Q*j?r{CQi!Gq_Lb=N|?aSk`N32T^-hI^THrUE%dE{$-;n!vT%76NfR{hT0 zZ}RQG-fbI~B=wsu`>2;E)sMHSzODU{2woy_eC69zFJtTKX|*wLQ>{~Fa?5X#+xdn0 z>`~?6Honvg_uh%D9o2PP-4AY92XHBh&Fko}(S&zI^k7`tFMH)>SxEHIOpW9$h} zcS}}o*C)?O;2gzhZ(Ld5jzEJoFdAJ{noM)shkZ0DR%BfYGEe;(Tdl9Qs~_(dJuL5~ zKGl7>dYN#1Ko`KO6Kmjoyf(Il{^DsQ*MIs4ucR4rb!obLR~L!L?m2%ah5^r2Y^7*&NKP}-DKg24 z?(jQ`nX$U;Vn{;E6JoeMue#&$=wfh+G$j8AWjXP_ZTq~fNJqOFh)&xB5WF`Tm=EDq zs=M`H5Cl7*O5mQxpnE0RKCS4S{8P#l8``u(E(hzPGluiYp1{M2wwb#Dn^ zZ)34oG2+`e#}?2ICanUltzO4!#j$=c6z5hiwvhC2$H=NC1WRn0ypEPuH9EJ@Sv>e@ zsqHU8z8wlyKZCed9(lgmkO0eAi$!8~AV?{C<@)~YcaL3=M+3fUS;L3e60_U+v523* zF*jhPQzCgMkX-)16RP%Urit^uc3F@VFr(vSW*q`LO7s~aDE3|8pCTfYWftjiIIQfl zM2*v}9px3<@B%KSvrb1H;){49It+bLu{H4^yy3jd=z#*Th$>Uvk!4+6y@!tsA|Nvnm8nntMY4(K;rH^tlfk>sBBhDq5Vu4|J6KaE#>=K?F)v{(eAYtcRRh${dO#~Zd5 zhyNP3sxgygE*sFYo_C#$E?}JI^1g;L<@mo*ra}Q*CSF4^C*m0s!eIu+95LIJ()^L>H+SK!c_pv(sbcfNmia6jjS07q8Wx6?yrpbg5buDwxPMN_-oM#yt-e45^;)FbSY{)MvBA5YW&HTHV7`Prac!-w zt@O>hou9mIh3%^AjhoJZL(Lofjn)wGXhLX}qx>IiCdoe^<#`Qal6e;u&Kn|(Q-D{( zly@$$(sLqGhSW;n3vx>%L^C;(t%8!J926DQ*u;=bDrhn&2kCZ+j^4goC=l0eu>3se!H>U$w ztJ_Nan%04m+>)UmYw?!x()E>)G0-r##4jv>CYLVdN5=f_I7z1|s+{T0QZO4?3?d7s zB!jL<&8Xcrc9l}@4}2R?nv=aqzfxxYA6wLZ8NMe6 zFvRaI@q0S|d#9JlAD!!+z&e@JKlFY!bupc9yj`bkSuBT5wjwn!=4k;H^36=j(GX zuIB`Bc+5M3TvFGz`&LQKqf4jfB!QeDp!?b}LC$xXHK50O(cs|USBb_pGl|EXKS zr<3d$!JsCUSf_S$D$ji*k=&BN+x=ho%&F;##3iGSl3+!c0!Sw~JE!jN3wKrfXYwK3 z>Lh}+xJVl%&u#IIV1F*hh1=?Gn}D4<(CBA{3S3)4{U7}9X`HwG=RekuT{%kRUA&IU zf;StVO6s3mlgo2GO+9xz&=Q$EINCmp_-(Xjq&F;iC7Yq|)&4%2XV$wY*5z4tbfFuA zzCGxB)a6!Q0(5GtY+5zgSY0~*Gp}*qJ0DIA62Do@57?|go%n!dP>DWUK4uRXBb)-~?I2_rn^By+);j*DkoBX<<%|89@z+}DA}btFE9Z@jL{ zI1_jo6TChC=lvSlRxzvXXO&jpT87;Jy$7BSREfSxjomgG$0ax(6AR%eBQcDEe4yTW9MLXZ&CsqfpYvQK`+OZNa0{I)R zRuhu9N(wj%Xq(gEkfPzLnO7NbsNYAzEY5?~JYyGtdV&h^9Pe|)jaIBEHHTxH0H8uS zooHHp5mzPh)vD08ov~r%v4&F0ZKB18B3hG&fkostV!8y z$iHJPZNDk)7?AQmV5t?xo^XWV2vS*L6?xb<#ffPQhxaj{55l1iXEIHl&0CHaExA`A zV-xe{C@~f!1r*v!xQuw}Z}d4TTSLKSu1V9Lp*JP}=>vPxZb^mE4Ksw4#|rVREeE8P zu;^FnpVaq6P!p1W8hJ#O(34|pY(XN~hAr{mOSsBiv^O4JBLI^cXXKP?d+nb8E7=OT zNEruTpZiTam|E{8Ol^@J&pw{dX6_;co@9$Q&_)8k5QNzG#OVqFen!yCyvFGHS2h6}le^$#lYzwuCug8T^N1M#6ublgTNR zbVsDT_EHtn`e4i<=Az=dom?puX#VWCx8K|BxRLvBi)~3+;L|z{145;+WU1!t=UJ~l zbx$RI?8ywsxQ$tPOpJn_SeYp3%Fnj9f`A3ARROViUvgS7HthviDxNCH)azK zI6~o{?>~1>4{3UI-MPN5A5CFC8q7A%){$BoPPwXmyYXSGU zS8@PL_YE(>&{&fb17tl>cH0X2_g=NJ$_WDjBr6k=!0(_fc>_v_$)S0)=RQ8*(y1pE z9L_XmGyoq^xgmB5h3o8;AzvRJLI%yG=1Sx%-CXIuDJ6JB#b%*b+-KORw_4+OQO16h zhlflpS0EtxMrG>ffpRdTb|FksG`FK9upP>Q_lc8VJt@9z&HL~E)LnVhFA|6uym*8zw*hj~>Wp|KLh~=A2}I?kSa>A(&~!^gggF+392~kv$i1-52UOXjg>n z8R#<6c%~)mYuwS>+G;Q|FICaqi!S0_eMspjkfN`WFB~@8HNm2DTT#{`smk!}`fVn= zi2!@({X29Q?i;08+qyV1h6#w#9N6Ush^hmX8UxR zsHg{7uc7-i(J6QrFWV%GP>eHUB#$cH(poJhHqLS9q9O-$5NWXNWH$P`uBR$9J-5lP zdF_Fp^XuJOeVbY36wtjoQv6w%8dh{OmaH3TM`q3?V=pF^|zuU^?`Gvr~)Vnb7D>3mgD1Vq zy)+Pc1i`l?l$-*$qZy6!n>CZ(gOwxM8RT;Qiv)McNEF`UI~xom>2I;HH&P$IZFebf zB*USKd|;B%c!N0&pCKd8F^SbhcmOkaV50ep!8|x^o#Tp*J(hTlrT&xlbIoZI_F_qfN8Zs!UZ$0su6AmoS z%|&?ZYRH)@-jLIfe_wqE-6#Tt;Hz`upmE?vT@JL{DvNsI-#QJKP&2f1;5d7n7LDM% zZ>=QI*vh?|j6v2Re~oAJeO0_GK_6cTlgVlDw@_kou&(OZ=6UGUus7yLQ$dfM^nzGKB*UB< z+n9ejzoVm&=@QhS%|f=rkl3Xw#*~qq4sI~%@&8QIMA|+KTIHIR;{SBUEf0oh z)pv}A&u<~N0$BQ|ht*Bm8Q3_(*x^wgPvp2fn3*R@yNGig7{G4JmwA|GbucLer${vC-1KyDHB- zDb=5(J@v`!XfJQ&o_bv5x$hAD_y@Cmy$Zm-Vf_cRQD;bj(yMQJlrF(yDH;EZ(mPLg) z#-(vY`AGglos9QrTZl18i~9?zw1aNM7E9K*s&k zU%ti);Oa_;@6yHcKlcZpO3&uc2M!mb6qvv4v0>lx82uU%G)jPHG6#ve721f7J2Nk z$M$t!=)*^!QrBIhPCDP}w7UIAfG2_>!#7*tZ{TBXW9}Rn(f0iq%-7Km5t0}h_Ebh3 z4*g30Q454}Fl+Th@EVrG)?q!awn&@0Rmwyca3B9P-Ni+`WIZ;5`O?*5*i$)G)F|UD z+-#ue$9%tb$U+S@Ko}Q)r;-1tKAFo1gIqQ za1vjIu^9KWZf#&21#~k(jy6=YUzLCm5`k`bl@*UMKXP3HVX1y+UGDB>`&{=dKmq$y z=_t1)ME?4(JqTal4p}vmtMPrTPEl^L@F*AbU17M>c*mQl&~9mef{l~W<)cdpXFbk- ztL|YVuy)tB68IB8bS+}X@@x}*>46Y-rk9L@rX|ytE+ribanY(O?p=QW0J?-6B3pN& zAd&{5D?oSFNs3X|l<*)ir=WHRWZM?{|Nbiv&YvrTwBrGZwi~^ty6~CuqXeUf97bjN z-J=@~1|rnNtH6QZD><`E0zvN{-ePt+m(7-tjphgv2C-x{Feft z2{LnaQ*u@6l`MQD{(8_p(-pQWf1~f#+yAF$f8ZCtaH1C(Z+e7AhmZXY2|gC!*r0~R z$cF0F1pE!9EOMI2uFU$oS)Ra7SX4gt^pd(DSyE@|KEN^62YY`ytx~Q4$E<8JMJg}6 zvF3!q;lad&!L?zIBNpOg{e?D|amXSsa7u+7iD75QScQa6dpgs`=81|UO=A1g)MD6r zd9_&lz3o2f6TT%9tw!>zxt-@W!TenF*rL{JTZUi0#GEd*I*vsV05#yy>)28xk^B`) zTnaacp07S}bKDq7Z`5%bo^ix6u)Yhz;#L~Ebj|gVLEGTJjEN9j>p-ZCZ9fB~I5X30 z0M1){q@Z@XIWPLSWv!T^twnZpWYKD8@nqIkd|^4--iw{wd~>74ySvsZWlvBSe0*-H ztyrxYC^FCWd(pM^$w>Wp&P7t}bG)Jj^VmYvlRsVe5Mq%`!a1Ypb182tVP24jjYPk% zbQYgfuz|Bm9P<+m@F&w}09dAmCot$@djkgpW#vB*RUy;<8Cwu1SyyBjr0oibvv5Zt zO?WKGZ|ox|ChRPMry5Hjiizaz;ALMS?DrRnry6_=OhAflm_12YR7tpKh4A3>HFyPl zYk<`%$xFB$rg1SOqAE;C((BtFLfKB3}DOrU2p zR-$jZZcC24c?(IW)%8#UNahMt|NI-tU+AvE-@Ev~mo5Iq=*p!0?$IHQ5P2sscT(Alv0_LYcto6odQU&Io+(U-+@o1R583a?n`lf`7|@S9w^ z$S!laN&XePWthg~hhQ&W5j&nqrPtz~S6p3nT0}ot((Hq{XCP-G30e$XMekYTYolhk-nH3hVUTt*0hh^kcfp*o1uz)Id^I!BVr7C2 zC?dScPUE4ED)V^Kra=a_s8EJ|rTKFO|Grl&6RFrBvE1rm(lz>$M0bXzgmmymCGMdq zEg8FOgq4smYT)gkp>TR~bW-j)<3SQ#@4Gj39svR^cES_Jb(#`ZN)G3Cx;GI}r6rHC zzN5hwBJcP0@w+l#)@arf!Nv||_K^fK-vJ-E>z*8#TxLxZ`0&ei+QDc7Ky^TcVNyL{ zs+>6^p(IQ)nVN=@JFTtV+{TBi#0=eWW(*^j#*I9`_b& zzftzw!ge_Oqh8l{V)ZW?-T`poQy=n<(szDmuIo`iJt}kbt{s#=x)<=i+ijoUyZ(4z z+gZh@fA-0d2p-!{&pf2E=RL@PO}}M&=ZH7u5$`?h-AAeOlAKPmeDB^pdDQEB-mkS) zn=2=4&DB|FK5&g-7-s)r5L=p11gorI1tkJSN{*v(aLBD_R7DeD@*V~MY8x*f6!UC& z&raJFbTPbx=^xDO_HVhmBa?yW4>@>Lmvs_cD)$ecU*)hy3dr$akwe3G@U1`9_@)ft z4DiLv4(hkVyLeJ8Xjziqed3PDzxCtS^5k7@ELwh+|48wQfWDJ9?jkxp$zwWJMlhI6 zHf<7^&e;MdqurT;q+Y)!oMo_O!p06D5elLyaf%%%pz^`3s1K(AH75e&o-+RH6~)la zh#M3t4$1D~m+Tt`_N`WT^I~)@!sA!vJ~g6>a6j-sj~t7-wVjv!OTTk!_X^hdMpb0c zRjJmv&&@T@c4}8URi!44 zN$c+`zayOvI+c0(HItwJjR*2qe(8a{^!kyUa|bRwGp3(axnhp}#bVLwvR#wywJE6J zubA*^)KW>hZ*PWKc4|##u|A|{3d8O2A755-Y`t#R@mfrw+EHck9=%|p1oRRdGfb0E z?k8pRFOYvBJ+Ql?(2m8=|9<{AZU|gUkann=Y?X^>XmIUt@MVe#V=MIX{8hv%RW$zT1L__>#08@7?9x5f?r~#QN6QOj@3T_Ue=i*A(#Sxg!4F7^NqUB&k*xlv67Jl&apxq zmTf3)&mEWOG_; zHRWS$OT0P*tjQN8b6(K!uJX$7G@vL{E8wxhcChyn{5LXz3mii4Gn_r5s|255H7fWp zDzqb`=vAT(jy*;n7BWFAA17o{Cz_r$Gn5~AnRHgHCe>I`HX6{#%DR9I$@1-?qq>mm zPDjDJ0zP1wu0AZeQn5XY#B}gh%*PCQ9Bz3WHy8~qvAb3r-vsB;vgQSecxz}#+oT9@ zO8SRZYsDp#!b1jVW$^a7ER~@NJqEj2lPqam5;)Za!ID^UR1`QhFq5zOFdy(lY&voY zo}A$ry(jHr7&YRzT6T7d|EHlT{J1HNuF>|qRJ}?qa2mQ%k%mrxFYJOWNsGqmis$if zL4q$KRiPxXCl3*9SCN)PG_rv->0ll+TOcyu>4`S8DxN!Ff``yu$p3&@J<*$fh8N1S zf>BqVmD}afco9~yycA5bkDUwp;Ow1S95?%4l-hqf?sOW;`|*uPpaZ4_JQVe>TFG7! z?xn}WMCMs~D{MN+MD~4BPbrUp6Cmaa-P#^IRggf>OUl1VEzTUPcbmgnD2#8=EyR;* zJcdM@;H^wV>wYn1^}XO1B8O_Ruy9U@tYph+fu|CG=wQVDl5MAVbR0$7D@iEoLF#Y7 zCsNAQsCXslh;Q2L76d221(vP3Ztcj>IX05TX(eq$T{Mh5RFaV4;2=Y`o*6IR%R9{x zH78ta)M26j63VO`c+g~Ng44JcG~=eqL;-80uYUSn9te3In$>19x*3X?1Cji!0>$rC zAlh4R4T^I<|2KVp03JM-+FJV1ZWMj)JG2s|1v7o_aH;{WgTa#v;<~w^cvrGOyJ|oM z!wNnE&r&2d9)ed{9fFVH3gqgGF-_bc2L8>G|YF$uH@1Le82rT;e6ya zk!6R5QDCe97G}Ko0w)r^NnI031gR~g#B7?^B2-86T{zY(|59o<9WV(d#)J*(^1NEu zpO5yRd;Fk#7p{ekJj-hF>i&~2J^1AJ=jVKnKyU4d9Jh7U9+$lY_dz@M+Vk%vf8V=q zy|zT~&FB6h;?Jne9Spr84!m zz8#rMJ#1YM%3ew*v#F>W_PzYTliNz*tBb+%+U}B6zbPe@bZ2+qXk-u%h>s6NW5EC# z50d`ylu?BWja!a{%qgNwU;z-QTg%4~X%60!!FmsDwWdMSNgtaz>qZums>q#kpzMUKWrB{rqvh07Gj<7>F z)1dcBC&+*U{w$Q*UQ&&Dv+BkdL}t<6JZ+-s1exhCNw_AtlkTc*-Mo!$CGfxY!`GF_ z(bTcRm(GD8k>pDNea!OAk28GxcQ`+!L}!(PZ3ja8-T{~sO8seZ6l<tlUI#rDvC2Esz_@4Inc0HNycX_CXb5gY31L-S! zSkHB8TKC`e2sUfont+}wqWdbOtB#9gz|O(eej~$@=iaf z;E9=c{NV*Z`P-*4v7pFIx;tgN!^%9OmzV>vSf{;NLvaH$WA0>U^?+adfEeX0ZO{jv z(zE~A6Z1Q^QQh5d^@DDKBp&bH~fyI?V2f4X#zl7y>109lg8w$tiG*p=}L2_XRTGQV>Qk=k27N+QmYBQ zy4A=kdi-MS++&_`Fsw=rwz0CM0%lilE7}|@txj=IF?Um9FIZ1dt+DMijS0qcoTMVY z!?rg;i4LzP;m_!+ugVhz{3(b+l#PD~{0_iQW31xvhGeKYU9eSdp5-bjBNC{vqzNk_ zUtI}3u&vnkzWa(`o9hHjCmi)fau~`fXzwrvuWe74pq#4UEg5v&Haq^Y~xrLOWnEBe@Ta6yGr(9D8dMLwbUD zfFU`lNu?zORhU6q{GXM~;G=U&U6uj^$5Q3A4NZP3I%p)w7xZ6>_u?JMRWAIY)JpIJ z^V5*gOgy_THmy}|y0ft0xrkH4GWp7Jk(TV0w4eSR=gYdz+3{NFE`0T|4)QJ$|3@FT zW<2eHter?Q2ODR~m8f*xGLp*6m=Gh_8h#;1h4D0b6eb5(P1cd7C}LGTCs@TG|JpnW zw+S(s!UDI(u9|xM-&D%}E0)lCos=9_G|-(Z&0GQC9=36r7w>f1O^ ztgeY@RArT(OZ0B{(k>~$XgG#vvAekd?5Dbjmeu2fa9(N07cM2iQ0{3q?e;{KX-RKZx^?~P*XM* zqSLij{m`eM6s{HtV-)P;R+V;7^Cbb#j;`FSWZP@?Z;pcv{4O!8N80LexxON`2l$mt zGyWv~h#T`pI)?@;ro_hqTh^H3F|{L@sMR`58it!rnw2L3j;spjcjWDp)%lSk2-cW1 zr^7N);e&@ztsDdZA~r_|0A4_$zfG)^7)dU!34&SoXm53Sfx~4-y|f8_NMv&OBVDS~ zPWQ}Hm;PP4jQ(+1o$lLI&s^u+&fL}cyUHH+83uaCS9t!1^4|5{_0a3|=2#8fCJP+N zh+TLl7k+bjn|{yyEYo`z?Emi4cleCjymR-Cyp!wMdjU`eSfp=7g+b3sTjXHty|sc; zZXDJOcwq)Kj8Yn-us9Yg8p80C1%x8b31dcnqz|B@v_yf`O4Tm2l2U023ZW>UUR_V| z+w!+GejmM>akV<$Ou_}}?VdkA`@%ZTn#@(L7p|5ln4h&@dxg4F%Po{K)!Up8FLQ}q znYr~gxHthZmH4rbU5$j52edA?+2OAn>`%hv`M6g5yRf&K!GNL{TD4y15^|_aJVpKe z`@p0-2h#9raHXWne=y9>15kz)w{(XLW^v_Cz!8%OBF=>qAdQ+MIf9{^#$-MhOX3rh z=5|Cdr!=;@+sR1b%>9@-d29s*LB!qfJo?xF;RAW&0r-ulTFdTYZkiKw7hl@73PKCv zvar`w$;3~u5p8|2 zsc)b9^hy5B|LELSa%HGMH`=~Y zJ7|biO6t@}2+$?fM#IvDh62-lkv3pIXr3FnRLmbaU+yq>}sfW5AolK^JEtf$VB~$Crb4k)xCR1D2}Z1 z(WkXPl!;CuuORu3pRG5vQlb>9YA64pi?%JGd!(Ua5u=zQI8Sh4`_hrbj7fy~zC;{N zL~H9t@#N}BcN0H+6&&e1jq@@i0i)S!?{5Ar8zTSBZfhkl09sg6z|IdJ>D^b$Z=0IVI-3AkS#$CS#X49xK|9@A0ufiZq6$h zKF${&n8+m?Yw6QWibj$+kzqA^Ep+YRn-%ugeH-6q+mbX%PqP05Kfp$A(_54M{i+qC zmPvYFyRFkPH<`SQWW0Af=zUt#z#C9xM)Z(~DOtt?9gS8qB%mYAgY>>NuaD83#y;WD z{o24&;26jF)MPRnE3REG##l_oVb17KBwoNPwcUF0kzf&#z{lAB^u&9>tIncJ4DS$k z)TZMMN2k#i$%?XCK4`E7kx7LYRt!BsD;Bd;5&tuspS@1;!4yEN@bMa}f0J-%W1(c* zP*fv9IwFU;$`=5ZXFPAmp3KE&3Ca58Rcbi5e|P+m4g?Z4$ykim<)3v~;Lo>qUXarW zYhs$Ko!7#pLC#|W_LwGtwv}{Dj{nm~$lOpRPbKt(uyPWugnR^_gE}pC3~4;D|Hd9T zp#zVBn=qD!G+8F4{k|eck$RC}G|DkV$(#(&=lu>`^#oprMgfP_lW1y<6}Im~3ufsTIyMn~rJXpn2mw74ENPll)W(=v z``PAWB$1n+LE@5q;fJvQq+Kcg9|^?SKMLwymsT7)^Lhmd=R^D?z1M7?QQwt`8!q zdIjnvkm1ZyZ->p&?{Sl-9us-;o|-2;MRX!JI+_YW+*p6@jpUwWZ0f%T z$HfNW8ahb9{G-TIkBdEa2LMV;XbjU6OZK%0A=Hi~?NXat^7+jv-5;45UWsne$oQ&U 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%0Ad+sElm%R_1drtmmMm$&+`(Hln!p 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

?gK-EV-yQj~6b)}#FOD7N% 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%0DfA>~WeHnSnU!88d?bAQLreQ!J7Yu|0WZ(RSOv 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

9hA|w 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 Date: Fri, 7 Feb 2025 22:56:19 +0100 Subject: [PATCH 0533/1144] feat: changed color and added header --- .../Settings/MultiServiceContainerCard.tsx | 22 +++++++++---------- src/views/settings/Settings.tsx | 2 +- src/views/settings/SettingsMultiService.tsx | 2 +- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/components/Settings/MultiServiceContainerCard.tsx b/src/components/Settings/MultiServiceContainerCard.tsx index 1fb04e5b4..b415073c4 100644 --- a/src/components/Settings/MultiServiceContainerCard.tsx +++ b/src/components/Settings/MultiServiceContainerCard.tsx @@ -1,6 +1,6 @@ import React, { useEffect } from "react"; -import { View } from "react-native"; +import { Image, View } from "react-native"; import LottieView from "lottie-react-native"; import { NativeItem, NativeList, NativeText } from "../Global/NativeComponents"; import { LinearGradient } from "expo-linear-gradient"; @@ -25,16 +25,14 @@ const MultiServiceContainerCard = ({ theme }: { theme: any }) => { alignItems: "center", overflow: "hidden", }}> - Maybe une image ici, là j'attend l'avis des #designers - {/**/} + @@ -48,7 +46,7 @@ const MultiServiceContainerCard = ({ theme }: { theme: any }) => { Activer le multi service - + Rassemble tes services scolaires en un espace virtuel unique, géré par Papillon. diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index 5a41ab11d..3a841f8a2 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -208,7 +208,7 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { }, { icon: , - color: "#cb7712", + color: "#1f76ce", label: "Multiservice (Bêta)", description: "Connecte plusieurs services en un seul espace de travail", onPress: () => navigation.navigate("SettingsMultiService"), diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index fbb58c8b5..d4bea9f22 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -172,7 +172,7 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => leading={ } - color="#cb7712" + color="#1f76ce" /> } > From a081669a49f7cd1b8772ae9084d685be30458b23 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Fri, 7 Feb 2025 23:01:15 +0100 Subject: [PATCH 0534/1144] fix: header height --- src/components/Settings/MultiServiceContainerCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Settings/MultiServiceContainerCard.tsx b/src/components/Settings/MultiServiceContainerCard.tsx index b415073c4..bf52a13ca 100644 --- a/src/components/Settings/MultiServiceContainerCard.tsx +++ b/src/components/Settings/MultiServiceContainerCard.tsx @@ -20,7 +20,7 @@ const MultiServiceContainerCard = ({ theme }: { theme: any }) => { colors={["#cb7712", "#dec46d"]} > Date: Fri, 7 Feb 2025 23:02:16 +0100 Subject: [PATCH 0535/1144] fix: duplicated values in & account creation --- src/views/login/IdentityProvider/providers/UnivRennes1.tsx | 3 +-- src/views/login/IdentityProvider/providers/UnivRennes2.tsx | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/views/login/IdentityProvider/providers/UnivRennes1.tsx b/src/views/login/IdentityProvider/providers/UnivRennes1.tsx index b1140dc4f..f2ffa5b70 100644 --- a/src/views/login/IdentityProvider/providers/UnivRennes1.tsx +++ b/src/views/login/IdentityProvider/providers/UnivRennes1.tsx @@ -55,8 +55,7 @@ const UnivRennes1_Login: Screen<"UnivRennes1_Login"> = ({ navigation }) => { personalization: await defaultPersonalization(), identity: {}, - serviceData: {}, - providers: [] + serviceData: {} }; createStoredAccount(local_account); diff --git a/src/views/login/IdentityProvider/providers/UnivRennes2.tsx b/src/views/login/IdentityProvider/providers/UnivRennes2.tsx index 5a5c7f069..bed02f263 100644 --- a/src/views/login/IdentityProvider/providers/UnivRennes2.tsx +++ b/src/views/login/IdentityProvider/providers/UnivRennes2.tsx @@ -97,8 +97,7 @@ const UnivRennes2_Login: Screen<"UnivRennes2_Login"> = ({ navigation }) => { personalization: await defaultPersonalization(), identity: {}, - serviceData: {}, - providers: [] + serviceData: {} }; createStoredAccount(local_account); From ef973d8bac0ecb2403600f10145d2a6660d8fabc Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Fri, 7 Feb 2025 23:03:08 +0100 Subject: [PATCH 0536/1144] fix(ts): in Menu.tsx --- src/views/account/Restaurant/Menu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 39264772d..9323c5926 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -601,7 +601,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { )} : <> - {allBalances?.length > 0 && ( + {(allBalances?.length || 0) > 0 && ( Date: Sat, 8 Feb 2025 10:54:31 +0100 Subject: [PATCH 0537/1144] fix: colors --- src/components/Settings/MultiServiceContainerCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Settings/MultiServiceContainerCard.tsx b/src/components/Settings/MultiServiceContainerCard.tsx index bf52a13ca..2c1f0baaa 100644 --- a/src/components/Settings/MultiServiceContainerCard.tsx +++ b/src/components/Settings/MultiServiceContainerCard.tsx @@ -17,7 +17,7 @@ const MultiServiceContainerCard = ({ theme }: { theme: any }) => { return ( Date: Sat, 8 Feb 2025 11:09:09 +0100 Subject: [PATCH 0538/1144] fix(ts): correct typing for `Week.tsx` --- src/views/account/Week/Week.tsx | 42 ++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index cdb653b11..d040da66c 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -1,22 +1,24 @@ import * as React from "react"; import { memo, useCallback, useMemo, useEffect } from "react"; import { Image, Linking, StyleSheet, Text, TouchableOpacity, View } from "react-native"; -import CalendarKit from "@howljs/calendar-kit"; +import CalendarKit, { PackedAllDayEvent, PackedEvent, HeaderItemProps as CalendarKitHeaderItemProps, EventItem as CalendarKitEventItem } from "@howljs/calendar-kit"; import { useCurrentAccount } from "@/stores/account"; import { useTimetableStore } from "@/stores/timetable"; import { useTheme } from "@react-navigation/native"; import { updateTimetableForWeekInCache } from "@/services/timetable"; import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import PapillonPicker from "@/components/Global/PapillonPicker"; +import PapillonPicker, {PickerDataItem} from "@/components/Global/PapillonPicker"; import { AlertTriangle, CalendarDays } from "lucide-react-native"; import { PapillonHeaderAction } from "@/components/Global/PapillonModernHeader"; import { getSubjectData } from "@/services/shared/Subject"; import { PapillonNavigation } from "@/router/refs"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; -import { TimetableClassStatus } from "@/services/shared/Timetable"; +import {TimetableClass, TimetableClassStatus} from "@/services/shared/Timetable"; import { NativeText } from "@/components/Global/NativeComponents"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; +import type { Screen } from "@/router/helpers/types"; +import {Account} from "@/stores/account/types"; const LOCALES = { en: { @@ -31,7 +33,11 @@ const LOCALES = { }, } as const; -const EventItem = memo(({ event }) => { +interface EventItemProps { + event: PackedEvent | PackedAllDayEvent +} + +const EventItem = memo(({ event }) => { const theme = useTheme(); const subjectData = useMemo( @@ -112,7 +118,11 @@ const EventItem = memo(({ event }) => { ); }); -const HeaderItem = memo(({ header }) => { +interface HeaderItemProps { + header: CalendarKitHeaderItemProps +} + +const HeaderItem = memo(({ header }) => { const theme = useTheme(); const cols = header.extra.columns; @@ -202,7 +212,7 @@ const HeaderItem = memo(({ header }) => { const displayModes = ["Semaine", "3 jours", "Journée"]; -const Week = ({ route, navigation }) => { +const Week: Screen<"Week"> = ({ route, navigation }) => { const theme = useTheme(); const insets = useSafeAreaInsets(); @@ -228,23 +238,23 @@ const Week = ({ route, navigation }) => { } }), [theme.colors]); - const [events, setEvents] = React.useState([]); + const [events, setEvents] = React.useState([]); useEffect(() => { const nevts = Object.values(timetables) .flat() .map(event => ({ - id: event.id, + id: event.id.toString(), title: event.title, - start: { dateTime: new Date(event.startTimestamp) }, - end: { dateTime: new Date(event.endTimestamp) }, + start: { dateTime: new Date(event.startTimestamp).toString() }, + end: { dateTime: new Date(event.endTimestamp).toString() }, event: event, })); setEvents(nevts); }, [timetables]); - const loadTimetableWeek = useCallback(async (weekNumber, force = false) => { + const loadTimetableWeek = useCallback(async (weekNumber: number, force = false) => { if (!force) { if (timetables[weekNumber]) return; } @@ -252,14 +262,14 @@ const Week = ({ route, navigation }) => { setIsLoading(true); requestAnimationFrame(async () => { try { - await updateTimetableForWeekInCache(account, weekNumber, force); + await updateTimetableForWeekInCache(account as Account, weekNumber, force); } finally { setIsLoading(false); } }); }, [account, timetables]); - const handleDateChange = useCallback(async (date) => { + const handleDateChange = useCallback(async (date: string) => { const weekNumber = dateToEpochWeekNumber(new Date(date)); await loadTimetableWeek(weekNumber); }, [loadTimetableWeek]); @@ -267,7 +277,7 @@ const Week = ({ route, navigation }) => { const [openedIcalModal, setOpenedIcalModal] = React.useState(false); React.useEffect(() => { - if(events.length === 0 && account?.personalization?.icalURLs?.length > 0) { + if(events.length === 0 && (account?.personalization?.icalURLs?.length || 0) > 0) { setIsLoading(true); requestAnimationFrame(async () => { const weekNumber = dateToEpochWeekNumber(new Date()); @@ -287,7 +297,7 @@ const Week = ({ route, navigation }) => { /> )} - {account.providers?.includes("ical") && Object.values(timetables).flat().length === 0 && ( + {account?.providers?.includes("ical") && Object.values(timetables).flat().length === 0 && ( { direction="left" delay={0} selected={displayMode} - onSelectionChange={(mode) => { + onSelectionChange={(mode: string) => { setIsLoading(true); requestAnimationFrame(() => { From 007f00d830f38e9644fa726c93e9cb6bab9917f9 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 8 Feb 2025 11:11:16 +0100 Subject: [PATCH 0539/1144] fix(ts): missing property for timetable store in `store/timetable/types.ts` --- src/stores/timetable/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stores/timetable/types.ts b/src/stores/timetable/types.ts index f41f97ae7..05f60c8de 100644 --- a/src/stores/timetable/types.ts +++ b/src/stores/timetable/types.ts @@ -3,6 +3,7 @@ import type { Timetable } from "@/services/shared/Timetable"; export interface TimetableStore { timetables: Record, updateClasses: (epochWeekNumber: number, classes: Timetable) => void, + injectClasses: (data: any) => void, removeClasses: (epochWeekNumber: number) => void, removeClassesFromSource: (source: string) => void } From bf31e298de24d74f900149de24a4a4ea85d770a4 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 8 Feb 2025 11:15:28 +0100 Subject: [PATCH 0540/1144] fix: linter fixes --- src/views/account/Week/Week.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index d040da66c..901e25bd1 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -8,13 +8,13 @@ import { useTheme } from "@react-navigation/native"; import { updateTimetableForWeekInCache } from "@/services/timetable"; import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import PapillonPicker, {PickerDataItem} from "@/components/Global/PapillonPicker"; +import PapillonPicker from "@/components/Global/PapillonPicker"; import { AlertTriangle, CalendarDays } from "lucide-react-native"; import { PapillonHeaderAction } from "@/components/Global/PapillonModernHeader"; import { getSubjectData } from "@/services/shared/Subject"; import { PapillonNavigation } from "@/router/refs"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; -import {TimetableClass, TimetableClassStatus} from "@/services/shared/Timetable"; +import { TimetableClassStatus} from "@/services/shared/Timetable"; import { NativeText } from "@/components/Global/NativeComponents"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import type { Screen } from "@/router/helpers/types"; From 1a8cbb95a83ac0dde806aca69dd8091ab362d433 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 8 Feb 2025 11:15:57 +0100 Subject: [PATCH 0541/1144] fix: correct typing for multiservice `getFeatureAccount` function --- src/utils/multiservice/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/multiservice/index.ts b/src/utils/multiservice/index.ts index 71e2947b1..eba0f0c8a 100644 --- a/src/utils/multiservice/index.ts +++ b/src/utils/multiservice/index.ts @@ -5,5 +5,5 @@ import {PrimaryAccount} from "@/stores/account/types"; export function getFeatureAccount (feature: MultiServiceFeature, spaceLocalID: string) { const accountId = useMultiService.getState().getFeatureAccountId(feature, spaceLocalID); - return useCurrentAccount.getState().associatedAccounts.find(account => account.localID === accountId) as PrimaryAccount; + return useCurrentAccount.getState().associatedAccounts.find(account => account.localID === accountId) as PrimaryAccount | undefined; } From 7a667f9543e3e6567809d6d8e582e1da2c50d424 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 8 Feb 2025 12:05:27 +0100 Subject: [PATCH 0542/1144] fix: reset cache if no service si set for attendance --- src/services/attendance.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/services/attendance.ts b/src/services/attendance.ts index b02cdb015..b334c63a0 100644 --- a/src/services/attendance.ts +++ b/src/services/attendance.ts @@ -137,6 +137,12 @@ export async function updateAttendanceInCache (account: T, p const service = getFeatureAccount(MultiServiceFeature.Attendance, account.localID); if (!service) { log("No service set in multi-service space for feature \"Attendance\"", "multiservice"); + attendance = { + delays: [], + absences: [], + punishments: [], + observations: [] + }; break; } return updateAttendanceInCache(service, periodName); From 5cc7fc835a00c1173c7f5aa0eb5971bbb90d4cf8 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 8 Feb 2025 12:06:14 +0100 Subject: [PATCH 0543/1144] fix: added function to know if a service has been setup in a multi service space --- src/utils/multiservice/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/utils/multiservice/index.ts b/src/utils/multiservice/index.ts index eba0f0c8a..a865a52a9 100644 --- a/src/utils/multiservice/index.ts +++ b/src/utils/multiservice/index.ts @@ -7,3 +7,7 @@ export function getFeatureAccount (feature: MultiServiceFeature, spaceLocalID: s const accountId = useMultiService.getState().getFeatureAccountId(feature, spaceLocalID); return useCurrentAccount.getState().associatedAccounts.find(account => account.localID === accountId) as PrimaryAccount | undefined; } + +export function hasFeatureAccountSetup (feature: MultiServiceFeature, spaceLocalID: string) { + return useMultiService.getState().getFeatureAccountId(feature, spaceLocalID) != undefined; +} From 414f69d98af4540f0662dce672db9b14bc0e19d6 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 8 Feb 2025 12:07:30 +0100 Subject: [PATCH 0544/1144] fix: proper type and behaviour for multi service store --- src/stores/multiService/index.ts | 2 +- src/stores/multiService/types.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stores/multiService/index.ts b/src/stores/multiService/index.ts index 95792e78e..6653263a4 100644 --- a/src/stores/multiService/index.ts +++ b/src/stores/multiService/index.ts @@ -79,7 +79,7 @@ export const useMultiService = create()( if (!space) return; let mutatedFeatureServices = space.featuresServices; - mutatedFeatureServices[feature] = account.localID; + mutatedFeatureServices[feature] = account?.localID; const spaceMutated: MultiServiceSpace = { ...space, diff --git a/src/stores/multiService/types.ts b/src/stores/multiService/types.ts index 42795b55a..156e2cfd8 100644 --- a/src/stores/multiService/types.ts +++ b/src/stores/multiService/types.ts @@ -29,6 +29,6 @@ export interface MultiServiceStore { remove: (localID: string) => void update: (localID: string, key: T, value: A[T]) => void toggleEnabledState: () => void - setFeatureAccount: (spaceLocalID: string, feature: MultiServiceFeature, account: PrimaryAccount) => void + setFeatureAccount: (spaceLocalID: string, feature: MultiServiceFeature, account: PrimaryAccount | undefined) => void getFeatureAccountId: (feature: MultiServiceFeature, spaceLocalID: string) => string | undefined } From 1bef77728e20308ac93b9433b525f5c197e3fcd8 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 8 Feb 2025 12:09:33 +0100 Subject: [PATCH 0545/1144] feat: added possibility to deleted linked service --- .../settings/SettingsMultiServiceSpace.tsx | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/views/settings/SettingsMultiServiceSpace.tsx b/src/views/settings/SettingsMultiServiceSpace.tsx index da6f35678..364ca1073 100644 --- a/src/views/settings/SettingsMultiServiceSpace.tsx +++ b/src/views/settings/SettingsMultiServiceSpace.tsx @@ -25,6 +25,7 @@ import Reanimated, {FadeOut, ZoomIn} from "react-native-reanimated"; import PapillonBottomSheet from "@/components/Modals/PapillonBottomSheet"; import * as Haptics from "expo-haptics"; import AccountItem from "@/components/Global/AccountItem"; +import ButtonCta from "@/components/FirstInstallation/ButtonCta"; const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ navigation, route }) => { const theme = useTheme(); @@ -104,11 +105,23 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga setAccountSelectorOpened(true); }; - const setAccountFeature = (account: PrimaryAccount, feature: MultiServiceFeature) => { + const setAccountFeature = (account: PrimaryAccount | undefined, feature: MultiServiceFeature) => { + const currentSelectedAccountID = space.featuresServices[feature]; setMultiServiceSpaceAccountFeature(space.accountLocalID, feature, account); - let linkedAccountsIds = [...(linkedAccount?.associatedAccountsLocalIDs || []), account.localID]; - linkedAccountsIds = linkedAccountsIds.filter((value, index) => linkedAccountsIds.indexOf(value) === index); // Remove duplicates - // Putting the space's associated accounts ids in linkedExternalLocalIDs permits the reload of their instance / authentication fields (like externals accounts) + let linkedAccountsIds = [...(linkedAccount?.associatedAccountsLocalIDs || [])]; + if (account) { + // Putting the space's associated accounts ids in linkedExternalLocalIDs permits the reload of their instance / authentication fields (like externals accounts) + linkedAccountsIds.push(account.localID); + linkedAccountsIds = linkedAccountsIds.filter((value, index) => linkedAccountsIds.indexOf(value) === index); // Remove duplicates + } else { + // If feature's service has been removed and service is not assigned to other feature, we remove it from "associatedAccountsLocalIDs" + const accountNoMoreUsed = !Object.keys(space.featuresServices).some(key => + (space.featuresServices[key as MultiServiceFeature] == currentSelectedAccountID && !((key as MultiServiceFeature) === feature)) + ); + if (accountNoMoreUsed) { + linkedAccountsIds = linkedAccountsIds.filter(localID => localID != currentSelectedAccountID); + } + } // @ts-expect-error accounts.update(space.accountLocalID, "associatedAccountsLocalIDs", linkedAccountsIds); setAccountSelectorOpened(false); @@ -423,7 +436,16 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga /> ))} + {/*setAccountFeature*/} + setAccountFeature(undefined, featureSelection)} + /> From b1e12aaf267ae0a7bd26af637fe757c9070a4086 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 8 Feb 2025 12:26:39 +0100 Subject: [PATCH 0546/1144] fix: no service linked messages --- src/views/account/Attendance/Attendance.tsx | 49 +++++++++++++-------- src/views/account/Chat/Messages.tsx | 15 ++++++- src/views/account/Evaluation/Evaluation.tsx | 14 +++++- src/views/account/Grades/Grades.tsx | 14 +++++- src/views/account/Homeworks/Homeworks.tsx | 22 ++++++--- src/views/account/Lessons/Atoms/Page.tsx | 14 ++++-- src/views/account/Lessons/Lessons.tsx | 5 +++ src/views/account/News/News.tsx | 13 +++++- 8 files changed, 112 insertions(+), 34 deletions(-) diff --git a/src/views/account/Attendance/Attendance.tsx b/src/views/account/Attendance/Attendance.tsx index c2f376b26..4ea688348 100644 --- a/src/views/account/Attendance/Attendance.tsx +++ b/src/views/account/Attendance/Attendance.tsx @@ -1,36 +1,38 @@ -import { useTheme } from "@react-navigation/native"; -import { useEffect, useMemo, useState } from "react"; -import { View, ActivityIndicator, Platform, RefreshControl } from "react-native"; - -import type { Screen } from "@/router/helpers/types"; -import { useCurrentAccount } from "@/stores/account"; -import { useAttendanceStore } from "@/stores/attendance"; -import { updateAttendanceInCache, updateAttendancePeriodsInCache } from "@/services/attendance"; -import { NativeText } from "@/components/Global/NativeComponents"; -import Reanimated, { FadeIn, FadeOut, LinearTransition } from "react-native-reanimated"; +import {useTheme} from "@react-navigation/native"; +import React, {useEffect, useMemo, useState} from "react"; +import {ActivityIndicator, Platform, RefreshControl, View} from "react-native"; + +import type {Screen} from "@/router/helpers/types"; +import {useCurrentAccount} from "@/stores/account"; +import {useAttendanceStore} from "@/stores/attendance"; +import {updateAttendanceInCache, updateAttendancePeriodsInCache} from "@/services/attendance"; +import {NativeText} from "@/components/Global/NativeComponents"; +import Reanimated, {FadeIn, FadeOut, LinearTransition} from "react-native-reanimated"; import PapillonPicker from "@/components/Global/PapillonPicker"; -import { ChevronDown, Eye, Scale, Timer, UserX } from "lucide-react-native"; +import {ChevronDown, Eye, Scale, Timer, UserX} from "lucide-react-native"; import PapillonHeader from "@/components/Global/PapillonHeader"; -import { animPapillon } from "@/utils/ui/animations"; +import {animPapillon} from "@/utils/ui/animations"; import AttendanceItem from "./Atoms/AttendanceItem"; -import { getAbsenceTime } from "@/utils/format/attendance_time"; +import {getAbsenceTime} from "@/utils/format/attendance_time"; import TotalMissed from "./Atoms/TotalMissed"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; -import { protectScreenComponent } from "@/router/helpers/protected-screen"; -import { Observation } from "@/services/shared/Observation"; +import {protectScreenComponent} from "@/router/helpers/protected-screen"; +import {Observation} from "@/services/shared/Observation"; import MissingItem from "@/components/Global/MissingItem"; -import React from "react"; +import {hasFeatureAccountSetup} from "@/utils/multiservice"; +import {MultiServiceFeature} from "@/stores/multiService/types"; +import {AccountService} from "@/stores/account/types"; const Attendance: Screen<"Attendance"> = ({ route, navigation }) => { const theme = useTheme(); const account = useCurrentAccount(store => store.account!); + const hasServiceSetup = account.service === AccountService.PapillonMultiService ? hasFeatureAccountSetup(MultiServiceFeature.Attendance, account.localID) : true; + const defaultPeriod = useAttendanceStore(store => store.defaultPeriod); const periods = useAttendanceStore(store => store.periods); const attendances = useAttendanceStore(store => store.attendances); - - const [isRefreshing, setIsRefreshing] = useState(false); const [isLoading, setLoading] = useState(true); @@ -242,7 +244,7 @@ const Attendance: Screen<"Attendance"> = ({ route, navigation }) => { /> } > - {attendances[selectedPeriod] && attendances[selectedPeriod].absences.length === 0 && attendances[selectedPeriod].delays.length === 0 && attendances[selectedPeriod].punishments.length === 0 && Object.keys(attendances_observations_details).length === 0 &&( + {hasServiceSetup && attendances[selectedPeriod] && attendances[selectedPeriod].absences.length === 0 && attendances[selectedPeriod].delays.length === 0 && attendances[selectedPeriod].punishments.length === 0 && Object.keys(attendances_observations_details).length === 0 &&( = ({ route, navigation }) => { /> )} + {!hasServiceSetup && ( + + )} + {(totalMissed.total.hours > 0 || totalMissed.total.minutes > 0) && ( )} diff --git a/src/views/account/Chat/Messages.tsx b/src/views/account/Chat/Messages.tsx index 6cdce0b23..523843472 100644 --- a/src/views/account/Chat/Messages.tsx +++ b/src/views/account/Chat/Messages.tsx @@ -35,6 +35,8 @@ import { SquarePen } from "lucide-react-native"; import { TouchableOpacity } from "react-native-gesture-handler"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; import { TabLocation } from "pawnote"; +import {hasFeatureAccountSetup} from "@/utils/multiservice"; +import {MultiServiceFeature} from "@/stores/multiService/types"; // Voir la documentation de `react-navigation`. // @@ -51,6 +53,7 @@ const Discussions: Screen<"Discussions"> = ({ navigation, route }) => { const { colors } = theme; const account = useCurrentAccount((state) => state.account!); + const hasServiceSetup = account.service === AccountService.PapillonMultiService ? hasFeatureAccountSetup(MultiServiceFeature.Chats, account.localID) : true; const [chats, setChats] = useState(null); const [refreshing, setRefreshing] = useState(false); @@ -122,7 +125,7 @@ const Discussions: Screen<"Discussions"> = ({ navigation, route }) => { padding: 20, }} > - {!supported ? ( + {hasServiceSetup && !supported ? ( = ({ navigation, route }) => { exiting={animPapillon(FadeOut)} style={{ paddingVertical: 26 }} /> - ) : !enabled && ( + ) : hasServiceSetup && !enabled && ( = ({ navigation, route }) => { style={{ paddingVertical: 26 }} /> )} + {!hasServiceSetup && ( + + )} ) : ( = ({ route, navigation }) => { const theme = useTheme(); @@ -23,6 +26,7 @@ const Evaluation: Screen<"Evaluation"> = ({ route, navigation }) => { const outsideNav = route.params?.outsideNav; const account = useCurrentAccount((store) => store.account!); + const hasServiceSetup = account.service === AccountService.PapillonMultiService ? hasFeatureAccountSetup(MultiServiceFeature.Evaluations, account.localID) : true; const defaultPeriod = useEvaluationStore((store) => store.defaultPeriod); const periods = useEvaluationStore((store) => store.periods); const evaluations = useEvaluationStore((store) => store.evaluations); @@ -174,7 +178,7 @@ const Evaluation: Screen<"Evaluation"> = ({ route, navigation }) => { > {(!evaluations[selectedPeriod] || evaluations[selectedPeriod].length === 0) && !isLoading && - !isRefreshing && ( + !isRefreshing && hasServiceSetup && ( = ({ route, navigation }) => { description="La période sélectionnée ne contient aucune compétence." /> )} + {!hasServiceSetup && ( + + )} {latestEvaluationsRef.current.length > 2 && ( import("./Graph/GradesAverage")); const GradesLatestList = lazy(() => import("./Latest/LatestGrades")); @@ -45,6 +47,7 @@ const Grades: Screen<"Grades"> = ({ route, navigation }) => { const outsideNav = route.params?.outsideNav; const account = useCurrentAccount((store) => store.account!); + const hasServiceSetup = account.service === AccountService.PapillonMultiService ? hasFeatureAccountSetup(MultiServiceFeature.Grades, account.localID) : true; const defaultPeriod = useGradesStore((store) => store.defaultPeriod); const periods = useGradesStore((store) => store.periods); const averages = useGradesStore((store) => store.averages); @@ -203,7 +206,7 @@ const Grades: Screen<"Grades"> = ({ route, navigation }) => { > {(!grades[selectedPeriod] || grades[selectedPeriod].length === 0) && !isLoading && - !isRefreshing && ( + !isRefreshing && hasServiceSetup && ( = ({ route, navigation }) => { /> )} + {!hasServiceSetup && ( + + )} + {grades[selectedPeriod] && grades[selectedPeriod].length > 1 && ( = ({ route, navigation }) => { const theme = useTheme(); const account = useCurrentAccount(store => store.account!); + const hasServiceSetup = account.service === AccountService.PapillonMultiService ? hasFeatureAccountSetup(MultiServiceFeature.Homeworks, account.localID) : true; const homeworks = useHomeworkStore(store => store.homeworks); // @ts-expect-error @@ -301,12 +304,17 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { title="Il ne reste rien à faire" description="Il n'y a aucun devoir non terminé pour cette semaine." /> - : - } + : hasServiceSetup ? + + : } } diff --git a/src/views/account/Lessons/Atoms/Page.tsx b/src/views/account/Lessons/Atoms/Page.tsx index 68369f4eb..1502e1466 100644 --- a/src/views/account/Lessons/Atoms/Page.tsx +++ b/src/views/account/Lessons/Atoms/Page.tsx @@ -33,9 +33,10 @@ interface PageProps { paddingTop: number refreshAction: () => unknown weekExists: boolean + hasServiceSetup: boolean } -export const Page = ({ day, date, current, paddingTop, refreshAction, loading, weekExists }: PageProps) => { +export const Page = ({ day, date, current, paddingTop, refreshAction, loading, weekExists, hasServiceSetup }: PageProps) => { return ( )} - {day && day.length === 0 && current && !loading && ( + {hasServiceSetup && day && day.length === 0 && current && !loading && ( weekExists && (new Date(date).getDay() == 6 || new Date(date).getDay() == 0) ? ( : <> )} + + {!hasServiceSetup && } ); }; diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index 09134d1ba..1e31512e6 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -32,9 +32,13 @@ import { import PapillonPicker from "@/components/Global/PapillonPicker"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { WeekFrequency } from "@/services/shared/Timetable"; +import {AccountService} from "@/stores/account/types"; +import {hasFeatureAccountSetup} from "@/utils/multiservice"; +import {MultiServiceFeature} from "@/stores/multiService/types"; const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const account = useCurrentAccount((store) => store.account!); + const hasServiceSetup = account.service === AccountService.PapillonMultiService ? hasFeatureAccountSetup(MultiServiceFeature.Timetable, account.localID) : true; const mutateProperty = useCurrentAccount((store) => store.mutateProperty); const timetables = useTimetableStore((store) => store.timetables); @@ -153,6 +157,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { return ( & { date: string, important: boolean }; const NewsScreen: Screen<"News"> = ({ route, navigation }) => { const theme = useTheme(); const account = useCurrentAccount((store) => store.account!); + const hasServiceSetup = account.service === AccountService.PapillonMultiService ? hasFeatureAccountSetup(MultiServiceFeature.News, account.localID) : true; const informations = useNewsStore((store) => store.informations); const [isLoading, setIsLoading] = useState(false); @@ -179,7 +182,15 @@ const NewsScreen: Screen<"News"> = ({ route, navigation }) => { )} - {!isLoading && !hasNews && } + {hasServiceSetup ? + !isLoading && !hasNews && + : + + } ); }; From 318c81471617e4dd96c577184db32bf3b45d7f5a Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 8 Feb 2025 13:53:11 +0100 Subject: [PATCH 0547/1144] fix: update of instance key for MultiService accounts --- src/stores/account/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index b63f7f366..de3ada5a3 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -140,7 +140,7 @@ export const useCurrentAccount = create()((set, get) => ({ // Setting instance to a non-null value after associated accounts reload, to keep the loading icon while instances are reloading... if (account.service === AccountService.PapillonMultiService) - account.instance = "PapillonPrime"; // A random string, so the instance is not "undefined" or "null", to prevent creating infinite loading (an undefined instance is interpreted as a loading or disconnected account...) + get().mutateProperty("instance", "PapillonPrime"); // A random string, so the instance is not "undefined" or "null", to prevent creating infinite loading (an undefined instance is interpreted as a loading or disconnected account...) log("reloaded all external and associated accounts", "[switchTo]"); From ff130bad023c620ef8d711d243402bf5ee2dd404 Mon Sep 17 00:00:00 2001 From: Gabriel29306 <73659505+Gabriel29306@users.noreply.github.com> Date: Sat, 8 Feb 2025 14:03:45 +0100 Subject: [PATCH 0548/1144] fix(homework): bad detection of tag when newline is used c'est vraiment un bug con ;) --- src/utils/format/format_pronote_homeworks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/format/format_pronote_homeworks.ts b/src/utils/format/format_pronote_homeworks.ts index d4a3353b9..850f27ad2 100644 --- a/src/utils/format/format_pronote_homeworks.ts +++ b/src/utils/format/format_pronote_homeworks.ts @@ -10,7 +10,7 @@ function parse_homeworks (content: string): string { const ignoredTags: Set = new Set(["div", "span", "style", "script", "footer", "header"]); - const tagRegex = /<\/?([a-zA-Z]+)(?:\s[^>]*)?>|([^<]+)/g; + const tagRegex = /<\/?([a-zA-Z]+)(?:\s[^>]*)?>|([^<]+)/gm; const htmlEntities: Record = { " ": " ", From 547b17d4cae72c609e26a11338310b46976e6fab Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 8 Feb 2025 15:20:13 +0100 Subject: [PATCH 0549/1144] =?UTF-8?q?fix:=20ic=C3=B4ne=20illisible=20sur?= =?UTF-8?q?=20Samsung=20+=20Erreur=20son=20sur=20iOS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/Notifications.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index 031a8ba17..2cb982bef 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -1,5 +1,6 @@ import { alertExpoGo, isExpoGo } from "@/utils/native/expoGoAlert"; import notifee, { + AndroidColor, AndroidImportance, AuthorizationStatus, Notification, @@ -130,7 +131,7 @@ const papillonNotify = async ( showTimestamp: channelId !== "Status" ? true : false, showChronometer: channelId === "Status" ? true : false, smallIcon: "@mipmap/ic_launcher_foreground", - color: "#32AB8E", + color: AndroidColor.GREEN, pressAction: { id: "default", launchActivity: "default", @@ -139,8 +140,7 @@ const papillonNotify = async ( ios: { threadId: channelId, badgeCount, - sound: channelId !== "Status" ? "default" : undefined, - // à intégrer => `categoryId` + sound: channelId !== "Status" ? "default" : "", } }); }; From 8393d349469bb4c038781f0409ed1ccf5fe50117 Mon Sep 17 00:00:00 2001 From: Tryon <68423470+tryon-dev@users.noreply.github.com> Date: Sat, 8 Feb 2025 21:09:30 +0100 Subject: [PATCH 0550/1144] Update GradeReaction.tsx --- src/views/account/Grades/Modals/GradeReaction.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Grades/Modals/GradeReaction.tsx b/src/views/account/Grades/Modals/GradeReaction.tsx index b72dc19a7..24103a474 100644 --- a/src/views/account/Grades/Modals/GradeReaction.tsx +++ b/src/views/account/Grades/Modals/GradeReaction.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useLayoutEffect, useRef, useState } from "react"; import { Text, View, Linking, StyleSheet, TouchableOpacity, Image, Alert } from "react-native"; -import { CameraView, useCameraPermissions, PermissionStatus } from "expo-camera";main +import { CameraView, useCameraPermissions, PermissionStatus } from "expo-camera"; import * as MediaLibrary from "expo-media-library"; import { Check, X } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; From 511a2cc35b33660a97dc71bc9fb9066b7e286e1f Mon Sep 17 00:00:00 2001 From: Tryon Date: Sat, 8 Feb 2025 21:27:40 +0100 Subject: [PATCH 0551/1144] fix(Cantine): Remplacer BlurView par View --- src/views/account/Restaurant/Menu.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 39264772d..9e821099f 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -51,7 +51,6 @@ import AccountButton from "@/components/Restaurant/AccountButton"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; import PapillonHeader from "@/components/Global/PapillonHeader"; import { PressableScale } from "react-native-pressable-scale"; -import { BlurView } from "expo-blur"; import { ChevronLeft, ChevronRight} from "lucide-react-native"; import DrawableImportRestaurant from "@/components/Drawables/DrawableImportRestaurant"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; @@ -417,20 +416,19 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { }} activeScale={0.8} > - - + setShowDatePicker(true)}> @@ -462,7 +460,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { }} activeScale={0.8} > - = ({ route, navigation }) => { color={theme.colors.text} strokeWidth={2.5} /> - + From e1374ed56eef5d447210e9c24643c4f7c40e81fd Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 8 Feb 2025 21:42:20 +0100 Subject: [PATCH 0552/1144] fix: @Kgeek33 suggestion for fluidity Co-authored-by: Kgeek33 --- src/views/account/Attendance/Attendance.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Attendance/Attendance.tsx b/src/views/account/Attendance/Attendance.tsx index 4ea688348..4fc80816c 100644 --- a/src/views/account/Attendance/Attendance.tsx +++ b/src/views/account/Attendance/Attendance.tsx @@ -34,7 +34,7 @@ const Attendance: Screen<"Attendance"> = ({ route, navigation }) => { const attendances = useAttendanceStore(store => store.attendances); const [isRefreshing, setIsRefreshing] = useState(false); - const [isLoading, setLoading] = useState(true); + const [isLoading, setLoading] = useState(hasServiceSetup); const [userSelectedPeriod, setUserSelectedPeriod] = useState(null); const selectedPeriod = useMemo(() => userSelectedPeriod ?? defaultPeriod, [userSelectedPeriod, defaultPeriod]); From c17bb71a35757f6960b35368e2587899a266e8c0 Mon Sep 17 00:00:00 2001 From: Tryon Date: Sat, 8 Feb 2025 21:43:15 +0100 Subject: [PATCH 0553/1144] fix: nettoyage du code dans GetDefaultProfilePicture et SettingsProfile --- src/stores/account/types.ts | 1 + src/utils/GetRessources/GetDefaultProfilePicture.tsx | 10 +++++----- src/views/settings/SettingsProfile.tsx | 8 ++++---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/stores/account/types.ts b/src/stores/account/types.ts index 354be129c..bb0f98c91 100644 --- a/src/stores/account/types.ts +++ b/src/stores/account/types.ts @@ -151,6 +151,7 @@ export interface PronoteAccount extends BaseAccount { } export interface EcoleDirecteAccount extends BaseAccount { + profilePictureURL: string; service: AccountService.EcoleDirecte; instance: {}; authentication: { diff --git a/src/utils/GetRessources/GetDefaultProfilePicture.tsx b/src/utils/GetRessources/GetDefaultProfilePicture.tsx index e02f873c1..30bb6ed6c 100644 --- a/src/utils/GetRessources/GetDefaultProfilePicture.tsx +++ b/src/utils/GetRessources/GetDefaultProfilePicture.tsx @@ -1,8 +1,8 @@ import downloadAsBase64 from "@/utils/external/download-as-base64"; -import { AccountService, PronoteAccount, SkolengoAccount, EcoleDirecteAccount } from "@/stores/account/types"; +import { AccountService, PronoteAccount, SkolengoAccount, EcoleDirecteAccount } from "@/stores/account/types"; // Depending on your account type, download the default profile photo -export async function getDefaultProfilePicture( +export async function getDefaultProfilePicture ( account: PronoteAccount | SkolengoAccount | EcoleDirecteAccount): Promise { try { switch (account.service) { @@ -19,9 +19,9 @@ export async function getDefaultProfilePicture( case AccountService.EcoleDirecte: return account.profilePictureURL ? await downloadAsBase64(account.profilePictureURL, { - Referer: ".ecoledirecte.com/", - Pragma: "no-cache", - }) + Referer: ".ecoledirecte.com/", + Pragma: "no-cache", + }) : undefined; default: diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index 9634be585..8d18dd1c5 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -56,7 +56,7 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { // Call up the function to obtain the profile picture const img = await getDefaultProfilePicture(account); - + // If the image is undefined, an alert is displayed with an error message if (!img) { if (Platform.OS === "ios") { @@ -86,9 +86,9 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { profilePictureB64: img, }); } - + setLoadingPic(false); - }; + }; const updateProfilePic = async () => { setLoadingPic(true); @@ -218,7 +218,7 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { )} - + {profilePic && ( Date: Sat, 8 Feb 2025 21:43:21 +0100 Subject: [PATCH 0554/1144] Discard changes to ios/Papillon/Info.plist --- ios/Papillon/Info.plist | 113 +--------------------------------------- 1 file changed, 1 insertion(+), 112 deletions(-) diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index 0a3b0a695..04c938763 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -1,116 +1,6 @@ -<<<<<<< HEAD - - CADisableMinimumFrameDurationOnPhone - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - Papillon - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 7.8.2 - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleURLSchemes - - papillon - izly - - - - CFBundleVersion - 1 - ITSAppUsesNonExemptEncryption - - LSApplicationQueriesSchemes - - fb - instagram - twitter - tiktoksharesdk - - LSRequiresIPhoneOS - - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - NSAllowsLocalNetworking - - - NSCameraUsageDescription - Papillon utilise ta caméra pour scanner des QR-codes pour te connecter, pour capturer des documents, ou pour des fonctionnalités amusantes telles que les réactions. - NSLocationAlwaysAndWhenInUseUsageDescription - Autoriser $(PRODUCT_NAME) à utiliser ta localisation. - NSLocationAlwaysUsageDescription - Autoriser $(PRODUCT_NAME) à utiliser ta localisation. - NSLocationWhenInUseUsageDescription - Papillon utilise ton emplacement pour trouver les établissements autour de toi. - NSMicrophoneUsageDescription - Papillon utilise ton micro pour enregistrer des travaux audio ou des cours. - NSMotionUsageDescription - Papillon utilise les capteurs de mouvement de ton appareil pour de nombreuses fonctionnalités interactives. - NSPhotoLibraryAddUsageDescription - Allow $(PRODUCT_NAME) to save photos - NSPhotoLibraryUsageDescription - Papillon utilise tes photos et vidéos pour personnaliser ton profil, ta gestion des cours et bien plus. - UIAppFonts - - FixelText-Bold.ttf - FixelText-Light.ttf - FixelText-Medium.ttf - FixelText-Regular.ttf - FixelText-SemiBold.ttf - - UIBackgroundModes - - fetch - remote-notification - - UILaunchStoryboardName - SplashScreen - UIRequiredDeviceCapabilities - - arm64 - - UIRequiresFullScreen - - UIStatusBarStyle - UIStatusBarStyleDefault - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIUserInterfaceStyle - Automatic - UIViewControllerBasedStatusBarAppearance - - - -======= CADisableMinimumFrameDurationOnPhone @@ -218,5 +108,4 @@ UIViewControllerBasedStatusBarAppearance - ->>>>>>> main + \ No newline at end of file From 8005c4fd2cd40d8794aa704e9420b1d48f5342c9 Mon Sep 17 00:00:00 2001 From: Tryon <68423470+tryon-dev@users.noreply.github.com> Date: Sat, 8 Feb 2025 21:45:54 +0100 Subject: [PATCH 0555/1144] Update src/views/account/Attendance/Attendance.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 𝕂𝕪𝕝𝕚𝕒𝕟 <164187100+Kgeek33@users.noreply.github.com> --- src/views/account/Attendance/Attendance.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Attendance/Attendance.tsx b/src/views/account/Attendance/Attendance.tsx index 4ea688348..c4477e04d 100644 --- a/src/views/account/Attendance/Attendance.tsx +++ b/src/views/account/Attendance/Attendance.tsx @@ -34,7 +34,7 @@ const Attendance: Screen<"Attendance"> = ({ route, navigation }) => { const attendances = useAttendanceStore(store => store.attendances); const [isRefreshing, setIsRefreshing] = useState(false); - const [isLoading, setLoading] = useState(true); + const [isLoading, setLoading] = useState(hasServiceSetup ? true : false); const [userSelectedPeriod, setUserSelectedPeriod] = useState(null); const selectedPeriod = useMemo(() => userSelectedPeriod ?? defaultPeriod, [userSelectedPeriod, defaultPeriod]); From a6dcb0c2d478f611facbc0647344518985b2a242 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 8 Feb 2025 21:53:51 +0100 Subject: [PATCH 0556/1144] fix: added disclaimer --- src/views/settings/SettingsMultiService.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index d4bea9f22..55f74c0d9 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -225,6 +225,20 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => + + Cette fonctionnalité est instable et peut engendrer des dysfonctionnements sur les comptes associés. + Si vous rencontrez un problème, déconnectez et reconnectez vos comptes pour retrouver leur fonctionnement normal. + setSpaceCreationSheetOpened(opened)} opened={spaceCreationSheetOpened} From d19bc4f93db2531df6211c0da05edfb17013d428 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 8 Feb 2025 21:56:51 +0100 Subject: [PATCH 0557/1144] fix(ts): fixed types in `EcoleDirecteCredentials.tsx` --- src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx b/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx index 105b3bc9a..58fc10a05 100644 --- a/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx +++ b/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx @@ -91,6 +91,7 @@ const EcoleDirecteCredentials: Screen<"EcoleDirecteCredentials"> = ({ navigation account }, personalization: await defaultPersonalization(account), + profilePictureURL: "", serviceData: {}, providers: [] From f63dca5c31e0b520f4646d23d0b09619bc77de2c Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 8 Feb 2025 21:57:15 +0100 Subject: [PATCH 0558/1144] fix(ts): fixed types in `GetDefaultProfilePicture.tsx` --- src/utils/GetRessources/GetDefaultProfilePicture.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/utils/GetRessources/GetDefaultProfilePicture.tsx b/src/utils/GetRessources/GetDefaultProfilePicture.tsx index 30bb6ed6c..132a16507 100644 --- a/src/utils/GetRessources/GetDefaultProfilePicture.tsx +++ b/src/utils/GetRessources/GetDefaultProfilePicture.tsx @@ -1,9 +1,12 @@ import downloadAsBase64 from "@/utils/external/download-as-base64"; -import { AccountService, PronoteAccount, SkolengoAccount, EcoleDirecteAccount } from "@/stores/account/types"; +import { + AccountService, + PrimaryAccount +} from "@/stores/account/types"; // Depending on your account type, download the default profile photo export async function getDefaultProfilePicture ( - account: PronoteAccount | SkolengoAccount | EcoleDirecteAccount): Promise { + account: PrimaryAccount): Promise { try { switch (account.service) { case AccountService.Pronote: { From bdc963c6023acfac48443df7beb516ee19d3a1e1 Mon Sep 17 00:00:00 2001 From: Armand Camponovo Date: Sat, 8 Feb 2025 22:05:26 +0100 Subject: [PATCH 0559/1144] Update src/views/settings/SettingsMultiService.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 𝕂𝕪𝕝𝕚𝕒𝕟 <164187100+Kgeek33@users.noreply.github.com> --- src/views/settings/SettingsMultiService.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index 55f74c0d9..93bee2f6e 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -237,7 +237,7 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => variant="subtitle" > Cette fonctionnalité est instable et peut engendrer des dysfonctionnements sur les comptes associés. - Si vous rencontrez un problème, déconnectez et reconnectez vos comptes pour retrouver leur fonctionnement normal. + Si tu rencontres un problème, déconnecte et reconnecte tes comptes pour retrouver leur fonctionnement normal. setSpaceCreationSheetOpened(opened)} From 48ac0fd7a4f435ace4a55bff8799896d8fc17af9 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 8 Feb 2025 22:42:28 +0100 Subject: [PATCH 0560/1144] refactor: utilisation du wrapper (suite) --- src/components/Home/AccountSwitcherContextMenu.tsx | 4 +++- src/views/account/Grades/Latest/LatestGrades.tsx | 8 +++++++- src/views/welcome/AccountCreated.tsx | 1 - 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/components/Home/AccountSwitcherContextMenu.tsx b/src/components/Home/AccountSwitcherContextMenu.tsx index cc9946f3f..7145083fc 100644 --- a/src/components/Home/AccountSwitcherContextMenu.tsx +++ b/src/components/Home/AccountSwitcherContextMenu.tsx @@ -136,7 +136,9 @@ const ContextMenu: React.FC<{ { - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Soft); + playHaptics("impact", { + impact: Haptics.ImpactFeedbackStyle.Soft, + }); switchTo(account).then(() => { setOpened(false); }); diff --git a/src/views/account/Grades/Latest/LatestGrades.tsx b/src/views/account/Grades/Latest/LatestGrades.tsx index bf2611828..f97da9359 100644 --- a/src/views/account/Grades/Latest/LatestGrades.tsx +++ b/src/views/account/Grades/Latest/LatestGrades.tsx @@ -8,6 +8,7 @@ import { Grade } from "@/services/shared/Grade"; import { NativeStackNavigationProp } from "@react-navigation/native-stack"; import { RouteParameters } from "@/router/helpers/types"; import * as Haptics from "expo-haptics"; +import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; interface GradesLatestListProps { latestGrades: Grade[] @@ -17,6 +18,7 @@ interface GradesLatestListProps { const GradesLatestList = (props: GradesLatestListProps) => { const { latestGrades, navigation, allGrades } = props; + const { playHaptics } = useSoundHapticsWrapper(); const renderItem = ({ item, index }: { item: Grade; index: number }) => ( { snapToInterval={240} decelerationRate="fast" onScroll={({ nativeEvent }) => { - if (nativeEvent.contentOffset.x % 240 === 0) Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); + if (nativeEvent.contentOffset.x % 240 === 0) { + playHaptics("impact", { + impact: Haptics.ImpactFeedbackStyle.Light, + }); + } }} /> diff --git a/src/views/welcome/AccountCreated.tsx b/src/views/welcome/AccountCreated.tsx index 1434a1e6a..79441fc21 100644 --- a/src/views/welcome/AccountCreated.tsx +++ b/src/views/welcome/AccountCreated.tsx @@ -38,7 +38,6 @@ const AccountCreated: Screen<"AccountCreated"> = ({ navigation }) => { // loop 20 times for (let i = 0; i < 15; i++) { setTimeout(() => { - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium); playHaptics("impact", { impact: Haptics.ImpactFeedbackStyle.Medium, }); From 012e73def18596fec77338adf6768540b5fe447e Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 8 Feb 2025 22:46:42 +0100 Subject: [PATCH 0561/1144] fix(vscode): configuring TypeScript to using typescript in node_modules --- .vscode/settings.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 71febfae3..10085819e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,5 +6,6 @@ "editor.formatOnSave": false, "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" - } + }, + "typescript.tsdk": "node_modules/typescript/lib" } \ No newline at end of file From c48f350ce16df74f2ab4077f6b581ddd32b79a3a Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 8 Feb 2025 22:49:39 +0100 Subject: [PATCH 0562/1144] fix: typescript version incorrect --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index f595ba8da..1ab9fde6c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16942,7 +16942,7 @@ } }, "node_modules/typescript": { - "version": "5.6.3", + "version": "5.3.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, From 3c1b61c35f129110d777392cec04fde0c9afbb0a Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 8 Feb 2025 22:50:04 +0100 Subject: [PATCH 0563/1144] bump typescript version to 5.5.4 --- package-lock.json | 9 +++++---- package.json | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1ab9fde6c..15a5d7b90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -118,7 +118,7 @@ "eslint-plugin-unused-imports": "^4.1.4", "expo": "~51.0.17", "react-native-svg-transformer": "^1.5.0", - "typescript": "^5.3.3" + "typescript": "^5.5.4" } }, "node_modules/@ampproject/remapping": { @@ -16942,10 +16942,11 @@ } }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/package.json b/package.json index 4d925c3bf..a11bfb1d9 100644 --- a/package.json +++ b/package.json @@ -120,7 +120,7 @@ "eslint-plugin-unused-imports": "^4.1.4", "expo": "~51.0.17", "react-native-svg-transformer": "^1.5.0", - "typescript": "^5.3.3" + "typescript": "^5.5.4" }, "private": true } From d46bc2df96a15c9a9efea25e95de9c65598a181e Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 8 Feb 2025 23:01:06 +0100 Subject: [PATCH 0564/1144] =?UTF-8?q?feat:=20github-actions=20peut=20d?= =?UTF-8?q?=C3=A9sormais=20review=20une=20pr=20!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/checks.yml | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 128471e09..2b12655d5 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -86,8 +86,8 @@ jobs: - name: 📄 Output Logs run: cat lint.log - - - name: 👁️‍🗨️ Comment on Pull Request + + - name: ❌ Request Changes on Pull Request if: env.is_empty == 'false' uses: actions/github-script@v7 with: @@ -95,11 +95,26 @@ jobs: script: | const fs = require('fs'); const lintLogContent = fs.readFileSync('lint.log', 'utf8'); - github.rest.issues.createComment({ - issue_number: context.issue.number, + github.rest.pulls.createReview({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + body: `⚠️ Erreur(s) détectée(s) par TypeScript/ESLint !\n\`\`\`\n${lintLogContent}\n\`\`\``, + event: 'REQUEST_CHANGES' + }); + + - name: ✅ Approve Pull Request + if: env.is_empty == 'true' + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.rest.pulls.createReview({ owner: context.repo.owner, repo: context.repo.repo, - body: `⚠️ Erreur(s) détectée(s) par TypeScript/ESLint !\n\`\`\`\n${lintLogContent}\n\`\`\`` + pull_number: context.issue.number, + body: 'LGTM 👍', + event: 'APPROVE' }); - name: 🧹 Clean up From c9ccf41fec867ad339a24aa82ca0cf4e117c58b3 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 8 Feb 2025 23:10:48 +0100 Subject: [PATCH 0565/1144] fix(eslint): error --- src/views/account/Homeworks/Homeworks.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 0ab41f93c..c9ce81fa9 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -32,7 +32,7 @@ import * as Haptics from "expo-haptics"; import MissingItem from "@/components/Global/MissingItem"; import { PapillonModernHeader } from "@/components/Global/PapillonModernHeader"; import {Homework} from "@/services/shared/Homework"; -import {Account, AccountService} from "@/stores/account/types"; +import { AccountService } from "@/stores/account/types"; import {Screen} from "@/router/helpers/types"; import {NativeSyntheticEvent} from "react-native/Libraries/Types/CoreEventTypes"; import {NativeScrollEvent, ScrollViewProps} from "react-native/Libraries/Components/ScrollView/ScrollView"; From dd6e423adacaa1d63007a0a060f556365174cab6 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 8 Feb 2025 23:14:37 +0100 Subject: [PATCH 0566/1144] fix(ts): errors --- src/views/account/Week/Week.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index 901e25bd1..b7baa8e10 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -51,7 +51,7 @@ const EventItem = memo(({ event }) => { const isWide = layout.width > 100; const handlePress = () => { - PapillonNavigation.current.navigate("LessonDocument", { lesson: event.event }); + PapillonNavigation.current?.navigate("LessonDocument", { lesson: event.event }); }; const containerStyle = [ @@ -347,7 +347,7 @@ const Week: Screen<"Week"> = ({ route, navigation }) => { onPress={() => { setOpenedIcalModal(true); setTimeout(() => { - PapillonNavigation.current.navigate("LessonsImportIcal"); + PapillonNavigation.current?.navigate("LessonsImportIcal", {}); }, 100); }} /> From e3d287a24c9f4067fb31b32d587f0642449f9a1c Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 8 Feb 2025 23:39:29 +0100 Subject: [PATCH 0567/1144] =?UTF-8?q?refactor:=20`require`=20dans=20des=20?= =?UTF-8?q?variables=20pour=20=C3=AAtre=20s=C3=BBr=20que=20le=20son=20se?= =?UTF-8?q?=20charge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/FirstInstallation/DuoListPressable.tsx | 3 ++- src/views/login/ServiceSelector.tsx | 9 +++++---- .../login/pronote/PronoteAuthenticationSelector.tsx | 3 ++- src/views/login/pronote/PronoteQRCode.tsx | 3 ++- src/views/login/pronote/PronoteWebview.tsx | 6 ++++-- .../login/skolengo/SkolengoAuthenticationSelector.tsx | 3 ++- src/views/login/skolengo/SkolengoWebview.tsx | 3 ++- src/views/welcome/AccountCreated.tsx | 6 ++++-- src/views/welcome/ColorSelector.tsx | 6 ++++-- 9 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/components/FirstInstallation/DuoListPressable.tsx b/src/components/FirstInstallation/DuoListPressable.tsx index fa8f4e8fa..ba8baa073 100644 --- a/src/components/FirstInstallation/DuoListPressable.tsx +++ b/src/components/FirstInstallation/DuoListPressable.tsx @@ -32,6 +32,7 @@ const DuoListPressable: React.FC<{ const opacity = useSharedValue(1); const { playHaptics, playSound } = useSoundHapticsWrapper(); + const LEson = require("@/../assets/sound/click_003.wav"); useEffect(() => { if (pressed) { @@ -41,7 +42,7 @@ const DuoListPressable: React.FC<{ playHaptics("impact", { impact: Haptics.ImpactFeedbackStyle.Light, }); - playSound(require("@/../assets/sound/click_003.wav")); + playSound(LEson); } else { scale.value = withTiming(1, { duration: 100, easing: Easing.linear }); diff --git a/src/views/login/ServiceSelector.tsx b/src/views/login/ServiceSelector.tsx index 1f2a4e149..9fb858d2d 100644 --- a/src/views/login/ServiceSelector.tsx +++ b/src/views/login/ServiceSelector.tsx @@ -28,6 +28,7 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { const [v6Data, setV6Data] = useState(null); const { playSound } = useSoundHapticsWrapper(); + const LEson = require("@/../assets/sound/1.wav"); useEffect(() => { setTimeout(async () => { @@ -52,7 +53,7 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { image: require("../../../assets/images/service_pronote.png"), login: () => { navigation.navigate("PronoteAuthenticationSelector"); - playSound(require("@/../assets/sound/1.wav")); + playSound(LEson); }, }, { @@ -61,7 +62,7 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { image: require("../../../assets/images/service_ed.png"), login: () => { navigation.navigate("EcoleDirecteCredentials"); - playSound(require("@/../assets/sound/1.wav")); + playSound(LEson); } }, { @@ -70,7 +71,7 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { image: require("../../../assets/images/service_skolengo.png"), login: () => { navigation.navigate("SkolengoAuthenticationSelector"); - playSound(require("@/../assets/sound/1.wav")); + playSound(LEson); } }, { @@ -81,7 +82,7 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { icon: , login: () => { navigation.navigate("IdentityProviderSelector"); - playSound(require("@/../assets/sound/1.wav")); + playSound(LEson); } }, ]; diff --git a/src/views/login/pronote/PronoteAuthenticationSelector.tsx b/src/views/login/pronote/PronoteAuthenticationSelector.tsx index 77d70daa6..ec3e968f7 100644 --- a/src/views/login/pronote/PronoteAuthenticationSelector.tsx +++ b/src/views/login/pronote/PronoteAuthenticationSelector.tsx @@ -21,6 +21,7 @@ const PronoteAuthenticationSelector: Screen<"PronoteAuthenticationSelector"> = ( const [method, setMethod] = useState(null); const { playSound } = useSoundHapticsWrapper(); + const LEson = require("@/../assets/sound/2.wav"); const handleConfirmation = () => { switch (method) { @@ -38,7 +39,7 @@ const PronoteAuthenticationSelector: Screen<"PronoteAuthenticationSelector"> = ( break; } - playSound(require("@/../assets/sound/2.wav")); + playSound(LEson); }; return ( diff --git a/src/views/login/pronote/PronoteQRCode.tsx b/src/views/login/pronote/PronoteQRCode.tsx index e4e2388a9..1759f9925 100644 --- a/src/views/login/pronote/PronoteQRCode.tsx +++ b/src/views/login/pronote/PronoteQRCode.tsx @@ -55,6 +55,7 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { const [QRData, setQRData] = useState(null); const { playHaptics, playSound } = useSoundHapticsWrapper(); + const LEson = require("@/../assets/sound/4.wav"); async function loginQR () { setScanned(false); @@ -133,7 +134,7 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { queueMicrotask(() => { // Reset the navigation stack to the "Home" screen. // Prevents the user from going back to the login screen. - playSound(require("@/../assets/sound/4.wav")); + playSound(LEson); navigation.reset({ index: 0, routes: [{ name: "AccountCreated" }], diff --git a/src/views/login/pronote/PronoteWebview.tsx b/src/views/login/pronote/PronoteWebview.tsx index ca712c2c2..48a46927c 100644 --- a/src/views/login/pronote/PronoteWebview.tsx +++ b/src/views/login/pronote/PronoteWebview.tsx @@ -50,6 +50,8 @@ const PronoteWebview: Screen<"PronoteWebview"> = ({ route, navigation }) => { const [loginStep, setLoginStep] = useState("Préparation de la connexion"); const { playSound } = useSoundHapticsWrapper(); + const LEson3 = require("@/../assets/sound/3.wav"); + const LEson4 = require("@/../assets/sound/4.wav"); const instanceURL = route.params.instanceURL.toLowerCase(); @@ -73,7 +75,7 @@ const PronoteWebview: Screen<"PronoteWebview"> = ({ route, navigation }) => { ).toUTCString(); useEffect(() => { - playSound(require("@/../assets/sound/3.wav")); + playSound(LEson3); }, []); const INJECT_PRONOTE_JSON = ` @@ -327,7 +329,7 @@ const PronoteWebview: Screen<"PronoteWebview"> = ({ route, navigation }) => { queueMicrotask(() => { // Reset the navigation stack to the "Home" screen. // Prevents the user from going back to the login screen. - playSound(require("@/../assets/sound/4.wav")); + playSound(LEson4); navigation.reset({ index: 0, routes: [{ name: "AccountCreated" }], diff --git a/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx b/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx index 82385f944..164d74352 100644 --- a/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx +++ b/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx @@ -20,6 +20,7 @@ const SkolengoAuthenticationSelector: Screen<"SkolengoAuthenticationSelector"> = const [method, setMethod] = useState(null); const { playSound } = useSoundHapticsWrapper(); + const LEson = require("@/../assets/sound/2.wav"); const handleConfirmation = () => { switch (method) { @@ -31,7 +32,7 @@ const SkolengoAuthenticationSelector: Screen<"SkolengoAuthenticationSelector"> = break; } - playSound(require("@/../assets/sound/2.wav")); + playSound(LEson); }; return ( diff --git a/src/views/login/skolengo/SkolengoWebview.tsx b/src/views/login/skolengo/SkolengoWebview.tsx index f6b4acf93..15503725e 100644 --- a/src/views/login/skolengo/SkolengoWebview.tsx +++ b/src/views/login/skolengo/SkolengoWebview.tsx @@ -42,6 +42,7 @@ const SkolengoWebview: Screen<"SkolengoWebview"> = ({ route, navigation }) => { const [pageUrl, setPageUrl] = useState(null); const [discovery, setDiscovery] = useState(null); const { playSound } = useSoundHapticsWrapper(); + const LEson = require("@/../assets/sound/3.wav"); const createStoredAccount = useAccounts((store) => store.create); const switchTo = useCurrentAccount((store) => store.switchTo); @@ -59,7 +60,7 @@ const SkolengoWebview: Screen<"SkolengoWebview"> = ({ route, navigation }) => { let webViewRef = createRef(); useEffect(() => { - playSound(require("@/../assets/sound/3.wav")); + playSound(LEson); }, []); return ( diff --git a/src/views/welcome/AccountCreated.tsx b/src/views/welcome/AccountCreated.tsx index 79441fc21..c225cded4 100644 --- a/src/views/welcome/AccountCreated.tsx +++ b/src/views/welcome/AccountCreated.tsx @@ -18,6 +18,8 @@ import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; const AccountCreated: Screen<"AccountCreated"> = ({ navigation }) => { const account = useCurrentAccount((state) => state.account!); const { playHaptics, playSound } = useSoundHapticsWrapper(); + const LEson5 = require("@/../assets/sound/5.wav"); + const LEson6 = require("@/../assets/sound/6.wav"); let name = !account.studentName?.first ? null : account.studentName?.first; @@ -95,14 +97,14 @@ const AccountCreated: Screen<"AccountCreated"> = ({ navigation }) => { primary onPress={() => { navigation.navigate("ColorSelector"); - playSound(require("@/../assets/sound/5.wav")); + playSound(LEson5); }} /> { navigation.navigate("AccountStack", { onboard: true }); - playSound(require("@/../assets/sound/6.wav")); + playSound(LEson6); }} /> diff --git a/src/views/welcome/ColorSelector.tsx b/src/views/welcome/ColorSelector.tsx index e4ce44497..af4323e0a 100644 --- a/src/views/welcome/ColorSelector.tsx +++ b/src/views/welcome/ColorSelector.tsx @@ -27,6 +27,8 @@ const ColorSelector: Screen<"ColorSelector"> = ({ route, navigation }) => { const mutateProperty = useCurrentAccount(store => store.mutateProperty); const settings = route.params?.settings || false; const { playHaptics, playSound } = useSoundHapticsWrapper(); + const LEson003 = require("@/../assets/sound/click_003.wav"); + const LEson6 = require("@/../assets/sound/6.wav"); useLayoutEffect(() => { navigation.setOptions({ @@ -45,7 +47,7 @@ const ColorSelector: Screen<"ColorSelector"> = ({ route, navigation }) => { playHaptics("notification", { notification: Haptics.NotificationFeedbackType.Success, }); - playSound(require("@/../assets/sound/click_003.wav")); + playSound(LEson003); if (!isExpoGo()) { getIconName().then((currentIcon) => { @@ -179,7 +181,7 @@ const ColorSelector: Screen<"ColorSelector"> = ({ route, navigation }) => { value="Finaliser" onPress={async () => { if (!settings) { - playSound(require("@/../assets/sound/6.wav")); + playSound(LEson6); } navigation.navigate("AccountStack", {onboard: true}); }} From 662722de521db645b0ac600c5ad7eb1c4bc921aa Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 8 Feb 2025 23:43:26 +0100 Subject: [PATCH 0568/1144] refractor: utilisation du wrapper dans `SettingsMultiServiceSpace` --- src/views/settings/SettingsMultiServiceSpace.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/views/settings/SettingsMultiServiceSpace.tsx b/src/views/settings/SettingsMultiServiceSpace.tsx index 364ca1073..3f80aecc7 100644 --- a/src/views/settings/SettingsMultiServiceSpace.tsx +++ b/src/views/settings/SettingsMultiServiceSpace.tsx @@ -26,6 +26,7 @@ import PapillonBottomSheet from "@/components/Modals/PapillonBottomSheet"; import * as Haptics from "expo-haptics"; import AccountItem from "@/components/Global/AccountItem"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; +import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ navigation, route }) => { const theme = useTheme(); @@ -36,6 +37,7 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga const deleteMultiServiceSpace = useMultiService(store => store.remove); const updateMultiServiceSpace = useMultiService(store => store.update); const setMultiServiceSpaceAccountFeature = useMultiService(store => store.setFeatureAccount); + const { playHaptics } = useSoundHapticsWrapper(); const linkedAccount = accounts.accounts.find(account => account.localID === space.accountLocalID) as PrimaryAccount | undefined; @@ -421,7 +423,9 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga { - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Soft); + playHaptics("impact", { + impact: Haptics.ImpactFeedbackStyle.Soft, + }); setAccountFeature(account, featureSelection); }} > From f8dc24e7b5219eecb0c76d4d2b175630dc8f3883 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 01:04:14 +0100 Subject: [PATCH 0569/1144] =?UTF-8?q?fix:=20explication=20de=20la=20sectio?= =?UTF-8?q?n=20"Issue(s)=20en=20rapport"=20adapt=C3=A9e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: camarm-dev --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index c5041236b..2e1dda731 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -30,7 +30,7 @@ _Le(s) capture(s) d'écran_ > [!NOTE] > -> Cette section permet de "linker" des issues à ta PR. Cela signifie qu'une fois ta PR mergée, les issues listées ci-dessous seront automatiquement fermées +> Cette section te permet de référencer des issues en lien avec ta PR. Pour que les issues soient fermées automatiquement au merge, utilise les "[closing keywords](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests)" de Github (exemples ci-dessous). - Closed #(_le numéro de l'issue_) From a2785a1bdf337e01087a7c57fb04d5334ae9129d Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 01:29:47 +0100 Subject: [PATCH 0570/1144] feat: retour de la todo list mais plus simple Co-authored-by: camarm-dev --- .github/PULL_REQUEST_TEMPLATE.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 2e1dda731..8d1db8e75 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -6,17 +6,14 @@ Tu te poses des questions sur les pull requests (PR) ? Une documentation a spéc ## Avant toute chose... -Pour nous aider à tester ta PR, merci de cocher une des cases suivantes (_en rajoutant un `x` dans les crochets_) : +Tu t'assures avoir respecté les points suivants (_en rajoutant un `x` dans les crochets_) : -Type de pull request : +- [ ] 📲 J'ai testé l'application via Expo Go et/ou Build et **elle fonctionne correctement** +- [ ] 🗣️ J'utilise le **langage informel** (tutoiement) +- [ ] 📃 Cette PR [**n'est pas un duplicata**](https://github.com/PapillonApp/Papillon/pulls) et **est prête à être review et merge** +- [ ] ❌ Je n'ai pas fait **d'erreurs TypeScript et ESLint** dans le code +- [ ] 📝 J'ai fait **une description des changement effectués** ci-dessous -- [ ] **Breaking change** (_des modifications avec un impact sur les fonctionnalités actuelles_) - - [ ] J'ai fait une build de Papillon pour m'assurer que je n'ai rien cassé -- [ ] **Feat** (_ajoute une amélioration/nouveauté_) - - [ ] Je m'assure que j'utilise le langage informel (tutoiement) -- [ ] **Fix** (_permet de corriger un bug_) -- [ ] **Chore** (_des modifications en dehors du dossier `src`_) -- [ ] **Styles** (_change/ajoute du style_) ## Résumé des changements effectués From 44a89d2ad3307a2689c38aaf85772d1522079539 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 01:33:03 +0100 Subject: [PATCH 0571/1144] =?UTF-8?q?refractor:=20simplification=20du=20wo?= =?UTF-8?q?rkflow=20pour=20plus=20d'efficacit=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/checks.yml | 51 +----------------------------------- 1 file changed, 1 insertion(+), 50 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 2b12655d5..9cfd25712 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -70,53 +70,4 @@ jobs: - name: 🔍 Run TypeScript and ESLint checks run: | - npx tsc > tsc.log 2>&1 || true - npx eslint . > eslint.log 2>&1 || true - cat tsc.log eslint.log > lint.log - continue-on-error: true - - - name: 📋 Check if lint logs are empty - id: check_logs - run: | - if [ -s lint.log ]; then - echo "is_empty=false" >> $GITHUB_ENV - else - echo "is_empty=true" >> $GITHUB_ENV - fi - - - name: 📄 Output Logs - run: cat lint.log - - - name: ❌ Request Changes on Pull Request - if: env.is_empty == 'false' - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const fs = require('fs'); - const lintLogContent = fs.readFileSync('lint.log', 'utf8'); - github.rest.pulls.createReview({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.issue.number, - body: `⚠️ Erreur(s) détectée(s) par TypeScript/ESLint !\n\`\`\`\n${lintLogContent}\n\`\`\``, - event: 'REQUEST_CHANGES' - }); - - - name: ✅ Approve Pull Request - if: env.is_empty == 'true' - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - github.rest.pulls.createReview({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.issue.number, - body: 'LGTM 👍', - event: 'APPROVE' - }); - - - name: 🧹 Clean up - if: always() - run: rm -f lint.log tsc.log eslint.log + npm run lint From 73c63fcc8267a8d842607e2020b6a5e7678c97e2 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 12:30:41 +0100 Subject: [PATCH 0572/1144] =?UTF-8?q?feat:=20ajout=20d'un=20workflow=20Git?= =?UTF-8?q?Hub=20Actions=20pour=20g=C3=A9n=C3=A9rer=20un=20bundle=20Expo?= =?UTF-8?q?=20et=20un=20code=20QR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/expo-qr.yml | 62 +++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 .github/workflows/expo-qr.yml diff --git a/.github/workflows/expo-qr.yml b/.github/workflows/expo-qr.yml new file mode 100644 index 000000000..65055766f --- /dev/null +++ b/.github/workflows/expo-qr.yml @@ -0,0 +1,62 @@ +name: Generate Expo Bundle and QR Code + +on: + pull_request: + types: [opened, synchronize] + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 20 + + - name: Install dependencies + run: npm ci + + - name: Export Expo Bundle + run: | + npx expo export --public-url https://kgeek33.github.io/Papillon-Bundles/pr-${{ github.event.pull_request.number }} + + - name: Clone Papillon-Bundles repo + env: + GITHUB_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} + run: | + git clone https://github.com/kgeek33/Papillon-Bundles.git + cd Papillon-Bundles + git config user.name "github-actions" + git config user.email "github-actions@github.com" + + - name: Upload Bundle to GitHub Pages + run: | + cd Papillon-Bundles + mkdir -p pr-${{ github.event.pull_request.number }} + cp -r ../dist/* pr-${{ github.event.pull_request.number }} + git add . + git commit -m "Deploy bundle for PR #${{ github.event.pull_request.number }}" + git push origin main + + - name: Generate QR Code + run: | + sudo apt-get install -y qrencode + BUNDLE_URL="https://kgeek33.github.io/Papillon-Bundles/pr-${{ github.event.pull_request.number }}" + qrencode -o qr.png "$BUNDLE_URL" + + - name: Upload QR Code + uses: actions/upload-artifact@v3 + with: + name: qr-code + path: qr.png + + - name: Update PR description with QR Code + run: | + PR_NUMBER=$(jq --raw-output .pull_request.number "$GITHUB_EVENT_PATH") + IMG_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts/qr-code" + gh pr edit $PR_NUMBER --body "### Scan this QR Code to test:\n![QR Code]($IMG_URL)\n\nOu ouvrez directement [ce lien]($BUNDLE_URL)" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 85ce2d03bac3a9cca53e8b2b62e550d11f9c8ddd Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 12:35:15 +0100 Subject: [PATCH 0573/1144] correction des fautes by @Bulgus Co-authored-by: Bulgus --- .github/ISSUE_TEMPLATE/bug.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index fdd6399d2..9c568dc29 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -1,12 +1,12 @@ name: 🐛 Signaler un bug -description: Signaler des bugs nous permet à améliorer Papillon ! +description: Signaler des bugs nous permet d'améliorer Papillon ! title: "[Bug]: " body: - type: textarea attributes: label: Description du bug - description: Plus de détails nous permettent de trouver plus vite le bug ! + description: Plus il y a de détail, plus nous pourrons vite trouver le bug ! placeholder: La connexion à mon établissement ne fonctionne pas, j'ai un chargement infini lors de la connexion validations: required: true @@ -25,7 +25,7 @@ body: - type: textarea attributes: label: Comportement attendu - description: Ce que Papillon doit faire + description: Ce que Papillon devrait faire placeholder: Que la connexion à mon établissement fonctionne et qu'il n'y ait pas de chargement infini validations: required: true @@ -84,7 +84,7 @@ body: - type: textarea attributes: label: "Capture(s) d'écran / vidéo" - description: Cela permettra que le bug soit trouvé et résolu encore plus vite + description: Cela permettra une résolution encore plus rapide du bug placeholder: Il faut cliquer sur l'icône 📎 pour pouvoir importer une/des photo(s)/vidéo(s) validations: required: false From 58ed78d4e55414d752638fea0253cdaff03c8769 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 12:36:24 +0100 Subject: [PATCH 0574/1144] fix: conjugaison Co-authored-by: Bulgus --- .github/ISSUE_TEMPLATE/feature.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml index 88f7b1c28..be391eff7 100644 --- a/.github/ISSUE_TEMPLATE/feature.yml +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -6,7 +6,7 @@ body: - type: textarea attributes: label: Description de la fonctionnalité - description: Fait place à ta créativité en détaillant ce que tu veux + description: Fais place à ta créativité en détaillant ce que tu veux placeholder: Ça serait cool une intégration d'un simulateur permettant de savoir si on aura le bac/brevet validations: required: true From fe3f77e0ab17db62a621e7a57bdf032d57cac2cf Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 12:37:10 +0100 Subject: [PATCH 0575/1144] fix: sans commentaire xD Co-authored-by: Bulgus --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 8d1db8e75..b9231528f 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,7 +2,7 @@ Merci de contribuer à l'amélioration de Papillon ! -Tu te poses des questions sur les pull requests (PR) ? Une documentation a spécialement été crée ici => https://gitbook.getpapillon.xyz/organisation/outils-internes/github +Tu te poses des questions sur les pull requests (PR) ? Une documentation a spécialement été créée ici => https://gitbook.getpapillon.xyz/organisation/outils-internes/github ## Avant toute chose... From 3524fdf77cc04fe47c9b27b0e6d8889ff7031971 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 12:41:40 +0100 Subject: [PATCH 0576/1144] change version --- .github/workflows/expo-qr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/expo-qr.yml b/.github/workflows/expo-qr.yml index 65055766f..ea4aae1b9 100644 --- a/.github/workflows/expo-qr.yml +++ b/.github/workflows/expo-qr.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v2 - name: Setup Node.js uses: actions/setup-node@v3 From a14699d4cd54d453fe3a2403de0a2ea08b2c0fc2 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 12:45:53 +0100 Subject: [PATCH 0577/1144] Revert "change version" This reverts commit 3524fdf77cc04fe47c9b27b0e6d8889ff7031971. --- .github/workflows/expo-qr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/expo-qr.yml b/.github/workflows/expo-qr.yml index ea4aae1b9..65055766f 100644 --- a/.github/workflows/expo-qr.yml +++ b/.github/workflows/expo-qr.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup Node.js uses: actions/setup-node@v3 From fb460ccc4e8a012878de907d851f4478431059a6 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 12:46:43 +0100 Subject: [PATCH 0578/1144] change version of `upload-artifact` who is deprecated --- .github/workflows/expo-qr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/expo-qr.yml b/.github/workflows/expo-qr.yml index 65055766f..f4a748d16 100644 --- a/.github/workflows/expo-qr.yml +++ b/.github/workflows/expo-qr.yml @@ -48,7 +48,7 @@ jobs: qrencode -o qr.png "$BUNDLE_URL" - name: Upload QR Code - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: qr-code path: qr.png From d5e7ba0ccb7f7290f71ce604f1ea22ebe8a60af4 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 12:52:51 +0100 Subject: [PATCH 0579/1144] fix: maj de la commande (n'existe pas) --- .github/workflows/expo-qr.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/expo-qr.yml b/.github/workflows/expo-qr.yml index f4a748d16..f586c4a7c 100644 --- a/.github/workflows/expo-qr.yml +++ b/.github/workflows/expo-qr.yml @@ -17,11 +17,11 @@ jobs: node-version: 20 - name: Install dependencies - run: npm ci + run: npm install - name: Export Expo Bundle run: | - npx expo export --public-url https://kgeek33.github.io/Papillon-Bundles/pr-${{ github.event.pull_request.number }} + npx expo export --output-dir dist - name: Clone Papillon-Bundles repo env: @@ -44,11 +44,11 @@ jobs: - name: Generate QR Code run: | sudo apt-get install -y qrencode - BUNDLE_URL="https://kgeek33.github.io/Papillon-Bundles/pr-${{ github.event.pull_request.number }}" + BUNDLE_URL="https://kgeek33.github.io/Papillon-Bundles/pr-${{ github.event.pull_request.number }}/index.json" qrencode -o qr.png "$BUNDLE_URL" - name: Upload QR Code - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v3 with: name: qr-code path: qr.png From 560bf6f301b593b5b550cd325d9ade722dd1d4c0 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 12:54:50 +0100 Subject: [PATCH 0580/1144] mauvaise version (encore) --- .github/workflows/expo-qr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/expo-qr.yml b/.github/workflows/expo-qr.yml index f586c4a7c..28f9cba79 100644 --- a/.github/workflows/expo-qr.yml +++ b/.github/workflows/expo-qr.yml @@ -48,7 +48,7 @@ jobs: qrencode -o qr.png "$BUNDLE_URL" - name: Upload QR Code - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: qr-code path: qr.png From deb922fc043dc168f155664bc7bca3caebd4b720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Sun, 9 Feb 2025 13:35:40 +0100 Subject: [PATCH 0581/1144] p'tit changement --- .github/workflows/expo-qr.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/expo-qr.yml b/.github/workflows/expo-qr.yml index 28f9cba79..b2e2d92ba 100644 --- a/.github/workflows/expo-qr.yml +++ b/.github/workflows/expo-qr.yml @@ -27,10 +27,11 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} run: | - git clone https://github.com/kgeek33/Papillon-Bundles.git + git clone https://github.com/Kgeek33/Papillon-Bundles.git cd Papillon-Bundles git config user.name "github-actions" git config user.email "github-actions@github.com" + echo "Good!" - name: Upload Bundle to GitHub Pages run: | From 846c9b637aca7fa9693e8e25692647063104ca68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Sun, 9 Feb 2025 13:43:56 +0100 Subject: [PATCH 0582/1144] Adaptation de la configuration de git --- .github/workflows/expo-qr.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/expo-qr.yml b/.github/workflows/expo-qr.yml index b2e2d92ba..3fc7ef699 100644 --- a/.github/workflows/expo-qr.yml +++ b/.github/workflows/expo-qr.yml @@ -27,11 +27,9 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions@github.com" git clone https://github.com/Kgeek33/Papillon-Bundles.git - cd Papillon-Bundles - git config user.name "github-actions" - git config user.email "github-actions@github.com" - echo "Good!" - name: Upload Bundle to GitHub Pages run: | From 78f93ed9036c31c4f5713062f62c289d52a05e1b Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 14:25:00 +0100 Subject: [PATCH 0583/1144] changement de la formulation Co-authored-by: Bulgus <88266443+Bulgus@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/bug.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 9c568dc29..f7436ced8 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -6,7 +6,7 @@ body: - type: textarea attributes: label: Description du bug - description: Plus il y a de détail, plus nous pourrons vite trouver le bug ! + description: Plus il y a de détails, plus vite nous pourrons trouver le bug ! placeholder: La connexion à mon établissement ne fonctionne pas, j'ai un chargement infini lors de la connexion validations: required: true From 92b3982157623f8efeca18fba58265c4f683ef93 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 14:25:38 +0100 Subject: [PATCH 0584/1144] bump to the latest version of Papillon --- .github/ISSUE_TEMPLATE/bug.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index f7436ced8..219719c4d 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -61,7 +61,7 @@ body: attributes: label: Version utilisée description: Paramètres (de Papillon) -> Version affichée en bas de la page - placeholder: "7.7.1" + placeholder: "7.8.3" validations: required: true From 1b7a6a613cd58035c6d44d6ee4050866734bfbfe Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 14:29:41 +0100 Subject: [PATCH 0585/1144] pour plus tard --- .github/workflows/expo-qr.yml | 61 ----------------------------------- 1 file changed, 61 deletions(-) delete mode 100644 .github/workflows/expo-qr.yml diff --git a/.github/workflows/expo-qr.yml b/.github/workflows/expo-qr.yml deleted file mode 100644 index 3fc7ef699..000000000 --- a/.github/workflows/expo-qr.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: Generate Expo Bundle and QR Code - -on: - pull_request: - types: [opened, synchronize] - -jobs: - build-and-deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Setup Node.js - uses: actions/setup-node@v3 - with: - node-version: 20 - - - name: Install dependencies - run: npm install - - - name: Export Expo Bundle - run: | - npx expo export --output-dir dist - - - name: Clone Papillon-Bundles repo - env: - GITHUB_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} - run: | - git config --global user.name "github-actions[bot]" - git config --global user.email "github-actions@github.com" - git clone https://github.com/Kgeek33/Papillon-Bundles.git - - - name: Upload Bundle to GitHub Pages - run: | - cd Papillon-Bundles - mkdir -p pr-${{ github.event.pull_request.number }} - cp -r ../dist/* pr-${{ github.event.pull_request.number }} - git add . - git commit -m "Deploy bundle for PR #${{ github.event.pull_request.number }}" - git push origin main - - - name: Generate QR Code - run: | - sudo apt-get install -y qrencode - BUNDLE_URL="https://kgeek33.github.io/Papillon-Bundles/pr-${{ github.event.pull_request.number }}/index.json" - qrencode -o qr.png "$BUNDLE_URL" - - - name: Upload QR Code - uses: actions/upload-artifact@v4 - with: - name: qr-code - path: qr.png - - - name: Update PR description with QR Code - run: | - PR_NUMBER=$(jq --raw-output .pull_request.number "$GITHUB_EVENT_PATH") - IMG_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts/qr-code" - gh pr edit $PR_NUMBER --body "### Scan this QR Code to test:\n![QR Code]($IMG_URL)\n\nOu ouvrez directement [ce lien]($BUNDLE_URL)" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From d01a6fbd1e0c241e379c556239be98cea21428e4 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 14:33:36 +0100 Subject: [PATCH 0586/1144] Fix ESLint + check lint without bypass in `main` + fix version node --- .github/workflows/checks.yml | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 9cfd25712..ec23f9e61 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -24,21 +24,11 @@ jobs: - name: 💾 Install dependencies run: npm ci - - name: 🔍 Run TypeScript and ESLint checks - run: | - tsc > tsc.log 2>&1 || true - eslint . > eslint.log 2>&1 || true - cat tsc.log eslint.log > lint.log - continue-on-error: true - - - name: 📄 Output Logs - run: cat lint.log - - - name: 🛠️ Commit and Push Fixes (Main) - if: success() || failure() + - name: 🛠️ Commit and Push Fixes ESLint (Main) run: | - git config user.name "GitHub Actions" - git config user.email "actions@github.com" + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions@github.com" + npx eslint . --fix if git diff --quiet; then echo "No changes to commit." else @@ -46,10 +36,9 @@ jobs: git commit -m "🔧 Auto-fix ESLint issues" git push fi - - - name: 🧹 Clean up - if: always() - run: rm -f lint.log tsc.log eslint.log + + - name: 🔍 Run TypeScript and ESLint checks + run: npm run lint lint-development: name: 🛠️ TypeScript and ESLint on Pull Request @@ -62,12 +51,11 @@ jobs: - name: ⚙️ Set up Node.js uses: actions/setup-node@v3 with: - node-version: 18 + node-version: 20 cache: 'npm' - name: 💾 Install dependencies run: npm ci - name: 🔍 Run TypeScript and ESLint checks - run: | - npm run lint + run: npm run lint From bc86d5522909be1bbc0e7decb30de1ab14a7beaa Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 14:55:01 +0100 Subject: [PATCH 0587/1144] feat(onlineStatus): ajout d'un hook et d'un composant d'avertissement hors ligne --- src/hooks/useOnlineStatus.tsx | 61 +++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/hooks/useOnlineStatus.tsx diff --git a/src/hooks/useOnlineStatus.tsx b/src/hooks/useOnlineStatus.tsx new file mode 100644 index 000000000..1d5c39f34 --- /dev/null +++ b/src/hooks/useOnlineStatus.tsx @@ -0,0 +1,61 @@ +import { useEffect, useState, useMemo } from "react"; +import NetInfo from "@react-native-community/netinfo"; +import { getErrorTitle } from "@/utils/format/get_papillon_error_title"; +import { + NativeItem, + NativeList, + NativeText, +} from "@/components/Global/NativeComponents"; +import Reanimated, { + FadeOutUp, + FlipInXDown, + LinearTransition, +} from "react-native-reanimated"; +import { WifiOff } from "lucide-react-native"; +import { animPapillon } from "@/utils/ui/animations"; + +const useOnlineStatus = () => { + const [isOnline, setIsOnline] = useState(true); + const errorTitle = useMemo(() => getErrorTitle(), []); + + useEffect(() => { + const unsubscribe = NetInfo.addEventListener((state) => { + setIsOnline(state.isConnected ?? false); + }); + + return () => unsubscribe(); + }, []); + + return { isOnline, errorTitle }; +}; + +const OfflineWarning = ({ cache = false }) => { + const { errorTitle } = useOnlineStatus(); + + return ( + + + }> + + {errorTitle.label} {errorTitle.emoji} + + + Tu es hors ligne.{" "} + {cache + ? "Les données affichées peuvent être obsolètes." + : "Vérifie ta connexion Internet et réessaye"} + + + + + ); +}; + +export { useOnlineStatus, OfflineWarning }; From 58f6c29f2e5fd9e70d4132641b8c775cd3af78da Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 15:54:06 +0100 Subject: [PATCH 0588/1144] =?UTF-8?q?feat(onlineStatus):=20int=C3=A9gratio?= =?UTF-8?q?n=20du=20hook=20useOnlineStatus=20et=20ajout=20d'avertissements?= =?UTF-8?q?=20hors=20ligne=20dans=20plusieurs=20composants?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Global/PapillonModernHeader.tsx | 4 +- src/components/Home/AccountSwitcher.tsx | 4 +- src/components/Home/Widget.tsx | 10 ++++- src/views/account/Attendance/Attendance.tsx | 10 +++++ src/views/account/Chat/Messages.tsx | 8 +++- src/views/account/Evaluation/Evaluation.tsx | 10 +++++ src/views/account/Grades/Grades.tsx | 10 +++++ src/views/account/Home/ModalContent.tsx | 42 +++---------------- src/views/account/Homeworks/Atoms/Item.tsx | 32 ++++++++++++-- src/views/account/Homeworks/Homeworks.tsx | 10 +++++ src/views/account/Lessons/Atoms/Page.tsx | 5 +++ src/views/account/News/Document.tsx | 38 ++++++++++++++--- src/views/account/News/News.tsx | 10 +++++ src/views/account/Restaurant/Menu.tsx | 6 ++- src/views/login/ServiceSelector.tsx | 35 +++++++++++++++- 15 files changed, 181 insertions(+), 53 deletions(-) diff --git a/src/components/Global/PapillonModernHeader.tsx b/src/components/Global/PapillonModernHeader.tsx index 9700d4f5e..cd8b084cc 100644 --- a/src/components/Global/PapillonModernHeader.tsx +++ b/src/components/Global/PapillonModernHeader.tsx @@ -9,6 +9,7 @@ import { PressableScale } from "react-native-pressable-scale"; import { useTheme } from "@react-navigation/native"; import { BlurView } from "expo-blur"; import { LinearGradient } from "expo-linear-gradient"; +import { useOnlineStatus } from "@/hooks/useOnlineStatus"; interface ModernHeaderProps { children: React.ReactNode, @@ -230,6 +231,7 @@ export const PapillonHeaderSelector: React.FC<{ loading = false, }) => { const theme = useTheme(); + const isOnline = useOnlineStatus(); return ( {children} - {loading && + {isOnline && loading && = ({ small, opened, modalOpen, translationY, loading }) => { const theme = useTheme(); const { colors } = theme; + const { isOnline } = useOnlineStatus(); const account = useCurrentAccount(store => store.account!); @@ -159,7 +161,7 @@ const AccountSwitcher: React.FC<{ ) : "Mon compte"} - {loading && ( + {isOnline && loading && ( > @@ -32,10 +33,17 @@ const Widget: React.FC = ({ widget: DynamicWidget, navigat const theme = useTheme(); const { colors } = theme; const widgetRef = useRef | null>(null); + const { isOnline } = useOnlineStatus(); const [loading, setLoading] = useState(true); const [hidden, setHidden] = useState(false); + useEffect(() => { + if (!isOnline && loading) { + setLoading(false); + } + }, [isOnline, loading]); + const handlePress = () => { const location = (widgetRef.current as any)?.handlePress(); if (location) { diff --git a/src/views/account/Attendance/Attendance.tsx b/src/views/account/Attendance/Attendance.tsx index 4fc80816c..a690a3f72 100644 --- a/src/views/account/Attendance/Attendance.tsx +++ b/src/views/account/Attendance/Attendance.tsx @@ -22,10 +22,12 @@ import MissingItem from "@/components/Global/MissingItem"; import {hasFeatureAccountSetup} from "@/utils/multiservice"; import {MultiServiceFeature} from "@/stores/multiService/types"; import {AccountService} from "@/stores/account/types"; +import { OfflineWarning, useOnlineStatus } from "@/hooks/useOnlineStatus"; const Attendance: Screen<"Attendance"> = ({ route, navigation }) => { const theme = useTheme(); const account = useCurrentAccount(store => store.account!); + const { isOnline } = useOnlineStatus(); const hasServiceSetup = account.service === AccountService.PapillonMultiService ? hasFeatureAccountSetup(MultiServiceFeature.Attendance, account.localID) : true; @@ -36,6 +38,12 @@ const Attendance: Screen<"Attendance"> = ({ route, navigation }) => { const [isRefreshing, setIsRefreshing] = useState(false); const [isLoading, setLoading] = useState(hasServiceSetup); + useEffect(() => { + if (!isOnline && isLoading) { + setLoading(false); + } + }, [isOnline, isLoading]); + const [userSelectedPeriod, setUserSelectedPeriod] = useState(null); const selectedPeriod = useMemo(() => userSelectedPeriod ?? defaultPeriod, [userSelectedPeriod, defaultPeriod]); @@ -244,6 +252,8 @@ const Attendance: Screen<"Attendance"> = ({ route, navigation }) => { /> } > + {!isOnline && } + {hasServiceSetup && attendances[selectedPeriod] && attendances[selectedPeriod].absences.length === 0 && attendances[selectedPeriod].delays.length === 0 && attendances[selectedPeriod].punishments.length === 0 && Object.keys(attendances_observations_details).length === 0 &&( = ({ navigation, route }) => { const theme = useTheme(); + const { isOnline } = useOnlineStatus(); const { colors } = theme; @@ -101,7 +103,7 @@ const Discussions: Screen<"Discussions"> = ({ navigation, route }) => { return ( <> - {supported && enabled && ( + {isOnline && supported && enabled && ( = ({ navigation, route }) => { }} refreshControl={} > - {!chats ? ( + {!isOnline ? ( + + ) : !chats ? ( = ({ route, navigation }) => { const theme = useTheme(); const insets = useSafeAreaInsets(); + const { isOnline } = useOnlineStatus(); const outsideNav = route.params?.outsideNav; @@ -48,6 +50,12 @@ const Evaluation: Screen<"Evaluation"> = ({ route, navigation }) => { const [isRefreshing, setIsRefreshing] = useState(false); const [isLoading, setIsLoading] = useState(true); + useEffect(() => { + if (!isOnline && isLoading) { + setIsLoading(false); + } + }, [isOnline, isLoading]); + useEffect(() => { setTimeout(() => { if (!periods.map((period) => period.name).includes(selectedPeriod)) { @@ -176,6 +184,8 @@ const Evaluation: Screen<"Evaluation"> = ({ route, navigation }) => { paddingBottom: 16 + insets.bottom, }} > + {!isOnline && } + {(!evaluations[selectedPeriod] || evaluations[selectedPeriod].length === 0) && !isLoading && !isRefreshing && hasServiceSetup && ( diff --git a/src/views/account/Grades/Grades.tsx b/src/views/account/Grades/Grades.tsx index c32102afa..05478d506 100644 --- a/src/views/account/Grades/Grades.tsx +++ b/src/views/account/Grades/Grades.tsx @@ -35,6 +35,7 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import GradesScodocUE from "./Atoms/GradesScodocUE"; import {hasFeatureAccountSetup} from "@/utils/multiservice"; import {MultiServiceFeature} from "@/stores/multiService/types"; +import { OfflineWarning, useOnlineStatus } from "@/hooks/useOnlineStatus"; const GradesAverageGraph = lazy(() => import("./Graph/GradesAverage")); const GradesLatestList = lazy(() => import("./Latest/LatestGrades")); @@ -43,6 +44,7 @@ const Subject = lazy(() => import("./Subject/Subject")); const Grades: Screen<"Grades"> = ({ route, navigation }) => { const theme = useTheme(); const insets = useSafeAreaInsets(); + const { isOnline } = useOnlineStatus(); const outsideNav = route.params?.outsideNav; @@ -69,6 +71,12 @@ const Grades: Screen<"Grades"> = ({ route, navigation }) => { const [isRefreshing, setIsRefreshing] = useState(false); const [isLoading, setIsLoading] = useState(true); + useEffect(() => { + if (!isOnline && isLoading) { + setIsLoading(false); + } + }, [isOnline, isLoading]); + useEffect(() => { setTimeout(() => { if (!periods.map((period) => period.name).includes(selectedPeriod)) { @@ -204,6 +212,8 @@ const Grades: Screen<"Grades"> = ({ route, navigation }) => { paddingBottom: 16 + insets.bottom, }} > + {!isOnline && } + {(!grades[selectedPeriod] || grades[selectedPeriod].length === 0) && !isLoading && !isRefreshing && hasServiceSetup && ( diff --git a/src/views/account/Home/ModalContent.tsx b/src/views/account/Home/ModalContent.tsx index b051a581f..dc1c17e7d 100644 --- a/src/views/account/Home/ModalContent.tsx +++ b/src/views/account/Home/ModalContent.tsx @@ -1,19 +1,15 @@ -import {NativeItem, NativeList, NativeText} from "@/components/Global/NativeComponents"; -import React, {useCallback, useEffect, useMemo, useState} from "react"; +import { NativeList, NativeText} from "@/components/Global/NativeComponents"; +import React, {useCallback, useEffect, useState} from "react"; import Reanimated, { FadeInUp, FadeOutDown, - FadeOutUp, - FlipInXDown, LinearTransition, } from "react-native-reanimated"; -import { Sparkles, WifiOff, X} from "lucide-react-native"; +import { Sparkles, X} from "lucide-react-native"; import {useTheme} from "@react-navigation/native"; import PackageJSON from "../../../../package.json"; import {Dimensions, View} from "react-native"; -import NetInfo from "@react-native-community/netinfo"; -import {getErrorTitle} from "@/utils/format/get_papillon_error_title"; import {Elements, type Element} from "./ElementIndex"; import {animPapillon} from "@/utils/ui/animations"; import {useFlagsStore} from "@/stores/flags"; @@ -23,6 +19,7 @@ import {defaultTabs} from "@/consts/DefaultTabs"; import {NativeStackNavigationProp} from "@react-navigation/native-stack"; import {RouteParameters} from "@/router/helpers/types"; import { TouchableOpacity } from "react-native-gesture-handler"; +import { OfflineWarning, useOnlineStatus } from "@/hooks/useOnlineStatus"; interface ModalContentProps { navigation: NativeStackNavigationProp @@ -32,6 +29,7 @@ interface ModalContentProps { const ModalContent: React.FC = ({ navigation, refresh, endRefresh }) => { const { colors } = useTheme(); + const { isOnline } = useOnlineStatus(); const account = useCurrentAccount(store => store.account!); const mutateProperty = useCurrentAccount(store => store.mutateProperty); @@ -39,9 +37,6 @@ const ModalContent: React.FC = ({ navigation, refresh, endRef const [updatedRecently, setUpdatedRecently] = useState(false); const defined = useFlagsStore(state => state.defined); - const [isOnline, setIsOnline] = useState(false); - const errorTitle = useMemo(() => getErrorTitle(), []); - const [elements, setElements] = useState([]); useEffect(() => { @@ -134,12 +129,6 @@ const ModalContent: React.FC = ({ navigation, refresh, endRef }); }, []); - useEffect(() => { - return NetInfo.addEventListener(state => { - setIsOnline(state.isConnected ?? false); - }); - }, []); - return ( = ({ navigation, refresh, endRef )} - {!isOnline && - - - } - > - - {errorTitle.label} {errorTitle.emoji} - - - Tu es hors ligne. Les données affichées peuvent être obsolètes. - - - - - } + {!isOnline && } (null); const [shouldShowMoreGradient, setShouldShowMoreGradient] = useState(false); const account = useCurrentAccount((store) => store.account!); + const { isOnline } = useOnlineStatus(); + const { showAlert } = useAlert(); const route = useRoute(); @@ -65,8 +69,30 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total } const [isLoading, setIsLoading] = useState(false); const handlePress = useCallback(() => { - setIsLoading(true); - onDonePressHandler(); + if (isOnline) { + setIsLoading(true); + onDonePressHandler(); + } else { + if (Platform.OS === "ios") { + Alert.alert("Information", "Tu es hors ligne. Vérifie ta connexion Internet et réessaye", [ + { + text: "OK", + }, + ]); + } else { + showAlert({ + title: "Information", + message: "Tu es hors ligne. Vérifie ta connexion Internet et réessaye", + actions: [ + { + title: "OK", + onPress: () => {}, + backgroundColor: theme.colors.card, + }, + ], + }); + } + } }, [onDonePressHandler]); const [mainLoaded, setMainLoaded] = useState(false); diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index a7c132ceb..851d5b217 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -40,6 +40,7 @@ import {NativeScrollEvent, ScrollViewProps} from "react-native/Libraries/Compone import AsyncStorage from "@react-native-async-storage/async-storage"; import {hasFeatureAccountSetup} from "@/utils/multiservice"; import {MultiServiceFeature} from "@/stores/multiService/types"; +import { OfflineWarning, useOnlineStatus } from "@/hooks/useOnlineStatus"; type HomeworksPageProps = { index: number; @@ -67,6 +68,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { 320 ) : 0); const insets = useSafeAreaInsets(); + const { isOnline } = useOnlineStatus(); const outsideNav = route.params?.outsideNav; @@ -120,6 +122,12 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const [loading, setLoading] = useState(false); const [refreshing, setRefreshing] = useState(false); + useEffect(() => { + if (!isOnline && loading) { + setLoading(false); + } + }, [isOnline, loading]); + const [loadedWeeks, setLoadedWeeks] = useState([]); const updateHomeworks = useCallback(async (force = false, showRefreshing = true, showLoading = true) => { @@ -254,6 +262,8 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { /> } > + {!isOnline && } + {groupedHomework && Object.keys(groupedHomework).map((day, index) => ( { + const { isOnline } = useOnlineStatus(); + return ( + {!isOnline && } + {day && day.length > 0 && day[0].type !== "vacation" && day.map((item, i) => ( diff --git a/src/views/account/News/Document.tsx b/src/views/account/News/Document.tsx index ff15b0529..05a2dca72 100644 --- a/src/views/account/News/Document.tsx +++ b/src/views/account/News/Document.tsx @@ -16,7 +16,7 @@ import { MoreHorizontal, } from "lucide-react-native"; import React, { useEffect, useLayoutEffect, useState } from "react"; -import { View, Linking, TouchableOpacity, type GestureResponderEvent, StyleSheet } from "react-native"; +import { View, Linking, TouchableOpacity, type GestureResponderEvent, StyleSheet, Platform, Alert } from "react-native"; import { ScrollView } from "react-native-gesture-handler"; import HTMLView from "react-native-htmlview"; import { PapillonModernHeader } from "@/components/Global/PapillonModernHeader"; @@ -29,12 +29,16 @@ import { AttachmentType } from "@/services/shared/Attachment"; import PapillonCheckbox from "@/components/Global/PapillonCheckbox"; import { newsInformationAcknowledge } from "pawnote"; import { AccountService } from "@/stores/account/types"; +import { useOnlineStatus } from "@/hooks/useOnlineStatus"; +import { useAlert } from "@/providers/AlertProvider"; const NewsItem: Screen<"NewsItem"> = ({ route, navigation }) => { const [message, setMessage] = useState(JSON.parse(route.params.message) as Information); const important = route.params.important; const isED = route.params.isED; const account = useCurrentAccount((store) => store.account!); + const { isOnline } = useOnlineStatus(); + const { showAlert } = useAlert(); const theme = useTheme(); const stylesText = StyleSheet.create({ @@ -105,11 +109,33 @@ const NewsItem: Screen<"NewsItem"> = ({ route, navigation }) => { ? "Marquer comme non lu" : "Marquer comme lu", onPress: () => { - setNewsRead(account, message, !message.read); - setMessage((prev) => ({ - ...prev, - read: !prev.read, - })); + if (isOnline) { + setNewsRead(account, message, !message.read); + setMessage((prev) => ({ + ...prev, + read: !prev.read, + })); + } else { + if (Platform.OS === "ios") { + Alert.alert("Information", "Tu es hors ligne. Vérifie ta connexion Internet et réessaye", [ + { + text: "OK", + }, + ]); + } else { + showAlert({ + title: "Information", + message: "Tu es hors ligne. Vérifie ta connexion Internet et réessaye", + actions: [ + { + title: "OK", + onPress: () => {}, + backgroundColor: theme.colors.card, + }, + ], + }); + } + } }, }, ]} diff --git a/src/views/account/News/News.tsx b/src/views/account/News/News.tsx index 7afe0c7bf..ef9ff0077 100644 --- a/src/views/account/News/News.tsx +++ b/src/views/account/News/News.tsx @@ -20,6 +20,7 @@ import {Information} from "@/services/shared/Information"; import {AccountService} from "@/stores/account/types"; import {hasFeatureAccountSetup} from "@/utils/multiservice"; import {MultiServiceFeature} from "@/stores/multiService/types"; +import { OfflineWarning, useOnlineStatus } from "@/hooks/useOnlineStatus"; type NewsItem = Omit & { date: string, important: boolean }; @@ -33,6 +34,13 @@ const NewsScreen: Screen<"News"> = ({ route, navigation }) => { const [importantMessages, setImportantMessages] = useState([]); const [sortedMessages, setSortedMessages] = useState([]); const [isED, setIsED] = useState(false); + const { isOnline } = useOnlineStatus(); + + useEffect(() => { + if (!isOnline && isLoading) { + setIsLoading(false); + } + }, [isOnline, isLoading]); useLayoutEffect(() => { navigation.setOptions({ @@ -128,6 +136,8 @@ const NewsScreen: Screen<"News"> = ({ route, navigation }) => { } > + {!isOnline && } + {importantMessages.length > 0 && ( = ({ route, navigation }) => { const theme = useTheme(); const { colors } = theme; + const { isOnline } = useOnlineStatus(); const account = useCurrentAccount((store) => store.account); const linkedAccounts = useCurrentAccount((store) => store.linkedAccounts); @@ -317,7 +319,9 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { /> } > - {!isInitialised ? ( + {!isOnline ? ( + + ) : !isInitialised ? ( ) : ( <> diff --git a/src/views/login/ServiceSelector.tsx b/src/views/login/ServiceSelector.tsx index beeeca622..399725430 100644 --- a/src/views/login/ServiceSelector.tsx +++ b/src/views/login/ServiceSelector.tsx @@ -1,5 +1,5 @@ import React, { memo, useEffect, useState } from "react"; -import { Image, View, StyleSheet } from "react-native"; +import { Image, View, StyleSheet, Platform, Alert } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; import Reanimated, { LinearTransition, FlipInXDown } from "react-native-reanimated"; @@ -15,11 +15,13 @@ import { useTheme } from "@react-navigation/native"; import GetV6Data from "@/utils/login/GetV6Data"; import { School } from "lucide-react-native"; import { LinearGradient } from "expo-linear-gradient"; +import { useOnlineStatus } from "@/hooks/useOnlineStatus"; const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { const theme = useTheme(); const { colors } = theme; const [sound, setSound] = useState(null); + const { isOnline } = useOnlineStatus(); const { showAlert } = useAlert(); @@ -206,7 +208,36 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { primary value="Confirmer" disabled={service === null} - onPress={services.find((srv) => srv.name === service)?.login} + onPress={ + isOnline + ? services.find((srv) => srv.name === service)?.login + : () => { + if (Platform.OS === "ios") { + Alert.alert( + "Information", + "Pour poursuivre la connexion, tu dois être connecté à Internet. Vérifie ta connexion Internet et réessaye", + [ + { + text: "OK", + }, + ] + ); + } else { + showAlert({ + title: "Information", + message: + "Pour poursuivre la connexion, tu dois être connecté à Internet. Vérifie ta connexion Internet et réessaye", + actions: [ + { + title: "OK", + onPress: () => {}, + backgroundColor: theme.colors.card, + }, + ], + }); + } + } + } /> {v6Data && v6Data.restore && ( From 6664d5ca887e6d337e043530dc1b7ca6b5b5de60 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 16:08:31 +0100 Subject: [PATCH 0589/1144] =?UTF-8?q?feat(onlineStatus):=20reconnexion=20a?= =?UTF-8?q?u=20compte=20quand=20l'utilisateur=20se=20reconnecte=20=C3=A0?= =?UTF-8?q?=20Internet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useOnlineStatus.tsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/hooks/useOnlineStatus.tsx b/src/hooks/useOnlineStatus.tsx index 1d5c39f34..84f272f50 100644 --- a/src/hooks/useOnlineStatus.tsx +++ b/src/hooks/useOnlineStatus.tsx @@ -13,8 +13,13 @@ import Reanimated, { } from "react-native-reanimated"; import { WifiOff } from "lucide-react-native"; import { animPapillon } from "@/utils/ui/animations"; +import { useCurrentAccount } from "@/stores/account"; const useOnlineStatus = () => { + const account = useCurrentAccount((store) => store.account!); + const switchTo = useCurrentAccount((store) => store.switchTo); + + const [isAlreadyOffline, setIsAlreadyOffline] = useState(false); const [isOnline, setIsOnline] = useState(true); const errorTitle = useMemo(() => getErrorTitle(), []); @@ -26,6 +31,17 @@ const useOnlineStatus = () => { return () => unsubscribe(); }, []); + useEffect(() => { + const reloadAccount = async () => await switchTo(account); + + if (isOnline && isAlreadyOffline) { + reloadAccount(); + setIsAlreadyOffline(false); + } else if (!isOnline) { + setIsAlreadyOffline(true); + } + }, [isOnline]); + return { isOnline, errorTitle }; }; From 54061cec5423ab41d5ef463494c8472c8cd105d5 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 16:12:44 +0100 Subject: [PATCH 0590/1144] =?UTF-8?q?feat(news):=20ajout=20de=20la=20gesti?= =?UTF-8?q?on=20de=20l'=C3=A9tat=20hors=20ligne=20lorsqu'on=20veut=20coche?= =?UTF-8?q?r=20la=20prise=20en=20connaissance=20d'une=20actualit=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/News/Document.tsx | 40 ++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/src/views/account/News/Document.tsx b/src/views/account/News/Document.tsx index 05a2dca72..203bc94a4 100644 --- a/src/views/account/News/Document.tsx +++ b/src/views/account/News/Document.tsx @@ -193,16 +193,38 @@ const NewsItem: Screen<"NewsItem"> = ({ route, navigation }) => { checked={message.acknowledged} onPress={async () => { if (!message.acknowledged && account.instance) { - await newsInformationAcknowledge( - account.instance, - message.ref - ); + if (isOnline) { + await newsInformationAcknowledge( + account.instance, + message.ref + ); - setMessage((prev) => ({ - ...prev, - read: true, - acknowledged: true, - })); + setMessage((prev) => ({ + ...prev, + read: true, + acknowledged: true, + })); + } else { + if (Platform.OS === "ios") { + Alert.alert("Information", "Tu es hors ligne. Vérifie ta connexion Internet et réessaye", [ + { + text: "OK", + }, + ]); + } else { + showAlert({ + title: "Information", + message: "Tu es hors ligne. Vérifie ta connexion Internet et réessaye", + actions: [ + { + title: "OK", + onPress: () => {}, + backgroundColor: theme.colors.card, + }, + ], + }); + } + } } }} color={theme.colors.primary} From 69ed5af579338618b5e9ce4c6036f7af4bb220e0 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 16:13:15 +0100 Subject: [PATCH 0591/1144] fix(alerts): correction de la formulation des messages d'alerte pour la connexion Internet --- src/hooks/useOnlineStatus.tsx | 2 +- src/views/account/Homeworks/Atoms/Item.tsx | 4 ++-- src/views/account/News/Document.tsx | 8 ++++---- .../IdentityProvider/actions/BackgroundIUTLannion.tsx | 4 ++-- src/views/login/ServiceSelector.tsx | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/hooks/useOnlineStatus.tsx b/src/hooks/useOnlineStatus.tsx index 84f272f50..1123406e0 100644 --- a/src/hooks/useOnlineStatus.tsx +++ b/src/hooks/useOnlineStatus.tsx @@ -66,7 +66,7 @@ const OfflineWarning = ({ cache = false }) => { Tu es hors ligne.{" "} {cache ? "Les données affichées peuvent être obsolètes." - : "Vérifie ta connexion Internet et réessaye"} + : "Vérifie ta connexion Internet et réessaie"} diff --git a/src/views/account/Homeworks/Atoms/Item.tsx b/src/views/account/Homeworks/Atoms/Item.tsx index ae2f30ea7..864dad88b 100644 --- a/src/views/account/Homeworks/Atoms/Item.tsx +++ b/src/views/account/Homeworks/Atoms/Item.tsx @@ -74,7 +74,7 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total } onDonePressHandler(); } else { if (Platform.OS === "ios") { - Alert.alert("Information", "Tu es hors ligne. Vérifie ta connexion Internet et réessaye", [ + Alert.alert("Information", "Tu es hors ligne. Vérifie ta connexion Internet et réessaie", [ { text: "OK", }, @@ -82,7 +82,7 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total } } else { showAlert({ title: "Information", - message: "Tu es hors ligne. Vérifie ta connexion Internet et réessaye", + message: "Tu es hors ligne. Vérifie ta connexion Internet et réessaie", actions: [ { title: "OK", diff --git a/src/views/account/News/Document.tsx b/src/views/account/News/Document.tsx index 203bc94a4..343711f9a 100644 --- a/src/views/account/News/Document.tsx +++ b/src/views/account/News/Document.tsx @@ -117,7 +117,7 @@ const NewsItem: Screen<"NewsItem"> = ({ route, navigation }) => { })); } else { if (Platform.OS === "ios") { - Alert.alert("Information", "Tu es hors ligne. Vérifie ta connexion Internet et réessaye", [ + Alert.alert("Information", "Tu es hors ligne. Vérifie ta connexion Internet et réessaie", [ { text: "OK", }, @@ -125,7 +125,7 @@ const NewsItem: Screen<"NewsItem"> = ({ route, navigation }) => { } else { showAlert({ title: "Information", - message: "Tu es hors ligne. Vérifie ta connexion Internet et réessaye", + message: "Tu es hors ligne. Vérifie ta connexion Internet et réessaie", actions: [ { title: "OK", @@ -206,7 +206,7 @@ const NewsItem: Screen<"NewsItem"> = ({ route, navigation }) => { })); } else { if (Platform.OS === "ios") { - Alert.alert("Information", "Tu es hors ligne. Vérifie ta connexion Internet et réessaye", [ + Alert.alert("Information", "Tu es hors ligne. Vérifie ta connexion Internet et réessaie", [ { text: "OK", }, @@ -214,7 +214,7 @@ const NewsItem: Screen<"NewsItem"> = ({ route, navigation }) => { } else { showAlert({ title: "Information", - message: "Tu es hors ligne. Vérifie ta connexion Internet et réessaye", + message: "Tu es hors ligne. Vérifie ta connexion Internet et réessaie", actions: [ { title: "OK", diff --git a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx index 9f748729c..45d2b7772 100644 --- a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx +++ b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx @@ -108,7 +108,7 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio console.error(e); Alert.alert( "Erreur", - "Impossible de récupérer les notes de l'IUT de Lannion. Vérifie ta connexion internet et réessaye.", + "Impossible de récupérer les notes de l'IUT de Lannion. Vérifie ta connexion Internet et réessaie.", [{ text: "OK", onPress: () => navigation.goBack() }] ); navigation.goBack(); @@ -320,7 +320,7 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio console.error(data); Alert.alert( "Erreur", - "Impossible de se connecter au portail de l'IUT de Lannion. Vérifie ta connexion internet et réessaye.", + "Impossible de se connecter au portail de l'IUT de Lannion. Vérifie ta connexion Internet et réessaie.", [{ text: "OK", onPress: () => navigation.goBack() }] ); navigation.goBack(); diff --git a/src/views/login/ServiceSelector.tsx b/src/views/login/ServiceSelector.tsx index 399725430..d73646f7b 100644 --- a/src/views/login/ServiceSelector.tsx +++ b/src/views/login/ServiceSelector.tsx @@ -215,7 +215,7 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { if (Platform.OS === "ios") { Alert.alert( "Information", - "Pour poursuivre la connexion, tu dois être connecté à Internet. Vérifie ta connexion Internet et réessaye", + "Pour poursuivre la connexion, tu dois être connecté à Internet. Vérifie ta connexion Internet et réessaie", [ { text: "OK", @@ -226,7 +226,7 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { showAlert({ title: "Information", message: - "Pour poursuivre la connexion, tu dois être connecté à Internet. Vérifie ta connexion Internet et réessaye", + "Pour poursuivre la connexion, tu dois être connecté à Internet. Vérifie ta connexion Internet et réessaie", actions: [ { title: "OK", From b2ee652d61c131fac84864dde2564f9f680b445f Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 17:50:27 +0100 Subject: [PATCH 0592/1144] fix(notifee): import notifee dynamically (when is not Expo Go) --- App.tsx | 13 +++++-- src/background/BackgroundTasks.ts | 62 +++++++++++++++++-------------- src/background/Notifications.ts | 15 +++++--- 3 files changed, 53 insertions(+), 37 deletions(-) diff --git a/App.tsx b/App.tsx index f5731b656..b681a2765 100644 --- a/App.tsx +++ b/App.tsx @@ -1,5 +1,4 @@ import "@/background/BackgroundTasks"; -import notifee, { Notification } from "@notifee/react-native"; import Router from "@/router"; import { useFonts } from "expo-font"; import * as SplashScreen from "expo-splash-screen"; @@ -47,6 +46,8 @@ export default function App () { }; const checkInitialNotification = async () => { + const notifee = (await import("@notifee/react-native")).default; + const initialNotification = await notifee.getInitialNotification(); if (initialNotification) { await handleNotificationPress(initialNotification.notification); @@ -54,7 +55,7 @@ export default function App () { }; useEffect(() => { - checkInitialNotification(); + if (!isExpoGo()) checkInitialNotification(); }, []); const [appState, setAppState] = useState(AppState.currentState); @@ -118,8 +119,12 @@ export default function App () { if (nextAppState === "active") { log("🔄 App is active", "AppState"); - await notifee.setBadgeCount(0); - await notifee.cancelAllNotifications(); + if (!isExpoGo()) { + const notifee = (await import("@notifee/react-native")).default; + + await notifee.setBadgeCount(0); + await notifee.cancelAllNotifications(); + } await handleBackgroundState(); backgroundStartTime.current = null; diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 9e18219d2..d9ea06643 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -1,4 +1,3 @@ -import notifee, { EventType } from "@notifee/react-native"; import * as BackgroundFetch from "expo-background-fetch"; import * as TaskManager from "expo-task-manager"; import { BackgroundFetchResult } from "expo-background-fetch"; @@ -14,41 +13,50 @@ import { fetchAttendance } from "./data/Attendance"; import { fetchEvaluation } from "./data/Evaluation"; import { papillonNotify } from "./Notifications"; -// Gestion des badges quand app en arrière-plan -notifee.onBackgroundEvent(async ({ type, detail }) => { - const { notification, pressAction } = detail; +const notifeeEvent = async () => { + const notifee = (await import("@notifee/react-native")).default; + const EventType = (await import("@notifee/react-native")).EventType; - switch (type) { - case EventType.ACTION_PRESS: - console.log(`[Notifee] Action press: ${pressAction?.id}`); + // Gestion des badges quand app en arrière-plan + notifee.onBackgroundEvent(async ({ type, detail }) => { + const { notification, pressAction } = detail; - case EventType.DISMISSED: - let badgeCount = await notifee.getBadgeCount(); - badgeCount--; - await notifee.setBadgeCount(badgeCount); - break; - } -}); + switch (type) { + case EventType.ACTION_PRESS: + console.log(`[Notifee] Action press: ${pressAction?.id}`); -// Gestion des badges quand app en premier plan -notifee.onForegroundEvent(async ({ type, detail }) => { - const { notification, pressAction } = detail; + case EventType.DISMISSED: + let badgeCount = await notifee.getBadgeCount(); + badgeCount--; + await notifee.setBadgeCount(badgeCount); + break; + } + }); - switch (type) { - case EventType.ACTION_PRESS: - console.log(`[Notifee] Action press: ${pressAction?.id}`); + // Gestion des badges quand app en premier plan + notifee.onForegroundEvent(async ({ type, detail }) => { + const { notification, pressAction } = detail; - case EventType.DISMISSED: - let badgeCount = await notifee.getBadgeCount(); - badgeCount--; - await notifee.setBadgeCount(badgeCount); - break; - } -}); + switch (type) { + case EventType.ACTION_PRESS: + console.log(`[Notifee] Action press: ${pressAction?.id}`); + + case EventType.DISMISSED: + let badgeCount = await notifee.getBadgeCount(); + badgeCount--; + await notifee.setBadgeCount(badgeCount); + break; + } + }); +}; + +if (!isExpoGo()) notifeeEvent(); let isBackgroundFetchRunning = false; const backgroundFetch = async () => { + const notifee = (await import("@notifee/react-native")).default; + if (isBackgroundFetchRunning) { warn("⚠️ Background fetch already running. Skipping...", "BackgroundEvent"); return BackgroundFetchResult.NoData; diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index 2cb982bef..637fe9d3a 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -1,14 +1,11 @@ import { alertExpoGo, isExpoGo } from "@/utils/native/expoGoAlert"; -import notifee, { - AndroidColor, - AndroidImportance, - AuthorizationStatus, - Notification, -} from "@notifee/react-native"; import { Platform } from "react-native"; const requestNotificationPermission = async () => { if (!isExpoGo()) { + const notifee = (await import("@notifee/react-native")).default; + const AuthorizationStatus = (await import("@notifee/react-native")).AuthorizationStatus; + const settings = await notifee.requestPermission(); if (Platform.OS === "ios") { if (settings.authorizationStatus >= AuthorizationStatus.AUTHORIZED) { @@ -28,6 +25,9 @@ const requestNotificationPermission = async () => { }; const createChannelNotification = async () => { + const notifee = (await import("@notifee/react-native")).default; + const AndroidImportance = (await import("@notifee/react-native")).AndroidImportance; + await notifee.createChannel({ id: "Test", name: "Test", @@ -112,6 +112,9 @@ const papillonNotify = async ( | "Attendance" | "Evaluation" ) => { + const notifee = (await import("@notifee/react-native")).default; + const AndroidColor = (await import("@notifee/react-native")).AndroidColor; + // Add timestamp for Android const timestamp = new Date().getTime(); From 25fd803a1e2421a85d6b3bf6364c5b26c9bcdf9e Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 17:52:54 +0100 Subject: [PATCH 0593/1144] fix(ts): ajouter l'importation de Notification depuis notifee --- App.tsx | 1 + src/background/Notifications.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/App.tsx b/App.tsx index b681a2765..15cbc8826 100644 --- a/App.tsx +++ b/App.tsx @@ -1,4 +1,5 @@ import "@/background/BackgroundTasks"; +import { Notification } from "@notifee/react-native"; import Router from "@/router"; import { useFonts } from "expo-font"; import * as SplashScreen from "expo-splash-screen"; diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index 637fe9d3a..16e06ba9a 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -1,4 +1,5 @@ import { alertExpoGo, isExpoGo } from "@/utils/native/expoGoAlert"; +import { Notification } from "@notifee/react-native"; import { Platform } from "react-native"; const requestNotificationPermission = async () => { From c9a8e873019b4103027a39911fe47ab1d7358578 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 18:16:02 +0100 Subject: [PATCH 0594/1144] =?UTF-8?q?refactor(background):=20simplifier=20?= =?UTF-8?q?la=20d=C3=A9finition=20et=20l'enregistrement=20des=20t=C3=A2che?= =?UTF-8?q?s=20en=20utilisant=20des=20promesses?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/BackgroundTasks.ts | 36 +++++++++++++++---------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index d9ea06643..629021156 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -112,23 +112,17 @@ const backgroundFetch = async () => { } }; -if (!isExpoGo()) { - TaskManager.defineTask("background-fetch", backgroundFetch); -} +if (!isExpoGo()) TaskManager.defineTask("background-fetch", backgroundFetch); -const unsetBackgroundFetch = async () => { +const unsetBackgroundFetch = async () => await BackgroundFetch.unregisterTaskAsync("background-fetch"); - log("✅ Background task unregistered", "BackgroundEvent"); -}; -const setBackgroundFetch = async () => { +const setBackgroundFetch = async () => await BackgroundFetch.registerTaskAsync("background-fetch", { minimumInterval: 60 * 15, stopOnTerminate: false, startOnBoot: true, }); - log("✅ Background task registered", "BackgroundEvent"); -}; const registerBackgroundTasks = async () => { const isRegistered = await TaskManager.isTaskRegisteredAsync( @@ -140,20 +134,24 @@ const registerBackgroundTasks = async () => { "⚠️ Background task already registered. Unregister background task...", "BackgroundEvent" ); - await unsetBackgroundFetch().catch((ERRfatal) => { - error( - `❌ Failed to unregister background task: ${ERRfatal}`, - "BackgroundEvent" + await unsetBackgroundFetch() + .then(() => log("✅ Background task unregistered", "BackgroundEvent")) + .catch((ERRfatal) => + error( + `❌ Failed to unregister background task: ${ERRfatal}`, + "BackgroundEvent" + ) ); - }); } - await setBackgroundFetch().catch((ERRfatal) => { - error( - `❌ Failed to register background task: ${ERRfatal}`, - "BackgroundEvent" + await setBackgroundFetch() + .then(() => log("✅ Background task registered", "BackgroundEvent")) + .catch((ERRfatal) => + error( + `❌ Failed to register background task: ${ERRfatal}`, + "BackgroundEvent" + ) ); - }); }; export { registerBackgroundTasks, unsetBackgroundFetch }; From 6f508f9c09c81fe76d7ee7da2d0e005cde056b18 Mon Sep 17 00:00:00 2001 From: Bulgus <88266443+Bulgus@users.noreply.github.com> Date: Sun, 9 Feb 2025 21:26:37 +0100 Subject: [PATCH 0595/1144] typo(homework): change no homework widget sentence --- src/views/account/Home/Elements/HomeworksElement.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 6e7e2de34..d56418121 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -99,7 +99,7 @@ const HomeworksElement: React.FC = ({ navigation, onImpor From 7afb1e9bfa45850336f399aa9c12d7528fdb2034 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 21:51:18 +0100 Subject: [PATCH 0596/1144] =?UTF-8?q?fix(background):=20uniformiser=20les?= =?UTF-8?q?=20messages=20de=20journalisation=20en=20rempla=C3=A7ant=20"Bac?= =?UTF-8?q?kgroundEvent"=20par=20"BACKGROUND"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/BackgroundTasks.ts | 28 ++++++++++++++-------------- src/views/welcome/DevMenu.tsx | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 629021156..719924fad 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -58,7 +58,7 @@ const backgroundFetch = async () => { const notifee = (await import("@notifee/react-native")).default; if (isBackgroundFetchRunning) { - warn("⚠️ Background fetch already running. Skipping...", "BackgroundEvent"); + warn("⚠️ Background fetch already running. Skipping...", "BACKGROUND"); return BackgroundFetchResult.NoData; } @@ -86,25 +86,25 @@ const backgroundFetch = async () => { account.personalization.notifications; if (notificationsTypesPermissions?.enabled) { - info("▶️ Running background News", "BackgroundEvent"); + info("▶️ Running background News", "BACKGROUND"); await fetchNews(); - info("▶️ Running background Homeworks", "BackgroundEvent"); + info("▶️ Running background Homeworks", "BACKGROUND"); await fetchHomeworks(); - info("▶️ Running background Grades", "BackgroundEvent"); + info("▶️ Running background Grades", "BACKGROUND"); await fetchGrade(); - info("▶️ Running background Lessons", "BackgroundEvent"); + info("▶️ Running background Lessons", "BACKGROUND"); await fetchLessons(); - info("▶️ Running background Attendance", "BackgroundEvent"); + info("▶️ Running background Attendance", "BACKGROUND"); await fetchAttendance(); - info("▶️ Running background Evaluation", "BackgroundEvent"); + info("▶️ Running background Evaluation", "BACKGROUND"); await fetchEvaluation(); } } - log("✅ Finish background fetch", "BackgroundEvent"); + log("✅ Finish background fetch", "BACKGROUND"); return BackgroundFetchResult.NewData; } catch (ERRfatal) { - error(`❌ Task failed: ${ERRfatal}`, "BackgroundEvent"); + error(`❌ Task failed: ${ERRfatal}`, "BACKGROUND"); return BackgroundFetchResult.Failed; } finally { isBackgroundFetchRunning = false; @@ -132,24 +132,24 @@ const registerBackgroundTasks = async () => { if (isRegistered) { warn( "⚠️ Background task already registered. Unregister background task...", - "BackgroundEvent" + "BACKGROUND" ); await unsetBackgroundFetch() - .then(() => log("✅ Background task unregistered", "BackgroundEvent")) + .then(() => log("✅ Background task unregistered", "BACKGROUND")) .catch((ERRfatal) => error( `❌ Failed to unregister background task: ${ERRfatal}`, - "BackgroundEvent" + "BACKGROUND" ) ); } await setBackgroundFetch() - .then(() => log("✅ Background task registered", "BackgroundEvent")) + .then(() => log("✅ Background task registered", "BACKGROUND")) .catch((ERRfatal) => error( `❌ Failed to register background task: ${ERRfatal}`, - "BackgroundEvent" + "BACKGROUND" ) ); }; diff --git a/src/views/welcome/DevMenu.tsx b/src/views/welcome/DevMenu.tsx index 57ca0aac1..7e3f17db9 100644 --- a/src/views/welcome/DevMenu.tsx +++ b/src/views/welcome/DevMenu.tsx @@ -32,7 +32,7 @@ const DevMenu: Screen<"DevMenu"> = ({ navigation }) => { setIsBackgroundActive(isRegistered); }, 500); } catch (err) { - error(`❌ Failed to register background task: ${err}`, "BackgroundEvent"); + error(`❌ Failed to register background task: ${err}`, "BACKGROUND"); setIsBackgroundActive(false); } }; From 852d7f00e3c8a63f3412990cc20e65c7560f172a Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 21:53:23 +0100 Subject: [PATCH 0597/1144] =?UTF-8?q?fix(devmenu):=20ajouter=20des=20journ?= =?UTF-8?q?aux=20pour=20l'enregistrement=20et=20la=20d=C3=A9sinscription?= =?UTF-8?q?=20des=20t=C3=A2ches=20en=20arri=C3=A8re-plan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/welcome/DevMenu.tsx | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/views/welcome/DevMenu.tsx b/src/views/welcome/DevMenu.tsx index 7e3f17db9..8f3e03719 100644 --- a/src/views/welcome/DevMenu.tsx +++ b/src/views/welcome/DevMenu.tsx @@ -12,7 +12,7 @@ import { import AsyncStorage from "@react-native-async-storage/async-storage"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; import * as TaskManager from "expo-task-manager"; -import { error } from "@/utils/logger/logger"; +import { error, log } from "@/utils/logger/logger"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { registerBackgroundTasks, unsetBackgroundFetch } from "@/background/BackgroundTasks"; import { isExpoGo } from "@/utils/native/expoGoAlert"; @@ -236,10 +236,24 @@ const DevMenu: Screen<"DevMenu"> = ({ navigation }) => { setLoading(true); setIsBackgroundActive(null); if (isBackgroundActive) { - await unsetBackgroundFetch(); + await unsetBackgroundFetch() + .then(() => log("✅ Background task unregistered", "BACKGROUND")) + .catch((ERRfatal) => + error( + `❌ Failed to unregister background task: ${ERRfatal}`, + "BACKGROUND" + ) + );; } - await registerBackgroundTasks(); + await registerBackgroundTasks() + .then(() => log("✅ Background task registered", "BACKGROUND")) + .catch((ERRfatal) => + error( + `❌ Failed to register background task: ${ERRfatal}`, + "BACKGROUND" + ) + ); setTimeout(() => { setLoading(false); }, 500); From ba5249bf00887cba0742a2f9cdb49baa8fe94400 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 22:25:55 +0100 Subject: [PATCH 0598/1144] =?UTF-8?q?feat(serviceSelector):=20ajout=20de?= =?UTF-8?q?=20la=20v=C3=A9rification=20de=20la=20connexion=20Internet=20av?= =?UTF-8?q?ant=20de=20naviguer=20vers=20la=20m=C3=A9thode=20de=20s=C3=A9le?= =?UTF-8?q?ction=20de=20compte?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExternalAccount/ServiceSelector.tsx | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/views/settings/ExternalAccount/ServiceSelector.tsx b/src/views/settings/ExternalAccount/ServiceSelector.tsx index 09fdcaf12..d04fcf759 100644 --- a/src/views/settings/ExternalAccount/ServiceSelector.tsx +++ b/src/views/settings/ExternalAccount/ServiceSelector.tsx @@ -2,19 +2,23 @@ import React, { useState } from "react"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { SafeAreaView, useSafeAreaInsets } from "react-native-safe-area-context"; -import { Image, View, StyleSheet } from "react-native"; +import { Image, View, StyleSheet, Platform, Alert } from "react-native"; import Reanimated, { LinearTransition, FlipInXDown } from "react-native-reanimated"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; import { AccountService } from "@/stores/account/types"; import { useCurrentAccount } from "@/stores/account"; import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; +import { useOnlineStatus } from "@/hooks/useOnlineStatus"; +import { useAlert } from "@/providers/AlertProvider"; const ExternalAccountSelector: Screen<"ExternalAccountSelector"> = ({ navigation, route }) => { const theme = useTheme(); const { colors } = theme; const insets = useSafeAreaInsets(); const account = useCurrentAccount(store => store.account!); + const { isOnline } = useOnlineStatus(); + const { showAlert } = useAlert(); type Service = AccountService | "Other"; @@ -104,7 +108,34 @@ const ExternalAccountSelector: Screen<"ExternalAccountSelector"> = ({ navigation disabled={!service || service === "Other"} onPress={() => { if (service) { - navigation.navigate("ExternalAccountSelectMethod", { service }); + if (isOnline) { + navigation.navigate("ExternalAccountSelectMethod", { service }); + } else { + if (Platform.OS === "ios") { + Alert.alert( + "Information", + "Pour poursuivre la connexion, tu dois être connecté à Internet. Vérifie ta connexion Internet et réessaie", + [ + { + text: "OK", + }, + ] + ); + } else { + showAlert({ + title: "Information", + message: + "Pour poursuivre la connexion, tu dois être connecté à Internet. Vérifie ta connexion Internet et réessaie", + actions: [ + { + title: "OK", + onPress: () => {}, + backgroundColor: theme.colors.card, + }, + ], + }); + } + } } }} /> From db7274e3595a33b7a6e5c63326b9c36c9a07a040 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 22:28:38 +0100 Subject: [PATCH 0599/1144] =?UTF-8?q?feat(changelog):=20ajout=20d'un=20ave?= =?UTF-8?q?rtissement=20hors=20ligne=20et=20am=C3=A9lioration=20des=20mess?= =?UTF-8?q?ages=20d'erreur?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/welcome/ChangelogScreen.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/views/welcome/ChangelogScreen.tsx b/src/views/welcome/ChangelogScreen.tsx index 2181af7b4..4cc79d069 100644 --- a/src/views/welcome/ChangelogScreen.tsx +++ b/src/views/welcome/ChangelogScreen.tsx @@ -18,6 +18,7 @@ import { PressableScale } from "react-native-pressable-scale"; import AsyncStorage from "@react-native-async-storage/async-storage"; import {Screen} from "@/router/helpers/types"; +import { OfflineWarning, useOnlineStatus } from "@/hooks/useOnlineStatus"; interface Feature { title: string; @@ -44,6 +45,7 @@ const changelogURL = datasets.changelog.replace("[version]", currentVersion); const ChangelogScreen: Screen<"ChangelogScreen"> = ({ route, navigation }) => { const theme = useTheme(); const insets = useSafeAreaInsets(); + const { isOnline } = useOnlineStatus(); const [changelog, setChangelog] = useState(null); const [loading, setLoading] = useState(true); @@ -129,6 +131,8 @@ const ChangelogScreen: Screen<"ChangelogScreen"> = ({ route, navigation }) => { )} + {!isOnline && } + {notFound && ( = ({ route, navigation }) => { Impossible de trouver les notes de mise à jour - Tu es peut-être hors-ligne ou alors une erreur est survenue... + Les nouveautés de la version n'ont pas du être renseignées ou + alors une erreur est survenue... From 01a49134a36c8ddf0d46419b1eff99d2bd44ec0a Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 22:33:00 +0100 Subject: [PATCH 0600/1144] =?UTF-8?q?refractor(changelog):=20r=C3=A9organi?= =?UTF-8?q?sation=20du=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/welcome/ChangelogScreen.tsx | 220 +++++++++++++------------- 1 file changed, 107 insertions(+), 113 deletions(-) diff --git a/src/views/welcome/ChangelogScreen.tsx b/src/views/welcome/ChangelogScreen.tsx index 4cc79d069..e9b698c1d 100644 --- a/src/views/welcome/ChangelogScreen.tsx +++ b/src/views/welcome/ChangelogScreen.tsx @@ -111,7 +111,9 @@ const ChangelogScreen: Screen<"ChangelogScreen"> = ({ route, navigation }) => { ]} contentInsetAdjustmentBehavior="automatic" > - {loading && ( + {!isOnline ? ( + + ) : loading ? ( = ({ route, navigation }) => { exiting={animPapillon(FadeOutUp)} > } + leading={ + + } > Chargement des nouveautés... @@ -129,20 +137,14 @@ const ChangelogScreen: Screen<"ChangelogScreen"> = ({ route, navigation }) => { - )} - - {!isOnline && } - - {notFound && ( + ) : notFound ? ( - } - > + }> Impossible de trouver les notes de mise à jour @@ -152,116 +154,108 @@ const ChangelogScreen: Screen<"ChangelogScreen"> = ({ route, navigation }) => { - )} - - {changelog && ( - - - - - - + + + - Papillon - version {changelog.version} - - - {changelog.title} - - - {changelog.subtitle} - - - - - {changelog.description} - - - - + /> + + + Papillon - version {changelog.version} + + + {changelog.title} + + + {changelog.subtitle} + + + + + {changelog.description} + + + + - - } - /> + + } + /> - - {changelog.features.map((feature: Feature, index) => { - return ( - - ); - })} - - + + {changelog.features.map((feature: Feature, index) => { + return ( + + ); + })} + + - - } - /> + + } /> - - {changelog.bugfixes.map((feature: Feature, index) => { - return ( - - ); - })} - + + {changelog.bugfixes.map((feature: Feature, index) => { + return ( + + ); + })} + + - + ) )} From 7d1833ebd61d30f5104b6f86c112257bd9bcf9ba Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Feb 2025 22:42:14 +0100 Subject: [PATCH 0601/1144] =?UTF-8?q?feat(Week):=20ajout=20d'un=20avertiss?= =?UTF-8?q?ement=20hors=20ligne=20bas=C3=A9=20sur=20l'=C3=A9tat=20de=20la?= =?UTF-8?q?=20connexion=20Internet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Week/Week.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index 901e25bd1..ecaa95c32 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -19,6 +19,7 @@ import { NativeText } from "@/components/Global/NativeComponents"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import type { Screen } from "@/router/helpers/types"; import {Account} from "@/stores/account/types"; +import { OfflineWarning, useOnlineStatus } from "@/hooks/useOnlineStatus"; const LOCALES = { en: { @@ -215,6 +216,7 @@ const displayModes = ["Semaine", "3 jours", "Journée"]; const Week: Screen<"Week"> = ({ route, navigation }) => { const theme = useTheme(); const insets = useSafeAreaInsets(); + const { isOnline } = useOnlineStatus(); const outsideNav = route.params?.outsideNav; @@ -297,6 +299,12 @@ const Week: Screen<"Week"> = ({ route, navigation }) => { /> )} + {!isOnline && ( + + + + )} + {account?.providers?.includes("ical") && Object.values(timetables).flat().length === 0 && ( Date: Sun, 9 Feb 2025 23:19:22 +0100 Subject: [PATCH 0602/1144] =?UTF-8?q?feat:=20remplacement=20des=20param?= =?UTF-8?q?=C3=A8tres=20de=20son=20et=20d'apparence=20par=20accessibilit?= =?UTF-8?q?=C3=A9=20dans=20les=20param=C3=A8tres?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...erCard.tsx => AccesilityContainerCard.tsx} | 10 +-- .../Settings/ApparenceContainerCard.tsx | 33 -------- src/router/helpers/types.ts | 3 +- src/router/screens/settings/index.ts | 10 +-- src/views/settings/Settings.tsx | 23 ++---- ...pparence.tsx => SettingsAccessibility.tsx} | 78 +++++++++++++++--- src/views/settings/SettingsSoundHaptics.tsx | 81 ------------------- 7 files changed, 85 insertions(+), 153 deletions(-) rename src/components/Settings/{SoundHapticsContainerCard.tsx => AccesilityContainerCard.tsx} (70%) delete mode 100644 src/components/Settings/ApparenceContainerCard.tsx rename src/views/settings/{SettingsApparence.tsx => SettingsAccessibility.tsx} (52%) delete mode 100644 src/views/settings/SettingsSoundHaptics.tsx diff --git a/src/components/Settings/SoundHapticsContainerCard.tsx b/src/components/Settings/AccesilityContainerCard.tsx similarity index 70% rename from src/components/Settings/SoundHapticsContainerCard.tsx rename to src/components/Settings/AccesilityContainerCard.tsx index a8ece8e46..84d4092a0 100644 --- a/src/components/Settings/SoundHapticsContainerCard.tsx +++ b/src/components/Settings/AccesilityContainerCard.tsx @@ -3,7 +3,7 @@ import React from "react"; import { View } from "react-native"; import { NativeItem, NativeList, NativeText } from "../Global/NativeComponents"; -const ApparenceContainerCard = () => { +const AccessibilityContainerCard = () => { return ( { flexDirection: "row", }} > - 🔊 + 🖌️ - Son et vibrations + Personnalise à ta manière - Par défaut, Papillon joue des sons et des vibrations mais cela peut être changé. + Adapte l'affichage et la navigation entre les pages ); }; -export default ApparenceContainerCard; +export default AccessibilityContainerCard; diff --git a/src/components/Settings/ApparenceContainerCard.tsx b/src/components/Settings/ApparenceContainerCard.tsx deleted file mode 100644 index 3dfa0d3c0..000000000 --- a/src/components/Settings/ApparenceContainerCard.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React from "react"; - -import { View } from "react-native"; -import { NativeItem, NativeList, NativeText } from "../Global/NativeComponents"; - -const ApparenceContainerCard = () => { - return ( - - - - 🌓 - - - - - Mode d'affichage - - - Par défaut, Papillon s'adapte à ton thème système. Mais tu peux choisir un thème clair ou sombre. - - - - ); -}; - -export default ApparenceContainerCard; \ No newline at end of file diff --git a/src/router/helpers/types.ts b/src/router/helpers/types.ts index a31dba9ee..65f4565c5 100644 --- a/src/router/helpers/types.ts +++ b/src/router/helpers/types.ts @@ -137,8 +137,7 @@ export type RouteParameters = { SettingsDevLogs: undefined; SettingsDonorsList: undefined; SettingsReactions: undefined; - SettingsApparence: undefined; - SettingsSoundHaptics: undefined; + SettingsAccessibility: undefined; Menu?: undefined; RestaurantQrCode: { diff --git a/src/router/screens/settings/index.ts b/src/router/screens/settings/index.ts index b02791d66..9dcddcd3c 100644 --- a/src/router/screens/settings/index.ts +++ b/src/router/screens/settings/index.ts @@ -26,11 +26,10 @@ import ExternalIzlyLogin from "@/views/settings/ExternalAccount/Izly"; import IzlyActivation from "@/views/settings/ExternalAccount/IzlyActivation"; import SettingsReactions from "@/views/settings/SettingsReactions"; import TurboselfAccountSelector from "@/views/settings/ExternalAccount/TurboselfAccountSelector"; -import SettingsSoundHaptics from "@/views/settings/SettingsSoundHaptics"; -import SettingsApparence from "@/views/settings/SettingsApparence"; import ExternalAliseLogin from "@/views/settings/ExternalAccount/Alise"; import SettingsMultiService from "@/views/settings/SettingsMultiService"; import SettingsMultiServiceSpace from "@/views/settings/SettingsMultiServiceSpace"; +import SettingsAccessibility from "@/views/settings/SettingsAccessibility"; const settingsScreens = [ createScreen("Settings", Settings, { @@ -142,11 +141,8 @@ const settingsScreens = [ createScreen("SettingsDonorsList", SettingsDonorsList, { headerTitle: "Donateurs", }), - createScreen("SettingsApparence", SettingsApparence, { - headerTitle: "Mode d'affichage", - }), - createScreen("SettingsSoundHaptics", SettingsSoundHaptics, { - headerTitle: "Son et vibrations", + createScreen("SettingsAccessibility", SettingsAccessibility, { + headerTitle: "Accessibilité", }), ] as const; diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index 8567bab23..dabac530a 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -29,14 +29,13 @@ import { Scroll, Settings as SettingsLucide, Sparkles, - SunMoon, Smile, SwatchBook, - Volume2, WandSparkles, X, Blocks, - HelpCircle + HelpCircle, + PersonStanding } from "lucide-react-native"; import { NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; @@ -158,24 +157,18 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { }, 10); } }, - { - icon: , - color: "#800077", - label: "Son et vibrations", - onPress: () => navigation.navigate("SettingsSoundHaptics"), - }, - { - icon: , - color: "#1e316a", - label: "Mode d'affichage", - onPress: () => navigation.navigate("SettingsApparence"), - }, ], }, { icon: , label: "Avancé", tabs: [ + { + icon: , + color: "#27CC58", + label: "Accessibilité", + onPress: () => navigation.navigate("SettingsAccessibility"), + }, { icon: click ? ( = () => { +const SettingsAccessibility: Screen<"SettingsAccessibility"> = () => { const theme = useTheme(); const insets = useSafeAreaInsets(); - const { whatTheme, setWhatTheme } = useThemeSoundHaptics(); + const { + enableSon, + setEnableSon, + enableHaptics, + setEnableHaptics, + whatTheme, + setWhatTheme, + } = useThemeSoundHaptics(); return ( = () => { paddingBottom: insets.bottom + 16, }} > - + - + = () => { Mode sombre + + + + setEnableSon(value)} + /> + } + leading={ + } + colors={["#04ACDC", "#6FE3CD"]} + /> + } + > + Jouer du son + + Un son est joué lors de l'ouverture de différentes pages + + + + + + + setEnableHaptics(value)} + /> + } + leading={ + } + colors={["#FFD700", "#FF8C00"]} + /> + } + > + Jouer des vibrations + + Des vibrations ont lieu lors de la navigation, lorsqu'on coche des + devoirs... + + + ); }; -export default SettingsApparence; +export default SettingsAccessibility; diff --git a/src/views/settings/SettingsSoundHaptics.tsx b/src/views/settings/SettingsSoundHaptics.tsx deleted file mode 100644 index eb771a928..000000000 --- a/src/views/settings/SettingsSoundHaptics.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import React from "react"; -import { ScrollView, Switch } from "react-native"; -import type { Screen } from "@/router/helpers/types"; -import { Vibrate, Volume2 } from "lucide-react-native"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; -import { - NativeList, - NativeItem, - NativeListHeader, - NativeIconGradient, -} from "@/components/Global/NativeComponents"; -import { NativeText } from "@/components/Global/NativeComponents"; -import SoundHapticsContainerCard from "@/components/Settings/SoundHapticsContainerCard"; -import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; - -const SettingsApparence: Screen<"SettingsSoundHaptics"> = () => { - const insets = useSafeAreaInsets(); - const { enableSon, setEnableSon, enableHaptics, setEnableHaptics } = - useThemeSoundHaptics(); - - return ( - - - - - - setEnableSon(value)} - /> - } - leading={ - } - colors={["#04ACDC", "#6FE3CD"]} - /> - } - > - Jouer du son - - Un son est joué lors de l'ouverture de différentes pages - - - - - - - setEnableHaptics(value)} - /> - } - leading={ - } - colors={["#FFD700", "#FF8C00"]} - /> - } - > - Jouer des vibrations - - Des vibrations ont lieu lors de la navigation, lorsqu'on coche des - devoirs... - - - - - ); -}; - -export default SettingsApparence; From 6e66ceec3c0a71aacbe7f9919d9dc595a536d97b Mon Sep 17 00:00:00 2001 From: TinAD17tin <163759571+TinAD17tin@users.noreply.github.com> Date: Mon, 10 Feb 2025 13:58:14 +0100 Subject: [PATCH 0603/1144] Changement texte dans SelectMethod.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ajout de « disponible prochainement » --- src/views/settings/ExternalAccount/SelectMethod.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/settings/ExternalAccount/SelectMethod.tsx b/src/views/settings/ExternalAccount/SelectMethod.tsx index 85a7be13b..537dd0db3 100644 --- a/src/views/settings/ExternalAccount/SelectMethod.tsx +++ b/src/views/settings/ExternalAccount/SelectMethod.tsx @@ -48,10 +48,10 @@ const ExternalAccountSelectMethod: Screen<"ExternalAccountSelectMethod"> = ({ na disabled > - Connexion via PRONOTE + Connexion automatique via PRONOTE - Connexion automatique via PRONOTE + Disponible prochainement )} From c8b75543c95536f2bb919bba81233cab91b880d8 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 10 Feb 2025 20:02:00 +0100 Subject: [PATCH 0604/1144] =?UTF-8?q?fix:=20Suppression=20des=20tags=20de?= =?UTF-8?q?=20transition=20partag=C3=A9s=20pour=20les=20cartes=20dans=20le?= =?UTF-8?q?=20menu=20et=20les=20d=C3=A9tails=20de=20la=20carte?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Restaurant/Menu.tsx | 1 - src/views/account/Restaurant/Modals/CardDetail.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 1fef2f0bb..4710968eb 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -387,7 +387,6 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { {allCards?.map((card, index) => ( { activeScale={0.95} > Date: Mon, 10 Feb 2025 21:33:05 +0100 Subject: [PATCH 0605/1144] fix(Menu): correction de la condition d'affichage des cartes dans le menu --- src/views/account/Restaurant/Menu.tsx | 64 ++++++++++++++------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 4710968eb..62f95f11e 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -368,39 +368,41 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { - 1} - decelerationRate={"fast"} - snapToInterval={(Dimensions.get("window").width - 32) + 6} - > - {allCards?.map((card, index) => ( - - O && ( + 1} + decelerationRate={"fast"} + snapToInterval={(Dimensions.get("window").width - 32) + 6} + > + {allCards?.map((card, index) => ( + { - navigation.navigate("RestaurantCardDetail", { card }); + style={{ + width: Dimensions.get("window").width - 32, }} - /> - - ))} - + > + { + navigation.navigate("RestaurantCardDetail", { card }); + }} + /> + + ))} + + )} {(currentMenu || (allBookings && allBookings?.some((terminal) => terminal.days.some((day) => day.date?.toDateString() === pickerDate.toDateString())))) && From 466db221ba11db957e7606692caad30f9aefcf9a Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 10 Feb 2025 21:37:55 +0100 Subject: [PATCH 0606/1144] =?UTF-8?q?fix(types):=20correction=20de=20la=20?= =?UTF-8?q?d=C3=A9finition=20de=20RestaurantPaymentSuccess=20et=20mise=20?= =?UTF-8?q?=C3=A0=20jour=20des=20types=20dans=20RestaurantCardDetail?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/helpers/types.ts | 2 +- src/router/screens/views/index.ts | 2 +- .../account/Restaurant/Modals/CardDetail.tsx | 11 +- .../account/Restaurant/Modals/History.tsx | 115 ------- .../account/Restaurant/Modals/QrCode.old.tsx | 289 ------------------ 5 files changed, 7 insertions(+), 412 deletions(-) delete mode 100644 src/views/account/Restaurant/Modals/History.tsx delete mode 100644 src/views/account/Restaurant/Modals/QrCode.old.tsx diff --git a/src/router/helpers/types.ts b/src/router/helpers/types.ts index 753c08da1..ace4d92a5 100644 --- a/src/router/helpers/types.ts +++ b/src/router/helpers/types.ts @@ -150,7 +150,7 @@ export type RouteParameters = { RestaurantCardDetail: { card: ServiceCard; }; - RestaurantPaymentSuccess;: { + RestaurantPaymentSuccess: { card: ServiceCard; }; diff --git a/src/router/screens/views/index.ts b/src/router/screens/views/index.ts index 89d31d8c8..cfce072a4 100644 --- a/src/router/screens/views/index.ts +++ b/src/router/screens/views/index.ts @@ -43,11 +43,11 @@ export default [ createScreen("RestaurantCardDetail", RestaurantCardDetail, { headerTitle: "Détail de la carte", presentation: "formSheet", - stackPresentation: "formSheet", headerShown: false, sheetCornerRadius: 16, sheetGrabberVisible: true, sheetExpandsWhenScrolledToEdge: true, + // @ts-expect-error sheetInitialDetent: 0, sheetAllowedDetents: "all", }), diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index 7afc86e89..e2394d3ce 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -12,7 +12,7 @@ import { useTheme } from "@react-navigation/native"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; import { PressableScale } from "react-native-pressable-scale"; import { useCurrentAccount } from "@/stores/account"; -import { AccountService } from "@/stores/account/types"; +import { AccountService, ExternalAccount } from "@/stores/account/types"; import { QrCode } from "lucide-react-native"; import { balanceFromExternal } from "@/services/balance"; import { reservationHistoryFromExternal } from "@/services/reservation-history"; @@ -34,11 +34,11 @@ const RestaurantCardDetail = ({ route, navigation }) => { const updateCardData = async () => { const [balance, history] = await Promise.all([ - balanceFromExternal(route.params.card.account).catch(err => { + balanceFromExternal(route.params.card.account as ExternalAccount).catch(err => { console.warn(`Error fetching balance for account ${account}:`, err); return []; }), - reservationHistoryFromExternal(route.params.card.account).catch(err => { + reservationHistoryFromExternal(route.params.card.account as ExternalAccount).catch(err => { console.warn(`Error fetching history for account ${account}:`, err); return []; }) @@ -120,7 +120,7 @@ const RestaurantCardDetail = ({ route, navigation }) => { color: theme.colors.text, }} > - Carte {AccountService[route.params.card.service]} {account?.identity?.firstName ? "de " + account.identity.firstName : ""} + Carte {AccountService[route.params.card.service as AccountService]} {account?.identity?.firstName ? "de " + account.identity.firstName : ""} { style={{ fontFamily: "semibold", fontSize: 28, - color: theme.colors.text, textAlign: "center", color: card.balance[0].amount > 0 ? "#00C853" : "#FF1744", }} @@ -220,7 +219,7 @@ const RestaurantCardDetail = ({ route, navigation }) => { key={"cardhistory-"+i} leading={ ; - -const RestaurantHistory = ({ route }: { route: NavigationProps }) => { - const histories = route.params?.histories ?? []; - - const formatDate = (timestamp: number) => { - const date = new Date(timestamp); - return date.toLocaleDateString("fr-FR", { - weekday: "long", - month: "long", - day: "numeric" - }).toUpperCase(); - }; - - const groupedHistories = useMemo(() => { - const historyMap = new Map(); - - histories.sort((a, b) => b.timestamp - a.timestamp); - - histories.forEach((history: ReservationHistory) => { - const formattedDate = formatDate(history.timestamp); - if (!historyMap.has(formattedDate)) { - historyMap.set(formattedDate, []); - } - historyMap.get(formattedDate)?.push(history); - }); - - historyMap.forEach((value) => { - value.sort((a, b) => b.timestamp - a.timestamp); - }); - - return Array.from(historyMap); - }, [histories]); - - - return ( - - {histories === null ? ( - Chargement... - ) : histories.length === 0 ? ( - - ) : ( - groupedHistories.map(([date, reservations], i) => ( - - - - {reservations.map((history: ReservationHistory, j: number) => { - const time = new Date(history.timestamp).toLocaleTimeString("fr-FR", { - hour: "2-digit", - minute: "2-digit", - }); - return ( - - - - {history.label} - {time !== "00:00" ? ( - {time} - ) : null} - - {history.amount !== 0 && ( - - {history.amount > 0 ? "+" : ""}{history.amount.toFixed(2)}€ - - )} - - - ); - })} - - - )) - )} - - - ); -}; - -const styles = StyleSheet.create({ - scrollViewContent: { - padding: 16, - paddingTop: 0, - }, - row: { - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center", - }, -}); - -export default RestaurantHistory; diff --git a/src/views/account/Restaurant/Modals/QrCode.old.tsx b/src/views/account/Restaurant/Modals/QrCode.old.tsx deleted file mode 100644 index cb01db582..000000000 --- a/src/views/account/Restaurant/Modals/QrCode.old.tsx +++ /dev/null @@ -1,289 +0,0 @@ -import React, { useEffect, useLayoutEffect, useState } from "react"; -import { - View, - Text, - StatusBar, - TouchableOpacity, - StyleSheet, - ScrollView, Platform, - AppState, - Image -} from "react-native"; -import { DeviceMotion } from "expo-sensors"; -import { SafeAreaView } from "react-native-safe-area-context"; -import QRCode from "react-native-qrcode-svg"; -import * as Brightness from "expo-brightness"; -import Animated, { - useSharedValue, - useAnimatedStyle, - withTiming, - Easing, -} from "react-native-reanimated"; -import { useTheme } from "@react-navigation/native"; -import { X } from "lucide-react-native"; -import ScanIcon from "@/components/Restaurant/ScanIcon"; -import type { Screen } from "@/router/helpers/types"; - -const BETA_THRESHOLD_LOW = -0.2; -const BETA_THRESHOLD_HIGH = -0.15; -const ANIMATION_DURATION = 500; - -const RestaurantQrCode: Screen<"RestaurantQrCode"> = ({ route, navigation }) => { - const [currentState, setCurrentState] = useState< - "neutral" | "tiltedUp" | "tiltedDown" - >("neutral"); - const opacity = useSharedValue(1); - const rotate = useSharedValue(0); - const theme = useTheme(); - const { colors } = theme; - - const qrcodes = route.params.QrCodes; - const [activeIndex, setActiveIndex] = useState(0); - const handleScroll = (event: { nativeEvent: { contentOffset: { x: any; }; }; }) => { - const contentOffsetX = event.nativeEvent.contentOffset.x; - const index = Math.floor(contentOffsetX / (100)); - setActiveIndex(Math.max(0, Math.min(index, qrcodes ? qrcodes.length - 1 : 0))); - }; - - useLayoutEffect(() => { - navigation.setOptions({ - headerRight: () => ( - - - - ), - }); - }, [navigation]); - - const [oldBrightness, setOldBrightness] = useState(0.5); - - useEffect(() => { - let isActive = true; - - const handleAppStateChange = async (nextAppState: string) => { - if (nextAppState === "background" || nextAppState === "inactive") { - if (isActive) { - isActive = false; - await Brightness.setBrightnessAsync(oldBrightness); - } - } else if (nextAppState === "active") { - isActive = true; - await Brightness.setBrightnessAsync(1); - } - }; - - const appStateSubscription = AppState.addEventListener( - "change", - handleAppStateChange - ); - - const navigationSubscription = navigation.addListener("beforeRemove", () => { - Brightness.setBrightnessAsync(oldBrightness); - }); - - (async () => { - if (Platform.OS === "android") { - const { status } = await Brightness.requestPermissionsAsync(); - if (status !== "granted") { - navigation.goBack(); - return; - } - } - try { - const brightness = await Brightness.getBrightnessAsync(); - setOldBrightness(brightness); - await Brightness.setBrightnessAsync(1); - } catch (e) { console.warn("Brightness error:", e); } - })(); - return () => { - appStateSubscription.remove(); - navigationSubscription(); - Brightness.setBrightnessAsync(oldBrightness); - }; - }, [navigation, oldBrightness]); - - - useEffect(() => { - const subscription = DeviceMotion.addListener(({ rotation }) => { - let newState: "neutral" | "tiltedUp" | "tiltedDown" = "neutral"; - - if (!rotation || typeof rotation.beta === "undefined") { - return; - } - - if (rotation.beta < BETA_THRESHOLD_LOW) { - newState = "tiltedDown"; - } else if (rotation.beta > BETA_THRESHOLD_HIGH) { - newState = "tiltedUp"; - } - - if (newState !== currentState) { - setCurrentState(newState); - const finalRotation = newState === "tiltedDown" ? 180 : 0; - - opacity.value = withTiming(0, { - duration: ANIMATION_DURATION / 2, - easing: Easing.out(Easing.ease), - }, () => { - rotate.value = withTiming(finalRotation, { - duration: ANIMATION_DURATION / 2, - easing: Easing.inOut(Easing.ease), - }, () => { - opacity.value = withTiming(1, { - duration: ANIMATION_DURATION / 2, - easing: Easing.in(Easing.ease), - }); - }); - }); - } - }); - - return () => { - subscription.remove(); - }; - }, [currentState, opacity, rotate]); - - const animatedStyle = useAnimatedStyle(() => ({ - opacity: opacity.value, - transform: [{ rotate: `${rotate.value}deg` }], - })); - - return ( - - - - - 1} - onScroll={handleScroll} - > - { qrcodes && qrcodes.map((code, index) => { - if (typeof code === "string") { - return ( - - - - ); - } else if (code instanceof Blob) { - const imageUrl = URL.createObjectURL(code); - - return ( - - - - ); - } - })} - - - { qrcodes && qrcodes.length > 1 && ( - - {qrcodes.map((_, index) => ( - - ))} - - )} - - - - Oriente le code QR vers le scanner de la borne - - - - - ); -}; - -const styles = StyleSheet.create({ - safeArea: { - flex: 1, - }, - headerButton: { - padding: 8, - borderRadius: 50, - margin: 5, - }, - qrCodeContainer: { - alignItems: "center", - justifyContent: "center", - alignContent: "center", - marginTop: 75 - }, - qrCodeInnerContainer: { - flex: 1, - justifyContent: "center", - alignItems: "center", - width: 300, - padding: 15, - borderRadius: 15, - alignSelf: "center", - backgroundColor: "#FFFFFF" - }, - instructionContainer: { - marginTop: 60, - justifyContent: "center", - alignItems: "center", - gap: 10, - }, - instructionText: { - color: "#FFFFFF", - fontSize: 15, - lineHeight: 20, - textAlign: "center", - maxWidth: 200, - fontFamily: "medium", - }, - dotsContainer: { - flexDirection: "row", - justifyContent: "center", - alignItems: "center", - marginTop: 16, - }, - dot: { - width: 7, - height: 7, - borderRadius: 5, - marginHorizontal: 4, - }, - activeDot: { - backgroundColor: "#ffffff", - }, - inactiveDot: { - backgroundColor: "#ffffff25", - }, - barcodeImage: { - width: "100%", - height: 50, - resizeMode: "cover", - }, -}); - -export default RestaurantQrCode; From 77ee578608eefa8685f4ad1003b059756bcfa271 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 10 Feb 2025 21:38:41 +0100 Subject: [PATCH 0607/1144] =?UTF-8?q?fix(Menu):=20conversion=20de=20l'ID?= =?UTF-8?q?=20de=20service=20en=20cha=C3=AEne=20et=20gestion=20des=20carte?= =?UTF-8?q?s=20nulles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Restaurant/Menu.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 62f95f11e..869181773 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -223,7 +223,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { balance: balance, history: history, cardnumber: cardnumber, - theme: STORE_THEMES.find((theme) => theme.id === account.service) ?? STORE_THEMES[0], + theme: STORE_THEMES.find((theme) => theme.id === account.service.toString()) ?? STORE_THEMES[0], }; newCards.push(newCard); @@ -601,7 +601,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { )} : <> - {allCards?.length > 0 && ( + {(allCards ?? []).length > 0 && ( Date: Mon, 10 Feb 2025 21:39:57 +0100 Subject: [PATCH 0608/1144] fix(CardDetail): ajout de la typage pour l'identifiant de carte et gestion des valeurs nulles --- src/views/account/Restaurant/Modals/CardDetail.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index e2394d3ce..79475523c 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -17,10 +17,10 @@ import { QrCode } from "lucide-react-native"; import { balanceFromExternal } from "@/services/balance"; import { reservationHistoryFromExternal } from "@/services/reservation-history"; -const formatCardIdentifier = (identifier) => { +const formatCardIdentifier = (identifier: string) => { const visiblePart = identifier.slice(-6); const maskedPart = identifier.slice(0, -6).replace(/./g, "•"); - return maskedPart + " " + visiblePart.match(/.{1,4}/g).join(" "); + return maskedPart + " " + (visiblePart.match(/.{1,4}/g) ?? []).join(" "); }; const RestaurantCardDetail = ({ route, navigation }) => { From f887540b7e5c86de0aa2193d29c7258cdbc4a210 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 10 Feb 2025 21:47:07 +0100 Subject: [PATCH 0609/1144] fix(Menu): ajout de typage pour les composants de menu et correction de la gestion des cartes --- src/router/navigator/atoms/MenuItem.tsx | 3 +-- src/views/account/Restaurant/Cards/Card.tsx | 3 ++- src/views/account/Restaurant/Menu.tsx | 2 +- src/views/account/Restaurant/Modals/CardDetail.tsx | 5 +++-- src/views/account/Restaurant/Modals/PaymentSuccess.tsx | 3 ++- src/views/account/Restaurant/Modals/QrCode.tsx | 3 ++- 6 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/router/navigator/atoms/MenuItem.tsx b/src/router/navigator/atoms/MenuItem.tsx index f53a74ad5..28959c711 100644 --- a/src/router/navigator/atoms/MenuItem.tsx +++ b/src/router/navigator/atoms/MenuItem.tsx @@ -1,9 +1,8 @@ import * as React from "react"; import { useTheme } from "@react-navigation/native"; -import { StyleSheet, Platform } from "react-native"; +import { StyleSheet, Platform, Pressable } from "react-native"; import LottieView from "lottie-react-native"; import colorsList from "@/utils/data/colors.json"; -import { Pressable } from "react-native-gesture-handler"; import * as Haptics from "expo-haptics"; import Reanimated, { FadeIn, FadeOut, LinearTransition } from "react-native-reanimated"; import { anim2Papillon } from "@/utils/ui/animations"; diff --git a/src/views/account/Restaurant/Cards/Card.tsx b/src/views/account/Restaurant/Cards/Card.tsx index 0a482208b..51e20782d 100644 --- a/src/views/account/Restaurant/Cards/Card.tsx +++ b/src/views/account/Restaurant/Cards/Card.tsx @@ -3,6 +3,7 @@ import { ServiceCard } from "../Menu"; import { useTheme } from "@react-navigation/native"; import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; import { PressableScale } from "react-native-pressable-scale"; +import { AccountService } from "@/stores/account/types"; const MenuCard = ({ card, onPress }: { card: ServiceCard, onPress?: () => void }) => { const theme = useTheme(); @@ -30,7 +31,7 @@ const MenuCard = ({ card, onPress }: { card: ServiceCard, onPress?: () => void } ]} > = ({ route, navigation }) => { - {(allCards ?? [])?.length > O && ( + {(allCards ?? [])?.length > 0 && ( { const visiblePart = identifier.slice(-6); @@ -23,7 +24,7 @@ const formatCardIdentifier = (identifier: string) => { return maskedPart + " " + (visiblePart.match(/.{1,4}/g) ?? []).join(" "); }; -const RestaurantCardDetail = ({ route, navigation }) => { +const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigation }) => { try { const { card } = route.params; const [cardData, setCardData] = useState(null); @@ -214,7 +215,7 @@ const RestaurantCardDetail = ({ route, navigation }) => { {card?.history.length > 0 && ( - {card.history.sort((a, b) => b.timestamp - a.timestamp).map((history, i) => ( + {card.history.sort((a: any, b: any) => b.timestamp - a.timestamp).map((history, i) => ( { +const RestaurantPaymentSuccess: Screen<"RestaurantPaymentSuccess"> = ({ route, navigation }) => { const { card, diff } = route.params; const theme = useTheme(); diff --git a/src/views/account/Restaurant/Modals/QrCode.tsx b/src/views/account/Restaurant/Modals/QrCode.tsx index 3170831e0..b208ece9c 100644 --- a/src/views/account/Restaurant/Modals/QrCode.tsx +++ b/src/views/account/Restaurant/Modals/QrCode.tsx @@ -9,9 +9,10 @@ import { TouchableOpacity } from "react-native-gesture-handler"; import { PressableScale } from "react-native-pressable-scale"; import QRCode from "react-native-qrcode-svg"; import * as Haptics from "expo-haptics"; +import { Screen } from "@/router/helpers/types"; -const RestaurantQrCode = ({ route, navigation }) => { +const RestaurantQrCode: Screen<"RestaurantQrCode"> = ({ route, navigation }) => { const { card } = route.params; const [qrCode, setQrCode] = useState(null); From 22f72f0df569e53b44945a9f2cbd2fa07adeb3f4 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 10 Feb 2025 21:50:30 +0100 Subject: [PATCH 0610/1144] =?UTF-8?q?fix(types):=20ajout=20de=20la=20propr?= =?UTF-8?q?i=C3=A9t=C3=A9=20'diff'=20dans=20RestaurantPaymentSuccess=20et?= =?UTF-8?q?=20typage=20des=20=C3=A9tats=20dans=20PaymentSuccess=20et=20QrC?= =?UTF-8?q?ode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/helpers/types.ts | 3 ++- src/views/account/Restaurant/Modals/PaymentSuccess.tsx | 2 +- src/views/account/Restaurant/Modals/QrCode.tsx | 2 +- src/widgets/Components/RestaurantQRCode.tsx | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/router/helpers/types.ts b/src/router/helpers/types.ts index ace4d92a5..b9cf924b1 100644 --- a/src/router/helpers/types.ts +++ b/src/router/helpers/types.ts @@ -142,7 +142,7 @@ export type RouteParameters = { Menu?: undefined; RestaurantQrCode: { - QrCodes: Array; + card: ServiceCard; }; RestaurantHistory: { histories: ReservationHistory[]; @@ -152,6 +152,7 @@ export type RouteParameters = { }; RestaurantPaymentSuccess: { card: ServiceCard; + diff: number; }; Discussions: undefined; diff --git a/src/views/account/Restaurant/Modals/PaymentSuccess.tsx b/src/views/account/Restaurant/Modals/PaymentSuccess.tsx index 1332c594e..1ec2b529f 100644 --- a/src/views/account/Restaurant/Modals/PaymentSuccess.tsx +++ b/src/views/account/Restaurant/Modals/PaymentSuccess.tsx @@ -13,7 +13,7 @@ const RestaurantPaymentSuccess: Screen<"RestaurantPaymentSuccess"> = ({ route, n const { card, diff } = route.params; const theme = useTheme(); - const [lastPayment, setLastPayment] = useState(null); + const [lastPayment, setLastPayment] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { diff --git a/src/views/account/Restaurant/Modals/QrCode.tsx b/src/views/account/Restaurant/Modals/QrCode.tsx index b208ece9c..7d44d6425 100644 --- a/src/views/account/Restaurant/Modals/QrCode.tsx +++ b/src/views/account/Restaurant/Modals/QrCode.tsx @@ -14,7 +14,7 @@ import { Screen } from "@/router/helpers/types"; const RestaurantQrCode: Screen<"RestaurantQrCode"> = ({ route, navigation }) => { const { card } = route.params; - const [qrCode, setQrCode] = useState(null); + const [qrCode, setQrCode] = useState(null); const PollingBalance = async () => { balanceFromExternal(card.account).then((newBalance) => { diff --git a/src/widgets/Components/RestaurantQRCode.tsx b/src/widgets/Components/RestaurantQRCode.tsx index ecfea4e43..f9d31253e 100644 --- a/src/widgets/Components/RestaurantQRCode.tsx +++ b/src/widgets/Components/RestaurantQRCode.tsx @@ -32,7 +32,7 @@ const RestaurantQRCodeWidget = forwardRef(({ useImperativeHandle(ref, () => ({ handlePress: () => { - navigation.navigate("RestaurantQrCode", { QrCodes: qrcode ?? [] }); + // navigation.navigate("RestaurantQrCode", { QrCodes: qrcode ?? [] }); } })); From ad6b319bdc3703ad76f1a61a3b709995f4280ca9 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 10 Feb 2025 21:54:06 +0100 Subject: [PATCH 0611/1144] fix(types): ajout de typage pour ExternalAccount dans les composants de paiement et QR code --- .../account/Restaurant/Modals/CardDetail.tsx | 1 + .../Restaurant/Modals/PaymentSuccess.tsx | 152 +++++++++--------- .../account/Restaurant/Modals/QrCode.tsx | 6 +- 3 files changed, 83 insertions(+), 76 deletions(-) diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index ebce807f6..d2517f930 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -47,6 +47,7 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio setCardData({ ...card, + // @ts-expect-error balance: balance, history: history, }); diff --git a/src/views/account/Restaurant/Modals/PaymentSuccess.tsx b/src/views/account/Restaurant/Modals/PaymentSuccess.tsx index 1ec2b529f..536cfdb23 100644 --- a/src/views/account/Restaurant/Modals/PaymentSuccess.tsx +++ b/src/views/account/Restaurant/Modals/PaymentSuccess.tsx @@ -2,6 +2,7 @@ import { NativeItem, NativeList, NativeText } from "@/components/Global/NativeCo import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { Screen } from "@/router/helpers/types"; import { reservationHistoryFromExternal } from "@/services/reservation-history"; +import { ExternalAccount } from "@/stores/account/types"; import { anim2Papillon } from "@/utils/ui/animations"; import { useTheme } from "@react-navigation/native"; import { AlertCircle, Check } from "lucide-react-native"; @@ -17,7 +18,8 @@ const RestaurantPaymentSuccess: Screen<"RestaurantPaymentSuccess"> = ({ route, n const [loading, setLoading] = useState(true); useEffect(() => { - reservationHistoryFromExternal(card.account).then((history) => { + reservationHistoryFromExternal(card.account as ExternalAccount).then((history) => { + // @ts-expect-error setLastPayment(history[0]); setLoading(false); }); @@ -88,85 +90,87 @@ const RestaurantPaymentSuccess: Screen<"RestaurantPaymentSuccess"> = ({ route, n textAlign: "center", }} > - {new Date(lastPayment.timestamp).toLocaleString("fr-FR", { weekday: "long", day: "numeric", month: "long", year: "numeric", hour: "numeric", minute: "numeric" })} + {// @ts-expect-error + new Date(lastPayment.timestamp).toLocaleString("fr-FR", { weekday: "long", day: "numeric", month: "long", year: "numeric", hour: "numeric", minute: "numeric" })} - {diff == lastPayment.amount ? ( - - - - - } + { // @ts-expect-error + diff == lastPayment.amount ? ( + - - Transaction valide - - - Comparaison de solde effectuée et validée par Papillon - - - - ) : ( - - - - - } + + + + } + > + + Transaction valide + + + Comparaison de solde effectuée et validée par Papillon + + + + ) : ( + - - Impossible de vérifier la transaction - - - Le paiement et le solde de votre compte ne correspondent pas - - - - )} + + + + } + > + + Impossible de vérifier la transaction + + + Le paiement et le solde de votre compte ne correspondent pas + + + + )} )} diff --git a/src/views/account/Restaurant/Modals/QrCode.tsx b/src/views/account/Restaurant/Modals/QrCode.tsx index 7d44d6425..0c9e505d7 100644 --- a/src/views/account/Restaurant/Modals/QrCode.tsx +++ b/src/views/account/Restaurant/Modals/QrCode.tsx @@ -10,6 +10,7 @@ import { PressableScale } from "react-native-pressable-scale"; import QRCode from "react-native-qrcode-svg"; import * as Haptics from "expo-haptics"; import { Screen } from "@/router/helpers/types"; +import { ExternalAccount } from "@/stores/account/types"; const RestaurantQrCode: Screen<"RestaurantQrCode"> = ({ route, navigation }) => { @@ -17,7 +18,7 @@ const RestaurantQrCode: Screen<"RestaurantQrCode"> = ({ route, navigation }) => const [qrCode, setQrCode] = useState(null); const PollingBalance = async () => { - balanceFromExternal(card.account).then((newBalance) => { + balanceFromExternal(card.account as ExternalAccount).then((newBalance) => { if(card.balance[0].amount !== newBalance[0].amount) { const diff = newBalance[0].amount - card.balance[0].amount; openFeedback(); @@ -51,7 +52,8 @@ const RestaurantQrCode: Screen<"RestaurantQrCode"> = ({ route, navigation }) => const theme = useTheme(); const GenerateQRCode = async () => { - qrcodeFromExternal(card.account).then((qrCode) => { + qrcodeFromExternal(card.account as ExternalAccount).then((qrCode) => { + // @ts-expect-error setQrCode(qrCode); }); }; From e12676729cd0f460088953164f7fbeced98ff022 Mon Sep 17 00:00:00 2001 From: Tryon Date: Mon, 10 Feb 2025 22:37:51 +0100 Subject: [PATCH 0612/1144] =?UTF-8?q?fix(StoreThemes):=20ajout=20d'un=20no?= =?UTF-8?q?uveau=20th=C3=A8me=20'Ard'=20et=20fix=20des=20types?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Restaurant/Cards/StoreThemes.ts | 10 ++++++++++ src/views/account/Restaurant/Menu.tsx | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/views/account/Restaurant/Cards/StoreThemes.ts b/src/views/account/Restaurant/Cards/StoreThemes.ts index 16e178cb6..d6a0d87cd 100644 --- a/src/views/account/Restaurant/Cards/StoreThemes.ts +++ b/src/views/account/Restaurant/Cards/StoreThemes.ts @@ -30,4 +30,14 @@ export const STORE_THEMES = [ }, background: require("../../../../../assets/images/cards/Carte_Cover_Turboself.png"), }, + { + id: "ARD", + name: "Ard", + colors: { + text: "#FFFFFF", + background: "#295888", + accent: "#3DB7A5", + }, + background: require("../../../../../assets/images/cards/Carte_Cover_ARD.png"), + }, ]; \ No newline at end of file diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 22457c7ee..99abe3698 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -223,7 +223,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { balance: balance, history: history, cardnumber: cardnumber, - theme: STORE_THEMES.find((theme) => theme.id === account.service.toString()) ?? STORE_THEMES[0], + theme: STORE_THEMES.find((theme) => theme.id === AccountService[account.service]) ?? STORE_THEMES[0], }; newCards.push(newCard); From 6ba24f238c37e17685f7ab8db247254dbe734d06 Mon Sep 17 00:00:00 2001 From: Tryon Date: Mon, 10 Feb 2025 22:38:51 +0100 Subject: [PATCH 0613/1144] =?UTF-8?q?fix(StoreThemes):=20ajout=20d'un=20no?= =?UTF-8?q?uveau=20th=C3=A8me=20'Alise'=20avec=20ses=20couleurs=20et=20ima?= =?UTF-8?q?ge=20de=20fond?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Restaurant/Cards/StoreThemes.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/views/account/Restaurant/Cards/StoreThemes.ts b/src/views/account/Restaurant/Cards/StoreThemes.ts index d6a0d87cd..b9d45f6bf 100644 --- a/src/views/account/Restaurant/Cards/StoreThemes.ts +++ b/src/views/account/Restaurant/Cards/StoreThemes.ts @@ -40,4 +40,15 @@ export const STORE_THEMES = [ }, background: require("../../../../../assets/images/cards/Carte_Cover_ARD.png"), }, + { + id: "Alise", + name: "Alise", + colors: { + text: "#FFFFFF", + background: "#339DD7", + accent: "#AFDEF8", + }, + background: require("../../../../../assets/images/cards/Carte_Cover_Alise.png"), + }, + ]; \ No newline at end of file From fdf9f7dffefe9aac18ed695749cfed8fa189fbf3 Mon Sep 17 00:00:00 2001 From: Tryon Date: Mon, 10 Feb 2025 22:44:41 +0100 Subject: [PATCH 0614/1144] =?UTF-8?q?fix(StoreThemes):=20ajout=20d'un=20no?= =?UTF-8?q?uveau=20th=C3=A8me=20'Service=20de=20cantine'=20avec=20ses=20co?= =?UTF-8?q?uleurs=20et=20image=20de=20fond?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/images/cards/Carte_Cover_Unknown.png | Bin 0 -> 59256 bytes .../account/Restaurant/Cards/StoreThemes.ts | 10 ++++++++++ 2 files changed, 10 insertions(+) create mode 100644 assets/images/cards/Carte_Cover_Unknown.png diff --git a/assets/images/cards/Carte_Cover_Unknown.png b/assets/images/cards/Carte_Cover_Unknown.png new file mode 100644 index 0000000000000000000000000000000000000000..d13696ae4a424764364d5ce4aaf3def22529840f GIT binary patch literal 59256 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 Date: Thu, 13 Feb 2025 15:29:10 +0100 Subject: [PATCH 0615/1144] =?UTF-8?q?fix(notifications):=20mettre=20=C3=A0?= =?UTF-8?q?=20jour=20le=20titre=20des=20notifications=20pour=20refl=C3=A9t?= =?UTF-8?q?er=20"Changement=20de=20cours"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Lessons.ts | 6 +++--- src/components/Settings/NotificationContainerCard.tsx | 10 +++++----- src/views/settings/SettingsNotifications.tsx | 5 ++++- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index 0e661916e..be7ecd4a6 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -101,7 +101,7 @@ const fetchLessons = async (): Promise => { await papillonNotify( { id: `${account.name}-lessons`, - title: `[${account.name}] EdT modifié`, + title: `[${account.name}] Changement de cours`, subtitle: new Date( differencesTimestamp[0].startTimestamp ).toLocaleDateString("fr-FR", { @@ -155,7 +155,7 @@ const fetchLessons = async (): Promise => { await papillonNotify( { id: `${account.name}-lessons`, - title: `[${account.name}] EdT modifié`, + title: `[${account.name}] Changement de cours`, subtitle: new Date( differencesStatus[0].startTimestamp ).toLocaleDateString("fr-FR", { @@ -189,7 +189,7 @@ const fetchLessons = async (): Promise => { await papillonNotify( { id: `${account.name}-lessons`, - title: `[${account.name}] EdT modifié`, + title: `[${account.name}] Changement de cours`, subtitle: new Date( differencesStatus[0].startTimestamp ).toLocaleDateString("fr-FR", { diff --git a/src/components/Settings/NotificationContainerCard.tsx b/src/components/Settings/NotificationContainerCard.tsx index 00c95573d..582d67819 100644 --- a/src/components/Settings/NotificationContainerCard.tsx +++ b/src/components/Settings/NotificationContainerCard.tsx @@ -285,11 +285,11 @@ const styles = StyleSheet.create({ flexDirection: "row", justifyContent: "space-between", width: "95%", - marginBottom: 1, + marginBottom: -1, }, title: { color: "#222222", - fontSize: 15, + fontSize: 16, fontFamily: "semibold", }, time: { @@ -302,11 +302,11 @@ const styles = StyleSheet.create({ }, message: { color: "#3F3F3F", - fontSize: 15, + fontSize: 14.5, maxWidth: "85%", minWidth: "85%", - lineHeight: 15, - letterSpacing: -0.4, + lineHeight: 20, + letterSpacing: -0.1, fontFamily: "medium", }, overlay: { diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 0495854d0..ac69d95c7 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -22,6 +22,7 @@ import NotificationContainerCard from "@/components/Settings/NotificationContain import { createChannelNotification, requestNotificationPermission } from "@/background/Notifications"; import { useCurrentAccount } from "@/stores/account"; import { useAlert } from "@/providers/AlertProvider"; +import InsetsBottomView from "@/components/Global/InsetsBottomView"; const SettingsNotifications: Screen<"SettingsNotifications"> = ({ navigation @@ -87,7 +88,7 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ const notificationSchoolary = [ { icon: } color={colors.primary} />, - title: "EdT modifié", + title: "Changement de cours", message: "Musique (10:00-11:00) : Prof. absent", personalizationValue: "timetable", }, @@ -187,6 +188,8 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ )} + + ); }; From b8f773b3f050637b9fab1027354d3f90c75c99e2 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Thu, 13 Feb 2025 15:35:04 +0100 Subject: [PATCH 0616/1144] =?UTF-8?q?fix(settings):=20ajouter=20des=20anim?= =?UTF-8?q?ations=20d'entr=C3=A9e=20et=20de=20sortie=20pour=20les=20notifi?= =?UTF-8?q?cations=20scolaires?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Settings/NotificationContainerCard.tsx | 3 ++- src/views/settings/SettingsNotifications.tsx | 18 +++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/components/Settings/NotificationContainerCard.tsx b/src/components/Settings/NotificationContainerCard.tsx index 582d67819..84957a6ea 100644 --- a/src/components/Settings/NotificationContainerCard.tsx +++ b/src/components/Settings/NotificationContainerCard.tsx @@ -14,6 +14,7 @@ import Reanimated, { useSharedValue, useAnimatedStyle, withTiming, + Easing, } from "react-native-reanimated"; import { NativeItem, NativeList, NativeText } from "../Global/NativeComponents"; import { Settings, X } from "lucide-react-native"; @@ -56,7 +57,7 @@ const NotificationContainerCard = ({ const animationref = React.useRef(null); useEffect(() => { - const timingConfig = { duration: 250 }; + const timingConfig = { duration: 250, easing: Easing.bezier(0.3, 0.3, 0, 1) }; opacity.value = withTiming(isEnable ? 1 : 0, timingConfig); invertedOpacity.value = withTiming(isEnable ? 0 : 1, { duration: 150 }); borderRadius.value = withTiming(isEnable ? 20 : 13, timingConfig); diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index ac69d95c7..aa9fa308b 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -10,7 +10,7 @@ import { NotepadText, BookPlus } from "lucide-react-native"; -import { useSharedValue, withTiming } from "react-native-reanimated"; +import { FadeInDown, FadeOutUp, useSharedValue, withTiming } from "react-native-reanimated"; import { NativeIcon, NativeItem, @@ -23,6 +23,7 @@ import { createChannelNotification, requestNotificationPermission } from "@/back import { useCurrentAccount } from "@/stores/account"; import { useAlert } from "@/providers/AlertProvider"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; +import { anim2Papillon } from "@/utils/ui/animations"; const SettingsNotifications: Screen<"SettingsNotifications"> = ({ navigation @@ -142,12 +143,23 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ {enabled && ( <> - - + + {notificationSchoolary.map((notification, index) => ( Date: Thu, 13 Feb 2025 15:36:18 +0100 Subject: [PATCH 0617/1144] fix(menu): remplacer Pressable de react-native-gesture-handler par celui de react-native --- src/router/navigator/atoms/MenuItem.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/router/navigator/atoms/MenuItem.tsx b/src/router/navigator/atoms/MenuItem.tsx index f53a74ad5..28959c711 100644 --- a/src/router/navigator/atoms/MenuItem.tsx +++ b/src/router/navigator/atoms/MenuItem.tsx @@ -1,9 +1,8 @@ import * as React from "react"; import { useTheme } from "@react-navigation/native"; -import { StyleSheet, Platform } from "react-native"; +import { StyleSheet, Platform, Pressable } from "react-native"; import LottieView from "lottie-react-native"; import colorsList from "@/utils/data/colors.json"; -import { Pressable } from "react-native-gesture-handler"; import * as Haptics from "expo-haptics"; import Reanimated, { FadeIn, FadeOut, LinearTransition } from "react-native-reanimated"; import { anim2Papillon } from "@/utils/ui/animations"; From 0f2a2a520e5953960008a95b9e0d3c02dbd511bc Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Thu, 13 Feb 2025 15:40:33 +0100 Subject: [PATCH 0618/1144] =?UTF-8?q?fix(grades):=20mise=20=C3=A0=20jour?= =?UTF-8?q?=20des=20messages=20d'absence=20de=20notes=20et=20mise=20=C3=A0?= =?UTF-8?q?=20jour=20de=20la=20d=C3=A9pendance=20react-native-reanimated-g?= =?UTF-8?q?raph?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 8 ++++---- package.json | 2 +- src/views/account/Grades/Grades.tsx | 4 ++-- src/views/account/Home/Elements/GradesElement.tsx | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index f595ba8da..0cd05f9f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "papillonvex", "version": "7.8.3", "dependencies": { - "@birdwingo/react-native-reanimated-graph": "^1.1.3", + "@birdwingo/react-native-reanimated-graph": "^1.1.4", "@candlefinance/app-icon": "^0.4.5", "@howljs/calendar-kit": "^2.2.1", "@notifee/react-native": "^7.8.2", @@ -2159,9 +2159,9 @@ } }, "node_modules/@birdwingo/react-native-reanimated-graph": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@birdwingo/react-native-reanimated-graph/-/react-native-reanimated-graph-1.1.3.tgz", - "integrity": "sha512-LlP+l3IfJNMJIiF/Wa5ltujfeDZd6AFNUtjwd0kmSqg0nTMexC+oZhKY/8VCKg8eLy9neTfVcDtkBnj8NKnGMw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@birdwingo/react-native-reanimated-graph/-/react-native-reanimated-graph-1.1.4.tgz", + "integrity": "sha512-kUZRnIFwfJbOMEDgaRi7ZcfXDPXzEvuU1/tSuhHHkDO5NwZcfdjz+BaNWndhZVOs0T65QRJS3AvAeWU4jw9cvA==", "peerDependencies": { "react": ">=18.0.0", "react-native": ">=0.70.0", diff --git a/package.json b/package.json index 4d925c3bf..0806239ac 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "ios": "expo run:ios" }, "dependencies": { - "@birdwingo/react-native-reanimated-graph": "^1.1.3", + "@birdwingo/react-native-reanimated-graph": "^1.1.4", "@candlefinance/app-icon": "^0.4.5", "@howljs/calendar-kit": "^2.2.1", "@notifee/react-native": "^7.8.2", diff --git a/src/views/account/Grades/Grades.tsx b/src/views/account/Grades/Grades.tsx index c32102afa..5d80bed0f 100644 --- a/src/views/account/Grades/Grades.tsx +++ b/src/views/account/Grades/Grades.tsx @@ -210,8 +210,8 @@ const Grades: Screen<"Grades"> = ({ route, navigation }) => { )} diff --git a/src/views/account/Home/Elements/GradesElement.tsx b/src/views/account/Home/Elements/GradesElement.tsx index 691864e43..6e4423b9c 100644 --- a/src/views/account/Home/Elements/GradesElement.tsx +++ b/src/views/account/Home/Elements/GradesElement.tsx @@ -85,7 +85,7 @@ const GradesElement: React.FC = ({ onImportance }) => { title="Aucune note disponible" description={ defaultPeriod - ? `Tu n'as aucune note au ${defaultPeriod}.` + ? `Tu n'as aucune note au ${defaultPeriod.toLowerCase()}.` : "Tu n'as aucune note pour cette période." } /> From fdbfa91fa360b2e43e7da27fabb062b89927cf37 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Thu, 13 Feb 2025 15:47:17 +0100 Subject: [PATCH 0619/1144] fix(animations): remplacer animPapillon par anim2Papillon dans NativeList et GradesAverage --- src/components/Global/NativeComponents.tsx | 9 +++-- .../account/Grades/Graph/GradesAverage.tsx | 38 ++++++++++--------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/components/Global/NativeComponents.tsx b/src/components/Global/NativeComponents.tsx index b91015f92..df4bddd57 100644 --- a/src/components/Global/NativeComponents.tsx +++ b/src/components/Global/NativeComponents.tsx @@ -71,7 +71,7 @@ export const NativeList: React.FC = ({ inline && { marginTop: 16 }, style, ]} - layout={animated && animPapillon(LinearTransition)} + layout={animated && (layout ?? animPapillon(LinearTransition))} entering={entering} exiting={exiting} > @@ -81,7 +81,7 @@ export const NativeList: React.FC = ({ borderCurve: "continuous", overflow: "hidden", }]} - layout={animated && animPapillon(LinearTransition)} + layout={animated && (layout ?? animPapillon(LinearTransition))} > {childrenWithProps} @@ -95,12 +95,13 @@ interface NativeListHeaderProps { leading?: ReactNode trailing?: ReactNode animated?: boolean + layout?: LayoutAnimation entering?: EntryOrExitLayoutType exiting?: EntryOrExitLayoutType style?: StyleProp } -export const NativeListHeader: React.FC = ({ icon, label, leading, trailing, animated, entering, exiting, style }) => { +export const NativeListHeader: React.FC = ({ icon, label, leading, trailing, animated, layout, entering, exiting, style }) => { const theme = useTheme(); const { colors } = theme; @@ -117,7 +118,7 @@ export const NativeListHeader: React.FC = ({ icon, label, return ( diff --git a/src/views/account/Grades/Graph/GradesAverage.tsx b/src/views/account/Grades/Graph/GradesAverage.tsx index c28cbfa02..096c573dd 100644 --- a/src/views/account/Grades/Graph/GradesAverage.tsx +++ b/src/views/account/Grades/Graph/GradesAverage.tsx @@ -21,7 +21,7 @@ import Reanimated, { FadeOutUp, LinearTransition, } from "react-native-reanimated"; -import { animPapillon } from "@/utils/ui/animations"; +import { anim2Papillon } from "@/utils/ui/animations"; import * as Haptics from "expo-haptics"; import { PressableScale } from "react-native-pressable-scale"; @@ -166,9 +166,9 @@ const GradesAverageGraph: React.FC = ({ onPress={() => setShowDetails(!showDetails)} > {hLength > 0 && ( - + {((showDetails && !overall) || selectedDate) && ( @@ -200,8 +200,8 @@ const GradesAverageGraph: React.FC = ({ borderCurve: "continuous", zIndex: 100, }} - entering={animPapillon(FadeInLeft)} - exiting={animPapillon(FadeOutLeft)} + entering={anim2Papillon(FadeInLeft)} + exiting={anim2Papillon(FadeOutLeft)} > = ({ {hLength > 1 ? ( = ({ marginTop: 0, }, ]} - layout={animPapillon(LinearTransition)} + layout={anim2Papillon(LinearTransition)} > {selectedDate ? ( = ({ ) : ( = ({ = ({ contentContainerStyle={{ marginLeft: -2 }} /> - + /20 @@ -334,7 +334,7 @@ const GradesAverageGraph: React.FC = ({ Moyenne classe { !Number.isNaN(classAvg) ? ( <> @@ -342,7 +342,7 @@ const GradesAverageGraph: React.FC = ({ value={classAvg.toFixed(2)} style={styles.gradeNumberClass} /> - + /20 @@ -355,17 +355,18 @@ const GradesAverageGraph: React.FC = ({ {showDetails && maxAvg > 0 && minAvg > 0 ? ( = ({ Moyenne théorique max. Date: Thu, 13 Feb 2025 15:52:26 +0100 Subject: [PATCH 0620/1144] =?UTF-8?q?fix(router):=20supprimer=20des=20prop?= =?UTF-8?q?ri=C3=A9t=C3=A9s=20inutilis=C3=A9es=20dans=20les=20=C3=A9crans?= =?UTF-8?q?=20et=20corriger=20la=20cl=C3=A9=20de=20rendu=20dans=20GradesSc?= =?UTF-8?q?odocUE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/screens/views/index.ts | 16 ---------------- .../account/Grades/Atoms/GradesScodocUE.tsx | 2 +- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/src/router/screens/views/index.ts b/src/router/screens/views/index.ts index d902a4f5a..679a77d1a 100644 --- a/src/router/screens/views/index.ts +++ b/src/router/screens/views/index.ts @@ -63,10 +63,6 @@ export default [ presentation: "formSheet", gestureDirection: "vertical", animation: "slide_from_bottom", - sheetGrabberVisible: false, - sheetInitialDetentIndex: 0, - // @ts-expect-error IDK why it is a list of number, it should be SheetDetentTypes - sheetAllowedDetents: [0.5, 1.0], headerShown: false, }), createScreen("HomeworksDocument", HomeworksDocument, { @@ -74,10 +70,6 @@ export default [ presentation: "formSheet", gestureDirection: "vertical", animation: "slide_from_bottom", - sheetGrabberVisible: false, - sheetInitialDetentIndex: 0, - // @ts-expect-error IDK why it is a list of number, it should be SheetDetentTypes - sheetAllowedDetents: [0.5, 1.0], headerShown: false, }), createScreen("GradeSubject", GradeSubjectScreen, { @@ -85,20 +77,12 @@ export default [ presentation: "formSheet", gestureDirection: "vertical", animation: "slide_from_bottom", - sheetGrabberVisible: true, - sheetInitialDetentIndex: 0, - // @ts-expect-error IDK why it is a list of number, it should be SheetDetentTypes - sheetAllowedDetents: [0.5, 1.0], }), createScreen("GradeDocument", GradeDocument, { headerTitle: "Détail de la note", presentation: "formSheet", gestureDirection: "vertical", animation: "slide_from_bottom", - sheetGrabberVisible: false, - sheetInitialDetentIndex: 0, - // @ts-expect-error IDK why it is a list of number, it should be SheetDetentTypes - sheetAllowedDetents: [0.5, 1.0], headerShown: Platform.OS !== "ios", }), createScreen("ChatCreate", ChatCreate, { diff --git a/src/views/account/Grades/Atoms/GradesScodocUE.tsx b/src/views/account/Grades/Atoms/GradesScodocUE.tsx index 2ad4f5381..89d40891b 100644 --- a/src/views/account/Grades/Atoms/GradesScodocUE.tsx +++ b/src/views/account/Grades/Atoms/GradesScodocUE.tsx @@ -122,7 +122,7 @@ const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: Prim } return ( - + Date: Thu, 13 Feb 2025 15:54:05 +0100 Subject: [PATCH 0621/1144] =?UTF-8?q?fix(grades):=20ajouter=20des=20index?= =?UTF-8?q?=20aux=20cl=C3=A9s=20de=20rendu=20dans=20GradesScodocUE=20pour?= =?UTF-8?q?=20=C3=A9viter=20les=20avertissements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Grades/Atoms/GradesScodocUE.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/views/account/Grades/Atoms/GradesScodocUE.tsx b/src/views/account/Grades/Atoms/GradesScodocUE.tsx index 89d40891b..0b7e78237 100644 --- a/src/views/account/Grades/Atoms/GradesScodocUE.tsx +++ b/src/views/account/Grades/Atoms/GradesScodocUE.tsx @@ -71,7 +71,7 @@ const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: Prim /> - {finalUes.map((ue) => { + {finalUes.map((ue, i) => { interface ueGrade { key: string, type: "ressources" | "saes" @@ -122,7 +122,7 @@ const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: Prim } return ( - + ( Date: Thu, 13 Feb 2025 15:56:50 +0100 Subject: [PATCH 0622/1144] =?UTF-8?q?fix(grades):=20ajouter=20des=20animat?= =?UTF-8?q?ions=20d'entr=C3=A9e=20et=20de=20sortie=20pour=20les=20=C3=A9l?= =?UTF-8?q?=C3=A9ments=20de=20notes=20dans=20GradesScodocUE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Grades/Atoms/GradesScodocUE.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/views/account/Grades/Atoms/GradesScodocUE.tsx b/src/views/account/Grades/Atoms/GradesScodocUE.tsx index 0b7e78237..94486a8b2 100644 --- a/src/views/account/Grades/Atoms/GradesScodocUE.tsx +++ b/src/views/account/Grades/Atoms/GradesScodocUE.tsx @@ -220,6 +220,8 @@ const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: Prim Date: Thu, 13 Feb 2025 16:00:50 +0100 Subject: [PATCH 0623/1144] =?UTF-8?q?fix(grades):=20ajuster=20le=20style?= =?UTF-8?q?=20de=20l'en-t=C3=AAte=20et=20des=20=C3=A9l=C3=A9ments=20de=20n?= =?UTF-8?q?otes=20dans=20GradesScodocUE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Global/NativeComponents.tsx | 10 +++++++++- src/views/account/Grades/Atoms/GradesScodocUE.tsx | 9 ++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/components/Global/NativeComponents.tsx b/src/components/Global/NativeComponents.tsx index df4bddd57..1ab91d452 100644 --- a/src/components/Global/NativeComponents.tsx +++ b/src/components/Global/NativeComponents.tsx @@ -142,7 +142,15 @@ export const NativeListHeader: React.FC = ({ icon, label, {label} - {trailing} + + {trailing} + ); }; diff --git a/src/views/account/Grades/Atoms/GradesScodocUE.tsx b/src/views/account/Grades/Atoms/GradesScodocUE.tsx index 94486a8b2..58805b93b 100644 --- a/src/views/account/Grades/Atoms/GradesScodocUE.tsx +++ b/src/views/account/Grades/Atoms/GradesScodocUE.tsx @@ -47,8 +47,16 @@ const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: Prim showAlert({ icon: , title: "Unités d'enseignement", @@ -60,7 +68,6 @@ const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: Prim width: 24, height: 24, borderRadius: 8, - marginVertical: -8, borderColor: colors.text + "32", borderWidth: 1, }} From 6f080cde8943c73c99699114f93a673fed722f6c Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Thu, 13 Feb 2025 16:02:10 +0100 Subject: [PATCH 0624/1144] fix(grades): ajouter des bordures arrondies au composant GradesLatestItem --- src/views/account/Grades/Latest/LatestGradesItem.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/views/account/Grades/Latest/LatestGradesItem.tsx b/src/views/account/Grades/Latest/LatestGradesItem.tsx index b253084e0..6fc2e7120 100644 --- a/src/views/account/Grades/Latest/LatestGradesItem.tsx +++ b/src/views/account/Grades/Latest/LatestGradesItem.tsx @@ -55,6 +55,9 @@ const GradesLatestItem: React.FC = ({ gap: 8, paddingHorizontal: 14, paddingVertical: 10, + borderTopLeftRadius: 12, + borderTopRightRadius: 12, + borderCurve: "continuous", backgroundColor: subjectData.color + "11", }} > From 2c2bf558634d040f631b0814d528d352261deaab Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Thu, 13 Feb 2025 16:07:54 +0100 Subject: [PATCH 0625/1144] =?UTF-8?q?fix(grades):=20ajuster=20les=20couleu?= =?UTF-8?q?rs=20des=20=C3=A9l=C3=A9ments=20de=20notes=20et=20des=20titres?= =?UTF-8?q?=20en=20utilisant=20une=20fonction=20d'ajustement=20des=20coule?= =?UTF-8?q?urs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/ui/colors.ts | 20 +++++++++++++++++++ .../account/Grades/Atoms/GradesScodocUE.tsx | 13 ++++++++++-- .../Grades/Latest/LatestGradesItem.tsx | 2 ++ .../account/Grades/Subject/SubjectTitle.tsx | 7 ++++++- 4 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 src/utils/ui/colors.ts diff --git a/src/utils/ui/colors.ts b/src/utils/ui/colors.ts new file mode 100644 index 000000000..7daff32a6 --- /dev/null +++ b/src/utils/ui/colors.ts @@ -0,0 +1,20 @@ +export function adjustColor (hex: string, amount: number): string { + // Remove the hash if it's there + hex = hex.replace(/^#/, ""); + + // Parse the hex color into its RGB components + let bigint = parseInt(hex, 16); + let r = (bigint >> 16) & 255; + let g = (bigint >> 8) & 255; + let b = bigint & 255; + + // Adjust the RGB values by the specified amount + r = Math.max(0, Math.min(255, r + amount)); + g = Math.max(0, Math.min(255, g + amount)); + b = Math.max(0, Math.min(255, b + amount)); + + // Convert the RGB values back to a hex string + let newHex = ((r << 16) + (g << 8) + b).toString(16).padStart(6, "0"); + + return `#${newHex}`; +} \ No newline at end of file diff --git a/src/views/account/Grades/Atoms/GradesScodocUE.tsx b/src/views/account/Grades/Atoms/GradesScodocUE.tsx index 58805b93b..80df0c390 100644 --- a/src/views/account/Grades/Atoms/GradesScodocUE.tsx +++ b/src/views/account/Grades/Atoms/GradesScodocUE.tsx @@ -3,6 +3,7 @@ import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/componen import { useAlert } from "@/providers/AlertProvider"; import { PrimaryAccount } from "@/stores/account/types"; import { anim2Papillon } from "@/utils/ui/animations"; +import { adjustColor } from "@/utils/ui/colors"; import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; import { useTheme } from "@react-navigation/native"; @@ -143,12 +144,17 @@ const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: Prim alignItems: "center", justifyContent: "center", borderRadius: 8, - borderColor: colors.text + "32", + borderColor: adjustColor(ue.color, -100) + "32", borderWidth: 1, }} onPress={navigateToSubject} > - + {ue.name} @@ -217,6 +223,9 @@ const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: Prim {ue.titre} diff --git a/src/views/account/Grades/Latest/LatestGradesItem.tsx b/src/views/account/Grades/Latest/LatestGradesItem.tsx index 6fc2e7120..1190cc6ca 100644 --- a/src/views/account/Grades/Latest/LatestGradesItem.tsx +++ b/src/views/account/Grades/Latest/LatestGradesItem.tsx @@ -6,6 +6,7 @@ import { PressableScale } from "react-native-pressable-scale"; import type { Grade } from "@/services/shared/Grade"; import { FadeInRight, FadeOutLeft } from "react-native-reanimated"; import { anim2Papillon } from "@/utils/ui/animations"; +import { adjustColor } from "@/utils/ui/colors"; type GradeLatestItemProps = { grade: Grade; @@ -73,6 +74,7 @@ const GradesLatestItem: React.FC = ({ @@ -62,6 +63,7 @@ const SubjectTitle = ({ navigation, subject, subjectData, allGrades }: SubjectTi {typeof subject.average.average?.value === "number" ? subject.average.average.value.toFixed(2) : calculatedAverage !== -1 ? calculatedAverage.toFixed(2) : "N/A"} From d5735fb32c04927afd5d447676eba3e9daf89f5a Mon Sep 17 00:00:00 2001 From: Gabriel29306 <73659505+Gabriel29306@users.noreply.github.com> Date: Sat, 15 Feb 2025 22:57:21 +0700 Subject: [PATCH 0626/1144] chore: remove unusable file --- .github/workflows/build.yml | 196 ------------------------------------ 1 file changed, 196 deletions(-) delete mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index f633f71a7..000000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,196 +0,0 @@ -name: Weekly Expo Build for Android - -on: - schedule: - - cron: "0 0 * * 1" # Tous les lundis à 00h00 UTC - workflow_dispatch: - inputs: - patch_selection: - description: "Patchs à appliquer (séparer par des virgules, ou 'all' pour tous)" - required: false - default: "all" - -jobs: - build-android: - runs-on: ubuntu-latest - - steps: - # 1. Configurer Java 17 - - name: 🔧 Set up Java 17 - uses: actions/setup-java@v3 - with: - distribution: 'temurin' # Distribution Temurin (privilégiée) - java-version: '17' - - # 1. Récupérer le fichier JSON des sources et patchs - - name: 📥 Fetch Patch List - id: fetch_patch_list - run: | - curl -sL -H "Cache-Control: no-cache, no-store" https://raw.githubusercontent.com/Gabriel29306/datasets/main/papillon_7_patchs.json -o patch_list.json - echo $(cat patch_list.json) - - # 2. Extraire les informations des sources et patchs - - name: 🗂 Parse Sources and Patch List - id: parse_patch_list - run: | - echo "Parsing sources and patch list..." - MAIN_REPO="https://github.com/PapillonApp/Papillon.git" - ALL_PATCHES=$(jq -r '.patchs | join(",")' patch_list.json) - echo "MAIN_REPO=$MAIN_REPO" >> $GITHUB_ENV - echo "ALL_PATCHES=$ALL_PATCHES" >> $GITHUB_ENV - - # Extraire les sources sous forme de paires "nom=url" - jq -r '.source | to_entries[] | "\(.key)=\(.value)"' patch_list.json > sources.txt - - # 3. Vérifier et choisir les patchs à appliquer - - name: 🔍 Select Patches - id: select_patches - run: | - PATCH_INPUT="${{ inputs.patch_selection }}" - if [[ "$PATCH_INPUT" == "all" ]]; then - SELECTED_PATCHES=$ALL_PATCHES - else - SELECTED_PATCHES=$PATCH_INPUT - fi - echo "Selected patches: $SELECTED_PATCHES" - echo "SELECTED_PATCHES=$SELECTED_PATCHES" >> $GITHUB_ENV - - # 4. Cloner le main repo - - name: 🔧 Clone Main Repository - run: | - git clone $MAIN_REPO target-repo - cd target-repo - git checkout main - - # 5. Ajout de la configuration Git - - name: 🔧 Configure Git User - run: | - git config --global user.email "github-actions@github.com" - git config --global user.name "GitHub Actions" - - # 6. Ajouter les upstreams et appliquer les patchs - - name: 🔧 Add Upstreams and Merge Patches - run: | - cd target-repo - # Ajouter chaque upstream depuis sources.txt - while IFS='=' read -r NAME URL; do - echo "Adding remote: $NAME -> $URL" - git remote add "$NAME" "$URL" - git fetch "$NAME" - done < ../sources.txt - - # Appliquer les patchs - IFS=',' read -ra PATCHES <<< "$SELECTED_PATCHES" - for PATCH in "${PATCHES[@]}"; do - # Extraire nom et branche (par exemple : Gabriel29306/branch-name) - NAME=$(echo "$PATCH" | cut -d'/' -f1) - BRANCH=$(echo "$PATCH" | cut -d'/' -f2) - echo "Merging patch: $NAME/$BRANCH" - git fetch "$NAME" "$BRANCH" - git merge "$NAME/$BRANCH" -s recursive || { - echo "Conflict detected. Forcing merge for patch $PATCH." - git merge --abort - git merge "$NAME/$BRANCH" -s recursive -X theirs - } - done - - # 7. Installer les dépendances - - name: 📥 Install Dependencies - run: | - cd target-repo - npm ci || npm install - - # 8. Prebuild l'app avec Expo - - name: ⚙️ Prepare Android Build - run: | - cd target-repo - npx expo prebuild -p android - - # 9. Configurer gradle.properties pour générer des APKs par architecture - - name: 🔧 Configure Build Gradle for Split APKs - run: | - sed -i '/android {/a \ - splits { \ - abi { \ - enable true \ - reset() \ - include "armeabi-v7a", "arm64-v8a", "x86", "x86_64" \ - universalApk true \ - } \ - }' target-repo/android/app/build.gradle - sed -i '/buildToolsVersion/d' target-repo/android/app/build.gradle - - # 10. Récupérer la dernière version distante, y compris les pré-releases - - name: 🔍 Get Latest Release Version - id: get_latest_release - uses: actions/github-script@v6 - with: - script: | - try { - // Liste des releases, incluant les pré-releases - const releases = await github.rest.repos.listReleases({ - owner: context.repo.owner, - repo: context.repo.repo, - per_page: 1 // On ne prend que la première release - }); - - if (releases.data.length > 0) { - const latestRelease = releases.data[0]; // La dernière release ou pré-release - const tagVersion = parseInt(latestRelease.tag_name.replace("v", ""), 10); - - if (isNaN(tagVersion)) { - console.log("Invalid tag format in latest release. Using default versionCode 100."); - core.setOutput("versionCode", 100); - } else { - console.log(`Latest version code (including pre-release): ${tagVersion}`); - core.setOutput("versionCode", tagVersion); - } - } else { - console.log("No release found. Using default versionCode 100."); - core.setOutput("versionCode", 100); - } - } catch (error) { - console.log("Error fetching releases: ", error); - console.log("Using default versionCode 100."); - core.setOutput("versionCode", 100); - } - - # 11. Incrémentation de la version - - name: 📈 Increment Version Code - run: | - cd target-repo - # Récupérer la dernière versionCode à partir de la sortie précédente - BASE_VERSION_CODE="${{ steps.get_latest_release.outputs.versionCode }}" - echo "Base versionCode: $BASE_VERSION_CODE" - NEW_VERSION_CODE=$((BASE_VERSION_CODE + 1)) - echo "New versionCode: $NEW_VERSION_CODE" - # Mettre à jour versionCode dans build.gradle - sed -i "s/versionCode [0-9]*/versionCode ${NEW_VERSION_CODE}/" android/app/build.gradle - echo "VERSION_CODE=${NEW_VERSION_CODE}" >> $GITHUB_ENV - - # 12. Build des apks - - name: 🏗️ Build Release APK - run: | - cd target-repo/android - ./gradlew :app:assembleRelease - - # 13. Renommer les apks - - name: ⚙️ Rename apks - run: | - cd target-repo/android/app/build/outputs/apk/release/ - mv app-arm64-v8a-release.apk Papillon-arm64-v8a-release.apk - mv app-armeabi-v7a-release.apk Papillon-armeabi-v7a-release.apk - mv app-universal-release.apk Papillon-universal-release.apk - mv app-x86-release.apk Papillon-x86-release.apk - mv app-x86_64-release.apk Papillon-x86_64-release.apk - - # 14. Publier les APKs dans une préversion - - name: 📤 Create Pre-Release with APKs - uses: ncipollo/release-action@v1 - with: - tag: "${{ env.VERSION_CODE }}" - name: "Weekly Build v${{ steps.get_latest_release.outputs.version }}" - body: "Build automatique hebdomadaire pour Android." - prerelease: true - artifacts: | - target-repo/android/app/build/outputs/apk/release/*.apk From 7533cf902ec3b6606abf2f7599a84ab851817969 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sat, 15 Feb 2025 22:49:11 +0100 Subject: [PATCH 0627/1144] =?UTF-8?q?fix(build):=20mettre=20=C3=A0=20jour?= =?UTF-8?q?=20la=20cible=20de=20d=C3=A9ploiement=20iOS=20=C3=A0=2015.6=20e?= =?UTF-8?q?t=20ajouter=20des=20propri=C3=A9t=C3=A9s=20de=20construction=20?= =?UTF-8?q?suppl=C3=A9mentaires?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/gradle.properties | 2 + app.config.ts | 21 +++-- ios/Papillon.xcodeproj/project.pbxproj | 122 ++++++++++++------------- ios/Podfile | 2 +- ios/Podfile.lock | 108 +++++++++++++++++++++- ios/Podfile.properties.json | 6 +- package-lock.json | 111 +++++++++++++++++++++- package.json | 6 +- 8 files changed, 301 insertions(+), 77 deletions(-) diff --git a/android/gradle.properties b/android/gradle.properties index 2c83d11df..08d347b5a 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -54,3 +54,5 @@ EX_DEV_CLIENT_NETWORK_INSPECTOR=true # Use legacy packaging to compress native libraries in the resulting APK. expo.useLegacyPackaging=false + +android.extraMavenRepos=[] \ No newline at end of file diff --git a/app.config.ts b/app.config.ts index 2519d418c..9f4ba7eb8 100644 --- a/app.config.ts +++ b/app.config.ts @@ -77,6 +77,14 @@ export default (): ExpoConfig => ({ ], }, plugins: [ + [ + "expo-build-properties", + { + "ios": { + "deploymentTarget": "15.6" + } + } + ], "./plugins/notifee-mod.js", [ "expo-font", @@ -127,19 +135,14 @@ export default (): ExpoConfig => ({ [ "react-native-share", { - ios: [ - "fb", - "instagram", - "twitter", - "tiktoksharesdk", - ], + ios: ["fb", "instagram", "twitter", "tiktoksharesdk"], android: [ "com.facebook.katana", "com.instagram.android", "com.twitter.android", "com.zhiliaoapp.musically", - ] - } - ] + ], + }, + ], ], }); diff --git a/ios/Papillon.xcodeproj/project.pbxproj b/ios/Papillon.xcodeproj/project.pbxproj index 01967ab06..fcefae4c9 100644 --- a/ios/Papillon.xcodeproj/project.pbxproj +++ b/ios/Papillon.xcodeproj/project.pbxproj @@ -10,10 +10,10 @@ 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 1DCAF8F7437CAA80FED004D0 /* libPods-Papillon.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F4D5C3B3ADAE08E4CE97F28A /* libPods-Papillon.a */; }; 2D6C6538E5F346C0964ED2DA /* FixelText-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2CA436545607494ABF55D4A0 /* FixelText-Light.ttf */; }; 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; }; 472EB3522C45E0ED00503877 /* Stickers.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 472EB3512C45E0ED00503877 /* Stickers.xcassets */; }; + 5A558BBA98FCE493BB598E45 /* libPods-Papillon.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 144E505BD11353C4D97F0A76 /* libPods-Papillon.a */; }; 63920DB919D54244A4D0D651 /* FixelText-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 222162480574474F81E8B151 /* FixelText-Regular.ttf */; }; 65B64A82951D461DBA5DCB67 /* FixelText-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 52806640827D4D76AC6BD305 /* FixelText-Bold.ttf */; }; 6C3E424F5C754637A91EF425 /* noop-file.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3597223C6B74832B9814997 /* noop-file.swift */; }; @@ -32,9 +32,10 @@ 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Papillon/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Papillon/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Papillon/main.m; sourceTree = ""; }; + 144E505BD11353C4D97F0A76 /* libPods-Papillon.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Papillon.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 222162480574474F81E8B151 /* FixelText-Regular.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "FixelText-Regular.ttf"; path = "../assets/fonts/FixelText-Regular.ttf"; sourceTree = ""; }; 2CA436545607494ABF55D4A0 /* FixelText-Light.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "FixelText-Light.ttf"; path = "../assets/fonts/FixelText-Light.ttf"; sourceTree = ""; }; - 3A8E4D54C16E8EFC3D40700F /* Pods-Papillon.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Papillon.debug.xcconfig"; path = "Target Support Files/Pods-Papillon/Pods-Papillon.debug.xcconfig"; sourceTree = ""; }; + 4075E87E5497E8E983BA2C6C /* Pods-Papillon.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Papillon.release.xcconfig"; path = "Target Support Files/Pods-Papillon/Pods-Papillon.release.xcconfig"; sourceTree = ""; }; 472EB33B2C45E06600503877 /* Messages.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Messages.framework; path = System/Library/Frameworks/Messages.framework; sourceTree = SDKROOT; }; 472EB34F2C45E0EC00503877 /* Autocollants Papillon.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Autocollants Papillon.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; 472EB3512C45E0ED00503877 /* Stickers.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Stickers.xcassets; sourceTree = ""; }; @@ -42,13 +43,12 @@ 523BD2EA68B24DC59000E016 /* FixelText-Medium.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "FixelText-Medium.ttf"; path = "../assets/fonts/FixelText-Medium.ttf"; sourceTree = ""; }; 52806640827D4D76AC6BD305 /* FixelText-Bold.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "FixelText-Bold.ttf"; path = "../assets/fonts/FixelText-Bold.ttf"; sourceTree = ""; }; 54DA555BF32B482E8C5E646E /* FixelText-SemiBold.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "FixelText-SemiBold.ttf"; path = "../assets/fonts/FixelText-SemiBold.ttf"; sourceTree = ""; }; - 5980E394169B8298F2271582 /* Pods-Papillon.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Papillon.release.xcconfig"; path = "Target Support Files/Pods-Papillon/Pods-Papillon.release.xcconfig"; sourceTree = ""; }; + 650FBB636F6B3FBDFC7FD932 /* Pods-Papillon.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Papillon.debug.xcconfig"; path = "Target Support Files/Pods-Papillon/Pods-Papillon.debug.xcconfig"; sourceTree = ""; }; AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = Papillon/SplashScreen.storyboard; sourceTree = ""; }; BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = ""; }; BBF321108A9C475FB9616C65 /* Papillon-Bridging-Header.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = "Papillon-Bridging-Header.h"; path = "Papillon/Papillon-Bridging-Header.h"; sourceTree = ""; }; E3597223C6B74832B9814997 /* noop-file.swift */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.swift; name = "noop-file.swift"; path = "Papillon/noop-file.swift"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; - F4D5C3B3ADAE08E4CE97F28A /* libPods-Papillon.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Papillon.a"; sourceTree = BUILT_PRODUCTS_DIR; }; FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-Papillon/ExpoModulesProvider.swift"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -57,7 +57,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 1DCAF8F7437CAA80FED004D0 /* libPods-Papillon.a in Frameworks */, + 5A558BBA98FCE493BB598E45 /* libPods-Papillon.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -86,7 +86,7 @@ children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, 472EB33B2C45E06600503877 /* Messages.framework */, - F4D5C3B3ADAE08E4CE97F28A /* libPods-Papillon.a */, + 144E505BD11353C4D97F0A76 /* libPods-Papillon.a */, ); name = Frameworks; sourceTree = ""; @@ -165,8 +165,8 @@ D65327D7A22EEC0BE12398D9 /* Pods */ = { isa = PBXGroup; children = ( - 3A8E4D54C16E8EFC3D40700F /* Pods-Papillon.debug.xcconfig */, - 5980E394169B8298F2271582 /* Pods-Papillon.release.xcconfig */, + 650FBB636F6B3FBDFC7FD932 /* Pods-Papillon.debug.xcconfig */, + 4075E87E5497E8E983BA2C6C /* Pods-Papillon.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -186,14 +186,14 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Papillon" */; buildPhases = ( - 01FFFC051C3C080886C1579E /* [CP] Check Pods Manifest.lock */, + 4E1C2362264A5656A716570E /* [CP] Check Pods Manifest.lock */, 1F5E2074DBF04DEF0D455912 /* [Expo] Configure project */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - 76C612767CA1E0C77C9DBC0D /* [CP] Embed Pods Frameworks */, - CF8CCF044100B2E613895955 /* [CP] Copy Pods Resources */, + B9535DB6117B504A1D5D0FA7 /* [CP] Embed Pods Frameworks */, + 18B588B8857BFFB16B20692A /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -299,26 +299,46 @@ shellPath = /bin/sh; shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" \"$PROJECT_ROOT\" ios absolute | tail -n 1)\"\nfi\n\nif [[ -z \"$CLI_PATH\" ]]; then\n # Use Expo CLI\n export CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })\")\"\nfi\nif [[ -z \"$BUNDLE_COMMAND\" ]]; then\n # Default Expo CLI command for bundling\n export BUNDLE_COMMAND=\"export:embed\"\nfi\n\n# Source .xcode.env.updates if it exists to allow\n# SKIP_BUNDLING to be unset if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.updates\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.updates\"\nfi\n# Source local changes to allow overrides\n# if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n`\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n\n"; }; - 01FFFC051C3C080886C1579E /* [CP] Check Pods Manifest.lock */ = { + 18B588B8857BFFB16B20692A /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Papillon/Pods-Papillon-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/EXApplication/ExpoApplication_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/EXTaskManager/ExpoTaskManager_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/ExpoDevice/ExpoDevice_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/ExpoMediaLibrary/ExpoMediaLibrary_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-menu/EXDevMenu.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/LottiePrivacyInfo.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/lottie-react-native/Lottie_React_Native_Privacy.bundle", ); + name = "[CP] Copy Pods Resources"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Papillon-checkManifestLockResult.txt", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoApplication_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoTaskManager_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoDevice_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoMediaLibrary_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevMenu.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/LottiePrivacyInfo.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Lottie_React_Native_Privacy.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Papillon/Pods-Papillon-resources.sh\"\n"; showEnvVarsInLog = 0; }; 1F5E2074DBF04DEF0D455912 /* [Expo] Configure project */ = { @@ -340,64 +360,44 @@ shellPath = /bin/sh; shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-Papillon/expo-configure-project.sh\"\n"; }; - 76C612767CA1E0C77C9DBC0D /* [CP] Embed Pods Frameworks */ = { + 4E1C2362264A5656A716570E /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Papillon/Pods-Papillon-frameworks.sh", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", + "$(DERIVED_FILE_DIR)/Pods-Papillon-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Papillon/Pods-Papillon-frameworks.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - CF8CCF044100B2E613895955 /* [CP] Copy Pods Resources */ = { + B9535DB6117B504A1D5D0FA7 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Papillon/Pods-Papillon-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/EXApplication/ExpoApplication_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/EXTaskManager/ExpoTaskManager_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/ExpoDevice/ExpoDevice_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/ExpoMediaLibrary/ExpoMediaLibrary_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-menu/EXDevMenu.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/LottiePrivacyInfo.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/lottie-react-native/Lottie_React_Native_Privacy.bundle", + "${PODS_ROOT}/Target Support Files/Pods-Papillon/Pods-Papillon-frameworks.sh", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", ); - name = "[CP] Copy Pods Resources"; + name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoApplication_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoTaskManager_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoDevice_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoMediaLibrary_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevMenu.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/LottiePrivacyInfo.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Lottie_React_Native_Privacy.bundle", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Papillon/Pods-Papillon-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Papillon/Pods-Papillon-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -426,7 +426,7 @@ /* Begin XCBuildConfiguration section */ 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 3A8E4D54C16E8EFC3D40700F /* Pods-Papillon.debug.xcconfig */; + baseConfigurationReference = 650FBB636F6B3FBDFC7FD932 /* Pods-Papillon.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; @@ -440,7 +440,7 @@ "FB_SONARKIT_ENABLED=1", ); INFOPLIST_FILE = Papillon/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.4; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -464,7 +464,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5980E394169B8298F2271582 /* Pods-Papillon.release.xcconfig */; + baseConfigurationReference = 4075E87E5497E8E983BA2C6C /* Pods-Papillon.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; @@ -473,7 +473,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 7RXNP6V83P; INFOPLIST_FILE = Papillon/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.4; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/ios/Podfile b/ios/Podfile index 42d3a8359..5e9e8ea65 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -7,7 +7,7 @@ podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] -platform :ios, podfile_properties['ios.deploymentTarget'] || '13.4' +platform :ios, podfile_properties['ios.deploymentTarget'] || '15.6' install! 'cocoapods', :deterministic_uuids => false diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 030f884b8..b2bb491fd 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -2,6 +2,11 @@ PODS: - boost (1.83.0) - candlefinance-app-icon (0.4.5): - React-Core + - ComputableLayout (0.7.0): + - DGSwiftUtilities (~> 0.11) + - ContextMenuAuxiliaryPreview (0.5.2): + - DGSwiftUtilities (~> 0.29) + - DGSwiftUtilities (0.46.3) - DoubleConversion (1.1.6) - EXApplication (5.9.1): - ExpoModulesCore @@ -1190,6 +1195,81 @@ PODS: - React-debug - react-native-cookies (6.2.1): - React-Core + - react-native-ios-context-menu (3.1.0): + - ContextMenuAuxiliaryPreview (~> 0.5) + - DGSwiftUtilities + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Codegen + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - react-native-ios-utilities + - React-NativeModulesApple + - React-RCTAppDelegate + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - react-native-ios-utilities (5.1.1): + - ComputableLayout (~> 0.7) + - DGSwiftUtilities (~> 0.46) + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Codegen + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTAppDelegate + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - react-native-ios-visual-effect-view (0.1.2): + - ComputableLayout + - DGSwiftUtilities (~> 0.46.3) + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Codegen + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - react-native-ios-utilities + - React-NativeModulesApple + - React-RCTAppDelegate + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - VisualEffectBlurView (~> 7.2) + - Yoga - react-native-netinfo (11.3.1): - React-Core - react-native-pager-view (6.3.0): @@ -1473,7 +1553,7 @@ PODS: - React-Core - RNDateTimePicker (8.0.1): - React-Core - - RNGestureHandler (2.16.2): + - RNGestureHandler (2.22.1): - DoubleConversion - glog - hermes-engine @@ -1567,6 +1647,8 @@ PODS: - React-Core - SocketRocket (0.7.0) - UMAppLoader (4.6.0) + - VisualEffectBlurView (7.2.0): + - DGSwiftUtilities (~> 0.46) - Yoga (0.0.0) - ZXingObjC/Core (3.6.9) - ZXingObjC/OneD (3.6.9): @@ -1644,6 +1726,9 @@ DEPENDENCIES: - React-logger (from `../node_modules/react-native/ReactCommon/logger`) - React-Mapbuffer (from `../node_modules/react-native/ReactCommon`) - "react-native-cookies (from `../node_modules/@react-native-cookies/cookies`)" + - react-native-ios-context-menu (from `../node_modules/react-native-ios-context-menu`) + - react-native-ios-utilities (from `../node_modules/react-native-ios-utilities`) + - react-native-ios-visual-effect-view (from `../node_modules/react-native-ios-visual-effect-view`) - "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)" - react-native-pager-view (from `../node_modules/react-native-pager-view`) - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) @@ -1686,8 +1771,12 @@ DEPENDENCIES: SPEC REPOS: trunk: + - ComputableLayout + - ContextMenuAuxiliaryPreview + - DGSwiftUtilities - lottie-ios - SocketRocket + - VisualEffectBlurView - ZXingObjC EXTERNAL SOURCES: @@ -1826,6 +1915,12 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon" react-native-cookies: :path: "../node_modules/@react-native-cookies/cookies" + react-native-ios-context-menu: + :path: "../node_modules/react-native-ios-context-menu" + react-native-ios-utilities: + :path: "../node_modules/react-native-ios-utilities" + react-native-ios-visual-effect-view: + :path: "../node_modules/react-native-ios-visual-effect-view" react-native-netinfo: :path: "../node_modules/@react-native-community/netinfo" react-native-pager-view: @@ -1908,6 +2003,9 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: d3f49c53809116a5d38da093a8aa78bf551aed09 candlefinance-app-icon: 9d7562e14a7a087c4ce87c262f59300a4f18106a + ComputableLayout: c50faffac4ed9f8f05b0ce5e6f3a60df1f6042c8 + ContextMenuAuxiliaryPreview: 20be0be795b783b68f8792732eed4bed9f202c1c + DGSwiftUtilities: 2f0d35d5ff3d57bd70ccc42f15971460db202c41 DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5 EXApplication: c08200c34daca7af7fd76ac4b9d606077410e8ad EXAV: afa491e598334bbbb92a92a2f4dd33d7149ad37f @@ -1974,6 +2072,9 @@ SPEC CHECKSUMS: React-logger: de9b65c8c7b71a663e6e99d347b1c445f5190c39 React-Mapbuffer: 766bb4d8f655d816913325b353d800debbde7209 react-native-cookies: f54fcded06bb0cda05c11d86788020b43528a26c + react-native-ios-context-menu: 486ed6e4d0fd44799f1b54f866e96d56c47beb48 + react-native-ios-utilities: 1e1fa0b4e68876e37bd26312f09aa07d79268c65 + react-native-ios-visual-effect-view: ce2f72588dab1a38049131887a1c4660e41dc8e1 react-native-netinfo: bdb108d340cdb41875c9ced535977cac6d2ff321 react-native-pager-view: c1e29e1a6105a02807392ba822ad322447a72f55 react-native-safe-area-context: a240ad4b683349e48b1d51fed1611138d1bdad97 @@ -2005,7 +2106,7 @@ SPEC CHECKSUMS: RNCAsyncStorage: 826b603ae9c0f88b5ac4e956801f755109fa4d5c RNCMaskedView: 090213d32d8b3bb83a4dcb7d12c18f0152591906 RNDateTimePicker: b6a9b35a785ecbe12b4e7d6de5439d0aa4614146 - RNGestureHandler: 2282cfbcf86c360d29f44ace393203afd5c6cff7 + RNGestureHandler: f7abf21d594742be28a3a72528069225a3187e26 RNNotifee: 935f3ea8c134c88cbf8b13ea0c97c72c09ad2116 RNReanimated: 35f9ac9c3ac42d0497ebd1cce5c39d7687a8493e RNScreens: b32a9ff15bea7fcdbe5dff6477bc503f792b1208 @@ -2013,9 +2114,10 @@ SPEC CHECKSUMS: RNSVG: 8b1a777d54096b8c2a0fd38fc9d5a454332bbb4d SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d UMAppLoader: f17a5ee8e85b536ace0fc254b447a37ed198d57e + VisualEffectBlurView: 2db84754ea90a2f4eccb0e84038653caddaa4eff Yoga: 4f4f07a17818e76d1b04edc01b68b6d49a682100 ZXingObjC: 8898711ab495761b2dbbdec76d90164a6d7e14c5 -PODFILE CHECKSUM: 7f0387b1ee3dbf846a6b743dd3d86d401c33d8c2 +PODFILE CHECKSUM: 52c2fc0f0da6749b1aa3175a834dddc6986ae26c COCOAPODS: 1.15.2 diff --git a/ios/Podfile.properties.json b/ios/Podfile.properties.json index de9f7b752..2fc14c3ee 100644 --- a/ios/Podfile.properties.json +++ b/ios/Podfile.properties.json @@ -1,4 +1,8 @@ { "expo.jsEngine": "hermes", - "EX_DEV_CLIENT_NETWORK_INSPECTOR": "true" + "EX_DEV_CLIENT_NETWORK_INSPECTOR": "true", + "ios.deploymentTarget": "15.6", + "apple.extraPods": "[]", + "apple.ccacheEnabled": "false", + "apple.privacyManifestAggregationEnabled": "true" } diff --git a/package-lock.json b/package-lock.json index 0cd05f9f1..52ca04261 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,11 +35,12 @@ "expo-barcode-scanner": "~13.0.1", "expo-blur": "~13.0.2", "expo-brightness": "~12.0.1", + "expo-build-properties": "~0.12.5", "expo-camera": "~15.0.13", "expo-clipboard": "~6.0.3", "expo-constants": "~16.0.2", "expo-crypto": "~13.0.2", - "expo-dev-menu": "^5.0.16", + "expo-dev-menu": "^5.0.23", "expo-device": "~6.0.2", "expo-file-system": "~17.0.1", "expo-font": "~12.0.7", @@ -83,6 +84,9 @@ "react-native-gesture-handler": "^2.22.1", "react-native-htmlview": "^0.17.0", "react-native-infinite-pager": "^0.3.16", + "react-native-ios-context-menu": "^3.1.0-0", + "react-native-ios-utilities": "^5.0.0-59", + "react-native-ios-visual-effect-view": "^0.1.0-17", "react-native-pager-view": "6.3.0", "react-native-parsed-text": "^0.0.22", "react-native-pressable-scale": "^2.1.0", @@ -2182,6 +2186,11 @@ "react-native": "*" } }, + "node_modules/@dominicstop/ts-event-emitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@dominicstop/ts-event-emitter/-/ts-event-emitter-1.1.0.tgz", + "integrity": "sha512-CcxmJIvUb1vsFheuGGVSQf4KdPZC44XolpUT34+vlal+LyQoBUOn31pjFET5M9ctOxEpt8xa0M3/2M7uUiAoJw==" + }, "node_modules/@egjs/hammerjs": { "version": "2.0.17", "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", @@ -9146,6 +9155,49 @@ "expo": "*" } }, + "node_modules/expo-build-properties": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/expo-build-properties/-/expo-build-properties-0.12.5.tgz", + "integrity": "sha512-donC1le0PYfLKCPKRMGQoixuWuwDWCngzXSoQXUPsgHTDHQUKr8aw+lcWkTwZcItgNovcnk784I0dyfYDcxybA==", + "dependencies": { + "ajv": "^8.11.0", + "semver": "^7.6.0" + }, + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-build-properties/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/expo-build-properties/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/expo-build-properties/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/expo-camera": { "version": "15.0.16", "resolved": "https://registry.npmjs.org/expo-camera/-/expo-camera-15.0.16.tgz", @@ -9617,6 +9669,21 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, "node_modules/fast-xml-parser": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", @@ -14809,6 +14876,48 @@ "react-native-reanimated": ">=3.8.0" } }, + "node_modules/react-native-ios-context-menu": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/react-native-ios-context-menu/-/react-native-ios-context-menu-3.1.0.tgz", + "integrity": "sha512-qdPSXMKUp5lDgmZeUPdv5sgBFhkFrIqma+zsnqJQYOvekb6Qs17yJy1Rqhrj0bJrwuduHzZX0aYbaA8whxqpDw==", + "workspaces": [ + "example", + "example-expo" + ], + "dependencies": { + "@dominicstop/ts-event-emitter": "^1.1.0" + }, + "peerDependencies": { + "react": "*", + "react-native": "*", + "react-native-ios-utilities": "*" + } + }, + "node_modules/react-native-ios-utilities": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/react-native-ios-utilities/-/react-native-ios-utilities-5.1.1.tgz", + "integrity": "sha512-fOm7IR2KCn3MzghITbrnZfpJ3Z7wai4S46GwXwTql1fzX25eO8MXVgaUeMd5EvPwg1zAqF5I6c3T6Dby8DoF3A==", + "workspaces": [ + "example" + ], + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/react-native-ios-visual-effect-view": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/react-native-ios-visual-effect-view/-/react-native-ios-visual-effect-view-0.1.2.tgz", + "integrity": "sha512-6fjeF+BO3rev+U3/K2CNwzkJmeaOiBNfiL7qSUftjs1oSgPIHQ+4WS6HvHdAVgAds76F1OgDWViQJrCYyVnHww==", + "workspaces": [ + "example" + ], + "peerDependencies": { + "react": "*", + "react-native": "*", + "react-native-ios-utilities": "*" + } + }, "node_modules/react-native-pager-view": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/react-native-pager-view/-/react-native-pager-view-6.3.0.tgz", diff --git a/package.json b/package.json index 0806239ac..39fda8125 100644 --- a/package.json +++ b/package.json @@ -37,11 +37,12 @@ "expo-barcode-scanner": "~13.0.1", "expo-blur": "~13.0.2", "expo-brightness": "~12.0.1", + "expo-build-properties": "~0.12.5", "expo-camera": "~15.0.13", "expo-clipboard": "~6.0.3", "expo-constants": "~16.0.2", "expo-crypto": "~13.0.2", - "expo-dev-menu": "^5.0.16", + "expo-dev-menu": "^5.0.23", "expo-device": "~6.0.2", "expo-file-system": "~17.0.1", "expo-font": "~12.0.7", @@ -85,6 +86,9 @@ "react-native-gesture-handler": "^2.22.1", "react-native-htmlview": "^0.17.0", "react-native-infinite-pager": "^0.3.16", + "react-native-ios-context-menu": "^3.1.0-0", + "react-native-ios-utilities": "^5.0.0-59", + "react-native-ios-visual-effect-view": "^0.1.0-17", "react-native-pager-view": "6.3.0", "react-native-parsed-text": "^0.0.22", "react-native-pressable-scale": "^2.1.0", From 4f08026eafa1c72e79ce09e1338572d72e245d33 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sat, 15 Feb 2025 23:01:16 +0100 Subject: [PATCH 0628/1144] =?UTF-8?q?feat(papillonPicker):=20ajouter=20un?= =?UTF-8?q?=20bouton=20de=20menu=20contextuel=20pour=20la=20s=C3=A9lection?= =?UTF-8?q?=20d'=C3=A9l=C3=A9ments=20sur=20iOS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Global/PapillonPicker.tsx | 50 +++++++++++++++++++++++- src/views/account/Lessons/Lessons.tsx | 1 + 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/components/Global/PapillonPicker.tsx b/src/components/Global/PapillonPicker.tsx index 75d214237..6412e2b56 100644 --- a/src/components/Global/PapillonPicker.tsx +++ b/src/components/Global/PapillonPicker.tsx @@ -7,10 +7,19 @@ import { TouchableOpacity } from "react-native-gesture-handler"; import Reanimated, { LinearTransition, type AnimatedStyle } from "react-native-reanimated"; import { NativeText } from "./NativeComponents"; +import { ContextMenuButton } from "react-native-ios-context-menu"; + import { BlurView } from "expo-blur"; import { Check } from "lucide-react-native"; +import { isExpoGo } from "@/utils/native/expoGoAlert"; -export type PickerDataItem = string | { label: string, icon?: JSX.Element, onPress?: () => unknown, checked?: boolean }; +export type PickerDataItem = string | { + label: string, + icon?: JSX.Element, + sfSymbol?: string, + onPress?: () => unknown, + checked?: boolean +}; type PickerData = PickerDataItem[]; @@ -47,6 +56,45 @@ const PapillonPicker: React.FC = ({ } }; + if (Platform.OS === "ios" && !isExpoGo()) { + return ( + { + const actionKey = event.nativeEvent.actionKey; + const index = parseInt(actionKey.split("-")[1]); + + const item = data[index]; + if (item !== null) { + if (typeof item === "string") { + handleSelectionChange(item); + } else { + item.onPress(); + } + } + }} + menuConfig={{ + menuTitle: "", + menuItems: data.filter((item) => item !== null).map((item, index) => { + return { + actionKey: "action-"+index.toString(), + actionTitle: typeof item === "string" ? item : item.label, + menuState: (typeof item !== "string" ? item.checked : item === selected) ? "on" : "off", + icon: { + type: typeof item !== "string" ? "IMAGE_SYSTEM" : "IMAGE_SYSTEM", + imageValue: { + systemName: typeof item !== "string" ? (item.sfSymbol ? item.sfSymbol : "") : "", + }, + } + }; + }), + }} + > + {children} + + ); + } + return ( = ({ route, navigation }) => { { icon: , label: "Importer un iCal", + sfSymbol: "calendar.badge.plus", onPress: () => { navigation.navigate("LessonsImportIcal", {}); } From ed649b91e858a7f940366a229ac8f72727d5cf46 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sat, 15 Feb 2025 23:07:35 +0100 Subject: [PATCH 0629/1144] =?UTF-8?q?fix(grades):=20ajuster=20les=20couleu?= =?UTF-8?q?rs=20des=20=C3=A9l=C3=A9ments=20en=20fonction=20du=20th=C3=A8me?= =?UTF-8?q?=20sombre?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Grades/Atoms/GradesScodocUE.tsx | 9 +++++---- src/views/account/Grades/Latest/LatestGradesItem.tsx | 5 ++++- src/views/account/Grades/Subject/SubjectTitle.tsx | 10 +++++----- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/views/account/Grades/Atoms/GradesScodocUE.tsx b/src/views/account/Grades/Atoms/GradesScodocUE.tsx index 80df0c390..7bdd7aa07 100644 --- a/src/views/account/Grades/Atoms/GradesScodocUE.tsx +++ b/src/views/account/Grades/Atoms/GradesScodocUE.tsx @@ -16,7 +16,8 @@ import Reanimated, { FadeIn, FadeInDown, FadeOut, FadeOutUp, LinearTransition } const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: PrimaryAccount, navigation: any, selectedPeriod: string }) => { try { - const { colors } = useTheme(); + const theme = useTheme(); + const colors = theme.colors as any; const { showAlert } = useAlert(); const data = account.serviceData.semestres as any; @@ -144,7 +145,7 @@ const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: Prim alignItems: "center", justifyContent: "center", borderRadius: 8, - borderColor: adjustColor(ue.color, -100) + "32", + borderColor: adjustColor(ue.color, theme.dark ? 100 : -100) + "32", borderWidth: 1, }} onPress={navigateToSubject} @@ -152,7 +153,7 @@ const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: Prim {ue.name} @@ -224,7 +225,7 @@ const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: Prim variant="body" numberOfLines={2} style={{ - color: adjustColor(ue.color, -100) + color: adjustColor(ue.color, theme.dark ? 100 : -100) }} > {ue.titre} diff --git a/src/views/account/Grades/Latest/LatestGradesItem.tsx b/src/views/account/Grades/Latest/LatestGradesItem.tsx index 1190cc6ca..aab056d21 100644 --- a/src/views/account/Grades/Latest/LatestGradesItem.tsx +++ b/src/views/account/Grades/Latest/LatestGradesItem.tsx @@ -7,6 +7,7 @@ import type { Grade } from "@/services/shared/Grade"; import { FadeInRight, FadeOutLeft } from "react-native-reanimated"; import { anim2Papillon } from "@/utils/ui/animations"; import { adjustColor } from "@/utils/ui/colors"; +import { useTheme } from "@react-navigation/native"; type GradeLatestItemProps = { grade: Grade; @@ -21,6 +22,8 @@ const GradesLatestItem: React.FC = ({ navigation, allGrades, }) => { + const theme = useTheme(); + const [subjectData, setSubjectData] = useState({ color: "#888888", pretty: "Matière inconnue", @@ -74,7 +77,7 @@ const GradesLatestItem: React.FC = ({ {typeof subject.average.average?.value === "number" ? subject.average.average.value.toFixed(2) : calculatedAverage !== -1 ? calculatedAverage.toFixed(2) : "N/A"} From 9c7119c2fa22e1d2ac4956ff5bddff629fa7a02e Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sat, 15 Feb 2025 23:36:26 +0100 Subject: [PATCH 0630/1144] =?UTF-8?q?feat(header):=20ajouter=20un=20filtre?= =?UTF-8?q?=20de=20flou=20personnalis=C3=A9=20pour=20le=20composant=20Papi?= =?UTF-8?q?llonModernHeader=20sur=20iOS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Global/PapillonModernHeader.tsx | 78 +++++++++++++++---- 1 file changed, 64 insertions(+), 14 deletions(-) diff --git a/src/components/Global/PapillonModernHeader.tsx b/src/components/Global/PapillonModernHeader.tsx index 9700d4f5e..c91d9a992 100644 --- a/src/components/Global/PapillonModernHeader.tsx +++ b/src/components/Global/PapillonModernHeader.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { StyleSheet, View } from "react-native"; +import { Dimensions, Platform, StyleSheet, View } from "react-native"; import Reanimated, { FadeIn, FadeOut, LinearTransition, ZoomIn, ZoomOut } from "react-native-reanimated"; import { animPapillon } from "@/utils/ui/animations"; @@ -8,7 +8,6 @@ import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { PressableScale } from "react-native-pressable-scale"; import { useTheme } from "@react-navigation/native"; import { BlurView } from "expo-blur"; -import { LinearGradient } from "expo-linear-gradient"; interface ModernHeaderProps { children: React.ReactNode, @@ -34,24 +33,75 @@ export const PapillonModernHeader: React.FC = (props) => { ); }; +import { CustomFilterView } from "react-native-ios-visual-effect-view"; +import { isExpoGo } from "@/utils/native/expoGoAlert"; + const LinearGradientModernHeader: React.FC = ({ children, outsideNav = false, height = 70, startLocation = 0.5, tint = null }) => { const theme = useTheme(); const insets = useSafeAreaInsets(); return ( <> - + + {Platform.OS === "ios" && !isExpoGo() ? ( + + ) : ( + + )} Date: Sat, 15 Feb 2025 23:38:52 +0100 Subject: [PATCH 0631/1144] =?UTF-8?q?fix(grades):=20ajuster=20les=20couleu?= =?UTF-8?q?rs=20des=20=C3=A9l=C3=A9ments=20en=20fonction=20du=20th=C3=A8me?= =?UTF-8?q?=20sombre=20pour=20une=20meilleure=20visibilit=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Grades/Atoms/GradesScodocUE.tsx | 6 +++--- src/views/account/Grades/Latest/LatestGradesItem.tsx | 2 +- src/views/account/Grades/Subject/SubjectTitle.tsx | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/views/account/Grades/Atoms/GradesScodocUE.tsx b/src/views/account/Grades/Atoms/GradesScodocUE.tsx index 7bdd7aa07..7f4c2b94a 100644 --- a/src/views/account/Grades/Atoms/GradesScodocUE.tsx +++ b/src/views/account/Grades/Atoms/GradesScodocUE.tsx @@ -145,7 +145,7 @@ const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: Prim alignItems: "center", justifyContent: "center", borderRadius: 8, - borderColor: adjustColor(ue.color, theme.dark ? 100 : -100) + "32", + borderColor: adjustColor(ue.color, theme.dark ? 180 : -100) + "32", borderWidth: 1, }} onPress={navigateToSubject} @@ -153,7 +153,7 @@ const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: Prim {ue.name} @@ -225,7 +225,7 @@ const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: Prim variant="body" numberOfLines={2} style={{ - color: adjustColor(ue.color, theme.dark ? 100 : -100) + color: adjustColor(ue.color, theme.dark ? 180 : -100) }} > {ue.titre} diff --git a/src/views/account/Grades/Latest/LatestGradesItem.tsx b/src/views/account/Grades/Latest/LatestGradesItem.tsx index aab056d21..5072d14dc 100644 --- a/src/views/account/Grades/Latest/LatestGradesItem.tsx +++ b/src/views/account/Grades/Latest/LatestGradesItem.tsx @@ -77,7 +77,7 @@ const GradesLatestItem: React.FC = ({ {typeof subject.average.average?.value === "number" ? subject.average.average.value.toFixed(2) : calculatedAverage !== -1 ? calculatedAverage.toFixed(2) : "N/A"} From 59f4f211b8d752264f663c4182ded33111e8c735 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sat, 15 Feb 2025 23:48:58 +0100 Subject: [PATCH 0632/1144] =?UTF-8?q?fix(colors):=20ajouter=20une=20gestio?= =?UTF-8?q?n=20des=20erreurs=20dans=20la=20fonction=20adjustColor=20pour?= =?UTF-8?q?=20=C3=A9viter=20les=20plantages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/ui/colors.ts | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/utils/ui/colors.ts b/src/utils/ui/colors.ts index 7daff32a6..cab7f6267 100644 --- a/src/utils/ui/colors.ts +++ b/src/utils/ui/colors.ts @@ -1,20 +1,25 @@ export function adjustColor (hex: string, amount: number): string { + try { // Remove the hash if it's there - hex = hex.replace(/^#/, ""); + hex = hex.replace(/^#/, ""); - // Parse the hex color into its RGB components - let bigint = parseInt(hex, 16); - let r = (bigint >> 16) & 255; - let g = (bigint >> 8) & 255; - let b = bigint & 255; + // Parse the hex color into its RGB components + let bigint = parseInt(hex, 16); + let r = (bigint >> 16) & 255; + let g = (bigint >> 8) & 255; + let b = bigint & 255; - // Adjust the RGB values by the specified amount - r = Math.max(0, Math.min(255, r + amount)); - g = Math.max(0, Math.min(255, g + amount)); - b = Math.max(0, Math.min(255, b + amount)); + // Adjust the RGB values by the specified amount + r = Math.max(0, Math.min(255, r + amount)); + g = Math.max(0, Math.min(255, g + amount)); + b = Math.max(0, Math.min(255, b + amount)); - // Convert the RGB values back to a hex string - let newHex = ((r << 16) + (g << 8) + b).toString(16).padStart(6, "0"); + // Convert the RGB values back to a hex string + let newHex = ((r << 16) + (g << 8) + b).toString(16).padStart(6, "0"); - return `#${newHex}`; + return `#${newHex}`; + } + catch (e) { + return hex; + } } \ No newline at end of file From 747aaa793aeefc178e30202eb2611838a1c65e6d Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sat, 15 Feb 2025 23:51:20 +0100 Subject: [PATCH 0633/1144] =?UTF-8?q?fix(header):=20ajouter=20une=20v?= =?UTF-8?q?=C3=A9rification=20de=20version=20pour=20le=20composant=20Linea?= =?UTF-8?q?rGradientModernHeader=20sur=20iOS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Global/PapillonModernHeader.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/Global/PapillonModernHeader.tsx b/src/components/Global/PapillonModernHeader.tsx index c91d9a992..58a05e413 100644 --- a/src/components/Global/PapillonModernHeader.tsx +++ b/src/components/Global/PapillonModernHeader.tsx @@ -35,15 +35,18 @@ export const PapillonModernHeader: React.FC = (props) => { import { CustomFilterView } from "react-native-ios-visual-effect-view"; import { isExpoGo } from "@/utils/native/expoGoAlert"; +import { LinearGradient } from "expo-linear-gradient"; const LinearGradientModernHeader: React.FC = ({ children, outsideNav = false, height = 70, startLocation = 0.5, tint = null }) => { const theme = useTheme(); const insets = useSafeAreaInsets(); + console.log(Platform); + return ( <> - {Platform.OS === "ios" && !isExpoGo() ? ( + {Platform.OS === "ios" && !isExpoGo() && parseInt(Platform.Version) >= 18 ? ( Date: Sat, 15 Feb 2025 23:52:27 +0100 Subject: [PATCH 0634/1144] =?UTF-8?q?fix(corner-radius):=20ajuster=20le=20?= =?UTF-8?q?rayon=20pour=20les=20mod=C3=A8les=20iPhone=2014=20et=2015?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/ui/corner-radius.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/ui/corner-radius.ts b/src/utils/ui/corner-radius.ts index 769ede184..0eb9612a4 100644 --- a/src/utils/ui/corner-radius.ts +++ b/src/utils/ui/corner-radius.ts @@ -25,7 +25,7 @@ const radiuses = [ }, { devices: "14 pro, 14 pro max, 15, 15 plus, 15 pro, 15 pro max", - radius: 55.0, + radius: 57.0, }, { devices: "16, 16 pro, 16 pro max, 16 plus", From 45e5d2b9724835b15b5b93c38849cd89e35f1836 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sat, 15 Feb 2025 23:55:45 +0100 Subject: [PATCH 0635/1144] fix(router): ajouter un rayon de coin pour la feuille de dialogue --- src/router/screens/views/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/router/screens/views/index.ts b/src/router/screens/views/index.ts index 907691451..16d96bb1c 100644 --- a/src/router/screens/views/index.ts +++ b/src/router/screens/views/index.ts @@ -98,6 +98,7 @@ export default [ gestureDirection: "vertical", animation: "slide_from_bottom", headerShown: Platform.OS !== "ios", + sheetCornerRadius: 24, }), createScreen("ChatCreate", ChatCreate, { headerTitle: "Créer une discussion", From 7a7ffe2386cf3625a6ca8c441fae873b9c40797b Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sat, 15 Feb 2025 23:58:24 +0100 Subject: [PATCH 0636/1144] =?UTF-8?q?fix(router):=20changer=20la=20pr?= =?UTF-8?q?=C3=A9sentation=20en=20formSheet=20et=20ajouter=20un=20rayon=20?= =?UTF-8?q?de=20coin=20de=2024?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/screens/account/home.tsx | 4 +++- src/router/screens/index.ts | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/router/screens/account/home.tsx b/src/router/screens/account/home.tsx index 828e802fa..1b6ee02d9 100644 --- a/src/router/screens/account/home.tsx +++ b/src/router/screens/account/home.tsx @@ -34,8 +34,10 @@ const HomeStackScreen = ({ accountScreens }: { tabData.options = { ...tabData.options, tabEnabled: tab.enabled, - presentation: "modal", + presentation: "formSheet", animation: Platform.OS === "android" ? "slide_from_right" : "default", + + sheetCornerRadius: 24, }; return tabData; diff --git a/src/router/screens/index.ts b/src/router/screens/index.ts index 89712277e..2ddc76835 100644 --- a/src/router/screens/index.ts +++ b/src/router/screens/index.ts @@ -14,9 +14,10 @@ export default [ createScreen("SettingStack", SettingsScreen, { headerShown: false, - presentation: "modal", + presentation: "formSheet", animation: Platform.OS === "android" ? "slide_from_right" : "default", - animationDuration: 100 + animationDuration: 100, + sheetCornerRadius: 24, }), createScreen("AccountStack", AccountScreen, { From f5fbdd10303583772198306386bca0f366b01dca Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sun, 16 Feb 2025 00:01:11 +0100 Subject: [PATCH 0637/1144] =?UTF-8?q?fix(news-item):=20corriger=20la=20con?= =?UTF-8?q?dition=20d'affichage=20pour=20le=20message=20=C3=A0=20reconna?= =?UTF-8?q?=C3=AEtre?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/News/Document.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/views/account/News/Document.tsx b/src/views/account/News/Document.tsx index ff15b0529..27a6c7c7d 100644 --- a/src/views/account/News/Document.tsx +++ b/src/views/account/News/Document.tsx @@ -155,7 +155,7 @@ const NewsItem: Screen<"NewsItem"> = ({ route, navigation }) => { }} > - {account.service === AccountService.Pronote && message.ref.needToAcknowledge && ( + {account.service === AccountService.Pronote && message.ref.needToAcknowledge ? ( = ({ route, navigation }) => { + ) : ( + )} From 148f9a034113198edb537ec6455b7e8fe1d18b5d Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sun, 16 Feb 2025 00:04:22 +0100 Subject: [PATCH 0638/1144] =?UTF-8?q?fix(format=5Fpronote=5Fnews):=20am?= =?UTF-8?q?=C3=A9liorer=20le=20traitement=20des=20nouvelles=20en=20pr?= =?UTF-8?q?=C3=A9servant=20les=20sauts=20de=20ligne?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/format/format_pronote_news.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/utils/format/format_pronote_news.ts b/src/utils/format/format_pronote_news.ts index 591f2b1fa..071901f81 100644 --- a/src/utils/format/format_pronote_news.ts +++ b/src/utils/format/format_pronote_news.ts @@ -1,8 +1,11 @@ import { convert as convertHTML } from "html-to-text"; function parse_news_resume (content: string): string { - const converted = convertHTML(content); - const formatted = converted.replace(/Bonjour,|Bonjour à tous|Bonjour !|Bonsoir|Bonjour|Bonjour à tous, |Bonjour , |Bonsoir, /g, "").replace(/\n/g, ""); + const converted = convertHTML(content, { + preserveNewlines: true, + whitespaceCharacters: " ", + }); + const formatted = converted.replace("\n", " ").replace(/Bonjour,|Bonjour à tous|Bonjour !|Bonsoir|Bonjour|Bonjour à tous, |Bonjour , |Bonsoir, /g, "").replace(/\n/g, ""); const trimmed = formatted.trim(); const decoma = (trimmed.startsWith(",") ? trimmed.slice(1) : trimmed).trim(); const uppercased = decoma.charAt(0).toUpperCase() + decoma.slice(1); From 0319330063a73a5cd153006f71a051891ed0aa7b Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sun, 16 Feb 2025 00:04:59 +0100 Subject: [PATCH 0639/1144] =?UTF-8?q?fix(header):=20ajuster=20la=20hauteur?= =?UTF-8?q?=20du=20header=20en=20r=C3=A9duisant=20de=2016=20pixels?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Global/PapillonModernHeader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Global/PapillonModernHeader.tsx b/src/components/Global/PapillonModernHeader.tsx index 58a05e413..a7b9bd3c2 100644 --- a/src/components/Global/PapillonModernHeader.tsx +++ b/src/components/Global/PapillonModernHeader.tsx @@ -83,7 +83,7 @@ const LinearGradientModernHeader: React.FC = ({ children, out startPointPreset: "topCenter", endPointPreset: "bottomCenter", size: { - height: outsideNav ? height : insets.top + height, + height: (outsideNav ? height : insets.top + height) - 16, width: Dimensions.get("window").width, }, } From 34c891c86546d5e028337b84ca2490d0db81bfe4 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sun, 16 Feb 2025 00:05:36 +0100 Subject: [PATCH 0640/1144] =?UTF-8?q?fix(header):=20r=C3=A9duire=20la=20ha?= =?UTF-8?q?uteur=20du=20header=20de=2016=20=C3=A0=2010=20pixels?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Global/PapillonModernHeader.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Global/PapillonModernHeader.tsx b/src/components/Global/PapillonModernHeader.tsx index a7b9bd3c2..c1c1100dd 100644 --- a/src/components/Global/PapillonModernHeader.tsx +++ b/src/components/Global/PapillonModernHeader.tsx @@ -55,7 +55,7 @@ const LinearGradientModernHeader: React.FC = ({ children, out left: 0, right: 0, width: Dimensions.get("window").width, - height: outsideNav ? height : insets.top + height, + height: (outsideNav ? height : insets.top + height) - 10, zIndex: 80, } ]} @@ -83,7 +83,7 @@ const LinearGradientModernHeader: React.FC = ({ children, out startPointPreset: "topCenter", endPointPreset: "bottomCenter", size: { - height: (outsideNav ? height : insets.top + height) - 16, + height: (outsideNav ? height : insets.top + height) - 10, width: Dimensions.get("window").width, }, } From 4ca9d76ce441eb420e447441e25c2b457d73657d Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 16 Feb 2025 00:17:21 +0100 Subject: [PATCH 0641/1144] fix(ts) in `PapillonPicker` --- src/components/Global/PapillonPicker.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Global/PapillonPicker.tsx b/src/components/Global/PapillonPicker.tsx index 6412e2b56..f06985802 100644 --- a/src/components/Global/PapillonPicker.tsx +++ b/src/components/Global/PapillonPicker.tsx @@ -69,7 +69,7 @@ const PapillonPicker: React.FC = ({ if (typeof item === "string") { handleSelectionChange(item); } else { - item.onPress(); + item.onPress?.(); } } }} From 4f847d1459df90edc37462ea1addaff1f0d25058 Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Sun, 16 Feb 2025 00:28:54 +0100 Subject: [PATCH 0642/1144] fix: configuration des identifiants des services incorrect --- src/views/account/Restaurant/Cards/StoreThemes.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views/account/Restaurant/Cards/StoreThemes.ts b/src/views/account/Restaurant/Cards/StoreThemes.ts index 44163e0c6..42d3fc5bd 100644 --- a/src/views/account/Restaurant/Cards/StoreThemes.ts +++ b/src/views/account/Restaurant/Cards/StoreThemes.ts @@ -21,7 +21,7 @@ export const STORE_THEMES = [ background: require("../../../../../assets/images/cards/Carte_Cover_Unknown.png"), }, { - id: "izly", + id: "Izly", name: "Izly by Crous", colors: { text: "#FFFFFF", @@ -31,7 +31,7 @@ export const STORE_THEMES = [ background: require("../../../../../assets/images/cards/Carte_Cover_Izly.png"), }, { - id: "turboself", + id: "Turboself", name: "TurboSelf", colors: { text: "#FFFFFF", @@ -42,7 +42,7 @@ export const STORE_THEMES = [ }, { id: "ARD", - name: "Ard", + name: "ARD", colors: { text: "#FFFFFF", background: "#295888", From 87e12915850a05decc9e76ce5809061e323f055b Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Sun, 16 Feb 2025 00:29:09 +0100 Subject: [PATCH 0643/1144] fix: affiche le solde restauration en premier --- src/services/ard/balance.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/ard/balance.ts b/src/services/ard/balance.ts index a2d3e15f6..31e2ff436 100644 --- a/src/services/ard/balance.ts +++ b/src/services/ard/balance.ts @@ -12,6 +12,6 @@ export const balance = async (account: ARDAccount): Promise => { currency: "€", remaining: wallet.walletName.toLowerCase() !== "cafetaria" ? Math.floor((wallet.walletAmount / mealPrice!)) : null, label: wallet.walletName[0].toUpperCase() + wallet.walletName.slice(1).toLowerCase() - })); + })).reverse(); }; \ No newline at end of file From 8d8d9fd6ce1503819f4844c06a2b6f7c31b42cb0 Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Sun, 16 Feb 2025 00:29:50 +0100 Subject: [PATCH 0644/1144] feat: affichage de tout les soldes --- src/views/account/Restaurant/Cards/Card.tsx | 30 ++++++++++++--------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/views/account/Restaurant/Cards/Card.tsx b/src/views/account/Restaurant/Cards/Card.tsx index 51e20782d..4fc9f5366 100644 --- a/src/views/account/Restaurant/Cards/Card.tsx +++ b/src/views/account/Restaurant/Cards/Card.tsx @@ -39,17 +39,20 @@ const MenuCard = ({ card, onPress }: { card: ServiceCard, onPress?: () => void } > {card?.theme?.name} - - {card.balance[0] && card.balance[0].amount && ( - - - Solde - - - {card.balance[0] ? card.balance[0].amount.toFixed(2) + " €" : "---"} - - - )} + + {card.balance?.map((balance, index) => + balance.amount && ( + + + {balance.label} + + + {balance.amount.toFixed(2) + " €"} + + + ) + )} + {card.identifier && ( @@ -107,10 +110,13 @@ const styles = StyleSheet.create({ width: "100%", padding: 10, flexDirection: "row", - alignItems: "center", gap: 10, }, + cardBalances: { + gap: 5 + }, + cardHeaderName: { fontSize: 16, fontFamily: "semibold", From 1d67c89497bbc17dc5267955feb1811209ae6579 Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Sun, 16 Feb 2025 01:00:58 +0100 Subject: [PATCH 0645/1144] fix: affichage correct de la note quand on appuie dessus --- src/views/account/Grades/Document.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Grades/Document.tsx b/src/views/account/Grades/Document.tsx index 2a6eaec4f..52f7ec225 100644 --- a/src/views/account/Grades/Document.tsx +++ b/src/views/account/Grades/Document.tsx @@ -434,7 +434,7 @@ const GradeDocument: Screen<"GradeDocument"> = ({ route, navigation }) => { }} numberOfLines={1} > - {grade.student.disabled ? "N. not" : grade.student.value?.toFixed(2)} + {grade.student.disabled ? grade.student.status : grade.student.value?.toFixed(2)} Date: Sun, 16 Feb 2025 18:39:12 +0100 Subject: [PATCH 0646/1144] =?UTF-8?q?fix(dependencies):=20mettre=20=C3=A0?= =?UTF-8?q?=20jour=20react-native-screens=20=C3=A0=20la=20version=204.6.0?= =?UTF-8?q?=20et=20ajuster=20les=20r=C3=A9f=C3=A9rences=20de=20produit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Papillon.xcodeproj/project.pbxproj | 4 ++-- ios/Podfile.lock | 4 ++-- package-lock.json | 8 ++++---- package.json | 2 +- src/components/Global/PapillonModernHeader.tsx | 2 +- src/router/screens/views/index.ts | 3 +++ 6 files changed, 13 insertions(+), 10 deletions(-) diff --git a/ios/Papillon.xcodeproj/project.pbxproj b/ios/Papillon.xcodeproj/project.pbxproj index 98dafc58d..fcefae4c9 100644 --- a/ios/Papillon.xcodeproj/project.pbxproj +++ b/ios/Papillon.xcodeproj/project.pbxproj @@ -453,7 +453,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = "Papillon"; + PRODUCT_NAME = Papillon; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -486,7 +486,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = "Papillon"; + PRODUCT_NAME = Papillon; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/ios/Podfile.lock b/ios/Podfile.lock index b2bb491fd..1a5a9ea21 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1600,7 +1600,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNScreens (3.31.1): + - RNScreens (4.6.0): - DoubleConversion - glog - hermes-engine @@ -2109,7 +2109,7 @@ SPEC CHECKSUMS: RNGestureHandler: f7abf21d594742be28a3a72528069225a3187e26 RNNotifee: 935f3ea8c134c88cbf8b13ea0c97c72c09ad2116 RNReanimated: 35f9ac9c3ac42d0497ebd1cce5c39d7687a8493e - RNScreens: b32a9ff15bea7fcdbe5dff6477bc503f792b1208 + RNScreens: 0063befb42dd85ae53a106b5d3529f2e5cd63643 RNShare: 22717e910836a66cb7255b3b8c4ab06cbe346e27 RNSVG: 8b1a777d54096b8c2a0fd38fc9d5a454332bbb4d SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d diff --git a/package-lock.json b/package-lock.json index 52ca04261..501f83672 100644 --- a/package-lock.json +++ b/package-lock.json @@ -93,7 +93,7 @@ "react-native-qrcode-svg": "^6.3.1", "react-native-reanimated": "~3.10.1", "react-native-safe-area-context": "4.10.5", - "react-native-screens": "3.31.1", + "react-native-screens": "^4.6.0", "react-native-share": "^12.0.3", "react-native-svg": "^15.2.0", "react-native-url-polyfill": "^2.0.0", @@ -14995,9 +14995,9 @@ } }, "node_modules/react-native-screens": { - "version": "3.31.1", - "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-3.31.1.tgz", - "integrity": "sha512-8fRW362pfZ9y4rS8KY5P3DFScrmwo/vu1RrRMMx0PNHbeC9TLq0Kw1ubD83591yz64gLNHFLTVkTJmWeWCXKtQ==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.6.0.tgz", + "integrity": "sha512-PqGtR/moJLiTMSavhfo5spKXNHZrlxffq3g5UUVPmyuu7MmazFlPvYqiAYnR2iB9tkJYgvZO6sbjYAE7619M0A==", "dependencies": { "react-freeze": "^1.0.0", "warn-once": "^0.1.0" diff --git a/package.json b/package.json index 39fda8125..264ee5005 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "react-native-qrcode-svg": "^6.3.1", "react-native-reanimated": "~3.10.1", "react-native-safe-area-context": "4.10.5", - "react-native-screens": "3.31.1", + "react-native-screens": "^4.6.0", "react-native-share": "^12.0.3", "react-native-svg": "^15.2.0", "react-native-url-polyfill": "^2.0.0", diff --git a/src/components/Global/PapillonModernHeader.tsx b/src/components/Global/PapillonModernHeader.tsx index c1c1100dd..26dc18f9d 100644 --- a/src/components/Global/PapillonModernHeader.tsx +++ b/src/components/Global/PapillonModernHeader.tsx @@ -158,7 +158,7 @@ const NativeModernHeader: React.FC = ({ children, outsideNav justifyContent: "space-between", alignItems: "center", gap: 8, - backgroundColor: tint ? tint : theme.colors.text + "10", + backgroundColor: tint ? tint : theme.colors.card + "10", borderBottomColor: theme.colors.border, borderBottomWidth: 0.5, }]} diff --git a/src/router/screens/views/index.ts b/src/router/screens/views/index.ts index 16d96bb1c..19519a673 100644 --- a/src/router/screens/views/index.ts +++ b/src/router/screens/views/index.ts @@ -78,6 +78,7 @@ export default [ gestureDirection: "vertical", animation: "slide_from_bottom", headerShown: false, + sheetCornerRadius: 16, }), createScreen("HomeworksDocument", HomeworksDocument, { headerTitle: "Devoir", @@ -85,12 +86,14 @@ export default [ gestureDirection: "vertical", animation: "slide_from_bottom", headerShown: false, + sheetCornerRadius: 16, }), createScreen("GradeSubject", GradeSubjectScreen, { headerTitle: "Détail de la matière", presentation: "formSheet", gestureDirection: "vertical", animation: "slide_from_bottom", + sheetCornerRadius: 16, }), createScreen("GradeDocument", GradeDocument, { headerTitle: "Détail de la note", From 2d16d5f112e736b4b7dbed071356d919883b9dd0 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sun, 16 Feb 2025 19:44:55 +0100 Subject: [PATCH 0647/1144] =?UTF-8?q?fix(router):=20d=C3=A9sactiver=20l'an?= =?UTF-8?q?imation=20de=20transition=20pour=20iOS=20et=20corriger=20le=20n?= =?UTF-8?q?om=20du=20produit=20dans=20le=20projet=20Xcode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Papillon.xcodeproj/project.pbxproj | 4 +- .../AppIcon.appiconset/Contents.json | 42 +- src/router/screens/index.ts | 2 +- src/views/welcome/AccountSelector.old.tsx | 476 ++++++++++++++++++ src/views/welcome/AccountSelector.tsx | 469 +---------------- 5 files changed, 510 insertions(+), 483 deletions(-) create mode 100644 src/views/welcome/AccountSelector.old.tsx diff --git a/ios/Papillon.xcodeproj/project.pbxproj b/ios/Papillon.xcodeproj/project.pbxproj index fcefae4c9..98dafc58d 100644 --- a/ios/Papillon.xcodeproj/project.pbxproj +++ b/ios/Papillon.xcodeproj/project.pbxproj @@ -453,7 +453,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = Papillon; + PRODUCT_NAME = "Papillon"; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -486,7 +486,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = Papillon; + PRODUCT_NAME = "Papillon"; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json index e91cbd33d..90d8d4c2a 100644 --- a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,38 +1,14 @@ { - "images" : [ + "images": [ { - "filename" : "Icon-Light-1024x1024.png", - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "Icon-Dark-1024x1024.png", - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "tinted" - } - ], - "filename" : "Icon-Tinted-1024x1024.png", - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" + "filename": "App-Icon-1024x1024@1x.png", + "idiom": "universal", + "platform": "ios", + "size": "1024x1024" } ], - "info" : { - "author" : "xcode", - "version" : 1 + "info": { + "version": 1, + "author": "expo" } -} +} \ No newline at end of file diff --git a/src/router/screens/index.ts b/src/router/screens/index.ts index 2ddc76835..ffd0957b7 100644 --- a/src/router/screens/index.ts +++ b/src/router/screens/index.ts @@ -23,7 +23,7 @@ export default [ createScreen("AccountStack", AccountScreen, { headerShown: false, gestureEnabled: false, - animation: Platform.OS === "android" ? "slide_from_right" : "default", + animation: Platform.OS === "android" ? "slide_from_right" : "none", animationDuration: 100, }), ] as const; diff --git a/src/views/welcome/AccountSelector.old.tsx b/src/views/welcome/AccountSelector.old.tsx new file mode 100644 index 000000000..56da2988d --- /dev/null +++ b/src/views/welcome/AccountSelector.old.tsx @@ -0,0 +1,476 @@ +import {NativeItem, NativeList, NativeListHeader, NativeText} from "@/components/Global/NativeComponents"; +import {useAccounts, useCurrentAccount} from "@/stores/account"; +import {defaultProfilePicture} from "@/utils/ui/default-profile-picture"; +import {useIsFocused, useTheme} from "@react-navigation/native"; +import {PlusIcon} from "lucide-react-native"; +import {useEffect, useState} from "react"; +import { + Alert, + Dimensions, + Image, + RefreshControl, + StatusBar, + Text, + TouchableHighlight, + View +} from "react-native"; +import {useSafeAreaInsets} from "react-native-safe-area-context"; +import * as SplashScreen from "expo-splash-screen"; + +import PapillonAvatar from "@/components/Global/PapillonAvatar"; + +import PackageJSON from "@/../package.json"; + +import Reanimated, { + Extrapolation, + FadeInDown, + FadeOut, + interpolate, + LinearTransition, + useAnimatedRef, useAnimatedStyle, + useScrollViewOffset, + ZoomIn, +} from "react-native-reanimated"; +import {LinearGradient} from "expo-linear-gradient"; +import {animPapillon} from "@/utils/ui/animations"; +import {Screen} from "@/router/helpers/types"; +import PapillonSpinner from "@/components/Global/PapillonSpinner"; +import {PressableScale} from "react-native-pressable-scale"; + +import datasets from "@/consts/datasets.json"; +import Animated from "react-native-reanimated"; +import {PrimaryAccount} from "@/stores/account/types"; + + +// https://raw.githubusercontent.com/PapillonApp/datasets/refs/heads/main/illustrations/index.json +type Illustration = { + name: string + image: string +}; + +const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { + const theme = useTheme(); + const insets = useSafeAreaInsets(); + + const isFocused = useIsFocused(); + + const currentAccount = useCurrentAccount((store) => store.account); + const switchTo = useCurrentAccount((store) => store.switchTo); + const removeAccount = useAccounts((store) => store.remove); + + const accounts = useAccounts((store) => store.accounts); + + const [loading, setLoading] = useState(null); + + const [downloadedIllustrations, setDownloadedIllustrations] = useState(false); + const [illustration, setIllustration] = useState(undefined); + const [illustrationLoaded, setIllustrationLoaded] = useState(false); + + const scrollRef = useAnimatedRef(); + const scrollOffset = useScrollViewOffset(scrollRef); + const headerRatioHeight = 250; + let headerAnimatedStyle = useAnimatedStyle(() => ({ + top: interpolate( + scrollOffset.value, + [headerRatioHeight - 1000, 0, headerRatioHeight - (insets.top + 64), headerRatioHeight + 1000], + [headerRatioHeight - 1000, 0, 0, headerRatioHeight + 1000 - (insets.top + 64)], + Extrapolation.CLAMP + ), + })); + let headerOpacity = useAnimatedStyle(() => ({ + opacity: interpolate(scrollOffset.value, [0, 100], [0, 0.75], Extrapolation.CLAMP), + })); + + useEffect(() => { + if(!downloadedIllustrations) { + updateIllustration(); + } + }, []); + + const updateIllustration = async () => { + fetch(datasets["illustrations"]) + .then((response) => response.json()) + .then((data) => { + setDownloadedIllustrations(true); + // select a random illustration + setIllustration(data[Math.floor(Math.random() * data.length)]); + }); + }; + + useEffect(() => { + void async function () { + if (!useAccounts.persist.hasHydrated()) return; + + // If there are no accounts, redirect the user to the first installation page. + if (accounts.filter((account) => !account.isExternal).length === 0) { + // Use the `reset` method to clear the navigation stack. + navigation.reset({ + index: 0, + routes: [{ name: "FirstInstallation" }], + }); + } + + if (accounts.filter((account) => !account.isExternal).length === 1) { + const selectedAccount = accounts.find((account) => !account.isExternal) as PrimaryAccount | undefined; + if (selectedAccount && currentAccount?.localID !== selectedAccount.localID) { + switchTo(selectedAccount); + + navigation.reset({ + index: 0, + routes: [{ name: "AccountStack" }], + }); + } + else { + SplashScreen.hideAsync(); + } + } + else { + SplashScreen.hideAsync(); + } + }(); + }, [accounts]); + + if (!accounts) return null; + + return ( + + + + navigation.navigate("ServiceSelector")} + > + + + + Ajouter un compte + + + + navigation.navigate("DevMenu")} + delayLongPress={2000} + > + + ver. {PackageJSON.version} + + + + + { + updateIllustration(); + }} + progressViewOffset={headerRatioHeight} + /> + } + contentContainerStyle={{ + minHeight: Dimensions.get("window").height, + }} + > + {isFocused && ( + + )} + + + {!illustrationLoaded && + + } + + setIllustrationLoaded(true)} + /> + + + + + + + Bienvenue sur Papillon ! + + + + Sélectionne un compte pour commencer. + + + + + {accounts.filter((account) => !account.isExternal).length > 0 && ( + + + + {accounts.map((account, index) => { + return !account.isExternal && ( + + } + /> + } + trailing={ + loading === account.localID && ( + + ) + } + onLongPress={async () => { + // delete account + Alert.alert( + "Supprimer le compte", + "Es-tu sûr de vouloir supprimer ce compte ?", + [ + { + text: "Annuler", + style: "cancel", + }, + { + text: "Supprimer", + style: "destructive", + onPress: () => { + Alert.alert( + "Es-tu sûr ?", + "Veux-tu supprimer définitivement " + account.studentName.first + " " + account.studentName.last + " ?", + [ + { + text: "Annuler", + style: "cancel", + }, + { + text: "Supprimer", + style: "destructive", + onPress: () => { + removeAccount(account.localID); + }, + }, + ] + ); + }, + }, + ] + ); + }} + onPress={async () => { + if (currentAccount?.localID !== account.localID) { + setLoading(account.localID); + await switchTo(account); + setLoading(null); + } + + navigation.reset({ + index: 0, + routes: [{ name: "AccountStack" }], + }); + }} + > + + + {account.studentName.first} {account.studentName.last} + + + {account.schoolName ? + account.schoolName : + account.identityProvider ? + account.identityProvider.name : + "Compte local" + } + + + + ); + })} + + + )} + + + + + ); +}; + +export default AccountSelector; diff --git a/src/views/welcome/AccountSelector.tsx b/src/views/welcome/AccountSelector.tsx index 56da2988d..2d4a92bd5 100644 --- a/src/views/welcome/AccountSelector.tsx +++ b/src/views/welcome/AccountSelector.tsx @@ -1,101 +1,15 @@ -import {NativeItem, NativeList, NativeListHeader, NativeText} from "@/components/Global/NativeComponents"; -import {useAccounts, useCurrentAccount} from "@/stores/account"; -import {defaultProfilePicture} from "@/utils/ui/default-profile-picture"; -import {useIsFocused, useTheme} from "@react-navigation/native"; -import {PlusIcon} from "lucide-react-native"; -import {useEffect, useState} from "react"; -import { - Alert, - Dimensions, - Image, - RefreshControl, - StatusBar, - Text, - TouchableHighlight, - View -} from "react-native"; -import {useSafeAreaInsets} from "react-native-safe-area-context"; -import * as SplashScreen from "expo-splash-screen"; - -import PapillonAvatar from "@/components/Global/PapillonAvatar"; - -import PackageJSON from "@/../package.json"; - -import Reanimated, { - Extrapolation, - FadeInDown, - FadeOut, - interpolate, - LinearTransition, - useAnimatedRef, useAnimatedStyle, - useScrollViewOffset, - ZoomIn, -} from "react-native-reanimated"; -import {LinearGradient} from "expo-linear-gradient"; -import {animPapillon} from "@/utils/ui/animations"; -import {Screen} from "@/router/helpers/types"; -import PapillonSpinner from "@/components/Global/PapillonSpinner"; -import {PressableScale} from "react-native-pressable-scale"; - -import datasets from "@/consts/datasets.json"; -import Animated from "react-native-reanimated"; -import {PrimaryAccount} from "@/stores/account/types"; +import { Screen } from "@/router/helpers/types"; +import { useAccounts, useCurrentAccount } from "@/stores/account"; +import React, { useEffect } from "react"; +import { Image, useColorScheme } from "react-native"; - -// https://raw.githubusercontent.com/PapillonApp/datasets/refs/heads/main/illustrations/index.json -type Illustration = { - name: string - image: string -}; +import * as SplashScreen from "expo-splash-screen"; const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { - const theme = useTheme(); - const insets = useSafeAreaInsets(); - - const isFocused = useIsFocused(); - + const colorScheme = useColorScheme(); + const accounts = useAccounts((store) => store.accounts); const currentAccount = useCurrentAccount((store) => store.account); const switchTo = useCurrentAccount((store) => store.switchTo); - const removeAccount = useAccounts((store) => store.remove); - - const accounts = useAccounts((store) => store.accounts); - - const [loading, setLoading] = useState(null); - - const [downloadedIllustrations, setDownloadedIllustrations] = useState(false); - const [illustration, setIllustration] = useState(undefined); - const [illustrationLoaded, setIllustrationLoaded] = useState(false); - - const scrollRef = useAnimatedRef(); - const scrollOffset = useScrollViewOffset(scrollRef); - const headerRatioHeight = 250; - let headerAnimatedStyle = useAnimatedStyle(() => ({ - top: interpolate( - scrollOffset.value, - [headerRatioHeight - 1000, 0, headerRatioHeight - (insets.top + 64), headerRatioHeight + 1000], - [headerRatioHeight - 1000, 0, 0, headerRatioHeight + 1000 - (insets.top + 64)], - Extrapolation.CLAMP - ), - })); - let headerOpacity = useAnimatedStyle(() => ({ - opacity: interpolate(scrollOffset.value, [0, 100], [0, 0.75], Extrapolation.CLAMP), - })); - - useEffect(() => { - if(!downloadedIllustrations) { - updateIllustration(); - } - }, []); - - const updateIllustration = async () => { - fetch(datasets["illustrations"]) - .then((response) => response.json()) - .then((data) => { - setDownloadedIllustrations(true); - // select a random illustration - setIllustration(data[Math.floor(Math.random() * data.length)]); - }); - }; useEffect(() => { void async function () { @@ -108,369 +22,30 @@ const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { index: 0, routes: [{ name: "FirstInstallation" }], }); + + SplashScreen.hideAsync(); } - if (accounts.filter((account) => !account.isExternal).length === 1) { - const selectedAccount = accounts.find((account) => !account.isExternal) as PrimaryAccount | undefined; - if (selectedAccount && currentAccount?.localID !== selectedAccount.localID) { - switchTo(selectedAccount); + const selectedAccount = accounts.find((account) => !account.isExternal) as PrimaryAccount | undefined; + if (selectedAccount && currentAccount?.localID !== selectedAccount.localID) { + switchTo(selectedAccount); + + navigation.reset({ + index: 0, + routes: [{ name: "AccountStack" }], + }); - navigation.reset({ - index: 0, - routes: [{ name: "AccountStack" }], - }); - } - else { - SplashScreen.hideAsync(); - } - } - else { SplashScreen.hideAsync(); } }(); }, [accounts]); - if (!accounts) return null; - return ( - - - - navigation.navigate("ServiceSelector")} - > - - - - Ajouter un compte - - - - navigation.navigate("DevMenu")} - delayLongPress={2000} - > - - ver. {PackageJSON.version} - - - - - { - updateIllustration(); - }} - progressViewOffset={headerRatioHeight} - /> - } - contentContainerStyle={{ - minHeight: Dimensions.get("window").height, - }} - > - {isFocused && ( - - )} - - - {!illustrationLoaded && - - } - - setIllustrationLoaded(true)} - /> - - - - - - - Bienvenue sur Papillon ! - - - - Sélectionne un compte pour commencer. - - - - - {accounts.filter((account) => !account.isExternal).length > 0 && ( - - - - {accounts.map((account, index) => { - return !account.isExternal && ( - - } - /> - } - trailing={ - loading === account.localID && ( - - ) - } - onLongPress={async () => { - // delete account - Alert.alert( - "Supprimer le compte", - "Es-tu sûr de vouloir supprimer ce compte ?", - [ - { - text: "Annuler", - style: "cancel", - }, - { - text: "Supprimer", - style: "destructive", - onPress: () => { - Alert.alert( - "Es-tu sûr ?", - "Veux-tu supprimer définitivement " + account.studentName.first + " " + account.studentName.last + " ?", - [ - { - text: "Annuler", - style: "cancel", - }, - { - text: "Supprimer", - style: "destructive", - onPress: () => { - removeAccount(account.localID); - }, - }, - ] - ); - }, - }, - ] - ); - }} - onPress={async () => { - if (currentAccount?.localID !== account.localID) { - setLoading(account.localID); - await switchTo(account); - setLoading(null); - } - - navigation.reset({ - index: 0, - routes: [{ name: "AccountStack" }], - }); - }} - > - - - {account.studentName.first} {account.studentName.last} - - - {account.schoolName ? - account.schoolName : - account.identityProvider ? - account.identityProvider.name : - "Compte local" - } - - - - ); - })} - - - )} - - - - + ); }; -export default AccountSelector; +export default AccountSelector; \ No newline at end of file From 5ef4610167357b5f1e2157078c8cead1bcc8a17f Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sun, 16 Feb 2025 20:10:28 +0100 Subject: [PATCH 0648/1144] =?UTF-8?q?fix(icon):=20mettre=20=C3=A0=20jour?= =?UTF-8?q?=20les=20ic=C3=B4nes=20d'application=20avec=20des=20variantes?= =?UTF-8?q?=20pour=20luminosit=C3=A9=20et=20ajuster=20le=20comportement=20?= =?UTF-8?q?de=20l'=C3=A9cran=20de=20d=C3=A9marrage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AppIcon.appiconset/Contents.json | 42 +++++++++++++++---- src/router/screens/account/home.tsx | 7 ++++ src/views/welcome/AccountSelector.old.tsx | 32 +++++++------- src/views/welcome/AccountSelector.tsx | 28 +++++++------ 4 files changed, 72 insertions(+), 37 deletions(-) diff --git a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json index 90d8d4c2a..e91cbd33d 100644 --- a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,14 +1,38 @@ { - "images": [ + "images" : [ { - "filename": "App-Icon-1024x1024@1x.png", - "idiom": "universal", - "platform": "ios", - "size": "1024x1024" + "filename" : "Icon-Light-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Dark-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "Icon-Tinted-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" } ], - "info": { - "version": 1, - "author": "expo" + "info" : { + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/src/router/screens/account/home.tsx b/src/router/screens/account/home.tsx index 1b6ee02d9..46b042f52 100644 --- a/src/router/screens/account/home.tsx +++ b/src/router/screens/account/home.tsx @@ -12,11 +12,14 @@ export const screenOptions: NativeStackNavigationOptions = { headerBackTitle: "Retour", }; +import * as SplashScreen from "expo-splash-screen"; + import { useCurrentAccount } from "@/stores/account"; import createScreen from "@/router/helpers/create-screen"; import Home from "@/views/account/Home/Home"; import type { RouteParameters } from "@/router/helpers/types"; import { Platform } from "react-native"; +import { useEffect } from "react"; const HomeStackScreen = ({ accountScreens }: { accountScreens: Array> @@ -24,6 +27,10 @@ const HomeStackScreen = ({ accountScreens }: { const account = useCurrentAccount(store => store.account); let newAccountScreens = accountScreens; + useEffect(() => { + SplashScreen.hideAsync(); + }, []); + if (account?.personalization.tabs) { let newTabs = account.personalization.tabs; newTabs = newTabs.filter(tab => !tab.enabled); diff --git a/src/views/welcome/AccountSelector.old.tsx b/src/views/welcome/AccountSelector.old.tsx index 56da2988d..217b5e13c 100644 --- a/src/views/welcome/AccountSelector.old.tsx +++ b/src/views/welcome/AccountSelector.old.tsx @@ -58,6 +58,7 @@ const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { const switchTo = useCurrentAccount((store) => store.switchTo); const removeAccount = useAccounts((store) => store.remove); + const lastOpenedAccountID = useAccounts((store) => store.lastOpenedAccountID); const accounts = useAccounts((store) => store.accounts); const [loading, setLoading] = useState(null); @@ -109,27 +110,26 @@ const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { routes: [{ name: "FirstInstallation" }], }); } - - if (accounts.filter((account) => !account.isExternal).length === 1) { - const selectedAccount = accounts.find((account) => !account.isExternal) as PrimaryAccount | undefined; - if (selectedAccount && currentAccount?.localID !== selectedAccount.localID) { - switchTo(selectedAccount); - - navigation.reset({ - index: 0, - routes: [{ name: "AccountStack" }], - }); - } - else { - SplashScreen.hideAsync(); - } - } else { - SplashScreen.hideAsync(); + const selectedAccount = + accounts.find((account) => account.localID === lastOpenedAccountID) as PrimaryAccount + ?? accounts.find((account) => !account.isExternal) as PrimaryAccount; + switchTo(selectedAccount); } }(); }, [accounts]); + useEffect(() => { + if (currentAccount && currentAccount?.localID) { + navigation.reset({ + index: 0, + routes: [{ name: "AccountStack" }], + }); + + SplashScreen.hideAsync(); + } + }, [currentAccount]); + if (!accounts) return null; return ( diff --git a/src/views/welcome/AccountSelector.tsx b/src/views/welcome/AccountSelector.tsx index 2d4a92bd5..29010a624 100644 --- a/src/views/welcome/AccountSelector.tsx +++ b/src/views/welcome/AccountSelector.tsx @@ -10,6 +10,7 @@ const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { const accounts = useAccounts((store) => store.accounts); const currentAccount = useCurrentAccount((store) => store.account); const switchTo = useCurrentAccount((store) => store.switchTo); + const lastOpenedAccountID = useAccounts((store) => store.lastOpenedAccountID); useEffect(() => { void async function () { @@ -26,24 +27,27 @@ const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { SplashScreen.hideAsync(); } - const selectedAccount = accounts.find((account) => !account.isExternal) as PrimaryAccount | undefined; - if (selectedAccount && currentAccount?.localID !== selectedAccount.localID) { - switchTo(selectedAccount); - - navigation.reset({ - index: 0, - routes: [{ name: "AccountStack" }], - }); - - SplashScreen.hideAsync(); - } + const selectedAccount = + accounts.find((account) => account.localID === lastOpenedAccountID) as PrimaryAccount + ?? accounts.find((account) => !account.isExternal) as PrimaryAccount; + switchTo(selectedAccount); }(); }, [accounts]); + useEffect(() => { + if (currentAccount && currentAccount?.localID) { + navigation.reset({ + index: 0, + routes: [{ name: "AccountStack" }], + }); + } + }, [currentAccount]); + return ( ); }; From bcf1e72e04b4dc24502ae39276674af332526c77 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sun, 16 Feb 2025 20:17:27 +0100 Subject: [PATCH 0649/1144] =?UTF-8?q?fix(grades):=20remplacer=20l'indicate?= =?UTF-8?q?ur=20d'activit=C3=A9=20par=20un=20spinner=20personnalis=C3=A9?= =?UTF-8?q?=20lors=20du=20chargement=20des=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Grades/Grades.tsx | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/views/account/Grades/Grades.tsx b/src/views/account/Grades/Grades.tsx index 5d80bed0f..620bc05d3 100644 --- a/src/views/account/Grades/Grades.tsx +++ b/src/views/account/Grades/Grades.tsx @@ -19,7 +19,6 @@ import { ChevronDown } from "lucide-react-native"; import React from "react"; import { lazy, Suspense, useEffect, useMemo, useRef, useState } from "react"; import { - ActivityIndicator, Platform, RefreshControl, ScrollView, @@ -35,6 +34,7 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import GradesScodocUE from "./Atoms/GradesScodocUE"; import {hasFeatureAccountSetup} from "@/utils/multiservice"; import {MultiServiceFeature} from "@/stores/multiService/types"; +import PapillonSpinner from "@/components/Global/PapillonSpinner"; const GradesAverageGraph = lazy(() => import("./Graph/GradesAverage")); const GradesLatestList = lazy(() => import("./Latest/LatestGrades")); @@ -195,7 +195,21 @@ const Grades: Screen<"Grades"> = ({ route, navigation }) => { }} scrollIndicatorInsets={{ top: outsideNav ? 64 : insets.top + 16 }} > - }> + + + + }> Date: Sun, 16 Feb 2025 20:28:31 +0100 Subject: [PATCH 0650/1144] fix(NativeComponents): optimiser le style de la liste avec useMemo et simplifier la gestion des enfants --- src/components/Global/NativeComponents.tsx | 54 +++++++++++----------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/src/components/Global/NativeComponents.tsx b/src/components/Global/NativeComponents.tsx index 1ab91d452..29bfb4592 100644 --- a/src/components/Global/NativeComponents.tsx +++ b/src/components/Global/NativeComponents.tsx @@ -1,4 +1,4 @@ -import React, { type ReactNode, isValidElement, Children } from "react"; +import React, { type ReactNode, isValidElement, Children, useMemo } from "react"; import { View, Text, Pressable, StyleSheet, type StyleProp, type ViewStyle, type TextStyle, Platform, TouchableNativeFeedback } from "react-native"; import Reanimated, { type AnimatedProps, LayoutAnimation, LinearTransition } from "react-native-reanimated"; import { useTheme } from "@react-navigation/native"; @@ -35,7 +35,24 @@ export const NativeList: React.FC = ({ const theme = useTheme(); const { colors } = theme; - const childrenWithProps = Children.map(children, (child, index) => { + const listStyle = useMemo(() => [ + list_styles.list, + { + borderWidth: 0.5, + borderColor: colors.border, + backgroundColor: colors.card, + shadowColor: "black", + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.1, + shadowRadius: 2, + overflow: "visible", + elevation: 1, + }, + inline && { marginTop: 16 }, + style, + ], [colors, inline, style]); + + const childrenWithProps = useMemo(() => Children.map(children, (child, index) => { if (!isValidElement(child)) return null; const newChild = child && React.cloneElement(child as React.ReactElement, { @@ -51,26 +68,11 @@ export const NativeList: React.FC = ({ {newChild} ); - }); + }), [children, animated, layout]); return ( = ({ icon, label, const theme = useTheme(); const { colors } = theme; - let newIcon = null; - - if (icon) { - newIcon = React.cloneElement(icon as React.ReactElement, { - size: 20, - strokeWidth: 2.2, - color: colors.text, - }); - } + let newIcon = useMemo(() => icon && React.cloneElement(icon as React.ReactElement, { + size: 20, + strokeWidth: 2.2, + color: colors.text, + }), [icon]); return ( Date: Sun, 16 Feb 2025 20:38:36 +0100 Subject: [PATCH 0651/1144] =?UTF-8?q?fix(Menu):=20ajouter=20une=20fonction?= =?UTF-8?q?=20de=20formatage=20pour=20l'identifiant=20de=20carte=20et=20l'?= =?UTF-8?q?utiliser=20dans=20les=20composants=20associ=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Restaurant/Cards/Card.tsx | 4 ++-- src/views/account/Restaurant/Menu.tsx | 12 +++++++++++- src/views/account/Restaurant/Modals/CardDetail.tsx | 11 +++-------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/views/account/Restaurant/Cards/Card.tsx b/src/views/account/Restaurant/Cards/Card.tsx index 51e20782d..7f43fc0b5 100644 --- a/src/views/account/Restaurant/Cards/Card.tsx +++ b/src/views/account/Restaurant/Cards/Card.tsx @@ -1,5 +1,5 @@ import { View, Text, StyleSheet, Image } from "react-native"; -import { ServiceCard } from "../Menu"; +import { formatCardIdentifier, ServiceCard } from "../Menu"; import { useTheme } from "@react-navigation/native"; import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; import { PressableScale } from "react-native-pressable-scale"; @@ -59,7 +59,7 @@ const MenuCard = ({ card, onPress }: { card: ServiceCard, onPress?: () => void } { color: card?.theme?.colors?.text } ]} > - •••• {card.identifier.slice(-4)} + {formatCardIdentifier(card.account?.localID)} )} diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 99abe3698..0102eb264 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -55,6 +55,16 @@ import { ReservationHistory } from "@/services/shared/ReservationHistory"; import { STORE_THEMES, StoreTheme } from "./Cards/StoreThemes"; import MenuCard from "./Cards/Card"; +export const formatCardIdentifier = (identifier: string, dots: number = 4, separator: string = " ") => { + if(!identifier) { + return ""; + } + + const visiblePart = identifier.slice(-4); + const maskedPart = identifier.slice(-(4 + dots), -4).replace(/./g, "•"); + return maskedPart + separator + (visiblePart.match(/.{1,4}/g) ?? []).join(" "); +}; + export interface ServiceCard { service: string | AccountService; account: Account | null; @@ -223,7 +233,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { balance: balance, history: history, cardnumber: cardnumber, - theme: STORE_THEMES.find((theme) => theme.id === AccountService[account.service]) ?? STORE_THEMES[0], + theme: STORE_THEMES.find((theme) => theme.id === AccountService[account.service].toLowerCase()) ?? STORE_THEMES[0], }; newCards.push(newCard); diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index d2517f930..5d9c9caa1 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -17,12 +17,7 @@ import { QrCode } from "lucide-react-native"; import { balanceFromExternal } from "@/services/balance"; import { reservationHistoryFromExternal } from "@/services/reservation-history"; import { Screen } from "@/router/helpers/types"; - -const formatCardIdentifier = (identifier: string) => { - const visiblePart = identifier.slice(-6); - const maskedPart = identifier.slice(0, -6).replace(/./g, "•"); - return maskedPart + " " + (visiblePart.match(/.{1,4}/g) ?? []).join(" "); -}; +import { formatCardIdentifier } from "../Menu"; const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigation }) => { try { @@ -132,10 +127,10 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio opacity: 0.5, textAlign: "center", color: theme.colors.text, - letterSpacing: 1.5, + letterSpacing: 3.5, }} > - {formatCardIdentifier(route.params.card.identifier)} + {formatCardIdentifier(card.account?.localID, 12, "")} From 85d16741b3db56fc8cefcf4efd729a266870d1bd Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sun, 16 Feb 2025 20:46:17 +0100 Subject: [PATCH 0652/1144] =?UTF-8?q?fix(PapillonPicker):=20ajouter=20un?= =?UTF-8?q?=20champ=20subtitle=20pour=20les=20=C3=A9l=C3=A9ments=20de=20s?= =?UTF-8?q?=C3=A9lection=20et=20mettre=20=C3=A0=20jour=20la=20gestion=20de?= =?UTF-8?q?s=20actions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Global/PapillonPicker.tsx | 12 +++++++++--- src/views/account/Grades/Subject/Subject.tsx | 3 +++ src/views/account/Lessons/Lessons.tsx | 3 +++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/components/Global/PapillonPicker.tsx b/src/components/Global/PapillonPicker.tsx index 6412e2b56..3e0176492 100644 --- a/src/components/Global/PapillonPicker.tsx +++ b/src/components/Global/PapillonPicker.tsx @@ -15,6 +15,7 @@ import { isExpoGo } from "@/utils/native/expoGoAlert"; export type PickerDataItem = string | { label: string, + subtitle?: string, icon?: JSX.Element, sfSymbol?: string, onPress?: () => unknown, @@ -66,7 +67,7 @@ const PapillonPicker: React.FC = ({ const item = data[index]; if (item !== null) { - if (typeof item === "string") { + if (!item.onPress) { handleSelectionChange(item); } else { item.onPress(); @@ -79,7 +80,8 @@ const PapillonPicker: React.FC = ({ return { actionKey: "action-"+index.toString(), actionTitle: typeof item === "string" ? item : item.label, - menuState: (typeof item !== "string" ? item.checked : item === selected) ? "on" : "off", + actionSubtitle: item.subtitle, + menuState: (item.checked || item === selected) ? "on" : "off", icon: { type: typeof item !== "string" ? "IMAGE_SYSTEM" : "IMAGE_SYSTEM", imageValue: { @@ -90,7 +92,11 @@ const PapillonPicker: React.FC = ({ }), }} > - {children} + {}} + > + {children} + ); } diff --git a/src/views/account/Grades/Subject/Subject.tsx b/src/views/account/Grades/Subject/Subject.tsx index a3785ca22..640576576 100644 --- a/src/views/account/Grades/Subject/Subject.tsx +++ b/src/views/account/Grades/Subject/Subject.tsx @@ -31,14 +31,17 @@ const sortings: PickerDataItem[] = [ { label: "Alphabétique", icon: , + sfSymbol: "arrow.up.arrow.down", }, { label: "Date", icon: , + sfSymbol: "calendar", }, { label: "Moyenne", icon: , + sfSymbol: "chart.line.uptrend.xyaxis", }, ]; diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index d014f11b4..e8aaf65cc 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -380,6 +380,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { { icon: , label: "Importer un iCal", + subtitle: "Ajouter un calendrier depuis une URL", sfSymbol: "calendar.badge.plus", onPress: () => { navigation.navigate("LessonsImportIcal", {}); @@ -388,6 +389,8 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { ...(weekFrequency != null) ? [{ icon: , label: "Afficher type sem.", + subtitle: "Afficher semaine paire / impaire", + sfSymbol: "eye", onPress: () => { setShouldShowWeekFrequency(!shouldShowWeekFrequency); }, From adaca9eda4190ec51ba9481f0efd7da2e7789217 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sun, 16 Feb 2025 20:52:04 +0100 Subject: [PATCH 0653/1144] =?UTF-8?q?fix(Grades):=20ajouter=20un=20champ?= =?UTF-8?q?=20subtitle=20pour=20les=20p=C3=A9riodes=20dans=20le=20s=C3=A9l?= =?UTF-8?q?ecteur?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Grades/Grades.tsx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/views/account/Grades/Grades.tsx b/src/views/account/Grades/Grades.tsx index 620bc05d3..2e8d8449d 100644 --- a/src/views/account/Grades/Grades.tsx +++ b/src/views/account/Grades/Grades.tsx @@ -145,7 +145,21 @@ const Grades: Screen<"Grades"> = ({ route, navigation }) => { period.name)} + data={periods.map((period) => { + return { + label: period.name, + subtitle: + new Date(period.startTimestamp).toLocaleDateString( + "fr-FR", + { + month: "long", + day: "numeric", + } + ), + onPress: () => setUserSelectedPeriod(period.name), + checked: period.name === selectedPeriod, + }; + })} selected={userSelectedPeriod ?? selectedPeriod} onSelectionChange={setUserSelectedPeriod} > From 4a08d5df92e3548a2d3afcc233bcf72d2580eb78 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sun, 16 Feb 2025 20:56:10 +0100 Subject: [PATCH 0654/1144] =?UTF-8?q?fix(PapillonModernHeader):=20supprime?= =?UTF-8?q?r=20les=20logs=20inutiles=20dans=20le=20composant=20d'en-t?= =?UTF-8?q?=C3=AAte?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Global/PapillonModernHeader.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/Global/PapillonModernHeader.tsx b/src/components/Global/PapillonModernHeader.tsx index 26dc18f9d..551443e09 100644 --- a/src/components/Global/PapillonModernHeader.tsx +++ b/src/components/Global/PapillonModernHeader.tsx @@ -41,8 +41,6 @@ const LinearGradientModernHeader: React.FC = ({ children, out const theme = useTheme(); const insets = useSafeAreaInsets(); - console.log(Platform); - return ( <> From 7477f3c83fe86a513fa5901a4ab308a912e74813 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sun, 16 Feb 2025 21:06:59 +0100 Subject: [PATCH 0655/1144] =?UTF-8?q?fix(Timetable):=20r=C3=A9introduire?= =?UTF-8?q?=20la=20r=C3=A9cup=C3=A9ration=20des=20donn=C3=A9es=20iCal=20da?= =?UTF-8?q?ns=20les=20composants=20de=20le=C3=A7on=20et=20de=20semaine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/timetable.ts | 4 ---- src/views/account/Lessons/Lessons.tsx | 2 ++ src/views/account/Week/Week.tsx | 6 ++++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/services/timetable.ts b/src/services/timetable.ts index 2e163cbe8..2f553d746 100644 --- a/src/services/timetable.ts +++ b/src/services/timetable.ts @@ -3,7 +3,6 @@ import { useTimetableStore } from "@/stores/timetable"; import { epochWNToPronoteWN, weekNumberToDateRange } from "@/utils/epochWeekNumber"; import { checkIfSkoSupported } from "./skolengo/default-personalization"; import { error, log } from "@/utils/logger/logger"; -import { fetchIcalData } from "./local/ical"; import {MultiServiceFeature} from "@/stores/multiService/types"; import {getFeatureAccount} from "@/utils/multiservice"; import { WeekFrequency } from "./shared/Timetable"; @@ -60,9 +59,6 @@ export async function updateTimetableForWeekInCache (account throw new Error("Service not implemented."); } } - - // Fetch iCal data - await fetchIcalData(account, force); } /** diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index e8aaf65cc..dd78ca1c4 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -35,6 +35,7 @@ import { WeekFrequency } from "@/services/shared/Timetable"; import {AccountService} from "@/stores/account/types"; import {hasFeatureAccountSetup} from "@/utils/multiservice"; import {MultiServiceFeature} from "@/stores/multiService/types"; +import { fetchIcalData } from "@/services/local/ical"; const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const account = useCurrentAccount((store) => store.account!); @@ -115,6 +116,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { try { await updateTimetableForWeekInCache(account, weekNumber, force); + await fetchIcalData(account, force); currentlyLoadingWeeks.current.add(weekNumber); } finally { currentlyLoadingWeeks.current.delete(weekNumber); diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index 901e25bd1..7c9fda572 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -19,6 +19,7 @@ import { NativeText } from "@/components/Global/NativeComponents"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import type { Screen } from "@/router/helpers/types"; import {Account} from "@/stores/account/types"; +import { fetchIcalData } from "@/services/local/ical"; const LOCALES = { en: { @@ -246,8 +247,8 @@ const Week: Screen<"Week"> = ({ route, navigation }) => { .map(event => ({ id: event.id.toString(), title: event.title, - start: { dateTime: new Date(event.startTimestamp).toString() }, - end: { dateTime: new Date(event.endTimestamp).toString() }, + start: { dateTime: new Date(event.startTimestamp) }, + end: { dateTime: new Date(event.endTimestamp) }, event: event, })); @@ -263,6 +264,7 @@ const Week: Screen<"Week"> = ({ route, navigation }) => { requestAnimationFrame(async () => { try { await updateTimetableForWeekInCache(account as Account, weekNumber, force); + await fetchIcalData(account as Account, force); } finally { setIsLoading(false); } From 353ce2ba4c31e1a26ea2980b7dbbad36a7e2eda5 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sun, 16 Feb 2025 21:16:32 +0100 Subject: [PATCH 0656/1144] =?UTF-8?q?fix(Grades):=20mettre=20=C3=A0=20jour?= =?UTF-8?q?=20la=20r=C3=A9cup=C3=A9ration=20des=20dates=20de=20d=C3=A9but?= =?UTF-8?q?=20et=20de=20fin=20pour=20les=20semestres?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/iutlan/grades.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/services/iutlan/grades.ts b/src/services/iutlan/grades.ts index 9987e6c4b..28faa4d50 100644 --- a/src/services/iutlan/grades.ts +++ b/src/services/iutlan/grades.ts @@ -194,11 +194,19 @@ export const saveIUTLanGrades = async (account: LocalAccount, periodName: string export const saveIUTLanPeriods = async (account: LocalAccount): Promise => { const scodocData = account.identityProvider.rawData; + const semestresData = account.serviceData.semestres as any; + const semestres = (scodocData["semestres"] as any).map((semestre: any) => { + const semestreName = "Semestre " + semestre.semestre_id; + const innerData = semestresData[semestreName] as any; + + const startTime = innerData["relevé"].semestre.date_debut ? new Date(innerData["relevé"].semestre.date_debut).getTime() : 1609459200; + const endTime = innerData["relevé"].semestre.date_fin ? new Date(innerData["relevé"].semestre.date_fin).getTime() : 1622505600; + return { - name: "Semestre " + semestre.semestre_id, - startTimestamp: 1609459200, - endTimestamp: 1622505600 + name: semestreName, + startTimestamp: startTime, + endTimestamp: endTime, }; }); From 788ba35059a30396377d20f1e5d3dbdccc7ed35a Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sun, 16 Feb 2025 21:47:07 +0100 Subject: [PATCH 0657/1144] =?UTF-8?q?fix(Header):=20ajouter=20le=20composa?= =?UTF-8?q?nt=20PapillonHeaderInsetHeight=20et=20ajuster=20la=20visibilit?= =?UTF-8?q?=C3=A9=20de=20l'en-t=C3=AAte?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Global/PapillonHeader.tsx | 101 ++++++------ .../Global/PapillonModernHeader.tsx | 2 +- src/components/Global/TabAnimatedTitle.tsx | 1 + src/router/screens/account/index.tsx | 1 + src/views/account/Attendance/Attendance.tsx | 4 +- src/views/account/Chat/Messages.tsx | 4 +- src/views/account/News/News.tsx | 146 +++++++++--------- src/views/account/Restaurant/Menu.tsx | 4 +- 8 files changed, 140 insertions(+), 123 deletions(-) diff --git a/src/components/Global/PapillonHeader.tsx b/src/components/Global/PapillonHeader.tsx index 7a31e1cc8..ffaa633ff 100644 --- a/src/components/Global/PapillonHeader.tsx +++ b/src/components/Global/PapillonHeader.tsx @@ -7,6 +7,7 @@ import { ArrowLeft } from "lucide-react-native"; import { type RouteProp, useTheme } from "@react-navigation/native"; import type { RouteParameters } from "@/router/helpers/types"; import type { NativeStackNavigationProp } from "@react-navigation/native-stack"; +import { PapillonModernHeader } from "./PapillonModernHeader"; interface PapillonHeaderProps { children?: React.ReactNode @@ -14,6 +15,10 @@ interface PapillonHeaderProps { navigation: NativeStackNavigationProp } +interface PapillonHeaderInsetHeightProps { + route: RouteProp +} + const PapillonHeader: React.FC = ({ children, route, @@ -26,58 +31,64 @@ const PapillonHeader: React.FC = ({ const largeHeader = route.params?.outsideNav || Platform.OS !== "ios"; return ( - - {route.params?.outsideNav && Platform.OS !== "ios" && ( - + + navigation.goBack()} > - - - )} - - + {route.params?.outsideNav && Platform.OS !== "ios" && ( + navigation.goBack()} + > + + + )} - - {children} - - {Platform.OS === "ios" && ( - - )} - - + + + {children} + + {Platform.OS === "ios" && ( + + )} + + + + + ); +}; + + +export const PapillonHeaderInsetHeight: React.FC = ({ + route, +}) => { + return ( + ); }; diff --git a/src/components/Global/PapillonModernHeader.tsx b/src/components/Global/PapillonModernHeader.tsx index 551443e09..48b57eb7b 100644 --- a/src/components/Global/PapillonModernHeader.tsx +++ b/src/components/Global/PapillonModernHeader.tsx @@ -43,11 +43,11 @@ const LinearGradientModernHeader: React.FC = ({ children, out return ( <> - {Platform.OS === "ios" && !isExpoGo() && parseInt(Platform.Version) >= 18 ? ( { navigation={navigation} /> ), + headerShadowVisible: false, }; }; diff --git a/src/router/screens/account/index.tsx b/src/router/screens/account/index.tsx index d93adbc1b..1654d2da2 100644 --- a/src/router/screens/account/index.tsx +++ b/src/router/screens/account/index.tsx @@ -43,6 +43,7 @@ export const screens = [ createScreen("News", NewsScreen, { headerTitle: "Actualités", tabBarLabel: "Actualités", + headerShown: false, tabBarLottie: require("@/../assets/lottie/tab_news.json"), }), createScreen("Attendance", Attendance, { diff --git a/src/views/account/Attendance/Attendance.tsx b/src/views/account/Attendance/Attendance.tsx index 4fc80816c..f2f02da65 100644 --- a/src/views/account/Attendance/Attendance.tsx +++ b/src/views/account/Attendance/Attendance.tsx @@ -10,7 +10,7 @@ import {NativeText} from "@/components/Global/NativeComponents"; import Reanimated, {FadeIn, FadeOut, LinearTransition} from "react-native-reanimated"; import PapillonPicker from "@/components/Global/PapillonPicker"; import {ChevronDown, Eye, Scale, Timer, UserX} from "lucide-react-native"; -import PapillonHeader from "@/components/Global/PapillonHeader"; +import PapillonHeader, { PapillonHeaderInsetHeight } from "@/components/Global/PapillonHeader"; import {animPapillon} from "@/utils/ui/animations"; import AttendanceItem from "./Atoms/AttendanceItem"; import {getAbsenceTime} from "@/utils/format/attendance_time"; @@ -244,6 +244,8 @@ const Attendance: Screen<"Attendance"> = ({ route, navigation }) => { /> } > + + {hasServiceSetup && attendances[selectedPeriod] && attendances[selectedPeriod].absences.length === 0 && attendances[selectedPeriod].delays.length === 0 && attendances[selectedPeriod].punishments.length === 0 && Object.keys(attendances_observations_details).length === 0 &&( = ({ navigation, route }) => { }} refreshControl={} > + + {!chats ? ( & { date: string, important: boolean }; @@ -34,12 +34,6 @@ const NewsScreen: Screen<"News"> = ({ route, navigation }) => { const [sortedMessages, setSortedMessages] = useState([]); const [isED, setIsED] = useState(false); - useLayoutEffect(() => { - navigation.setOptions({ - ...TabAnimatedTitle({ route, navigation }), - }); - }, [navigation, route.params, theme.colors.text]); - const fetchData = useCallback(async (hidden: boolean = false) => { if (!hidden) setIsLoading(true); await updateNewsInCache(account); @@ -122,83 +116,87 @@ const NewsScreen: Screen<"News"> = ({ route, navigation }) => { const hasNews = importantMessages.length > 0 || sortedMessages.length > 0; return ( - - } - > - {importantMessages.length > 0 && ( - - - } - trailing={} - /> + <> + + + } + > + + + {importantMessages.length > 0 && ( + + + } + trailing={} + /> - - + + + `important-${index}`} + scrollEnabled={false} + /> + + + + )} + + {sortedMessages.length > 0 && ( + + `important-${index}`} + keyExtractor={(_, index) => `sorted-${index}`} scrollEnabled={false} + initialNumToRender={6} /> - - - - )} - - {sortedMessages.length > 0 && ( - - - `sorted-${index}`} - scrollEnabled={false} - initialNumToRender={6} - /> - - - )} - - {hasServiceSetup ? - !isLoading && !hasNews && - : - - } - + + + )} + + {hasServiceSetup ? + !isLoading && !hasNews && + : + + } + + ); }; const styles = StyleSheet.create({ scrollViewContent: { padding: 16, - paddingTop: 0, }, magicIcon: { width: 26, diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 0102eb264..950fb43f1 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -44,7 +44,7 @@ import { LessonsDateModal } from "../Lessons/LessonsHeader"; import { BookingTerminal, BookingDay } from "@/services/shared/Booking"; import { bookDayFromExternal, getBookingsAvailableFromExternal } from "@/services/booking"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; -import PapillonHeader from "@/components/Global/PapillonHeader"; +import PapillonHeader, { PapillonHeaderInsetHeight } from "@/components/Global/PapillonHeader"; import { PressableScale } from "react-native-pressable-scale"; import { ChevronLeft, ChevronRight} from "lucide-react-native"; import DrawableImportRestaurant from "@/components/Drawables/DrawableImportRestaurant"; @@ -342,6 +342,8 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { /> } > + + {!isInitialised ? ( ) : ( From 9db33e46f88c940b3f6daa9b3ad6e3ed32130453 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sun, 16 Feb 2025 21:50:46 +0100 Subject: [PATCH 0658/1144] =?UTF-8?q?fix(Header):=20ajouter=20la=20gestion?= =?UTF-8?q?=20de=20l'opacit=C3=A9=20du=20d=C3=A9grad=C3=A9=20et=20ajuster?= =?UTF-8?q?=20les=20propri=C3=A9t=C3=A9s=20de=20d=C3=A9filement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Global/PapillonModernHeader.tsx | 33 ++++++++++--------- src/views/account/Chat/Messages.tsx | 1 + src/views/account/News/News.tsx | 1 + src/views/account/Restaurant/Menu.tsx | 3 +- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/components/Global/PapillonModernHeader.tsx b/src/components/Global/PapillonModernHeader.tsx index 48b57eb7b..b6b436d5d 100644 --- a/src/components/Global/PapillonModernHeader.tsx +++ b/src/components/Global/PapillonModernHeader.tsx @@ -41,9 +41,11 @@ const LinearGradientModernHeader: React.FC = ({ children, out const theme = useTheme(); const insets = useSafeAreaInsets(); + const enableBlur = Platform.OS === "ios" && !isExpoGo() && parseInt(Platform.Version) >= 18; + return ( <> - {Platform.OS === "ios" && !isExpoGo() && parseInt(Platform.Version) >= 18 ? ( + {enableBlur ? ( = ({ children, out ] }} /> - ) : ( - - )} + ) : null} + + = ({ navigation, route }) => { padding: 20, paddingTop: 0, }} + scrollIndicatorInsets={{ top: 42 }} refreshControl={} > diff --git a/src/views/account/News/News.tsx b/src/views/account/News/News.tsx index 5319d00cf..e54c8e0aa 100644 --- a/src/views/account/News/News.tsx +++ b/src/views/account/News/News.tsx @@ -123,6 +123,7 @@ const NewsScreen: Screen<"News"> = ({ route, navigation }) => { refreshControl={ } + scrollIndicatorInsets={{ top: 42 }} > diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 950fb43f1..582298934 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -333,6 +333,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { = ({ route, navigation }) => { }; const styles = StyleSheet.create({ - scrollViewContent: { padding: 16, paddingTop: 0, flexGrow: 1 }, + scrollViewContent: { padding: 16, flexGrow: 1 }, accountButtonContainer: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", gap: 16, marginBottom: 16 }, horizontalList: { marginTop: 10 }, calendarContainer: { flexDirection: "row", justifyContent: "center", alignItems: "center", marginTop: 16, marginBottom: -10, gap: 10 }, From faa15c9c2b17adef15f40b38e25eaccce11343f7 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sun, 16 Feb 2025 21:53:35 +0100 Subject: [PATCH 0659/1144] fix(News, Menu): ajouter progressViewOffset aux composants RefreshControl --- src/views/account/News/News.tsx | 3 ++- src/views/account/Restaurant/Menu.tsx | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/views/account/News/News.tsx b/src/views/account/News/News.tsx index e54c8e0aa..08d7c7da5 100644 --- a/src/views/account/News/News.tsx +++ b/src/views/account/News/News.tsx @@ -121,7 +121,8 @@ const NewsScreen: Screen<"News"> = ({ route, navigation }) => { + } scrollIndicatorInsets={{ top: 42 }} > diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 582298934..d040b6806 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -337,6 +337,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { refreshControl={ { refreshData(); }} From 3700df7711751ae7a9e9465fe6e7654031f0f9a8 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 17 Feb 2025 00:54:06 +0100 Subject: [PATCH 0660/1144] =?UTF-8?q?fix(AccountSwitcher):=20am=C3=A9liore?= =?UTF-8?q?r=20la=20gestion=20des=20interactions=20tactiles=20avec=20le=20?= =?UTF-8?q?menu=20contextuel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Home/AccountSwitcherContextMenu.tsx | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/components/Home/AccountSwitcherContextMenu.tsx b/src/components/Home/AccountSwitcherContextMenu.tsx index 86087eb0a..591d8bab2 100644 --- a/src/components/Home/AccountSwitcherContextMenu.tsx +++ b/src/components/Home/AccountSwitcherContextMenu.tsx @@ -13,8 +13,7 @@ import { import Reanimated, { FadeIn, - FadeOut, - ZoomIn + FadeOut } from "react-native-reanimated"; import { useNavigation } from "@react-navigation/native"; @@ -22,7 +21,7 @@ import * as Haptics from "expo-haptics"; import { useAccounts, useCurrentAccount } from "@/stores/account"; import { AccountService } from "@/stores/account/types"; -import { animPapillon, PapillonContextEnter, PapillonContextExit } from "@/utils/ui/animations"; +import { PapillonContextEnter, PapillonContextExit } from "@/utils/ui/animations"; import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; import { useTheme } from "@react-navigation/native"; import { BlurView } from "expo-blur"; @@ -58,6 +57,12 @@ const ContextMenu: React.FC<{ Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); }; + const [touchLongPress, setTouchLongPress] = useState(false); + + useEffect(() => { + setTouchLongPress(false); + }, [opened]); + return ( <> { - setOpened(!opened); - openEffects(); + onPressIn={() => { + if (!touchLongPress) { + setOpened(!opened); + openEffects(); + } + }} + onLongPress={() => { + setTouchLongPress(true); + }} + onPressOut={() => { + if (touchLongPress) { + setOpened(false); + openEffects(); + } }} // @ts-expect-error pointerEvents="auto" @@ -225,8 +241,6 @@ const ContextMenu: React.FC<{ position: "absolute", right: 15, }} - entering={animPapillon(ZoomIn)} - exiting={FadeOut.duration(200)} > Date: Mon, 17 Feb 2025 00:56:04 +0100 Subject: [PATCH 0661/1144] =?UTF-8?q?fix(version):=20mettre=20=C3=A0=20jou?= =?UTF-8?q?r=20la=20version=20de=20l'application=20=C3=A0=207.9.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/build.gradle | 4 ++-- ios/Papillon/Info.plist | 2 +- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 5b3d5424d..059efc776 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -88,8 +88,8 @@ android { applicationId 'xyz.getpapillon.app' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 7830 - versionName "7.8.3" + versionCode 7900 + versionName "7.9.0" } signingConfigs { debug { diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index 04c938763..853e2457f 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 7.8.3 + 7.9.0 CFBundleSignature ???? CFBundleURLTypes diff --git a/package.json b/package.json index 264ee5005..9817e603c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "papillonvex", - "version": "7.8.3", + "version": "7.9.0", "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", From 24f4143f680678f43346167e980b9b1cd7ebbc5e Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 17 Feb 2025 00:57:57 +0100 Subject: [PATCH 0662/1144] =?UTF-8?q?fix(Card,=20Menu):=20ajuster=20le=20s?= =?UTF-8?q?tyle=20et=20corriger=20la=20recherche=20de=20th=C3=A8me?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Restaurant/Cards/Card.tsx | 1 + src/views/account/Restaurant/Menu.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/account/Restaurant/Cards/Card.tsx b/src/views/account/Restaurant/Cards/Card.tsx index 4d908dab1..5fc27d5e5 100644 --- a/src/views/account/Restaurant/Cards/Card.tsx +++ b/src/views/account/Restaurant/Cards/Card.tsx @@ -121,6 +121,7 @@ const styles = StyleSheet.create({ fontSize: 16, fontFamily: "semibold", flex: 1, + marginTop: 9, }, cardHeaderIcon: { diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index d040b6806..ee8ed4c31 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -233,7 +233,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { balance: balance, history: history, cardnumber: cardnumber, - theme: STORE_THEMES.find((theme) => theme.id === AccountService[account.service].toLowerCase()) ?? STORE_THEMES[0], + theme: STORE_THEMES.find((theme) => theme.id === AccountService[account.service]) ?? STORE_THEMES[0], }; newCards.push(newCard); From 413c68945f2d1ce5c77e0498aa307e53e597c48a Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 17 Feb 2025 00:59:57 +0100 Subject: [PATCH 0663/1144] =?UTF-8?q?fix(AttendanceItem):=20am=C3=A9liorer?= =?UTF-8?q?=20le=20formatage=20des=20dates=20et=20heures=20en=20fran=C3=A7?= =?UTF-8?q?ais?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Attendance/Atoms/AttendanceItem.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/views/account/Attendance/Atoms/AttendanceItem.tsx b/src/views/account/Attendance/Atoms/AttendanceItem.tsx index e96914e2d..8fcaccbbe 100644 --- a/src/views/account/Attendance/Atoms/AttendanceItem.tsx +++ b/src/views/account/Attendance/Atoms/AttendanceItem.tsx @@ -89,16 +89,18 @@ const AttendanceItem: React.FC = ({ const not_justified = "justified" in item && !item.justified; const justification = "reasons" in item ? item.reasons || NO_JUSTICATION : "reason" in item ? item.reason.text : NO_JUSTICATION; const dateString = toTimestamp - ? `Du ${new Date(timestamp).toLocaleDateString("fr-FR", { + ? `du ${new Date(timestamp).toLocaleDateString("fr-FR", { + weekday: "long", day: "2-digit", - month: "2-digit", + month: "short", year: new Date(timestamp).getFullYear() !== new Date().getFullYear() ? "2-digit" : undefined, })} à ${new Date(timestamp).toLocaleTimeString("fr-FR", { hour: "2-digit", minute: "2-digit", - })}\nAu ${new Date(toTimestamp).toLocaleDateString("fr-FR", { + })}\nau ${new Date(toTimestamp).toLocaleDateString("fr-FR", { + weekday: "long", day: "2-digit", - month: "2-digit", + month: "short", year: new Date(toTimestamp).getFullYear() !== new Date().getFullYear() ? "2-digit" : undefined, })} à ${new Date(toTimestamp).toLocaleTimeString("fr-FR", { hour: "2-digit", From 882d5989af047a8cc1048c14794aade6aeb23ab1 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 17 Feb 2025 08:25:53 +0100 Subject: [PATCH 0664/1144] =?UTF-8?q?fix:=20Changement=20lors=20de=20la=20?= =?UTF-8?q?d=C3=A9tection=20du=20Mode=20tablette?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useScreenDimensions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useScreenDimensions.ts b/src/hooks/useScreenDimensions.ts index 104088795..9d8198220 100644 --- a/src/hooks/useScreenDimensions.ts +++ b/src/hooks/useScreenDimensions.ts @@ -29,6 +29,6 @@ export default function useScreenDimensions () { return { width: screenDimensions.width, height: screenDimensions.height, - isTablet: (screenDimensions.width / screenDimensions.height) * 10 >= 6.9, + isTablet: screenDimensions.width > screenDimensions.height, }; } From 1684113deb3c0656c88e1ff060d445a14f26fb48 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 17 Feb 2025 12:26:45 +0100 Subject: [PATCH 0665/1144] refactor: fix des erreurs de lint --- src/components/Global/NativeComponents.tsx | 2 +- src/components/Global/PapillonPicker.tsx | 8 ++++++-- src/views/account/Grades/Grades.tsx | 2 +- src/views/account/Restaurant/Cards/Card.tsx | 2 +- src/views/account/Restaurant/Modals/CardDetail.tsx | 2 +- src/views/account/Week/Week.tsx | 1 + src/views/welcome/AccountSelector.tsx | 7 ++++--- 7 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/components/Global/NativeComponents.tsx b/src/components/Global/NativeComponents.tsx index 29bfb4592..c16a080d8 100644 --- a/src/components/Global/NativeComponents.tsx +++ b/src/components/Global/NativeComponents.tsx @@ -35,7 +35,7 @@ export const NativeList: React.FC = ({ const theme = useTheme(); const { colors } = theme; - const listStyle = useMemo(() => [ + const listStyle: StyleProp = useMemo(() => [ list_styles.list, { borderWidth: 0.5, diff --git a/src/components/Global/PapillonPicker.tsx b/src/components/Global/PapillonPicker.tsx index 3e0176492..adbaf6673 100644 --- a/src/components/Global/PapillonPicker.tsx +++ b/src/components/Global/PapillonPicker.tsx @@ -18,7 +18,7 @@ export type PickerDataItem = string | { subtitle?: string, icon?: JSX.Element, sfSymbol?: string, - onPress?: () => unknown, + onPress?: () => {} | void, checked?: boolean }; @@ -65,11 +65,13 @@ const PapillonPicker: React.FC = ({ const actionKey = event.nativeEvent.actionKey; const index = parseInt(actionKey.split("-")[1]); - const item = data[index]; + const item: PickerDataItem = data[index]; if (item !== null) { + // @ts-ignore if (!item.onPress) { handleSelectionChange(item); } else { + // @ts-ignore item.onPress(); } } @@ -80,7 +82,9 @@ const PapillonPicker: React.FC = ({ return { actionKey: "action-"+index.toString(), actionTitle: typeof item === "string" ? item : item.label, + // @ts-ignore actionSubtitle: item.subtitle, + // @ts-ignore menuState: (item.checked || item === selected) ? "on" : "off", icon: { type: typeof item !== "string" ? "IMAGE_SYSTEM" : "IMAGE_SYSTEM", diff --git a/src/views/account/Grades/Grades.tsx b/src/views/account/Grades/Grades.tsx index 2e8d8449d..b606a7a8e 100644 --- a/src/views/account/Grades/Grades.tsx +++ b/src/views/account/Grades/Grades.tsx @@ -149,7 +149,7 @@ const Grades: Screen<"Grades"> = ({ route, navigation }) => { return { label: period.name, subtitle: - new Date(period.startTimestamp).toLocaleDateString( + new Date(period.startTimestamp as number).toLocaleDateString( "fr-FR", { month: "long", diff --git a/src/views/account/Restaurant/Cards/Card.tsx b/src/views/account/Restaurant/Cards/Card.tsx index 5fc27d5e5..4671909ca 100644 --- a/src/views/account/Restaurant/Cards/Card.tsx +++ b/src/views/account/Restaurant/Cards/Card.tsx @@ -62,7 +62,7 @@ const MenuCard = ({ card, onPress }: { card: ServiceCard, onPress?: () => void } { color: card?.theme?.colors?.text } ]} > - {formatCardIdentifier(card.account?.localID)} + {formatCardIdentifier(card.account?.localID as string)} )} diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index 5d9c9caa1..829b22849 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -130,7 +130,7 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio letterSpacing: 3.5, }} > - {formatCardIdentifier(card.account?.localID, 12, "")} + {formatCardIdentifier(card.account?.localID as string, 12, "")} diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index 7c9fda572..773842018 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -252,6 +252,7 @@ const Week: Screen<"Week"> = ({ route, navigation }) => { event: event, })); + // @ts-ignore setEvents(nevts); }, [timetables]); diff --git a/src/views/welcome/AccountSelector.tsx b/src/views/welcome/AccountSelector.tsx index 29010a624..da24a46bc 100644 --- a/src/views/welcome/AccountSelector.tsx +++ b/src/views/welcome/AccountSelector.tsx @@ -4,6 +4,7 @@ import React, { useEffect } from "react"; import { Image, useColorScheme } from "react-native"; import * as SplashScreen from "expo-splash-screen"; +import { PrimaryAccount } from "@/stores/account/types"; const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { const colorScheme = useColorScheme(); @@ -28,9 +29,9 @@ const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { } const selectedAccount = - accounts.find((account) => account.localID === lastOpenedAccountID) as PrimaryAccount - ?? accounts.find((account) => !account.isExternal) as PrimaryAccount; - switchTo(selectedAccount); + accounts.find((account) => account.localID === lastOpenedAccountID) + ?? accounts.find((account) => !account.isExternal); + switchTo(selectedAccount as PrimaryAccount); }(); }, [accounts]); From a3d12ec5b2f149c0e10e5cf28d532621f2cc268a Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 17 Feb 2025 19:28:40 +0100 Subject: [PATCH 0666/1144] =?UTF-8?q?feat(AlertProvider):=20am=C3=A9liorat?= =?UTF-8?q?ion=20du=20rendu,=20simplification=20de=20la=20gestion=20des=20?= =?UTF-8?q?alertes=20et=20am=C3=A9lioration=20de=20la=20structure=20du=20c?= =?UTF-8?q?ode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/providers/AlertProvider.tsx | 224 ++++++++------------------------ 1 file changed, 52 insertions(+), 172 deletions(-) diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index 810608d82..2664d43a5 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -1,7 +1,7 @@ import { useTheme } from "@react-navigation/native"; import { Check } from "lucide-react-native"; import React, { createContext, useState, useContext, ReactNode } from "react"; -import { Modal, View, Text, StyleSheet, Dimensions, Pressable } from "react-native"; +import { Modal, View, Text, StyleSheet, Pressable } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import Reanimated, { FadeIn, FadeOut, LinearTransition } from "react-native-reanimated"; import { PapillonContextEnter, PapillonContextExit } from "@/utils/ui/animations"; @@ -19,7 +19,7 @@ type AlertAction = { export type Alert = { title: string; message: string; - icon? : React.ReactElement | null; + icon?: React.ReactElement | null; actions?: AlertAction[]; }; @@ -42,148 +42,58 @@ type AlertProviderProps = { }; const AlertProvider = ({ children }: AlertProviderProps) => { - const [alert, setAlert] = useState({ title: "", message: "", icon: null, actions: [] }); + const [alert, setAlert] = useState(null); const [visible, setVisible] = useState(false); - const [modalVisible, setModalVisible] = useState(false); const { colors } = useTheme(); const insets = useSafeAreaInsets(); const showAlert = ({ - title = "", - message = "", + title, + message, icon = null, - actions = [ - { - title: "Compris !", - onPress: () => hideAlert(), - icon: , - primary: true, - }, - ], - }: Partial) => { - actions.forEach(action => { - let mainColor = colors.text; - - if (action.primary) { - mainColor = "#ffffff"; - } - - // for each action icon, create a new component and change color to primary - if (action.icon) { - action.icon = React.cloneElement(action.icon, { color: mainColor, size: 24 }); - } else { - action.icon = React.cloneElement(, { color: mainColor, size: 24 }); - } - - // if no onPress function is provided, hide the alert - if (!action.onPress) { - action.onPress = () => hideAlert(); - } - - // if action has no primary prop, set it to false - if (action.primary === undefined) { - action.primary = false; - } - }); - - setVisible(true); - setModalVisible(true); - + actions = [{ title: "Compris !", onPress: hideAlert, icon: , primary: true }], + }: Alert) => { setAlert({ title, message, icon, actions }); + setVisible(true); }; - const hideAlert = () => { + function hideAlert () { setVisible(false); - setAlert({ title: "", message: "", actions: [] }); - - setTimeout(() => { - setModalVisible(false); - }, 100); - }; - - const finalIcon = alert.icon ? - React.cloneElement(alert.icon, { color: colors.text, size: 24 }) : - null; + setTimeout(() => setAlert(null), 200); + } return ( {children} - - {visible && ( + {visible && alert && ( + - + - )} - - - - {visible && ( + + - - {finalIcon} - - {alert.title} - + + {alert.icon && React.cloneElement(alert.icon, { color: colors.text, size: 24 })} + {alert.title} - - - {alert.message} - + {alert.message} - - {(alert.actions ?? []).map(({ title, onPress, icon, primary, danger, backgroundColor }) => ( + + {alert.actions?.map(({ title, onPress, icon, primary, danger, backgroundColor }) => ( { @@ -193,79 +103,68 @@ const AlertProvider = ({ children }: AlertProviderProps) => { style={({ pressed }) => [ styles.button, primary && styles.primaryButton, - primary && { - backgroundColor: backgroundColor ? backgroundColor : colors.primary, - }, - danger && { - backgroundColor: "#b62000", - }, - { - opacity: primary ? (pressed ? 0.6 : 1) : (pressed ? 0.3 : 0.6), - } + primary && { backgroundColor: backgroundColor || colors.primary }, + danger && { backgroundColor: "#b62000" }, + { opacity: pressed ? 0.6 : 1 }, ]} > - {icon ? icon : null} - - - {title} - + {icon && React.cloneElement(icon, { color: primary ? "#ffffff" : colors.text, size: 24 })} + {title} ))} - )} - - + + + )} ); }; const styles = StyleSheet.create({ + overlay: { + position: "absolute", + width: "100%", + height: "100%", + backgroundColor: "rgba(0, 0, 0, 0.5)", + }, + blur: { + position: "absolute", + width: "100%", + height: "100%", + }, modalContainer: { flex: 1, justifyContent: "flex-end", alignItems: "center", - backgroundColor: "rgba(0, 0, 0, 0)", }, - + pressable: { + flex: 1, + width: "100%", + }, alertBox: { borderRadius: 16, - shadowColor: "#000", - shadowOffset: { - width: 0, - height: 2, - }, - shadowOpacity: 0.2, - shadowRadius: 4, - elevation: 6, + padding: 20, + paddingBottom: 5, + maxWidth: "85%", }, - contentContainer: { - paddingVertical: 18, - paddingHorizontal: 20, - paddingBottom: 0, gap: 6, }, - titleContainer: { flexDirection: "row", alignItems: "center", gap: 10, }, - title: { fontSize: 18, - lineHeight: 20, fontFamily: "semibold", }, - message: { fontSize: 16, - lineHeight: 21, fontFamily: "medium", opacity: 0.6, }, - buttons: { flexDirection: "row", justifyContent: "flex-end", @@ -275,40 +174,21 @@ const styles = StyleSheet.create({ paddingTop: 10, gap: 10, borderTopWidth: 1, - borderBottomLeftRadius: 16, - borderBottomRightRadius: 16, }, - button: { flexDirection: "row", - gap: 8, + gap: 5, alignItems: "center", - justifyContent: "center", borderRadius: 300, - paddingVertical: 7, - paddingHorizontal: 10, - opacity: 0.6, + paddingVertical: 5, }, - buttonText: { fontSize: 16, - lineHeight: 20, fontFamily: "medium", }, - primaryButton: { - opacity: 1, paddingHorizontal: 14, - shadowColor: "#000", - shadowOffset: { - width: 0, - height: 1, - }, - shadowOpacity: 0.2, - shadowRadius: 2, - elevation: 2, }, - primaryButtonText: { color: "#ffffff", fontFamily: "semibold", From 40d8e9a5f3d1afc463a4bb2e8e31a41786f559f2 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 17 Feb 2025 19:31:05 +0100 Subject: [PATCH 0667/1144] fix(AlertProvider): indentation du code --- src/providers/AlertProvider.tsx | 108 ++++++++++++++++++++++++-------- 1 file changed, 81 insertions(+), 27 deletions(-) diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index 2664d43a5..9b40d1c0e 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -3,8 +3,15 @@ import { Check } from "lucide-react-native"; import React, { createContext, useState, useContext, ReactNode } from "react"; import { Modal, View, Text, StyleSheet, Pressable } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import Reanimated, { FadeIn, FadeOut, LinearTransition } from "react-native-reanimated"; -import { PapillonContextEnter, PapillonContextExit } from "@/utils/ui/animations"; +import Reanimated, { + FadeIn, + FadeOut, + LinearTransition, +} from "react-native-reanimated"; +import { + PapillonContextEnter, + PapillonContextExit, +} from "@/utils/ui/animations"; import { BlurView } from "expo-blur"; type AlertAction = { @@ -52,7 +59,14 @@ const AlertProvider = ({ children }: AlertProviderProps) => { title, message, icon = null, - actions = [{ title: "Compris !", onPress: hideAlert, icon: , primary: true }], + actions = [ + { + title: "Compris !", + onPress: hideAlert, + icon: , + primary: true, + }, + ], }: Alert) => { setAlert({ title, message, icon, actions }); setVisible(true); @@ -77,41 +91,81 @@ const AlertProvider = ({ children }: AlertProviderProps) => { - + - {alert.icon && React.cloneElement(alert.icon, { color: colors.text, size: 24 })} - {alert.title} + {alert.icon && + React.cloneElement(alert.icon, { + color: colors.text, + size: 24, + })} + + {alert.title} + - {alert.message} + + {alert.message} + - {alert.actions?.map(({ title, onPress, icon, primary, danger, backgroundColor }) => ( - { - onPress?.(); - hideAlert(); - }} - style={({ pressed }) => [ - styles.button, - primary && styles.primaryButton, - primary && { backgroundColor: backgroundColor || colors.primary }, - danger && { backgroundColor: "#b62000" }, - { opacity: pressed ? 0.6 : 1 }, - ]} - > - {icon && React.cloneElement(icon, { color: primary ? "#ffffff" : colors.text, size: 24 })} - {title} - - ))} + {alert.actions?.map( + ({ + title, + onPress, + icon, + primary, + danger, + backgroundColor, + }) => ( + { + onPress?.(); + hideAlert(); + }} + style={({ pressed }) => [ + styles.button, + primary && styles.primaryButton, + primary && { + backgroundColor: backgroundColor || colors.primary, + }, + danger && { backgroundColor: "#b62000" }, + { opacity: pressed ? 0.6 : 1 }, + ]} + > + {icon && + React.cloneElement(icon, { + color: primary ? "#ffffff" : colors.text, + size: 24, + })} + + {title} + + + ) + )} From 469f80df24759b55036b3ee1dcbe653ab9915aa2 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 17 Feb 2025 19:42:25 +0100 Subject: [PATCH 0668/1144] fix(AlertProvider): ajout d'une bordure pour les boutons non primaires et ajustement du padding --- src/providers/AlertProvider.tsx | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index 9b40d1c0e..162c114e3 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -141,10 +141,18 @@ const AlertProvider = ({ children }: AlertProviderProps) => { }} style={({ pressed }) => [ styles.button, - primary && styles.primaryButton, - primary && { - backgroundColor: backgroundColor || colors.primary, - }, + primary + ? styles.primaryButton + : styles.notPrimaryButton, + primary + ? { + backgroundColor: + backgroundColor || colors.primary, + } + : { + borderColor: "#CCC", + borderWidth: 1, + }, danger && { backgroundColor: "#b62000" }, { opacity: pressed ? 0.6 : 1 }, ]} @@ -223,7 +231,7 @@ const styles = StyleSheet.create({ flexDirection: "row", justifyContent: "flex-end", paddingVertical: 12, - paddingHorizontal: 14, + paddingHorizontal: 5, marginTop: 16, paddingTop: 10, gap: 10, @@ -243,6 +251,9 @@ const styles = StyleSheet.create({ primaryButton: { paddingHorizontal: 14, }, + notPrimaryButton: { + paddingHorizontal: 5, + }, primaryButtonText: { color: "#ffffff", fontFamily: "semibold", From bb7d9606e822eda5a0e255d0d04084a9b4ca7502 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 17 Feb 2025 21:28:13 +0100 Subject: [PATCH 0669/1144] =?UTF-8?q?fix:=20ajout=20d'ic=C3=B4nes=20et=20d?= =?UTF-8?q?e=20primary=20pour=20les=20actions=20dans=20plusieurs=20composa?= =?UTF-8?q?nts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/providers/AlertProvider.tsx | 2 +- src/views/login/IdentityProvider/providers/UnivLimoges.tsx | 3 +++ src/views/login/pronote/PronoteManualURL.tsx | 4 ++-- src/views/login/pronote/PronoteWebview.tsx | 4 +++- src/views/login/skolengo/SkolengoWebview.tsx | 3 +++ src/views/settings/SettingsProfile.tsx | 4 +++- src/views/settings/SettingsTabs.tsx | 3 +++ 7 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index 162c114e3..1c3f3aa2e 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -17,7 +17,7 @@ import { BlurView } from "expo-blur"; type AlertAction = { title: string; onPress?: () => void; - icon?: React.ReactElement; + icon: React.ReactElement; primary?: boolean; danger?: boolean; backgroundColor?: string; diff --git a/src/views/login/IdentityProvider/providers/UnivLimoges.tsx b/src/views/login/IdentityProvider/providers/UnivLimoges.tsx index da79aacc1..450601ed2 100644 --- a/src/views/login/IdentityProvider/providers/UnivLimoges.tsx +++ b/src/views/login/IdentityProvider/providers/UnivLimoges.tsx @@ -12,6 +12,7 @@ import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { NativeText } from "@/components/Global/NativeComponents"; import { log } from "@/utils/logger/logger"; import { useAlert } from "@/providers/AlertProvider"; +import { Check } from "lucide-react-native"; const UnivLimoges_Login: Screen<"UnivLimoges_Login"> = ({ navigation }) => { const createStoredAccount = useAccounts(store => store.create); @@ -89,6 +90,8 @@ const UnivLimoges_Login: Screen<"UnivLimoges_Login"> = ({ navigation }) => { actions: [ { title: "OK", + icon: , + primary: true, onPress: () => navigation.goBack() }, ], diff --git a/src/views/login/pronote/PronoteManualURL.tsx b/src/views/login/pronote/PronoteManualURL.tsx index b92c92fa7..c998bc1c6 100644 --- a/src/views/login/pronote/PronoteManualURL.tsx +++ b/src/views/login/pronote/PronoteManualURL.tsx @@ -12,7 +12,7 @@ import MaskStars from "@/components/FirstInstallation/MaskStars"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import { Check, Link2, TriangleAlert, X } from "lucide-react-native"; +import { Link2, TriangleAlert, Undo2, X } from "lucide-react-native"; import { useAlert } from "@/providers/AlertProvider"; import { NativeStackNavigationProp } from "@react-navigation/native-stack"; @@ -68,7 +68,7 @@ const PronoteManualURL: Screen<"PronoteManualURL"> = ({ route, navigation }) => }, { title: "Annuler", - icon: , + icon: , primary: true, backgroundColor: "#29947A", } diff --git a/src/views/login/pronote/PronoteWebview.tsx b/src/views/login/pronote/PronoteWebview.tsx index 738b1c4fa..c3aca2951 100644 --- a/src/views/login/pronote/PronoteWebview.tsx +++ b/src/views/login/pronote/PronoteWebview.tsx @@ -34,6 +34,7 @@ import extract_pronote_name from "@/utils/format/extract_pronote_name"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { animPapillon } from "@/utils/ui/animations"; import { useAlert } from "@/providers/AlertProvider"; +import { Undo2 } from "lucide-react-native"; const PronoteWebview: Screen<"PronoteWebview"> = ({ route, navigation }) => { const theme = useTheme(); @@ -403,8 +404,9 @@ const PronoteWebview: Screen<"PronoteWebview"> = ({ route, navigation }) => { actions: [ { title: "OK", + primary: true, + icon: , onPress: () => navigation.goBack(), - backgroundColor: theme.colors.card, }, ], }); diff --git a/src/views/login/skolengo/SkolengoWebview.tsx b/src/views/login/skolengo/SkolengoWebview.tsx index 6c8667761..86b28a7ec 100644 --- a/src/views/login/skolengo/SkolengoWebview.tsx +++ b/src/views/login/skolengo/SkolengoWebview.tsx @@ -27,6 +27,7 @@ import { Audio } from "expo-av"; import { authTokenToSkolengoTokenSet } from "@/services/skolengo/skolengo-types"; import { getSkolengoAccount } from "@/services/skolengo/skolengo-account"; import { wait } from "@/services/skolengo/data/utils"; +import { Undo2 } from "lucide-react-native"; // TODO : When the app is not started with Expo Go (so with a prebuild or a release build), use the expo auth-session module completely with the deeplink and without the webview. @@ -206,6 +207,8 @@ const SkolengoWebview: Screen<"SkolengoWebview"> = ({ route, navigation }) => { actions: [ { title: "OK", + primary: true, + icon: , onPress: () => navigation.goBack(), } ] diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index db14d8a61..d828b089f 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -3,7 +3,7 @@ import { Screen } from "@/router/helpers/types"; import { useCurrentAccount } from "@/stores/account"; import { useTheme } from "@react-navigation/native"; import * as ImagePicker from "expo-image-picker"; -import { Camera, ChevronDown, ChevronUp, TextCursorInput, User2, UserCircle2, WholeWord } from "lucide-react-native"; +import { Camera, ChevronDown, ChevronUp, TextCursorInput, Undo2, User2, UserCircle2, WholeWord } from "lucide-react-native"; import React, { useEffect, useRef, useState } from "react"; import { ActivityIndicator, Alert, Image, KeyboardAvoidingView, ScrollView, Switch, TextInput, Platform } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; @@ -74,6 +74,8 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { title: "OK", onPress: () => {}, backgroundColor: theme.colors.card, + primary: true, + icon: , }, ], }); diff --git a/src/views/settings/SettingsTabs.tsx b/src/views/settings/SettingsTabs.tsx index f0ebb16a7..f1a9463b5 100644 --- a/src/views/settings/SettingsTabs.tsx +++ b/src/views/settings/SettingsTabs.tsx @@ -16,6 +16,7 @@ import { Equal, SendToBack, Gift, + Undo2, } from "lucide-react-native"; import { NestableDraggableFlatList, @@ -453,6 +454,8 @@ const SettingsTabs = () => { title: "OK", onPress: () => {}, backgroundColor: theme.colors.card, + primary: true, + icon: , }, ], }); From 8d0cf11a5a454ea9692c35ecae6e6ca949b33448 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 17 Feb 2025 22:39:38 +0100 Subject: [PATCH 0670/1144] =?UTF-8?q?refactor:=20remplacer=20Alert=20par?= =?UTF-8?q?=20useAlert=20dans=20`GradeModal`=20+=20utilisation=20de=20la?= =?UTF-8?q?=20fonction=20`isExpoGo()`=20=C3=A0=20la=20place=20de=20Constan?= =?UTF-8?q?ts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Grades/GradeModal.tsx | 26 +++++++++++++------ .../account/Grades/Modals/GradeReaction.tsx | 5 ++-- src/views/settings/SettingsAbout.tsx | 4 +-- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/components/Grades/GradeModal.tsx b/src/components/Grades/GradeModal.tsx index fceb209b2..d8434983a 100644 --- a/src/components/Grades/GradeModal.tsx +++ b/src/components/Grades/GradeModal.tsx @@ -4,7 +4,6 @@ import { View, Image, Text, - Alert, ScrollView, StyleSheet, Dimensions @@ -18,10 +17,11 @@ import { PressableScale } from "react-native-pressable-scale"; import { NativeText } from "@/components/Global/NativeComponents"; import { Reel } from "@/services/shared/Reel"; import { captureRef } from "react-native-view-shot"; -import Constants from "expo-constants"; import Animated, { Easing, FadeInRight, ZoomIn } from "react-native-reanimated"; import PapillonBottomSheet from "@/components/Modals/PapillonBottomSheet"; import { useTheme } from "@react-navigation/native"; +import { useAlert } from "@/providers/AlertProvider"; +import { isExpoGo } from "@/utils/native/expoGoAlert"; interface GradeModalProps { isVisible: boolean; reel: Reel; @@ -70,10 +70,14 @@ const GradeModal: React.FC = ({ const stickersRef = React.useRef(null); const [showDeleteWarning, setShowDeleteWarning] = React.useState(false); + const { showAlert } = useAlert(); + const shareToSocial = async (option: ShareOptions) => { - const isExpoGo = Constants.appOwnership === "expo"; - if (isExpoGo) { - Alert.alert("Fonctionnalité indisponible", "Cette fonctionnalité n'est pas disponible dans Expo Go. Pour l'utiliser, tu peux tester l'application sur ton propre appareil."); + if (isExpoGo()) { + showAlert({ + title: "Fonctionnalité indisponible", + message: "Cette fonctionnalité n'est pas disponible dans Expo Go. Pour l'utiliser, tu peux tester l'application sur ton propre appareil.", + }); return; } await require("react-native-share").default.shareSingle(option); @@ -85,7 +89,10 @@ const GradeModal: React.FC = ({ await FileSystem.writeAsStringAsync(fileUri, reel.image, { encoding: FileSystem.EncodingType.Base64 }); const asset = await MediaLibrary.createAssetAsync(fileUri); await MediaLibrary.createAlbumAsync("Download", asset, false); - Alert.alert("Image sauvegardée", "L'image a été sauvegardée dans ta galerie."); + showAlert({ + title: "Image sauvegardée", + message: "L'image a été sauvegardée dans ta galerie.", + }); } catch (error) { console.error("Failed to save image:", error); } @@ -332,8 +339,11 @@ const GradeModal: React.FC = ({ gap: 8, }} onPress={async () => { - if (Constants.appOwnership === "expo") { - Alert.alert("Fonctionnalité indisponible", "Cette fonctionnalité n'est pas disponible dans Expo Go. Pour l'utiliser, tu peux tester l'application sur ton propre appareil."); + if (isExpoGo()) { + showAlert({ + title: "Fonctionnalité indisponible", + message: "Cette fonctionnalité n'est pas disponible dans Expo Go. Pour l'utiliser, tu peux tester l'application sur ton propre appareil.", + }); return; } await require("react-native-share").default.open({ diff --git a/src/views/account/Grades/Modals/GradeReaction.tsx b/src/views/account/Grades/Modals/GradeReaction.tsx index 24103a474..d076d8979 100644 --- a/src/views/account/Grades/Modals/GradeReaction.tsx +++ b/src/views/account/Grades/Modals/GradeReaction.tsx @@ -12,7 +12,7 @@ import { Reel } from "@/services/shared/Reel"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { NativeText } from "@/components/Global/NativeComponents"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; -import Constants from "expo-constants"; +import { isExpoGo } from "@/utils/native/expoGoAlert"; // Types interface SubjectData { @@ -106,8 +106,7 @@ const GradeReaction: Screen<"GradeReaction"> = ({ navigation, route }) => { // Volume button to take picture useEffect(() => { - if (Constants.appOwnership === "expo") return; - + if (isExpoGo()) return; }, []); useLayoutEffect(() => { diff --git a/src/views/settings/SettingsAbout.tsx b/src/views/settings/SettingsAbout.tsx index 702266a44..5b35a161d 100644 --- a/src/views/settings/SettingsAbout.tsx +++ b/src/views/settings/SettingsAbout.tsx @@ -11,8 +11,8 @@ import PackageJSON from "../../../package.json"; import AboutContainerCard from "@/components/Settings/AboutContainerCard"; import * as Linking from "expo-linking"; import teams from "@/utils/data/teams.json"; -import Constants from "expo-constants"; import { getContributors, Contributor } from "@/utils/GetRessources/GetContribs"; +import { isExpoGo } from "@/utils/native/expoGoAlert"; const SettingsAbout: Screen<"SettingsAbout"> = ({ navigation }) => { const theme = useTheme(); @@ -188,7 +188,7 @@ const SettingsAbout: Screen<"SettingsAbout"> = ({ navigation }) => { Version de l'application - ver. {PackageJSON.version} {Constants.appOwnership === "expo" ? "(Expo Go)" : ""} {__DEV__ ? "(debug)" : ""} + ver. {PackageJSON.version} {isExpoGo() ? "(Expo Go)" : ""} {__DEV__ ? "(debug)" : ""} Date: Tue, 18 Feb 2025 13:53:45 +0100 Subject: [PATCH 0671/1144] =?UTF-8?q?refactor:=20remplacer=20Alert=20par?= =?UTF-8?q?=20useAlert=20dans=20plusieurs=20composants=20pour=20une=20gest?= =?UTF-8?q?ion=20centralis=C3=A9e=20des=20alertes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/skolengo/skolengo-account.ts | 12 ++-- src/utils/native/expoGoAlert.ts | 11 +-- src/views/account/Chat/Modals/ChatCreate.tsx | 30 ++++---- .../account/Grades/Modals/GradeReaction.tsx | 20 ++++-- src/views/account/Homeworks/Document.tsx | 35 +++++---- .../Lessons/Options/LessonsImportIcal.tsx | 71 ++++++++++++------- src/views/account/Restaurant/Menu.tsx | 9 ++- src/views/addon/AddonPage.tsx | 10 ++- .../actions/BackgroundIUTLannion.tsx | 59 ++++++++++----- src/views/login/pronote/PronoteQRCode.tsx | 15 +++- src/views/login/pronote/PronoteV6Import.tsx | 30 ++++---- src/views/login/pronote/PronoteWebview.tsx | 38 ++++------ .../ExternalAccount/IzlyActivation.tsx | 42 ++++++++--- .../settings/ExternalAccount/PriceError.tsx | 10 ++- .../TurboselfAccountSelector.tsx | 42 ++++++++--- src/views/settings/Settings.tsx | 51 ++++--------- src/views/settings/SettingsAddons.tsx | 9 ++- src/views/settings/SettingsDevLogs.tsx | 63 ++++++---------- .../settings/SettingsExternalServices.tsx | 54 ++++++++------ src/views/settings/SettingsFlags.tsx | 30 +++++--- src/views/settings/SettingsMultiService.tsx | 36 ++++++---- .../settings/SettingsMultiServiceSpace.tsx | 30 ++++---- src/views/settings/SettingsProfile.tsx | 36 ++++------ src/views/settings/SettingsSubjects.tsx | 6 +- src/views/settings/SettingsTabs.tsx | 31 +++----- src/views/welcome/AccountSelector.old.tsx | 63 ++++++++-------- src/views/welcome/DevMenu.tsx | 30 ++++---- 27 files changed, 507 insertions(+), 366 deletions(-) diff --git a/src/services/skolengo/skolengo-account.ts b/src/services/skolengo/skolengo-account.ts index 6d73da973..f3a2c953c 100644 --- a/src/services/skolengo/skolengo-account.ts +++ b/src/services/skolengo/skolengo-account.ts @@ -4,17 +4,19 @@ import { DiscoveryDocument } from "expo-auth-session"; import { SkolengoAccount, AccountService } from "@/stores/account/types"; import axios, { type AxiosResponse } from "axios"; import { decode as b64decode, encode as b64encode} from "js-base64"; -import { Alert } from "react-native"; import { decode as htmlDecode } from "html-entities"; import { useCurrentAccount } from "@/stores/account"; import defaultSkolengoPersonalization from "./default-personalization"; import { User } from "scolengo-api/types/models/Common"; +import { useAlert } from "@/providers/AlertProvider"; const getSkolengoAxiosInstance = () => { const axioss = axios.create({ baseURL: BASE_URL }); + const { showAlert } = useAlert(); + axioss.interceptors.response.use((r: AxiosResponse) => r, (error)=>{ if(error.response?.data?.errors?.find((e:any)=>e.title.includes("PRONOTE_RESOURCES"))) return Promise.resolve(error); @@ -29,10 +31,10 @@ const getSkolengoAxiosInstance = () => { // if unknown error, don't display the error message if(!e["title"] || e["title"] === "FORBIDDEN") return; - Alert.alert( - "Skolengo - " + (e["title"].toString() || "Erreur"), - htmlDecode(e["detail"]?.toString().replace(/<(\/)?([a-z0-9]+)>/g, "") || "Erreur inconnue")+"\n\nSi cette erreur persiste, contacte les équipes de Papillon.", - ); + showAlert({ + title: "Skolengo - " + (e["title"].toString() || "Erreur"), + message: htmlDecode(e["detail"]?.toString().replace(/<(\/)?([a-z0-9]+)>/g, "") || "Erreur inconnue")+"\n\nSi cette erreur persiste, contacte les équipes de Papillon.", + }); }); return Promise.reject(error); }); diff --git a/src/utils/native/expoGoAlert.ts b/src/utils/native/expoGoAlert.ts index 9eca89860..c13f52164 100644 --- a/src/utils/native/expoGoAlert.ts +++ b/src/utils/native/expoGoAlert.ts @@ -1,13 +1,14 @@ -import { Alert } from "react-native"; import Constants, { ExecutionEnvironment } from "expo-constants"; +import { useAlert } from "@/providers/AlertProvider"; export const isExpoGo = () => { return Constants.executionEnvironment !== ExecutionEnvironment.Bare; }; export const alertExpoGo = async () => { - Alert.alert( - "Tu développes à l'aide d'Expo Go", - "Sous Expo Go, les appels aux API natives sont indisponibles. Utilise un build de développement pour accéder à toutes les fonctionnalités." - ); + const { showAlert } = useAlert(); + showAlert({ + title: "Tu développes à l'aide d'Expo Go", + message: "Sous Expo Go, les appels aux API natives sont indisponibles. Utilise un build de développement pour accéder à toutes les fonctionnalités.", + }); }; diff --git a/src/views/account/Chat/Modals/ChatCreate.tsx b/src/views/account/Chat/Modals/ChatCreate.tsx index 9e35c3059..c7f5a1396 100644 --- a/src/views/account/Chat/Modals/ChatCreate.tsx +++ b/src/views/account/Chat/Modals/ChatCreate.tsx @@ -6,7 +6,6 @@ import { StyleSheet, ActivityIndicator, Platform, - Alert, } from "react-native"; import { useTheme } from "@react-navigation/native"; @@ -20,7 +19,7 @@ import { import { useCurrentAccount } from "@/stores/account"; import { Recipient } from "@/services/shared/Recipient"; import { createDiscussion, createDiscussionRecipients } from "@/services/chats"; -import { PenTool, Tag } from "lucide-react-native"; +import { PenTool, Send, Tag, Undo2 } from "lucide-react-native"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; import PapillonCheckbox from "@/components/Global/PapillonCheckbox"; import { getProfileColorByName } from "@/services/local/default-personalization"; @@ -28,6 +27,7 @@ import InitialIndicator from "@/components/News/InitialIndicator"; import parse_initials from "@/utils/format/format_pronote_initials"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import Reanimated, { FadeIn, FadeOut } from "react-native-reanimated"; +import { useAlert } from "@/providers/AlertProvider"; const ChatCreate: Screen<"ChatCreate"> = ({ navigation }) => { const theme = useTheme(); @@ -63,6 +63,8 @@ const ChatCreate: Screen<"ChatCreate"> = ({ navigation }) => { ]); }; + const { showAlert } = useAlert(); + return ( = ({ navigation }) => { 0)} onPress={() => { if (!subject) { - Alert.alert( - "Veux-tu continuer sans objet ?", - "Tu es sur le point de créer une discussion sans objet. Veux-tu continuer ?", - [ + showAlert({ + title: "Veux-tu continuer sans objet ?", + message: "Tu es sur le point de créer une discussion sans objet. Veux-tu continuer ?", + actions: [ { - text: "Annuler", - style: "cancel", + title: "Annuler", + icon: , + primary: true, + backgroundColor: theme.colors.primary, }, { - text: "Continuer", + title: "Continuer", + icon: , onPress: () => { createDiscussion(account, "Aucun objet", content, selectedRecipients); navigation.goBack(); }, - }, - ], - { cancelable: true } - ); + } + ] + }); } else { createDiscussion(account, subject, content, selectedRecipients); navigation.goBack(); diff --git a/src/views/account/Grades/Modals/GradeReaction.tsx b/src/views/account/Grades/Modals/GradeReaction.tsx index d076d8979..e939beade 100644 --- a/src/views/account/Grades/Modals/GradeReaction.tsx +++ b/src/views/account/Grades/Modals/GradeReaction.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useLayoutEffect, useRef, useState } from "react"; -import { Text, View, Linking, StyleSheet, TouchableOpacity, Image, Alert } from "react-native"; +import { Text, View, Linking, StyleSheet, TouchableOpacity, Image } from "react-native"; import { CameraView, useCameraPermissions, PermissionStatus } from "expo-camera"; import * as MediaLibrary from "expo-media-library"; import { Check, X } from "lucide-react-native"; @@ -13,6 +13,7 @@ import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { NativeText } from "@/components/Global/NativeComponents"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { isExpoGo } from "@/utils/native/expoGoAlert"; +import { useAlert } from "@/providers/AlertProvider"; // Types interface SubjectData { @@ -94,6 +95,8 @@ const GradeReaction: Screen<"GradeReaction"> = ({ navigation, route }) => { } }; + const { showAlert } = useAlert(); + // Setup permissions useEffect(() => { setupPermissions(); @@ -124,7 +127,10 @@ const GradeReaction: Screen<"GradeReaction"> = ({ navigation, route }) => { const handleCapture = async () => { if (cameraPermission?.status !== PermissionStatus.GRANTED) { - Alert.alert("Permission Error", "Camera permission not granted"); + showAlert({ + title: "Accès à la caméra", + message: "L'autorisation d'accès à la caméra n'a pas été acceptée." + }); return; } @@ -153,14 +159,20 @@ const GradeReaction: Screen<"GradeReaction"> = ({ navigation, route }) => { navigation.goBack(); } catch (error) { console.error("Failed to save image:", error); - Alert.alert("Erreur", "Erreur lors de l'enregistrement de l'image"); + showAlert({ + title: "Erreur", + message: "Erreur lors de l'enregistrement de l'image", + }); } finally { setIsLoading(false); } }, 1000); } catch (error) { console.error("Failed to take picture:", error); - Alert.alert("Error", "Failed to capture image"); + showAlert({ + title: "Erreur", + message: "Impossible de capturer l'image." + }); } }; diff --git a/src/views/account/Homeworks/Document.tsx b/src/views/account/Homeworks/Document.tsx index 71b1c2c78..55b3702df 100644 --- a/src/views/account/Homeworks/Document.tsx +++ b/src/views/account/Homeworks/Document.tsx @@ -11,7 +11,6 @@ import { ScrollView, Text, TouchableOpacity, - Alert, Platform, StyleSheet, } from "react-native"; @@ -31,6 +30,7 @@ import { Paperclip, CircleAlert } from "lucide-react-native"; import LinkFavicon, { getURLDomain } from "@/components/Global/LinkFavicon"; import { timestampToString } from "@/utils/format/DateHelper"; import parse_homeworks from "@/utils/format/format_pronote_homeworks"; +import { useAlert } from "@/providers/AlertProvider"; const HomeworksDocument: Screen<"HomeworksDocument"> = ({ route }) => { const theme = useTheme(); @@ -81,6 +81,7 @@ const HomeworksDocument: Screen<"HomeworksDocument"> = ({ route }) => { fetchSubjectData(); }, [homework.subject]); + const { showAlert } = useAlert(); return ( @@ -124,18 +125,26 @@ const HomeworksDocument: Screen<"HomeworksDocument"> = ({ route }) => { > { - Alert.alert( - homework.returnType === "file_upload" - ? "Tu dois rendre ce devoir sur ton ENT" - : homework.returnType === "paper" - ? "Tu dois rendre ce devoir en classe" - : "Ce devoir est à rendre", - homework.returnType === "file_upload" - ? "Papillon ne permet pas de rendre des devoirs sur l'ENT. Tu dois le faire sur l'ENT de ton établissement" - : homework.returnType === "paper" - ? "Ton professeur t'indiquera comment rendre ce devoir" - : "Ton professeur t'indiquera comment rendre ce devoir", - ); + switch (homework.returnType) { + case "file_upload": + showAlert({ + title: "Tu dois rendre ce devoir sur ton ENT", + message: "Papillon ne permet pas de rendre des devoirs sur l'ENT. Tu dois le faire sur l'ENT de ton établissement", + }); + break; + case "paper": + showAlert({ + title: "Tu dois rendre ce devoir en classe", + message: "Ton professeur t'indiquera comment rendre ce devoir", + }); + break; + default: + showAlert({ + title: "Ce devoir est à rendre", + message: "Ton professeur t'indiquera comment rendre ce devoir.", + }); + break; + } }} > = ({ route, navigation }) = } }, [defaultIcal]); + const { showAlert } = useAlert(); + const saveIcal = async () => { setLoading(true); const oldUrls = account.personalization.icalURLs || []; @@ -82,7 +85,10 @@ const LessonsImportIcal: Screen<"LessonsImportIcal"> = ({ route, navigation }) = fetchIcalData(account); }) .catch(() => { - Alert.alert("Erreur", "Impossible de récupérer les données du calendrier. Vérifie l'URL et réessaye."); + showAlert({ + title: "Erreur", + message: "Impossible de récupérer les données du calendrier. Vérifie l'URL et réessaye.", + }); }) .finally(() => { setLoading(false); @@ -228,32 +234,43 @@ const LessonsImportIcal: Screen<"LessonsImportIcal"> = ({ route, navigation }) = key={index} icon={} onPress={() => { - Alert.alert(url.name, url.url, [ - { - text: "Annuler", - style: "cancel", - }, - { - text: "Copier l'URL", - onPress: () => { - Clipboard.setString(url.url); - Alert.alert("Copié", "L'URL a été copiée dans le presse-papiers."); + showAlert({ + title: url.name, + message: url.url, + actions: [ + { + title: "Annuler", + icon: , }, - }, - { - text: "Supprimer le calendrier", - style: "destructive", - onPress: () => { - useTimetableStore.getState().removeClassesFromSource("ical://"+url.url); - const urls = account.personalization.icalURLs || []; - urls.splice(index, 1); - mutateProperty("personalization", { - ...account.personalization, - icalURLs: urls, - }); + { + title: "Copier l'URL", + icon: , + onPress: () => { + Clipboard.setString(url.url); + showAlert({ + title: "Copié", + message: "L'URL a été copiée dans le presse-papiers.", + }); + }, + primary: true, + backgroundColor: theme.colors.primary, }, - }, - ]); + { + title: "Supprimer le calendrier", + icon: , + onPress: () => { + useTimetableStore.getState().removeClassesFromSource("ical://"+url.url); + const urls = account.personalization.icalURLs || []; + urls.splice(index, 1); + mutateProperty("personalization", { + ...account.personalization, + icalURLs: urls, + }); + }, + danger: true, + } + ] + }); }} > {url.name} diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index d040b6806..c9a0f8958 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -4,7 +4,6 @@ import { ScrollView, StyleSheet, Switch, - Alert, ActivityIndicator, RefreshControl, Text, @@ -54,6 +53,7 @@ import { Balance } from "@/services/shared/Balance"; import { ReservationHistory } from "@/services/shared/ReservationHistory"; import { STORE_THEMES, StoreTheme } from "./Cards/StoreThemes"; import MenuCard from "./Cards/Card"; +import { useAlert } from "@/providers/AlertProvider"; export const formatCardIdentifier = (identifier: string, dots: number = 4, separator: string = " ") => { if(!identifier) { @@ -97,6 +97,8 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { const [selectedIndex, setSelectedIndex] = useState(0); const [allCards, setAllCards] = useState | null>(null); + const { showAlert } = useAlert(); + const refreshData = async () => { setIsRefreshing(true); setRefreshCount(refreshCount + 1); @@ -174,7 +176,10 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { : term ); setAllBookings(revertedBookings ?? null); - Alert.alert("Erreur", "Une erreur est survenue lors de la réservation du repas"); + showAlert({ + title: "Erreur", + message: "Une erreur est survenue lors de la réservation du repas", + }); } }; diff --git a/src/views/addon/AddonPage.tsx b/src/views/addon/AddonPage.tsx index 12c9e69b2..23981f72a 100644 --- a/src/views/addon/AddonPage.tsx +++ b/src/views/addon/AddonPage.tsx @@ -1,9 +1,10 @@ import AddonsWebview from "@/components/Addons/AddonsWebview"; -import {Alert, View} from "react-native"; +import { View} from "react-native"; import {useSafeAreaInsets} from "react-native-safe-area-context"; import React from "react"; import {AddonPlacementManifest} from "@/addons/types"; import {Screen} from "@/router/helpers/types"; +import { useAlert } from "@/providers/AlertProvider"; const AddonPage: Screen<"AddonPage"> = ({ navigation, route }) => { const addon: AddonPlacementManifest = route.params?.addon; @@ -11,6 +12,8 @@ const AddonPage: Screen<"AddonPage"> = ({ navigation, route }) => { let data = route.params.data; const insets = useSafeAreaInsets(); + const { showAlert } = useAlert(); + React.useEffect(() => { navigation.setOptions({ title: addon.manifest.name @@ -41,7 +44,10 @@ const AddonPage: Screen<"AddonPage"> = ({ navigation, route }) => { } } if (index == -1) { - Alert.alert("Error", "The requested page was not found."); //TODO: transfer error to webview + showAlert({ + title: "Erreur", + message: "La page accédée n'a pas été trouvée.", + }); //TODO: transfer error to webview return; } else { let newAddon: AddonPlacementManifest = {manifest: addon.manifest, index: index}; diff --git a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx index 9f748729c..f3bb886cd 100644 --- a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx +++ b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx @@ -4,13 +4,15 @@ import { AccountService, Identity, LocalAccount } from "@/stores/account/types"; import uuid from "@/utils/uuid-v4"; import { useTheme } from "@react-navigation/native"; import React from "react"; -import { Alert, View } from "react-native"; +import { View } from "react-native"; import { WebView } from "react-native-webview"; import type { Screen } from "@/router/helpers/types"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { NativeText } from "@/components/Global/NativeComponents"; import { animPapillon } from "@/utils/ui/animations"; import { FadeInDown, FadeOutUp } from "react-native-reanimated"; +import { useAlert } from "@/providers/AlertProvider"; +import { Undo2 } from "lucide-react-native"; const providers = ["scodoc", "moodle", "ical"]; @@ -72,6 +74,8 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio const switchTo = useCurrentAccount(store => store.switchTo); const mutateProperty = useCurrentAccount(store => store.mutateProperty); + const { showAlert } = useAlert(); + const useData = async (data: any) => { if(firstLogin) { await actionFirstLogin(data); @@ -106,12 +110,18 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio } catch (e) { console.error(e); - Alert.alert( - "Erreur", - "Impossible de récupérer les notes de l'IUT de Lannion. Vérifie ta connexion internet et réessaye.", - [{ text: "OK", onPress: () => navigation.goBack() }] - ); - navigation.goBack(); + showAlert({ + title: "Erreur", + message: "Impossible de récupérer les notes du l'IUT de Lannion. Vérifie ta connexion Internet et réessaye.", + actions: [ + { + title: "OK", + icon: , + onPress: () => navigation.goBack(), + primary: true, + } + ] + }); } }; @@ -222,11 +232,18 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio const injectPassword = () => { if(redirectCount >= 2) { - Alert.alert( - "Erreur", - "Impossible de se connecter au portail de l'IUT de Lannion. Vérifie tes identifiants et réessaye.", - [{ text: "OK", onPress: () => navigation.goBack() }] - ); + showAlert({ + title: "Erreur", + message: "Impossible de se connecter au portail du l'IUT de Lannion. Vérifie tes identifiants et réessaye.", + actions: [ + { + title: "OK", + icon: , + onPress: () => navigation.goBack(), + primary: true, + } + ] + }); navigation.goBack(); return; } @@ -318,12 +335,18 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio onError={(data) => { console.error(data); - Alert.alert( - "Erreur", - "Impossible de se connecter au portail de l'IUT de Lannion. Vérifie ta connexion internet et réessaye.", - [{ text: "OK", onPress: () => navigation.goBack() }] - ); - navigation.goBack(); + showAlert({ + title: "Erreur", + message: "Impossible de se connecter au portail du l'IUT de Lannion. Vérifie ta connexion Internet et réessaye.", + actions: [ + { + title: "OK", + icon: , + onPress: () => navigation.goBack(), + primary: true, + } + ] + }); }} onMessage={(event) => { diff --git a/src/views/login/pronote/PronoteQRCode.tsx b/src/views/login/pronote/PronoteQRCode.tsx index 37ef05fd2..93b77eb90 100644 --- a/src/views/login/pronote/PronoteQRCode.tsx +++ b/src/views/login/pronote/PronoteQRCode.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { ActivityIndicator, Text, View, StyleSheet, Modal, Alert, KeyboardAvoidingView, TextInput, Pressable } from "react-native"; +import { ActivityIndicator, Text, View, StyleSheet, Modal, KeyboardAvoidingView, TextInput, Pressable } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { SafeAreaView, useSafeAreaInsets } from "react-native-safe-area-context"; import { useTheme } from "@react-navigation/native"; @@ -19,6 +19,7 @@ import { Account, AccountService } from "@/stores/account/types"; import { Audio } from "expo-av"; import defaultPersonalization from "@/services/pronote/default-personalization"; import extract_pronote_name from "@/utils/format/extract_pronote_name"; +import { useAlert } from "@/providers/AlertProvider"; const makeUUID = (): string => { let dt = new Date().getTime(); @@ -55,12 +56,17 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { const codeInput = React.createRef(); const [QRData, setQRData] = useState(null); + const { showAlert } = useAlert(); + async function loginQR () { setScanned(false); setLoadingModalVisible(true); if (QRValidationCode === "" || QRValidationCode.length !== 4) { - Alert.alert("Code invalide", "Entre un code à 4 chiffres."); + showAlert({ + title: "Code invalide", + message: "Entre un code à 4 chiffres.", + }); return; } @@ -142,7 +148,10 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { } catch (error) { console.error(error); - Alert.alert("Erreur", "Une erreur est survenue lors de la connexion."); + showAlert({ + title: "Erreur", + message: "Une erreur est survenue lors de la connexion.", + }); return; } } diff --git a/src/views/login/pronote/PronoteV6Import.tsx b/src/views/login/pronote/PronoteV6Import.tsx index 95f5b01b4..3f88c274a 100644 --- a/src/views/login/pronote/PronoteV6Import.tsx +++ b/src/views/login/pronote/PronoteV6Import.tsx @@ -1,9 +1,9 @@ import React from "react"; -import { View, StyleSheet, ActivityIndicator, Alert } from "react-native"; +import { View, StyleSheet, ActivityIndicator } from "react-native"; import { Screen } from "@/router/helpers/types"; import { ScrollView } from "react-native-gesture-handler"; import { NativeItem, NativeList, NativeText } from "@/components/Global/NativeComponents"; -import { Info } from "lucide-react-native"; +import { Info, PlugZap, Undo2 } from "lucide-react-native"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import pronote from "pawnote"; import { Account, AccountService } from "@/stores/account/types"; @@ -11,6 +11,7 @@ import defaultPersonalization from "@/services/pronote/default-personalization"; import { useAccounts, useCurrentAccount } from "@/stores/account"; import AsyncStorage from "@react-native-async-storage/async-storage"; import extract_pronote_name from "@/utils/format/extract_pronote_name"; +import { useAlert } from "@/providers/AlertProvider"; const PronoteV6Import: Screen<"PronoteV6Import"> = ({ route, navigation }) => { const { data } = route.params; @@ -20,6 +21,8 @@ const PronoteV6Import: Screen<"PronoteV6Import"> = ({ route, navigation }) => { const [loading, setLoading] = React.useState(false); + const { showAlert } = useAlert(); + const ResetImport = () => { navigation.pop(); }; @@ -98,25 +101,28 @@ const PronoteV6Import: Screen<"PronoteV6Import"> = ({ route, navigation }) => { } catch (error) { setLoading(false); - Alert.alert( - "Impossible de te reconnecter automatiquement", - "Tu peux cependant te connecter manuellement en indiquant ton identifiant et mot de passe.", - [ + showAlert({ + title: "Impossible de te reconnecter automatiquement", + message: "Tu peux cependant te connecter manuellement en indiquant ton identifiant et mot de passe.", + actions: [ { - text: "Se connecter manuellement", + title: "Se connecter manuellement", + icon: , onPress: async () => { navigation.pop(); await AsyncStorage.setItem("pronote:imported", "true"); navigation.navigate("PronoteManualURL", { url: data.instanceUrl }); - } + }, + primary: true, }, { - text: "Annuler", - style: "cancel", - onPress: ResetImport + title: "Annuler", + icon: , + onPress: ResetImport, + danger: true, } ] - ); + }); } }; diff --git a/src/views/login/pronote/PronoteWebview.tsx b/src/views/login/pronote/PronoteWebview.tsx index c3aca2951..9cfb03a6b 100644 --- a/src/views/login/pronote/PronoteWebview.tsx +++ b/src/views/login/pronote/PronoteWebview.tsx @@ -5,7 +5,6 @@ import { Dimensions, KeyboardAvoidingView, Platform, - Alert, } from "react-native"; import { WebView } from "react-native-webview"; @@ -385,32 +384,19 @@ const PronoteWebview: Screen<"PronoteWebview"> = ({ route, navigation }) => { setLoading(false); if (url.includes("pronote/mobile.eleve.html")) { if (!url.includes("identifiant")) { - if (Platform.OS === "ios") { - Alert.alert( - "Attention", - "Désolé, seuls les comptes élèves sont compatibles pour le moment.", - [ - { - text: "OK", - onPress: () => navigation.goBack(), - }, - ] - ); - } else { - showAlert({ - title: "Attention", - message: + showAlert({ + title: "Attention", + message: "Désolé, seuls les comptes élèves sont compatibles pour le moment.", - actions: [ - { - title: "OK", - primary: true, - icon: , - onPress: () => navigation.goBack(), - }, - ], - }); - } + actions: [ + { + title: "OK", + primary: true, + icon: , + onPress: () => navigation.goBack(), + }, + ], + }); } else { webViewRef.current?.injectJavaScript( INJECT_PRONOTE_INITIAL_LOGIN_HOOK diff --git a/src/views/settings/ExternalAccount/IzlyActivation.tsx b/src/views/settings/ExternalAccount/IzlyActivation.tsx index 49da237a6..6702d2b0c 100644 --- a/src/views/settings/ExternalAccount/IzlyActivation.tsx +++ b/src/views/settings/ExternalAccount/IzlyActivation.tsx @@ -2,7 +2,7 @@ import React, {useState, useEffect} from "react"; import type {Screen} from "@/router/helpers/types"; import {useTheme} from "@react-navigation/native"; import {SafeAreaView, useSafeAreaInsets} from "react-native-safe-area-context"; -import {Alert, Keyboard, KeyboardAvoidingView, StyleSheet, TouchableWithoutFeedback, View} from "react-native"; +import { Keyboard, KeyboardAvoidingView, StyleSheet, TouchableWithoutFeedback, View} from "react-native"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; import { NativeText,} from "@/components/Global/NativeComponents"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; @@ -12,6 +12,8 @@ import {useAccounts, useCurrentAccount} from "@/stores/account"; import uuid from "@/utils/uuid-v4"; import * as Linking from "expo-linking"; +import { useAlert } from "@/providers/AlertProvider"; +import { ArrowRightFromLine, Undo2 } from "lucide-react-native"; const IzlyActivation: Screen<"IzlyActivation"> = ({ navigation, route }) => { const theme = useTheme(); @@ -25,6 +27,8 @@ const IzlyActivation: Screen<"IzlyActivation"> = ({ navigation, route }) => { const secret = route.params?.password; const username = route.params?.username; + const { showAlert } = useAlert(); + useEffect(() => { const handleDeepLink = (event: { url: string }) => { const url = event.url; @@ -68,11 +72,18 @@ const IzlyActivation: Screen<"IzlyActivation"> = ({ navigation, route }) => { navigation.pop(); } catch (error) { if (error instanceof Error) { - Alert.alert("Erreur", error.message); + showAlert({ + title: "Erreur", + message: error.message, + }); } else { - Alert.alert("Erreur", "Une erreur est survenue lors de l'activation."); - } } + showAlert({ + title: "Erreur", + message: "Une erreur est survenue lors de l'activation.", + }); + } + } finally { setLoading(false); } @@ -115,10 +126,25 @@ const IzlyActivation: Screen<"IzlyActivation"> = ({ navigation, route }) => { (Alert.alert("Annuler", "Es-tu sûr de vouloir annuler l'activation ?", [ - { text: "Continuer l'activation", style: "cancel" }, - { text: "Confirmer", style: "destructive", onPress: () => navigation.pop() } - ]))} + onPress={() => { + showAlert({ + title: "Annuler", + message: "Es-tu sûr de vouloir annuler l'activation ?", + actions: [ + { + title: "Continuer l'activation", + icon: , + primary: true, + }, + { + title: "Confirmer", + icon: , + onPress: () => navigation.pop(), + danger: true, + } + ] + }); + }} /> diff --git a/src/views/settings/ExternalAccount/PriceError.tsx b/src/views/settings/ExternalAccount/PriceError.tsx index 7896010c7..11af323de 100644 --- a/src/views/settings/ExternalAccount/PriceError.tsx +++ b/src/views/settings/ExternalAccount/PriceError.tsx @@ -9,6 +9,7 @@ import { useAccounts } from "@/stores/account"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import {ExternalAccount} from "@/stores/account/types"; import {detectMealPrice as ARDPriceDetector} from "@/views/settings/ExternalAccount/ARD"; +import { useAlert } from "@/providers/AlertProvider"; type Props = { navigation: any; @@ -23,6 +24,8 @@ const PriceError: Screen<"PriceError"> = ({ navigation, route }) => { const account = route.params?.account; const accountId = route.params?.accountId; + const { showAlert } = useAlert(); + const manualInput = () => { Alert.prompt( "Entre le prix d'un repas", @@ -47,7 +50,12 @@ const PriceError: Screen<"PriceError"> = ({ navigation, route }) => { const reloadMealPrice = async () => { const mealPrice = await ARDPriceDetector(account); - if (!mealPrice) return Alert.alert("Erreur", "Impossible de déterminer le prix d'un repas"); + if (!mealPrice) { + return showAlert({ + title: "Erreur", + message: "Impossible de déterminer le prix d'un repas", + }); + } update(accountId, "authentication", { "mealPrice": mealPrice }); navigation.pop(); navigation.pop(); diff --git a/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx b/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx index f575a2c5a..b3cb14f20 100644 --- a/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx +++ b/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx @@ -2,7 +2,7 @@ import React, { useState } from "react"; import type {Screen} from "@/router/helpers/types"; import {useTheme} from "@react-navigation/native"; import {SafeAreaView, useSafeAreaInsets} from "react-native-safe-area-context"; -import {Alert, Keyboard, KeyboardAvoidingView, StyleSheet, TouchableWithoutFeedback, View} from "react-native"; +import { Keyboard, KeyboardAvoidingView, StyleSheet, TouchableWithoutFeedback, View} from "react-native"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; import { NativeText } from "@/components/Global/NativeComponents"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; @@ -13,6 +13,8 @@ import uuid from "@/utils/uuid-v4"; import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import { authenticateWithCredentials } from "turboself-api"; +import { useAlert } from "@/providers/AlertProvider"; +import { ArrowRightFromLine, Undo2 } from "lucide-react-native"; const TurboselfAccountSelector: Screen<"TurboselfAccountSelector"> = ({ navigation, route }) => { const theme = useTheme(); @@ -28,6 +30,8 @@ const TurboselfAccountSelector: Screen<"TurboselfAccountSelector"> = ({ navigati const password = route.params.password; const [choosenHostId, setChoosenHostId] = useState(account[0].id); + const { showAlert } = useAlert(); + const handleLogin = async (): Promise => { try { setLoading(true); @@ -52,11 +56,18 @@ const TurboselfAccountSelector: Screen<"TurboselfAccountSelector"> = ({ navigati navigation.pop(); } catch (error) { if (error instanceof Error) { - Alert.alert("Erreur", error.message); + showAlert({ + title: "Erreur", + message: error.message, + }); } else { - Alert.alert("Erreur", "Une erreur est survenue lors de la connexion."); - } } + showAlert({ + title: "Erreur", + message: "Une erreur est survenue lors de la connexion.", + }); + } + } finally { setLoading(false); } @@ -123,10 +134,25 @@ const TurboselfAccountSelector: Screen<"TurboselfAccountSelector"> = ({ navigati primary={false} value="Annuler" disabled={loading} - onPress={() => (Alert.alert("Annuler", "Es-tu sûr de vouloir annuler la connexion ?", [ - { text: "Continuer la connexion", style: "cancel" }, - { text: "Confirmer", style: "destructive", onPress: () => navigation.pop() } - ]))} + onPress={() => { + showAlert({ + title: "Annuler", + message: "Es-tu sûr de vouloir annuler la connexion ?", + actions: [ + { + title: "Continuer la connexion", + icon: , + primary: true, + }, + { + title: "Confirmer", + icon: , + onPress: () => navigation.pop(), + danger: true, + } + ] + }); + }} /> diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index 3a841f8a2..3370ac721 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -1,6 +1,6 @@ import type { Screen } from "@/router/helpers/types"; import React, { useEffect, useLayoutEffect, useState } from "react"; -import { Alert, Image, Platform, Text, View } from "react-native"; +import { Image, Platform, Text, View } from "react-native"; import { useAccounts, useCurrentAccount } from "@/stores/account"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import PackageJSON from "../../../package.json"; @@ -246,15 +246,17 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { color: "#CF0029", label: "Se déconnecter", onPress: () => { - if (Platform.OS === "ios") { - Alert.alert("Se déconnecter", "Es-tu sûr de vouloir te déconnecter ?", [ + showAlert({ + title: "Se déconnecter", + message: "Es-tu sûr de vouloir te déconnecter ?", + actions: [ { - text: "Annuler", - style: "cancel", + title: "Annuler", + backgroundColor: colors.card, + icon: , }, { - text: "Se déconnecter", - style: "destructive", + title: "Se déconnecter", onPress: () => { removeAccount(account.localID); navigation.reset({ @@ -262,36 +264,13 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { routes: [{ name: "AccountSelector" }], }); }, + primary: true, + backgroundColor: "#CF0029", + icon: , }, - ]); - } else { - showAlert({ - title: "Se déconnecter", - message: "Es-tu sûr de vouloir te déconnecter ?", - actions: [ - { - title: "Annuler", - onPress: () => {}, - backgroundColor: colors.card, - icon: , - }, - { - title: "Se déconnecter", - onPress: () => { - removeAccount(account.localID); - navigation.reset({ - index: 0, - routes: [{ name: "AccountSelector" }], - }); - }, - primary: true, - backgroundColor: "#CF0029", - icon: , - }, - ], - }); - } - }, + ], + }); + } }, ] } diff --git a/src/views/settings/SettingsAddons.tsx b/src/views/settings/SettingsAddons.tsx index c51bf57da..ab68d0de2 100644 --- a/src/views/settings/SettingsAddons.tsx +++ b/src/views/settings/SettingsAddons.tsx @@ -10,7 +10,6 @@ import { ScrollView, Image, Switch, - Alert, Platform, Modal, } from "react-native"; @@ -36,6 +35,7 @@ import {PressableScale} from "react-native-pressable-scale"; import * as Linking from "expo-linking"; import * as FileSystem from "expo-file-system"; import {AddonManifest} from "@/addons/types"; +import { useAlert } from "@/providers/AlertProvider"; const SettingsAddons: Screen<"SettingsAddons"> = () => { let [ opened, setOpened ] = React.useState(false); @@ -63,6 +63,8 @@ const SettingsAddons: Screen<"SettingsAddons"> = () => { }, []); const insets = useSafeAreaInsets(); + const { showAlert } = useAlert(); + return ( = () => { trailing={addon.error && ( { - Alert.alert(`Impossible de charger le plugin "${addon.name}"`, addon.error); + showAlert({ + title: `Impossible de charger le plugin "${addon.name}"`, + message: addon.error ?? "Erreur inconnue", + }); }} > = ({ navigation }) => { logs.length > 0 && ( { - if (Platform.OS === "ios") { - Alert.alert( - "Supprimer les logs ?", - "Es-tu sûr de vouloir supprimer toutes les logs ?", [ - { - text: "Annuler", - style: "cancel", + showAlert({ + title: "Supprimer les logs ?", + message: "Es-tu sûr de vouloir supprimer toutes les logs ?", + actions: [ + { + title: "Annuler", + backgroundColor: colors.card, + icon: , + }, + { + title: "Supprimer", + primary: true, + onPress: () => { + delete_logs(); + setLogs([]); }, - { - text: "Supprimer", - style: "destructive", - onPress: () => { - delete_logs(); - setLogs([]); - }, - }, - ] - ); - } else { - showAlert({ - title: "Supprimer les logs ?", - message: "Es-tu sûr de vouloir supprimer toutes les logs ?", - actions: [ - { - title: "Annuler", - onPress: () => {}, - backgroundColor: colors.card, - icon: , - }, - { - title: "Supprimer", - primary: true, - onPress: () => { - delete_logs(); - setLogs([]); - }, - backgroundColor: "#CF0029", - icon: , - }, - ], - }); - } + backgroundColor: "#CF0029", + icon: , + }, + ], + }); }} style={{ padding: 5, diff --git a/src/views/settings/SettingsExternalServices.tsx b/src/views/settings/SettingsExternalServices.tsx index 4f9c14733..c2a8d33c3 100644 --- a/src/views/settings/SettingsExternalServices.tsx +++ b/src/views/settings/SettingsExternalServices.tsx @@ -1,8 +1,8 @@ import React from "react"; -import { ScrollView, View, Alert } from "react-native"; +import { ScrollView, View } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; -import { GraduationCap, Utensils, BookOpen, School, BookmarkMinus, Compass } from "lucide-react-native"; +import { GraduationCap, Utensils, BookOpen, School, BookmarkMinus, Compass, Check, Trash2, Undo2 } from "lucide-react-native"; import ExternalServicesContainerCard from "@/components/Settings/ExternalServicesContainerCard"; import { NativeList, @@ -13,6 +13,7 @@ import { } from "@/components/Global/NativeComponents"; import { AccountService } from "@/stores/account/types"; import { useAccounts } from "@/stores/account"; +import { useAlert } from "@/providers/AlertProvider"; const serviceConfig = { [AccountService.Pronote]: { icon: GraduationCap, name: "Pronote" }, @@ -36,6 +37,7 @@ const SettingsExternalServices: Screen<"SettingsExternalServices"> = ({ const theme = useTheme(); const accounts = useAccounts((state) => state.accounts); const removeAccount = useAccounts((state) => state.remove); + const { showAlert } = useAlert(); const getServiceIcon = (service: AccountService) => { const IconComponent = serviceConfig[service]?.icon || GraduationCap; @@ -52,33 +54,43 @@ const SettingsExternalServices: Screen<"SettingsExternalServices"> = ({ info += `Établissement : ${account.authentication.schoolID || "N. not"}\n`; - Alert.alert( - "Informations du compte", - info, - [ - { text: "OK", style: "cancel" }, + showAlert({ + title: "Informations du compte", + message: info, + actions: [ { - text: "Supprimer", - style: "destructive", - onPress: () => confirmDeleteAccount(account) - } + title: "OK", + icon: , + primary: true, + }, + { + title: "Supprimer", + icon: , + onPress: () => confirmDeleteAccount(account), + danger: true, + }, ] - ); + }); }; const confirmDeleteAccount = (account: any) => { - Alert.alert( - "Supprimer le compte", - "Es-tu sûr de vouloir supprimer ce compte ?", - [ - { text: "Annuler", style: "cancel" }, + showAlert({ + title: "Supprimer le compte", + message: "Es-tu sûr de vouloir supprimer ce compte ?", + actions: [ + { + title: "Annuler", + icon: , + primary: true, + }, { - text: "Supprimer", - style: "destructive", - onPress: () => removeAccount(account.localID) + title: "Supprimer", + icon: , + onPress: () => removeAccount(account.localID), + danger: true, } ] - ); + }); }; const filteredAccounts = accounts.filter((acc, index) => { diff --git a/src/views/settings/SettingsFlags.tsx b/src/views/settings/SettingsFlags.tsx index 23de90910..d636c7bf2 100644 --- a/src/views/settings/SettingsFlags.tsx +++ b/src/views/settings/SettingsFlags.tsx @@ -1,13 +1,14 @@ import React, { Fragment, useRef } from "react"; -import { ScrollView, TextInput, Alert, KeyboardAvoidingView, StyleSheet } from "react-native"; +import { ScrollView, TextInput, KeyboardAvoidingView, StyleSheet } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; -import { Code } from "lucide-react-native"; +import { Code, Trash2, Undo2 } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; import { useFlagsStore } from "@/stores/flags"; import { useCurrentAccount } from "@/stores/account"; import { AccountService } from "@/stores/account/types"; +import { useAlert } from "@/providers/AlertProvider"; const SettingsFlags: Screen<"SettingsFlags"> = ({ navigation }) => { const { flags, remove, set } = useFlagsStore(); @@ -17,6 +18,8 @@ const SettingsFlags: Screen<"SettingsFlags"> = ({ navigation }) => { const insets = useSafeAreaInsets(); const textInputRef = useRef(null); + const { showAlert } = useAlert(); + const isBase64Image = (str: string) => { return typeof str === "string" && str.startsWith("data:image/jpeg"); }; @@ -66,14 +69,23 @@ const SettingsFlags: Screen<"SettingsFlags"> = ({ navigation }) => { }; const confirmRemoveFlag = (flag: string) => { - Alert.alert( - "Supprimer le flag", - `Veux-tu vraiment supprimer le flag "${flag}" ?`, - [ - { text: "Annuler" }, - { text: "Supprimer", onPress: () => remove(flag), style: "destructive" } + showAlert({ + title: "Supprimer le flag", + message: `Veux-tu vraiment supprimer le flag "${flag}" ?`, + actions: [ + { + title: "Annuler", + icon: , + primary: true, + }, + { + title: "Supprimer", + icon: , + onPress: () => remove(flag), + danger: true, + } ] - ); + }); }; return ( diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index 93bee2f6e..fcce0144b 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -1,10 +1,10 @@ import React, {useRef, useState} from "react"; -import {Alert, Image, ScrollView, Switch, TextInput, View} from "react-native"; +import { Image, ScrollView, Switch, TextInput, View} from "react-native"; import {useTheme} from "@react-navigation/native"; import type {Screen} from "@/router/helpers/types"; import MultiServiceContainerCard from "@/components/Settings/MultiServiceContainerCard"; import {NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText} from "@/components/Global/NativeComponents"; -import {ImageIcon, PlugZap, Plus, Type} from "lucide-react-native"; +import {Check, ImageIcon, PlugZap, Plus, Type, Undo2} from "lucide-react-native"; import {useAccounts, useCurrentAccount} from "@/stores/account"; import {useMultiService} from "@/stores/multiService"; import BottomSheet from "@/components/Modals/PapillonBottomSheet"; @@ -18,6 +18,7 @@ import uuid from "@/utils/uuid-v4"; import {defaultProfilePicture} from "@/utils/ui/default-profile-picture"; import {defaultTabs} from "@/consts/DefaultTabs"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; +import { useAlert } from "@/providers/AlertProvider"; const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => { const theme = useTheme(); @@ -35,6 +36,9 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => const [loadingImage, setLoadingImage] = useState(false); const [selectedImage, setSelectedImage] = useState(null); + + const { showAlert } = useAlert(); + const selectPicture = async () => { setLoadingImage(true); @@ -56,7 +60,10 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => const createSpace = () => { if (spaceName == "") { - Alert.alert("Aucun titre défini", "Vous devez définir un titre à l'environnement multi service pour pouvoir le créer."); + showAlert({ + title: "Aucun titre défini", + message: "Tu dois définir un titre à l'environnement multi service pour pouvoir le créer.", + }); return; } @@ -146,23 +153,26 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => value={multiServiceEnabled ?? false} onValueChange={() => { if (multiServiceEnabled) { - Alert.alert( - "Attention", - "La désactivation du multi-service entrainera la suppression de vos environnement multi-services créés.", - [ + showAlert({ + title: "Attention", + message: "La désactivation du multi-service entrainera la suppression de tes environnement multi-services créés.", + actions: [ { - text: "Annuler", - style: "cancel" + title: "Annuler", + icon: , + primary: true, }, { - text: "Confirmer", - style: "destructive", + title: "Confirmer", + icon: , onPress: () => { deleteAllSpaces(); toggleMultiService(); - } + }, + danger: true, } - ]); + ] + }); } else { toggleMultiService(); } diff --git a/src/views/settings/SettingsMultiServiceSpace.tsx b/src/views/settings/SettingsMultiServiceSpace.tsx index 364ca1073..8c3c3216e 100644 --- a/src/views/settings/SettingsMultiServiceSpace.tsx +++ b/src/views/settings/SettingsMultiServiceSpace.tsx @@ -1,7 +1,6 @@ import React, {useRef, useState} from "react"; import { ActivityIndicator, - Alert, Image, KeyboardAvoidingView, Pressable, @@ -12,7 +11,7 @@ import { import {useTheme} from "@react-navigation/native"; import type {Screen} from "@/router/helpers/types"; import {NativeItem, NativeList, NativeListHeader, NativeText} from "@/components/Global/NativeComponents"; -import {Camera, ChevronDown, CircleAlert, TextCursorInput, Trash2, Type, User2} from "lucide-react-native"; +import {Camera, ChevronDown, CircleAlert, TextCursorInput, Trash2, Type, Undo2, User2} from "lucide-react-native"; import {useAccounts} from "@/stores/account"; import {AccountService, PrimaryAccount} from "@/stores/account/types"; import * as ImagePicker from "expo-image-picker"; @@ -26,6 +25,7 @@ import PapillonBottomSheet from "@/components/Modals/PapillonBottomSheet"; import * as Haptics from "expo-haptics"; import AccountItem from "@/components/Global/AccountItem"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; +import { useAlert } from "@/providers/AlertProvider"; const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ navigation, route }) => { const theme = useTheme(); @@ -78,24 +78,30 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga const [featureSelection, setFeatureSelection] = useState(MultiServiceFeature.Grades); const [featureSelectionName, setFeatureSelectionName] = useState(""); + const { showAlert } = useAlert(); + const deleteSpace = () => { - Alert.alert( - "Êtes-vous sur ?", - "Cette action entrainera la suppression de votre espace multi-service.", - [ + showAlert({ + title: "Es-tu sûr ?", + message: "Cette action entrainera la suppression de ton espace multi-service.", + actions: [ { - text: "Annuler", - style: "cancel" + title: "Annuler", + icon: , + primary: true, }, - { text: "Confirmer", - style: "destructive", + { + title: "Confirmer", + icon: , onPress: () => { accounts.remove(space.accountLocalID); deleteMultiServiceSpace(space.accountLocalID); navigation.goBack(); - } + }, + danger: true, } - ]); + ] + }); }; const openAccountSelector = (feature: MultiServiceFeature, name: string) => { diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index d828b089f..c36a22211 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -5,7 +5,7 @@ import { useTheme } from "@react-navigation/native"; import * as ImagePicker from "expo-image-picker"; import { Camera, ChevronDown, ChevronUp, TextCursorInput, Undo2, User2, UserCircle2, WholeWord } from "lucide-react-native"; import React, { useEffect, useRef, useState } from "react"; -import { ActivityIndicator, Alert, Image, KeyboardAvoidingView, ScrollView, Switch, TextInput, Platform } from "react-native"; +import { ActivityIndicator, Image, KeyboardAvoidingView, ScrollView, Switch, TextInput } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useAlert } from "@/providers/AlertProvider"; import * as Clipboard from "expo-clipboard"; @@ -59,27 +59,18 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { // If the image is undefined, an alert is displayed with an error message if (!img) { - if (Platform.OS === "ios") { - Alert.alert("Erreur", "Impossible de récupérer de la photo de profil", [ + showAlert({ + title: "Erreur", + message: "Impossible de récupérer de la photo de profil", + actions: [ { - text: "OK", + title: "OK", + backgroundColor: theme.colors.card, + primary: true, + icon: , }, - ]); - } else { - showAlert({ - title: "Erreur", - message: "Impossible de récupérer de la photo de profil", - actions: [ - { - title: "OK", - onPress: () => {}, - backgroundColor: theme.colors.card, - primary: true, - icon: , - }, - ], - }); - } + ], + }); } else { // If image available, update profile picture setProfilePic(img); @@ -366,7 +357,10 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { key={"identityData_"+index} onPress={async () => { await Clipboard.setStringAsync(item.value); - Alert.alert("Copié", "L'information a été copiée dans le presse-papier."); + showAlert({ + title: "Copié", + message: "L'informatio, a été copiée dans le presse-papier." + }); }} chevron={false} > diff --git a/src/views/settings/SettingsSubjects.tsx b/src/views/settings/SettingsSubjects.tsx index bbed830ea..a0c2b7b25 100644 --- a/src/views/settings/SettingsSubjects.tsx +++ b/src/views/settings/SettingsSubjects.tsx @@ -1,6 +1,5 @@ import React, { useEffect, useState, useCallback, useLayoutEffect, useMemo } from "react"; import { - Alert, FlatList, KeyboardAvoidingView, Text, @@ -233,7 +232,10 @@ const SettingsSubjects: Screen<"SettingsSubjects"> = ({ navigation }) => { handleSubjectTitleBlur(); // Update subject title when closing the bottom sheet } } else { - Alert.alert("Aucun émoji défini", "Tu dois définir un émoji pour cette matière avant de pouvoir quitter cette page."); + showAlert({ + title: "Aucun émoji défini", + message: "Tu dois définir un émoji pour cette matière avant de pouvoir quitter cette page.", + }); emojiInput.current?.focus(); } }} diff --git a/src/views/settings/SettingsTabs.tsx b/src/views/settings/SettingsTabs.tsx index f1a9463b5..65176605f 100644 --- a/src/views/settings/SettingsTabs.tsx +++ b/src/views/settings/SettingsTabs.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useLayoutEffect, useRef, useState } from "react"; -import { View, Switch, Platform, Alert } from "react-native"; +import { View, Switch } from "react-native"; import { NativeItem, NativeList, @@ -439,27 +439,18 @@ const SettingsTabs = () => { checked={item.enabled} onPress={() => { if (!item.enabled && tabs.filter(t => t.enabled).length === 5) { - if (Platform.OS === "ios") { - Alert.alert("Information", "Tu ne peux pas ajouter plus de 5 onglets sur la page d'accueil.", [ + showAlert({ + title: "Information", + message: "Tu ne peux pas ajouter plus de 5 onglets sur la page d'accueil.", + actions: [ { - text: "OK", + title: "OK", + backgroundColor: theme.colors.card, + primary: true, + icon: , }, - ]); - } else { - showAlert({ - title: "Information", - message: "Tu ne peux pas ajouter plus de 5 onglets sur la page d'accueil.", - actions: [ - { - title: "OK", - onPress: () => {}, - backgroundColor: theme.colors.card, - primary: true, - icon: , - }, - ], - }); - } + ], + }); } toggleTab(item.tab); }} diff --git a/src/views/welcome/AccountSelector.old.tsx b/src/views/welcome/AccountSelector.old.tsx index 217b5e13c..d4c7eb458 100644 --- a/src/views/welcome/AccountSelector.old.tsx +++ b/src/views/welcome/AccountSelector.old.tsx @@ -2,10 +2,9 @@ import {NativeItem, NativeList, NativeListHeader, NativeText} from "@/components import {useAccounts, useCurrentAccount} from "@/stores/account"; import {defaultProfilePicture} from "@/utils/ui/default-profile-picture"; import {useIsFocused, useTheme} from "@react-navigation/native"; -import {PlusIcon} from "lucide-react-native"; +import {PlusIcon, Trash2, Undo2} from "lucide-react-native"; import {useEffect, useState} from "react"; import { - Alert, Dimensions, Image, RefreshControl, @@ -40,6 +39,7 @@ import {PressableScale} from "react-native-pressable-scale"; import datasets from "@/consts/datasets.json"; import Animated from "react-native-reanimated"; import {PrimaryAccount} from "@/stores/account/types"; +import { useAlert } from "@/providers/AlertProvider"; // https://raw.githubusercontent.com/PapillonApp/datasets/refs/heads/main/illustrations/index.json @@ -82,6 +82,8 @@ const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { opacity: interpolate(scrollOffset.value, [0, 100], [0, 0.75], Extrapolation.CLAMP), })); + const { showAlert } = useAlert(); + useEffect(() => { if(!downloadedIllustrations) { updateIllustration(); @@ -388,39 +390,44 @@ const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { } onLongPress={async () => { // delete account - Alert.alert( - "Supprimer le compte", - "Es-tu sûr de vouloir supprimer ce compte ?", - [ + showAlert({ + title: "Supprimer le compte", + message: "Es-tu sûr de vouloir supprimer ce compte ?", + actions: [ { - text: "Annuler", - style: "cancel", + title: "Annuler", + icon: , + primary: true, }, { - text: "Supprimer", - style: "destructive", + title: "Supprimer", + icon: , onPress: () => { - Alert.alert( - "Es-tu sûr ?", - "Veux-tu supprimer définitivement " + account.studentName.first + " " + account.studentName.last + " ?", - [ - { - text: "Annuler", - style: "cancel", - }, - { - text: "Supprimer", - style: "destructive", - onPress: () => { - removeAccount(account.localID); + // setTimeout pour laisser le temps à la précédente alerte de s'enlever + setTimeout(() => { + showAlert({ + title: "Es-tu sûr ?", + message: `Veux-tu supprimer définitivement ${account.studentName.first} ${account.studentName.last} ?`, + actions: [ + { + title: "Annuler", + icon: , + primary: true, }, - }, - ] - ); + { + title: "Supprimer", + icon: , + onPress: () => removeAccount(account.localID), + danger: true, + } + ] + }); + }, 500); }, - }, + danger: true, + } ] - ); + }); }} onPress={async () => { if (currentAccount?.localID !== account.localID) { diff --git a/src/views/welcome/DevMenu.tsx b/src/views/welcome/DevMenu.tsx index 78813ac72..ef27bd3c8 100644 --- a/src/views/welcome/DevMenu.tsx +++ b/src/views/welcome/DevMenu.tsx @@ -1,14 +1,16 @@ import { useTheme } from "@react-navigation/native"; -import { ChevronRight } from "lucide-react-native"; +import { ChevronRight, Eraser, Undo2 } from "lucide-react-native"; import React, { useLayoutEffect } from "react"; -import { View, Text, TouchableOpacity, ScrollView, Alert } from "react-native"; +import { View, Text, TouchableOpacity, ScrollView } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; import AsyncStorage from "@react-native-async-storage/async-storage"; +import { useAlert } from "@/providers/AlertProvider"; const DevMenu: Screen<"DevMenu"> = ({ navigation }) => { const theme = useTheme(); const { colors } = theme; + const { showAlert } = useAlert(); // add button to header useLayoutEffect(() => { @@ -179,24 +181,26 @@ const DevMenu: Screen<"DevMenu"> = ({ navigation }) => { { - Alert.alert( - "Réinitialisation de Papillon", - "Es-tu sûr de vouloir réinitialiser toutes les données de l'application ?", - [ + showAlert({ + title: "Réinitialisation de Papillon", + message: "Es-tu sûr de vouloir réinitialiser toutes les données de l'application ?", + actions: [ { - text: "Annuler", - style: "cancel", + title: "Annuler", + icon: , + primary: true, }, { - text: "Réinitialiser", - style: "destructive", + title: "Réinitialiser", + icon: , onPress: () => { AsyncStorage.clear(); navigation.popToTop(); }, - }, - ], - ); + danger: true, + } + ] + }); }} > Date: Tue, 18 Feb 2025 14:05:42 +0100 Subject: [PATCH 0672/1144] fix(AlertProvider): corriger l'ordre d'appel de onPress et hideAlert dans le composant Pressable --- src/providers/AlertProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index 1c3f3aa2e..2bdc940cc 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -136,8 +136,8 @@ const AlertProvider = ({ children }: AlertProviderProps) => { { - onPress?.(); hideAlert(); + onPress?.(); }} style={({ pressed }) => [ styles.button, From 3bf0846ab70f1b8a9fcc1bb94c89aee2c0f15f0c Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 18 Feb 2025 14:07:01 +0100 Subject: [PATCH 0673/1144] =?UTF-8?q?fix(AlertProvider):=20supprimer=20le?= =?UTF-8?q?=20d=C3=A9lai=20dans=20la=20fonction=20hideAlert=20pour=20une?= =?UTF-8?q?=20gestion=20imm=C3=A9diate=20de=20l'alerte?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/providers/AlertProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index 2bdc940cc..c0387436a 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -73,8 +73,8 @@ const AlertProvider = ({ children }: AlertProviderProps) => { }; function hideAlert () { + setAlert(null); setVisible(false); - setTimeout(() => setAlert(null), 200); } return ( From 9b6a922675fbfa54b96c120ae7f4a0a7427e8acf Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 18 Feb 2025 14:10:12 +0100 Subject: [PATCH 0674/1144] =?UTF-8?q?fix(AlertProvider):=20ajuster=20le=20?= =?UTF-8?q?style=20des=20boutons=20pour=20g=C3=A9rer=20l'orientation=20(co?= =?UTF-8?q?lonne=20ou=20ligne)=20en=20fonction=20du=20nombre=20d'actions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/providers/AlertProvider.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index c0387436a..9871662f3 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -123,7 +123,17 @@ const AlertProvider = ({ children }: AlertProviderProps) => { - + 2 ? "column" : "row", + alignItems: "stretch", + }, + ]} + > {alert.actions?.map( ({ title, From 94d502d3bdb243ba48c45fe95ffafd47d4ca6f4f Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 18 Feb 2025 14:15:00 +0100 Subject: [PATCH 0675/1144] =?UTF-8?q?fix(AlertProvider):=20modifier=20la?= =?UTF-8?q?=20couleur=20de=20fond=20pour=20l'=C3=A9tat=20danger=20des=20al?= =?UTF-8?q?ertes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/providers/AlertProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index 9871662f3..d1e950898 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -163,7 +163,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { borderColor: "#CCC", borderWidth: 1, }, - danger && { backgroundColor: "#b62000" }, + danger && { backgroundColor: "#FC1E0D" }, { opacity: pressed ? 0.6 : 1 }, ]} > From 5c387468d77ea6e9235fc809062ec237ab87fdf7 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 18 Feb 2025 18:27:38 +0100 Subject: [PATCH 0676/1144] fix: suppression du background sur certains composants --- src/providers/AlertProvider.tsx | 3 +-- src/views/settings/SettingsProfile.tsx | 1 - src/views/settings/SettingsTabs.tsx | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index d1e950898..989f380c6 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -156,8 +156,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { : styles.notPrimaryButton, primary ? { - backgroundColor: - backgroundColor || colors.primary, + backgroundColor: backgroundColor ?? colors.primary, } : { borderColor: "#CCC", diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index c36a22211..5ef3325cd 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -65,7 +65,6 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { actions: [ { title: "OK", - backgroundColor: theme.colors.card, primary: true, icon: , }, diff --git a/src/views/settings/SettingsTabs.tsx b/src/views/settings/SettingsTabs.tsx index 65176605f..1659e329e 100644 --- a/src/views/settings/SettingsTabs.tsx +++ b/src/views/settings/SettingsTabs.tsx @@ -445,7 +445,6 @@ const SettingsTabs = () => { actions: [ { title: "OK", - backgroundColor: theme.colors.card, primary: true, icon: , }, From f9e00c977ff1ae2e1f74fe7e5824781537ef70ba Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 18 Feb 2025 19:32:31 +0100 Subject: [PATCH 0677/1144] =?UTF-8?q?feat:=20ajouter=20des=20ic=C3=B4nes?= =?UTF-8?q?=20aux=20alertes=20dans=20plusieurs=20composants?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Grades/GradeModal.tsx | 5 ++++- src/providers/AlertProvider.tsx | 4 ++-- .../pronote/determinate-authentication-view.tsx | 4 +++- .../{skolengo-account.ts => skolengo-account.tsx} | 2 ++ src/utils/native/{expoGoAlert.ts => expoGoAlert.tsx} | 2 ++ src/views/account/Chat/Modals/ChatCreate.tsx | 3 ++- src/views/account/Grades/Atoms/GradesScodocUE.tsx | 4 ++-- src/views/account/Grades/Graph/GradesAverage.tsx | 6 +++--- src/views/account/Grades/Modals/GradeReaction.tsx | 9 ++++++--- src/views/account/Homeworks/Document.tsx | 5 ++++- .../account/Lessons/Options/LessonsImportIcal.tsx | 5 ++++- src/views/account/Restaurant/Menu.tsx | 2 ++ src/views/addon/AddonPage.tsx | 2 ++ .../IdentityProvider/actions/BackgroundIUTLannion.tsx | 5 ++++- .../login/IdentityProvider/providers/UnivLimoges.tsx | 3 ++- src/views/login/ServiceSelector.tsx | 11 ----------- src/views/login/pronote/PronoteManualURL.tsx | 3 ++- src/views/login/pronote/PronoteQRCode.tsx | 4 +++- src/views/login/pronote/PronoteV6Import.tsx | 3 ++- src/views/login/pronote/PronoteWebview.tsx | 6 +++--- src/views/login/skolengo/SkolengoWebview.tsx | 3 ++- src/views/settings/ExternalAccount/IzlyActivation.tsx | 5 ++++- src/views/settings/ExternalAccount/PriceError.tsx | 3 ++- .../ExternalAccount/TurboselfAccountSelector.tsx | 5 ++++- src/views/settings/Settings.tsx | 4 +++- src/views/settings/SettingsAddons.tsx | 2 ++ src/views/settings/SettingsDevLogs.tsx | 2 ++ src/views/settings/SettingsExternalServices.tsx | 4 +++- src/views/settings/SettingsFlags.tsx | 3 ++- src/views/settings/SettingsIcons.tsx | 6 +++--- src/views/settings/SettingsMultiService.tsx | 4 +++- src/views/settings/SettingsMultiServiceSpace.tsx | 3 ++- src/views/settings/SettingsProfile.tsx | 6 ++++-- src/views/settings/SettingsSubjects.tsx | 4 +++- src/views/settings/SettingsTabs.tsx | 2 ++ src/views/welcome/AccountSelector.old.tsx | 4 +++- src/views/welcome/DevMenu.tsx | 3 ++- 37 files changed, 100 insertions(+), 51 deletions(-) rename src/services/skolengo/{skolengo-account.ts => skolengo-account.tsx} (98%) rename src/utils/native/{expoGoAlert.ts => expoGoAlert.tsx} (85%) diff --git a/src/components/Grades/GradeModal.tsx b/src/components/Grades/GradeModal.tsx index d8434983a..571e871c6 100644 --- a/src/components/Grades/GradeModal.tsx +++ b/src/components/Grades/GradeModal.tsx @@ -8,7 +8,7 @@ import { StyleSheet, Dimensions } from "react-native"; -import { Download, Trash2, Ellipsis } from "lucide-react-native"; +import { Download, Trash2, Ellipsis, OctagonX, ImageDown } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { BlurView } from "expo-blur"; import * as FileSystem from "expo-file-system"; @@ -77,6 +77,7 @@ const GradeModal: React.FC = ({ showAlert({ title: "Fonctionnalité indisponible", message: "Cette fonctionnalité n'est pas disponible dans Expo Go. Pour l'utiliser, tu peux tester l'application sur ton propre appareil.", + icon: , }); return; } @@ -92,6 +93,7 @@ const GradeModal: React.FC = ({ showAlert({ title: "Image sauvegardée", message: "L'image a été sauvegardée dans ta galerie.", + icon: , }); } catch (error) { console.error("Failed to save image:", error); @@ -343,6 +345,7 @@ const GradeModal: React.FC = ({ showAlert({ title: "Fonctionnalité indisponible", message: "Cette fonctionnalité n'est pas disponible dans Expo Go. Pour l'utiliser, tu peux tester l'application sur ton propre appareil.", + icon: , }); return; } diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index 989f380c6..a53695e2e 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -26,7 +26,7 @@ type AlertAction = { export type Alert = { title: string; message: string; - icon?: React.ReactElement | null; + icon: React.ReactElement; actions?: AlertAction[]; }; @@ -58,7 +58,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { const showAlert = ({ title, message, - icon = null, + icon, actions = [ { title: "Compris !", diff --git a/src/services/pronote/determinate-authentication-view.tsx b/src/services/pronote/determinate-authentication-view.tsx index 8cf3bcd77..60e4753de 100644 --- a/src/services/pronote/determinate-authentication-view.tsx +++ b/src/services/pronote/determinate-authentication-view.tsx @@ -1,6 +1,6 @@ import type { NativeStackNavigationProp } from "@react-navigation/native-stack"; import type { RouteParameters } from "@/router/helpers/types"; -import { KeyRound, LockKeyhole } from "lucide-react-native"; +import { BadgeX, KeyRound, LockKeyhole, PlugZap } from "lucide-react-native"; import pronote from "pawnote"; import {info, warn} from "@/utils/logger/logger"; import type { Alert } from "@/providers/AlertProvider"; @@ -38,6 +38,7 @@ const determinateAuthenticationView = async , }); return; @@ -60,6 +61,7 @@ const determinateAuthenticationView = async , actions: [ { title: "Identifiants", diff --git a/src/services/skolengo/skolengo-account.ts b/src/services/skolengo/skolengo-account.tsx similarity index 98% rename from src/services/skolengo/skolengo-account.ts rename to src/services/skolengo/skolengo-account.tsx index f3a2c953c..804d3999e 100644 --- a/src/services/skolengo/skolengo-account.ts +++ b/src/services/skolengo/skolengo-account.tsx @@ -9,6 +9,7 @@ import { useCurrentAccount } from "@/stores/account"; import defaultSkolengoPersonalization from "./default-personalization"; import { User } from "scolengo-api/types/models/Common"; import { useAlert } from "@/providers/AlertProvider"; +import { BadgeX } from "lucide-react-native"; const getSkolengoAxiosInstance = () => { const axioss = axios.create({ @@ -34,6 +35,7 @@ const getSkolengoAxiosInstance = () => { showAlert({ title: "Skolengo - " + (e["title"].toString() || "Erreur"), message: htmlDecode(e["detail"]?.toString().replace(/<(\/)?([a-z0-9]+)>/g, "") || "Erreur inconnue")+"\n\nSi cette erreur persiste, contacte les équipes de Papillon.", + icon: , }); }); return Promise.reject(error); diff --git a/src/utils/native/expoGoAlert.ts b/src/utils/native/expoGoAlert.tsx similarity index 85% rename from src/utils/native/expoGoAlert.ts rename to src/utils/native/expoGoAlert.tsx index c13f52164..5f95c2ac0 100644 --- a/src/utils/native/expoGoAlert.ts +++ b/src/utils/native/expoGoAlert.tsx @@ -1,5 +1,6 @@ import Constants, { ExecutionEnvironment } from "expo-constants"; import { useAlert } from "@/providers/AlertProvider"; +import { MonitorSmartphone } from "lucide-react-native"; export const isExpoGo = () => { return Constants.executionEnvironment !== ExecutionEnvironment.Bare; @@ -10,5 +11,6 @@ export const alertExpoGo = async () => { showAlert({ title: "Tu développes à l'aide d'Expo Go", message: "Sous Expo Go, les appels aux API natives sont indisponibles. Utilise un build de développement pour accéder à toutes les fonctionnalités.", + icon: }); }; diff --git a/src/views/account/Chat/Modals/ChatCreate.tsx b/src/views/account/Chat/Modals/ChatCreate.tsx index c7f5a1396..dea2a4bb2 100644 --- a/src/views/account/Chat/Modals/ChatCreate.tsx +++ b/src/views/account/Chat/Modals/ChatCreate.tsx @@ -19,7 +19,7 @@ import { import { useCurrentAccount } from "@/stores/account"; import { Recipient } from "@/services/shared/Recipient"; import { createDiscussion, createDiscussionRecipients } from "@/services/chats"; -import { PenTool, Send, Tag, Undo2 } from "lucide-react-native"; +import { BadgeHelp, PenTool, Send, Tag, Undo2 } from "lucide-react-native"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; import PapillonCheckbox from "@/components/Global/PapillonCheckbox"; import { getProfileColorByName } from "@/services/local/default-personalization"; @@ -179,6 +179,7 @@ const ChatCreate: Screen<"ChatCreate"> = ({ navigation }) => { showAlert({ title: "Veux-tu continuer sans objet ?", message: "Tu es sur le point de créer une discussion sans objet. Veux-tu continuer ?", + icon: , actions: [ { title: "Annuler", diff --git a/src/views/account/Grades/Atoms/GradesScodocUE.tsx b/src/views/account/Grades/Atoms/GradesScodocUE.tsx index 7f4c2b94a..51dc25959 100644 --- a/src/views/account/Grades/Atoms/GradesScodocUE.tsx +++ b/src/views/account/Grades/Atoms/GradesScodocUE.tsx @@ -60,9 +60,9 @@ const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: Prim height: 24, }} onPress={() => showAlert({ - icon: , title: "Unités d'enseignement", - message: `Les données, rangs et notes sont fournies par les services de ${account.identityProvider?.name}.` + message: `Les données, rangs et notes sont fournies par les services de ${account.identityProvider?.name}.`, + icon: , })} > = ({ const theoryAvgDisclaimer = useCallback(() => { showAlert({ - icon: , title: "Moyenne théorique", - message: "La moyenne théorique est calculée en prenant en compte toutes les moyennes de tes matières. Elle est donc purement indicative et ne reflète pas la réalité des différentes options ou variations." + message: "La moyenne théorique est calculée en prenant en compte toutes les moyennes de tes matières. Elle est donc purement indicative et ne reflète pas la réalité des différentes options ou variations.", + icon: , }); }, []); const estimatedAvgDisclaimer = useCallback(() => { showAlert({ - icon: , title: "Moyenne générale estimée", message: "L'estimation automatique des moyennes n'est pas une information exacte, mais une approximation qui essaye de s'en rapprocher un maximum.", + icon: , actions: [ { title: "En savoir plus", diff --git a/src/views/account/Grades/Modals/GradeReaction.tsx b/src/views/account/Grades/Modals/GradeReaction.tsx index e939beade..77571c328 100644 --- a/src/views/account/Grades/Modals/GradeReaction.tsx +++ b/src/views/account/Grades/Modals/GradeReaction.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useLayoutEffect, useRef, useState } from "react"; import { Text, View, Linking, StyleSheet, TouchableOpacity, Image } from "react-native"; import { CameraView, useCameraPermissions, PermissionStatus } from "expo-camera"; import * as MediaLibrary from "expo-media-library"; -import { Check, X } from "lucide-react-native"; +import { BadgeX, CameraOff, Check, X } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { captureRef } from "react-native-view-shot"; import { Screen } from "@/router/helpers/types"; @@ -129,7 +129,8 @@ const GradeReaction: Screen<"GradeReaction"> = ({ navigation, route }) => { if (cameraPermission?.status !== PermissionStatus.GRANTED) { showAlert({ title: "Accès à la caméra", - message: "L'autorisation d'accès à la caméra n'a pas été acceptée." + message: "L'autorisation d'accès à la caméra n'a pas été acceptée.", + icon: , }); return; } @@ -162,6 +163,7 @@ const GradeReaction: Screen<"GradeReaction"> = ({ navigation, route }) => { showAlert({ title: "Erreur", message: "Erreur lors de l'enregistrement de l'image", + icon: }); } finally { setIsLoading(false); @@ -171,7 +173,8 @@ const GradeReaction: Screen<"GradeReaction"> = ({ navigation, route }) => { console.error("Failed to take picture:", error); showAlert({ title: "Erreur", - message: "Impossible de capturer l'image." + message: "Impossible de capturer l'image.", + icon: , }); } }; diff --git a/src/views/account/Homeworks/Document.tsx b/src/views/account/Homeworks/Document.tsx index 55b3702df..f04dd5433 100644 --- a/src/views/account/Homeworks/Document.tsx +++ b/src/views/account/Homeworks/Document.tsx @@ -26,7 +26,7 @@ import { useCurrentAccount } from "@/stores/account"; import { AccountService } from "@/stores/account/types"; import getAndOpenFile from "@/utils/files/getAndOpenFile"; import { AutoFileIcon } from "@/components/Global/FileIcon"; -import { Paperclip, CircleAlert } from "lucide-react-native"; +import { Paperclip, CircleAlert, FileUp, School } from "lucide-react-native"; import LinkFavicon, { getURLDomain } from "@/components/Global/LinkFavicon"; import { timestampToString } from "@/utils/format/DateHelper"; import parse_homeworks from "@/utils/format/format_pronote_homeworks"; @@ -130,18 +130,21 @@ const HomeworksDocument: Screen<"HomeworksDocument"> = ({ route }) => { showAlert({ title: "Tu dois rendre ce devoir sur ton ENT", message: "Papillon ne permet pas de rendre des devoirs sur l'ENT. Tu dois le faire sur l'ENT de ton établissement", + icon: , }); break; case "paper": showAlert({ title: "Tu dois rendre ce devoir en classe", message: "Ton professeur t'indiquera comment rendre ce devoir", + icon: , }); break; default: showAlert({ title: "Ce devoir est à rendre", message: "Ton professeur t'indiquera comment rendre ce devoir.", + icon: , }); break; } diff --git a/src/views/account/Lessons/Options/LessonsImportIcal.tsx b/src/views/account/Lessons/Options/LessonsImportIcal.tsx index cf8b3dbe1..d22cfa1dd 100644 --- a/src/views/account/Lessons/Options/LessonsImportIcal.tsx +++ b/src/views/account/Lessons/Options/LessonsImportIcal.tsx @@ -3,7 +3,7 @@ import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/componen import { useCurrentAccount } from "@/stores/account"; import { useTimetableStore } from "@/stores/timetable"; import { useTheme } from "@react-navigation/native"; -import { Calendar, CalendarOff, ClipboardCopy, Info, QrCode, Undo2, X } from "lucide-react-native"; +import { BadgeInfo, BadgeX, Calendar, CalendarOff, ClipboardCopy, Info, QrCode, Undo2, X } from "lucide-react-native"; import React, { useEffect } from "react"; import { Modal, TextInput, TouchableOpacity, View } from "react-native"; import { ScrollView } from "react-native-gesture-handler"; @@ -88,6 +88,7 @@ const LessonsImportIcal: Screen<"LessonsImportIcal"> = ({ route, navigation }) = showAlert({ title: "Erreur", message: "Impossible de récupérer les données du calendrier. Vérifie l'URL et réessaye.", + icon: , }); }) .finally(() => { @@ -237,6 +238,7 @@ const LessonsImportIcal: Screen<"LessonsImportIcal"> = ({ route, navigation }) = showAlert({ title: url.name, message: url.url, + icon: , actions: [ { title: "Annuler", @@ -250,6 +252,7 @@ const LessonsImportIcal: Screen<"LessonsImportIcal"> = ({ route, navigation }) = showAlert({ title: "Copié", message: "L'URL a été copiée dans le presse-papiers.", + icon: , }); }, primary: true, diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index c9a0f8958..b2460f6b8 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -12,6 +12,7 @@ import { import { useTheme } from "@react-navigation/native"; import { AlertTriangle, + BadgeX, ChefHat, CookingPot, MapPin, @@ -179,6 +180,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { showAlert({ title: "Erreur", message: "Une erreur est survenue lors de la réservation du repas", + icon: , }); } }; diff --git a/src/views/addon/AddonPage.tsx b/src/views/addon/AddonPage.tsx index 23981f72a..043402916 100644 --- a/src/views/addon/AddonPage.tsx +++ b/src/views/addon/AddonPage.tsx @@ -5,6 +5,7 @@ import React from "react"; import {AddonPlacementManifest} from "@/addons/types"; import {Screen} from "@/router/helpers/types"; import { useAlert } from "@/providers/AlertProvider"; +import { BadgeX } from "lucide-react-native"; const AddonPage: Screen<"AddonPage"> = ({ navigation, route }) => { const addon: AddonPlacementManifest = route.params?.addon; @@ -47,6 +48,7 @@ const AddonPage: Screen<"AddonPage"> = ({ navigation, route }) => { showAlert({ title: "Erreur", message: "La page accédée n'a pas été trouvée.", + icon: , }); //TODO: transfer error to webview return; } else { diff --git a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx index f3bb886cd..c1c9fa3d8 100644 --- a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx +++ b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx @@ -12,7 +12,7 @@ import { NativeText } from "@/components/Global/NativeComponents"; import { animPapillon } from "@/utils/ui/animations"; import { FadeInDown, FadeOutUp } from "react-native-reanimated"; import { useAlert } from "@/providers/AlertProvider"; -import { Undo2 } from "lucide-react-native"; +import { BadgeX, Undo2 } from "lucide-react-native"; const providers = ["scodoc", "moodle", "ical"]; @@ -113,6 +113,7 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio showAlert({ title: "Erreur", message: "Impossible de récupérer les notes du l'IUT de Lannion. Vérifie ta connexion Internet et réessaye.", + icon: , actions: [ { title: "OK", @@ -235,6 +236,7 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio showAlert({ title: "Erreur", message: "Impossible de se connecter au portail du l'IUT de Lannion. Vérifie tes identifiants et réessaye.", + icon: , actions: [ { title: "OK", @@ -338,6 +340,7 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio showAlert({ title: "Erreur", message: "Impossible de se connecter au portail du l'IUT de Lannion. Vérifie ta connexion Internet et réessaye.", + icon: , actions: [ { title: "OK", diff --git a/src/views/login/IdentityProvider/providers/UnivLimoges.tsx b/src/views/login/IdentityProvider/providers/UnivLimoges.tsx index 450601ed2..65d228078 100644 --- a/src/views/login/IdentityProvider/providers/UnivLimoges.tsx +++ b/src/views/login/IdentityProvider/providers/UnivLimoges.tsx @@ -12,7 +12,7 @@ import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { NativeText } from "@/components/Global/NativeComponents"; import { log } from "@/utils/logger/logger"; import { useAlert } from "@/providers/AlertProvider"; -import { Check } from "lucide-react-native"; +import { BadgeX, Check } from "lucide-react-native"; const UnivLimoges_Login: Screen<"UnivLimoges_Login"> = ({ navigation }) => { const createStoredAccount = useAccounts(store => store.create); @@ -87,6 +87,7 @@ const UnivLimoges_Login: Screen<"UnivLimoges_Login"> = ({ navigation }) => { showAlert({ title: "Erreur lors de la connexion", message: "Une erreur est survenue lors de la connexion à ton compte Biome, réessaye plus tard.", + icon: , actions: [ { title: "OK", diff --git a/src/views/login/ServiceSelector.tsx b/src/views/login/ServiceSelector.tsx index beeeca622..46be3446e 100644 --- a/src/views/login/ServiceSelector.tsx +++ b/src/views/login/ServiceSelector.tsx @@ -9,7 +9,6 @@ import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBub import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import MaskStars from "@/components/FirstInstallation/MaskStars"; -import { useAlert } from "@/providers/AlertProvider"; import { Audio } from "expo-av"; import { useTheme } from "@react-navigation/native"; import GetV6Data from "@/utils/login/GetV6Data"; @@ -20,9 +19,6 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { const theme = useTheme(); const { colors } = theme; const [sound, setSound] = useState(null); - - const { showAlert } = useAlert(); - type Services = "pronote" | "ed" | "skolengo"; const [service, setService] = useState(null); @@ -85,13 +81,6 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { }, ]; - const UnsupportedAlert = () => { - showAlert({ - title: "Service non supporté", - message: "Désolé, ce service n'est pas encore supporté. Réessaye dans une prochaine version." - }); - }; - useEffect(() => { const loadSound = async () => { const { sound } = await Audio.Sound.createAsync( diff --git a/src/views/login/pronote/PronoteManualURL.tsx b/src/views/login/pronote/PronoteManualURL.tsx index c998bc1c6..4c253fcea 100644 --- a/src/views/login/pronote/PronoteManualURL.tsx +++ b/src/views/login/pronote/PronoteManualURL.tsx @@ -12,7 +12,7 @@ import MaskStars from "@/components/FirstInstallation/MaskStars"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import { Link2, TriangleAlert, Undo2, X } from "lucide-react-native"; +import { BadgeInfo, Link2, TriangleAlert, Undo2, X } from "lucide-react-native"; import { useAlert } from "@/providers/AlertProvider"; import { NativeStackNavigationProp } from "@react-navigation/native-stack"; @@ -59,6 +59,7 @@ const PronoteManualURL: Screen<"PronoteManualURL"> = ({ route, navigation }) => showAlert({ title: "Instance non prise en charge", message: "Désolé, les instances de démonstration ne sont pas prises en charge, elles peuvent être instables ou ne pas fonctionner correctement.", + icon: , actions: [ { title: "Continuer", diff --git a/src/views/login/pronote/PronoteQRCode.tsx b/src/views/login/pronote/PronoteQRCode.tsx index 93b77eb90..9703bceee 100644 --- a/src/views/login/pronote/PronoteQRCode.tsx +++ b/src/views/login/pronote/PronoteQRCode.tsx @@ -9,7 +9,7 @@ import * as Haptics from "expo-haptics"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; -import { QrCode } from "lucide-react-native"; +import { BadgeX, QrCode } from "lucide-react-native"; import Reanimated, { LinearTransition, FadeOutUp, FadeInUp } from "react-native-reanimated"; import pronote from "pawnote"; @@ -66,6 +66,7 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { showAlert({ title: "Code invalide", message: "Entre un code à 4 chiffres.", + icon: , }); return; } @@ -151,6 +152,7 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { showAlert({ title: "Erreur", message: "Une erreur est survenue lors de la connexion.", + icon: , }); return; } diff --git a/src/views/login/pronote/PronoteV6Import.tsx b/src/views/login/pronote/PronoteV6Import.tsx index 3f88c274a..26641068f 100644 --- a/src/views/login/pronote/PronoteV6Import.tsx +++ b/src/views/login/pronote/PronoteV6Import.tsx @@ -3,7 +3,7 @@ import { View, StyleSheet, ActivityIndicator } from "react-native"; import { Screen } from "@/router/helpers/types"; import { ScrollView } from "react-native-gesture-handler"; import { NativeItem, NativeList, NativeText } from "@/components/Global/NativeComponents"; -import { Info, PlugZap, Undo2 } from "lucide-react-native"; +import { BadgeX, Info, PlugZap, Undo2 } from "lucide-react-native"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import pronote from "pawnote"; import { Account, AccountService } from "@/stores/account/types"; @@ -104,6 +104,7 @@ const PronoteV6Import: Screen<"PronoteV6Import"> = ({ route, navigation }) => { showAlert({ title: "Impossible de te reconnecter automatiquement", message: "Tu peux cependant te connecter manuellement en indiquant ton identifiant et mot de passe.", + icon: , actions: [ { title: "Se connecter manuellement", diff --git a/src/views/login/pronote/PronoteWebview.tsx b/src/views/login/pronote/PronoteWebview.tsx index 9cfb03a6b..5bfa162d1 100644 --- a/src/views/login/pronote/PronoteWebview.tsx +++ b/src/views/login/pronote/PronoteWebview.tsx @@ -33,7 +33,7 @@ import extract_pronote_name from "@/utils/format/extract_pronote_name"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { animPapillon } from "@/utils/ui/animations"; import { useAlert } from "@/providers/AlertProvider"; -import { Undo2 } from "lucide-react-native"; +import { BadgeInfo, Undo2 } from "lucide-react-native"; const PronoteWebview: Screen<"PronoteWebview"> = ({ route, navigation }) => { const theme = useTheme(); @@ -386,8 +386,8 @@ const PronoteWebview: Screen<"PronoteWebview"> = ({ route, navigation }) => { if (!url.includes("identifiant")) { showAlert({ title: "Attention", - message: - "Désolé, seuls les comptes élèves sont compatibles pour le moment.", + message: "Désolé, seuls les comptes élèves sont compatibles pour le moment.", + icon: , actions: [ { title: "OK", diff --git a/src/views/login/skolengo/SkolengoWebview.tsx b/src/views/login/skolengo/SkolengoWebview.tsx index 86b28a7ec..85867dfe0 100644 --- a/src/views/login/skolengo/SkolengoWebview.tsx +++ b/src/views/login/skolengo/SkolengoWebview.tsx @@ -27,7 +27,7 @@ import { Audio } from "expo-av"; import { authTokenToSkolengoTokenSet } from "@/services/skolengo/skolengo-types"; import { getSkolengoAccount } from "@/services/skolengo/skolengo-account"; import { wait } from "@/services/skolengo/data/utils"; -import { Undo2 } from "lucide-react-native"; +import { BadgeX, Undo2 } from "lucide-react-native"; // TODO : When the app is not started with Expo Go (so with a prebuild or a release build), use the expo auth-session module completely with the deeplink and without the webview. @@ -204,6 +204,7 @@ const SkolengoWebview: Screen<"SkolengoWebview"> = ({ route, navigation }) => { showAlert({ title: "Erreur", message: "Impossible de récupérer le code d'authentification.", + icon: , actions: [ { title: "OK", diff --git a/src/views/settings/ExternalAccount/IzlyActivation.tsx b/src/views/settings/ExternalAccount/IzlyActivation.tsx index 6702d2b0c..33d55f575 100644 --- a/src/views/settings/ExternalAccount/IzlyActivation.tsx +++ b/src/views/settings/ExternalAccount/IzlyActivation.tsx @@ -13,7 +13,7 @@ import uuid from "@/utils/uuid-v4"; import * as Linking from "expo-linking"; import { useAlert } from "@/providers/AlertProvider"; -import { ArrowRightFromLine, Undo2 } from "lucide-react-native"; +import { ArrowRightFromLine, BadgeHelp, BadgeX, Undo2 } from "lucide-react-native"; const IzlyActivation: Screen<"IzlyActivation"> = ({ navigation, route }) => { const theme = useTheme(); @@ -75,12 +75,14 @@ const IzlyActivation: Screen<"IzlyActivation"> = ({ navigation, route }) => { showAlert({ title: "Erreur", message: error.message, + icon: , }); } else { showAlert({ title: "Erreur", message: "Une erreur est survenue lors de l'activation.", + icon: , }); } } @@ -130,6 +132,7 @@ const IzlyActivation: Screen<"IzlyActivation"> = ({ navigation, route }) => { showAlert({ title: "Annuler", message: "Es-tu sûr de vouloir annuler l'activation ?", + icon: , actions: [ { title: "Continuer l'activation", diff --git a/src/views/settings/ExternalAccount/PriceError.tsx b/src/views/settings/ExternalAccount/PriceError.tsx index 11af323de..e8c572623 100644 --- a/src/views/settings/ExternalAccount/PriceError.tsx +++ b/src/views/settings/ExternalAccount/PriceError.tsx @@ -1,7 +1,7 @@ import React from "react"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; -import { CircleHelp } from "lucide-react-native"; +import { BadgeX, CircleHelp } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import {View, StyleSheet, Text, Alert} from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; @@ -54,6 +54,7 @@ const PriceError: Screen<"PriceError"> = ({ navigation, route }) => { return showAlert({ title: "Erreur", message: "Impossible de déterminer le prix d'un repas", + icon: , }); } update(accountId, "authentication", { "mealPrice": mealPrice }); diff --git a/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx b/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx index b3cb14f20..0698a1032 100644 --- a/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx +++ b/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx @@ -14,7 +14,7 @@ import uuid from "@/utils/uuid-v4"; import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import { authenticateWithCredentials } from "turboself-api"; import { useAlert } from "@/providers/AlertProvider"; -import { ArrowRightFromLine, Undo2 } from "lucide-react-native"; +import { ArrowRightFromLine, BadgeHelp, BadgeX, Undo2 } from "lucide-react-native"; const TurboselfAccountSelector: Screen<"TurboselfAccountSelector"> = ({ navigation, route }) => { const theme = useTheme(); @@ -59,12 +59,14 @@ const TurboselfAccountSelector: Screen<"TurboselfAccountSelector"> = ({ navigati showAlert({ title: "Erreur", message: error.message, + icon: , }); } else { showAlert({ title: "Erreur", message: "Une erreur est survenue lors de la connexion.", + icon: , }); } } @@ -138,6 +140,7 @@ const TurboselfAccountSelector: Screen<"TurboselfAccountSelector"> = ({ navigati showAlert({ title: "Annuler", message: "Es-tu sûr de vouloir annuler la connexion ?", + icon: , actions: [ { title: "Continuer la connexion", diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index 3370ac721..f802a959b 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -35,7 +35,8 @@ import { WandSparkles, X, Blocks, - HelpCircle + HelpCircle, + BadgeHelp } from "lucide-react-native"; import { NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; @@ -249,6 +250,7 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { showAlert({ title: "Se déconnecter", message: "Es-tu sûr de vouloir te déconnecter ?", + icon: , actions: [ { title: "Annuler", diff --git a/src/views/settings/SettingsAddons.tsx b/src/views/settings/SettingsAddons.tsx index ab68d0de2..7f66435df 100644 --- a/src/views/settings/SettingsAddons.tsx +++ b/src/views/settings/SettingsAddons.tsx @@ -17,6 +17,7 @@ import React from "react"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import {useSafeAreaInsets} from "react-native-safe-area-context"; import { + BadgeX, Calendar, Camera, Carrot, Clock, Code, Cog, @@ -297,6 +298,7 @@ const SettingsAddons: Screen<"SettingsAddons"> = () => { showAlert({ title: `Impossible de charger le plugin "${addon.name}"`, message: addon.error ?? "Erreur inconnue", + icon: , }); }} > diff --git a/src/views/settings/SettingsDevLogs.tsx b/src/views/settings/SettingsDevLogs.tsx index 68994edf0..ce338b4b9 100644 --- a/src/views/settings/SettingsDevLogs.tsx +++ b/src/views/settings/SettingsDevLogs.tsx @@ -25,6 +25,7 @@ import { Calendar, Folder, X, + BadgeHelp, } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { @@ -91,6 +92,7 @@ const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { showAlert({ title: "Supprimer les logs ?", message: "Es-tu sûr de vouloir supprimer toutes les logs ?", + icon: , actions: [ { title: "Annuler", diff --git a/src/views/settings/SettingsExternalServices.tsx b/src/views/settings/SettingsExternalServices.tsx index c2a8d33c3..0e57e974b 100644 --- a/src/views/settings/SettingsExternalServices.tsx +++ b/src/views/settings/SettingsExternalServices.tsx @@ -2,7 +2,7 @@ import React from "react"; import { ScrollView, View } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; -import { GraduationCap, Utensils, BookOpen, School, BookmarkMinus, Compass, Check, Trash2, Undo2 } from "lucide-react-native"; +import { GraduationCap, Utensils, BookOpen, School, BookmarkMinus, Compass, Check, Trash2, Undo2, BadgeInfo, BadgeHelp } from "lucide-react-native"; import ExternalServicesContainerCard from "@/components/Settings/ExternalServicesContainerCard"; import { NativeList, @@ -57,6 +57,7 @@ const SettingsExternalServices: Screen<"SettingsExternalServices"> = ({ showAlert({ title: "Informations du compte", message: info, + icon: , actions: [ { title: "OK", @@ -77,6 +78,7 @@ const SettingsExternalServices: Screen<"SettingsExternalServices"> = ({ showAlert({ title: "Supprimer le compte", message: "Es-tu sûr de vouloir supprimer ce compte ?", + icon: , actions: [ { title: "Annuler", diff --git a/src/views/settings/SettingsFlags.tsx b/src/views/settings/SettingsFlags.tsx index d636c7bf2..e965b6d24 100644 --- a/src/views/settings/SettingsFlags.tsx +++ b/src/views/settings/SettingsFlags.tsx @@ -2,7 +2,7 @@ import React, { Fragment, useRef } from "react"; import { ScrollView, TextInput, KeyboardAvoidingView, StyleSheet } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; -import { Code, Trash2, Undo2 } from "lucide-react-native"; +import { BadgeHelp, Code, Trash2, Undo2 } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; import { useFlagsStore } from "@/stores/flags"; @@ -72,6 +72,7 @@ const SettingsFlags: Screen<"SettingsFlags"> = ({ navigation }) => { showAlert({ title: "Supprimer le flag", message: `Veux-tu vraiment supprimer le flag "${flag}" ?`, + icon: , actions: [ { title: "Annuler", diff --git a/src/views/settings/SettingsIcons.tsx b/src/views/settings/SettingsIcons.tsx index e52000994..ff3b445d9 100644 --- a/src/views/settings/SettingsIcons.tsx +++ b/src/views/settings/SettingsIcons.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from "react"; import { Text, ScrollView, View, TouchableOpacity, Image } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; -import { Info, Sparkles } from "lucide-react-native"; +import { BadgeInfo, Sparkles } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { NativeList, NativeItem, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; import IconsContainerCard from "@/components/Settings/IconsContainerCard"; @@ -110,9 +110,9 @@ const SettingsIcons: Screen<"SettingsIcons"> = ({ navigation }) => { }} onPress={() => { showAlert({ - icon: , title: "Icônes dynamiques", message: "Les icônes dynamiques changent de couleur en fonction de ton thème.", + icon: , }); }} > @@ -166,9 +166,9 @@ const SettingsIcons: Screen<"SettingsIcons"> = ({ navigation }) => { { showAlert({ - icon: , title: "Icônes dynamiques", message: "Les icônes dynamiques changent de couleur en fonction de ton thème.", + icon: , }); }} > diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index fcce0144b..4e6ecae58 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -4,7 +4,7 @@ import {useTheme} from "@react-navigation/native"; import type {Screen} from "@/router/helpers/types"; import MultiServiceContainerCard from "@/components/Settings/MultiServiceContainerCard"; import {NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText} from "@/components/Global/NativeComponents"; -import {Check, ImageIcon, PlugZap, Plus, Type, Undo2} from "lucide-react-native"; +import {BadgeInfo, Check, ImageIcon, PlugZap, Plus, ShieldAlert, Type, Undo2} from "lucide-react-native"; import {useAccounts, useCurrentAccount} from "@/stores/account"; import {useMultiService} from "@/stores/multiService"; import BottomSheet from "@/components/Modals/PapillonBottomSheet"; @@ -63,6 +63,7 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => showAlert({ title: "Aucun titre défini", message: "Tu dois définir un titre à l'environnement multi service pour pouvoir le créer.", + icon: , }); return; } @@ -156,6 +157,7 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => showAlert({ title: "Attention", message: "La désactivation du multi-service entrainera la suppression de tes environnement multi-services créés.", + icon: , actions: [ { title: "Annuler", diff --git a/src/views/settings/SettingsMultiServiceSpace.tsx b/src/views/settings/SettingsMultiServiceSpace.tsx index 8c3c3216e..a96ef7521 100644 --- a/src/views/settings/SettingsMultiServiceSpace.tsx +++ b/src/views/settings/SettingsMultiServiceSpace.tsx @@ -11,7 +11,7 @@ import { import {useTheme} from "@react-navigation/native"; import type {Screen} from "@/router/helpers/types"; import {NativeItem, NativeList, NativeListHeader, NativeText} from "@/components/Global/NativeComponents"; -import {Camera, ChevronDown, CircleAlert, TextCursorInput, Trash2, Type, Undo2, User2} from "lucide-react-native"; +import {BadgeHelp, Camera, ChevronDown, CircleAlert, TextCursorInput, Trash2, Type, Undo2, User2} from "lucide-react-native"; import {useAccounts} from "@/stores/account"; import {AccountService, PrimaryAccount} from "@/stores/account/types"; import * as ImagePicker from "expo-image-picker"; @@ -84,6 +84,7 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga showAlert({ title: "Es-tu sûr ?", message: "Cette action entrainera la suppression de ton espace multi-service.", + icon: , actions: [ { title: "Annuler", diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index 5ef3325cd..4b6bf115e 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -3,7 +3,7 @@ import { Screen } from "@/router/helpers/types"; import { useCurrentAccount } from "@/stores/account"; import { useTheme } from "@react-navigation/native"; import * as ImagePicker from "expo-image-picker"; -import { Camera, ChevronDown, ChevronUp, TextCursorInput, Undo2, User2, UserCircle2, WholeWord } from "lucide-react-native"; +import { BadgeX, Camera, ChevronDown, ChevronUp, ClipboardCopy, TextCursorInput, Undo2, User2, UserCircle2, WholeWord } from "lucide-react-native"; import React, { useEffect, useRef, useState } from "react"; import { ActivityIndicator, Image, KeyboardAvoidingView, ScrollView, Switch, TextInput } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; @@ -62,6 +62,7 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { showAlert({ title: "Erreur", message: "Impossible de récupérer de la photo de profil", + icon: , actions: [ { title: "OK", @@ -358,7 +359,8 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { await Clipboard.setStringAsync(item.value); showAlert({ title: "Copié", - message: "L'informatio, a été copiée dans le presse-papier." + message: "L'information a été copiée dans le presse-papier.", + icon: , }); }} chevron={false} diff --git a/src/views/settings/SettingsSubjects.tsx b/src/views/settings/SettingsSubjects.tsx index a0c2b7b25..b01a1ac4b 100644 --- a/src/views/settings/SettingsSubjects.tsx +++ b/src/views/settings/SettingsSubjects.tsx @@ -16,7 +16,7 @@ import { NativeItem, NativeList, NativeText } from "@/components/Global/NativeCo import { useCurrentAccount } from "@/stores/account"; import MissingItem from "@/components/Global/MissingItem"; import BottomSheet from "@/components/Modals/PapillonBottomSheet"; -import { Trash2, X } from "lucide-react-native"; +import { BadgeHelp, BadgeX, Trash2, X } from "lucide-react-native"; import ColorIndicator from "@/components/Lessons/ColorIndicator"; import { COLORS_LIST } from "@/services/shared/Subject"; import type { Screen } from "@/router/helpers/types"; @@ -137,6 +137,7 @@ const SettingsSubjects: Screen<"SettingsSubjects"> = ({ navigation }) => { showAlert({ title: "Réinitialiser les matières", message: "Tu es sûr de vouloir réinitialiser toutes les matières ?", + icon: , actions: [ { title: "Annuler", @@ -235,6 +236,7 @@ const SettingsSubjects: Screen<"SettingsSubjects"> = ({ navigation }) => { showAlert({ title: "Aucun émoji défini", message: "Tu dois définir un émoji pour cette matière avant de pouvoir quitter cette page.", + icon: , }); emojiInput.current?.focus(); } diff --git a/src/views/settings/SettingsTabs.tsx b/src/views/settings/SettingsTabs.tsx index 1659e329e..f739438cc 100644 --- a/src/views/settings/SettingsTabs.tsx +++ b/src/views/settings/SettingsTabs.tsx @@ -17,6 +17,7 @@ import { SendToBack, Gift, Undo2, + BadgeInfo, } from "lucide-react-native"; import { NestableDraggableFlatList, @@ -442,6 +443,7 @@ const SettingsTabs = () => { showAlert({ title: "Information", message: "Tu ne peux pas ajouter plus de 5 onglets sur la page d'accueil.", + icon: , actions: [ { title: "OK", diff --git a/src/views/welcome/AccountSelector.old.tsx b/src/views/welcome/AccountSelector.old.tsx index d4c7eb458..40a40c2d6 100644 --- a/src/views/welcome/AccountSelector.old.tsx +++ b/src/views/welcome/AccountSelector.old.tsx @@ -2,7 +2,7 @@ import {NativeItem, NativeList, NativeListHeader, NativeText} from "@/components import {useAccounts, useCurrentAccount} from "@/stores/account"; import {defaultProfilePicture} from "@/utils/ui/default-profile-picture"; import {useIsFocused, useTheme} from "@react-navigation/native"; -import {PlusIcon, Trash2, Undo2} from "lucide-react-native"; +import {BadgeHelp, PlusIcon, Trash2, Undo2} from "lucide-react-native"; import {useEffect, useState} from "react"; import { Dimensions, @@ -393,6 +393,7 @@ const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { showAlert({ title: "Supprimer le compte", message: "Es-tu sûr de vouloir supprimer ce compte ?", + icon: , actions: [ { title: "Annuler", @@ -408,6 +409,7 @@ const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { showAlert({ title: "Es-tu sûr ?", message: `Veux-tu supprimer définitivement ${account.studentName.first} ${account.studentName.last} ?`, + icon: , actions: [ { title: "Annuler", diff --git a/src/views/welcome/DevMenu.tsx b/src/views/welcome/DevMenu.tsx index ef27bd3c8..2d106185b 100644 --- a/src/views/welcome/DevMenu.tsx +++ b/src/views/welcome/DevMenu.tsx @@ -1,5 +1,5 @@ import { useTheme } from "@react-navigation/native"; -import { ChevronRight, Eraser, Undo2 } from "lucide-react-native"; +import { BadgeHelp, ChevronRight, Eraser, Undo2 } from "lucide-react-native"; import React, { useLayoutEffect } from "react"; import { View, Text, TouchableOpacity, ScrollView } from "react-native"; import type { Screen } from "@/router/helpers/types"; @@ -184,6 +184,7 @@ const DevMenu: Screen<"DevMenu"> = ({ navigation }) => { showAlert({ title: "Réinitialisation de Papillon", message: "Es-tu sûr de vouloir réinitialiser toutes les données de l'application ?", + icon: , actions: [ { title: "Annuler", From 077912b68044239d65c448a9ecb3530abf267e46 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 18 Feb 2025 19:47:05 +0100 Subject: [PATCH 0678/1144] =?UTF-8?q?fix(AlertProvider):=20ajuster=20la=20?= =?UTF-8?q?couleur=20de=20fond=20en=20fonction=20du=20th=C3=A8me=20sombre?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/providers/AlertProvider.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index a53695e2e..234afb0f5 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -52,7 +52,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { const [alert, setAlert] = useState(null); const [visible, setVisible] = useState(false); - const { colors } = useTheme(); + const { dark, colors } = useTheme(); const insets = useSafeAreaInsets(); const showAlert = ({ @@ -100,7 +100,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { style={[ styles.alertBox, { - backgroundColor: colors.card, + backgroundColor: dark ? "#222" : colors.card, marginBottom: 10 + insets.bottom, }, ]} From ae5555305d232591cb763feabae7e3bd24cecbed Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 18 Feb 2025 21:21:44 +0100 Subject: [PATCH 0679/1144] =?UTF-8?q?fix(AlertProvider):=20ajuster=20la=20?= =?UTF-8?q?couleur=20de=20fond=20et=20le=20style=20des=20boutons=20pour=20?= =?UTF-8?q?une=20meilleure=20pr=C3=A9sentation=20(lorsque=20plusieurs=20ac?= =?UTF-8?q?tions)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/providers/AlertProvider.tsx | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index 234afb0f5..620cfffe0 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -100,7 +100,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { style={[ styles.alertBox, { - backgroundColor: dark ? "#222" : colors.card, + backgroundColor: dark ? "#333" : colors.card, marginBottom: 10 + insets.bottom, }, ]} @@ -128,9 +128,8 @@ const AlertProvider = ({ children }: AlertProviderProps) => { styles.buttons, { borderColor: colors.border, - flexDirection: - (alert.actions ?? []).length > 2 ? "column" : "row", - alignItems: "stretch", + flexDirection: (alert.actions ?? []).length > 2 ? "column" : "row", + alignItems: "center", }, ]} > @@ -151,19 +150,17 @@ const AlertProvider = ({ children }: AlertProviderProps) => { }} style={({ pressed }) => [ styles.button, + { + width: (alert.actions ?? []).length > 2 ? "100%" : "auto", + justifyContent: "center", + alignItems: "center", + opacity: pressed ? 0.6 : 1, + }, + primary ? styles.primaryButton : styles.notPrimaryButton, primary - ? styles.primaryButton - : styles.notPrimaryButton, - primary - ? { - backgroundColor: backgroundColor ?? colors.primary, - } - : { - borderColor: "#CCC", - borderWidth: 1, - }, + ? { backgroundColor: backgroundColor ?? colors.primary } + : { borderColor: "#CCC", borderWidth: 1 }, danger && { backgroundColor: "#FC1E0D" }, - { opacity: pressed ? 0.6 : 1 }, ]} > {icon && @@ -217,7 +214,7 @@ const styles = StyleSheet.create({ borderRadius: 16, padding: 20, paddingBottom: 5, - maxWidth: "85%", + maxWidth: "90%", }, contentContainer: { gap: 6, @@ -250,8 +247,10 @@ const styles = StyleSheet.create({ flexDirection: "row", gap: 5, alignItems: "center", + justifyContent: "center", borderRadius: 300, - paddingVertical: 5, + paddingVertical: 10, + width: "100%", }, buttonText: { fontSize: 16, From 718e91106e60c118310eac20005d7d3f98af2450 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 18 Feb 2025 21:32:29 +0100 Subject: [PATCH 0680/1144] =?UTF-8?q?fix:=20passer=20showAlert=20=C3=A0=20?= =?UTF-8?q?alertExpoGo=20pour=20une=20meilleure=20gestion=20des=20alertes?= =?UTF-8?q?=20dans=20les=20param=C3=A8tres=20(et=20=C3=A9viter=20une=20err?= =?UTF-8?q?eur=20de=20Hooks)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/Notifications.ts | 5 ++++- src/utils/native/expoGoAlert.tsx | 7 +++---- src/views/settings/SettingsIcons.tsx | 4 ++-- src/views/settings/SettingsNotifications.tsx | 4 +++- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index de9ceb984..2d9deca5f 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -1,14 +1,17 @@ +import { useAlert } from "@/providers/AlertProvider"; import { alertExpoGo, isExpoGo } from "@/utils/native/expoGoAlert"; import { Notification } from "@notifee/react-native"; const requestNotificationPermission = async () => { try { + const { showAlert } = useAlert(); + if (!isExpoGo()) { console.log("Requesting notification permission..."); const notifee = (await import("@notifee/react-native")).default; return notifee.requestPermission(); } else { - alertExpoGo(); + alertExpoGo(showAlert); return false; } } diff --git a/src/utils/native/expoGoAlert.tsx b/src/utils/native/expoGoAlert.tsx index 5f95c2ac0..bcf49bd32 100644 --- a/src/utils/native/expoGoAlert.tsx +++ b/src/utils/native/expoGoAlert.tsx @@ -6,11 +6,10 @@ export const isExpoGo = () => { return Constants.executionEnvironment !== ExecutionEnvironment.Bare; }; -export const alertExpoGo = async () => { - const { showAlert } = useAlert(); - showAlert({ +export const alertExpoGo = (showAlert: ReturnType["showAlert"]) => { + return showAlert({ title: "Tu développes à l'aide d'Expo Go", message: "Sous Expo Go, les appels aux API natives sont indisponibles. Utilise un build de développement pour accéder à toutes les fonctionnalités.", - icon: + icon: , }); }; diff --git a/src/views/settings/SettingsIcons.tsx b/src/views/settings/SettingsIcons.tsx index ff3b445d9..f09fc2a3b 100644 --- a/src/views/settings/SettingsIcons.tsx +++ b/src/views/settings/SettingsIcons.tsx @@ -65,7 +65,7 @@ const SettingsIcons: Screen<"SettingsIcons"> = ({ navigation }) => { setIconName(iconConstructName); setIcon(iconConstructName); } else { - alertExpoGo(); + alertExpoGo(showAlert); }; } else { @@ -73,7 +73,7 @@ const SettingsIcons: Screen<"SettingsIcons"> = ({ navigation }) => { setIconName(icon.id); setIcon(icon.id); } else { - alertExpoGo(); + alertExpoGo(showAlert); }; } }; diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index e80706ecd..c7b271dcd 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -9,6 +9,7 @@ import NotificationContainerCard from "@/components/Settings/NotificationContain import { requestNotificationPermission } from "@/background/Notifications"; import { alertExpoGo, isExpoGo } from "@/utils/native/expoGoAlert"; import { useCurrentAccount } from "@/stores/account"; +import { useAlert } from "@/providers/AlertProvider"; const SettingsNotifications: Screen<"SettingsNotifications"> = () => { const theme = useTheme(); @@ -21,6 +22,7 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = () => { // Global state const [enabled, setEnabled] = useState(notifications?.enabled || false); + const { showAlert } = useAlert(); // Animation states const opacity = useSharedValue(0); @@ -40,7 +42,7 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = () => { const askEnabled = async (newValue: boolean) => { if (isExpoGo()) { - alertExpoGo(); + alertExpoGo(showAlert); return; } From aba3b7138f35762c2928214ff745e1691ee6d06d Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 19 Feb 2025 19:01:46 +0100 Subject: [PATCH 0681/1144] =?UTF-8?q?fix(AlertProvider):=20bordure=20appli?= =?UTF-8?q?qu=C3=A9e=20sur=20les=20boutons=20lorsque=20`danger`=20=C3=A9ta?= =?UTF-8?q?it=20sur=20true?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/providers/AlertProvider.tsx | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index 620cfffe0..0a0be9ee7 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -128,7 +128,8 @@ const AlertProvider = ({ children }: AlertProviderProps) => { styles.buttons, { borderColor: colors.border, - flexDirection: (alert.actions ?? []).length > 2 ? "column" : "row", + flexDirection: + (alert.actions ?? []).length > 2 ? "column" : "row", alignItems: "center", }, ]} @@ -151,16 +152,23 @@ const AlertProvider = ({ children }: AlertProviderProps) => { style={({ pressed }) => [ styles.button, { - width: (alert.actions ?? []).length > 2 ? "100%" : "auto", + width: + (alert.actions ?? []).length > 2 ? "100%" : "auto", justifyContent: "center", alignItems: "center", opacity: pressed ? 0.6 : 1, }, - primary ? styles.primaryButton : styles.notPrimaryButton, primary - ? { backgroundColor: backgroundColor ?? colors.primary } - : { borderColor: "#CCC", borderWidth: 1 }, - danger && { backgroundColor: "#FC1E0D" }, + ? styles.primaryButton + : styles.notPrimaryButton, + primary + ? { + backgroundColor: + backgroundColor ?? colors.primary, + } + : danger + ? { backgroundColor: "#FC1E0D" } + : { borderColor: "#CCC", borderWidth: 1 }, ]} > {icon && From 7aca3c1bd7013f1d7de1f4ad6e40ed6f0fee4a8f Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 19 Feb 2025 19:02:01 +0100 Subject: [PATCH 0682/1144] =?UTF-8?q?fix(Settings):=20ajuster=20les=20acti?= =?UTF-8?q?ons=20de=20d=C3=A9connexion=20et=20d'annulation=20pour=20une=20?= =?UTF-8?q?meilleure=20pr=C3=A9sentation=20et=20gestion=20des=20ic=C3=B4ne?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/Settings.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index f802a959b..67003c5c3 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -254,8 +254,8 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { actions: [ { title: "Annuler", - backgroundColor: colors.card, - icon: , + icon: , + primary: true, }, { title: "Se déconnecter", @@ -266,9 +266,8 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { routes: [{ name: "AccountSelector" }], }); }, - primary: true, - backgroundColor: "#CF0029", - icon: , + danger: true, + icon: , }, ], }); From bd76dfeeb52f2547698a76a1db1078b8d7debbdd Mon Sep 17 00:00:00 2001 From: Cleboost Date: Wed, 19 Feb 2025 21:57:16 +0100 Subject: [PATCH 0683/1144] =?UTF-8?q?ui=F0=9F=92=84:=20ajuster=20l'opacit?= =?UTF-8?q?=C3=A9=20et=20le=20style=20du=20widget?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Home/Widget.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/Home/Widget.tsx b/src/components/Home/Widget.tsx index e97792a6f..e5eefbc00 100644 --- a/src/components/Home/Widget.tsx +++ b/src/components/Home/Widget.tsx @@ -47,7 +47,7 @@ const Widget: React.FC = ({ widget: DynamicWidget, navigat + )} diff --git a/src/views/login/pronote/PronoteManualLocation.tsx b/src/views/login/pronote/PronoteManualLocation.tsx index 3d58e0973..683784a2c 100644 --- a/src/views/login/pronote/PronoteManualLocation.tsx +++ b/src/views/login/pronote/PronoteManualLocation.tsx @@ -220,6 +220,7 @@ const PronoteManualLocation: Screen<"PronoteManualLocation"> = ({ navigation }) onPress={() => void navigation.navigate("PronoteInstanceSelector", { longitude: municipality.geometry.coordinates[0], latitude: municipality.geometry.coordinates[1], + hideDistance: true })} /> From 18ad9fcbea80834ecc2c2d793c910247f62892c3 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Thu, 27 Feb 2025 19:41:44 +0100 Subject: [PATCH 0699/1144] refactor(lint): :rotating_light: hideDistance conditionnel --- .vscode/settings.json | 3 ++- src/router/helpers/types.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index cb047f414..2cfe9c204 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,6 +8,7 @@ "source.fixAll.eslint": "explicit" }, "conventionalCommits.scopes": [ - "android" + "android", + "lint" ] } \ No newline at end of file diff --git a/src/router/helpers/types.ts b/src/router/helpers/types.ts index b1dcafc1d..d410302a7 100644 --- a/src/router/helpers/types.ts +++ b/src/router/helpers/types.ts @@ -36,7 +36,7 @@ export type RouteParameters = { PronoteInstanceSelector: { longitude: number; latitude: number; - hideDistance: boolean; + hideDistance?: boolean; }; PronoteCredentials: { instanceURL: string; information: pronote.Instance }; PronoteManualURL?: { url?: string; method?: string }; From 6e7facbe19f91b28c3b5110d11e6614ea27ccf63 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Thu, 27 Feb 2025 20:03:56 +0100 Subject: [PATCH 0700/1144] =?UTF-8?q?fix(Settings):=20r=C3=A9organiser=20l?= =?UTF-8?q?es=20onglets=20pour=20am=C3=A9liorer=20l'accessibilit=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/Settings.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index dabac530a..72c13a94a 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -163,12 +163,6 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { icon: , label: "Avancé", tabs: [ - { - icon: , - color: "#27CC58", - label: "Accessibilité", - onPress: () => navigation.navigate("SettingsAccessibility"), - }, { icon: click ? ( = ({ route, navigation }) => { }, }, { - icon: , + icon: , color: "#bf547d", + label: "Accessibilité", + onPress: () => navigation.navigate("SettingsAccessibility"), + }, + { + icon: , + color: "#498c75", label: "Extensions", description: "Disponible prochainement", onPress: () => navigation.navigate("SettingsAddons"), From adc49238c3f9fd36141c360671278438c2f13e55 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Fri, 28 Feb 2025 18:55:30 +0100 Subject: [PATCH 0701/1144] =?UTF-8?q?fix:=20Laisser=20l'option=20type=20se?= =?UTF-8?q?maine=20activ=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Lessons/Lessons.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index dd78ca1c4..3976d0ddb 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -388,7 +388,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { navigation.navigate("LessonsImportIcal", {}); } }, - ...(weekFrequency != null) ? [{ + { icon: , label: "Afficher type sem.", subtitle: "Afficher semaine paire / impaire", @@ -397,7 +397,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { setShouldShowWeekFrequency(!shouldShowWeekFrequency); }, checked: shouldShowWeekFrequency, - }] : [] + }, ]} > Date: Fri, 28 Feb 2025 18:57:57 +0100 Subject: [PATCH 0702/1144] =?UTF-8?q?ui=F0=9F=92=84:=20mettre=20=C3=A0=20j?= =?UTF-8?q?our=20l'ic=C3=B4ne,=20le=20label=20et=20le=20subtitle=20en=20fo?= =?UTF-8?q?nction=20de=20l'activation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Lessons/Lessons.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index 3976d0ddb..c5c694aba 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -22,7 +22,7 @@ import { animPapillon } from "@/utils/ui/animations"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useTheme } from "@react-navigation/native"; import AnimatedNumber from "@/components/Global/AnimatedNumber"; -import { CalendarPlus, Eye, MoreVertical } from "lucide-react-native"; +import { CalendarPlus, Eye, EyeOff, MoreVertical } from "lucide-react-native"; import { PapillonHeaderAction, PapillonHeaderSelector, @@ -389,9 +389,13 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { } }, { - icon: , - label: "Afficher type sem.", - subtitle: "Afficher semaine paire / impaire", + icon: shouldShowWeekFrequency ? : , + label: shouldShowWeekFrequency + ? "Masquer alternance semaine" + : "Afficher alternance semaine", + subtitle: shouldShowWeekFrequency + ? "Masquer semaine paire / impaire" + : "Afficher semaine paire / impaire", sfSymbol: "eye", onPress: () => { setShouldShowWeekFrequency(!shouldShowWeekFrequency); From bbd74c1dd565445395e1ba1df146463774397b13 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Fri, 28 Feb 2025 19:43:48 +0100 Subject: [PATCH 0703/1144] feat: remplacer TextInput par ResponsiveTextInput dans plusieurs composants pour un input natif --- .../FirstInstallation/ResponsiveTextInput.tsx | 48 +++++++++++++++++++ src/components/Templates/LoginView.tsx | 8 ++-- src/views/account/Chat/Modals/Chat.tsx | 5 +- src/views/account/Chat/Modals/ChatCreate.tsx | 6 +-- src/views/account/Homeworks/Homeworks.tsx | 3 +- .../Lessons/Options/LessonsImportIcal.tsx | 5 +- src/views/login/pronote/Pronote2FA_Auth.tsx | 5 +- .../login/pronote/PronoteInstanceSelector.tsx | 3 +- .../login/pronote/PronoteManualLocation.tsx | 3 +- src/views/login/pronote/PronoteManualURL.tsx | 5 +- src/views/login/pronote/PronoteQRCode.tsx | 3 +- .../skolengo/SkolengoInstanceSelector.tsx | 3 +- src/views/settings/SettingsDevLogs.tsx | 5 +- src/views/settings/SettingsFlags.tsx | 3 +- src/views/settings/SettingsMultiService.tsx | 3 +- .../settings/SettingsMultiServiceSpace.tsx | 7 +-- src/views/settings/SettingsProfile.tsx | 5 +- src/views/settings/SettingsSubjects.tsx | 7 +-- 18 files changed, 95 insertions(+), 32 deletions(-) create mode 100644 src/components/FirstInstallation/ResponsiveTextInput.tsx diff --git a/src/components/FirstInstallation/ResponsiveTextInput.tsx b/src/components/FirstInstallation/ResponsiveTextInput.tsx new file mode 100644 index 000000000..041da69a6 --- /dev/null +++ b/src/components/FirstInstallation/ResponsiveTextInput.tsx @@ -0,0 +1,48 @@ +import React from "react"; +import { TextInput, TextInputProps } from "react-native"; +import { useTheme } from "@react-navigation/native"; +import useScreenDimensions from "@/hooks/useScreenDimensions"; + +const ResponsiveTextInput = React.forwardRef( + ( + { + defaultValue, + value, + onChangeText, + placeholder, + secureTextEntry = false, + autoCapitalize = "none", + keyboardType = "default", + style, + }, + ref + ) => { + const theme = useTheme(); + const { colors } = theme; + const { isTablet } = useScreenDimensions(); + + return ( + + ); + } +); + +export default React.memo(ResponsiveTextInput); diff --git a/src/components/Templates/LoginView.tsx b/src/components/Templates/LoginView.tsx index 09b22b7e1..c67c23c90 100644 --- a/src/components/Templates/LoginView.tsx +++ b/src/components/Templates/LoginView.tsx @@ -6,7 +6,6 @@ import { KeyboardAvoidingView, type KeyboardType, ScrollView, - TextInput, TouchableOpacity, View, } from "react-native"; @@ -21,6 +20,7 @@ import { import { AlertTriangle, Eye, EyeOff, Info } from "lucide-react-native"; import { useTheme } from "@react-navigation/native"; import ButtonCta from "../FirstInstallation/ButtonCta"; +import ResponsiveTextInput from "../FirstInstallation/ResponsiveTextInput"; export interface LoginViewCustomInput { identifier: string; @@ -173,7 +173,7 @@ const LoginView: React.FC<{ - - - { setCustomFieldsInputs( diff --git a/src/views/account/Chat/Modals/Chat.tsx b/src/views/account/Chat/Modals/Chat.tsx index 3fb563104..103909b41 100644 --- a/src/views/account/Chat/Modals/Chat.tsx +++ b/src/views/account/Chat/Modals/Chat.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useState } from "react"; -import { Image, ActivityIndicator, FlatList, ImageBackground, Platform, StyleSheet, Text, TextInput, TouchableOpacity, View, KeyboardAvoidingView, } from "react-native"; +import { Image, ActivityIndicator, FlatList, ImageBackground, Platform, StyleSheet, Text, TouchableOpacity, View, KeyboardAvoidingView, } from "react-native"; import { useTheme } from "@react-navigation/native"; import type { Screen } from "@/router/helpers/types"; @@ -27,6 +27,7 @@ import { Theme } from "@/utils/chat/themes/Themes.types"; import { type Attachment, AttachmentType } from "@/services/shared/Attachment"; import { AutoFileIcon } from "@/components/Global/FileIcon"; import LinkFavicon from "@/components/Global/LinkFavicon"; +import ResponsiveTextInput from "@/components/FirstInstallation/ResponsiveTextInput"; const Chat: Screen<"Chat"> = ({ navigation, route }) => { const theme = useTheme(); @@ -347,7 +348,7 @@ const Chat: Screen<"Chat"> = ({ navigation, route }) => { }} > - = ({ navigation }) => { const theme = useTheme(); @@ -76,7 +76,7 @@ const ChatCreate: Screen<"ChatCreate"> = ({ navigation }) => { }> Sujet - = ({ navigation }) => { }> Contenu - { return new Date(date).toLocaleDateString("fr-FR", { @@ -643,7 +644,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { entering={FadeIn.duration(250).delay(20)} exiting={FadeOut.duration(100)} > - = ({ route, navigation }) = } > - = ({ navigation, @@ -254,7 +255,7 @@ export const Pronote2FA_Auth: Screen<"Pronote2FA_Auth"> = ({ }} key={index} > - = ({ - = ({ route: { params }, @@ -227,7 +228,7 @@ const PronoteInstanceSelector: Screen<"PronoteInstanceSelector"> = ({ > - = ({ navigation }) > - = ({ route, navigation }) => { const theme = useTheme(); @@ -136,7 +137,7 @@ const PronoteManualURL: Screen<"PronoteManualURL"> = ({ route, navigation }) => > - { let dt = new Date().getTime(); @@ -323,7 +324,7 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { borderCurve: "continuous", }} > - = ({ route: { params }, @@ -135,7 +136,7 @@ const SkolengoInstanceSelector: Screen<"SkolengoInstanceSelector"> = ({ > - = ({ navigation }) => { const { colors } = useTheme(); @@ -65,7 +66,7 @@ const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { paddingTop: 0, }} > - = ({ navigation }) => { const { flags, remove, set } = useFlagsStore(); @@ -82,7 +83,7 @@ const SettingsFlags: Screen<"SettingsFlags"> = ({ navigation }) => { - = ({ navigation }) => { const theme = useTheme(); @@ -265,7 +266,7 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => Titre de l'espace - = ({ navigation, route }) => { const theme = useTheme(); @@ -239,7 +240,7 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga Titre - = ({ naviga Prénom - = ({ naviga Nom de famille - = ({ navigation }) => { const theme = useTheme(); @@ -249,7 +250,7 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { Prénom - = ({ navigation }) => { Nom de famille - = ({ navigation }) => { width: 42, }} > - = ({ navigation }) => { Nom de la matière - = ({ navigation }) => { borderRadius: 80 }} /> - Date: Fri, 28 Feb 2025 23:44:43 +0100 Subject: [PATCH 0704/1144] update turboself api --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 69a777598..aaecf9dac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -103,7 +103,7 @@ "reanimated-color-picker": "^3.0.4", "scolengo-api": "^3.0.5", "text-encoding": "^0.7.0", - "turboself-api": "^2.1.7", + "turboself-api": "^2.1.8", "zustand": "^4.5.2" }, "devDependencies": { @@ -16991,9 +16991,9 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/turboself-api": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/turboself-api/-/turboself-api-2.1.7.tgz", - "integrity": "sha512-TyWqelaNygIo1Waad5Qw0UVGsB3xGzIzWOQYA82kv9k3fUsBYDv5w2y8zkDW7a7zfKNIKiPOs+LldFqX8dj5jg==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/turboself-api/-/turboself-api-2.1.8.tgz", + "integrity": "sha512-5Gc9oBgKe3q0cWphIxXERM2fQMSshb1CyqpMhVoG8v+V7F6C8EYHqm8uPK0omDojGpBNV4dx9WUuSN1NNzGYog==", "license": "GPL-3.0", "engines": { "node": ">=18" diff --git a/package.json b/package.json index 3a71c74ce..6ad5f7651 100644 --- a/package.json +++ b/package.json @@ -105,7 +105,7 @@ "reanimated-color-picker": "^3.0.4", "scolengo-api": "^3.0.5", "text-encoding": "^0.7.0", - "turboself-api": "^2.1.7", + "turboself-api": "^2.1.8", "zustand": "^4.5.2" }, "devDependencies": { From a4def22350833a639f379f83fb4c8d6fe52a0cc3 Mon Sep 17 00:00:00 2001 From: "Samy M." Date: Sat, 1 Mar 2025 12:14:27 +0100 Subject: [PATCH 0705/1144] =?UTF-8?q?fix(date):=20=F0=9F=97=93=EF=B8=8F=20?= =?UTF-8?q?Fix=20de=20l'affichage=20NaN=20jours=20(#747)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Ajout de conditions spécifiques pour éviter le display NaN - Corrections du calcul des différences de dates --- src/utils/format/DateHelper.ts | 43 ++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/src/utils/format/DateHelper.ts b/src/utils/format/DateHelper.ts index 3949642b7..d6eca0253 100644 --- a/src/utils/format/DateHelper.ts +++ b/src/utils/format/DateHelper.ts @@ -1,6 +1,15 @@ export const timestampToString = (timestamp: number) => { + + if (!timestamp || isNaN(timestamp)) { + return "Date invalide"; + } + const date = new Date(timestamp); const today = new Date(); + + if (isNaN(date.getTime())) { + return "Date invalide"; + } today.setHours(0, 0, 0, 0); date.setHours(0, 0, 0, 0); @@ -13,32 +22,40 @@ export const timestampToString = (timestamp: number) => { date.getDate() - today.getDate(), ]; - let yearDifference = Math.trunc( - dateDifference[0] + dateDifference[1] / 12 + dateDifference[2] / 365 - ); - - let monthDifference = Math.trunc( - dateDifference[0] * 12 + dateDifference[1] + dateDifference[2] / 30.4 - ); + let yearDifference = dateDifference[0]; + + if (dateDifference[1] < 0 || (dateDifference[1] === 0 && dateDifference[2] < 0)) { + yearDifference--; + } else if (dateDifference[1] > 0 || (dateDifference[1] === 0 && dateDifference[2] > 0)) { + yearDifference++; + } + + let monthDifference = dateDifference[0] * 12 + dateDifference[1]; + if (dateDifference[2] < 0) { + monthDifference--; + } else if (dateDifference[2] > 0) { + monthDifference++; + } + let dayDifference = Math.round( (date.getTime() - today.getTime()) / (1000 * 60 * 60 * 24) ); - if (yearDifference <= -1) { - formattedDate = `Il y a ${0 - yearDifference} an${ - yearDifference < -1 ? "s" : "" + if (yearDifference < 0) { + formattedDate = `Il y a ${Math.abs(yearDifference)} an${ + Math.abs(yearDifference) > 1 ? "s" : "" }`; - } else if (yearDifference >= 1) { + } else if (yearDifference > 0) { formattedDate = `Dans ${yearDifference} an${yearDifference > 1 ? "s" : ""}`; } else { if (monthDifference < 0) { - formattedDate = `Il y a ${0 - monthDifference} mois`; + formattedDate = `Il y a ${Math.abs(monthDifference)} mois`; } else if (monthDifference > 0) { formattedDate = `Dans ${monthDifference} mois`; } else { if (dayDifference < -2) { - formattedDate = `Il y a ${0 - dayDifference} jours`; + formattedDate = `Il y a ${Math.abs(dayDifference)} jours`; } else if (dayDifference === -2) { formattedDate = "Avant-hier"; } else if (dayDifference === -1) { From 8d2d86399eb46861a14b6a6a244fa8a0eb441503 Mon Sep 17 00:00:00 2001 From: "Samy M." Date: Sat, 1 Mar 2025 20:04:40 +0100 Subject: [PATCH 0706/1144] fix(pr): Correct NativeText (#747) --- src/views/account/Chat/Messages.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/account/Chat/Messages.tsx b/src/views/account/Chat/Messages.tsx index 26126f43e..b77c97e40 100644 --- a/src/views/account/Chat/Messages.tsx +++ b/src/views/account/Chat/Messages.tsx @@ -242,8 +242,8 @@ const Discussions: Screen<"Discussions"> = ({ navigation, route }) => { )} {getChatCreator(chat)} - {chat.subject || "Aucun sujet"} - Il y a {Math.floor((new Date().getTime() - new Date(chat.date).getTime()) / (1000 * 60 * 60 * 24))} jours + {chat.subject || "Aucun sujet"} + {timestampToString(chat.date.getTime())} ))} From c2864729f6b1c6c9b73b093e3350b4ee802e7eeb Mon Sep 17 00:00:00 2001 From: "Samy M." Date: Sat, 1 Mar 2025 20:20:01 +0100 Subject: [PATCH 0707/1144] fix(lint): Correction on lint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sur téléphone donc un peu laborieux 😅 --- src/utils/format/DateHelper.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/utils/format/DateHelper.ts b/src/utils/format/DateHelper.ts index d6eca0253..58927450c 100644 --- a/src/utils/format/DateHelper.ts +++ b/src/utils/format/DateHelper.ts @@ -6,8 +6,7 @@ export const timestampToString = (timestamp: number) => { const date = new Date(timestamp); const today = new Date(); - - if (isNaN(date.getTime())) { + if (Number.isNaN(date.getTime())) { return "Date invalide"; } @@ -23,13 +22,11 @@ export const timestampToString = (timestamp: number) => { ]; let yearDifference = dateDifference[0]; - if (dateDifference[1] < 0 || (dateDifference[1] === 0 && dateDifference[2] < 0)) { yearDifference--; } else if (dateDifference[1] > 0 || (dateDifference[1] === 0 && dateDifference[2] > 0)) { yearDifference++; } - let monthDifference = dateDifference[0] * 12 + dateDifference[1]; if (dateDifference[2] < 0) { @@ -37,7 +34,6 @@ export const timestampToString = (timestamp: number) => { } else if (dateDifference[2] > 0) { monthDifference++; } - let dayDifference = Math.round( (date.getTime() - today.getTime()) / (1000 * 60 * 60 * 24) ); From e589e6f6ec0d69a150aa9b94e17eef53e09bc2b9 Mon Sep 17 00:00:00 2001 From: "Samy M." Date: Sat, 1 Mar 2025 20:45:52 +0100 Subject: [PATCH 0708/1144] fix(date): import on Message Component --- src/views/account/Chat/Messages.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/account/Chat/Messages.tsx b/src/views/account/Chat/Messages.tsx index b77c97e40..b507f3268 100644 --- a/src/views/account/Chat/Messages.tsx +++ b/src/views/account/Chat/Messages.tsx @@ -37,6 +37,7 @@ import InsetsBottomView from "@/components/Global/InsetsBottomView"; import { TabLocation } from "pawnote"; import {hasFeatureAccountSetup} from "@/utils/multiservice"; import {MultiServiceFeature} from "@/stores/multiService/types"; +import { timestampToString } from "@/utils/format/DateHelper"; // Voir la documentation de `react-navigation`. // From 582605e81c77020d74fbdab9a4f647ecfc44846e Mon Sep 17 00:00:00 2001 From: "Samy M." Date: Sat, 1 Mar 2025 20:47:05 +0100 Subject: [PATCH 0709/1144] fix(date): Date Helper isNaN correction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 𝕂𝕪𝕝𝕚𝕒𝕟 <164187100+Kgeek33@users.noreply.github.com> --- src/utils/format/DateHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/format/DateHelper.ts b/src/utils/format/DateHelper.ts index 58927450c..05ab4751f 100644 --- a/src/utils/format/DateHelper.ts +++ b/src/utils/format/DateHelper.ts @@ -1,6 +1,6 @@ export const timestampToString = (timestamp: number) => { - if (!timestamp || isNaN(timestamp)) { + if (!timestamp || Number.isNaN(timestamp)) { return "Date invalide"; } From dc3bac01842ff7cb47309e9f430103eca75b1502 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 1 Mar 2025 22:25:03 +0100 Subject: [PATCH 0710/1144] feat(router): change default animation for android --- src/router/helpers/create-screen.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/router/helpers/create-screen.ts b/src/router/helpers/create-screen.ts index 1de6ca610..15f30b3bd 100644 --- a/src/router/helpers/create-screen.ts +++ b/src/router/helpers/create-screen.ts @@ -21,10 +21,13 @@ const createScreen = ( tabBarLottie?: any tabEnabled?: boolean }) = {} -) => ({ - name, - component, - options -}); +) => { + if (!options.animation && Platform.OS === "android") options.animation = "slide_from_bottom"; + return { + name, + component, + options + }; +}; export default createScreen; From 9a5304c5c776753c2338c7682b3a62d137c81995 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 1 Mar 2025 22:33:14 +0100 Subject: [PATCH 0711/1144] feat(router): animation depends if presentation is a modal --- src/router/helpers/create-screen.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/router/helpers/create-screen.ts b/src/router/helpers/create-screen.ts index 15f30b3bd..61e129b9a 100644 --- a/src/router/helpers/create-screen.ts +++ b/src/router/helpers/create-screen.ts @@ -22,7 +22,9 @@ const createScreen = ( tabEnabled?: boolean }) = {} ) => { - if (!options.animation && Platform.OS === "android") options.animation = "slide_from_bottom"; + if (!options.animation && Platform.OS === "android") { + options.animation = options.presentation === "modal" ? "slide_from_bottom" : "slide_from_right"; + } return { name, component, From 81f541c5b03a284378d1919dd39386dd8d34e160 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 1 Mar 2025 22:34:55 +0100 Subject: [PATCH 0712/1144] chore(router): remove unecessary line --- src/router/helpers/create-screen.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/router/helpers/create-screen.ts b/src/router/helpers/create-screen.ts index 61e129b9a..c16918ce3 100644 --- a/src/router/helpers/create-screen.ts +++ b/src/router/helpers/create-screen.ts @@ -9,8 +9,7 @@ export const navigatorScreenOptions: NativeStackNavigationOptions = { headerTitleStyle: { fontFamily: "semibold", }, - headerBackTitle: "Retour", - animation: Platform.OS === "android" ? "default" : "default", + headerBackTitle: "Retour" }; const createScreen = ( From fb8d18b248a7cedf968869f9f91a3edc250acf35 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 3 Mar 2025 11:51:20 +0100 Subject: [PATCH 0713/1144] fix: ajouter le type Alert pour la fonction showAlert dans PronoteManualURL --- src/views/login/pronote/PronoteManualURL.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/login/pronote/PronoteManualURL.tsx b/src/views/login/pronote/PronoteManualURL.tsx index 4c253fcea..6a2b11248 100644 --- a/src/views/login/pronote/PronoteManualURL.tsx +++ b/src/views/login/pronote/PronoteManualURL.tsx @@ -13,7 +13,7 @@ import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBub import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { BadgeInfo, Link2, TriangleAlert, Undo2, X } from "lucide-react-native"; -import { useAlert } from "@/providers/AlertProvider"; +import { Alert, useAlert } from "@/providers/AlertProvider"; import { NativeStackNavigationProp } from "@react-navigation/native-stack"; const PronoteManualURL: Screen<"PronoteManualURL"> = ({ route, navigation }) => { @@ -53,7 +53,7 @@ const PronoteManualURL: Screen<"PronoteManualURL"> = ({ route, navigation }) => const checkForDemoInstance = async ( instanceURL: string, navigation: NativeStackNavigationProp, - showAlert: any + showAlert: (alert: Alert) => void, ): Promise => { if (!instanceURL.includes("demo.index-education.net")) return determinateAuthenticationView(instanceURL.trim(), navigation, showAlert); showAlert({ From 5e70ff134cdca9ce0dab003b8442d235e569071e Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 3 Mar 2025 12:01:41 +0100 Subject: [PATCH 0714/1144] =?UTF-8?q?fix:=20am=C3=A9liorer=20le=20style=20?= =?UTF-8?q?des=20boutons=20dans=20AlertProvider=20et=20ajouter=20le=20type?= =?UTF-8?q?=20danger=20dans=20PronoteManualURL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/providers/AlertProvider.tsx | 14 +++----------- src/views/login/pronote/PronoteManualURL.tsx | 5 ++--- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index 0a0be9ee7..189a81a43 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -158,9 +158,6 @@ const AlertProvider = ({ children }: AlertProviderProps) => { alignItems: "center", opacity: pressed ? 0.6 : 1, }, - primary - ? styles.primaryButton - : styles.notPrimaryButton, primary ? { backgroundColor: @@ -173,13 +170,13 @@ const AlertProvider = ({ children }: AlertProviderProps) => { > {icon && React.cloneElement(icon, { - color: primary ? "#ffffff" : colors.text, + color: primary || danger ? "#ffffff" : colors.text, size: 24, })} @@ -259,17 +256,12 @@ const styles = StyleSheet.create({ borderRadius: 300, paddingVertical: 10, width: "100%", + paddingHorizontal: 14, }, buttonText: { fontSize: 16, fontFamily: "medium", }, - primaryButton: { - paddingHorizontal: 14, - }, - notPrimaryButton: { - paddingHorizontal: 5, - }, primaryButtonText: { color: "#ffffff", fontFamily: "semibold", diff --git a/src/views/login/pronote/PronoteManualURL.tsx b/src/views/login/pronote/PronoteManualURL.tsx index 6a2b11248..8a4b11e2d 100644 --- a/src/views/login/pronote/PronoteManualURL.tsx +++ b/src/views/login/pronote/PronoteManualURL.tsx @@ -63,15 +63,14 @@ const PronoteManualURL: Screen<"PronoteManualURL"> = ({ route, navigation }) => actions: [ { title: "Continuer", - primary: false, icon: , - onPress: () => determinateAuthenticationView(instanceURL, navigation, showAlert) + onPress: () => determinateAuthenticationView(instanceURL, navigation, showAlert), + danger: true, }, { title: "Annuler", icon: , primary: true, - backgroundColor: "#29947A", } ] }); From 3d55c10409f8f7d2ed6a33caa095a643683af2ad Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 3 Mar 2025 12:14:20 +0100 Subject: [PATCH 0715/1144] =?UTF-8?q?feat:=20ajouter=20un=20d=C3=A9lai=20d?= =?UTF-8?q?e=20d=C3=A9sactivation=20pour=20les=20actions=20d'alerte=20dans?= =?UTF-8?q?=20AlertProvider?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/providers/AlertProvider.tsx | 46 +++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index 189a81a43..35a43a7d4 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -1,6 +1,12 @@ import { useTheme } from "@react-navigation/native"; import { Check } from "lucide-react-native"; -import React, { createContext, useState, useContext, ReactNode } from "react"; +import React, { + createContext, + useState, + useContext, + ReactNode, + useEffect, +} from "react"; import { Modal, View, Text, StyleSheet, Pressable } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import Reanimated, { @@ -21,6 +27,7 @@ type AlertAction = { primary?: boolean; danger?: boolean; backgroundColor?: string; + delayDisable?: number; }; export type Alert = { @@ -51,6 +58,7 @@ type AlertProviderProps = { const AlertProvider = ({ children }: AlertProviderProps) => { const [alert, setAlert] = useState(null); const [visible, setVisible] = useState(false); + const [delays, setDelays] = useState<{ [key: string]: number }>({}); const { dark, colors } = useTheme(); const insets = useSafeAreaInsets(); @@ -70,6 +78,16 @@ const AlertProvider = ({ children }: AlertProviderProps) => { }: Alert) => { setAlert({ title, message, icon, actions }); setVisible(true); + + const initialDelays: { [key: string]: number } = {}; + actions.forEach((action) => { + if (action.delayDisable) { + initialDelays[action.title] = action.delayDisable; + } else { + initialDelays[action.title] = 0; + } + }); + setDelays(initialDelays); }; function hideAlert () { @@ -77,6 +95,22 @@ const AlertProvider = ({ children }: AlertProviderProps) => { setVisible(false); } + useEffect(() => { + const interval = setInterval(() => { + setDelays((prevDelays) => { + const newDelays = { ...prevDelays }; + Object.keys(newDelays).forEach((key) => { + if (newDelays[key] > 0) { + newDelays[key] -= 1; + } + }); + return newDelays; + }); + }, 1000); + + return () => clearInterval(interval); + }, []); + return ( {children} @@ -149,6 +183,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { hideAlert(); onPress?.(); }} + disabled={delays[title] > 0} style={({ pressed }) => [ styles.button, { @@ -156,7 +191,11 @@ const AlertProvider = ({ children }: AlertProviderProps) => { (alert.actions ?? []).length > 2 ? "100%" : "auto", justifyContent: "center", alignItems: "center", - opacity: pressed ? 0.6 : 1, + opacity: pressed + ? 0.6 + : delays[title] === 0 + ? 1 + : 0.5, }, primary ? { @@ -181,6 +220,9 @@ const AlertProvider = ({ children }: AlertProviderProps) => { ]} > {title} + {delays[title] !== undefined && delays[title] > 0 + ? ` (${delays[title]})` + : ""} ) From 6f9fef75eb5a8ac18e1d7c176ca8917c06729e58 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 3 Mar 2025 12:20:58 +0100 Subject: [PATCH 0716/1144] =?UTF-8?q?feat:=20ajouter=20un=20d=C3=A9lai=20d?= =?UTF-8?q?e=20d=C3=A9sactivation=20pour=20les=20actions=20dangereuses=20d?= =?UTF-8?q?ans=20plusieurs=20composants?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Lessons/Options/LessonsImportIcal.tsx | 1 + src/views/login/pronote/PronoteManualURL.tsx | 1 + src/views/settings/Settings.tsx | 1 + src/views/settings/SettingsExternalServices.tsx | 1 + src/views/settings/SettingsFlags.tsx | 1 + src/views/settings/SettingsMultiService.tsx | 1 + src/views/settings/SettingsMultiServiceSpace.tsx | 1 + src/views/settings/SettingsSubjects.tsx | 1 + src/views/welcome/AccountSelector.old.tsx | 1 + src/views/welcome/DevMenu.tsx | 1 + 10 files changed, 10 insertions(+) diff --git a/src/views/account/Lessons/Options/LessonsImportIcal.tsx b/src/views/account/Lessons/Options/LessonsImportIcal.tsx index d22cfa1dd..be2fa1c12 100644 --- a/src/views/account/Lessons/Options/LessonsImportIcal.tsx +++ b/src/views/account/Lessons/Options/LessonsImportIcal.tsx @@ -271,6 +271,7 @@ const LessonsImportIcal: Screen<"LessonsImportIcal"> = ({ route, navigation }) = }); }, danger: true, + delayDisable: 3, } ] }); diff --git a/src/views/login/pronote/PronoteManualURL.tsx b/src/views/login/pronote/PronoteManualURL.tsx index 8a4b11e2d..adc047fd1 100644 --- a/src/views/login/pronote/PronoteManualURL.tsx +++ b/src/views/login/pronote/PronoteManualURL.tsx @@ -66,6 +66,7 @@ const PronoteManualURL: Screen<"PronoteManualURL"> = ({ route, navigation }) => icon: , onPress: () => determinateAuthenticationView(instanceURL, navigation, showAlert), danger: true, + delayDisable: 5, }, { title: "Annuler", diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index e1090510e..66bc38340 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -268,6 +268,7 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { }, danger: true, icon: , + delayDisable: 5, }, ], }); diff --git a/src/views/settings/SettingsExternalServices.tsx b/src/views/settings/SettingsExternalServices.tsx index 0e57e974b..c8df2d5c2 100644 --- a/src/views/settings/SettingsExternalServices.tsx +++ b/src/views/settings/SettingsExternalServices.tsx @@ -90,6 +90,7 @@ const SettingsExternalServices: Screen<"SettingsExternalServices"> = ({ icon: , onPress: () => removeAccount(account.localID), danger: true, + delayDisable: 5, } ] }); diff --git a/src/views/settings/SettingsFlags.tsx b/src/views/settings/SettingsFlags.tsx index e965b6d24..7c603de30 100644 --- a/src/views/settings/SettingsFlags.tsx +++ b/src/views/settings/SettingsFlags.tsx @@ -84,6 +84,7 @@ const SettingsFlags: Screen<"SettingsFlags"> = ({ navigation }) => { icon: , onPress: () => remove(flag), danger: true, + delayDisable: 3, } ] }); diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index 4e6ecae58..59960ea80 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -172,6 +172,7 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => toggleMultiService(); }, danger: true, + delayDisable: 5, } ] }); diff --git a/src/views/settings/SettingsMultiServiceSpace.tsx b/src/views/settings/SettingsMultiServiceSpace.tsx index 4ee0b4bce..e1f126085 100644 --- a/src/views/settings/SettingsMultiServiceSpace.tsx +++ b/src/views/settings/SettingsMultiServiceSpace.tsx @@ -102,6 +102,7 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga navigation.goBack(); }, danger: true, + delayDisable: 5, } ] }); diff --git a/src/views/settings/SettingsSubjects.tsx b/src/views/settings/SettingsSubjects.tsx index b01a1ac4b..10659644a 100644 --- a/src/views/settings/SettingsSubjects.tsx +++ b/src/views/settings/SettingsSubjects.tsx @@ -148,6 +148,7 @@ const SettingsSubjects: Screen<"SettingsSubjects"> = ({ navigation }) => { icon: , primary: true, danger: true, + delayDisable: 3, onPress: () => { setSubjects([]); setLocalSubjects([]); diff --git a/src/views/welcome/AccountSelector.old.tsx b/src/views/welcome/AccountSelector.old.tsx index 40a40c2d6..feeba2e7a 100644 --- a/src/views/welcome/AccountSelector.old.tsx +++ b/src/views/welcome/AccountSelector.old.tsx @@ -421,6 +421,7 @@ const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { icon: , onPress: () => removeAccount(account.localID), danger: true, + delayDisable: 5, } ] }); diff --git a/src/views/welcome/DevMenu.tsx b/src/views/welcome/DevMenu.tsx index 2d106185b..b58751e48 100644 --- a/src/views/welcome/DevMenu.tsx +++ b/src/views/welcome/DevMenu.tsx @@ -199,6 +199,7 @@ const DevMenu: Screen<"DevMenu"> = ({ navigation }) => { navigation.popToTop(); }, danger: true, + delayDisable: 10, } ] }); From 9b05220c26dbed57840dc0a5faa127223c07ddbd Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 3 Mar 2025 12:25:18 +0100 Subject: [PATCH 0717/1144] fix: attendre 1 seconde avant le lancement du compteur dans AlertProvider --- src/providers/AlertProvider.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index 35a43a7d4..0124a2245 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -108,7 +108,9 @@ const AlertProvider = ({ children }: AlertProviderProps) => { }); }, 1000); - return () => clearInterval(interval); + setTimeout(() => { + return () => clearInterval(interval); + }, 1000); }, []); return ( From dc5a881201189be544c1507813ff2f76b7e40af7 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 3 Mar 2025 14:05:40 +0100 Subject: [PATCH 0718/1144] fix: affichage des devoirs de la semaine prochaine si aucun devoir cette semaine --- .../Home/Elements/HomeworksElement.tsx | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 6e7e2de34..5528f05a6 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -127,18 +127,19 @@ const HomeworksElement: React.FC = ({ navigation, onImpor }} /> ))} - {new Date().getDay() >= 2 && hwSemaineProchaine.map((hw, index) => ( - { - handleDonePress(hw); - }} - /> - ))} + {(hwSemaineActuelle.length === 0 || new Date().getDay() >= 2) && + hwSemaineProchaine.map((hw, index) => ( + { + handleDonePress(hw); + }} + /> + ))} ); From 2b4b27903f9b3014e688cdd789a20a6ea004745e Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Mon, 3 Mar 2025 17:19:31 +0100 Subject: [PATCH 0719/1144] fix(navHeader): add missing outsideNav --- src/components/Home/Header.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Home/Header.tsx b/src/components/Home/Header.tsx index cc677f6f7..c3fef43ee 100644 --- a/src/components/Home/Header.tsx +++ b/src/components/Home/Header.tsx @@ -1,6 +1,6 @@ import { CopyPlus } from "lucide-react-native"; import React, { forwardRef, useEffect, useState } from "react"; -import { Dimensions, Image, ScrollView, StyleSheet, Text, TouchableOpacity, View } from "react-native"; +import { Dimensions, Image, Platform, ScrollView, StyleSheet, Text, TouchableOpacity, View } from "react-native"; import { useTheme } from "@react-navigation/native"; @@ -162,7 +162,7 @@ const Header: React.FC<{ text={defaultTab.label} scrolled={scrolled} onPress={() => { - navigation.navigate(tab.name as RouteParameters[keyof RouteParameters]); + navigation.navigate(tab.name as RouteParameters[keyof RouteParameters], {outsideNav: Platform.OS !== "android"}); }} /> ); @@ -434,4 +434,4 @@ const styles = StyleSheet.create({ }, }); -export default Header; \ No newline at end of file +export default Header; From 04f95e895e73048b73ec10127132398c9c2f8f25 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Mon, 3 Mar 2025 16:25:25 +0000 Subject: [PATCH 0720/1144] feat(idx): add IDX dev config --- .idx/dev.nix | 41 ++++++++++++++++++++++++++++++++++++++++ .idx/secrets.example.nix | 4 ++++ 2 files changed, 45 insertions(+) create mode 100644 .idx/dev.nix create mode 100644 .idx/secrets.example.nix diff --git a/.idx/dev.nix b/.idx/dev.nix new file mode 100644 index 000000000..ee71fda34 --- /dev/null +++ b/.idx/dev.nix @@ -0,0 +1,41 @@ +# To learn more about how to use Nix to configure your environment +# see: https://developers.google.com/idx/guides/customize-idx-env + +let secrets = import ./secrets.nix; +in +{ pkgs, ... }: { + # Which nixpkgs channel to use. + channel = "stable-24.05"; # or "unstable" + + # Use https://search.nixos.org/packages to find packages + packages = [ + pkgs.nodejs_latest + pkgs.jre + pkgs.bun + ]; + + # Sets environment variables in the workspace + env = pkgs.lib.recursiveUpdate {} secrets; + idx = { + # Search for the extensions you want on https://open-vsx.org/ and use "publisher.id" + extensions = [ + "dbaeumer.vscode-eslint" + "expo.vscode-expo-tools" + "SonarSource.sonarlint-vscode" + ]; + + # Enable previews + previews = { + enable = false; + }; + + # Workspace lifecycle hooks + workspace = { + onStart = { + # install JS dependencies from NPM + npm-install = "npm ci"; + configure-git = "git config --global user.email $GIT_MAIL && git config --global user.name $GIT_USER"; + }; + }; + }; +} diff --git a/.idx/secrets.example.nix b/.idx/secrets.example.nix new file mode 100644 index 000000000..9189da36a --- /dev/null +++ b/.idx/secrets.example.nix @@ -0,0 +1,4 @@ +{ + GIT_USER = ""; + GIT_MAIL = ""; +} \ No newline at end of file From a353c973b5af4260d39e2cc7738619356b92e276 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Mon, 3 Mar 2025 16:30:01 +0000 Subject: [PATCH 0721/1144] chore: revert src/components/Home/Header.tsx to 5f220ee2 --- src/components/Home/Header.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Home/Header.tsx b/src/components/Home/Header.tsx index c3fef43ee..cc677f6f7 100644 --- a/src/components/Home/Header.tsx +++ b/src/components/Home/Header.tsx @@ -1,6 +1,6 @@ import { CopyPlus } from "lucide-react-native"; import React, { forwardRef, useEffect, useState } from "react"; -import { Dimensions, Image, Platform, ScrollView, StyleSheet, Text, TouchableOpacity, View } from "react-native"; +import { Dimensions, Image, ScrollView, StyleSheet, Text, TouchableOpacity, View } from "react-native"; import { useTheme } from "@react-navigation/native"; @@ -162,7 +162,7 @@ const Header: React.FC<{ text={defaultTab.label} scrolled={scrolled} onPress={() => { - navigation.navigate(tab.name as RouteParameters[keyof RouteParameters], {outsideNav: Platform.OS !== "android"}); + navigation.navigate(tab.name as RouteParameters[keyof RouteParameters]); }} /> ); @@ -434,4 +434,4 @@ const styles = StyleSheet.create({ }, }); -export default Header; +export default Header; \ No newline at end of file From 5ae016410ad5b6756e43f45501e639d0e9706f39 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Mon, 3 Mar 2025 17:36:21 +0000 Subject: [PATCH 0722/1144] fix(style): wrong insets inheader for android --- src/components/Global/PapillonModernHeader.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Global/PapillonModernHeader.tsx b/src/components/Global/PapillonModernHeader.tsx index b6b436d5d..2b371a1d5 100644 --- a/src/components/Global/PapillonModernHeader.tsx +++ b/src/components/Global/PapillonModernHeader.tsx @@ -44,7 +44,7 @@ const LinearGradientModernHeader: React.FC = ({ children, out const enableBlur = Platform.OS === "ios" && !isExpoGo() && parseInt(Platform.Version) >= 18; return ( - <> + {enableBlur ? ( = ({ children, out }} /> } - + ); }; @@ -159,7 +159,7 @@ const NativeModernHeader: React.FC = ({ children, outsideNav justifyContent: "space-between", alignItems: "center", gap: 8, - backgroundColor: tint ? tint : theme.colors.card + "10", + backgroundColor: tint ?? theme.colors.card + "10", borderBottomColor: theme.colors.border, borderBottomWidth: 0.5, }]} From 4433b78c6ebbe804170048744bebb525c450c678 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Mon, 3 Mar 2025 17:36:58 +0000 Subject: [PATCH 0723/1144] feat(anim): change anim for outsideNav tabs for android in home --- src/router/screens/account/home.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/router/screens/account/home.tsx b/src/router/screens/account/home.tsx index 46b042f52..bf1945e64 100644 --- a/src/router/screens/account/home.tsx +++ b/src/router/screens/account/home.tsx @@ -42,7 +42,7 @@ const HomeStackScreen = ({ accountScreens }: { ...tabData.options, tabEnabled: tab.enabled, presentation: "formSheet", - animation: Platform.OS === "android" ? "slide_from_right" : "default", + animation: Platform.OS === "android" ? "slide_from_bottom" : "default", sheetCornerRadius: 24, }; From 606a720e0af4ccf4112bb3a5139e717939a116a1 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Mon, 3 Mar 2025 17:51:17 +0000 Subject: [PATCH 0724/1144] fix: inset top in Week Screen --- src/views/account/Week/Week.tsx | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index 773842018..6a43485f6 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -291,15 +291,7 @@ const Week: Screen<"Week"> = ({ route, navigation }) => { }, [account?.personalization?.icalURLs]); return ( - - {!outsideNav && ( - - )} - + {account?.providers?.includes("ical") && Object.values(timetables).flat().length === 0 && ( = ({ route, navigation }) => { overflow: "visible", position: "absolute", left: 12, - top: !outsideNav ? (insets.top + 3) : 6, + top: 3, }} > Date: Mon, 3 Mar 2025 17:51:47 +0000 Subject: [PATCH 0725/1144] chore(idx): add secrets to gitignore --- .gitignore | 1 + .idx/dev.nix | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index afbf18c05..304056a17 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ yarn-error.* # local env files .env*.local +.idx/secrets.nix # typescript *.tsbuildinfo diff --git a/.idx/dev.nix b/.idx/dev.nix index ee71fda34..2e2fbfa89 100644 --- a/.idx/dev.nix +++ b/.idx/dev.nix @@ -35,6 +35,7 @@ in # install JS dependencies from NPM npm-install = "npm ci"; configure-git = "git config --global user.email $GIT_MAIL && git config --global user.name $GIT_USER"; + install-tunnel-package = "npm i --save-dev @expo/ngrok@^4.1.0"; # Attention à ne pas l'inclure dans les commits git }; }; }; From 0934a834735a9f0442fbc01ee975c19076f5244d Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 4 Mar 2025 12:08:46 +0100 Subject: [PATCH 0726/1144] fix: ajout d'un emoji --- .github/workflows/checks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index ec23fe01d..ae794a268 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -1,4 +1,4 @@ -name: Checks (TypeScript + ESLint) +name: 🛠️ Checks (TypeScript + ESLint) on: push: From bc29ed7e62f1a763db0eb027e45e4906d5ddec40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Tue, 4 Mar 2025 12:21:21 +0100 Subject: [PATCH 0727/1144] =?UTF-8?q?fix:=20retour=20de=20l'upload=20sur?= =?UTF-8?q?=20les=20stores=20=F0=9F=98=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/start_build.yml | 32 +++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/.github/workflows/start_build.yml b/.github/workflows/start_build.yml index 9804d030b..a91a6d0bf 100644 --- a/.github/workflows/start_build.yml +++ b/.github/workflows/start_build.yml @@ -206,11 +206,9 @@ jobs: - name: 🔗 Generate comparison link id: comparison_link run: | - # Récupérer le dernier tag LAST_TAG=$(git describe --tags --abbrev=0 HEAD^) echo "LAST_TAG=$LAST_TAG" >> $GITHUB_ENV - # Construire le lien de comparaison COMPARE_URL="https://github.com/${{ github.repository }}/compare/$LAST_TAG...v${{ env.VERSION }}" echo "COMPARE_URL=$COMPARE_URL" >> $GITHUB_ENV @@ -232,6 +230,11 @@ jobs: name: split-apks path: split-apks + - name: 🔧 Prepare artifacts + run: | + mv ios-app/Papillon.ipa ./Papillon.ipa + mv android-aab/app-release.aab ./Papillon.aab + - name: 🛜 Create GitHub Release uses: comnoco/create-release@v2 env: @@ -276,3 +279,28 @@ jobs: asset_path: android/app/build/outputs/apk/release/*.apk asset_name: Papillon-{arch}.apk asset_content_type: application/octet-stream + + - name: 📥 Upload to TestFlight + env: + APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }} + APP_STORE_CONNECT_API_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_ISSUER_ID }} + APP_STORE_CONNECT_API_KEY_CONTENT: ${{ secrets.APP_STORE_CONNECT_API_KEY_CONTENT }} + run: | + echo "$APP_STORE_CONNECT_API_KEY_CONTENT" | base64 --decode > appstore_connect_api_key.p8 + + xcrun notarytool submit Papillon.ipa --key appstore_connect_api_key.p8 \ + --key-id "$APP_STORE_CONNECT_API_KEY_ID" \ + --issuer "$APP_STORE_CONNECT_API_ISSUER_ID" \ + --wait + + echo "iOS build uploaded to TestFlight for beta testing" + + - name: 📥 Upload Android Release to Play Store Beta + uses: r0adkll/upload-google-play@v1.1.3 + with: + serviceAccountJsonPlainText: ${{ secrets.PLAY_STORE_JSON_KEY }} + packageName: xyz.getpapillon.app + releaseFiles: Papillon.aab + track: beta + status: completed + changesNotSentForReview: false From ab76f8e5e5d111c9a21e7af7800a0d55a5677aad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Tue, 4 Mar 2025 12:23:08 +0100 Subject: [PATCH 0728/1144] fix: utilisation artifacts --- .github/workflows/start_build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/start_build.yml b/.github/workflows/start_build.yml index a91a6d0bf..e9357bf03 100644 --- a/.github/workflows/start_build.yml +++ b/.github/workflows/start_build.yml @@ -256,7 +256,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ios-app/Papillon.ipa + asset_path: Papillon.ipa asset_name: Papillon.ipa asset_content_type: application/octet-stream @@ -266,7 +266,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: android-aab/app-release.aab + asset_path: Papillon.aab asset_name: Papillon.aab asset_content_type: application/octet-stream From 5edb56cea22958dab5fe11cbe5fa8edca0ce42bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Tue, 4 Mar 2025 12:43:42 +0100 Subject: [PATCH 0729/1144] Ajout d'un emoji xD --- .github/workflows/start_build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/start_build.yml b/.github/workflows/start_build.yml index e9357bf03..7fb943fde 100644 --- a/.github/workflows/start_build.yml +++ b/.github/workflows/start_build.yml @@ -1,4 +1,4 @@ -name: New Release (Build + Deploy) +name: 🆕 New Release (Build + Deploy) on: workflow_dispatch: From ce5e5c737d88ada883683ec32202c6dab5ed3683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Tue, 4 Mar 2025 13:43:17 +0100 Subject: [PATCH 0730/1144] Update version of artifact --- .github/workflows/start_build.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/start_build.yml b/.github/workflows/start_build.yml index 7fb943fde..f00a3e296 100644 --- a/.github/workflows/start_build.yml +++ b/.github/workflows/start_build.yml @@ -24,7 +24,7 @@ jobs: run: npm ci - name: 📤 Upload node_modules - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: node_modules path: node_modules @@ -40,7 +40,7 @@ jobs: uses: actions/checkout@v3 - name: 📥 Download node_modules - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: node_modules @@ -134,7 +134,7 @@ jobs: fi - name: 🛜 Upload IPA - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ios-app path: ios/build/Papillon.ipa @@ -148,7 +148,7 @@ jobs: uses: actions/checkout@v3 - name: 📥 Download node_modules - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: node_modules @@ -172,7 +172,7 @@ jobs: ls app/build/outputs/apk/release/ - name: 🛜 Upload split APKs - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: split-apks path: android/app/build/outputs/apk/release/*.apk @@ -213,19 +213,19 @@ jobs: echo "COMPARE_URL=$COMPARE_URL" >> $GITHUB_ENV - name: 📡 Download iOS artifact - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ios-app path: ios-app - name: 📡 Download Android AAB artifact - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: android-aab path: android-aab - name: 📡 Download Android APKs - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: split-apks path: split-apks From 28b7769baa0a2fd38de0943216d56fe44b779ec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Tue, 4 Mar 2025 23:55:23 +0100 Subject: [PATCH 0731/1144] Update Github Release creation --- .github/workflows/start_build.yml | 59 +++++++++++-------------------- 1 file changed, 20 insertions(+), 39 deletions(-) diff --git a/.github/workflows/start_build.yml b/.github/workflows/start_build.yml index f00a3e296..cf59cd8ce 100644 --- a/.github/workflows/start_build.yml +++ b/.github/workflows/start_build.yml @@ -171,6 +171,13 @@ jobs: ./gradlew clean assembleRelease ls app/build/outputs/apk/release/ + - name: 🛠️ Rename APK files + run: | + cd android/app/build/outputs/apk/release/ + for file in *.apk; do + mv "$file" "Papillon Dev-${file%-release.apk}.apk" + done + - name: 🛜 Upload split APKs uses: actions/upload-artifact@v4 with: @@ -228,57 +235,31 @@ jobs: uses: actions/download-artifact@v4 with: name: split-apks - path: split-apks + path: . - name: 🔧 Prepare artifacts run: | mv ios-app/Papillon.ipa ./Papillon.ipa mv android-aab/app-release.aab ./Papillon.aab - - name: 🛜 Create GitHub Release - uses: comnoco/create-release@v2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: 🛜 Create or Update GitHub Release + uses: softprops/action-gh-release@v2 with: - tag_name: v${{ env.VERSION }} - release_name: "Version ${{ env.VERSION }}" + tag_name: ${{ env.VERSION }} + name: "ver. ${{ env.VERSION }}" body: | - ### Les nouveautés depuis la dernière version : + ### What's Changed + --- + ${{ env.PULL_REQUESTS }} - [Pour plus de détails...](${{ env.COMPARE_URL }}) + **Full Changelog**: ${{ env.COMPARE_URL }} draft: false prerelease: true - - - name: 📥 Upload IPA to release - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: Papillon.ipa - asset_name: Papillon.ipa - asset_content_type: application/octet-stream - - - name: 📥 Upload AAB to release - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: Papillon.aab - asset_name: Papillon.aab - asset_content_type: application/octet-stream - - - name: 📥 Upload split APKs to release - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: android/app/build/outputs/apk/release/*.apk - asset_name: Papillon-{arch}.apk - asset_content_type: application/octet-stream + files: | + *.apk + Papillon.ipa + Papillon.aab - name: 📥 Upload to TestFlight env: From 45cd280b01a2154cc46f188282aec1cd3faa76de Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 6 Mar 2025 11:27:41 +0100 Subject: [PATCH 0732/1144] fix: afficher toujours les devoirs de la semaine prochaine --- .../Home/Elements/HomeworksElement.tsx | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 5528f05a6..06fa1e39b 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -127,19 +127,18 @@ const HomeworksElement: React.FC = ({ navigation, onImpor }} /> ))} - {(hwSemaineActuelle.length === 0 || new Date().getDay() >= 2) && - hwSemaineProchaine.map((hw, index) => ( - { - handleDonePress(hw); - }} - /> - ))} + {hwSemaineProchaine.map((hw, index) => ( + { + handleDonePress(hw); + }} + /> + ))} ); From 651b0f30be3db3f6acd66757bde7a40326ce1093 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 6 Mar 2025 11:30:02 +0100 Subject: [PATCH 0733/1144] =?UTF-8?q?fix:=20ne=20pas=20afficher=20les=20de?= =?UTF-8?q?voirs=20termin=C3=A9s=20dans=20la=20liste?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Home/Elements/HomeworksElement.tsx | 54 ++++++++++--------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 06fa1e39b..dd1b2d41a 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -115,30 +115,36 @@ const HomeworksElement: React.FC = ({ navigation, onImpor )} /> - {hwSemaineActuelle.map((hw, index) => ( - { - handleDonePress(hw); - }} - /> - ))} - {hwSemaineProchaine.map((hw, index) => ( - { - handleDonePress(hw); - }} - /> - ))} + {hwSemaineActuelle + .filter((element) => !element.done) + .map((hw, index) => ( + { + handleDonePress(hw); + }} + /> + ))} + {hwSemaineProchaine + .filter((element) => !element.done) + .map((hw, index) => ( + { + handleDonePress(hw); + }} + /> + ))} ); From 18b0d59b4acd9cff960815276f7abfe286c23ba5 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 6 Mar 2025 11:35:29 +0100 Subject: [PATCH 0734/1144] =?UTF-8?q?Revert=20"patch=20le=20texte=20coup?= =?UTF-8?q?=C3=A9"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 80eac7ccb88dc021e5b4d0f0b7afe2e5d13e3458. --- src/views/account/Home/Elements/HomeworksElement.tsx | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index dd1b2d41a..25cbc01d8 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -66,17 +66,6 @@ const HomeworksElement: React.FC = ({ navigation, onImpor const hwSemaineActuelle = homeworks[dateToEpochWeekNumber(actualDay)]?.filter( (hw) => hw.due / 1000 >= startTime && hw.due / 1000 <= endTime ) ?? []; - const truncateContent = (content: string) => { - if (content.length <= 120) { - return content; - } - const truncated = content.slice(0, 120); - const lastSpaceIndex = truncated.lastIndexOf(" "); - return `${truncated.slice(0, lastSpaceIndex)}...`; - }; - hwSemaineActuelle.forEach(hw => { - hw.content = truncateContent(hw.content); - }); const hwSemaineProchaine = homeworks[dateToEpochWeekNumber(actualDay) + 1]?.filter( (hw) => hw.due / 1000 >= startTime && hw.due / 1000 <= endTime ) ?? []; From 3d606df9ad5719047d20596e0765429cd9e90e59 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 6 Mar 2025 11:52:38 +0100 Subject: [PATCH 0735/1144] =?UTF-8?q?feat:=20afficher=20un=20maximum=20de?= =?UTF-8?q?=207=20devoirs=20=C3=A0=20faire=20+=20titre=20de=20``=20dynamique=20!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Home/Elements/HomeworksElement.tsx | 32 ++++++++----------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 25cbc01d8..77734d3fa 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -96,32 +96,26 @@ const HomeworksElement: React.FC = ({ navigation, onImpor ); } + const hw2Semaines = hwSemaineActuelle + .concat(hwSemaineProchaine) + .filter((element) => !element.done); + return ( <> - 7 + ? `7 / ${hw2Semaines.length} Devoirs à faire` + : "Devoirs à faire" + } trailing={( )} /> - {hwSemaineActuelle - .filter((element) => !element.done) - .map((hw, index) => ( - { - handleDonePress(hw); - }} - /> - ))} - {hwSemaineProchaine - .filter((element) => !element.done) + {hw2Semaines + .slice(0, 7) .map((hw, index) => ( Date: Thu, 6 Mar 2025 12:02:52 +0100 Subject: [PATCH 0736/1144] fix: rester sur la semaine actuelle pour les devoirs le jeudi et vendredi --- src/views/account/Homeworks/Homeworks.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index d3cf3bbe7..78b8268c7 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -94,6 +94,11 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { start.setHours(0, 0, 0, 0); const diff = now.getTime() - start.getTime(); const oneWeek = 1000 * 60 * 60 * 24 * 7; + + if (now.getDay() === 4 || now.getDay() === 5) { + return Math.floor(diff / oneWeek); + } + return Math.floor(diff / oneWeek) + 1; }; From 712b0443ba53d0c3bd85a90eaf8926e1a383ad1e Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 6 Mar 2025 12:20:46 +0100 Subject: [PATCH 0737/1144] fix: maxHeight des devoirs dynamique pour un fonctionnement correct de `` --- src/views/account/Homeworks/Atoms/Item.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/views/account/Homeworks/Atoms/Item.tsx b/src/views/account/Homeworks/Atoms/Item.tsx index 47a768063..7a66c249b 100644 --- a/src/views/account/Homeworks/Atoms/Item.tsx +++ b/src/views/account/Homeworks/Atoms/Item.tsx @@ -47,7 +47,7 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total } fontFamily: "medium", fontSize: 16, lineHeight: 22, - maxHeight: 22 * 5, + maxHeight: shouldShowMoreGradient ? 20 * 5 : 25 * 5, }, }); @@ -212,7 +212,10 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total } /> } > - ${parse_homeworks(homework.content).replace("\n", "")}`} stylesheet={stylesText} /> + ${parse_homeworks(homework.content).replace("\n", "")}`} + stylesheet={stylesText} + /> {route.name === "HomeScreen" && ( From 22047dfa89a0afee05c7a6e4775897d484254c53 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Fri, 7 Mar 2025 11:21:26 +0100 Subject: [PATCH 0738/1144] =?UTF-8?q?utilisation=20de=20`dateToEpochWeekNu?= =?UTF-8?q?mber`=20pour=20calculer=20le=20num=C3=A9ro=20de=20semaine=20act?= =?UTF-8?q?uel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/Homeworks.tsx | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 78b8268c7..19efbf4e9 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -86,23 +86,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { } const firstDateEpoch = dateToEpochWeekNumber(firstDate); - // Function to get the current week number since epoch - const getCurrentWeekNumber = () => { - const now = new Date(); - now.setHours(0, 0, 0, 0); - const start = new Date(1970, 0, 0); - start.setHours(0, 0, 0, 0); - const diff = now.getTime() - start.getTime(); - const oneWeek = 1000 * 60 * 60 * 24 * 7; - - if (now.getDay() === 4 || now.getDay() === 5) { - return Math.floor(diff / oneWeek); - } - - return Math.floor(diff / oneWeek) + 1; - }; - - const currentWeek = getCurrentWeekNumber(); + const currentWeek = dateToEpochWeekNumber(new Date()); const [data, setData] = useState(Array.from({ length: 100 }, (_, i) => currentWeek - 50 + i)); const [selectedWeek, setSelectedWeek] = useState(currentWeek); From 852a890aa7cfcf4a5180f6c9b94edfed45195857 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Fri, 7 Mar 2025 15:13:46 +0100 Subject: [PATCH 0739/1144] =?UTF-8?q?ui=F0=9F=92=84:=20afficher=20le=20sta?= =?UTF-8?q?tut=20justifi=C3=A9/non=20justifi=C3=A9=20dans=20l'=C3=A9l?= =?UTF-8?q?=C3=A9ment=20de=20pr=C3=A9sence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Attendance/Atoms/AttendanceItem.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/views/account/Attendance/Atoms/AttendanceItem.tsx b/src/views/account/Attendance/Atoms/AttendanceItem.tsx index 03a65796d..7ba2e6143 100644 --- a/src/views/account/Attendance/Atoms/AttendanceItem.tsx +++ b/src/views/account/Attendance/Atoms/AttendanceItem.tsx @@ -151,12 +151,18 @@ const AttendanceItem: React.FC = ({ {justification} - {not_justified && ( + {not_justified ? ( Non justifié + ) : ( + + Justifié + )} From d624fc70f2ebe980d3960c5d62d06e31ea139808 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Fri, 7 Mar 2025 15:22:52 +0100 Subject: [PATCH 0740/1144] =?UTF-8?q?fix(Header):=20corriger=20la=20struct?= =?UTF-8?q?ure=20du=20composant=20et=20am=C3=A9liorer=20la=20gestion=20des?= =?UTF-8?q?=20couleurs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Global/PapillonModernHeader.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Global/PapillonModernHeader.tsx b/src/components/Global/PapillonModernHeader.tsx index 2b371a1d5..b6b436d5d 100644 --- a/src/components/Global/PapillonModernHeader.tsx +++ b/src/components/Global/PapillonModernHeader.tsx @@ -44,7 +44,7 @@ const LinearGradientModernHeader: React.FC = ({ children, out const enableBlur = Platform.OS === "ios" && !isExpoGo() && parseInt(Platform.Version) >= 18; return ( - + <> {enableBlur ? ( = ({ children, out }} /> } - + ); }; @@ -159,7 +159,7 @@ const NativeModernHeader: React.FC = ({ children, outsideNav justifyContent: "space-between", alignItems: "center", gap: 8, - backgroundColor: tint ?? theme.colors.card + "10", + backgroundColor: tint ? tint : theme.colors.card + "10", borderBottomColor: theme.colors.border, borderBottomWidth: 0.5, }]} From f7c8e4625119d0c20bd4e0f686b8a38cfbf4a03f Mon Sep 17 00:00:00 2001 From: "Samy M." Date: Fri, 7 Mar 2025 21:53:17 +0100 Subject: [PATCH 0741/1144] =?UTF-8?q?fix(homework):=20Add=20math=20symbols?= =?UTF-8?q?=20conversion=20=F0=9F=A7=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/consts/LatexVocabulary.ts | 50 ++++++++++++++++++++ src/utils/format/format_pronote_homeworks.ts | 8 +++- 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 src/consts/LatexVocabulary.ts diff --git a/src/consts/LatexVocabulary.ts b/src/consts/LatexVocabulary.ts new file mode 100644 index 000000000..064495ee9 --- /dev/null +++ b/src/consts/LatexVocabulary.ts @@ -0,0 +1,50 @@ +export const latexVocabulary: Record = { + "∈": "\\in", + "∉": "\\notin", + "∀": "\\forall", + "∃": "\\exists", + "∅": "\\emptyset", + "∪": "\\cup", + "∩": "\\cap", + "⊂": "\\subset", + "⊆": "\\subseteq", + "⊃": "\\supset", + "⊇": "\\supseteq", + "π": "\\pi", + "∑": "\\sum", + "√": "\\sqrt{}", + "∞": "\\infty", + "→": "\\to", + "⇒": "\\Rightarrow", + "↔": "\\leftrightarrow", + "±": "\\pm", + "≤": "\\leq", + "≥": "\\geq", + "≠": "\\neq", + "×": "\\times", + "÷": "\\div", + "α": "\\alpha", + "β": "\\beta", + "γ": "\\gamma", + "θ": "\\theta", + "λ": "\\lambda", + "μ": "\\mu", + "σ": "\\sigma", + "φ": "\\phi", + "ω": "\\omega", + "≈": "\\approx", + "≡": "\\equiv", + "≜": "\\triangleq", + "⊥": "\\perp", + "∠": "\\angle", + "∥": "\\parallel", + "∂": "\\partial", + "∇": "\\nabla", + "∧": "\\land", + "∨": "\\lor", + "¬": "\\neg", + "⊢": "\\vdash", + "⊨": "\\models", + "⊤": "\\top", + "⊻": "\\veebar" +} as const; \ No newline at end of file diff --git a/src/utils/format/format_pronote_homeworks.ts b/src/utils/format/format_pronote_homeworks.ts index 850f27ad2..859cc1de8 100644 --- a/src/utils/format/format_pronote_homeworks.ts +++ b/src/utils/format/format_pronote_homeworks.ts @@ -1,3 +1,5 @@ +import { latexVocabulary } from "@/consts/LatexVocabulary"; + type SupportedTags = { [key: string]: string | ((text: string) => string); }; @@ -6,6 +8,8 @@ function parse_homeworks (content: string): string { const supportedTags: SupportedTags = { br: "\n", strong: (text: string) => `**${text}**`, + sub: (text: string) => `_{${text}}`, + sup: (text: string) => `^{${text}}`, }; const ignoredTags: Set = new Set(["div", "span", "style", "script", "footer", "header"]); @@ -16,13 +20,15 @@ function parse_homeworks (content: string): string { " ": " ", """: "\"", "'": "'", + ...latexVocabulary }; // Décodage des entités HTML function decodeHtmlEntities (text: string): string { return text .replace(/&#(\d+);/g, (_, dec) => String.fromCharCode(parseInt(dec, 10))) - .replace(/&([a-zA-Z]+);/g, (_, entity) => htmlEntities[entity] || `&${entity};`); + .replace(/&([a-zA-Z]+);/g, (_, entity) => htmlEntities[entity] || `&${entity};`) + .replace(/[\u2200-\u22FF\u03B1-\u03C9]/g, match => latexVocabulary[match] || match); } let result = ""; From 6b14a79625ada9b88056e6a0a8796c7b5b354e41 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Fri, 7 Mar 2025 22:11:05 +0100 Subject: [PATCH 0742/1144] =?UTF-8?q?fix(PronoteQRCode):=20am=C3=A9liorer?= =?UTF-8?q?=20la=20gestion=20de=20l'=C3=A9tat=20du=20clavier=20et=20ajuste?= =?UTF-8?q?r=20l'affichage=20en=20cons=C3=A9quence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/login/pronote/PronoteQRCode.tsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/views/login/pronote/PronoteQRCode.tsx b/src/views/login/pronote/PronoteQRCode.tsx index 589e4b016..87ec9a5b7 100644 --- a/src/views/login/pronote/PronoteQRCode.tsx +++ b/src/views/login/pronote/PronoteQRCode.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { ActivityIndicator, Text, View, StyleSheet, Modal, Alert, KeyboardAvoidingView, TextInput, Pressable } from "react-native"; +import { ActivityIndicator, Text, View, StyleSheet, Modal, Alert, KeyboardAvoidingView, TextInput, Pressable, Keyboard } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { SafeAreaView, useSafeAreaInsets } from "react-native-safe-area-context"; import { useTheme } from "@react-navigation/native"; @@ -45,7 +45,7 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { const [hasPermission, setHasPermission] = useState(null); const [scanned, setScanned] = useState(false); - const [inputFocus, setInputFocus] = useState(false); + const [keyboardOpen, setKeyboardOpen] = useState(false); const [QRValidationCode, setQRValidationCode] = useState(""); const [pinModalVisible, setPinModalVisible] = useState(false); @@ -181,6 +181,19 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { } }, [pinModalVisible]); + const keyboardDidShow = () => setKeyboardOpen(true); + const keyboardDidHide = () => setKeyboardOpen(false); + + useEffect(() => { + Keyboard.addListener("keyboardDidShow", keyboardDidShow); + Keyboard.addListener("keyboardDidHide", keyboardDidHide); + + return () => { + Keyboard.removeAllListeners("keyboardDidShow"); + Keyboard.removeAllListeners("keyboardDidHide"); + }; + }, []); + return ( = ({ navigation }) => { layout={LinearTransition} > - {!inputFocus && ( + {!keyboardOpen && ( Date: Fri, 7 Mar 2025 22:52:43 +0100 Subject: [PATCH 0743/1144] =?UTF-8?q?fix(PronoteQRCode):=20am=C3=A9liorer?= =?UTF-8?q?=20l'affichage=20de=20la=20page=20pour=20les=20tablettes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/login/pronote/PronoteQRCode.tsx | 153 +++++++++------------- 1 file changed, 65 insertions(+), 88 deletions(-) diff --git a/src/views/login/pronote/PronoteQRCode.tsx b/src/views/login/pronote/PronoteQRCode.tsx index 87ec9a5b7..70a605cfe 100644 --- a/src/views/login/pronote/PronoteQRCode.tsx +++ b/src/views/login/pronote/PronoteQRCode.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { ActivityIndicator, Text, View, StyleSheet, Modal, Alert, KeyboardAvoidingView, TextInput, Pressable, Keyboard } from "react-native"; +import { ActivityIndicator, Text, View, StyleSheet, Modal, Alert, KeyboardAvoidingView, TextInput, Keyboard } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { SafeAreaView, useSafeAreaInsets } from "react-native-safe-area-context"; import { useTheme } from "@react-navigation/native"; @@ -299,102 +299,79 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { - + + + )} + + - {!keyboardOpen && ( - - - - )} - - codeInput.current?.blur()} /> - - - setQRValidationCode(text)} - onFocus={() => setInputFocus(true)} - onBlur={() => setInputFocus(false)} - ref={codeInput} - /> - - - codeInput.current?.blur()} /> + placeholderTextColor={colors.text + "80"} + placeholder="Code à 4 chiffres" + keyboardType="number-pad" + maxLength={4} + secureTextEntry + value={QRValidationCode} + onChangeText={(text) => setQRValidationCode(text)} + ref={codeInput} + /> + - + { + setPinModalVisible(false); + loginQR(); }} - > - - { - setPinModalVisible(false); - }} - /> - - - { - setPinModalVisible(false); - loginQR(); - }} - /> - - - + /> + { + setPinModalVisible(false); + }} + /> + From 94559083671cb52d7454790ff802c4f427b3df56 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Fri, 7 Mar 2025 23:00:59 +0100 Subject: [PATCH 0744/1144] =?UTF-8?q?fix:=20am=C3=A9lioration=20de=20l'aff?= =?UTF-8?q?ichage=20du=20texte=20sur=20les=20tablettes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/login/pronote/PronoteAuthenticationSelector.tsx | 4 +--- src/views/login/skolengo/SkolengoAuthenticationSelector.tsx | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/views/login/pronote/PronoteAuthenticationSelector.tsx b/src/views/login/pronote/PronoteAuthenticationSelector.tsx index 4b32d0231..409dd6ce6 100644 --- a/src/views/login/pronote/PronoteAuthenticationSelector.tsx +++ b/src/views/login/pronote/PronoteAuthenticationSelector.tsx @@ -50,7 +50,7 @@ const PronoteAuthenticationSelector: Screen<"PronoteAuthenticationSelector"> = ( message="Que préfères-tu utiliser pour te connecter à Pronote ?" numberOfLines={2} width={260} - offsetTop={"16%"} + offsetTop={"20%"} /> = ( = ( /> = message="Que préfères-tu pour te connecter à Skolengo ?" numberOfLines={2} width={260} - offsetTop={"16%"} + offsetTop={"20%"} /> = = /> Date: Fri, 7 Mar 2025 23:15:48 +0100 Subject: [PATCH 0745/1144] =?UTF-8?q?feat:=20am=C3=A9lioration=20de=20l'af?= =?UTF-8?q?fichage=20pour=20tablette=20et=20g=C3=A9n=C3=A9ralisation=20ave?= =?UTF-8?q?c=20`StolenkoInstanceSelector`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../login/pronote/PronoteManualLocation.tsx | 219 ++++++++---------- 1 file changed, 103 insertions(+), 116 deletions(-) diff --git a/src/views/login/pronote/PronoteManualLocation.tsx b/src/views/login/pronote/PronoteManualLocation.tsx index 89d01510e..7d09f2b49 100644 --- a/src/views/login/pronote/PronoteManualLocation.tsx +++ b/src/views/login/pronote/PronoteManualLocation.tsx @@ -1,5 +1,5 @@ import React, { createRef, useEffect, useState } from "react"; -import { Keyboard, Text, TextInput, View, StyleSheet, KeyboardEvent, ActivityIndicator, TouchableOpacity } from "react-native"; +import { Keyboard, Text, TextInput, StyleSheet, KeyboardEvent, ActivityIndicator, TouchableOpacity, View } from "react-native"; import { type GeographicMunicipality, getGeographicMunicipalities } from "@/utils/external/geo-gouv-api"; import { useDebounce } from "@/hooks/debounce"; import type { Screen } from "@/router/helpers/types"; @@ -8,7 +8,7 @@ import Reanimated, { LinearTransition, FlipInXDown, ZoomIn, ZoomOut, FadeInDown, import MaskStars from "@/components/FirstInstallation/MaskStars"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { SafeAreaView, useSafeAreaInsets } from "react-native-safe-area-context"; import { useTheme } from "@react-navigation/native"; import { Search, X } from "lucide-react-native"; import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; @@ -97,139 +97,124 @@ const PronoteManualLocation: Screen<"PronoteManualLocation"> = ({ navigation }) }, [debouncedSearch]); return ( - + + + + + + {!keyboardOpen && municipalities.results.length === 0 && ( + + + + )} + - + + + - {!keyboardOpen && municipalities.results.length == 0 && ( + {search.length > 0 && ( - + { + setSearch(""); + searchInputRef.current?.focus(); + }}> + + )} + + - - - - - { search.length > 0 && ( + {municipalities.loading ? ( - { - setSearch(""); - searchInputRef.current?.focus(); - }}> - - + + + Chargement... + - )} - - - - - {municipalities.loading ? ( + ) : ( + municipalities.results.map((municipality, index) => ( - - - Chargement... - + void navigation.navigate("PronoteInstanceSelector", { + longitude: municipality.geometry.coordinates[0], + latitude: municipality.geometry.coordinates[1], + hideDistance: true + })} + /> - ) : ( - municipalities.results.map((municipality, index) => ( - - void navigation.navigate("PronoteInstanceSelector", { - longitude: municipality.geometry.coordinates[0], - latitude: municipality.geometry.coordinates[1], - hideDistance: true - })} - /> - - )) - )} - - - - + )) + )} + + + ); }; @@ -237,9 +222,9 @@ const styles = StyleSheet.create({ container: { flex: 1, alignItems: "center", - justifyContent: "center", + // justifyContent: "center", gap: 20, - paddingTop: -40, + // paddingTop: -40, }, overScrollContainer: { @@ -249,6 +234,7 @@ const styles = StyleSheet.create({ overScroll: { width: "100%", + marginTop: -20, }, list: { @@ -258,7 +244,7 @@ const styles = StyleSheet.create({ justifyContent: "center", paddingHorizontal: 20, gap: 10, - paddingBottom: 40, + paddingBottom: 60, paddingTop: 20, }, @@ -278,7 +264,7 @@ const styles = StyleSheet.create({ searchContainer: { marginHorizontal: 16, - marginTop: 10, + marginTop: -10, flexDirection: "row", @@ -287,6 +273,7 @@ const styles = StyleSheet.create({ borderRadius: 300, gap: 12, + zIndex: 9999, }, searchInput: { From 0217d41ca66a5e56dcd1562383858d38e95ec49e Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Fri, 7 Mar 2025 23:22:07 +0100 Subject: [PATCH 0746/1144] =?UTF-8?q?fix:=20am=C3=A9lioration=20du=20rendu?= =?UTF-8?q?=20avec=20les=20tablettes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/login/pronote/PronoteManualURL.tsx | 134 +++++++++---------- 1 file changed, 66 insertions(+), 68 deletions(-) diff --git a/src/views/login/pronote/PronoteManualURL.tsx b/src/views/login/pronote/PronoteManualURL.tsx index cbe29962d..96c955542 100644 --- a/src/views/login/pronote/PronoteManualURL.tsx +++ b/src/views/login/pronote/PronoteManualURL.tsx @@ -6,12 +6,12 @@ import determinateAuthenticationView from "@/services/pronote/determinate-authen import * as Clipboard from "expo-clipboard"; -import Reanimated, { LinearTransition, ZoomIn, ZoomOut } from "react-native-reanimated"; +import Reanimated, { FadeInUp, FadeOutUp, LinearTransition, ZoomIn, ZoomOut } from "react-native-reanimated"; import MaskStars from "@/components/FirstInstallation/MaskStars"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { SafeAreaView, useSafeAreaInsets } from "react-native-safe-area-context"; import { Check, Link2, TriangleAlert, X } from "lucide-react-native"; import { useAlert } from "@/providers/AlertProvider"; import { NativeStackNavigationProp } from "@react-navigation/native-stack"; @@ -97,80 +97,78 @@ const PronoteManualURL: Screen<"PronoteManualURL"> = ({ route, navigation }) => }; return ( - + - + {!keyboardOpen && ( - - )} - - - - - { - if (instanceURL.length > 0) { - checkForDemoInstance(instanceURL, navigation, showAlert); - }; - }} + - - {instanceURL.length > 0 && ( - - { - setInstanceURL(""); - }}> - - - - )} - + )} + + + + + + { + if (instanceURL.length > 0) { + checkForDemoInstance(instanceURL, navigation, showAlert); + }; + }} + /> - + {instanceURL.length > 0 && ( + + { + setInstanceURL(""); + }}> + + + + )} + + + = ({ route, navigation }) => onPress={() => navigation.navigate("AccountSelector")} />)} - + ); }; From 9dd0656629a6a4b408752927b6ae3666c0ad960b Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Fri, 7 Mar 2025 23:32:49 +0100 Subject: [PATCH 0747/1144] =?UTF-8?q?fix:=20bouton=20d'envoi=20de=20messag?= =?UTF-8?q?es=20affich=C3=A9=20au=20centre=20sur=20les=20tablettes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Chat/Modals/Chat.tsx | 53 +++++++++++++++----------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/src/views/account/Chat/Modals/Chat.tsx b/src/views/account/Chat/Modals/Chat.tsx index 103909b41..ce7aa702b 100644 --- a/src/views/account/Chat/Modals/Chat.tsx +++ b/src/views/account/Chat/Modals/Chat.tsx @@ -336,16 +336,16 @@ const Chat: Screen<"Chat"> = ({ navigation, route }) => { /> )} /> - = ({ navigation, route }) => { onChangeText={(text) => setText(text)} value={text} /> - { - sendMessageInChat(account, route.params.handle, text); + justifyContent: "flex-end", + alignItems: "flex-end", }} > - - + { + sendMessageInChat(account, route.params.handle, text); + }} + > + + + From 5a473a0d7c1fe03d9589932197c9937ca4619afa Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 8 Mar 2025 16:14:47 +0100 Subject: [PATCH 0748/1144] =?UTF-8?q?fix(types):=20corriger=20la=20d=C3=A9?= =?UTF-8?q?finition=20des=20types=20dans=20PapillonPicker=20et=20ajouter?= =?UTF-8?q?=20une=20v=C3=A9rification=20dans=20Subject?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Global/PapillonPicker.tsx | 2 +- src/views/account/Grades/Subject/Subject.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Global/PapillonPicker.tsx b/src/components/Global/PapillonPicker.tsx index adbaf6673..9168d9216 100644 --- a/src/components/Global/PapillonPicker.tsx +++ b/src/components/Global/PapillonPicker.tsx @@ -20,7 +20,7 @@ export type PickerDataItem = string | { sfSymbol?: string, onPress?: () => {} | void, checked?: boolean -}; +} | null; type PickerData = PickerDataItem[]; diff --git a/src/views/account/Grades/Subject/Subject.tsx b/src/views/account/Grades/Subject/Subject.tsx index 640576576..4e501695d 100644 --- a/src/views/account/Grades/Subject/Subject.tsx +++ b/src/views/account/Grades/Subject/Subject.tsx @@ -117,7 +117,7 @@ const Subject: React.FC = ({ { typeof sortings[sorting] === "string" ? sortings[sorting] - : sortings[sorting].label + : sortings[sorting]?.label } {isLoading && ( From 125fd5d9f18a6ca1bdd4b1f5e2eb130195262f2b Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 8 Mar 2025 16:15:35 +0100 Subject: [PATCH 0749/1144] fix(Lessons): afficher l'alternance des semaines uniquement sur Pronote Co-authored-by: Gabriel29306 --- src/views/account/Lessons/Lessons.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index c5c694aba..08e8a1c5e 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -388,7 +388,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { navigation.navigate("LessonsImportIcal", {}); } }, - { + account.service !== AccountService.Pronote ? { icon: shouldShowWeekFrequency ? : , label: shouldShowWeekFrequency ? "Masquer alternance semaine" @@ -401,7 +401,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { setShouldShowWeekFrequency(!shouldShowWeekFrequency); }, checked: shouldShowWeekFrequency, - }, + } : null, ]} > Date: Sat, 8 Mar 2025 16:22:45 +0100 Subject: [PATCH 0750/1144] =?UTF-8?q?revert=20pr=20#700=20sur=20PapillonPi?= =?UTF-8?q?cker,=20emp=C3=AAchant=20le=20fonctionnement=20du=20``?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Global/PapillonPicker.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Global/PapillonPicker.tsx b/src/components/Global/PapillonPicker.tsx index 9168d9216..72f23a85e 100644 --- a/src/components/Global/PapillonPicker.tsx +++ b/src/components/Global/PapillonPicker.tsx @@ -196,7 +196,7 @@ const PapillonPicker: React.FC = ({ - {isNotString ? item.checked : item === selected && ( + {item === selected || (isNotString && item.checked) && ( Date: Sat, 8 Mar 2025 16:35:53 +0100 Subject: [PATCH 0751/1144] fix(PapillonPicker): remplacer TouchableOpacity par Pressable pour une meilleure gestion des interactions --- src/components/Global/PapillonPicker.tsx | 29 ++++++++---------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/components/Global/PapillonPicker.tsx b/src/components/Global/PapillonPicker.tsx index 72f23a85e..f1d959ff2 100644 --- a/src/components/Global/PapillonPicker.tsx +++ b/src/components/Global/PapillonPicker.tsx @@ -3,7 +3,7 @@ import { View, Platform, StyleSheet, type StyleProp, type ViewStyle } from "reac import { animPapillon, PapillonContextEnter, PapillonContextExit } from "@/utils/ui/animations"; import { useTheme } from "@react-navigation/native"; -import { TouchableOpacity } from "react-native-gesture-handler"; +import { Pressable } from "react-native-gesture-handler"; import Reanimated, { LinearTransition, type AnimatedStyle } from "react-native-reanimated"; import { NativeText } from "./NativeComponents"; @@ -96,20 +96,16 @@ const PapillonPicker: React.FC = ({ }), }} > - {}} - > + {}}> {children} - + ); } return ( - setOpened(!opened)} - > + setOpened(!opened)}> = ({ > {children} - + {opened && ( = ({ return ( - { setOpened(false); @@ -191,8 +187,7 @@ const PapillonPicker: React.FC = ({ )} - {label} + {label} @@ -204,7 +199,7 @@ const PapillonPicker: React.FC = ({ style={{ marginRight: 10}} /> )} - + {index === data.length - 1 ? null : ( = ({ }; const styles = StyleSheet.create({ - container: { - - }, - children: { - - }, + container: {}, + children: {}, picker: { position: "absolute", top: 0, From da4d9399eb06b90a147d837778909eb356dfbce4 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 8 Mar 2025 19:18:46 +0100 Subject: [PATCH 0752/1144] fix(AlertProvider): modifier la couleur de fond quand le bouton a `danger` sur true MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Raphaël <41128238+raphckrman@users.noreply.github.com> --- src/providers/AlertProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index 0124a2245..4d54fbd4d 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -205,7 +205,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { backgroundColor ?? colors.primary, } : danger - ? { backgroundColor: "#FC1E0D" } + ? { backgroundColor: "#BE0B00" } : { borderColor: "#CCC", borderWidth: 1 }, ]} > From bd863b81353fc421699cd5e18969f65afe313581 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 8 Mar 2025 19:25:44 +0100 Subject: [PATCH 0753/1144] fix: adaptation des boutons sur `danger` et `primary` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Raphaël <41128238+raphckrman@users.noreply.github.com> --- src/views/account/Chat/Modals/ChatCreate.tsx | 2 +- src/views/login/pronote/PronoteManualURL.tsx | 2 +- src/views/settings/ExternalAccount/IzlyActivation.tsx | 2 +- .../settings/ExternalAccount/TurboselfAccountSelector.tsx | 2 +- src/views/settings/Settings.tsx | 2 +- src/views/settings/SettingsExternalServices.tsx | 6 +++--- src/views/settings/SettingsFlags.tsx | 2 +- src/views/settings/SettingsMultiService.tsx | 2 +- src/views/settings/SettingsMultiServiceSpace.tsx | 2 +- src/views/welcome/AccountSelector.old.tsx | 2 +- src/views/welcome/DevMenu.tsx | 2 +- 11 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/views/account/Chat/Modals/ChatCreate.tsx b/src/views/account/Chat/Modals/ChatCreate.tsx index dea2a4bb2..52c5be8c7 100644 --- a/src/views/account/Chat/Modals/ChatCreate.tsx +++ b/src/views/account/Chat/Modals/ChatCreate.tsx @@ -184,7 +184,7 @@ const ChatCreate: Screen<"ChatCreate"> = ({ navigation }) => { { title: "Annuler", icon: , - primary: true, + primary: false, backgroundColor: theme.colors.primary, }, { diff --git a/src/views/login/pronote/PronoteManualURL.tsx b/src/views/login/pronote/PronoteManualURL.tsx index adc047fd1..a6c7bfad1 100644 --- a/src/views/login/pronote/PronoteManualURL.tsx +++ b/src/views/login/pronote/PronoteManualURL.tsx @@ -65,7 +65,7 @@ const PronoteManualURL: Screen<"PronoteManualURL"> = ({ route, navigation }) => title: "Continuer", icon: , onPress: () => determinateAuthenticationView(instanceURL, navigation, showAlert), - danger: true, + danger: false, delayDisable: 5, }, { diff --git a/src/views/settings/ExternalAccount/IzlyActivation.tsx b/src/views/settings/ExternalAccount/IzlyActivation.tsx index 33d55f575..d990f112e 100644 --- a/src/views/settings/ExternalAccount/IzlyActivation.tsx +++ b/src/views/settings/ExternalAccount/IzlyActivation.tsx @@ -137,7 +137,7 @@ const IzlyActivation: Screen<"IzlyActivation"> = ({ navigation, route }) => { { title: "Continuer l'activation", icon: , - primary: true, + primary: false, }, { title: "Confirmer", diff --git a/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx b/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx index 0698a1032..b818da3d2 100644 --- a/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx +++ b/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx @@ -145,7 +145,7 @@ const TurboselfAccountSelector: Screen<"TurboselfAccountSelector"> = ({ navigati { title: "Continuer la connexion", icon: , - primary: true, + primary: false, }, { title: "Confirmer", diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index 66bc38340..dab1a0d0c 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -255,7 +255,7 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { { title: "Annuler", icon: , - primary: true, + primary: false, }, { title: "Déconnexion", diff --git a/src/views/settings/SettingsExternalServices.tsx b/src/views/settings/SettingsExternalServices.tsx index c8df2d5c2..65e6d5aad 100644 --- a/src/views/settings/SettingsExternalServices.tsx +++ b/src/views/settings/SettingsExternalServices.tsx @@ -62,7 +62,7 @@ const SettingsExternalServices: Screen<"SettingsExternalServices"> = ({ { title: "OK", icon: , - primary: true, + primary: false, }, { title: "Supprimer", @@ -83,10 +83,10 @@ const SettingsExternalServices: Screen<"SettingsExternalServices"> = ({ { title: "Annuler", icon: , - primary: true, + primary: false, }, { - title: "Supprimer", + title: "Confirmer", icon: , onPress: () => removeAccount(account.localID), danger: true, diff --git a/src/views/settings/SettingsFlags.tsx b/src/views/settings/SettingsFlags.tsx index 7c603de30..b9c1594e4 100644 --- a/src/views/settings/SettingsFlags.tsx +++ b/src/views/settings/SettingsFlags.tsx @@ -77,7 +77,7 @@ const SettingsFlags: Screen<"SettingsFlags"> = ({ navigation }) => { { title: "Annuler", icon: , - primary: true, + primary: false, }, { title: "Supprimer", diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index 59960ea80..b3fc8122c 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -162,7 +162,7 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => { title: "Annuler", icon: , - primary: true, + primary: false, }, { title: "Confirmer", diff --git a/src/views/settings/SettingsMultiServiceSpace.tsx b/src/views/settings/SettingsMultiServiceSpace.tsx index e1f126085..3a639e355 100644 --- a/src/views/settings/SettingsMultiServiceSpace.tsx +++ b/src/views/settings/SettingsMultiServiceSpace.tsx @@ -91,7 +91,7 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga { title: "Annuler", icon: , - primary: true, + primary: false, }, { title: "Confirmer", diff --git a/src/views/welcome/AccountSelector.old.tsx b/src/views/welcome/AccountSelector.old.tsx index feeba2e7a..627ed5d47 100644 --- a/src/views/welcome/AccountSelector.old.tsx +++ b/src/views/welcome/AccountSelector.old.tsx @@ -414,7 +414,7 @@ const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { { title: "Annuler", icon: , - primary: true, + primary: false, }, { title: "Supprimer", diff --git a/src/views/welcome/DevMenu.tsx b/src/views/welcome/DevMenu.tsx index b58751e48..baa922bf2 100644 --- a/src/views/welcome/DevMenu.tsx +++ b/src/views/welcome/DevMenu.tsx @@ -189,7 +189,7 @@ const DevMenu: Screen<"DevMenu"> = ({ navigation }) => { { title: "Annuler", icon: , - primary: true, + primary: false, }, { title: "Réinitialiser", From fac128e92f22dca1a5e09d81fe3c1f2cc5ea0367 Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Sat, 8 Mar 2025 19:34:39 +0100 Subject: [PATCH 0754/1144] fix: empty tables --- src/views/account/Grades/Graph/GradesAverage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/account/Grades/Graph/GradesAverage.tsx b/src/views/account/Grades/Graph/GradesAverage.tsx index b624d7d7a..5acbf96c4 100644 --- a/src/views/account/Grades/Graph/GradesAverage.tsx +++ b/src/views/account/Grades/Graph/GradesAverage.tsx @@ -109,8 +109,8 @@ const GradesAverageGraph: React.FC = ({ hst = hst.filter((p) => isNaN(p.value) === false); graphRef.current?.updateData({ - xAxis: hst.map((p, i) => new Date(p.date).getTime()), - yAxis: hst.map((p) => p.value), + xAxis: hst.length > 0 ? hst.map((p, i) => new Date(p.date).getTime()) : [Date.now()], + yAxis: hst.length > 0 ? hst.map((p) => p.value) : [10], }); }, [grades, account.instance]); From a4cd174da815c8d37b511555129adb23d3821118 Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Sat, 8 Mar 2025 20:25:19 +0100 Subject: [PATCH 0755/1144] fix: empty valid grades --- src/views/account/Grades/Grades.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Grades/Grades.tsx b/src/views/account/Grades/Grades.tsx index b606a7a8e..5e48e6440 100644 --- a/src/views/account/Grades/Grades.tsx +++ b/src/views/account/Grades/Grades.tsx @@ -253,7 +253,7 @@ const Grades: Screen<"Grades"> = ({ route, navigation }) => { )} {grades[selectedPeriod] && - grades[selectedPeriod].length > 1 && ( + grades[selectedPeriod].filter((grade) => grade.student.value !== null && !isNaN(grade.student.value)).length > 1 && ( Date: Sat, 8 Mar 2025 23:25:41 +0100 Subject: [PATCH 0756/1144] fix(homepage): refresh for attendance and homeworks --- .../Home/Elements/AttendanceElement.tsx | 9 +++-- .../Home/Elements/HomeworksElement.tsx | 35 ++++++++++++++++++- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/views/account/Home/Elements/AttendanceElement.tsx b/src/views/account/Home/Elements/AttendanceElement.tsx index f9c86e928..7958c43cb 100644 --- a/src/views/account/Home/Elements/AttendanceElement.tsx +++ b/src/views/account/Home/Elements/AttendanceElement.tsx @@ -1,7 +1,6 @@ import React from "react"; import { useEffect } from "react"; import { NativeItem, NativeList, NativeListHeader } from "@/components/Global/NativeComponents"; -import { updateGradesPeriodsInCache } from "@/services/grades"; import { useCurrentAccount } from "@/stores/account"; import { useAttendanceStore } from "@/stores/attendance"; import TotalMissed from "../../Attendance/Atoms/TotalMissed"; @@ -12,6 +11,7 @@ import { log } from "@/utils/logger/logger"; import type { Attendance } from "@/services/shared/Attendance"; import { FadeInDown, FadeOut } from "react-native-reanimated"; import MissingItem from "@/components/Global/MissingItem"; +import { updateAttendanceInCache, updateAttendancePeriodsInCache } from "@/services/attendance"; interface AttendanceElementProps { @@ -38,9 +38,12 @@ const AttendanceElement: React.FC = ({ onImportance }) = useEffect(() => { void (async () => { - log("update grades periods in cache", "attendance:updateGradesPeriodsInCache"); + log("update attendance periods in cache", "attendance:updateAttendancePeriodsInCache"); if (account?.instance) { - await updateGradesPeriodsInCache(account); + await updateAttendancePeriodsInCache(account); + if (defaultPeriod) { + await updateAttendanceInCache(account, defaultPeriod); + } } ImportanceHandler(); })(); diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index d56418121..dc5af72da 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -1,6 +1,6 @@ import { NativeItem, NativeList, NativeListHeader } from "@/components/Global/NativeComponents"; import { useCurrentAccount } from "@/stores/account"; -import React, { useCallback, useEffect, useMemo } from "react"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; import { useHomeworkStore } from "@/stores/homework"; import { toggleHomeworkState, updateHomeworkForWeekInCache } from "@/services/homework"; import HomeworkItem from "../../Homeworks/Atoms/Item"; @@ -23,7 +23,10 @@ const HomeworksElement: React.FC = ({ navigation, onImpor const account = useCurrentAccount(store => store.account!); const homeworks = useHomeworkStore(store => store.homeworks); + const [loading, setLoading] = useState(true); + const actualDay = useMemo(()=>new Date(), []); + const nextWeek = useMemo(() => new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), []); const ImportanceHandler = () => { if (!homeworks[dateToEpochWeekNumber(actualDay)]) return; @@ -43,7 +46,9 @@ const HomeworksElement: React.FC = ({ navigation, onImpor const updateHomeworks = useCallback(async () => { await updateHomeworkForWeekInCache(account, actualDay); + await updateHomeworkForWeekInCache(account, nextWeek); ImportanceHandler(); + setLoading(false); }, [account, actualDay]); const debouncedUpdateHomeworks = useMemo(() => debounce(updateHomeworks, 500), [updateHomeworks]); @@ -81,6 +86,34 @@ const HomeworksElement: React.FC = ({ navigation, onImpor (hw) => hw.due / 1000 >= startTime && hw.due / 1000 <= endTime ) ?? []; + if (loading) { + return ( + <> + <> + + )} + /> + + + + + + + + ); + } + if (hwSemaineActuelle.length === 0 && hwSemaineProchaine.length === 0) { return ( <> From 51b26859d85a6a16d9dc468c0c1176a94ba0f99d Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Sun, 9 Mar 2025 11:47:26 +0100 Subject: [PATCH 0757/1144] force reload to keep updated EDT --- src/views/account/Home/Elements/TimetableElement.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Home/Elements/TimetableElement.tsx b/src/views/account/Home/Elements/TimetableElement.tsx index cbca06ce8..067a87ab0 100644 --- a/src/views/account/Home/Elements/TimetableElement.tsx +++ b/src/views/account/Home/Elements/TimetableElement.tsx @@ -88,7 +88,7 @@ const TimetableElement: React.FC = ({ onImportance }) => }; const fetchTimetable = async () => { - if (!timetables[currentWeekNumber] && account.instance) { + if (account.instance) { setLoading(true); try { await updateTimetableForWeekInCache(account, currentWeekNumber); From 2e8a938026fa727e73ee5d2890aba8751e8af4d1 Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Sun, 9 Mar 2025 11:48:03 +0100 Subject: [PATCH 0758/1144] fix(homepage): add loading for attendance --- .../Home/Elements/AttendanceElement.tsx | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/views/account/Home/Elements/AttendanceElement.tsx b/src/views/account/Home/Elements/AttendanceElement.tsx index 7958c43cb..5838bcd69 100644 --- a/src/views/account/Home/Elements/AttendanceElement.tsx +++ b/src/views/account/Home/Elements/AttendanceElement.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState } from "react"; import { useEffect } from "react"; import { NativeItem, NativeList, NativeListHeader } from "@/components/Global/NativeComponents"; import { useCurrentAccount } from "@/stores/account"; @@ -23,6 +23,8 @@ const AttendanceElement: React.FC = ({ onImportance }) = const defaultPeriod = useAttendanceStore((store) => store.defaultPeriod) as string | null; const attendances = useAttendanceStore((store) => store.attendances) as Record | null; + const [loading, setLoading] = useState(false); + const ImportanceHandler = () => { if (attendances && defaultPeriod) { const totalMissed = formatTotalMissed(attendances[defaultPeriod]); @@ -93,6 +95,34 @@ const AttendanceElement: React.FC = ({ onImportance }) = }; }; + if (loading) { + return ( + <> + <> + + )} + /> + + + + + + + + ); + } + if (!totalMissed || totalMissed.absences.length === 0) { return ( <> From f8a73ee25fa570e94bcc7069647e00713d0f8ee8 Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Sun, 9 Mar 2025 13:05:53 +0100 Subject: [PATCH 0759/1144] fix(homepage): add account.instance check --- src/views/account/Home/Elements/HomeworksElement.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index dc5af72da..6031867fc 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -45,10 +45,12 @@ const HomeworksElement: React.FC = ({ navigation, onImpor }; const updateHomeworks = useCallback(async () => { - await updateHomeworkForWeekInCache(account, actualDay); - await updateHomeworkForWeekInCache(account, nextWeek); - ImportanceHandler(); - setLoading(false); + if (account.instance) { + await updateHomeworkForWeekInCache(account, actualDay); + await updateHomeworkForWeekInCache(account, nextWeek); + ImportanceHandler(); + setLoading(false); + } }, [account, actualDay]); const debouncedUpdateHomeworks = useMemo(() => debounce(updateHomeworks, 500), [updateHomeworks]); From 69539e7e4fa9504b344eb3ece47153cf6418f1ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl?= <41128238+raphckrman@users.noreply.github.com> Date: Sun, 9 Mar 2025 20:33:10 +0100 Subject: [PATCH 0760/1144] fix(homepage): set default loading to false MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 𝕂𝕪𝕝𝕚𝕒𝕟 <164187100+Kgeek33@users.noreply.github.com> --- src/views/account/Home/Elements/HomeworksElement.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 6031867fc..aa078c880 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -23,7 +23,7 @@ const HomeworksElement: React.FC = ({ navigation, onImpor const account = useCurrentAccount(store => store.account!); const homeworks = useHomeworkStore(store => store.homeworks); - const [loading, setLoading] = useState(true); + const [loading, setLoading] = useState(false); const actualDay = useMemo(()=>new Date(), []); const nextWeek = useMemo(() => new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), []); From 5969efef0f4b33b43be59f63a5121bcbfcfee9b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl?= <41128238+raphckrman@users.noreply.github.com> Date: Sun, 9 Mar 2025 20:33:21 +0100 Subject: [PATCH 0761/1144] fix: indentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 𝕂𝕪𝕝𝕚𝕒𝕟 <164187100+Kgeek33@users.noreply.github.com> --- src/views/account/Home/Elements/HomeworksElement.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index aa078c880..0a1242e22 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -25,7 +25,7 @@ const HomeworksElement: React.FC = ({ navigation, onImpor const [loading, setLoading] = useState(false); - const actualDay = useMemo(()=>new Date(), []); + const actualDay = useMemo(()=> new Date(), []); const nextWeek = useMemo(() => new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), []); const ImportanceHandler = () => { From 71a608e3ddb7064e6ef541ec2901b64a10d3da87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl?= <41128238+raphckrman@users.noreply.github.com> Date: Sun, 9 Mar 2025 20:33:35 +0100 Subject: [PATCH 0762/1144] fix: check for account.instance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 𝕂𝕪𝕝𝕚𝕒𝕟 <164187100+Kgeek33@users.noreply.github.com> --- src/views/account/Home/Elements/HomeworksElement.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 0a1242e22..9ef45bf33 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -46,6 +46,7 @@ const HomeworksElement: React.FC = ({ navigation, onImpor const updateHomeworks = useCallback(async () => { if (account.instance) { + setLoading(true); await updateHomeworkForWeekInCache(account, actualDay); await updateHomeworkForWeekInCache(account, nextWeek); ImportanceHandler(); From c2753f3834d3931c3e3bbe95744cb2e41ed7b56c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl?= <41128238+raphckrman@users.noreply.github.com> Date: Sun, 9 Mar 2025 20:33:49 +0100 Subject: [PATCH 0763/1144] remove useless log MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 𝕂𝕪𝕝𝕚𝕒𝕟 <164187100+Kgeek33@users.noreply.github.com> --- src/views/account/Home/Elements/AttendanceElement.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/views/account/Home/Elements/AttendanceElement.tsx b/src/views/account/Home/Elements/AttendanceElement.tsx index 5838bcd69..ccd2f3b03 100644 --- a/src/views/account/Home/Elements/AttendanceElement.tsx +++ b/src/views/account/Home/Elements/AttendanceElement.tsx @@ -40,7 +40,6 @@ const AttendanceElement: React.FC = ({ onImportance }) = useEffect(() => { void (async () => { - log("update attendance periods in cache", "attendance:updateAttendancePeriodsInCache"); if (account?.instance) { await updateAttendancePeriodsInCache(account); if (defaultPeriod) { From 5342a8f4a55275006ed53a6dcdeef217822e75eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl?= <41128238+raphckrman@users.noreply.github.com> Date: Sun, 9 Mar 2025 20:34:28 +0100 Subject: [PATCH 0764/1144] fix: duplicate key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 𝕂𝕪𝕝𝕚𝕒𝕟 <164187100+Kgeek33@users.noreply.github.com> --- src/views/account/Home/Elements/AttendanceElement.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Home/Elements/AttendanceElement.tsx b/src/views/account/Home/Elements/AttendanceElement.tsx index ccd2f3b03..189c45c35 100644 --- a/src/views/account/Home/Elements/AttendanceElement.tsx +++ b/src/views/account/Home/Elements/AttendanceElement.tsx @@ -105,7 +105,7 @@ const AttendanceElement: React.FC = ({ onImportance }) = /> From 406e17b5a4be1617052e0222366485b6656651a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl?= <41128238+raphckrman@users.noreply.github.com> Date: Sun, 9 Mar 2025 20:34:42 +0100 Subject: [PATCH 0765/1144] fix: duplicate key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 𝕂𝕪𝕝𝕚𝕒𝕟 <164187100+Kgeek33@users.noreply.github.com> --- src/views/account/Home/Elements/HomeworksElement.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 9ef45bf33..af1ba9983 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -100,7 +100,7 @@ const HomeworksElement: React.FC = ({ navigation, onImpor /> From cf172704d139d9c3703cd73d6cf03f004de516ec Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Sun, 9 Mar 2025 20:36:17 +0100 Subject: [PATCH 0766/1144] fix: lint --- src/views/account/Home/Elements/AttendanceElement.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/views/account/Home/Elements/AttendanceElement.tsx b/src/views/account/Home/Elements/AttendanceElement.tsx index 189c45c35..08d1c532f 100644 --- a/src/views/account/Home/Elements/AttendanceElement.tsx +++ b/src/views/account/Home/Elements/AttendanceElement.tsx @@ -7,7 +7,6 @@ import TotalMissed from "../../Attendance/Atoms/TotalMissed"; import { PressableScale } from "react-native-pressable-scale"; import RedirectButton from "@/components/Home/RedirectButton"; import { PapillonNavigation } from "@/router/refs"; -import { log } from "@/utils/logger/logger"; import type { Attendance } from "@/services/shared/Attendance"; import { FadeInDown, FadeOut } from "react-native-reanimated"; import MissingItem from "@/components/Global/MissingItem"; From 55e81d9a0becc5aa9d6b56f340654d7050e09ba0 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Mar 2025 22:27:17 +0100 Subject: [PATCH 0767/1144] fix: manage loading state during attendance updates --- src/views/account/Home/Elements/AttendanceElement.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/views/account/Home/Elements/AttendanceElement.tsx b/src/views/account/Home/Elements/AttendanceElement.tsx index 08d1c532f..9cab3d762 100644 --- a/src/views/account/Home/Elements/AttendanceElement.tsx +++ b/src/views/account/Home/Elements/AttendanceElement.tsx @@ -40,10 +40,12 @@ const AttendanceElement: React.FC = ({ onImportance }) = useEffect(() => { void (async () => { if (account?.instance) { + setLoading(true); await updateAttendancePeriodsInCache(account); if (defaultPeriod) { await updateAttendanceInCache(account, defaultPeriod); } + setLoading(false); } ImportanceHandler(); })(); From a4ed965ab2cdca314aff1a50ad45afb0f31e5ebd Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Mar 2025 22:30:20 +0100 Subject: [PATCH 0768/1144] fix: indentation v2 --- src/views/account/Home/Elements/HomeworksElement.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index af1ba9983..2df2dbef4 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -25,7 +25,7 @@ const HomeworksElement: React.FC = ({ navigation, onImpor const [loading, setLoading] = useState(false); - const actualDay = useMemo(()=> new Date(), []); + const actualDay = useMemo(() => new Date(), []); const nextWeek = useMemo(() => new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), []); const ImportanceHandler = () => { From 0c6329e9dec5a584b13ab4858a379a7d19ffdb07 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Mar 2025 22:32:11 +0100 Subject: [PATCH 0769/1144] fix: simplify loading state management in timetable update --- src/views/account/Home/Elements/TimetableElement.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/views/account/Home/Elements/TimetableElement.tsx b/src/views/account/Home/Elements/TimetableElement.tsx index 067a87ab0..a1ef72e86 100644 --- a/src/views/account/Home/Elements/TimetableElement.tsx +++ b/src/views/account/Home/Elements/TimetableElement.tsx @@ -90,11 +90,8 @@ const TimetableElement: React.FC = ({ onImportance }) => const fetchTimetable = async () => { if (account.instance) { setLoading(true); - try { - await updateTimetableForWeekInCache(account, currentWeekNumber); - } finally { - setLoading(false); - } + await updateTimetableForWeekInCache(account, currentWeekNumber); + setLoading(false); } }; From 68225b794d5a5d190ff97bbe16f946cd52d9f511 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Mar 2025 22:34:50 +0100 Subject: [PATCH 0770/1144] fix: manage loading state during grades update --- .../account/Home/Elements/GradesElement.tsx | 45 +++++++++++++++---- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/src/views/account/Home/Elements/GradesElement.tsx b/src/views/account/Home/Elements/GradesElement.tsx index 6e4423b9c..bcec0b867 100644 --- a/src/views/account/Home/Elements/GradesElement.tsx +++ b/src/views/account/Home/Elements/GradesElement.tsx @@ -20,6 +20,8 @@ const GradesElement: React.FC = ({ onImportance }) => { const defaultPeriod = useGradesStore(store => store.defaultPeriod); const grades = useGradesStore((store) => store.grades); + const [loading, setLoading] = useState(false); + const ImportanceHandler = () => { if (grades && grades[defaultPeriod] && grades[defaultPeriod].length > 0) { let score = 0; @@ -38,18 +40,17 @@ const GradesElement: React.FC = ({ onImportance }) => { useEffect(() => { void async function () { - if (!account?.instance) return; - await updateGradesPeriodsInCache(account); + if (account?.instance) { + setLoading(true); + await updateGradesPeriodsInCache(account); + if (defaultPeriod) { + await updateGradesAndAveragesInCache(account, defaultPeriod); + } + setLoading(false); + } }(); }, [account?.instance]); - useEffect(() => { - void async function () { - if (!account?.instance || !defaultPeriod) return; - await updateGradesAndAveragesInCache(account, defaultPeriod); - }(); - }, [defaultPeriod]); - const [lastThreeGrades, setLastThreeGrades] = useState = ({ onImportance }) => { } }, [grades]); + if (loading) { + return ( + <> + + )} + /> + + + + + + + ); + } + if (!grades || lastThreeGrades.length === 0) { return ( Date: Sun, 9 Mar 2025 22:48:52 +0100 Subject: [PATCH 0771/1144] update `package-lock.json` --- package-lock.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package-lock.json b/package-lock.json index aaecf9dac..4a02a5d52 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7978,6 +7978,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/kossnocorp" From 5cdacf352cbea48d53ad85641274b67708a9d824 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Mar 2025 22:50:42 +0100 Subject: [PATCH 0772/1144] refactor(DateHelper): simplifier la fonction de formatage des dates en utilisant date-fns --- src/utils/format/DateHelper.ts | 74 +++++++--------------------------- 1 file changed, 15 insertions(+), 59 deletions(-) diff --git a/src/utils/format/DateHelper.ts b/src/utils/format/DateHelper.ts index 05ab4751f..068c1db7f 100644 --- a/src/utils/format/DateHelper.ts +++ b/src/utils/format/DateHelper.ts @@ -1,72 +1,28 @@ -export const timestampToString = (timestamp: number) => { +import { + formatDistanceToNow, + isToday, + isTomorrow, + isYesterday, +} from "date-fns"; +import { fr } from "date-fns/locale"; +export const timestampToString = (timestamp: number) => { if (!timestamp || Number.isNaN(timestamp)) { return "Date invalide"; } const date = new Date(timestamp); - const today = new Date(); if (Number.isNaN(date.getTime())) { return "Date invalide"; } - today.setHours(0, 0, 0, 0); - date.setHours(0, 0, 0, 0); - - let formattedDate: string; - - let dateDifference = [ - date.getFullYear() - today.getFullYear(), - date.getMonth() - today.getMonth(), - date.getDate() - today.getDate(), - ]; - - let yearDifference = dateDifference[0]; - if (dateDifference[1] < 0 || (dateDifference[1] === 0 && dateDifference[2] < 0)) { - yearDifference--; - } else if (dateDifference[1] > 0 || (dateDifference[1] === 0 && dateDifference[2] > 0)) { - yearDifference++; - } - let monthDifference = dateDifference[0] * 12 + dateDifference[1]; - - if (dateDifference[2] < 0) { - monthDifference--; - } else if (dateDifference[2] > 0) { - monthDifference++; - } - let dayDifference = Math.round( - (date.getTime() - today.getTime()) / (1000 * 60 * 60 * 24) - ); - - if (yearDifference < 0) { - formattedDate = `Il y a ${Math.abs(yearDifference)} an${ - Math.abs(yearDifference) > 1 ? "s" : "" - }`; - } else if (yearDifference > 0) { - formattedDate = `Dans ${yearDifference} an${yearDifference > 1 ? "s" : ""}`; + if (isToday(date)) { + return "Aujourd'hui"; + } else if (isTomorrow(date)) { + return "Demain"; + } else if (isYesterday(date)) { + return "Hier"; } else { - if (monthDifference < 0) { - formattedDate = `Il y a ${Math.abs(monthDifference)} mois`; - } else if (monthDifference > 0) { - formattedDate = `Dans ${monthDifference} mois`; - } else { - if (dayDifference < -2) { - formattedDate = `Il y a ${Math.abs(dayDifference)} jours`; - } else if (dayDifference === -2) { - formattedDate = "Avant-hier"; - } else if (dayDifference === -1) { - formattedDate = "Hier"; - } else if (dayDifference === 0) { - formattedDate = "Aujourd'hui"; - } else if (dayDifference === 1) { - formattedDate = "Demain"; - } else if (dayDifference === 2) { - formattedDate = "Après-demain"; - } else { - formattedDate = `Dans ${dayDifference} jours`; - } - } + return formatDistanceToNow(date, { addSuffix: true, locale: fr }); } - - return formattedDate; }; From 23496b6a91800319665aaa776fd534f6249fc01c Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Mar 2025 22:55:08 +0100 Subject: [PATCH 0773/1144] =?UTF-8?q?fix(formatDate):=20am=C3=A9liorer=20l?= =?UTF-8?q?e=20formatage=20des=20dates=20en=20utilisant=20date-fns=20et=20?= =?UTF-8?q?g=C3=A9rer=20les=20dates=20invalides?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/format/format_date_complets.ts | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/src/utils/format/format_date_complets.ts b/src/utils/format/format_date_complets.ts index 624d99d1a..2ef073229 100644 --- a/src/utils/format/format_date_complets.ts +++ b/src/utils/format/format_date_complets.ts @@ -1,23 +1,14 @@ +import { formatDistanceToNow } from "date-fns"; +import { fr } from "date-fns/locale"; + function formatDate (date: string): string { - const currentDate = new Date(); const messageDate = new Date(date); - const diffTime = Math.abs(currentDate.getTime() - messageDate.getTime()); - const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24)); - const diffMonths = Math.floor(diffDays / 30); - if (diffDays < 1) { - return "Aujourd'hui"; - } else if (diffDays < 30) { - return `Il y a ${diffDays} jour${diffDays !== 1 ? "s" : ""}`; - } else if (diffMonths === 1) { - return "Il y a 1 mois"; - } else { - return `Le ${messageDate.getDay().toString().padStart(2, "0")}/${( - messageDate.getMonth() + 1 - ) - .toString() - .padStart(2, "0")}/${messageDate.getFullYear()}`; + if (Number.isNaN(messageDate.getTime())) { + return "Date invalide"; } + + return formatDistanceToNow(messageDate, { addSuffix: true, locale: fr }); } export default formatDate; From 6fb2be61cf061c384e9969458155234bf5a6460e Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Mar 2025 22:58:19 +0100 Subject: [PATCH 0774/1144] =?UTF-8?q?delete(format=5Fdate):=20supprimer=20?= =?UTF-8?q?la=20fonction=20de=20formatage=20de=20date=20non=20utilis=C3=A9?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/format/format_date.ts | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 src/utils/format/format_date.ts diff --git a/src/utils/format/format_date.ts b/src/utils/format/format_date.ts deleted file mode 100644 index 47aaa28a2..000000000 --- a/src/utils/format/format_date.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * @example - * dateAsShortString(new Date('2021-12-31')) // '31/12/2021' - */ -export const dateAsShortString = (date: Date): string => { - return date.toLocaleDateString("en-GB"); -}; \ No newline at end of file From 447371aeee2f3bbe269631d3066f67a71fab9e3e Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Mar 2025 23:00:50 +0100 Subject: [PATCH 0775/1144] =?UTF-8?q?fix(attendance=5Ftime):=20am=C3=A9lio?= =?UTF-8?q?rer=20le=20calcul=20du=20temps=20d'absence=20en=20utilisant=20d?= =?UTF-8?q?ate-fns?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/format/attendance_time.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/utils/format/attendance_time.ts b/src/utils/format/attendance_time.ts index e93bd317a..7d7b01ceb 100644 --- a/src/utils/format/attendance_time.ts +++ b/src/utils/format/attendance_time.ts @@ -1,3 +1,5 @@ +import { differenceInHours, differenceInMinutes } from "date-fns"; + const leadingZero = (num: number) => { return num < 10 ? `0${num}` : num; }; @@ -5,13 +7,16 @@ const leadingZero = (num: number) => { const getAbsenceTime = (fromTimestamp: number, toTimestamp: number) => { const from = new Date(fromTimestamp); const to = new Date(toTimestamp); - const diff = to.getTime() - from.getTime(); + + const hours = differenceInHours(to, from); + const minutes = differenceInMinutes(to, from) % 60; + return { - diff: diff, - hours: Math.floor(diff / 1000 / 60 / 60), - withMinutes: leadingZero(Math.floor(diff / 1000 / 60) % 60), - minutes: Math.floor(diff / 1000 / 60), + diff: to.getTime() - from.getTime(), + hours, + withMinutes: leadingZero(minutes), + minutes: differenceInMinutes(to, from), }; }; -export { getAbsenceTime, leadingZero }; \ No newline at end of file +export { getAbsenceTime, leadingZero }; From 4cdb90a8a083d390db40092ae7358a2f11bba0d1 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 9 Mar 2025 23:02:36 +0100 Subject: [PATCH 0776/1144] =?UTF-8?q?fix(formatDate):=20am=C3=A9liorer=20l?= =?UTF-8?q?e=20formatage=20des=20dates=20en=20utilisant=20date-fns?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/ecoledirecte/format-date.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/services/ecoledirecte/format-date.ts b/src/services/ecoledirecte/format-date.ts index a7327789e..135789894 100644 --- a/src/services/ecoledirecte/format-date.ts +++ b/src/services/ecoledirecte/format-date.ts @@ -1,7 +1,5 @@ -export const formatDate = (date: Date): string => { - const year = date.getFullYear(); - const month = (date.getMonth() + 1).toString().padStart(2, "0"); // months are 0-indexed, so +1 - const day = date.getDate().toString().padStart(2, "0"); +import { format } from "date-fns"; - return `${year}-${month}-${day}`; +export const formatDate = (date: Date): string => { + return format(date, "yyyy-MM-dd"); }; From f65e37c254589fa027d0e66f5a9fb716488d4f07 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 11 Mar 2025 11:43:02 +0100 Subject: [PATCH 0777/1144] =?UTF-8?q?fix(Document):=20supprimer=20la=20lim?= =?UTF-8?q?ite=20maxWidth=20pour=20une=20meilleure=20r=C3=A9activit=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Grades/Document.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/views/account/Grades/Document.tsx b/src/views/account/Grades/Document.tsx index 52f7ec225..67780ffa2 100644 --- a/src/views/account/Grades/Document.tsx +++ b/src/views/account/Grades/Document.tsx @@ -460,7 +460,6 @@ const GradeDocument: Screen<"GradeDocument"> = ({ route, navigation }) => { style={{ flex: 1, width: "100%", - maxWidth: 500, backgroundColor: theme.colors.background, borderCurve: "continuous", overflow: "hidden", From c2af36b3815ed7577f2118d71f68dc8931c1bf6d Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 11 Mar 2025 11:47:59 +0100 Subject: [PATCH 0778/1144] =?UTF-8?q?fix(Week):=20ajouter=20une=20v=C3=A9r?= =?UTF-8?q?ification=20pour=20=C3=A9viter=20les=20erreurs=20si=20timetable?= =?UTF-8?q?s=20est=20ind=C3=A9fini?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/screens/index.ts | 1 - src/views/account/Week/Week.tsx | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/router/screens/index.ts b/src/router/screens/index.ts index 1eb0e9ba3..8c7022e89 100644 --- a/src/router/screens/index.ts +++ b/src/router/screens/index.ts @@ -17,7 +17,6 @@ export default [ presentation: Platform.OS === "android" ? "modal" : "formSheet", animation: Platform.OS === "android" ? "slide_from_right" : "default", animationDuration: 100, - sheetCornerRadius: 24, }), createScreen("AccountStack", AccountScreen, { diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index 6a43485f6..371263771 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -242,6 +242,8 @@ const Week: Screen<"Week"> = ({ route, navigation }) => { const [events, setEvents] = React.useState([]); useEffect(() => { + if(!timetables) return; + const nevts = Object.values(timetables) .flat() .map(event => ({ From cafa0cce184f2a3dca925d3b0b38210ed85e213c Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 11 Mar 2025 11:56:49 +0100 Subject: [PATCH 0779/1144] =?UTF-8?q?fix(Lessons):=20ajuster=20la=20largeu?= =?UTF-8?q?r=20des=20=C3=A9l=C3=A9ments=20en=20fonction=20des=20dimensions?= =?UTF-8?q?=20de=20l'=C3=A9cran?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Lessons/Lessons.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index c5c694aba..8a5f62d66 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -36,6 +36,7 @@ import {AccountService} from "@/stores/account/types"; import {hasFeatureAccountSetup} from "@/utils/multiservice"; import {MultiServiceFeature} from "@/stores/multiService/types"; import { fetchIcalData } from "@/services/local/ical"; +import useScreenDimensions from "@/hooks/useScreenDimensions"; const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const account = useCurrentAccount((store) => store.account!); @@ -43,6 +44,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const mutateProperty = useCurrentAccount((store) => store.mutateProperty); const timetables = useTimetableStore((store) => store.timetables); + const {isTablet} = useScreenDimensions(); const outsideNav = route.params?.outsideNav; const insets = useSafeAreaInsets(); @@ -99,6 +101,8 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const [showDatePicker, setShowDatePicker] = useState(false); + const finalWidth = Dimensions.get("window").width - (isTablet ? 320 : 0); + const loadTimetableWeek = async (weekNumber: number, force = false) => { if ( (currentlyLoadingWeeks.current.has(weekNumber) || @@ -157,7 +161,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const renderItem = useCallback(({ item: date }: { item: Date }) => { const weekNumber = getWeekFromDate(date); return ( - + = ({ route, navigation }) => { ); const getItemLayout = useCallback((_: any, index: number) => ({ - length: Dimensions.get("window").width, - offset: Dimensions.get("window").width * index, + length: finalWidth, + offset: finalWidth * index, index, }), [], From cf4fedd6787d7d9e2bd7d02b042a8211cd8845bb Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 11 Mar 2025 12:14:13 +0100 Subject: [PATCH 0780/1144] =?UTF-8?q?fix(Animations):=20ajuster=20les=20pa?= =?UTF-8?q?ram=C3=A8tres=20d'=C3=A9chelle=20pour=20les=20animations=20d'en?= =?UTF-8?q?tr=C3=A9e=20et=20de=20sortie=20fix(Lessons):=20ajouter=20un=20d?= =?UTF-8?q?=C3=A9calage=20sup=C3=A9rieur=20pour=20le=20modal=20de=20s?= =?UTF-8?q?=C3=A9lection=20de=20date=20fix(LessonsHeader):=20am=C3=A9liore?= =?UTF-8?q?r=20l'importation=20des=20animations=20et=20la=20gestion=20de?= =?UTF-8?q?=20l'affichage=20du=20modal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/ui/animations.ts | 20 +- src/views/account/Lessons/Lessons.tsx | 1 + src/views/account/Lessons/LessonsHeader.tsx | 214 +++++++++++--------- 3 files changed, 136 insertions(+), 99 deletions(-) diff --git a/src/utils/ui/animations.ts b/src/utils/ui/animations.ts index 11be7f53e..7dbef7d12 100644 --- a/src/utils/ui/animations.ts +++ b/src/utils/ui/animations.ts @@ -13,9 +13,12 @@ const anim2Papillon = (animation: any) => { }; const EnteringDuration = 180; -const EnteringScale = 0.8; +const EnteringScaleX = 0.8; +const EnteringScaleY = 0.65; + const ExitingDuration = 120; -const ExitingScale = 0.9; +const ExitingScaleX = 0.9; +const ExitingScaleY = 0.7; // Paramètres d'animation pour l'entrée du menu contextuel const PapillonAnimSettings = { @@ -36,14 +39,18 @@ const PapillonContextEnter = () => { opacity: withTiming(1, PapillonAnimSettings), transform: [ { - scale: withTiming(1, PapillonAnimSettings), + scaleX: withTiming(1, PapillonAnimSettings), + }, + { + scaleY: withTiming(1, PapillonAnimSettings), }, ], }; const initialValues = { opacity: 0, transform: [ - { scale: EnteringScale }, + { scaleX: EnteringScaleX}, + { scaleY: EnteringScaleY }, ], }; return { @@ -58,12 +65,13 @@ const PapillonContextExit = () => { const animations = { opacity: withTiming(0, PapillonAnimSettingsExit), transform: [ - { scale: withTiming(ExitingScale, PapillonAnimSettingsExit) }, + { scaleX: withTiming(ExitingScaleX, PapillonAnimSettingsExit) }, + { scaleY: withTiming(ExitingScaleY, PapillonAnimSettingsExit) }, ], }; const initialValues = { opacity: 1, - transform: [{ scale: 1 }], + transform: [{ scaleX: 1 }, { scaleY: 1 }], }; return { initialValues, diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index 8a5f62d66..cb397d224 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -442,6 +442,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { /> , getDateFromIndex?: (index: number) => Date @@ -53,11 +55,25 @@ const LessonsDateModal: React.FC = ({ showDatePicker, setShowDatePicker, onDateSelect, - currentDate + currentDate, + topOffset }) => { const { colors } = useTheme(); const insets = useSafeAreaInsets(); + const [showModalPicker, setShowModalPicker] = React.useState(false); + const {isTablet} = useScreenDimensions(); + + React.useEffect(() => { + if (showDatePicker) { + setShowModalPicker(true); + } else { + setTimeout(() => { + setShowModalPicker(false); + }, 200); + } + }, [showDatePicker]); + if (Platform.OS === "android") { return ( showDatePicker && ( @@ -84,108 +100,120 @@ const LessonsDateModal: React.FC = ({ return ( - - setShowDatePicker(false)} - /> - - {showDatePicker && ( - + - setShowDatePicker(false)} + /> + + {showDatePicker && ( + - - Sélection de la date - - - {new Date(currentDate).toLocaleDateString("fr-FR", { weekday: "long", day: "numeric", month: "long" })} - + + Sélection de la date + + + {new Date(currentDate).toLocaleDateString("fr-FR", { weekday: "long", day: "numeric", month: "long" })} + - setShowDatePicker(false)} + > + + + + setShowDatePicker(false)} - > - - - - { - const newSelectedDate = selectedDate || currentDate; - // set hours to 0 - newSelectedDate.setHours(0, 0, 0, 0); - onDateSelect(newSelectedDate); - }} - /> - - )} - + value={new Date(currentDate)} + display="inline" + mode="date" + locale="fr-FR" + accentColor={colors.primary} + onChange={(_event, selectedDate) => { + const newSelectedDate = selectedDate || currentDate; + // set hours to 0 + newSelectedDate.setHours(0, 0, 0, 0); + onDateSelect(newSelectedDate); + }} + /> + + )} + + )} ); }; From 4fbdb721ef8905d4c9844a6370ee6357fa7b637e Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 11 Mar 2025 13:27:21 +0100 Subject: [PATCH 0781/1144] =?UTF-8?q?fix(Podfile):=20ajouter=20expo-dev-la?= =?UTF-8?q?uncher=20et=20swiftui-react-native=20aux=20d=C3=A9pendances?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Papillon.xcodeproj/project.pbxproj | 6 +- .../AppIcon.appiconset/Contents.json | 42 ++----- ios/Podfile.lock | 116 +++++++++++++++++- package-lock.json | 11 ++ package.json | 1 + 5 files changed, 139 insertions(+), 37 deletions(-) diff --git a/ios/Papillon.xcodeproj/project.pbxproj b/ios/Papillon.xcodeproj/project.pbxproj index 98dafc58d..8bf658ffe 100644 --- a/ios/Papillon.xcodeproj/project.pbxproj +++ b/ios/Papillon.xcodeproj/project.pbxproj @@ -316,6 +316,7 @@ "${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-launcher/EXDevLauncher.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-menu/EXDevMenu.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/LottiePrivacyInfo.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/lottie-react-native/Lottie_React_Native_Privacy.bundle", @@ -332,6 +333,7 @@ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevLauncher.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevMenu.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/LottiePrivacyInfo.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Lottie_React_Native_Privacy.bundle", @@ -453,7 +455,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = "Papillon"; + PRODUCT_NAME = Papillon; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -486,7 +488,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = "Papillon"; + PRODUCT_NAME = Papillon; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json index e91cbd33d..90d8d4c2a 100644 --- a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,38 +1,14 @@ { - "images" : [ + "images": [ { - "filename" : "Icon-Light-1024x1024.png", - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "Icon-Dark-1024x1024.png", - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "tinted" - } - ], - "filename" : "Icon-Tinted-1024x1024.png", - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" + "filename": "App-Icon-1024x1024@1x.png", + "idiom": "universal", + "platform": "ios", + "size": "1024x1024" } ], - "info" : { - "author" : "xcode", - "version" : 1 + "info": { + "version": 1, + "author": "expo" } -} +} \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 1a5a9ea21..3917628a1 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -30,6 +30,98 @@ PODS: - ExpoModulesCore - Expo (51.0.39): - ExpoModulesCore + - expo-dev-client (4.0.29): + - EXManifests + - expo-dev-launcher + - expo-dev-menu + - expo-dev-menu-interface + - EXUpdatesInterface + - expo-dev-launcher (4.0.29): + - DoubleConversion + - EXManifests + - expo-dev-launcher/Main (= 4.0.29) + - expo-dev-menu + - expo-dev-menu-interface + - ExpoModulesCore + - EXUpdatesInterface + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Codegen + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsinspector + - React-NativeModulesApple + - React-RCTAppDelegate + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - expo-dev-launcher/Main (4.0.29): + - DoubleConversion + - EXManifests + - expo-dev-launcher/Unsafe + - expo-dev-menu + - expo-dev-menu-interface + - ExpoModulesCore + - EXUpdatesInterface + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Codegen + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsinspector + - React-NativeModulesApple + - React-RCTAppDelegate + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - expo-dev-launcher/Unsafe (4.0.29): + - DoubleConversion + - EXManifests + - expo-dev-menu + - expo-dev-menu-interface + - ExpoModulesCore + - EXUpdatesInterface + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Codegen + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsinspector + - React-NativeModulesApple + - React-RCTAppDelegate + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga - expo-dev-menu (5.0.23): - DoubleConversion - expo-dev-menu/Main (= 5.0.23) @@ -236,6 +328,8 @@ PODS: - EXTaskManager (11.8.2): - ExpoModulesCore - UMAppLoader + - EXUpdatesInterface (0.16.2): + - ExpoModulesCore - FBLazyVector (0.74.6) - fmt (9.1.0) - glog (0.3.5) @@ -1600,7 +1694,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNScreens (4.6.0): + - RNScreens (4.9.1): - DoubleConversion - glog - hermes-engine @@ -1646,6 +1740,8 @@ PODS: - RNSVG (15.8.0): - React-Core - SocketRocket (0.7.0) + - SwiftUIReactNative (5.0.0): + - ExpoModulesCore - UMAppLoader (4.6.0) - VisualEffectBlurView (7.2.0): - DGSwiftUtilities (~> 0.46) @@ -1669,6 +1765,8 @@ DEPENDENCIES: - EXLocation (from `../node_modules/expo-location/ios`) - EXManifests (from `../node_modules/expo-manifests/ios`) - Expo (from `../node_modules/expo`) + - expo-dev-client (from `../node_modules/expo-dev-client/ios`) + - expo-dev-launcher (from `../node_modules/expo-dev-launcher`) - expo-dev-menu (from `../node_modules/expo-dev-menu`) - expo-dev-menu-interface (from `../node_modules/expo-dev-menu-interface/ios`) - ExpoAsset (from `../node_modules/expo-asset/ios`) @@ -1694,6 +1792,7 @@ DEPENDENCIES: - ExpoWebBrowser (from `../node_modules/expo-web-browser/ios`) - EXSplashScreen (from `../node_modules/expo-splash-screen/ios`) - EXTaskManager (from `../node_modules/expo-task-manager/ios`) + - EXUpdatesInterface (from `../node_modules/expo-updates-interface/ios`) - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`) - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) @@ -1766,6 +1865,7 @@ DEPENDENCIES: - RNScreens (from `../node_modules/react-native-screens`) - RNShare (from `../node_modules/react-native-share`) - RNSVG (from `../node_modules/react-native-svg`) + - SwiftUIReactNative (from `../node_modules/swiftui-react-native/ios`) - UMAppLoader (from `../node_modules/unimodules-app-loader/ios`) - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) @@ -1804,6 +1904,10 @@ EXTERNAL SOURCES: :path: "../node_modules/expo-manifests/ios" Expo: :path: "../node_modules/expo" + expo-dev-client: + :path: "../node_modules/expo-dev-client/ios" + expo-dev-launcher: + :path: "../node_modules/expo-dev-launcher" expo-dev-menu: :path: "../node_modules/expo-dev-menu" expo-dev-menu-interface: @@ -1854,6 +1958,8 @@ EXTERNAL SOURCES: :path: "../node_modules/expo-splash-screen/ios" EXTaskManager: :path: "../node_modules/expo-task-manager/ios" + EXUpdatesInterface: + :path: "../node_modules/expo-updates-interface/ios" FBLazyVector: :path: "../node_modules/react-native/Libraries/FBLazyVector" fmt: @@ -1995,6 +2101,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-share" RNSVG: :path: "../node_modules/react-native-svg" + SwiftUIReactNative: + :path: "../node_modules/swiftui-react-native/ios" UMAppLoader: :path: "../node_modules/unimodules-app-loader/ios" Yoga: @@ -2016,6 +2124,8 @@ SPEC CHECKSUMS: EXLocation: 43e9b582ca63a23c6f0a18d8cbe2145b3a388b55 EXManifests: c1fab4c3237675e7b0299ea8df0bcb14baca4f42 Expo: 8c995afb875c15bf8439af0b20bcb9ed8f90d0bd + expo-dev-client: 0cec8ec81fd01c10d9afcd9f6de3768b10644aee + expo-dev-launcher: c510982f3437742353389750748520580af0ee43 expo-dev-menu: 12b319a9bc73d76a1ed47ce52055b5356edb971d expo-dev-menu-interface: 5764ad537419c1a5e8f66f668e29c81e8aca290c ExpoAsset: 323700f291684f110fb55f0d4022a3362ea9f875 @@ -2041,6 +2151,7 @@ SPEC CHECKSUMS: ExpoWebBrowser: 7595ccac6938eb65b076385fd23d035db9ecdc8e EXSplashScreen: 17a656c08a0095be15b620c52e61dfdb665863d2 EXTaskManager: 9c3520305c3aa1b4a12a7c6d1e3f85f2779c06e9 + EXUpdatesInterface: 996527fd7d1a5d271eb523258d603f8f92038f24 FBLazyVector: 4b1589d37c9ff4dba11a63083fe7515fad3ac111 fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120 glog: fdfdfe5479092de0c4bdbebedd9056951f092c4f @@ -2109,10 +2220,11 @@ SPEC CHECKSUMS: RNGestureHandler: f7abf21d594742be28a3a72528069225a3187e26 RNNotifee: 935f3ea8c134c88cbf8b13ea0c97c72c09ad2116 RNReanimated: 35f9ac9c3ac42d0497ebd1cce5c39d7687a8493e - RNScreens: 0063befb42dd85ae53a106b5d3529f2e5cd63643 + RNScreens: 3cafbf1a9de9fa8ddb547ada94704cbb59540f39 RNShare: 22717e910836a66cb7255b3b8c4ab06cbe346e27 RNSVG: 8b1a777d54096b8c2a0fd38fc9d5a454332bbb4d SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d + SwiftUIReactNative: 3241f6b3d9cf6dc29b3dc64f7d838770f19fd604 UMAppLoader: f17a5ee8e85b536ace0fc254b447a37ed198d57e VisualEffectBlurView: 2db84754ea90a2f4eccb0e84038653caddaa4eff Yoga: 4f4f07a17818e76d1b04edc01b68b6d49a682100 diff --git a/package-lock.json b/package-lock.json index aaecf9dac..e68cb0f04 100644 --- a/package-lock.json +++ b/package-lock.json @@ -102,6 +102,7 @@ "react-native-webview": "13.8.6", "reanimated-color-picker": "^3.0.4", "scolengo-api": "^3.0.5", + "swiftui-react-native": "^6.3.3", "text-encoding": "^0.7.0", "turboself-api": "^2.1.8", "zustand": "^4.5.2" @@ -16642,6 +16643,16 @@ "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", "dev": true }, + "node_modules/swiftui-react-native": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/swiftui-react-native/-/swiftui-react-native-6.3.3.tgz", + "integrity": "sha512-/3CygnDsGkXFat+1LRujW1a7WSw/KOCl62CbGTOFLWQNOE3uGM0tylNO4PuiUDilxDqc/krOkjGORbplNfZYxw==", + "peerDependencies": { + "expo": "*", + "react": "*", + "react-native": "*" + } + }, "node_modules/tar": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", diff --git a/package.json b/package.json index 6ad5f7651..d616bcb4a 100644 --- a/package.json +++ b/package.json @@ -104,6 +104,7 @@ "react-native-webview": "13.8.6", "reanimated-color-picker": "^3.0.4", "scolengo-api": "^3.0.5", + "swiftui-react-native": "^6.3.3", "text-encoding": "^0.7.0", "turboself-api": "^2.1.8", "zustand": "^4.5.2" From 181275e78f1eb96f77bea1447e0f9fd4dcb21c36 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 11 Mar 2025 18:37:33 +0100 Subject: [PATCH 0782/1144] =?UTF-8?q?fix(App):=20ajouter=20la=20v=C3=A9rif?= =?UTF-8?q?ication=20du=20compte=20actuel=20avant=20le=20rafra=C3=AEchisse?= =?UTF-8?q?ment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/App.tsx b/App.tsx index c88baa930..128d874bf 100644 --- a/App.tsx +++ b/App.tsx @@ -25,6 +25,7 @@ const BACKGROUND_LIMITS: Partial> = { export default function App () { const [appState, setAppState] = useState(AppState.currentState); const backgroundStartTime = useRef(null); + const currentAccount = useCurrentAccount((store) => store.account); const switchTo = useCurrentAccount((store) => store.switchTo); const accounts: PrimaryAccount[] = useAccounts((store) => store.accounts) .filter(account => !account.isExternal) as PrimaryAccount[]; @@ -59,7 +60,7 @@ export default function App () { log(`Account type: ${serviceName}`, "RefreshToken"); log(`Using ${BACKGROUND_LIMITS[account.service] ? "specific" : "default"} time limit`, "RefreshToken"); - if (timeInBackground >= timeLimit) { + if (timeInBackground >= timeLimit && currentAccount === account) { log(`⚠️ Refreshing account ${account.studentName.first} ${account.studentName.last} after ${timeInBackgroundSeconds}s in background`, "RefreshToken"); // Prevent React state updates during render From da017b32de29305c3ff02020e044ba6082464a11 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 11 Mar 2025 18:38:47 +0100 Subject: [PATCH 0783/1144] =?UTF-8?q?fix(NativeComponents):=20supprimer=20?= =?UTF-8?q?la=20propri=C3=A9t=C3=A9=20flex=20pour=20am=C3=A9liorer=20l'ali?= =?UTF-8?q?gnement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Global/NativeComponents.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Global/NativeComponents.tsx b/src/components/Global/NativeComponents.tsx index c16a080d8..7bc120dc4 100644 --- a/src/components/Global/NativeComponents.tsx +++ b/src/components/Global/NativeComponents.tsx @@ -142,7 +142,6 @@ export const NativeListHeader: React.FC = ({ icon, label, Date: Tue, 11 Mar 2025 18:44:45 +0100 Subject: [PATCH 0784/1144] =?UTF-8?q?fix(GradesScodocUE):=20ajuster=20les?= =?UTF-8?q?=20animations=20d'entr=C3=A9e=20et=20de=20sortie=20pour=20une?= =?UTF-8?q?=20transition=20plus=20fluide?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Grades/Atoms/GradesScodocUE.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views/account/Grades/Atoms/GradesScodocUE.tsx b/src/views/account/Grades/Atoms/GradesScodocUE.tsx index 7f4c2b94a..64b25fe39 100644 --- a/src/views/account/Grades/Atoms/GradesScodocUE.tsx +++ b/src/views/account/Grades/Atoms/GradesScodocUE.tsx @@ -12,7 +12,7 @@ import { memo, useState } from "react"; import { Image, View } from "react-native"; import { TouchableOpacity } from "react-native-gesture-handler"; -import Reanimated, { FadeIn, FadeInDown, FadeOut, FadeOutUp, LinearTransition } from "react-native-reanimated"; +import Reanimated, { FadeIn, FadeInDown, FadeInUp, FadeOut, FadeOutDown, FadeOutUp, LinearTransition } from "react-native-reanimated"; const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: PrimaryAccount, navigation: any, selectedPeriod: string }) => { try { @@ -209,8 +209,8 @@ const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: Prim > {opened ? : } From 76911dc8d45460ce417f055fd8664d90c093d315 Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Tue, 11 Mar 2025 19:14:01 +0100 Subject: [PATCH 0785/1144] fix(widgets): add qrcode navigation --- src/widgets/Components/RestaurantQRCode.tsx | 76 +++++++++++++++++---- 1 file changed, 64 insertions(+), 12 deletions(-) diff --git a/src/widgets/Components/RestaurantQRCode.tsx b/src/widgets/Components/RestaurantQRCode.tsx index f9d31253e..462fa42a1 100644 --- a/src/widgets/Components/RestaurantQRCode.tsx +++ b/src/widgets/Components/RestaurantQRCode.tsx @@ -14,6 +14,8 @@ import { qrcodeFromExternal } from "@/services/qrcode"; import { useNavigation } from "@react-navigation/native"; import { StackNavigationProp } from "@react-navigation/stack"; import { RouteParameters } from "./../../router/helpers/types"; +import { formatCardIdentifier, ServiceCard } from "@/views/account/Restaurant/Menu"; +import { STORE_THEMES } from "@/views/account/Restaurant/Cards/StoreThemes"; type NavigationProps = StackNavigationProp; @@ -25,36 +27,75 @@ const RestaurantQRCodeWidget = forwardRef(({ const theme = useTheme(); const { colors } = theme; - const account = useCurrentAccount((store) => store.account); const linkedAccounts = useCurrentAccount(store => store.linkedAccounts); - const [qrcode, setQRCodes] = useState | null>(null); const navigation = useNavigation(); + const [allCards, setAllCards] = useState | null>(null); + const [currentCardIndex, setCurrentCardIndex] = useState(0); useImperativeHandle(ref, () => ({ handlePress: () => { - // navigation.navigate("RestaurantQrCode", { QrCodes: qrcode ?? [] }); + if (currentCard) { + navigation.navigate("RestaurantQrCode", { card: currentCard }); + } } })); + const getWeekNumber = (date: Date) => { + const firstDayOfYear = new Date(date.getFullYear(), 0, 1); + const pastDaysOfYear = (date.getTime() - firstDayOfYear.getTime()) / 86400000; + return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7); + }; + useEffect(() => { void async function () { setHidden(true); setLoading(true); - const qrcodes: Array = []; + const newCards: Array = []; const currentHour = new Date().getHours(); - for (const account of linkedAccounts) { - if (account.service === AccountService.Turboself || account.service === AccountService.ARD) { - const cardNumber = await qrcodeFromExternal(account); - if (cardNumber) qrcodes.push(cardNumber); + const accountPromises = linkedAccounts.map(async (account) => { + try { + const [cardnumber] = await Promise.all([ + qrcodeFromExternal(account).catch(err => { + console.warn(`Error fetching QR code for account ${account}:`, err); + return "0"; + }), + ]); + + const newCard: ServiceCard = { + service: account.service, + identifier: account.username, + account: account, + balance: [], + history: [], + cardnumber: cardnumber, + theme: STORE_THEMES.find((theme) => theme.id === AccountService[account.service]) ?? STORE_THEMES[0], + }; + + newCards.push(newCard); + } catch (error) { + console.warn(`An error occurred with account ${account}:`, error); } - } + }); - setQRCodes(qrcodes); - setHidden(qrcodes.length === 0 || currentHour < 11 || currentHour > 14); + await Promise.all(accountPromises); + setAllCards(newCards); + setHidden(allCards?.length === 0 || currentHour < 11 || currentHour > 14); setLoading(false); }(); }, [linkedAccounts, setHidden]); + useEffect(() => { + if (allCards && allCards.length > 1) { + const interval = setInterval(() => { + setCurrentCardIndex((prevIndex) => (prevIndex + 1) % allCards.length); + }, 5000); + + return () => clearInterval(interval); + } + }, [allCards]); + + const currentCard = allCards?.[currentCardIndex]; + return ( <> - {qrcode && qrcode.length > 1 ? "Toucher pour afficher les QR-Codes" : "Toucher pour afficher le QR-Code"} + Toucher pour afficher le QR-Code + + + {formatCardIdentifier(currentCard?.account?.localID as string)} Date: Tue, 11 Mar 2025 20:15:33 +0100 Subject: [PATCH 0786/1144] =?UTF-8?q?fix(AnimatedNumber,=20DateHelper):=20?= =?UTF-8?q?am=C3=A9liorer=20la=20cl=C3=A9=20d'animation=20et=20simplifier?= =?UTF-8?q?=20le=20formatage=20de=20date?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Global/AnimatedNumber.tsx | 2 +- src/utils/format/DateHelper.ts | 77 +++--------------------- 2 files changed, 8 insertions(+), 71 deletions(-) diff --git a/src/components/Global/AnimatedNumber.tsx b/src/components/Global/AnimatedNumber.tsx index fcbf06bc1..bb2107fcd 100644 --- a/src/components/Global/AnimatedNumber.tsx +++ b/src/components/Global/AnimatedNumber.tsx @@ -45,7 +45,7 @@ const AnimatedNumber: React.FC = ({ > {value.toString().split("").map((n: string, i: number) => ( { - - if (!timestamp || Number.isNaN(timestamp)) { - return "Date invalide"; - } - - const date = new Date(timestamp); - const today = new Date(); - if (Number.isNaN(date.getTime())) { - return "Date invalide"; - } - - today.setHours(0, 0, 0, 0); - date.setHours(0, 0, 0, 0); - - let formattedDate: string; +import { formatDistance } from "date-fns"; +import { fr } from "date-fns/locale"; - let dateDifference = [ - date.getFullYear() - today.getFullYear(), - date.getMonth() - today.getMonth(), - date.getDate() - today.getDate(), - ]; - - let yearDifference = dateDifference[0]; - if (dateDifference[1] < 0 || (dateDifference[1] === 0 && dateDifference[2] < 0)) { - yearDifference--; - } else if (dateDifference[1] > 0 || (dateDifference[1] === 0 && dateDifference[2] > 0)) { - yearDifference++; - } - let monthDifference = dateDifference[0] * 12 + dateDifference[1]; - - if (dateDifference[2] < 0) { - monthDifference--; - } else if (dateDifference[2] > 0) { - monthDifference++; - } - let dayDifference = Math.round( - (date.getTime() - today.getTime()) / (1000 * 60 * 60 * 24) - ); - - if (yearDifference < 0) { - formattedDate = `Il y a ${Math.abs(yearDifference)} an${ - Math.abs(yearDifference) > 1 ? "s" : "" - }`; - } else if (yearDifference > 0) { - formattedDate = `Dans ${yearDifference} an${yearDifference > 1 ? "s" : ""}`; - } else { - if (monthDifference < 0) { - formattedDate = `Il y a ${Math.abs(monthDifference)} mois`; - } else if (monthDifference > 0) { - formattedDate = `Dans ${monthDifference} mois`; - } else { - if (dayDifference < -2) { - formattedDate = `Il y a ${Math.abs(dayDifference)} jours`; - } else if (dayDifference === -2) { - formattedDate = "Avant-hier"; - } else if (dayDifference === -1) { - formattedDate = "Hier"; - } else if (dayDifference === 0) { - formattedDate = "Aujourd'hui"; - } else if (dayDifference === 1) { - formattedDate = "Demain"; - } else if (dayDifference === 2) { - formattedDate = "Après-demain"; - } else { - formattedDate = `Dans ${dayDifference} jours`; - } - } - } - - return formattedDate; +export const timestampToString = (timestamp: number) => { + return formatDistance(new Date(timestamp), new Date(), { + locale: fr, + addSuffix: true, + }); }; From 63e774ec7c77a3e8963c2d5fb5a4c044ce774d08 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 11 Mar 2025 20:18:04 +0100 Subject: [PATCH 0787/1144] =?UTF-8?q?fix(AttendanceItem):=20ajouter=20le?= =?UTF-8?q?=20formatage=20de=20timestamp=20et=20am=C3=A9liorer=20l'afficha?= =?UTF-8?q?ge=20des=20=C3=A9tats=20de=20justification?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Attendance/Atoms/AttendanceItem.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/views/account/Attendance/Atoms/AttendanceItem.tsx b/src/views/account/Attendance/Atoms/AttendanceItem.tsx index 7ba2e6143..30a91751b 100644 --- a/src/views/account/Attendance/Atoms/AttendanceItem.tsx +++ b/src/views/account/Attendance/Atoms/AttendanceItem.tsx @@ -9,6 +9,7 @@ import { NativeItem, NativeList, NativeText } from "@/components/Global/NativeCo import { leadingZero } from "@/utils/format/attendance_time"; import { animPapillon } from "@/utils/ui/animations"; import { useTheme } from "@react-navigation/native"; +import { timestampToString } from "@/utils/format/DateHelper"; interface AttendanceItemProps { title: string @@ -152,19 +153,22 @@ const AttendanceItem: React.FC = ({ {not_justified ? ( - Non justifié ) : ( - Justifié )} + + {timestampToString(timestamp)} + {dateString} From 0d6588f53ea21df3fef3478f455bed418189b5b3 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 11 Mar 2025 20:20:28 +0100 Subject: [PATCH 0788/1144] =?UTF-8?q?fix(TotalMissed):=20remplacer=20View?= =?UTF-8?q?=20par=20Reanimated.View=20et=20ajouter=20des=20styles=20pour?= =?UTF-8?q?=20am=C3=A9liorer=20l'animation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Attendance/Atoms/TotalMissed.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/views/account/Attendance/Atoms/TotalMissed.tsx b/src/views/account/Attendance/Atoms/TotalMissed.tsx index dcd7b8730..3c5493574 100644 --- a/src/views/account/Attendance/Atoms/TotalMissed.tsx +++ b/src/views/account/Attendance/Atoms/TotalMissed.tsx @@ -3,7 +3,7 @@ import { View } from "react-native"; import { NativeList, NativeText } from "@/components/Global/NativeComponents"; import { leadingZero } from "@/utils/format/attendance_time"; import { animPapillon } from "@/utils/ui/animations"; -import { FadeIn, FadeOut } from "react-native-reanimated"; +import Reanimated, { FadeIn, FadeOut } from "react-native-reanimated"; import { useTheme } from "@react-navigation/native"; import AnimatedNumber from "@/components/Global/AnimatedNumber"; @@ -37,11 +37,12 @@ const TotalMissed = ({ totalMissed }: TotalMissedProps) => { }} > - { }} /> { > h {leadingZero(totalMissed.total.minutes)} - + Date: Tue, 11 Mar 2025 20:21:57 +0100 Subject: [PATCH 0789/1144] fix(Lessons): suppression des doublons --- src/views/account/Lessons/Lessons.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index ac24dfe1f..b02f71cea 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -44,7 +44,6 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const mutateProperty = useCurrentAccount((store) => store.mutateProperty); const timetables = useTimetableStore((store) => store.timetables); - const {isTablet} = useScreenDimensions(); const outsideNav = route.params?.outsideNav; const insets = useSafeAreaInsets(); @@ -107,8 +106,6 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const [showDatePicker, setShowDatePicker] = useState(false); - const finalWidth = Dimensions.get("window").width - (isTablet ? 320 : 0); - const loadTimetableWeek = async (weekNumber: number, force = false) => { if ( (currentlyLoadingWeeks.current.has(weekNumber) || From 9cc83ab3680a98ee425c6c9fdff7ec4cb4c2312d Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 11 Mar 2025 20:26:04 +0100 Subject: [PATCH 0790/1144] =?UTF-8?q?fix(Attendance,=20Week):=20ajuster=20?= =?UTF-8?q?le=20contr=C3=B4le=20de=20rafra=C3=AEchissement=20et=20am=C3=A9?= =?UTF-8?q?liorer=20le=20style=20des=20=C3=A9l=C3=A9ments=20annul=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Attendance/Attendance.tsx | 1 + src/views/account/Week/Week.tsx | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/views/account/Attendance/Attendance.tsx b/src/views/account/Attendance/Attendance.tsx index f2f02da65..c3d01dad0 100644 --- a/src/views/account/Attendance/Attendance.tsx +++ b/src/views/account/Attendance/Attendance.tsx @@ -231,6 +231,7 @@ const Attendance: Screen<"Attendance"> = ({ route, navigation }) => { refreshControl={ { setIsRefreshing(true); if(account.identityProvider?.identifier) { diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index 371263771..d470297a5 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -429,7 +429,9 @@ const styles = StyleSheet.create({ }, canceledContainer: { borderColor: "red", - borderWidth: 2, + borderWidth: 4, + borderStyle: "dotted", + backgroundColor: "transparent", }, alertBadge: { position: "absolute", @@ -456,8 +458,9 @@ const styles = StyleSheet.create({ borderWidth: 0, }, canceledContent: { - opacity: 0.3, + opacity: 0.5, backgroundColor: "grey", + borderWidth: 0, }, title: { fontSize: 13, From f6a586530d84cb07060313ffc141694afae5409e Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 11 Mar 2025 20:27:26 +0100 Subject: [PATCH 0791/1144] fix(Week): ajuster la taille de police pour les titres et les salles --- src/views/account/Week/Week.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index d470297a5..3adda8a64 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -463,7 +463,7 @@ const styles = StyleSheet.create({ borderWidth: 0, }, title: { - fontSize: 13, + fontSize: 11.5, letterSpacing: 0.2, fontFamily: "semibold", textTransform: "uppercase", @@ -475,7 +475,7 @@ const styles = StyleSheet.create({ textTransform: "none", }, room: { - fontSize: 13, + fontSize: 11, letterSpacing: 0.2, fontFamily: "medium", opacity: 0.6, From fb74c7c660c2fcb026d296920e276d744ae57e4b Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Tue, 11 Mar 2025 20:43:19 +0100 Subject: [PATCH 0792/1144] feat(settings): add menthe et cristaux --- .../Settings/SupportContainerCard.tsx | 36 ++++ src/router/helpers/types.ts | 1 + src/router/screens/settings/index.ts | 4 + src/views/settings/Settings.tsx | 9 +- src/views/settings/SettingsSupport.tsx | 180 ++++++++++++++++++ 5 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 src/components/Settings/SupportContainerCard.tsx create mode 100644 src/views/settings/SettingsSupport.tsx diff --git a/src/components/Settings/SupportContainerCard.tsx b/src/components/Settings/SupportContainerCard.tsx new file mode 100644 index 000000000..6201a2ab4 --- /dev/null +++ b/src/components/Settings/SupportContainerCard.tsx @@ -0,0 +1,36 @@ +import React from "react"; + +import { View } from "react-native"; +import { NativeItem, NativeList, NativeText } from "../Global/NativeComponents"; +import { MessageCircleQuestion } from "lucide-react-native"; + +const SupportContainerCard = ({ theme }: { theme: any }) => { + const { colors } = theme; + + return ( + + + + + + + Un problème, une question ? + + + Laissez-nous un message depuis cette page et nous vous répondrons dans les plus brefs délais ! + + + + ); +}; + +export default SupportContainerCard; \ No newline at end of file diff --git a/src/router/helpers/types.ts b/src/router/helpers/types.ts index 4e82ee30d..9097b283b 100644 --- a/src/router/helpers/types.ts +++ b/src/router/helpers/types.ts @@ -130,6 +130,7 @@ export type RouteParameters = { SettingsProfile: undefined; SettingsTabs: undefined; SettingsAbout: undefined; + SettingsSupport: undefined; SettingsIcons: undefined; SettingsSubjects: undefined; SettingsExternalServices: undefined; diff --git a/src/router/screens/settings/index.ts b/src/router/screens/settings/index.ts index 9dcddcd3c..66b1069be 100644 --- a/src/router/screens/settings/index.ts +++ b/src/router/screens/settings/index.ts @@ -4,6 +4,7 @@ import SettingsNotifications from "@/views/settings/SettingsNotifications"; import SettingsProfile from "@/views/settings/SettingsProfile"; import SettingsTrophies from "@/views/settings/SettingsTrophies"; import SettingsAbout from "@/views/settings/SettingsAbout"; +import SettingsSupport from "@/views/settings/SettingsSupport"; import SettingsIcons from "@/views/settings/SettingsIcons"; import SettingsSubjects from "@/views/settings/SettingsSubjects"; import SettingsExternalServices from "@/views/settings/SettingsExternalServices"; @@ -49,6 +50,9 @@ const settingsScreens = [ createScreen("SettingsAbout", SettingsAbout, { headerTitle: "À propos", }), + createScreen("SettingsSupport", SettingsSupport, { + headerTitle: "Contacter le support", + }), createScreen("SettingsIcons", SettingsIcons, { headerTitle: "Icônes", }), diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index 72c13a94a..c9e176057 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -35,7 +35,8 @@ import { X, Blocks, HelpCircle, - PersonStanding + PersonStanding, + Bug } from "lucide-react-native"; import { NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; @@ -231,6 +232,12 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { label: "Besoin d'aide ?", onPress: () => openUrl("https://support.papillon.bzh/"), }, + { + icon: , + color: "#CF0029", + label: "Signaler un problème", + onPress: () => navigation.navigate("SettingsSupport"), + }, { icon: , color: "#888888", diff --git a/src/views/settings/SettingsSupport.tsx b/src/views/settings/SettingsSupport.tsx new file mode 100644 index 000000000..4de0fdb49 --- /dev/null +++ b/src/views/settings/SettingsSupport.tsx @@ -0,0 +1,180 @@ +import React, { useState } from "react"; +import { ScrollView, StyleSheet, TextInput, View } from "react-native"; +import type { Screen } from "@/router/helpers/types"; +import { useTheme } from "@react-navigation/native"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import SupportContainerCard from "@/components/Settings/SupportContainerCard"; +import PapillonCheckbox from "@/components/Global/PapillonCheckbox"; +import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; +import { Mail, Tag, Text } from "lucide-react-native"; +import ButtonCta from "@/components/FirstInstallation/ButtonCta"; +import { get_logs, Log } from "@/utils/logger/logger"; + +const SettingsSupport: Screen<"SettingsSupport"> = ({ navigation }) => { + const theme = useTheme(); + const insets = useSafeAreaInsets(); + + const [sendLogs, setSendLogs] = useState(true); + const [email, setEmail] = useState(); + const [subject, setSubject] = useState(); + const [description, setDescription] = useState(); + + const handlePress = async () => { + const logs: Log[] = await get_logs(); + const formattedLogs = logs + .filter((log) => log.type === "ERROR") + .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()) + .map((log) => { + if (!log.date) return `[${log.type}] ${log.message}`; + + const logDate = new Date(log.date); + if (isNaN(logDate.getTime())) return `[${log.type}] ${log.message}`; + + return `[${log.date}] [${log.type}] ${log.message}`; + }) + .join("
"); + + const data = { + email: email, + title: subject, + detail: `Description de mon problème:
${description}

Journaux:
${formattedLogs}`, + }; + + const response = await fetch("https://api-menthe-et-cristaux.papillon.bzh/api/v1/ticket/public/create", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }); + + const result = await response.json(); + }; + + return ( + + + + + }> + Adresse E-Mail + + + }> + Sujet + + + }> + Description + + + + + + { + setSendLogs(!sendLogs); + }} + /> + }> + J’accepte de transmettre les journaux et les données du formulaire pour le traitement de ma demande + + + + handlePress()} /> + + + ); +}; + +// Styles +const styles = StyleSheet.create({ + title: { + color: "#222222", + fontSize: 15, + }, + time: { + color: "#3F3F3F", + opacity: 0.5, + textAlign: "right", + fontFamily: "sfmedium", + fontSize: 13, + marginRight: 10, + }, + message: { + color: "#3F3F3F", + fontFamily: "sfmedium", + fontSize: 14, + maxWidth: "85%", + minWidth: "85%", + lineHeight: 15, + letterSpacing: -0.4, + }, + + overlay: { + backgroundColor: "#EEF5F5", + borderWidth: 1, + borderColor: "#00000030", + borderRadius: 20, + height: 25, + padding: 9, + marginHorizontal: 20, + }, + + fixedButtonContainer: { + position: "absolute", + bottom: 0, + width: "100%", + padding: 10, + borderTopWidth: 1, + borderTopColor: "#ddd", + } +}); + +export default SettingsSupport; From 1c1de2a4212988493d6eeff465e7d358f78036b7 Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Tue, 11 Mar 2025 20:45:52 +0100 Subject: [PATCH 0793/1144] fix(support): change checks --- src/views/settings/SettingsSupport.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/settings/SettingsSupport.tsx b/src/views/settings/SettingsSupport.tsx index 4de0fdb49..e07c8c381 100644 --- a/src/views/settings/SettingsSupport.tsx +++ b/src/views/settings/SettingsSupport.tsx @@ -123,11 +123,11 @@ const SettingsSupport: Screen<"SettingsSupport"> = ({ navigation }) => { }} /> }> - J’accepte de transmettre les journaux et les données du formulaire pour le traitement de ma demande + J’accepte de transmettre les journaux d'erreurs et les données du formulaire pour le traitement de ma demande
- handlePress()} /> + handlePress()} /> ); From e55b1377687d0f5c929f7c85f58ce737f0d8769f Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 11 Mar 2025 20:59:03 +0100 Subject: [PATCH 0794/1144] =?UTF-8?q?fix(RestaurantCardDetail):=20ajouter?= =?UTF-8?q?=20des=20liens=20pour=20g=C3=A9rer=20la=20carte=20et=20am=C3=A9?= =?UTF-8?q?liorer=20l'affichage=20du=20titre?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Global/PapillonPicker.tsx | 6 +- src/router/screens/views/index.ts | 5 +- .../account/Restaurant/Cards/StoreThemes.ts | 18 ++- src/views/account/Restaurant/Menu.tsx | 4 + .../account/Restaurant/Modals/CardDetail.tsx | 113 +++++++++++++++--- 5 files changed, 123 insertions(+), 23 deletions(-) diff --git a/src/components/Global/PapillonPicker.tsx b/src/components/Global/PapillonPicker.tsx index adbaf6673..47a52e870 100644 --- a/src/components/Global/PapillonPicker.tsx +++ b/src/components/Global/PapillonPicker.tsx @@ -19,7 +19,8 @@ export type PickerDataItem = string | { icon?: JSX.Element, sfSymbol?: string, onPress?: () => {} | void, - checked?: boolean + checked?: boolean, + destructive?: boolean, }; type PickerData = PickerDataItem[]; @@ -86,6 +87,8 @@ const PapillonPicker: React.FC = ({ actionSubtitle: item.subtitle, // @ts-ignore menuState: (item.checked || item === selected) ? "on" : "off", + // @ts-ignore + menuAttributes: [item.destructive ? "destructive" : "normal"], icon: { type: typeof item !== "string" ? "IMAGE_SYSTEM" : "IMAGE_SYSTEM", imageValue: { @@ -98,6 +101,7 @@ const PapillonPicker: React.FC = ({ > {}} + activeOpacity={0.3} > {children} diff --git a/src/router/screens/views/index.ts b/src/router/screens/views/index.ts index d2eba31c9..a36d85fe2 100644 --- a/src/router/screens/views/index.ts +++ b/src/router/screens/views/index.ts @@ -43,13 +43,14 @@ export default [ createScreen("RestaurantCardDetail", RestaurantCardDetail, { headerTitle: "Détail de la carte", presentation: "formSheet", - headerShown: false, + headerShown: true, + headerLargeTitle: true, + headerTransparent: true, sheetCornerRadius: 16, sheetGrabberVisible: true, sheetExpandsWhenScrolledToEdge: true, // @ts-expect-error sheetInitialDetent: 0, - sheetAllowedDetents: "all", }), createScreen("SettingsTabs", SettingsTabs, { headerTitle: "Onglets et navigation", diff --git a/src/views/account/Restaurant/Cards/StoreThemes.ts b/src/views/account/Restaurant/Cards/StoreThemes.ts index 42d3fc5bd..3c986e2a6 100644 --- a/src/views/account/Restaurant/Cards/StoreThemes.ts +++ b/src/views/account/Restaurant/Cards/StoreThemes.ts @@ -1,3 +1,4 @@ + export interface StoreTheme { id: string; name: string; @@ -7,6 +8,14 @@ export interface StoreTheme { accent: string; }; background: any; + links?: [ + { + label: string; + subtitle?: string; + sfSymbol?: string; + url: string; + } + ]; } export const STORE_THEMES = [ @@ -29,6 +38,14 @@ export const STORE_THEMES = [ accent: "#DD1314", }, background: require("../../../../../assets/images/cards/Carte_Cover_Izly.png"), + links: [ + { + label: "Recharger ou gérer ma carte", + subtitle: "Mon Espace Izly", + sfSymbol: "arrow.up.forward.app", + url: "https://mon-espace.izly.fr/", + }, + ], }, { id: "Turboself", @@ -60,5 +77,4 @@ export const STORE_THEMES = [ }, background: require("../../../../../assets/images/cards/Carte_Cover_Alise.png"), }, - ]; \ No newline at end of file diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index ee8ed4c31..4b73c78e0 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -205,6 +205,10 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { const dailyMenu = account ? await getMenu(account, pickerDate).catch(() => null) : null; const accountPromises = linkedAccounts.map(async (account) => { try { + if (!account || !account.service) { + return; + } + const [balance, history, cardnumber, booking] = await Promise.all([ balanceFromExternal(account, isRefreshing).catch(err => { console.warn(`Error fetching balance for account ${account}:`, err); diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index 829b22849..d8c800e44 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -1,8 +1,7 @@ -import { Image, ScrollView, Text, View } from "react-native"; +import { Alert, Image, Linking, Platform, ScrollView, Text, View } from "react-native"; import MenuCard from "../Cards/Card"; import Reanimated from "react-native-reanimated"; import React, { useState } from "react"; -import { LinearGradient } from "expo-linear-gradient"; import { NativeItem, NativeList, NativeText } from "@/components/Global/NativeComponents"; import { formatDistance } from "date-fns"; @@ -11,13 +10,16 @@ import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; import { useTheme } from "@react-navigation/native"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; import { PressableScale } from "react-native-pressable-scale"; -import { useCurrentAccount } from "@/stores/account"; +import { useAccounts, useCurrentAccount } from "@/stores/account"; import { AccountService, ExternalAccount } from "@/stores/account/types"; -import { QrCode } from "lucide-react-native"; +import { ExternalLink, MoreHorizontal, QrCode, Trash2 } from "lucide-react-native"; import { balanceFromExternal } from "@/services/balance"; import { reservationHistoryFromExternal } from "@/services/reservation-history"; import { Screen } from "@/router/helpers/types"; import { formatCardIdentifier } from "../Menu"; +import { LinearGradient } from "expo-linear-gradient"; +import { TouchableOpacity } from "react-native-gesture-handler"; +import PapillonPicker from "@/components/Global/PapillonPicker"; const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigation }) => { try { @@ -27,6 +29,9 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio const theme = useTheme(); const account = useCurrentAccount((store) => store.account); + const removeAccount = useAccounts((state) => state.remove); + + const cardName = `Carte ${AccountService[route.params.card.service as AccountService]} ${account?.identity?.firstName ? "de " + account.identity.firstName : ""}`; const updateCardData = async () => { const [balance, history] = await Promise.all([ @@ -57,21 +62,73 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio return unsubscribe; }, []); + React.useLayoutEffect(() => { + navigation.setOptions({ + headerTitle: cardName ?? "Détail de la carte", + headerLargeTitleStyle: { + color: "transparent", + }, + headerLargeStyle: { + backgroundColor: "transparent", + }, + headerStyle: { + backgroundColor: theme.colors.card + "55", + }, + headerBlurEffect: "regular", + headerRight: () => ( + ({ + label: link.label, + subtitle: link.subtitle, + sfSymbol: link.sfSymbol, + icon: , + onPress: () => Linking.openURL(link.url), + })) ?? [], + { + label: "Supprimer", + icon: , + sfSymbol: "trash", + destructive: true, + onPress: () => { + Alert.alert( + "Supprimer la carte", + "Es-tu sûr de vouloir supprimer la " + (cardName ?? "carte") + " ?", + [ + { text: "Annuler", style: "cancel" }, + { + text: "Supprimer", + style: "destructive", + onPress: () => { + try { + removeAccount(card.account?.localID); + navigation.goBack(); + } + catch (e) { + console.log(e); + } + } + } + ] + ); + } + } + ]} + > + + + + + ), + }); + }, [navigation, theme]); + return ( <> - - = ({ route, navigatio @@ -117,7 +179,7 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio color: theme.colors.text, }} > - Carte {AccountService[route.params.card.service as AccountService]} {account?.identity?.firstName ? "de " + account.identity.firstName : ""} + {cardName} = ({ route, navigatio + + ); } From 2041f764f020dc55757e0a27f4a8ba9e4f58d5c1 Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Tue, 11 Mar 2025 21:04:24 +0100 Subject: [PATCH 0795/1144] fix(qrcode): update brightness on appear and disappear --- .../account/Restaurant/Modals/QrCode.tsx | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/views/account/Restaurant/Modals/QrCode.tsx b/src/views/account/Restaurant/Modals/QrCode.tsx index 914604778..a08d51dcc 100644 --- a/src/views/account/Restaurant/Modals/QrCode.tsx +++ b/src/views/account/Restaurant/Modals/QrCode.tsx @@ -12,11 +12,12 @@ import * as Haptics from "expo-haptics"; import { Screen } from "@/router/helpers/types"; import { ExternalAccount } from "@/stores/account/types"; import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; - +import * as Brightness from "expo-brightness"; const RestaurantQrCode: Screen<"RestaurantQrCode"> = ({ route, navigation }) => { const { card } = route.params; const [qrCode, setQrCode] = useState(null); + const [defaultBrightness, setDefaultBrightness] = useState(0.5); const { playHaptics } = useSoundHapticsWrapper(); const PollingBalance = async () => { @@ -38,6 +39,32 @@ const RestaurantQrCode: Screen<"RestaurantQrCode"> = ({ route, navigation }) => }, 1000); }; + useEffect(() => { + const handleBrightness = async () => { + const { status } = await Brightness.requestPermissionsAsync(); + if (status === "granted") { + const currentBrightness = await Brightness.getBrightnessAsync(); + setDefaultBrightness(currentBrightness); + Brightness.setSystemBrightnessAsync(1); + } + }; + + handleBrightness(); + + const handleBeforeRemove = async () => { + const { status } = await Brightness.requestPermissionsAsync(); + if (status === "granted" && defaultBrightness !== undefined) { + Brightness.setSystemBrightnessAsync(defaultBrightness); + } + }; + + navigation.addListener("beforeRemove", handleBeforeRemove); + + return () => { + navigation.removeListener("beforeRemove", handleBeforeRemove); + }; + }, [defaultBrightness, navigation]); + useEffect(() => { // Si Izly if(card.service === 10) { From c0ae67e071fad3523c7b05c30d481b3afbf183a3 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 11 Mar 2025 21:31:43 +0100 Subject: [PATCH 0796/1144] fix(lesson_formats): simplifier la structure des formats en supprimant les tableaux inutiles --- src/utils/data/lesson_formats.json | 153 +++++++---------------------- 1 file changed, 38 insertions(+), 115 deletions(-) diff --git a/src/utils/data/lesson_formats.json b/src/utils/data/lesson_formats.json index ac298cc31..f90010376 100644 --- a/src/utils/data/lesson_formats.json +++ b/src/utils/data/lesson_formats.json @@ -3,20 +3,14 @@ "label": "accompagnementperso", "pretty": "Accompagnement personnalisé", "formats": { - "default": [ - "accompagnement personnalise", - "accompagnemt perso" - ] + "default": ["accompagnement personnalise", "accompagnemt perso"] } }, { "label": "atprofessionnalis", "pretty": "Atelier de professionnalisation", "formats": { - "default": [ - "atelier de professionnalisation", - "at professionnalis" - ] + "default": ["atelier de professionnalisation", "at professionnalis"] } }, { @@ -53,10 +47,7 @@ "label": "bloc3tp", "pretty": "Bloc 3 : Travaux pratiques", "formats": { - "default": [ - "bloc 3 travaux pratiques", - "bloc 3 tp" - ] + "default": ["bloc 3 travaux pratiques", "bloc 3 tp"] } }, { @@ -73,10 +64,7 @@ "label": "culturegeneetexpr", "pretty": "Culture génerale et expression", "formats": { - "default": [ - "culture generale et expression", - "culture gene et expr" - ] + "default": ["culture generale et expression", "culture gene et expr"] } }, { @@ -93,60 +81,42 @@ "label": "dnlsesanglais", "pretty": "DNL : Sciences économiques et sociales en anglais", "formats": { - "default": [ - "dnl ses anglais" - ] + "default": ["dnl ses anglais"] } }, { "label": "educationcivique", "pretty": "Éducation civique", "formats": { - "default": [ - "education civique" - ] + "default": ["education civique"] } }, { "label": "enseignscientifique", "pretty": "Enseignement scientifique", "formats": { - "default": [ - "enseignement scientifique", - "enseign scientifique" - ] + "default": ["enseignement scientifique", "enseign scientifique"] } }, { "label": "edphysiquesport", "pretty": "Éducation physique & sportive", "formats": { - "default": [ - "education physique et sportive", - "ed physique sport", - "eps" - ] + "default": ["education physique et sportive", "ed physique sport", "eps"] } }, { "label": "educationmusicale", "pretty": "Éducation musicale", "formats": { - "default": [ - "education musicale", - "educ musicale", - "musique" - ] + "default": ["education musicale", "educ musicale", "musique"] } }, { "label": "francais", "pretty": "Français", "formats": { - "default": [ - "français", - "francais" - ] + "default": ["français", "francais"] } }, { @@ -165,30 +135,21 @@ "label": "humanlitterphilo", "pretty": "Humanites, Littérature & Philosophie", "formats": { - "default": [ - "humanites litterature philosophie", - "human litter philo" - ] + "default": ["humanites litterature philosophie", "human litter philo"] } }, { "label": "llcanglmondcont", "pretty": "LLCER Anglais Monde Contemporain", "formats": { - "default": [ - "llcer anglais monde contemporain", - "llc angl mond cont" - ] + "default": ["llcer anglais monde contemporain", "llc angl mond cont"] } }, { "label": "mathspourinformatq", "pretty": "Mathématiques pour l’Informatique", "formats": { - "default": [ - "mathematiques pour l’informatique", - "maths pour informatq" - ] + "default": ["mathematiques pour l’informatique", "maths pour informatq"] } }, { @@ -208,10 +169,7 @@ "label": "numeriquescinform", "pretty": "Numérique & Sciences Informatiques", "formats": { - "default": [ - "numerique et sciences informatiques", - "numerique sc inform" - ] + "default": ["numerique et sciences informatiques", "numerique sc inform"] } }, { @@ -231,10 +189,7 @@ "label": "sceconosociales", "pretty": "Sciences Économiques & Sociales", "formats": { - "default": [ - "sciences economiques et sociales", - "sc econo sociales" - ] + "default": ["sciences economiques et sociales", "sc econo sociales"] } }, { @@ -252,151 +207,119 @@ "label": "scnumeriqtechnol", "pretty": "Sciences Numériques & Technologie", "formats": { - "default": [ - "sciences numeriques et technologie", - "sc numeriq technol" - ] + "default": ["sciences numeriques et technologie", "sc numeriq technol"] } }, { "label": "viedeclasse", "pretty": "Vie de classe", "formats": { - "default": [ - "vie de classe", - "vie de classe" - ] + "default": ["vie de classe", "vie de classe"] } }, { "label": "integration", "pretty": "Intégration", "formats": { - "default": [ - "integration" - ] + "default": ["integration"] } }, { "label": "marketing", "pretty": "Marketing", "formats": { - "default": [ - "sdc marketing" - ] + "default": ["sdc marketing"] } }, { "label": "gestiondeprojet", "pretty": "Gestion de projet", "formats": { - "default": [ - "gestion de projet" - ] + "default": ["gestion de projet"] } }, { "label": "culturenum", "pretty": "Culture numérique", "formats": { - "default": [ - "culture num" - ] + "default": ["culture num"] } }, { "label": "developpementweb", "pretty": "Développement web", "formats": { - "default": [ - "developpement web" - ] + "default": ["developpement web"] } }, { "label": "traitementdelinformation", "pretty": "Traitement de l'information", "formats": { - "default": [ - "traitement de l information", - "traitement de linformation" - ] + "default": ["traitement de l information", "traitement de linformation"] } }, { "label": "prodgraphique", "pretty": "Production graphique", "formats": { - "default": [ - "prod graphique" - ] + "default": ["prod graphique"] } }, { "label": "expressioncom", "pretty": "Expression & communication", "formats": { - "default": [ - "expression com" - ] + "default": ["expression com"] } }, { "label": "prodaudiovisuelle", "pretty": "Production audiovisuelle", "formats": { - "default": [ - "prod audiovisuelle", - "prod audio visuelle" - ] + "default": ["prod audiovisuelle", "prod audio visuelle"] } }, { "label": "ecrituremedianum", "pretty": "Écriture média numérique", "formats": { - "default": [ - "ecriture media num" - ] + "default": ["ecriture media num"] } }, { "label": "methodologie", "pretty": "Méthodologie", "formats": { - "default": [ - "methodologie" - ] + "default": ["methodologie"] } }, { "label": "ecoetdroitdunumerique", "pretty": "Éco. & droit du numérique", "formats": { - "default": [ - "eco et droit du numerique" - ] + "default": ["eco et droit du numerique"] } }, { "label": "special", "pretty": "Cours exceptionnel obligatoire", "formats": { - "default": [ - "action intervention sortie examen" - ] + "default": ["action intervention sortie examen"] + } + }, + { + "label": "sysdinfo", + "pretty": "Systèmes d'information", + "formats": { + "default": ["sys d info", "sysdinfo", "sys dinfo", "systemes information", "systemes info"] } }, { "label": "art", "pretty": "Arts Plastiques", "formats": { - "default": [ - "arts plastiques", - "arts", - "arts plast", - "arts pla" - ] + "default": ["arts plastiques", "arts", "arts plast", "arts pla"] } } ] \ No newline at end of file From 449d1215598cd84c4cde607df4a94a52080d3e42 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 11 Mar 2025 23:14:34 +0100 Subject: [PATCH 0797/1144] =?UTF-8?q?fix(AlertProvider):=20am=C3=A9liorer?= =?UTF-8?q?=20la=20gestion=20des=20alertes=20avec=20des=20ajustements=20de?= =?UTF-8?q?=20style=20et=20de=20pr=C3=A9sentation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/providers/AlertProvider.tsx | 247 ++++++++++++++++---------------- 1 file changed, 127 insertions(+), 120 deletions(-) diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index 0a0be9ee7..77849c4b4 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -73,124 +73,129 @@ const AlertProvider = ({ children }: AlertProviderProps) => { }; function hideAlert () { - setAlert(null); setVisible(false); + setTimeout(() => setAlert(null), 150); } return ( {children} - {visible && alert && ( + {alert && ( - - - - - - - - - - {alert.icon && - React.cloneElement(alert.icon, { - color: colors.text, - size: 24, - })} - - {alert.title} - - - - {alert.message} - - + {visible && + + + + - 2 ? "column" : "row", - alignItems: "center", - }, - ]} + - {alert.actions?.map( - ({ - title, - onPress, - icon, - primary, - danger, - backgroundColor, - }) => ( - { - hideAlert(); - onPress?.(); - }} - style={({ pressed }) => [ - styles.button, - { - width: - (alert.actions ?? []).length > 2 ? "100%" : "auto", - justifyContent: "center", - alignItems: "center", - opacity: pressed ? 0.6 : 1, - }, - primary - ? styles.primaryButton - : styles.notPrimaryButton, - primary - ? { - backgroundColor: - backgroundColor ?? colors.primary, - } - : danger - ? { backgroundColor: "#FC1E0D" } - : { borderColor: "#CCC", borderWidth: 1 }, - ]} - > - {icon && - React.cloneElement(icon, { - color: primary ? "#ffffff" : colors.text, - size: 24, - })} - - {title} + + + + + {alert.icon && + React.cloneElement(alert.icon, { + color: colors.text, + size: 24, + })} + + {alert.title} - - ) - )} - - - + + + {alert.message} + + + + 2 ? "column" : "row", + alignItems: "center", + }, + ]} + > + {alert.actions?.map( + ({ + title, + onPress, + icon, + primary, + danger, + backgroundColor, + }) => ( + { + hideAlert(); + onPress?.(); + }} + style={({ pressed }) => [ + styles.button, + { + width: + (alert.actions ?? []).length > 2 ? "100%" : "auto", + justifyContent: "center", + alignItems: "center", + opacity: pressed ? 0.6 : 1, + }, + primary + ? styles.primaryButton + : styles.notPrimaryButton, + primary + ? { + backgroundColor: + backgroundColor ?? colors.primary, + } + : danger + ? { backgroundColor: "#FC1E0D" } + : { borderColor: "#CCC", borderWidth: 1 }, + ]} + > + {icon && + React.cloneElement(icon, { + color: primary ? "#ffffff" : colors.text, + size: 24, + })} + + {title} + + + ) + )} + + + + + } )} @@ -213,19 +218,21 @@ const styles = StyleSheet.create({ flex: 1, justifyContent: "flex-end", alignItems: "center", + paddingHorizontal: 10, }, pressable: { flex: 1, width: "100%", }, alertBox: { - borderRadius: 16, - padding: 20, - paddingBottom: 5, - maxWidth: "90%", + borderRadius: 17, + borderCurve: "continuous", + width: "100%", + transformOrigin: "bottom center", }, contentContainer: { - gap: 6, + gap: 10, + padding: 18, }, titleContainer: { flexDirection: "row", @@ -234,10 +241,12 @@ const styles = StyleSheet.create({ }, title: { fontSize: 18, + lineHeight: 22, fontFamily: "semibold", }, message: { fontSize: 16, + lineHeight: 20, fontFamily: "medium", opacity: 0.6, }, @@ -245,19 +254,17 @@ const styles = StyleSheet.create({ flexDirection: "row", justifyContent: "flex-end", paddingVertical: 12, - paddingHorizontal: 5, - marginTop: 16, - paddingTop: 10, - gap: 10, + paddingHorizontal: 12, borderTopWidth: 1, + gap: 10, }, button: { flexDirection: "row", - gap: 5, + gap: 8, alignItems: "center", justifyContent: "center", borderRadius: 300, - paddingVertical: 10, + paddingVertical: 8, width: "100%", }, buttonText: { @@ -265,10 +272,10 @@ const styles = StyleSheet.create({ fontFamily: "medium", }, primaryButton: { - paddingHorizontal: 14, + paddingHorizontal: 16, }, notPrimaryButton: { - paddingHorizontal: 5, + paddingHorizontal: 16, }, primaryButtonText: { color: "#ffffff", From 443125e7539ea7949201284bf9c119d89bd2732e Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 11 Mar 2025 23:16:47 +0100 Subject: [PATCH 0798/1144] =?UTF-8?q?fix(AlertProvider):=20am=C3=A9liorer?= =?UTF-8?q?=20la=20gestion=20des=20alertes=20en=20ajoutant=20un=20d=C3=A9l?= =?UTF-8?q?ai=20pour=20masquer=20les=20alertes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/providers/AlertProvider.tsx | 135 ++++++++++++++++---------------- 1 file changed, 69 insertions(+), 66 deletions(-) diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index a3beb0939..b483252d2 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -92,6 +92,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { function hideAlert () { setVisible(false); + setTimeout(() => setAlert(null), 150); } useEffect(() => { @@ -160,79 +161,81 @@ const AlertProvider = ({ children }: AlertProviderProps) => { - 2 ? "column" : "row", - alignItems: "center", - }, - ]} - > - {alert.actions?.map( - ({ - title, - onPress, - icon, - primary, - danger, - backgroundColor, - }) => ( - { - hideAlert(); - onPress?.(); - }} - disabled={delays[title] > 0} - style={({ pressed }) => [ - styles.button, - { - width: + alignItems: "center", + }, + ]} + > + {alert.actions?.map( + ({ + title, + onPress, + icon, + primary, + danger, + backgroundColor, + }) => ( + { + hideAlert(); + onPress?.(); + }} + disabled={delays[title] > 0} + style={({ pressed }) => [ + styles.button, + { + width: (alert.actions ?? []).length > 2 ? "100%" : "auto", - justifyContent: "center", - alignItems: "center", - opacity: pressed - ? 0.6 - : delays[title] === 0 - ? 1 - : 0.5, - }, - primary - ? { - backgroundColor: + justifyContent: "center", + alignItems: "center", + opacity: pressed + ? 0.6 + : delays[title] === 0 + ? 1 + : 0.5, + }, + primary + ? { + backgroundColor: backgroundColor ?? colors.primary, - } - : danger - ? { backgroundColor: "#BE0B00" } - : { borderColor: "#CCC", borderWidth: 1 }, - ]} - > - {icon && + } + : danger + ? { backgroundColor: "#BE0B00" } + : { borderColor: "#CCC", borderWidth: 1 }, + ]} + > + {icon && React.cloneElement(icon, { color: primary || danger ? "#ffffff" : colors.text, size: 24, })} - - {title} - {delays[title] !== undefined && delays[title] > 0 - ? ` (${delays[title]})` - : ""} - - - ) - )} - - - + + {title} + {delays[title] !== undefined && delays[title] > 0 + ? ` (${delays[title]})` + : ""} + + + ) + )} + + + + + } )} From a544a16ddcd0e9419967701cb5bd9ca020749878 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 11 Mar 2025 23:34:22 +0100 Subject: [PATCH 0799/1144] =?UTF-8?q?fix(AlertProvider):=20am=C3=A9liorer?= =?UTF-8?q?=20l'interaction=20des=20alertes=20avec=20des=20animations=20et?= =?UTF-8?q?=20des=20ajustements=20de=20style?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/providers/AlertProvider.tsx | 112 +++++++++++++++++++------------- 1 file changed, 66 insertions(+), 46 deletions(-) diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index b483252d2..2f4d69de7 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -15,10 +15,12 @@ import Reanimated, { LinearTransition, } from "react-native-reanimated"; import { + anim2Papillon, PapillonContextEnter, PapillonContextExit, } from "@/utils/ui/animations"; import { BlurView } from "expo-blur"; +import { TouchableOpacity } from "react-native-gesture-handler"; type AlertAction = { title: string; @@ -138,7 +140,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { style={[ styles.alertBox, { - backgroundColor: dark ? "#333" : colors.card, + backgroundColor: colors.card, marginBottom: 10 + insets.bottom, }, ]} @@ -165,7 +167,8 @@ const AlertProvider = ({ children }: AlertProviderProps) => { style={[ styles.buttons, { - borderColor: colors.border, + borderColor: colors.text + "20", + backgroundColor: colors.text + "06", flexDirection: (alert.actions ?? []).length > 2 ? "column" : "row", alignItems: "center", @@ -180,55 +183,72 @@ const AlertProvider = ({ children }: AlertProviderProps) => { primary, danger, backgroundColor, + delayDisable, }) => ( - { - hideAlert(); - onPress?.(); - }} - disabled={delays[title] > 0} - style={({ pressed }) => [ - styles.button, - { - width: - (alert.actions ?? []).length > 2 ? "100%" : "auto", - justifyContent: "center", - alignItems: "center", - opacity: pressed - ? 0.6 - : delays[title] === 0 - ? 1 - : 0.5, - }, - primary - ? { - backgroundColor: - backgroundColor ?? colors.primary, - } - : danger - ? { backgroundColor: "#BE0B00" } - : { borderColor: "#CCC", borderWidth: 1 }, - ]} + - {icon && + { + hideAlert(); + onPress?.(); + }} + > + 0} + style={[ + styles.button, + { + justifyContent: "center", + alignItems: "center", + }, + primary && !danger + ? { + backgroundColor: + (backgroundColor ?? colors.primary) + (delays[title] > 0 ? "99" : ""), + } + : danger + ? { backgroundColor: "#BE0B00" + (delays[title] > 0 ? "99" : "") } + : { borderColor: colors.text + "44", borderWidth: 1 }, + ]} + > + {icon && React.cloneElement(icon, { color: primary || danger ? "#ffffff" : colors.text, size: 24, })} - - {title} - {delays[title] !== undefined && delays[title] > 0 - ? ` (${delays[title]})` - : ""} - - + + {title} + {delays[title] !== undefined && delays[title] > 0 + ? ` (${delays[title]})` + : ""} + + + {delays[title] !== undefined && + + } + + + ) )} @@ -305,8 +325,8 @@ const styles = StyleSheet.create({ justifyContent: "center", borderRadius: 300, paddingVertical: 8, - width: "100%", paddingHorizontal: 14, + overflow: "hidden", }, buttonText: { fontSize: 16, From 647cc452bc121e897e12d7f451d4e633de6386aa Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 11 Mar 2025 23:39:25 +0100 Subject: [PATCH 0800/1144] fix(AlertProvider): ajuster le style des boutons pour les alertes avec un seul bouton --- src/providers/AlertProvider.tsx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index 2f4d69de7..f1961202e 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -187,6 +187,9 @@ const AlertProvider = ({ children }: AlertProviderProps) => { }) => ( 2) && styles.singleButtonContainer, + ]} > { hideAlert(); onPress?.(); }} + style={[ + (alert.actions?.length === 1 || (alert.actions ?? []).length > 2) && styles.singleButtonContainer, + ]} > { justifyContent: "center", alignItems: "center", }, + (alert.actions?.length === 1 || (alert.actions ?? []).length > 2) && styles.singleButton, primary && !danger ? { backgroundColor: @@ -328,6 +335,12 @@ const styles = StyleSheet.create({ paddingHorizontal: 14, overflow: "hidden", }, + singleButton: { + width: "100%", + }, + singleButtonContainer: { + width: "100%", + }, buttonText: { fontSize: 16, fontFamily: "medium", From eda87731644052792c1ab0be7e4d950ad752377a Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 11 Mar 2025 23:41:52 +0100 Subject: [PATCH 0801/1144] =?UTF-8?q?fix:=20ajouter=20des=20v=C3=A9rificat?= =?UTF-8?q?ions=20de=20type=20et=20d=C3=A9sactiver=20les=20boutons=20en=20?= =?UTF-8?q?fonction=20des=20d=C3=A9lais=20dans=20AlertProvider?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Global/PapillonPicker.tsx | 1 + src/providers/AlertProvider.tsx | 2 +- src/views/account/Restaurant/Menu.tsx | 3 ++- src/views/account/Restaurant/Modals/CardDetail.tsx | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/Global/PapillonPicker.tsx b/src/components/Global/PapillonPicker.tsx index 47a52e870..c40afdda7 100644 --- a/src/components/Global/PapillonPicker.tsx +++ b/src/components/Global/PapillonPicker.tsx @@ -79,6 +79,7 @@ const PapillonPicker: React.FC = ({ }} menuConfig={{ menuTitle: "", + // @ts-ignore menuItems: data.filter((item) => item !== null).map((item, index) => { return { actionKey: "action-"+index.toString(), diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index f1961202e..e16d072c5 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -193,6 +193,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { > 0} onPress={() => { hideAlert(); onPress?.(); @@ -203,7 +204,6 @@ const AlertProvider = ({ children }: AlertProviderProps) => { > 0} style={[ styles.button, { diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 173bad364..14a6af95c 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -244,7 +244,8 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { balance: balance, history: history, cardnumber: cardnumber, - theme: STORE_THEMES.find((theme) => theme.id === AccountService[account.service]) ?? STORE_THEMES[0], + // @ts-ignore + theme: STORE_THEMES.find((theme) => theme.id === AccountService[account.service]) ?? STORE_THEMES[0] as StoreTheme, }; newCards.push(newCard); diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index d8c800e44..fffae0444 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -101,7 +101,7 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio style: "destructive", onPress: () => { try { - removeAccount(card.account?.localID); + removeAccount(card.account?.localID as string); navigation.goBack(); } catch (e) { From 4ee8743d77d8b7e6841a577af46c4bcd55eb659b Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 11 Mar 2025 23:46:25 +0100 Subject: [PATCH 0802/1144] =?UTF-8?q?fix(settings):=20d=C3=A9sactiver=20l'?= =?UTF-8?q?envoi=20des=20journaux=20et=20am=C3=A9liorer=20le=20style=20du?= =?UTF-8?q?=20champ=20de=20description?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/SettingsSupport.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/views/settings/SettingsSupport.tsx b/src/views/settings/SettingsSupport.tsx index e07c8c381..5031f19ad 100644 --- a/src/views/settings/SettingsSupport.tsx +++ b/src/views/settings/SettingsSupport.tsx @@ -14,7 +14,7 @@ const SettingsSupport: Screen<"SettingsSupport"> = ({ navigation }) => { const theme = useTheme(); const insets = useSafeAreaInsets(); - const [sendLogs, setSendLogs] = useState(true); + const [sendLogs, setSendLogs] = useState(false); const [email, setEmail] = useState(); const [subject, setSubject] = useState(); const [description, setDescription] = useState(); @@ -100,9 +100,11 @@ const SettingsSupport: Screen<"SettingsSupport"> = ({ navigation }) => { = ({ navigation }) => { J’accepte de transmettre les journaux d'erreurs et les données du formulaire pour le traitement de ma demande - + handlePress()} /> From 18bce02765a0d505265481d5bd78fdc06162c70c Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 11 Mar 2025 23:47:43 +0100 Subject: [PATCH 0803/1144] =?UTF-8?q?feat(settings):=20ajouter=20l'ic?= =?UTF-8?q?=C3=B4ne=20BadgeHelp=20dans=20les=20param=C3=A8tres?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/Settings.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index f1647b8d9..e6f3dc0a1 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -36,7 +36,8 @@ import { Blocks, HelpCircle, PersonStanding, - Bug + Bug, + BadgeHelp } from "lucide-react-native"; import { NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; From 1b47f0ffde47437e3c679eae8785957a09f01c93 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 11 Mar 2025 23:49:25 +0100 Subject: [PATCH 0804/1144] =?UTF-8?q?fix(Menu):=20ajouter=20une=20annotati?= =?UTF-8?q?on=20@ts-ignore=20pour=20=C3=A9viter=20les=20erreurs=20de=20typ?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Restaurant/Menu.tsx | 1 + src/widgets/Components/RestaurantQRCode.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 14a6af95c..ad7ed0884 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -73,6 +73,7 @@ export interface ServiceCard { balance: never[] | Balance[]; history: never[] | ReservationHistory[]; cardnumber: string | Blob | null; + // @ts-ignore theme: StoreTheme; } diff --git a/src/widgets/Components/RestaurantQRCode.tsx b/src/widgets/Components/RestaurantQRCode.tsx index 462fa42a1..48389009f 100644 --- a/src/widgets/Components/RestaurantQRCode.tsx +++ b/src/widgets/Components/RestaurantQRCode.tsx @@ -68,6 +68,7 @@ const RestaurantQRCodeWidget = forwardRef(({ balance: [], history: [], cardnumber: cardnumber, + // @ts-ignore theme: STORE_THEMES.find((theme) => theme.id === AccountService[account.service]) ?? STORE_THEMES[0], }; From b6299e416b250ac8047b4e02094b05eeaeb911f8 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 12 Mar 2025 00:18:16 +0100 Subject: [PATCH 0805/1144] =?UTF-8?q?fix(GradesAverage):=20ajouter=20une?= =?UTF-8?q?=20gestion=20d'erreur=20lors=20de=20la=20mise=20=C3=A0=20jour?= =?UTF-8?q?=20de=20la=20date=20et=20de=20la=20moyenne?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../account/Grades/Graph/GradesAverage.tsx | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/views/account/Grades/Graph/GradesAverage.tsx b/src/views/account/Grades/Graph/GradesAverage.tsx index ab600d18f..3e4b4bcbc 100644 --- a/src/views/account/Grades/Graph/GradesAverage.tsx +++ b/src/views/account/Grades/Graph/GradesAverage.tsx @@ -115,12 +115,17 @@ const GradesAverageGraph: React.FC = ({ }, [grades, account.instance]); const updateTo = useCallback( - (index: number) => { - if (index < 0 || index > gradesHistoryRef.current.length - 1) return; - if (!gradesHistoryRef.current[index]?.value) return; - - setSelectedDate(gradesHistoryRef.current[index].date); - setCurrentAvg(gradesHistoryRef.current[index].value); + (index: number, x: number, y: number) => { + try { + if (index < 0 || index > gradesHistoryRef.current.length - 1) return; + if (!gradesHistoryRef.current[index]?.value) return; + + setSelectedDate(gradesHistoryRef.current[index].date); + setCurrentAvg(gradesHistoryRef.current[index].value); + } + catch (e) { + console.error(e); + } }, [gradesHistoryRef] ); @@ -247,10 +252,7 @@ const GradesAverageGraph: React.FC = ({ ref={graphRef} animationDuration={400} onGestureUpdate={(x, y, index) => { - if (index < 0 || index > gradesHistory.length - 1) return; - if (!gradesHistory[index]?.value) return; - - updateTo(index); + updateTo(index, x, y); }} onGestureEnd={() => { resetToOriginal(); From c69cce041d7462055d992e9fb478f1f20081024c Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 12 Mar 2025 00:28:18 +0100 Subject: [PATCH 0806/1144] =?UTF-8?q?feat(GradesAverage):=20int=C3=A9grer?= =?UTF-8?q?=20SwiftUIText=20pour=20une=20meilleure=20animation=20sur=20iOS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../account/Grades/Graph/GradesAverage.tsx | 48 +++++++++++++++---- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/src/views/account/Grades/Graph/GradesAverage.tsx b/src/views/account/Grades/Graph/GradesAverage.tsx index 3e4b4bcbc..40925b66f 100644 --- a/src/views/account/Grades/Graph/GradesAverage.tsx +++ b/src/views/account/Grades/Graph/GradesAverage.tsx @@ -23,6 +23,8 @@ import Reanimated, { } from "react-native-reanimated"; import { anim2Papillon } from "@/utils/ui/animations"; +import { Text as SwiftUIText } from "swiftui-react-native"; + import * as Haptics from "expo-haptics"; import { PressableScale } from "react-native-pressable-scale"; import { ReanimatedGraphProps, ReanimatedGraphPublicMethods } from "@birdwingo/react-native-reanimated-graph/src/core/dto/graphDTO"; @@ -34,6 +36,7 @@ import { useAlert } from "@/providers/AlertProvider"; // Using require to set custom types bc module types are broken const ReanimatedGraph: React.ForwardRefExoticComponent> = require("@birdwingo/react-native-reanimated-graph").default; import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; +import { isExpoGo } from "@/utils/native/expoGoAlert"; interface GradesAverageGraphProps { grades: Grade[]; @@ -325,11 +328,24 @@ const GradesAverageGraph: React.FC = ({ style={[styles.gradeValue]} layout={anim2Papillon(LinearTransition)} > - + {Platform.OS === "ios" && !isExpoGo() ? ( + + {currentAvg.toFixed(2)} + ) : ( + )} /20 @@ -344,10 +360,24 @@ const GradesAverageGraph: React.FC = ({ > { !Number.isNaN(classAvg) ? ( <> - + {Platform.OS === "ios" && !isExpoGo() ? ( + + {classAvg.toFixed(2)} + ) : ( + )} /20 From c74f970accdabb73dbf4eed30b2f381265c95c3b Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 12 Mar 2025 00:54:44 +0100 Subject: [PATCH 0807/1144] =?UTF-8?q?chore:=20mise=20=C3=A0=20jour=20de=20?= =?UTF-8?q?la=20version=20=C3=A0=207.9.1=20et=20ajout=20de=20nouveaux=20ic?= =?UTF-8?q?=C3=B4nes=20pour=20iOS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/build.gradle | 4 +- ios/Papillon.xcodeproj/project.pbxproj | 4 +- .../AppIcon.appiconset/Contents.json | 42 +++++++++++++++---- ios/Papillon/Info.plist | 2 +- package.json | 2 +- 5 files changed, 39 insertions(+), 15 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 059efc776..794ec038f 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -88,8 +88,8 @@ android { applicationId 'xyz.getpapillon.app' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 7900 - versionName "7.9.0" + versionCode 7910 + versionName "7.9.1" } signingConfigs { debug { diff --git a/ios/Papillon.xcodeproj/project.pbxproj b/ios/Papillon.xcodeproj/project.pbxproj index 8bf658ffe..e3b19051c 100644 --- a/ios/Papillon.xcodeproj/project.pbxproj +++ b/ios/Papillon.xcodeproj/project.pbxproj @@ -455,7 +455,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = Papillon; + PRODUCT_NAME = "Papillon"; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -488,7 +488,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = Papillon; + PRODUCT_NAME = "Papillon"; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json index 90d8d4c2a..e91cbd33d 100644 --- a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,14 +1,38 @@ { - "images": [ + "images" : [ { - "filename": "App-Icon-1024x1024@1x.png", - "idiom": "universal", - "platform": "ios", - "size": "1024x1024" + "filename" : "Icon-Light-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Dark-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "Icon-Tinted-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" } ], - "info": { - "version": 1, - "author": "expo" + "info" : { + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index 2910e2bff..e7eb53aa7 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 7.9.0 + 7.9.1 CFBundleSignature ???? CFBundleURLTypes diff --git a/package.json b/package.json index d616bcb4a..f46f955d4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "papillonvex", - "version": "7.9.0", + "version": "7.9.1", "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", From 51e9e677c517556845959c0d61163deba9b431a6 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 12 Mar 2025 18:43:34 +0100 Subject: [PATCH 0808/1144] update package-lock.json --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index e0800b45f..cec84f80e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.9.0", + "version": "7.9.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.9.0", + "version": "7.9.1", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.4", "@candlefinance/app-icon": "^0.4.5", From 9cf2c845cf5fb893e6697e83748ead0f284a07c6 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 12 Mar 2025 18:44:06 +0100 Subject: [PATCH 0809/1144] =?UTF-8?q?conversion=20`Alert.Alert`=20=3D>=20`?= =?UTF-8?q?showAlert`=20+=20ajout=20d'ic=C3=B4nes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/Atoms/Item.tsx | 30 ++++------ src/views/account/News/Document.tsx | 56 +++++++------------ src/views/login/ServiceSelector.tsx | 44 ++++++--------- .../ExternalAccount/ServiceSelector.tsx | 38 +++++-------- 4 files changed, 63 insertions(+), 105 deletions(-) diff --git a/src/views/account/Homeworks/Atoms/Item.tsx b/src/views/account/Homeworks/Atoms/Item.tsx index 63b48dc0b..322ffb1eb 100644 --- a/src/views/account/Homeworks/Atoms/Item.tsx +++ b/src/views/account/Homeworks/Atoms/Item.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState, useCallback } from "react"; -import {Clock, Paperclip, Sparkles} from "lucide-react-native"; +import {Check, Clock, Paperclip, Sparkles, WifiOff} from "lucide-react-native"; import { getSubjectData } from "@/services/shared/Subject"; import { useRoute, useTheme} from "@react-navigation/native"; import { NativeItem, NativeText } from "@/components/Global/NativeComponents"; @@ -10,7 +10,7 @@ import { animPapillon } from "@/utils/ui/animations"; import HTMLView from "react-native-htmlview"; import { NativeStackNavigationProp } from "@react-navigation/native-stack"; import { RouteParameters } from "@/router/helpers/types"; -import { Alert, Platform, StyleSheet, View } from "react-native"; +import { StyleSheet, View } from "react-native"; import { Homework, HomeworkReturnType } from "@/services/shared/Homework"; import detectCategory from "@/utils/magic/categorizeHomeworks"; import { LinearGradient } from "expo-linear-gradient"; @@ -73,25 +73,17 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total } setIsLoading(true); onDonePressHandler(); } else { - if (Platform.OS === "ios") { - Alert.alert("Information", "Tu es hors ligne. Vérifie ta connexion Internet et réessaie", [ + showAlert({ + title: "Information", + message: "Tu es hors ligne. Vérifie ta connexion Internet et réessaie", + icon: , + actions: [ { - text: "OK", + title: "OK", + icon: , }, - ]); - } else { - showAlert({ - title: "Information", - message: "Tu es hors ligne. Vérifie ta connexion Internet et réessaie", - actions: [ - { - title: "OK", - onPress: () => {}, - backgroundColor: theme.colors.card, - }, - ], - }); - } + ], + }); } }, [onDonePressHandler]); diff --git a/src/views/account/News/Document.tsx b/src/views/account/News/Document.tsx index 09fc9077c..447c78f93 100644 --- a/src/views/account/News/Document.tsx +++ b/src/views/account/News/Document.tsx @@ -9,14 +9,16 @@ import { Information } from "@/services/shared/Information"; import formatDate from "@/utils/format/format_date_complets"; import { useTheme } from "@react-navigation/native"; import { + Check, Eye, EyeOff, FileIcon, Link, MoreHorizontal, + WifiOff, } from "lucide-react-native"; import React, { useEffect, useLayoutEffect, useState } from "react"; -import { View, Linking, TouchableOpacity, type GestureResponderEvent, StyleSheet, Platform, Alert } from "react-native"; +import { View, Linking, TouchableOpacity, type GestureResponderEvent, StyleSheet } from "react-native"; import { ScrollView } from "react-native-gesture-handler"; import HTMLView from "react-native-htmlview"; import { PapillonModernHeader } from "@/components/Global/PapillonModernHeader"; @@ -116,25 +118,17 @@ const NewsItem: Screen<"NewsItem"> = ({ route, navigation }) => { read: !prev.read, })); } else { - if (Platform.OS === "ios") { - Alert.alert("Information", "Tu es hors ligne. Vérifie ta connexion Internet et réessaie", [ + showAlert({ + title: "Information", + message: "Tu es hors ligne. Vérifie ta connexion Internet et réessaie", + icon: , + actions: [ { - text: "OK", + title: "OK", + icon: , }, - ]); - } else { - showAlert({ - title: "Information", - message: "Tu es hors ligne. Vérifie ta connexion Internet et réessaie", - actions: [ - { - title: "OK", - onPress: () => {}, - backgroundColor: theme.colors.card, - }, - ], - }); - } + ], + }); } }, }, @@ -205,25 +199,17 @@ const NewsItem: Screen<"NewsItem"> = ({ route, navigation }) => { acknowledged: true, })); } else { - if (Platform.OS === "ios") { - Alert.alert("Information", "Tu es hors ligne. Vérifie ta connexion Internet et réessaie", [ + showAlert({ + title: "Information", + message: "Tu es hors ligne. Vérifie ta connexion Internet et réessaie", + icon: , + actions: [ { - text: "OK", + title: "OK", + icon: , }, - ]); - } else { - showAlert({ - title: "Information", - message: "Tu es hors ligne. Vérifie ta connexion Internet et réessaie", - actions: [ - { - title: "OK", - onPress: () => {}, - backgroundColor: theme.colors.card, - }, - ], - }); - } + ], + }); } } }} diff --git a/src/views/login/ServiceSelector.tsx b/src/views/login/ServiceSelector.tsx index f982955b8..b40098cbe 100644 --- a/src/views/login/ServiceSelector.tsx +++ b/src/views/login/ServiceSelector.tsx @@ -1,5 +1,5 @@ import React, { memo, useEffect, useState } from "react"; -import { Image, View, StyleSheet, Platform, Alert } from "react-native"; +import { Image, View, StyleSheet } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; import Reanimated, { LinearTransition, FlipInXDown } from "react-native-reanimated"; @@ -11,15 +11,19 @@ import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import MaskStars from "@/components/FirstInstallation/MaskStars"; import { useTheme } from "@react-navigation/native"; import GetV6Data from "@/utils/login/GetV6Data"; -import { School } from "lucide-react-native"; +import { Check, School, WifiOff } from "lucide-react-native"; import { LinearGradient } from "expo-linear-gradient"; import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; import { useOnlineStatus } from "@/hooks/useOnlineStatus"; +import { useAlert } from "@/providers/AlertProvider"; const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { const theme = useTheme(); const { colors } = theme; + const { isOnline } = useOnlineStatus(); + const { showAlert } = useAlert(); + type Services = "pronote" | "ed" | "skolengo"; const [service, setService] = useState(null); @@ -183,30 +187,18 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { isOnline ? services.find((srv) => srv.name === service)?.login : () => { - if (Platform.OS === "ios") { - Alert.alert( - "Information", - "Pour poursuivre la connexion, tu dois être connecté à Internet. Vérifie ta connexion Internet et réessaie", - [ - { - text: "OK", - }, - ] - ); - } else { - showAlert({ - title: "Information", - message: - "Pour poursuivre la connexion, tu dois être connecté à Internet. Vérifie ta connexion Internet et réessaie", - actions: [ - { - title: "OK", - onPress: () => {}, - backgroundColor: theme.colors.card, - }, - ], - }); - } + showAlert({ + title: "Information", + message: + "Pour poursuivre la connexion, tu dois être connecté à Internet. Vérifie ta connexion Internet et réessaie", + icon: , + actions: [ + { + title: "OK", + icon: , + }, + ], + }); } } /> diff --git a/src/views/settings/ExternalAccount/ServiceSelector.tsx b/src/views/settings/ExternalAccount/ServiceSelector.tsx index d04fcf759..f2c69bc9b 100644 --- a/src/views/settings/ExternalAccount/ServiceSelector.tsx +++ b/src/views/settings/ExternalAccount/ServiceSelector.tsx @@ -2,7 +2,7 @@ import React, { useState } from "react"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { SafeAreaView, useSafeAreaInsets } from "react-native-safe-area-context"; -import { Image, View, StyleSheet, Platform, Alert } from "react-native"; +import { Image, View, StyleSheet } from "react-native"; import Reanimated, { LinearTransition, FlipInXDown } from "react-native-reanimated"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; import { AccountService } from "@/stores/account/types"; @@ -11,6 +11,7 @@ import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { useOnlineStatus } from "@/hooks/useOnlineStatus"; import { useAlert } from "@/providers/AlertProvider"; +import { Check, WifiOff } from "lucide-react-native"; const ExternalAccountSelector: Screen<"ExternalAccountSelector"> = ({ navigation, route }) => { const theme = useTheme(); @@ -111,30 +112,17 @@ const ExternalAccountSelector: Screen<"ExternalAccountSelector"> = ({ navigation if (isOnline) { navigation.navigate("ExternalAccountSelectMethod", { service }); } else { - if (Platform.OS === "ios") { - Alert.alert( - "Information", - "Pour poursuivre la connexion, tu dois être connecté à Internet. Vérifie ta connexion Internet et réessaie", - [ - { - text: "OK", - }, - ] - ); - } else { - showAlert({ - title: "Information", - message: - "Pour poursuivre la connexion, tu dois être connecté à Internet. Vérifie ta connexion Internet et réessaie", - actions: [ - { - title: "OK", - onPress: () => {}, - backgroundColor: theme.colors.card, - }, - ], - }); - } + showAlert({ + title: "Information", + message: "Pour poursuivre la connexion, tu dois être connecté à Internet. Vérifie ta connexion Internet et réessaie", + icon: , + actions: [ + { + title: "OK", + icon: , + }, + ], + }); } } }} From 74607d33021d5890a31310ed25f1f04909c94355 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 12 Mar 2025 18:48:07 +0100 Subject: [PATCH 0810/1144] =?UTF-8?q?ajout=20d'ic=C3=B4ne=20sur=20le=20`sh?= =?UTF-8?q?owAlert`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Settings/NotificationContainerCard.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/Settings/NotificationContainerCard.tsx b/src/components/Settings/NotificationContainerCard.tsx index 8e5f00250..329772d94 100644 --- a/src/components/Settings/NotificationContainerCard.tsx +++ b/src/components/Settings/NotificationContainerCard.tsx @@ -17,7 +17,7 @@ import Reanimated, { Easing, } from "react-native-reanimated"; import { NativeItem, NativeList, NativeText } from "../Global/NativeComponents"; -import { Settings, X } from "lucide-react-native"; +import { BellOff, Settings, X } from "lucide-react-native"; import { useAlert } from "@/providers/AlertProvider"; import { NativeStackNavigationProp } from "@react-navigation/native-stack"; import { RouteParameters } from "@/router/helpers/types"; @@ -180,11 +180,10 @@ const NotificationContainerCard = ({ title: "Notifications désactivées", message: "Il faut activer les notifications dans les paramètres du téléphone pour pouvoir les activer dans Papillon.", + icon: , actions: [ { title: "Annuler", - onPress: () => {}, - backgroundColor: colors.card, icon: , }, { From 31c64a2250597c7f544b6df34e9b0573cd37d36c Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 12 Mar 2025 18:49:42 +0100 Subject: [PATCH 0811/1144] fix(eslint): error --- src/views/settings/SettingsNotifications.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 09fa5ba29..212d1a399 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -29,7 +29,6 @@ import { requestNotificationPermission, } from "@/background/Notifications"; import { useCurrentAccount } from "@/stores/account"; -import { useAlert } from "@/providers/AlertProvider"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; import { anim2Papillon } from "@/utils/ui/animations"; From 544c1ca221c0cd76c095c7ec2f5b4cfd03361550 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 12 Mar 2025 19:01:30 +0100 Subject: [PATCH 0812/1144] fix: changement de la provenance de `.length` --- src/views/account/Home/Elements/HomeworksElement.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 0453724e2..dd6941e03 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -100,6 +100,8 @@ const HomeworksElement: React.FC = ({ navigation, onImpor .concat(hwSemaineProchaine) .filter((element) => !element.done); + console.log(hw2Semaines.length); + return ( <> = ({ navigation, onImpor key={index} index={index} navigation={navigation} - total={homeworks[dateToEpochWeekNumber(actualDay) + 1].length} + total={hw2Semaines.length} onDonePressHandler={() => { handleDonePress(hw); }} From 0c10e01916ba3e083212869394aa1d13094a53cc Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 12 Mar 2025 19:03:57 +0100 Subject: [PATCH 0813/1144] refractor: delete console.log() --- src/views/account/Home/Elements/HomeworksElement.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index dd6941e03..32bd824d9 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -100,8 +100,6 @@ const HomeworksElement: React.FC = ({ navigation, onImpor .concat(hwSemaineProchaine) .filter((element) => !element.done); - console.log(hw2Semaines.length); - return ( <> Date: Wed, 12 Mar 2025 19:11:27 +0100 Subject: [PATCH 0814/1144] refactor: remplacer TouchableOpacity par NativeTouchable et supprimer l'utilisation de experimentalBlurMethod --- src/components/Global/NativeTouchable.tsx | 4 ++- .../Global/PapillonModernHeader.tsx | 1 - src/components/Home/AccountSwitcher.tsx | 1 - .../Home/AccountSwitcherContextMenu.tsx | 1 - src/providers/AlertProvider.tsx | 25 ++++++++++++++++--- .../account/Restaurant/Modals/QrCode.tsx | 1 - 6 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/components/Global/NativeTouchable.tsx b/src/components/Global/NativeTouchable.tsx index 130b62cbb..d0bd01bf2 100644 --- a/src/components/Global/NativeTouchable.tsx +++ b/src/components/Global/NativeTouchable.tsx @@ -3,16 +3,18 @@ import { Platform, TouchableHighlight, TouchableNativeFeedback, View } from "rea type NativeTouchableProps = { children: React.ReactNode; + contentContainerStyle?: any; underlayColor?: string; } & React.ComponentProps; const NativeTouchable: React.FC = ({ children, + contentContainerStyle, ...props }) => { if(Platform.OS === "android") { return ( - + {children} diff --git a/src/components/Global/PapillonModernHeader.tsx b/src/components/Global/PapillonModernHeader.tsx index b6b436d5d..ed1444bcf 100644 --- a/src/components/Global/PapillonModernHeader.tsx +++ b/src/components/Global/PapillonModernHeader.tsx @@ -166,7 +166,6 @@ const NativeModernHeader: React.FC = ({ children, outsideNav layout={animPapillon(LinearTransition)} > )} diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index e16d072c5..41009753e 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -20,7 +20,7 @@ import { PapillonContextExit, } from "@/utils/ui/animations"; import { BlurView } from "expo-blur"; -import { TouchableOpacity } from "react-native-gesture-handler"; +import NativeTouchable from "@/components/Global/NativeTouchable"; type AlertAction = { title: string; @@ -186,20 +186,32 @@ const AlertProvider = ({ children }: AlertProviderProps) => { delayDisable, }) => ( 2) && styles.singleButtonContainer, + { + borderRadius: 300, + overflow: "hidden", + } ]} > - 0} onPress={() => { hideAlert(); onPress?.(); }} + contentContainerStyle={{ + borderRadius: 300, + overflow: "hidden", + }} style={[ (alert.actions?.length === 1 || (alert.actions ?? []).length > 2) && styles.singleButtonContainer, + { + borderRadius: 300, + overflow: "hidden", + } ]} > { justifyContent: "center", alignItems: "center", }, + { + borderRadius: 300, + overflow: "hidden", + }, (alert.actions?.length === 1 || (alert.actions ?? []).length > 2) && styles.singleButton, primary && !danger ? { @@ -254,7 +270,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { /> } - + ) )} @@ -286,6 +302,7 @@ const styles = StyleSheet.create({ justifyContent: "flex-end", alignItems: "center", paddingHorizontal: 10, + zIndex: 100000000, }, pressable: { flex: 1, diff --git a/src/views/account/Restaurant/Modals/QrCode.tsx b/src/views/account/Restaurant/Modals/QrCode.tsx index a08d51dcc..8f223a22f 100644 --- a/src/views/account/Restaurant/Modals/QrCode.tsx +++ b/src/views/account/Restaurant/Modals/QrCode.tsx @@ -109,7 +109,6 @@ const RestaurantQrCode: Screen<"RestaurantQrCode"> = ({ route, navigation }) => justifyContent: "center", alignItems: "center", }} - experimentalBlurMethod="dimezisBlurView" intensity={100} tint={theme.dark ? "dark" : "light"} > From 2c0a57fb922e5282dfaf2c14e1dd49c4b447ae45 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 12 Mar 2025 19:28:45 +0100 Subject: [PATCH 0815/1144] =?UTF-8?q?fix:=20ajuster=20les=20animations=20p?= =?UTF-8?q?ar=20d=C3=A9faut=20pour=20les=20=C3=A9crans=20Android=20et=20g?= =?UTF-8?q?=C3=A9rer=20les=20erreurs=20lors=20de=20la=20mise=20=C3=A0=20jo?= =?UTF-8?q?ur=20des=20devoirs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/helpers/create-screen.ts | 3 +- src/router/screens/account/home.tsx | 4 +- src/router/screens/index.ts | 4 +- src/router/screens/login/index.ts | 6 +-- src/router/screens/views/index.ts | 2 +- .../account/Grades/Graph/GradesAverage.tsx | 48 ++++--------------- .../Home/Elements/HomeworksElement.tsx | 8 +++- src/views/account/Home/Home.tsx | 20 +++++++- 8 files changed, 44 insertions(+), 51 deletions(-) diff --git a/src/router/helpers/create-screen.ts b/src/router/helpers/create-screen.ts index c16918ce3..8aa0a5060 100644 --- a/src/router/helpers/create-screen.ts +++ b/src/router/helpers/create-screen.ts @@ -22,7 +22,8 @@ const createScreen = ( }) = {} ) => { if (!options.animation && Platform.OS === "android") { - options.animation = options.presentation === "modal" ? "slide_from_bottom" : "slide_from_right"; + options.animation = + options.presentation === "modal" ? "default" : "default"; } return { name, diff --git a/src/router/screens/account/home.tsx b/src/router/screens/account/home.tsx index bf1945e64..1e5ab2357 100644 --- a/src/router/screens/account/home.tsx +++ b/src/router/screens/account/home.tsx @@ -42,7 +42,7 @@ const HomeStackScreen = ({ accountScreens }: { ...tabData.options, tabEnabled: tab.enabled, presentation: "formSheet", - animation: Platform.OS === "android" ? "slide_from_bottom" : "default", + animation: Platform.OS === "android" ? "default" : "default", sheetCornerRadius: 24, }; @@ -61,7 +61,7 @@ const HomeStackScreen = ({ accountScreens }: { newAccountScreens.unshift( createScreen("HomeScreen", Home, { headerShown: false, - animation: Platform.OS === "android" ? "slide_from_right" : "default", + animation: Platform.OS === "android" ? "default" : "default", }) as ReturnType ); diff --git a/src/router/screens/index.ts b/src/router/screens/index.ts index 8c7022e89..352a5efa9 100644 --- a/src/router/screens/index.ts +++ b/src/router/screens/index.ts @@ -15,14 +15,14 @@ export default [ createScreen("SettingStack", SettingsScreen, { headerShown: false, presentation: Platform.OS === "android" ? "modal" : "formSheet", - animation: Platform.OS === "android" ? "slide_from_right" : "default", + animation: Platform.OS === "android" ? "default" : "default", animationDuration: 100, }), createScreen("AccountStack", AccountScreen, { headerShown: false, gestureEnabled: false, - animation: Platform.OS === "android" ? "slide_from_right" : "none", + animation: Platform.OS === "android" ? "default" : "none", animationDuration: 100, }), ] as const; diff --git a/src/router/screens/login/index.ts b/src/router/screens/login/index.ts index 582388ed0..f35df1921 100644 --- a/src/router/screens/login/index.ts +++ b/src/router/screens/login/index.ts @@ -12,12 +12,12 @@ export default [ headerTitle: "", headerTransparent: true, headerBackVisible: true, - animation: Platform.OS === "android" ? "slide_from_bottom" : undefined, - animationDuration: 250 + animation: Platform.OS === "android" ? "default" : undefined, + animationDuration: 250, }), ...pronote, ...ecoledirecte, ...skolengo, - ...identityProvider + ...identityProvider, ] as const; diff --git a/src/router/screens/views/index.ts b/src/router/screens/views/index.ts index a36d85fe2..6de23df0f 100644 --- a/src/router/screens/views/index.ts +++ b/src/router/screens/views/index.ts @@ -59,7 +59,7 @@ export default [ headerTitle: "Item", presentation: "modal", headerShown: false, - animation: "slide_from_right", + animation: "default", }), createScreen("AddonLogs", AddonLogs, { headerTitle: "Logs", diff --git a/src/views/account/Grades/Graph/GradesAverage.tsx b/src/views/account/Grades/Graph/GradesAverage.tsx index 40925b66f..3af2ffb9c 100644 --- a/src/views/account/Grades/Graph/GradesAverage.tsx +++ b/src/views/account/Grades/Graph/GradesAverage.tsx @@ -23,7 +23,6 @@ import Reanimated, { } from "react-native-reanimated"; import { anim2Papillon } from "@/utils/ui/animations"; -import { Text as SwiftUIText } from "swiftui-react-native"; import * as Haptics from "expo-haptics"; import { PressableScale } from "react-native-pressable-scale"; @@ -36,7 +35,6 @@ import { useAlert } from "@/providers/AlertProvider"; // Using require to set custom types bc module types are broken const ReanimatedGraph: React.ForwardRefExoticComponent> = require("@birdwingo/react-native-reanimated-graph").default; import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; -import { isExpoGo } from "@/utils/native/expoGoAlert"; interface GradesAverageGraphProps { grades: Grade[]; @@ -328,24 +326,11 @@ const GradesAverageGraph: React.FC = ({ style={[styles.gradeValue]} layout={anim2Papillon(LinearTransition)} > - {Platform.OS === "ios" && !isExpoGo() ? ( - - {currentAvg.toFixed(2)} - ) : ( - )} + /20 @@ -360,24 +345,11 @@ const GradesAverageGraph: React.FC = ({ > { !Number.isNaN(classAvg) ? ( <> - {Platform.OS === "ios" && !isExpoGo() ? ( - - {classAvg.toFixed(2)} - ) : ( - )} + /20 diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 0453724e2..40619d227 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -122,9 +122,13 @@ const HomeworksElement: React.FC = ({ navigation, onImpor key={index} index={index} navigation={navigation} - total={homeworks[dateToEpochWeekNumber(actualDay) + 1].length} + total={homeworks[dateToEpochWeekNumber(actualDay) + 1] ?homeworks[dateToEpochWeekNumber(actualDay) + 1].length : 0} onDonePressHandler={() => { - handleDonePress(hw); + try { + handleDonePress(hw); + } catch (e) { + console.error(e); + } }} /> ))} diff --git a/src/views/account/Home/Home.tsx b/src/views/account/Home/Home.tsx index 05c04110d..7e405f576 100644 --- a/src/views/account/Home/Home.tsx +++ b/src/views/account/Home/Home.tsx @@ -33,10 +33,10 @@ import {protectScreenComponent} from "@/router/helpers/protected-screen"; import type {Screen} from "@/router/helpers/types"; -import {useCurrentAccount} from "@/stores/account"; +import {useAccounts, useCurrentAccount} from "@/stores/account"; import getCorners from "@/utils/ui/corner-radius"; import {useIsFocused, useTheme} from "@react-navigation/native"; -import React, {useCallback, useMemo, useState} from "react"; +import React, {useCallback, useEffect, useMemo, useState} from "react"; import { Dimensions, Platform, @@ -76,6 +76,22 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { let scrollOffset = useScrollViewOffset(scrollRef); let account = useCurrentAccount(store => store.account!); + const accounts = useAccounts((store) => store.accounts); + + useEffect(() => { + void async function () { + if (!useAccounts.persist.hasHydrated()) return; + + // If there are no accounts, redirect the user to the first installation page. + if (accounts.filter((account) => !account.isExternal).length === 0) { + // Use the `reset` method to clear the navigation stack. + navigation.reset({ + index: 0, + routes: [{ name: "FirstInstallation" }], + }); + } + }(); + }, [accounts]); const [shouldOpenContextMenu, setShouldOpenContextMenu] = useState(false); From a1ec03fe3d09f9ebf47f3d938a5f33a47351df73 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 12 Mar 2025 19:29:24 +0100 Subject: [PATCH 0816/1144] =?UTF-8?q?chore:=20mise=20=C3=A0=20jour=20de=20?= =?UTF-8?q?la=20version=20=C3=A0=207.9.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/build.gradle | 4 ++-- ios/Papillon/Info.plist | 2 +- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 794ec038f..83208dae3 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -88,8 +88,8 @@ android { applicationId 'xyz.getpapillon.app' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 7910 - versionName "7.9.1" + versionCode 7920 + versionName "7.9.2" } signingConfigs { debug { diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index e7eb53aa7..b3de9b269 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 7.9.1 + 7.9.2 CFBundleSignature ???? CFBundleURLTypes diff --git a/package.json b/package.json index f46f955d4..e5ad11bf3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "papillonvex", - "version": "7.9.1", + "version": "7.9.2", "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", From 846e2e80a535bc091951469eae693a7f2a8d9f8c Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 12 Mar 2025 19:37:52 +0100 Subject: [PATCH 0817/1144] update package-lock.json --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 15f782273..450be5d1f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.9.0", + "version": "7.9.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.9.0", + "version": "7.9.1", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.4", "@candlefinance/app-icon": "^0.4.5", From f4a942c86363435d1efda8b06d1af7a02f7a87ef Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 12 Mar 2025 19:39:57 +0100 Subject: [PATCH 0818/1144] fx: vulnerabilities --- package-lock.json | 64 ++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/package-lock.json b/package-lock.json index 450be5d1f..d19fdf897 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.9.1", + "version": "7.9.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.9.1", + "version": "7.9.2", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.4", "@candlefinance/app-icon": "^0.4.5", @@ -474,12 +474,13 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz", + "integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==", + "license": "MIT", "dependencies": { - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.10" }, "engines": { "node": ">=6.9.0" @@ -564,11 +565,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz", + "integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.10" }, "bin": { "parser": "bin/babel-parser.js" @@ -2112,9 +2114,10 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", + "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==", + "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -2123,13 +2126,14 @@ } }, "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", + "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9" }, "engines": { "node": ">=6.9.0" @@ -2153,9 +2157,10 @@ } }, "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz", + "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==", + "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -2435,7 +2440,7 @@ "resolve-from": "^5.0.0", "resolve.exports": "^2.0.2", "semver": "^7.6.0", - "send": "^0.18.0", + "send": "^0.19.0", "slugify": "^1.3.4", "source-map-support": "~0.5.21", "stacktrace-parser": "^0.1.10", @@ -6852,9 +6857,10 @@ } }, "node_modules/axios": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.3.tgz", + "integrity": "sha512-iP4DebzoNlP/YN2dpwCgb8zoCmhtkajzS48JvwmkSkXvPI3DHc7m+XYL5tGnSlJtR6nImXZmdCuN5aP8dh1d8A==", + "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -15723,8 +15729,8 @@ } }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "license": "MIT", "dependencies": { From 5e6b5ae44cce88a95c2842dc85bca8329d24593f Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Wed, 12 Mar 2025 19:49:07 +0100 Subject: [PATCH 0819/1144] fix: qrcode not hidden correctly --- src/widgets/Components/RestaurantQRCode.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/Components/RestaurantQRCode.tsx b/src/widgets/Components/RestaurantQRCode.tsx index 48389009f..5a0010003 100644 --- a/src/widgets/Components/RestaurantQRCode.tsx +++ b/src/widgets/Components/RestaurantQRCode.tsx @@ -80,7 +80,7 @@ const RestaurantQRCodeWidget = forwardRef(({ await Promise.all(accountPromises); setAllCards(newCards); - setHidden(allCards?.length === 0 || currentHour < 11 || currentHour > 14); + setHidden(!(allCards?.some(card => card.cardnumber) && currentHour >= 11 && currentHour <= 14)); setLoading(false); }(); }, [linkedAccounts, setHidden]); From 7a8deba4024c3255586281b48d0e999d579d30b7 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 12 Mar 2025 19:59:03 +0100 Subject: [PATCH 0820/1144] =?UTF-8?q?fix:=20am=C3=A9lioration=20de=20la=20?= =?UTF-8?q?compr=C3=A9hension?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bulgus <88266443+Bulgus@users.noreply.github.com> --- src/views/welcome/ChangelogScreen.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/welcome/ChangelogScreen.tsx b/src/views/welcome/ChangelogScreen.tsx index e9b698c1d..5c8617965 100644 --- a/src/views/welcome/ChangelogScreen.tsx +++ b/src/views/welcome/ChangelogScreen.tsx @@ -149,7 +149,7 @@ const ChangelogScreen: Screen<"ChangelogScreen"> = ({ route, navigation }) => { Impossible de trouver les notes de mise à jour - Les nouveautés de la version n'ont pas du être renseignées ou + Les nouveautés de la version n'ont pas du être publiées ou alors une erreur est survenue... From 7716f9ac652e0eeb475c86cc519313db85f8a296 Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Thu, 13 Mar 2025 00:23:41 +0100 Subject: [PATCH 0821/1144] fix(chats): handle new type of date --- src/services/pronote/chats.ts | 28 ++++++++++++++++++++++------ src/utils/format/DateHelper.ts | 3 +++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/services/pronote/chats.ts b/src/services/pronote/chats.ts index 915e33e89..e52320874 100644 --- a/src/services/pronote/chats.ts +++ b/src/services/pronote/chats.ts @@ -17,12 +17,28 @@ export const getChats = async (account: PronoteAccount): Promise> => const studentName = account.instance.user.resources[0].name; const parseFrenchDate = (dateText: string): Date => { - const datePart = dateText.split(" ").pop(); - if (!datePart) return new Date(); - - const [day, month, year] = datePart.split("/"); - const currentYear = new Date().getFullYear(); - return new Date(`${year ? 20 + year : currentYear}-${month}-${day}`); + const days = ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"]; + const parts = dateText.split(" "); + const datePart = parts.find(part => part.includes("/")); + + if (datePart) { + const [day, month, year] = datePart.split("/"); + return new Date(`20${year}-${month}-${day}`); + } + + const today = new Date(); + const todayIndex = today.getDay(); + const dayName = parts[0].toLowerCase(); + const targetIndex = days.indexOf(dayName); + + if (targetIndex !== -1) { + const diff = targetIndex - todayIndex; + const targetDate = new Date(); + targetDate.setDate(today.getDate() + (diff <= 0 ? diff : diff - 7)); + return targetDate; + } + + return today; }; return chats.items.map((chat) => ({ diff --git a/src/utils/format/DateHelper.ts b/src/utils/format/DateHelper.ts index 837a152ae..c3cc216d0 100644 --- a/src/utils/format/DateHelper.ts +++ b/src/utils/format/DateHelper.ts @@ -2,6 +2,9 @@ import { formatDistance } from "date-fns"; import { fr } from "date-fns/locale"; export const timestampToString = (timestamp: number) => { + if (isNaN(timestamp)) { + return; + } return formatDistance(new Date(timestamp), new Date(), { locale: fr, addSuffix: true, From dbde8df2d691be3e09ed92d8f60228af1d4ad267 Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Thu, 13 Mar 2025 00:31:30 +0100 Subject: [PATCH 0822/1144] fix(support): anti-spam + alert --- src/views/settings/SettingsSupport.tsx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/views/settings/SettingsSupport.tsx b/src/views/settings/SettingsSupport.tsx index 5031f19ad..96671fcb4 100644 --- a/src/views/settings/SettingsSupport.tsx +++ b/src/views/settings/SettingsSupport.tsx @@ -6,14 +6,17 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import SupportContainerCard from "@/components/Settings/SupportContainerCard"; import PapillonCheckbox from "@/components/Global/PapillonCheckbox"; import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; -import { Mail, Tag, Text } from "lucide-react-native"; +import { Check, Mail, Tag, Text } from "lucide-react-native"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { get_logs, Log } from "@/utils/logger/logger"; +import { useAlert } from "@/providers/AlertProvider"; const SettingsSupport: Screen<"SettingsSupport"> = ({ navigation }) => { const theme = useTheme(); const insets = useSafeAreaInsets(); + const { showAlert } = useAlert(); + const [sendLogs, setSendLogs] = useState(false); const [email, setEmail] = useState(); const [subject, setSubject] = useState(); @@ -49,6 +52,15 @@ const SettingsSupport: Screen<"SettingsSupport"> = ({ navigation }) => { }); const result = await response.json(); + setSubject(""); + setEmail(""); + setDescription(""); + setSendLogs(false); + showAlert({ + title: "Merci de vos retours !", + message: "Nous avons reçu votre demande et allons la regardé avec la plus grande attention.", + icon: , + }); }; return ( From 4c53fc419b859cf2c3c303c50eeb8e592ff8843e Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Thu, 13 Mar 2025 00:32:14 +0100 Subject: [PATCH 0823/1144] fix: mistake --- src/views/settings/SettingsSupport.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/settings/SettingsSupport.tsx b/src/views/settings/SettingsSupport.tsx index 96671fcb4..fb89880b3 100644 --- a/src/views/settings/SettingsSupport.tsx +++ b/src/views/settings/SettingsSupport.tsx @@ -58,7 +58,7 @@ const SettingsSupport: Screen<"SettingsSupport"> = ({ navigation }) => { setSendLogs(false); showAlert({ title: "Merci de vos retours !", - message: "Nous avons reçu votre demande et allons la regardé avec la plus grande attention.", + message: "Nous avons reçu votre demande et allons la regarder avec la plus grande attention.", icon: , }); }; From 76ff4e4267061f684ddba040dc2a476fe8000d44 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Thu, 13 Mar 2025 11:09:43 +0100 Subject: [PATCH 0824/1144] =?UTF-8?q?feat:=20mise=20=C3=A0=20jour=20des=20?= =?UTF-8?q?ic=C3=B4nes=20et=20r=C3=A9organisation=20des=20=C3=A9l=C3=A9men?= =?UTF-8?q?ts=20du=20menu=20de=20d=C3=A9veloppement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/welcome/DevMenu.tsx | 184 ++++++++++++++++------------------ 1 file changed, 86 insertions(+), 98 deletions(-) diff --git a/src/views/welcome/DevMenu.tsx b/src/views/welcome/DevMenu.tsx index fed5da8a7..d1ecf127d 100644 --- a/src/views/welcome/DevMenu.tsx +++ b/src/views/welcome/DevMenu.tsx @@ -1,5 +1,5 @@ import { useTheme } from "@react-navigation/native"; -import { BadgeHelp, CheckCircle2, ChevronRight, Eraser, Undo2, CircleAlert } from "lucide-react-native"; +import { AlertTriangle, BadgeHelp, CheckCheck, ChevronRight, Eraser, Undo2 } from "lucide-react-native"; import React, { useEffect, useLayoutEffect, useState } from "react"; import { View, Text, TouchableOpacity, ScrollView } from "react-native"; import type { Screen } from "@/router/helpers/types"; @@ -11,13 +11,12 @@ import { } from "@/components/Global/NativeComponents"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { useAlert } from "@/providers/AlertProvider"; -import PapillonSpinner from "@/components/Global/PapillonSpinner"; import * as TaskManager from "expo-task-manager"; import { error, log } from "@/utils/logger/logger"; -import ButtonCta from "@/components/FirstInstallation/ButtonCta"; -import { registerBackgroundTasks, unsetBackgroundFetch } from "@/background/BackgroundTasks"; import { isExpoGo } from "@/utils/native/expoGoAlert"; import { papillonNotify } from "@/background/Notifications"; +import PapillonSpinner from "@/components/Global/PapillonSpinner"; +import { registerBackgroundTasks, unsetBackgroundFetch } from "@/background/BackgroundTasks"; const DevMenu: Screen<"DevMenu"> = ({ navigation }) => { const theme = useTheme(); @@ -203,108 +202,97 @@ const DevMenu: Screen<"DevMenu"> = ({ navigation }) => { Logs de l'application - {!isExpoGo() && ( - <> - - ) : isBackgroundActive === false ? ( - - ) : ( - - ) - } - > - - {isBackgroundActive === true - ? "Le background est actuellement actif." - : isBackgroundActive === false - ? "Le background n'est pas actif." - : "Vérification du background..."} - - {isBackgroundActive !== null && ( - { - setLoading(true); - setIsBackgroundActive(null); - if (isBackgroundActive) { - await unsetBackgroundFetch() - .then(() => log("✅ Background task unregistered", "BACKGROUND")) - .catch((ERRfatal) => - error( - `❌ Failed to unregister background task: ${ERRfatal}`, - "BACKGROUND" - ) - );; - } + + - await registerBackgroundTasks() - .then(() => log("✅ Background task registered", "BACKGROUND")) - .catch((ERRfatal) => - error( - `❌ Failed to register background task: ${ERRfatal}`, - "BACKGROUND" - ) - ); - setTimeout(() => { - setLoading(false); - }, 500); - }} - /> - )} - - - - - ) : undefined - } - disabled={loading} - primary={!loading} - style={{ - marginTop: 14, - minWidth: null, - maxWidth: null, - width: "75%", - alignSelf: "center", - }} + + {!isExpoGo() && ( + + + + + + ) : isBackgroundActive === false ? ( + + ) : ( + + ) + } + > + + {isBackgroundActive === true + ? "Le background est actuellement actif." + : isBackgroundActive === false + ? "Le background n'est pas actif." + : "Vérification du background..."} + + + {isBackgroundActive !== null && ( + { setLoading(true); - await papillonNotify( - { - id: "test", - title: "Coucou, c'est Papillon 👋", - subtitle: "Test", - body: "Si tu me vois, c'est que tout fonctionne correctement !", - }, - "Test" - ); + setIsBackgroundActive(null); + if (isBackgroundActive) { + await unsetBackgroundFetch() + .then(() => log("✅ Background task unregistered", "BACKGROUND")) + .catch((ERRfatal) => + error( + `❌ Failed to unregister background task: ${ERRfatal}`, + "BACKGROUND" + ) + );; + } + + await registerBackgroundTasks() + .then(() => log("✅ Background task registered", "BACKGROUND")) + .catch((ERRfatal) => + error( + `❌ Failed to register background task: ${ERRfatal}`, + "BACKGROUND" + ) + ); setTimeout(() => { setLoading(false); }, 500); }} /> - - )} - - + )} + + + + ) : undefined + } + disabled={loading} + onPress={async () => { + setLoading(true); + await papillonNotify( + { + id: "test", + title: "Coucou, c'est Papillon 👋", + subtitle: "Test", + body: "Si tu me vois, c'est que tout fonctionne correctement !", + }, + "Test" + ); + setTimeout(() => { + setLoading(false); + }, 500); + }} + /> + + )} From b1d4c9d751b0d316df1e5961e7249935813ef0f1 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Thu, 13 Mar 2025 20:32:05 +0100 Subject: [PATCH 0825/1144] =?UTF-8?q?chore:=20Mettre=20=C3=A0=20jour=20la?= =?UTF-8?q?=20version=20de=20l'application=20=C3=A0=207.10.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/build.gradle | 4 ++-- ios/Papillon/Info.plist | 2 +- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 141343058..f6dba24c3 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -88,8 +88,8 @@ android { applicationId 'xyz.getpapillon.app' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 7920 - versionName "7.9.2" + versionCode 71000 + versionName "7.10.0" } signingConfigs { debug { diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index b3de9b269..ce8c84193 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 7.9.2 + 7.10.0 CFBundleSignature ???? CFBundleURLTypes diff --git a/package.json b/package.json index d4fad3559..17eb236fe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "papillonvex", - "version": "7.9.2", + "version": "7.10.0", "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", From e62d121e6e6a9d71c3652854032b35b2eb4f6c42 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Thu, 13 Mar 2025 20:47:38 +0100 Subject: [PATCH 0826/1144] =?UTF-8?q?feat:=20Ajouter=20la=20prise=20en=20c?= =?UTF-8?q?harge=20des=20orientations=20paysage=20et=20mise=20=C3=A0=20jou?= =?UTF-8?q?r=20de=20l'URL=20du=20changelog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Papillon/Info.plist | 2 ++ src/utils/data/teams.json | 16 ---------------- src/views/welcome/ChangelogScreen.tsx | 2 +- 3 files changed, 3 insertions(+), 17 deletions(-) diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index ce8c84193..55e64381e 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -101,6 +101,8 @@ UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad diff --git a/src/utils/data/teams.json b/src/utils/data/teams.json index acb54a7b0..4081fa44c 100644 --- a/src/utils/data/teams.json +++ b/src/utils/data/teams.json @@ -46,21 +46,5 @@ "ppimage": "https://avatars.githubusercontent.com/u/135361669?v=4", "github": "https://github.com/toi-et-moi", "location": "Rennes, France" - }, - { - "name": "Rémy Godet", - "description": "Développeur & Designer", - "link": "https://godetremy.com/", - "ppimage": "https://avatars.githubusercontent.com/u/77058107?v=4", - "github": "https://github.com/godetremy", - "location": "Angoulême, France" - }, - { - "name": "Romane Vaucelle", - "description": "Support", - "link": "https://www.instagram.com/romane_v_/", - "ppimage": "https://avatars.githubusercontent.com/u/148280931?v=4", - "github": "https://github.com/Romanevcl", - "location": "Tours, France" } ] diff --git a/src/views/welcome/ChangelogScreen.tsx b/src/views/welcome/ChangelogScreen.tsx index 5c8617965..2058603de 100644 --- a/src/views/welcome/ChangelogScreen.tsx +++ b/src/views/welcome/ChangelogScreen.tsx @@ -40,7 +40,7 @@ interface Version { } const currentVersion = PackageJSON.version; -const changelogURL = datasets.changelog.replace("[version]", currentVersion); +const changelogURL = datasets.changelog.replace("[version]", currentVersion.split(".").slice(0, 2).join(".")); const ChangelogScreen: Screen<"ChangelogScreen"> = ({ route, navigation }) => { const theme = useTheme(); From 87553865c99be498dfdd6d406b4edd86e9e88bda Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 13 Mar 2025 22:59:55 +0100 Subject: [PATCH 0827/1144] update `package-lock.json` --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index d19fdf897..6182e8ffc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.9.2", + "version": "7.10.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.9.2", + "version": "7.10.0", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.4", "@candlefinance/app-icon": "^0.4.5", From 717a0a96a659ee18bd690fd4ef97997bd561e0f9 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 13 Mar 2025 23:00:43 +0100 Subject: [PATCH 0828/1144] refractor: bump `bug.yml` to the latest version of Papillon --- .github/ISSUE_TEMPLATE/bug.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 219719c4d..b32273982 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -61,7 +61,7 @@ body: attributes: label: Version utilisée description: Paramètres (de Papillon) -> Version affichée en bas de la page - placeholder: "7.8.3" + placeholder: "7.10.0" validations: required: true From 3c276856058befc28e665aed85323305cb1bcfb5 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 13 Mar 2025 23:02:36 +0100 Subject: [PATCH 0829/1144] fix: don't use cache of npm --- .github/workflows/checks.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index ae794a268..b03fc9dfc 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -19,7 +19,6 @@ jobs: uses: actions/setup-node@v3 with: node-version: 20 - cache: 'npm' - name: 💾 Install dependencies run: npm ci @@ -52,7 +51,6 @@ jobs: uses: actions/setup-node@v3 with: node-version: 20 - cache: 'npm' - name: 💾 Install dependencies run: npm ci From ede0b7d691b10c85c5bff4bbab5254bdd07b2656 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 13 Mar 2025 23:20:17 +0100 Subject: [PATCH 0830/1144] =?UTF-8?q?refactor:=20d=C3=A9placer=20formatCar?= =?UTF-8?q?dIdentifier=20et=20ServiceCard=20vers=20un=20nouveau=20fichier?= =?UTF-8?q?=20utilitaire?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/external/restaurant.ts | 30 +++++++++++++++++++ src/views/account/Restaurant/Cards/Card.tsx | 2 +- src/views/account/Restaurant/Menu.tsx | 26 ++-------------- .../account/Restaurant/Modals/CardDetail.tsx | 2 +- src/widgets/Components/RestaurantQRCode.tsx | 2 +- 5 files changed, 35 insertions(+), 27 deletions(-) create mode 100644 src/utils/external/restaurant.ts diff --git a/src/utils/external/restaurant.ts b/src/utils/external/restaurant.ts new file mode 100644 index 000000000..d791137c8 --- /dev/null +++ b/src/utils/external/restaurant.ts @@ -0,0 +1,30 @@ +import { Balance } from "@/services/shared/Balance"; +import { ReservationHistory } from "@/services/shared/ReservationHistory"; +import { Account, AccountService } from "@/stores/account/types"; +import { StoreTheme } from "@/views/account/Restaurant/Cards/StoreThemes"; + +export const formatCardIdentifier = ( + identifier: string, + dots: number = 4, + separator: string = " " +) => { + if (!identifier) { + return ""; + } + + const visiblePart = identifier.slice(-4); + const maskedPart = identifier.slice(-(4 + dots), -4).replace(/./g, "•"); + return ( + maskedPart + separator + (visiblePart.match(/.{1,4}/g) ?? []).join(" ") + ); +}; + +export interface ServiceCard { + service: string | AccountService; + account: Account | null; + identifier: string; + balance: never[] | Balance[]; + history: never[] | ReservationHistory[]; + cardnumber: string | Blob | null; + theme: StoreTheme; +} diff --git a/src/views/account/Restaurant/Cards/Card.tsx b/src/views/account/Restaurant/Cards/Card.tsx index 4671909ca..12975a668 100644 --- a/src/views/account/Restaurant/Cards/Card.tsx +++ b/src/views/account/Restaurant/Cards/Card.tsx @@ -1,5 +1,5 @@ import { View, Text, StyleSheet, Image } from "react-native"; -import { formatCardIdentifier, ServiceCard } from "../Menu"; +import { formatCardIdentifier, ServiceCard } from "@/utils/external/restaurant"; import { useTheme } from "@react-navigation/native"; import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; import { PressableScale } from "react-native-pressable-scale"; diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 0992c13a3..177051c3f 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -50,33 +50,11 @@ import { ChevronLeft, ChevronRight} from "lucide-react-native"; import DrawableImportRestaurant from "@/components/Drawables/DrawableImportRestaurant"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { OfflineWarning, useOnlineStatus } from "@/hooks/useOnlineStatus"; -import { Account, AccountService } from "@/stores/account/types"; -import { Balance } from "@/services/shared/Balance"; -import { ReservationHistory } from "@/services/shared/ReservationHistory"; +import { AccountService } from "@/stores/account/types"; import { STORE_THEMES, StoreTheme } from "./Cards/StoreThemes"; import MenuCard from "./Cards/Card"; import { useAlert } from "@/providers/AlertProvider"; - -export const formatCardIdentifier = (identifier: string, dots: number = 4, separator: string = " ") => { - if(!identifier) { - return ""; - } - - const visiblePart = identifier.slice(-4); - const maskedPart = identifier.slice(-(4 + dots), -4).replace(/./g, "•"); - return maskedPart + separator + (visiblePart.match(/.{1,4}/g) ?? []).join(" "); -}; - -export interface ServiceCard { - service: string | AccountService; - account: Account | null; - identifier: string; - balance: never[] | Balance[]; - history: never[] | ReservationHistory[]; - cardnumber: string | Blob | null; - // @ts-ignore - theme: StoreTheme; -} +import { ServiceCard } from "@/utils/external/restaurant"; const Menu: Screen<"Menu"> = ({ route, navigation }) => { const theme = useTheme(); diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index fffae0444..a8e9a1cb9 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -16,10 +16,10 @@ import { ExternalLink, MoreHorizontal, QrCode, Trash2 } from "lucide-react-nativ import { balanceFromExternal } from "@/services/balance"; import { reservationHistoryFromExternal } from "@/services/reservation-history"; import { Screen } from "@/router/helpers/types"; -import { formatCardIdentifier } from "../Menu"; import { LinearGradient } from "expo-linear-gradient"; import { TouchableOpacity } from "react-native-gesture-handler"; import PapillonPicker from "@/components/Global/PapillonPicker"; +import { formatCardIdentifier } from "@/utils/external/restaurant"; const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigation }) => { try { diff --git a/src/widgets/Components/RestaurantQRCode.tsx b/src/widgets/Components/RestaurantQRCode.tsx index 5a0010003..b299a9130 100644 --- a/src/widgets/Components/RestaurantQRCode.tsx +++ b/src/widgets/Components/RestaurantQRCode.tsx @@ -14,8 +14,8 @@ import { qrcodeFromExternal } from "@/services/qrcode"; import { useNavigation } from "@react-navigation/native"; import { StackNavigationProp } from "@react-navigation/stack"; import { RouteParameters } from "./../../router/helpers/types"; -import { formatCardIdentifier, ServiceCard } from "@/views/account/Restaurant/Menu"; import { STORE_THEMES } from "@/views/account/Restaurant/Cards/StoreThemes"; +import { formatCardIdentifier, ServiceCard } from "@/utils/external/restaurant"; type NavigationProps = StackNavigationProp; From 28cabbe7b9f537587792a475da5d7950b13942d8 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 13 Mar 2025 23:24:17 +0100 Subject: [PATCH 0831/1144] fix(ts): errors --- src/components/Global/NativeTouchable.tsx | 1 + src/router/helpers/types.ts | 2 +- src/views/settings/ExternalAccount/ServiceSelector.tsx | 4 +++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/Global/NativeTouchable.tsx b/src/components/Global/NativeTouchable.tsx index d0bd01bf2..18d4b6192 100644 --- a/src/components/Global/NativeTouchable.tsx +++ b/src/components/Global/NativeTouchable.tsx @@ -14,6 +14,7 @@ const NativeTouchable: React.FC = ({ }) => { if(Platform.OS === "android") { return ( + // @ts-expect-error {children} diff --git a/src/router/helpers/types.ts b/src/router/helpers/types.ts index fe96f17f0..f7598dc41 100644 --- a/src/router/helpers/types.ts +++ b/src/router/helpers/types.ts @@ -14,9 +14,9 @@ import {Client} from "pawrd"; import { Host } from "turboself-api"; import {Evaluation} from "@/services/shared/Evaluation"; import { ThemesMeta } from "@/utils/chat/themes/Themes.types"; -import { ServiceCard } from "@/views/account/Restaurant/Menu"; import {MultiServiceSpace} from "@/stores/multiService/types"; import { TimetableClass } from "@/services/shared/Timetable"; +import { ServiceCard } from "@/utils/external/restaurant"; export type RouteParameters = { // welcome.index diff --git a/src/views/settings/ExternalAccount/ServiceSelector.tsx b/src/views/settings/ExternalAccount/ServiceSelector.tsx index 6add9d038..f5ba0d2ed 100644 --- a/src/views/settings/ExternalAccount/ServiceSelector.tsx +++ b/src/views/settings/ExternalAccount/ServiceSelector.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import type { Screen } from "@/router/helpers/types"; -import { SafeAreaView } from "react-native-safe-area-context"; +import { SafeAreaView, useSafeAreaInsets } from "react-native-safe-area-context"; import { Image, View, StyleSheet } from "react-native"; import Reanimated, { LinearTransition, FlipInXDown } from "react-native-reanimated"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; @@ -10,6 +10,8 @@ import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { useOnlineStatus } from "@/hooks/useOnlineStatus"; import { useAlert } from "@/providers/AlertProvider"; import { Check, WifiOff } from "lucide-react-native"; +import { useTheme } from "@react-navigation/native"; +import { useCurrentAccount } from "@/stores/account"; const ExternalAccountSelector: Screen<"ExternalAccountSelector"> = ({ navigation, route }) => { const theme = useTheme(); From 2e1dae3515ae84ed1b2cc412031425cfd87d5260 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 13 Mar 2025 23:28:49 +0100 Subject: [PATCH 0832/1144] fix: non affichage du graphique tant que `isLoading` est true --- src/views/account/Grades/Grades.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/account/Grades/Grades.tsx b/src/views/account/Grades/Grades.tsx index b3e678427..c96123565 100644 --- a/src/views/account/Grades/Grades.tsx +++ b/src/views/account/Grades/Grades.tsx @@ -263,7 +263,8 @@ const Grades: Screen<"Grades"> = ({ route, navigation }) => { )} {grades[selectedPeriod] && - grades[selectedPeriod].filter((grade) => grade.student.value !== null && !isNaN(grade.student.value)).length > 1 && ( + grades[selectedPeriod].filter((grade) => grade.student.value !== null && !isNaN(grade.student.value)).length > 1 && + !isLoading && ( Date: Thu, 13 Mar 2025 23:31:08 +0100 Subject: [PATCH 0833/1144] =?UTF-8?q?fix:=20mise=20=C3=A0=20jour=20des=20m?= =?UTF-8?q?essages=20pour=20un=20ton=20informel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Settings/ReelGallery.tsx | 2 +- src/components/Settings/SupportContainerCard.tsx | 2 +- src/views/login/pronote/PronoteCredentials.tsx | 4 ++-- src/views/settings/SettingsDonorsList.tsx | 2 +- src/views/settings/SettingsReactions.tsx | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/Settings/ReelGallery.tsx b/src/components/Settings/ReelGallery.tsx index db67ca95b..96ec4e1f1 100644 --- a/src/components/Settings/ReelGallery.tsx +++ b/src/components/Settings/ReelGallery.tsx @@ -39,7 +39,7 @@ const ReelThumbnail = ({ reel, onPress, width }: { reel: Reel; onPress: () => vo // Vérification de sécurité pour l'image const imageSource = reel.imagewithouteffect ? { uri: `data:image/jpeg;base64,${reel.imagewithouteffect}` } - : null; // Vous pouvez aussi mettre une image par défaut ici + : null; // Tu peux aussi mettre une image par défaut ici if (!imageSource) { return ( diff --git a/src/components/Settings/SupportContainerCard.tsx b/src/components/Settings/SupportContainerCard.tsx index 6201a2ab4..e956b98eb 100644 --- a/src/components/Settings/SupportContainerCard.tsx +++ b/src/components/Settings/SupportContainerCard.tsx @@ -26,7 +26,7 @@ const SupportContainerCard = ({ theme }: { theme: any }) => { Un problème, une question ? - Laissez-nous un message depuis cette page et nous vous répondrons dans les plus brefs délais ! + Laisse-nous un message depuis cette page et nous te répondrons dans les plus brefs délais ! diff --git a/src/views/login/pronote/PronoteCredentials.tsx b/src/views/login/pronote/PronoteCredentials.tsx index da635022f..e5abf0081 100644 --- a/src/views/login/pronote/PronoteCredentials.tsx +++ b/src/views/login/pronote/PronoteCredentials.tsx @@ -100,10 +100,10 @@ const PronoteCredentials: Screen<"PronoteCredentials"> = ({ route, navigation }) setError("Impossible de s'authentifier : " + error.message); break; case "AccessDeniedError": - setError("Vous n'êtes pas autorisé à vous connecter à cet établissement"); + setError("Tu n'es pas autorisé à te connecter à cet établissement"); break; case "AccountDisabledError": - setError("Votre compte a été désactivé. Contactez votre établissement."); + setError("Ton compte a été désactivé. Contacte ton établissement."); break; default: setError(error.message); diff --git a/src/views/settings/SettingsDonorsList.tsx b/src/views/settings/SettingsDonorsList.tsx index 56b241be2..13d7815b4 100644 --- a/src/views/settings/SettingsDonorsList.tsx +++ b/src/views/settings/SettingsDonorsList.tsx @@ -57,7 +57,7 @@ const SettingsDonorsList = () => { Merci à tous les donateurs qui soutiennent l'application ! - Grâce à vous, l'application peut survivre et bénéficier de meilleures conditions pour évoluer jour après jour. + Grâce à eux, l'application peut survivre et bénéficier de meilleures conditions pour évoluer jour après jour. diff --git a/src/views/settings/SettingsReactions.tsx b/src/views/settings/SettingsReactions.tsx index 100680d87..ffd4401bb 100644 --- a/src/views/settings/SettingsReactions.tsx +++ b/src/views/settings/SettingsReactions.tsx @@ -29,7 +29,7 @@ const SettingsReactions: Screen<"SettingsReactions"> = () => { ) : ( From e2733a3482070de90edafa17e32e85455154e0a3 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 13 Mar 2025 23:47:40 +0100 Subject: [PATCH 0834/1144] =?UTF-8?q?fix:=20"Invalid=20hook=20call"=20+=20?= =?UTF-8?q?am=C3=A9lioration=20des=20performances=20(notamment=20sur=20Exp?= =?UTF-8?q?o=20Go)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/Notifications.ts | 8 ++--- .../Settings/NotificationContainerCard.tsx | 32 +++---------------- src/views/settings/SettingsNotifications.tsx | 25 ++++++++++----- 3 files changed, 26 insertions(+), 39 deletions(-) diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index 2e99cd545..762b36c31 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -3,9 +3,9 @@ import { alertExpoGo, isExpoGo } from "@/utils/native/expoGoAlert"; import { Notification } from "@notifee/react-native"; import { Platform } from "react-native"; -const requestNotificationPermission = async () => { - const { showAlert } = useAlert(); - +const requestNotificationPermission = async ( + showAlert: ReturnType["showAlert"] +) => { if (!isExpoGo()) { const notifee = (await import("@notifee/react-native")).default; const AuthorizationStatus = (await import("@notifee/react-native")).AuthorizationStatus; @@ -24,7 +24,7 @@ const requestNotificationPermission = async () => { } } else { alertExpoGo(showAlert); - return false; + return undefined; } }; diff --git a/src/components/Settings/NotificationContainerCard.tsx b/src/components/Settings/NotificationContainerCard.tsx index 329772d94..b3325deb1 100644 --- a/src/components/Settings/NotificationContainerCard.tsx +++ b/src/components/Settings/NotificationContainerCard.tsx @@ -6,7 +6,6 @@ import { Switch, Pressable, Platform, - Alert, Linking, } from "react-native"; import LottieView from "lottie-react-native"; @@ -21,10 +20,11 @@ import { BellOff, Settings, X } from "lucide-react-native"; import { useAlert } from "@/providers/AlertProvider"; import { NativeStackNavigationProp } from "@react-navigation/native-stack"; import { RouteParameters } from "@/router/helpers/types"; +import { alertExpoGo } from "@/utils/native/expoGoAlert"; type NotificationContainerCardProps = { theme: any; - isEnable: boolean | null; + isEnable: boolean | null | undefined; setEnabled: (value: boolean) => void; navigation: NativeStackNavigationProp< RouteParameters, @@ -136,7 +136,7 @@ const NotificationContainerCard = ({ { - if (Platform.OS === "ios") { - Alert.alert( - "Notifications désactivées", - "Il faut activer les notifications dans les paramètres du téléphone pour pouvoir les activer dans Papillon.", - [ - { - text: "Annuler", - style: "cancel", - }, - { - text: "Paramètres système", - style: "default", - onPress: () => { - openNotificationSettings(); - setTimeout(() => { - navigation.reset({ - index: 0, - routes: [{ name: "SettingsNotifications" }], - }); - }, 1000); - }, - }, - ] - ); + if (isEnable === undefined) { + alertExpoGo(showAlert); } else { showAlert({ title: "Notifications désactivées", diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 212d1a399..62ff82e42 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -31,12 +31,14 @@ import { import { useCurrentAccount } from "@/stores/account"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; import { anim2Papillon } from "@/utils/ui/animations"; +import { useAlert } from "@/providers/AlertProvider"; const SettingsNotifications: Screen<"SettingsNotifications"> = ({ navigation, }) => { const theme = useTheme(); const { colors } = theme; + const { showAlert } = useAlert(); // User data const account = useCurrentAccount((store) => store.account!); @@ -44,20 +46,27 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ const notifications = account.personalization.notifications; // Global state - const [enabled, setEnabled] = useState( + const [enabled, setEnabled] = useState( notifications?.enabled ?? false ); useEffect(() => { const handleNotificationPermission = async () => { - const statut = await requestNotificationPermission(); + const statut = await requestNotificationPermission(showAlert); if (!statut) { - setEnabled(null); - setTimeout(() => { - mutateProperty("personalization", { - notifications: { ...notifications, enabled: false }, - }); - }, 1500); + if (statut === undefined) { + setEnabled(undefined); + } else { + setEnabled(null); + } + + if (notifications?.enabled) { + setTimeout(() => { + mutateProperty("personalization", { + notifications: { ...notifications, enabled: false }, + }); + }, 1500); + } } else if (enabled !== null) { if (enabled) createChannelNotification(); setTimeout(() => { From f1452e0a6b343c8f90554aa74f4e6e40ddb8e693 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 13 Mar 2025 23:53:19 +0100 Subject: [PATCH 0835/1144] =?UTF-8?q?refactor(playSoundHaptics):=20simplif?= =?UTF-8?q?ication=20du=20code=20en=20supprimant=20le=20code=20de=20mise?= =?UTF-8?q?=20=C3=A0=20jour=20du=20statut=20de=20lecture?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/native/playSoundHaptics.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/utils/native/playSoundHaptics.ts b/src/utils/native/playSoundHaptics.ts index 68486fc9d..35591f4dd 100644 --- a/src/utils/native/playSoundHaptics.ts +++ b/src/utils/native/playSoundHaptics.ts @@ -2,7 +2,6 @@ import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; import { Sound } from "expo-av/build/Audio"; import * as Haptics from "expo-haptics"; -import type { AVPlaybackStatus } from "expo-av/build/AV.types"; const useSoundHapticsWrapper = () => { const { enableHaptics, enableSon } = useThemeSoundHaptics(); @@ -24,11 +23,6 @@ const useSoundHapticsWrapper = () => { const playSound = async (srcSound: any) => { if (enableSon) { const { sound } = await Sound.createAsync(srcSound); - const onPlaybackStatusUpdate = async (status: AVPlaybackStatus) => { - if (status.isLoaded && status.didJustFinish) await sound.unloadAsync(); - }; - sound.setOnPlaybackStatusUpdate(onPlaybackStatusUpdate); - await sound.setPositionAsync(0); await sound.playAsync(); } }; From a6c9b818e6312bedc9cb683c1ba5770a0b467452 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Fri, 14 Mar 2025 00:01:39 +0100 Subject: [PATCH 0836/1144] =?UTF-8?q?fix:=20afficher=20=C3=A0=20droite=20l?= =?UTF-8?q?e=20``=20pour=20indiquer=20une=20actualit=C3=A9=20non=20l?= =?UTF-8?q?ue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/News/Atoms/Item.tsx | 31 ++++++++++++++++----------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/views/account/News/Atoms/Item.tsx b/src/views/account/News/Atoms/Item.tsx index 5345e5f37..df682f924 100644 --- a/src/views/account/News/Atoms/Item.tsx +++ b/src/views/account/News/Atoms/Item.tsx @@ -38,22 +38,27 @@ const NewsListItem: React.FC = ({ index, message, navigation, flex: 1, gap: 10, marginBottom: 2, + justifyContent: "space-between", }}> + {message.title !== "" && ( + + {message.title} + ) + } {!message.read && !isED && ( - + )} - {message.title !== "" && - {message.title} - } {message.content && ( From 76c8aa40df2db783004dc2c23b494a6dd67f5a8b Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Fri, 14 Mar 2025 11:13:21 +0100 Subject: [PATCH 0837/1144] =?UTF-8?q?fix:=20Corriger=20la=20r=C3=A9f=C3=A9?= =?UTF-8?q?rence=20=C3=A0=20contentContainerStyle=20dans=20NativeTouchable?= =?UTF-8?q?=20et=20ajouter=20useSafeAreaInsets=20dans=20ServiceSelector?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Global/NativeTouchable.tsx | 2 +- src/views/settings/ExternalAccount/ServiceSelector.tsx | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/Global/NativeTouchable.tsx b/src/components/Global/NativeTouchable.tsx index d0bd01bf2..bd5da5e63 100644 --- a/src/components/Global/NativeTouchable.tsx +++ b/src/components/Global/NativeTouchable.tsx @@ -14,7 +14,7 @@ const NativeTouchable: React.FC = ({ }) => { if(Platform.OS === "android") { return ( - + {children} diff --git a/src/views/settings/ExternalAccount/ServiceSelector.tsx b/src/views/settings/ExternalAccount/ServiceSelector.tsx index 6add9d038..f5ba0d2ed 100644 --- a/src/views/settings/ExternalAccount/ServiceSelector.tsx +++ b/src/views/settings/ExternalAccount/ServiceSelector.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import type { Screen } from "@/router/helpers/types"; -import { SafeAreaView } from "react-native-safe-area-context"; +import { SafeAreaView, useSafeAreaInsets } from "react-native-safe-area-context"; import { Image, View, StyleSheet } from "react-native"; import Reanimated, { LinearTransition, FlipInXDown } from "react-native-reanimated"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; @@ -10,6 +10,8 @@ import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { useOnlineStatus } from "@/hooks/useOnlineStatus"; import { useAlert } from "@/providers/AlertProvider"; import { Check, WifiOff } from "lucide-react-native"; +import { useTheme } from "@react-navigation/native"; +import { useCurrentAccount } from "@/stores/account"; const ExternalAccountSelector: Screen<"ExternalAccountSelector"> = ({ navigation, route }) => { const theme = useTheme(); From 2a05e5ddebc2ee968a7aa9476526b83162f0eded Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Fri, 14 Mar 2025 11:13:40 +0100 Subject: [PATCH 0838/1144] =?UTF-8?q?fix:=20Mettre=20=C3=A0=20jour=20la=20?= =?UTF-8?q?version=20=C3=A0=207.10.1=20dans=20package.json?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 17eb236fe..9788f9f8c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "papillonvex", - "version": "7.10.0", + "version": "7.10.1", "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", From 9199c6cea957b71b622a6691496382355c6202ab Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Fri, 14 Mar 2025 11:26:41 +0100 Subject: [PATCH 0839/1144] fix: Supprimer l'utilisation de useOnlineStatus et ajuster le style du BlurView dans AccountSwitcher et ContextMenu --- src/components/Home/AccountSwitcher.tsx | 8 +++----- src/components/Home/AccountSwitcherContextMenu.tsx | 7 ++++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/components/Home/AccountSwitcher.tsx b/src/components/Home/AccountSwitcher.tsx index 1d7b961d1..78f42ce8d 100644 --- a/src/components/Home/AccountSwitcher.tsx +++ b/src/components/Home/AccountSwitcher.tsx @@ -16,7 +16,6 @@ import PapillonSpinner from "../Global/PapillonSpinner"; import { animPapillon } from "@/utils/ui/animations"; import Animated from "react-native-reanimated"; import { BlurView } from "expo-blur"; -import { useOnlineStatus } from "@/hooks/useOnlineStatus"; const ReanimatedBlurView = Reanimated.createAnimatedComponent(BlurView); @@ -29,7 +28,6 @@ const AccountSwitcher: React.FC<{ }> = ({ small, opened, modalOpen, translationY, loading }) => { const theme = useTheme(); const { colors } = theme; - const { isOnline } = useOnlineStatus(); const account = useCurrentAccount(store => store.account!); @@ -89,8 +87,8 @@ const AccountSwitcher: React.FC<{ > - {isOnline && loading && ( + {loading && ( Date: Fri, 14 Mar 2025 11:36:49 +0100 Subject: [PATCH 0840/1144] =?UTF-8?q?feat:=20Ajouter=20la=20gestion=20des?= =?UTF-8?q?=20liens=20profonds=20pour=20l'activation=20de=20compte=20Izly?= =?UTF-8?q?=20et=20remplacer=20les=20alertes=20personnalis=C3=A9es=20par?= =?UTF-8?q?=20des=20alertes=20natives?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Home/Home.tsx | 30 ++++++++++++++ .../ExternalAccount/IzlyActivation.tsx | 40 +++++++++---------- 2 files changed, 50 insertions(+), 20 deletions(-) diff --git a/src/views/account/Home/Home.tsx b/src/views/account/Home/Home.tsx index c530c406c..66805c300 100644 --- a/src/views/account/Home/Home.tsx +++ b/src/views/account/Home/Home.tsx @@ -39,6 +39,7 @@ import {useIsFocused, useTheme} from "@react-navigation/native"; import React, {useCallback, useEffect, useMemo, useState} from "react"; import { Dimensions, + Linking, Platform, RefreshControl, StatusBar, @@ -62,6 +63,8 @@ import ModalContent from "@/views/account/Home/ModalContent"; import {AnimatedScrollView} from "react-native-reanimated/lib/typescript/reanimated2/component/ScrollView"; import useScreenDimensions from "@/hooks/useScreenDimensions"; import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; +import { useAlert } from "@/providers/AlertProvider"; +import { ArrowLeft, Menu, Plus } from "lucide-react-native"; const Home: Screen<"HomeScreen"> = ({ navigation }) => { const { colors } = useTheme(); @@ -71,6 +74,7 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { const { playHaptics } = useSoundHapticsWrapper(); const {isTablet} = useScreenDimensions(); + const { showAlert } = useAlert(); let scrollRef = useAnimatedRef(); let scrollOffset = useScrollViewOffset(scrollRef); @@ -90,6 +94,32 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { routes: [{ name: "FirstInstallation" }], }); } + else { + Linking.getInitialURL().then((url) => { + if (url) { + const scheme = url.split(":")[0]; + if (scheme === "izly") { + showAlert({ + title: "Activation de compte Izly", + message: "Papillon gère la connexion au service Izly. Ouvrez les paramètres de services de cantine pour activer votre compte.", + icon:

, + actions: [ + { + title: "Annuler", + icon: , + }, + { + title: "Ajouter mon compte", + icon: , + onPress: () => navigation.navigate("SettingStack", { view: "IzlyActivation" }), + primary: true, + } + ], + }); + } + } + }); + } }(); }, [accounts]); diff --git a/src/views/settings/ExternalAccount/IzlyActivation.tsx b/src/views/settings/ExternalAccount/IzlyActivation.tsx index d990f112e..69a3cc259 100644 --- a/src/views/settings/ExternalAccount/IzlyActivation.tsx +++ b/src/views/settings/ExternalAccount/IzlyActivation.tsx @@ -2,7 +2,7 @@ import React, {useState, useEffect} from "react"; import type {Screen} from "@/router/helpers/types"; import {useTheme} from "@react-navigation/native"; import {SafeAreaView, useSafeAreaInsets} from "react-native-safe-area-context"; -import { Keyboard, KeyboardAvoidingView, StyleSheet, TouchableWithoutFeedback, View} from "react-native"; +import { Alert, Keyboard, KeyboardAvoidingView, StyleSheet, TouchableWithoutFeedback, View} from "react-native"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; import { NativeText,} from "@/components/Global/NativeComponents"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; @@ -13,7 +13,7 @@ import uuid from "@/utils/uuid-v4"; import * as Linking from "expo-linking"; import { useAlert } from "@/providers/AlertProvider"; -import { ArrowRightFromLine, BadgeHelp, BadgeX, Undo2 } from "lucide-react-native"; +import { BadgeX } from "lucide-react-native"; const IzlyActivation: Screen<"IzlyActivation"> = ({ navigation, route }) => { const theme = useTheme(); @@ -40,6 +40,13 @@ const IzlyActivation: Screen<"IzlyActivation"> = ({ navigation, route }) => { console.log("[IzlyActivation] Ignoring link:", url); } }; + + Linking.getInitialURL().then((url) => { + if (url) { + handleDeepLink({ url }); + } + }); + Linking.addEventListener("url", handleDeepLink); }, []); @@ -129,24 +136,17 @@ const IzlyActivation: Screen<"IzlyActivation"> = ({ navigation, route }) => { value="Annuler" disabled={loading} onPress={() => { - showAlert({ - title: "Annuler", - message: "Es-tu sûr de vouloir annuler l'activation ?", - icon: , - actions: [ - { - title: "Continuer l'activation", - icon: , - primary: false, - }, - { - title: "Confirmer", - icon: , - onPress: () => navigation.pop(), - danger: true, - } - ] - }); + Alert.alert("Annuler", "Es-tu sûr de vouloir annuler l'activation ?", [ + { + text: "Continuer", + style: "cancel", + }, + { + text: "Annuler l'activation", + onPress: () => navigation.pop(), + style: "destructive", + }, + ]); }} /> From ef6a700d189f8c484d04c13f6973ef7f8f293c73 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Fri, 14 Mar 2025 11:38:00 +0100 Subject: [PATCH 0841/1144] =?UTF-8?q?feat:=20Refactor=20la=20gestion=20de?= =?UTF-8?q?=20la=20connexion=20Izly=20pour=20utiliser=20une=20fonction=20d?= =?UTF-8?q?=C3=A9di=C3=A9e=20et=20ajouter=20un=20=C3=A9couteur=20d'=C3=A9v?= =?UTF-8?q?=C3=A9nements=20pour=20les=20URL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Home/Home.tsx | 52 +++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/src/views/account/Home/Home.tsx b/src/views/account/Home/Home.tsx index 66805c300..50493141e 100644 --- a/src/views/account/Home/Home.tsx +++ b/src/views/account/Home/Home.tsx @@ -96,33 +96,41 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { } else { Linking.getInitialURL().then((url) => { - if (url) { - const scheme = url.split(":")[0]; - if (scheme === "izly") { - showAlert({ - title: "Activation de compte Izly", - message: "Papillon gère la connexion au service Izly. Ouvrez les paramètres de services de cantine pour activer votre compte.", - icon: , - actions: [ - { - title: "Annuler", - icon: , - }, - { - title: "Ajouter mon compte", - icon: , - onPress: () => navigation.navigate("SettingStack", { view: "IzlyActivation" }), - primary: true, - } - ], - }); - } - } + manageIzlyLogin(url || ""); }); } }(); + + Linking.addEventListener("url", (event) => { + manageIzlyLogin(event.url); + }); }, [accounts]); + const manageIzlyLogin = (url: string) => { + if (url) { + const scheme = url.split(":")[0]; + if (scheme === "izly") { + showAlert({ + title: "Activation de compte Izly", + message: "Papillon gère la connexion au service Izly. Ouvrez les paramètres de services de cantine pour activer votre compte.", + icon: , + actions: [ + { + title: "Annuler", + icon: , + }, + { + title: "Ajouter mon compte", + icon: , + onPress: () => navigation.navigate("SettingStack", { view: "IzlyActivation" }), + primary: true, + } + ], + }); + } + } + }; + const [shouldOpenContextMenu, setShouldOpenContextMenu] = useState(false); const [modalOpen, setModalOpen] = useState(false); From 7607e56a4c18094e4520762f689b1eb4fc1bcc04 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Fri, 14 Mar 2025 11:48:55 +0100 Subject: [PATCH 0842/1144] =?UTF-8?q?feat:=20Ajouter=20le=20composant=20Pa?= =?UTF-8?q?pillonLoading=20pour=20am=C3=A9liorer=20l'affichage=20de=20char?= =?UTF-8?q?gement=20dans=20plusieurs=20=C3=A9l=C3=A9ments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Global/PapillonLoading.tsx | 59 +++++++++++++++++++ .../Home/Elements/AttendanceElement.tsx | 7 +-- .../account/Home/Elements/GradesElement.tsx | 5 +- .../Home/Elements/HomeworksElement.tsx | 5 +- .../Home/Elements/TimetableElement.tsx | 5 +- src/views/account/Homeworks/Atoms/Loading.tsx | 48 ++------------- src/views/account/Lessons/Atoms/Loading.tsx | 45 ++------------ 7 files changed, 76 insertions(+), 98 deletions(-) create mode 100644 src/components/Global/PapillonLoading.tsx diff --git a/src/components/Global/PapillonLoading.tsx b/src/components/Global/PapillonLoading.tsx new file mode 100644 index 000000000..05e374492 --- /dev/null +++ b/src/components/Global/PapillonLoading.tsx @@ -0,0 +1,59 @@ +import React from "react"; +import { View, StyleSheet } from "react-native"; +import { NativeText } from "./NativeComponents"; +import PapillonSpinner from "./PapillonSpinner"; +import { useTheme } from "@react-navigation/native"; + +const PapillonLoading: React.FC<{ + title: string; + subtitle?: string; + color?: string; +}> = ({ title, subtitle, color }) => { + const theme = useTheme(); + + return ( + + + {title} + {subtitle && + {subtitle} + } + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: "center", + alignItems: "center", + paddingHorizontal: 20, + paddingVertical: 10, + gap: 4, + }, + title: { + fontSize: 18, + lineHeight: 20, + fontFamily: "semibold", + marginTop: 6, + }, + singleText: { + fontSize: 16, + lineHeight: 20, + fontFamily: "medium", + opacity: 0.7, + marginTop: 6, + }, + subtitle: { + fontSize: 15, + lineHeight: 20, + fontFamily: "medium", + opacity: 0.7, + }, +}); + +export default PapillonLoading; \ No newline at end of file diff --git a/src/views/account/Home/Elements/AttendanceElement.tsx b/src/views/account/Home/Elements/AttendanceElement.tsx index 7bff42d84..92bba5466 100644 --- a/src/views/account/Home/Elements/AttendanceElement.tsx +++ b/src/views/account/Home/Elements/AttendanceElement.tsx @@ -11,6 +11,7 @@ import type { Attendance } from "@/services/shared/Attendance"; import { FadeInDown, FadeOut } from "react-native-reanimated"; import MissingItem from "@/components/Global/MissingItem"; import { updateAttendanceInCache, updateAttendancePeriodsInCache } from "@/services/attendance"; +import PapillonLoading from "@/components/Global/PapillonLoading"; interface AttendanceElementProps { @@ -111,10 +112,8 @@ const AttendanceElement: React.FC = ({ onImportance }) = exiting={FadeOut.duration(300)} > - diff --git a/src/views/account/Home/Elements/GradesElement.tsx b/src/views/account/Home/Elements/GradesElement.tsx index bcec0b867..b50ce2431 100644 --- a/src/views/account/Home/Elements/GradesElement.tsx +++ b/src/views/account/Home/Elements/GradesElement.tsx @@ -9,6 +9,7 @@ import type { Grade } from "@/services/shared/Grade"; import RedirectButton from "@/components/Home/RedirectButton"; import { FadeInDown, FadeOut } from "react-native-reanimated"; import MissingItem from "@/components/Global/MissingItem"; +import PapillonLoading from "@/components/Global/PapillonLoading"; interface GradesElementProps { onImportance: (value: number) => unknown @@ -86,10 +87,8 @@ const GradesElement: React.FC = ({ onImportance }) => { exiting={FadeOut.duration(300)} > - diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index f46861b7b..b37e6c0d8 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -13,6 +13,7 @@ import {NativeStackNavigationProp} from "@react-navigation/native-stack"; import {RouteParameters} from "@/router/helpers/types"; import { FadeInDown, FadeOut } from "react-native-reanimated"; import MissingItem from "@/components/Global/MissingItem"; +import PapillonLoading from "@/components/Global/PapillonLoading"; interface HomeworksElementProps { onImportance: (value: number) => unknown @@ -94,10 +95,8 @@ const HomeworksElement: React.FC = ({ navigation, onImpor exiting={FadeOut.duration(300)} > - diff --git a/src/views/account/Home/Elements/TimetableElement.tsx b/src/views/account/Home/Elements/TimetableElement.tsx index a1ef72e86..8319ed8f5 100644 --- a/src/views/account/Home/Elements/TimetableElement.tsx +++ b/src/views/account/Home/Elements/TimetableElement.tsx @@ -11,6 +11,7 @@ import { updateTimetableForWeekInCache } from "@/services/timetable"; import MissingItem from "@/components/Global/MissingItem"; import { TimetableItem } from "../../Lessons/Atoms/Item"; import { getHolidayEmoji } from "@/utils/format/holidayEmoji"; +import PapillonLoading from "@/components/Global/PapillonLoading"; interface TimetableElementProps { onImportance: (value: number) => unknown; @@ -127,10 +128,8 @@ const TimetableElement: React.FC = ({ onImportance }) => exiting={FadeOut.duration(300)} > - diff --git a/src/views/account/Homeworks/Atoms/Loading.tsx b/src/views/account/Homeworks/Atoms/Loading.tsx index 3487cb610..02bb97505 100644 --- a/src/views/account/Homeworks/Atoms/Loading.tsx +++ b/src/views/account/Homeworks/Atoms/Loading.tsx @@ -1,50 +1,10 @@ -import { useTheme } from "@react-navigation/native"; -import { ActivityIndicator, Platform, Text } from "react-native"; - -import Reanimated, { - FadeIn, - FadeOut -} from "react-native-reanimated"; +import PapillonLoading from "@/components/Global/PapillonLoading"; const HomeworksLoading = () => { - const colors = useTheme().colors; - return ( - - - - - Chargement des cours... - - - - Patiente, s'il te plaît - - + ); }; diff --git a/src/views/account/Lessons/Atoms/Loading.tsx b/src/views/account/Lessons/Atoms/Loading.tsx index 7e7d0a8a2..74bec79b1 100644 --- a/src/views/account/Lessons/Atoms/Loading.tsx +++ b/src/views/account/Lessons/Atoms/Loading.tsx @@ -1,50 +1,13 @@ +import PapillonLoading from "@/components/Global/PapillonLoading"; import { useTheme } from "@react-navigation/native"; -import { ActivityIndicator, Platform, Text } from "react-native"; - -import Reanimated, { - FadeIn, - FadeOut -} from "react-native-reanimated"; const LessonsLoading = () => { const colors = useTheme().colors; return ( - - - - - Chargement des cours... - - - - Patiente, s'il te plaît - - + ); }; From 31ca3e872dfb4cb3f4b9de16634e71ea9dead8e6 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Fri, 14 Mar 2025 11:53:18 +0100 Subject: [PATCH 0843/1144] =?UTF-8?q?fix:=20Mettre=20=C3=A0=20jour=20la=20?= =?UTF-8?q?version=20=C3=A0=207.10.1=20dans=20les=20fichiers=20build.gradl?= =?UTF-8?q?e=20et=20Info.plist?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/build.gradle | 4 ++-- ios/Papillon/Info.plist | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index f6dba24c3..f4dd7e183 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -88,8 +88,8 @@ android { applicationId 'xyz.getpapillon.app' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 71000 - versionName "7.10.0" + versionCode 71010 + versionName "7.10.1" } signingConfigs { debug { diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index 55e64381e..283b7836a 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 7.10.0 + 7.10.1 CFBundleSignature ???? CFBundleURLTypes From 033cdd512921c07976a180725cb53e18546c073a Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Fri, 14 Mar 2025 13:16:35 +0100 Subject: [PATCH 0844/1144] =?UTF-8?q?fix:=20ex=C3=A9cution=20incorrecte=20?= =?UTF-8?q?du=20workflow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/checks.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index b03fc9dfc..384c1d2f5 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -10,7 +10,7 @@ jobs: lint-main: name: 🛠️ TypeScript and ESLint on Main runs-on: ubuntu-latest - if: github.ref == 'refs/remotes/origin/main' + if: github.ref == 'refs/heads/main' steps: - name: 📥 Checkout code uses: actions/checkout@v3 @@ -42,7 +42,7 @@ jobs: lint-development: name: 🛠️ TypeScript and ESLint on Pull Request runs-on: ubuntu-latest - if: github.ref != 'refs/remotes/origin/main' + if: github.ref != 'refs/heads/main' steps: - name: 📥 Checkout code uses: actions/checkout@v3 From 96cfdc86be6f66ab8c75135e9ab163811808fe5c Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Fri, 14 Mar 2025 13:20:14 +0100 Subject: [PATCH 0845/1144] fix: update `package-lock.json` to fix workflow error --- package-lock.json | 4043 ++++++++++++++++++++++++++++++--------------- 1 file changed, 2717 insertions(+), 1326 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6182e8ffc..5381dbe7e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.10.0", + "version": "7.10.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.10.0", + "version": "7.10.1", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.4", "@candlefinance/app-icon": "^0.4.5", @@ -131,6 +131,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -143,6 +144,7 @@ "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", @@ -153,28 +155,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", - "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", - "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -190,12 +194,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", - "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.10.tgz", + "integrity": "sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang==", + "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.2", - "@babel/types": "^7.26.0", + "@babel/parser": "^7.26.10", + "@babel/types": "^7.26.10", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -208,6 +213,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "license": "MIT", "dependencies": { "@babel/types": "^7.25.9" }, @@ -215,25 +221,13 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz", - "integrity": "sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==", - "peer": true, - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", - "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.9", + "@babel/compat-data": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -244,16 +238,17 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", - "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.26.9.tgz", + "integrity": "sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg==", + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/helper-replace-supers": "^7.25.9", + "@babel/helper-replace-supers": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", - "@babel/traverse": "^7.25.9", + "@babel/traverse": "^7.26.9", "semver": "^6.3.1" }, "engines": { @@ -264,12 +259,13 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz", - "integrity": "sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz", + "integrity": "sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong==", + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", - "regexpu-core": "^6.1.1", + "regexpu-core": "^6.2.0", "semver": "^6.3.1" }, "engines": { @@ -280,9 +276,10 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", - "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz", + "integrity": "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==", + "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", @@ -295,17 +292,21 @@ } }, "node_modules/@babel/helper-define-polyfill-provider/node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -314,6 +315,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", + "license": "MIT", "dependencies": { "@babel/types": "^7.24.7" }, @@ -325,6 +327,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", + "license": "MIT", "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -337,6 +340,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "license": "MIT", "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -349,6 +353,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9", @@ -365,6 +370,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", + "license": "MIT", "dependencies": { "@babel/types": "^7.25.9" }, @@ -373,9 +379,10 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", - "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -384,6 +391,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-wrap-function": "^7.25.9", @@ -397,13 +405,14 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", - "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz", + "integrity": "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==", + "license": "MIT", "dependencies": { "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/traverse": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -412,22 +421,11 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz", - "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==", - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", + "license": "MIT", "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -440,6 +438,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -448,6 +447,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -456,6 +456,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -464,6 +465,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", + "license": "MIT", "dependencies": { "@babel/template": "^7.25.9", "@babel/traverse": "^7.25.9", @@ -490,6 +492,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.9.tgz", "integrity": "sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==", + "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "chalk": "^2.4.2", @@ -504,6 +507,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -515,6 +519,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -528,6 +533,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", "dependencies": { "color-name": "1.1.3" } @@ -535,12 +541,14 @@ "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", "engines": { "node": ">=0.8.0" } @@ -549,6 +557,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", "engines": { "node": ">=4" } @@ -557,6 +566,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, @@ -583,6 +593,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", @@ -599,6 +610,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -614,6 +626,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -629,6 +642,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", @@ -646,6 +660,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", @@ -663,6 +678,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-async-generator-functions instead.", + "license": "MIT", "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-plugin-utils": "^7.20.2", @@ -681,6 +697,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", + "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -696,6 +713,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.25.9.tgz", "integrity": "sha512-smkNLL/O1ezy9Nhy4CNosc4Va+1wo5w4gzSZeLe6y6dM4mmHfYOCPolXQPHQxonZCF+ZyebxN9vqOolkYrSn5g==", + "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -712,6 +730,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.25.9.tgz", "integrity": "sha512-ykqgwNfSnNOB+C8fV5X4mG3AVmvu+WVxcaU9xHHtBb7PCrPeweMmPjGsn8eMaeJg6SJuoUuZENeeSWaarWqonQ==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -727,6 +746,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-logical-assignment-operators instead.", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" @@ -743,6 +763,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" @@ -759,6 +780,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead.", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-numeric-separator": "^7.10.4" @@ -775,6 +797,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.", + "license": "MIT", "dependencies": { "@babel/compat-data": "^7.20.5", "@babel/helper-compilation-targets": "^7.20.7", @@ -794,6 +817,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-catch-binding instead.", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" @@ -810,6 +834,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", @@ -826,6 +851,7 @@ "version": "7.21.0-placeholder-for-preset-env.2", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "license": "MIT", "peer": true, "engines": { "node": ">=6.9.0" @@ -838,6 +864,7 @@ "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -849,6 +876,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.25.9.tgz", "integrity": "sha512-ryzI0McXUPJnRCvMo4lumIKZUzhYUO/ScI+Mz4YVaTLt04DHNSjEUjKVvbzQjZFLuod/cYEc07mJWhzl6v4DPg==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -863,6 +891,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -874,6 +903,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.25.9.tgz", "integrity": "sha512-9MhJ/SMTsVqsd69GyQg89lYR4o9T+oDGv5F6IsigxxqFVOyR/IflDLYP8WDI1l8fkhNGGktqkvL5qwNCtGEpgQ==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -888,6 +918,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.26.0.tgz", "integrity": "sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -902,6 +933,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -917,6 +949,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -932,6 +965,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -946,6 +980,7 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -957,6 +992,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -968,6 +1004,7 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -979,6 +1016,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -990,6 +1028,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1001,6 +1040,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1012,6 +1052,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1026,6 +1067,7 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", @@ -1042,6 +1084,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1053,14 +1096,15 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz", - "integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz", + "integrity": "sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg==", + "license": "MIT", "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-remap-async-to-generator": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/traverse": "^7.26.8" }, "engines": { "node": ">=6.9.0" @@ -1073,6 +1117,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -1086,12 +1131,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", - "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz", + "integrity": "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==", + "license": "MIT", "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1104,6 +1150,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1118,6 +1165,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", @@ -1134,6 +1182,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", + "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1149,6 +1198,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-compilation-targets": "^7.25.9", @@ -1168,6 +1218,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/template": "^7.25.9" @@ -1183,6 +1234,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1197,6 +1249,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", @@ -1213,6 +1266,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -1228,6 +1282,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", @@ -1244,6 +1299,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -1256,12 +1312,12 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz", - "integrity": "sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz", + "integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==", + "license": "MIT", "peer": true, "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { @@ -1275,6 +1331,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1286,12 +1343,13 @@ } }, "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.25.9.tgz", - "integrity": "sha512-/VVukELzPDdci7UUsWQaSkhgnjIWXnIyRpM02ldxaVoFK96c41So8JcKT3m0gYjyv7j5FNPGS5vfELrWalkbDA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.26.5.tgz", + "integrity": "sha512-eGK26RsbIkYUns3Y8qKl362juDDYK+wEdPGHGrhzUl6CewZFo55VZ7hg+CyMFU4dd5QQakBN86nBMpRsFpRvbQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/plugin-syntax-flow": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/plugin-syntax-flow": "^7.26.0" }, "engines": { "node": ">=6.9.0" @@ -1301,12 +1359,13 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", - "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.26.9.tgz", + "integrity": "sha512-Hry8AusVm8LW5BVFgiyUReuoGzPUpdHQQqJY5bZnbbf+ngOHWuCuYFKw/BqaaWlvEUrF91HMhDtEaI1hZzNbLg==", + "license": "MIT", "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { @@ -1320,6 +1379,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", + "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -1336,6 +1396,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -1351,6 +1412,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1365,6 +1427,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -1380,6 +1443,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -1395,6 +1459,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.25.9", @@ -1408,13 +1473,13 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz", - "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz", + "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==", + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-simple-access": "^7.25.9" + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1427,6 +1492,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.25.9", @@ -1445,6 +1511,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.25.9", @@ -1461,6 +1528,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1476,6 +1544,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -1488,11 +1557,12 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz", - "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==", + "version": "7.26.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz", + "integrity": "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1505,6 +1575,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -1520,6 +1591,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", + "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -1536,6 +1608,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", @@ -1552,6 +1625,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -1567,6 +1641,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" @@ -1582,6 +1657,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1596,6 +1672,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", + "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1611,6 +1688,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-create-class-features-plugin": "^7.25.9", @@ -1627,6 +1705,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -1642,6 +1721,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz", "integrity": "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1656,6 +1736,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-module-imports": "^7.25.9", @@ -1674,6 +1755,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz", "integrity": "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==", + "license": "MIT", "dependencies": { "@babel/plugin-transform-react-jsx": "^7.25.9" }, @@ -1688,6 +1770,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1702,6 +1785,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1716,6 +1800,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.9.tgz", "integrity": "sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==", + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1731,6 +1816,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", @@ -1747,6 +1833,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", @@ -1763,6 +1850,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -1775,14 +1863,15 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.9.tgz", - "integrity": "sha512-nZp7GlEl+yULJrClz0SwHPqir3lc0zsPrDHQUcxGspSL7AKrexNSEfTbfqnDNJUO13bgKyfuOLMF8Xqtu8j3YQ==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.26.10.tgz", + "integrity": "sha512-NWaL2qG6HRpONTnj4JvDU6th4jYeZOJgu3QhmFTCihib0ermtOJqktA5BduGm3suhhVe9EMP9c9+mfJ/I9slqw==", + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-corejs3": "^0.11.0", "babel-plugin-polyfill-regenerator": "^0.6.1", "semver": "^6.3.1" }, @@ -1797,6 +1886,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1811,6 +1901,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" @@ -1826,6 +1917,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1837,11 +1929,12 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", - "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.26.8.tgz", + "integrity": "sha512-OmGDL5/J0CJPJZTHZbi2XpO0tyT2Ia7fzpW5GURwdtp2X3fMmN8au/ej6peC/T33/+CRiIpA8Krse8hFGVmT5Q==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1851,12 +1944,13 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", - "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz", + "integrity": "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw==", + "license": "MIT", "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1866,13 +1960,14 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.9.tgz", - "integrity": "sha512-7PbZQZP50tzv2KGGnhh82GSyMB01yKY9scIjf1a+GfZCtInOWqUH5+1EBU4t9fyR5Oykkkc9vFTs4OHrhHXljQ==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.26.8.tgz", + "integrity": "sha512-bME5J9AC8ChwA7aEPJ6zym3w7aObZULHhbNLU0bKUhKsAkylkzUdq+0kdymh9rzi8nlNFl2bmldFBCKNJBUpuw==", + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", "@babel/plugin-syntax-typescript": "^7.25.9" }, @@ -1887,6 +1982,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -1902,6 +1998,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", @@ -1918,6 +2015,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1933,6 +2031,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", @@ -1946,14 +2045,15 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", - "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.9.tgz", + "integrity": "sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ==", + "license": "MIT", "peer": true, "dependencies": { - "@babel/compat-data": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/compat-data": "^7.26.8", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", @@ -1965,9 +2065,9 @@ "@babel/plugin-syntax-import-attributes": "^7.26.0", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.25.9", - "@babel/plugin-transform-async-generator-functions": "^7.25.9", + "@babel/plugin-transform-async-generator-functions": "^7.26.8", "@babel/plugin-transform-async-to-generator": "^7.25.9", - "@babel/plugin-transform-block-scoped-functions": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.26.5", "@babel/plugin-transform-block-scoping": "^7.25.9", "@babel/plugin-transform-class-properties": "^7.25.9", "@babel/plugin-transform-class-static-block": "^7.26.0", @@ -1978,21 +2078,21 @@ "@babel/plugin-transform-duplicate-keys": "^7.25.9", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-dynamic-import": "^7.25.9", - "@babel/plugin-transform-exponentiation-operator": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.26.3", "@babel/plugin-transform-export-namespace-from": "^7.25.9", - "@babel/plugin-transform-for-of": "^7.25.9", + "@babel/plugin-transform-for-of": "^7.26.9", "@babel/plugin-transform-function-name": "^7.25.9", "@babel/plugin-transform-json-strings": "^7.25.9", "@babel/plugin-transform-literals": "^7.25.9", "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", "@babel/plugin-transform-member-expression-literals": "^7.25.9", "@babel/plugin-transform-modules-amd": "^7.25.9", - "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.26.3", "@babel/plugin-transform-modules-systemjs": "^7.25.9", "@babel/plugin-transform-modules-umd": "^7.25.9", "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-new-target": "^7.25.9", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", "@babel/plugin-transform-numeric-separator": "^7.25.9", "@babel/plugin-transform-object-rest-spread": "^7.25.9", "@babel/plugin-transform-object-super": "^7.25.9", @@ -2008,17 +2108,17 @@ "@babel/plugin-transform-shorthand-properties": "^7.25.9", "@babel/plugin-transform-spread": "^7.25.9", "@babel/plugin-transform-sticky-regex": "^7.25.9", - "@babel/plugin-transform-template-literals": "^7.25.9", - "@babel/plugin-transform-typeof-symbol": "^7.25.9", + "@babel/plugin-transform-template-literals": "^7.26.8", + "@babel/plugin-transform-typeof-symbol": "^7.26.7", "@babel/plugin-transform-unicode-escapes": "^7.25.9", "@babel/plugin-transform-unicode-property-regex": "^7.25.9", "@babel/plugin-transform-unicode-regex": "^7.25.9", "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-corejs3": "^0.11.0", "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.38.1", + "core-js-compat": "^3.40.0", "semver": "^6.3.1" }, "engines": { @@ -2032,6 +2132,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.25.9.tgz", "integrity": "sha512-EASHsAhE+SSlEzJ4bzfusnXSHiU+JfAYzj+jbw2vgQKgq5HrUr8qs+vgtiEL5dOH6sEweI+PNt2D7AqrDSHyqQ==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-validator-option": "^7.25.9", @@ -2048,6 +2149,7 @@ "version": "0.1.6-no-external-plugins", "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", @@ -2059,9 +2161,10 @@ } }, "node_modules/@babel/preset-react": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.25.9.tgz", - "integrity": "sha512-D3to0uSPiWE7rBrdIICCd0tJSIGpLaaGptna2+w7Pft5xMqLpA1sz99DK5TZ1TjGbdQ/VI1eCSZ06dv3lT4JOw==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.26.3.tgz", + "integrity": "sha512-Nl03d6T9ky516DGK2YMxrTqvnpUW63TnJMOMonj+Zae0JiPC5BC9xPMSL6L8fiSpA5vP88qfygavVQvnLp+6Cw==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-validator-option": "^7.25.9", @@ -2081,6 +2184,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz", "integrity": "sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-validator-option": "^7.25.9", @@ -2099,6 +2203,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.25.9.tgz", "integrity": "sha512-8D43jXtGsYmEeDvm4MWHYUpWf8iiXgWYx3fW7E7Wb7Oe6FWqJPl5K6TuFW0dOwNZzEE5rjlaSJYH9JjrUKJszA==", + "license": "MIT", "dependencies": { "clone-deep": "^4.0.1", "find-cache-dir": "^2.0.0", @@ -2140,15 +2245,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.10.tgz", + "integrity": "sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.10", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2173,6 +2279,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/@birdwingo/react-native-reanimated-graph/-/react-native-reanimated-graph-1.1.4.tgz", "integrity": "sha512-kUZRnIFwfJbOMEDgaRi7ZcfXDPXzEvuU1/tSuhHHkDO5NwZcfdjz+BaNWndhZVOs0T65QRJS3AvAeWU4jw9cvA==", + "license": "MIT", "peerDependencies": { "react": ">=18.0.0", "react-native": ">=0.70.0", @@ -2185,6 +2292,7 @@ "version": "0.4.5", "resolved": "https://registry.npmjs.org/@candlefinance/app-icon/-/app-icon-0.4.5.tgz", "integrity": "sha512-8y4e+AwxX14EmBq9IGenRYIchqJ4GAyLAw4gHk+ceRnMeDuSTo7CZMMd/eJDTH6Ae2rsc/qU+90tdOwozlb0FQ==", + "license": "MIT", "engines": { "node": ">= 16.0.0" }, @@ -2196,12 +2304,14 @@ "node_modules/@dominicstop/ts-event-emitter": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@dominicstop/ts-event-emitter/-/ts-event-emitter-1.1.0.tgz", - "integrity": "sha512-CcxmJIvUb1vsFheuGGVSQf4KdPZC44XolpUT34+vlal+LyQoBUOn31pjFET5M9ctOxEpt8xa0M3/2M7uUiAoJw==" + "integrity": "sha512-CcxmJIvUb1vsFheuGGVSQf4KdPZC44XolpUT34+vlal+LyQoBUOn31pjFET5M9ctOxEpt8xa0M3/2M7uUiAoJw==", + "license": "MIT" }, "node_modules/@egjs/hammerjs": { "version": "2.0.17", "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==", + "license": "MIT", "dependencies": { "@types/hammerjs": "^2.0.36" }, @@ -2210,10 +2320,11 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", - "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz", + "integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==", "dev": true, + "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" }, @@ -2232,6 +2343,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -2244,6 +2356,7 @@ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -2253,6 +2366,7 @@ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -2276,6 +2390,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2286,6 +2401,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -2298,6 +2414,7 @@ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -2315,6 +2432,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, @@ -2330,6 +2448,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2342,6 +2461,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -2354,6 +2474,7 @@ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, + "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -2440,7 +2561,7 @@ "resolve-from": "^5.0.0", "resolve.exports": "^2.0.2", "semver": "^7.6.0", - "send": "^0.19.0", + "send": "^0.18.0", "slugify": "^1.3.4", "source-map-support": "~0.5.21", "stacktrace-parser": "^0.1.10", @@ -2478,14 +2599,15 @@ } }, "node_modules/@expo/cli/node_modules/form-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.2.tgz", - "integrity": "sha512-sJe+TQb2vIaIyO783qN6BlMYWMw3WBOHA1Ay2qxsnjuafEOQFJ2JakedOQirT6D5XPRxDvS7AHYyem9fTpb4LQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.3.tgz", + "integrity": "sha512-q5YBMeWy6E2Un0nMGWMgI65MAKtaylxfNJGJxpGh45YDciZB4epbWpaAfImil6CPAPTYB4sh0URQNDRIZG5F2w==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" @@ -2529,9 +2651,9 @@ } }, "node_modules/@expo/cli/node_modules/resolve": { - "version": "1.22.9", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.9.tgz", - "integrity": "sha512-QxrmX1DzraFIi9PxdG5VkRfRwIgjwyud+z/iBwfRRrVmHc+P9Q7u2lSSpQ6bjr2gy5lrqIiU9vb6iAeGf2400A==", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "license": "MIT", "dependencies": { "is-core-module": "^2.16.0", @@ -2541,14 +2663,17 @@ "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/@expo/cli/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -2571,6 +2696,7 @@ "version": "9.0.4", "resolved": "https://registry.npmjs.org/@expo/config/-/config-9.0.4.tgz", "integrity": "sha512-g5ns5u1JSKudHYhjo1zaSfkJ/iZIcWmUmIQptMJZ6ag1C0ShL2sj8qdfU8MmAMuKLOgcIfSaiWlQnm4X3VJVkg==", + "license": "MIT", "dependencies": { "@babel/code-frame": "~7.10.4", "@expo/config-plugins": "~8.0.8", @@ -2612,6 +2738,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2622,6 +2749,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2641,6 +2769,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2649,9 +2778,10 @@ } }, "node_modules/@expo/config-plugins/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -2662,12 +2792,14 @@ "node_modules/@expo/config-types": { "version": "51.0.3", "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-51.0.3.tgz", - "integrity": "sha512-hMfuq++b8VySb+m9uNNrlpbvGxYc8OcFCUX9yTmi9tlx6A4k8SDabWFBgmnr4ao3wEArvWrtUQIfQCVtPRdpKA==" + "integrity": "sha512-hMfuq++b8VySb+m9uNNrlpbvGxYc8OcFCUX9yTmi9tlx6A4k8SDabWFBgmnr4ao3wEArvWrtUQIfQCVtPRdpKA==", + "license": "MIT" }, "node_modules/@expo/config/node_modules/@babel/code-frame": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "license": "MIT", "dependencies": { "@babel/highlight": "^7.10.4" } @@ -2676,6 +2808,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2686,6 +2819,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2705,6 +2839,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2713,9 +2848,10 @@ } }, "node_modules/@expo/config/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -2776,6 +2912,7 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/@expo/env/-/env-0.3.0.tgz", "integrity": "sha512-OtB9XVHWaXidLbHvrVDeeXa09yvTl3+IQN884sO6PhIi2/StXfgSH/9zC7IvzrDB8kW3EBJ1PPLuCUJ2hxAT7Q==", + "license": "MIT", "dependencies": { "chalk": "^4.0.0", "debug": "^4.3.4", @@ -2788,6 +2925,7 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.5.1.tgz", "integrity": "sha512-U/GsFfFox88lXULmFJ9Shfl2aQGcwoKPF7fawSCLixIKtMCpsI+1r0h+5i0nQnmt9tHuzXZDL8+Dg1z6OhkI9A==", + "license": "MIT", "dependencies": { "@expo/spawn-async": "^1.7.2", "chalk": "^4.0.0", @@ -2805,6 +2943,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", "integrity": "sha512-GsVpkFPlycH7/fRR7Dhcmnoii54gV1nz7y4CWyeFS14N+JVBBhY+r8amRHE4BwSYal7BPTDp8isvAlCxyFt3Hg==", + "license": "MIT", "engines": { "node": ">=4" } @@ -2813,6 +2952,7 @@ "version": "9.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.0.tgz", "integrity": "sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g==", + "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -2827,6 +2967,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -2838,14 +2979,16 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", "engines": { "node": ">= 10.0.0" } }, "node_modules/@expo/image-utils/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -2857,6 +3000,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", "integrity": "sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ==", + "license": "MIT", "engines": { "node": ">=4" } @@ -2865,6 +3009,7 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.3.0.tgz", "integrity": "sha512-WrH/pui8YCwmeiAoxV+lpRH9HpRtgBhSR2ViBPgpGb/wnYDzp21R4MN45fsCGvLROvY67o3byhJRYRONJyImVQ==", + "license": "MIT", "dependencies": { "temp-dir": "^1.0.0", "type-fest": "^0.3.1", @@ -2878,6 +3023,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=6" } @@ -2886,6 +3032,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", "integrity": "sha512-ODgiYu03y5g76A1I9Gt0/chLCzQjvzDy7DsZGsLOE/1MrF6wriEskSncj1+/C58Xk/kPZDppSctDybCwOSaGAg==", + "license": "MIT", "dependencies": { "crypto-random-string": "^1.0.0" }, @@ -2897,6 +3044,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -2905,6 +3053,7 @@ "version": "8.3.3", "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-8.3.3.tgz", "integrity": "sha512-eZ5dld9AD0PrVRiIWpRkm5aIoWBw3kAyd8VkuWEy92sEthBKDDDHAnK2a0dw0Eil6j7rK7lS/Qaq/Zzngv2h5A==", + "license": "MIT", "dependencies": { "@babel/code-frame": "~7.10.4", "json5": "^2.2.2", @@ -2915,6 +3064,7 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "license": "MIT", "dependencies": { "@babel/highlight": "^7.10.4" } @@ -2982,9 +3132,9 @@ } }, "node_modules/@expo/osascript": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.1.4.tgz", - "integrity": "sha512-LcPjxJ5FOFpqPORm+5MRLV0CuYWMthJYV6eerF+lQVXKlvgSn3EOqaHC3Vf3H+vmB0f6G4kdvvFtg40vG4bIhA==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.1.6.tgz", + "integrity": "sha512-SbMp4BUwDAKiFF4zZEJf32rRYMeNnLK9u4FaPo0lQRer60F+SKd20NTSys0wgssiVeQyQz2OhGLRx3cxYowAGw==", "license": "MIT", "dependencies": { "@expo/spawn-async": "^1.7.2", @@ -2995,12 +3145,12 @@ } }, "node_modules/@expo/package-manager": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-1.6.1.tgz", - "integrity": "sha512-4rT46wP/94Ll+CWXtFKok1Lbo9XncSUtErFOo/9/3FVughGbIfdG4SKZOAWIpr9wxwEfkyhHfAP9q71ONlWODw==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-1.7.2.tgz", + "integrity": "sha512-wT/qh9ebNjl6xr00bYkSh93b6E/78J3JPlT6WzGbxbsnv5FIZKB/nr522oWqVe1E+ML7BpXs8WugErWDN9kOFg==", "license": "MIT", "dependencies": { - "@expo/json-file": "^9.0.0", + "@expo/json-file": "^9.0.2", "@expo/spawn-async": "^1.7.2", "ansi-regex": "^5.0.0", "chalk": "^4.0.0", @@ -3024,9 +3174,9 @@ } }, "node_modules/@expo/package-manager/node_modules/@expo/json-file": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-9.0.0.tgz", - "integrity": "sha512-M+55xFVrFzDcgMDf+52lPDLjKB5xwRfStWlv/b/Vu2OLgxGZLWpxoPYjlRoHqxjPbCQIi2ZCbobK+0KuNhsELg==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-9.0.2.tgz", + "integrity": "sha512-yAznIUrybOIWp3Uax7yRflB0xsEpvIwIEqIjao9SGi2Gaa+N0OamWfe0fnXBSWF+2zzF4VvqwT4W5zwelchfgw==", "license": "MIT", "dependencies": { "@babel/code-frame": "~7.10.4", @@ -3090,9 +3240,9 @@ } }, "node_modules/@expo/package-manager/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -3105,6 +3255,7 @@ "version": "9.1.1", "resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.1.1.tgz", "integrity": "sha512-es33J1g2HjMpyAhz8lOR+ICmXXAqTuKbuXuUWLhOLew20oN9oUCgCJx615U/v7aioZg7IX5lIh9x34vwneu4pA==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "license": "MIT" }, "node_modules/@expo/package-manager/node_modules/validate-npm-package-name": { @@ -3120,6 +3271,7 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.1.3.tgz", "integrity": "sha512-GW/7hVlAylYg1tUrEASclw1MMk9FP4ZwyFAY/SUTJIhPDQHtfOlXREyWV3hhrHdX/K+pS73GNgdfT6E/e+kBbg==", + "license": "MIT", "dependencies": { "@xmldom/xmldom": "~0.7.7", "base64-js": "^1.2.3", @@ -3176,9 +3328,9 @@ } }, "node_modules/@expo/prebuild-config/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -3217,12 +3369,14 @@ "node_modules/@expo/sdk-runtime-versions": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@expo/sdk-runtime-versions/-/sdk-runtime-versions-1.0.0.tgz", - "integrity": "sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==" + "integrity": "sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==", + "license": "MIT" }, "node_modules/@expo/spawn-async": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/@expo/spawn-async/-/spawn-async-1.7.2.tgz", "integrity": "sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew==", + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3" }, @@ -3234,6 +3388,7 @@ "version": "14.0.4", "resolved": "https://registry.npmjs.org/@expo/vector-icons/-/vector-icons-14.0.4.tgz", "integrity": "sha512-+yKshcbpDfbV4zoXOgHxCwh7lkE9VVTT5T03OUlBsqfze1PLy6Hi4jp1vSb1GVbY6eskvMIivGVc9SKzIv0oEQ==", + "license": "MIT", "dependencies": { "prop-types": "^15.8.1" } @@ -3274,12 +3429,14 @@ "node_modules/@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "license": "BSD-3-Clause" }, "node_modules/@hapi/topo": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "license": "BSD-3-Clause", "dependencies": { "@hapi/hoek": "^9.0.0" } @@ -3288,6 +3445,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/@howljs/calendar-kit/-/calendar-kit-2.2.1.tgz", "integrity": "sha512-3ZgPS46/14pfZ/gBbMsdrSpCy/jTfVpgeHcvOHW7itflUnFBOSCRhGc4cWRd27vD9gIAu63PdWngQ34p/5EwFw==", + "license": "MIT", "dependencies": { "lodash.debounce": "^4.0.8", "lodash.isequal": "^4.5.0", @@ -3318,6 +3476,7 @@ "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "deprecated": "Use @eslint/config-array instead", "dev": true, + "license": "Apache-2.0", "dependencies": { "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", @@ -3332,6 +3491,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3342,6 +3502,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -3354,6 +3515,7 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -3367,7 +3529,8 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "deprecated": "Use @eslint/object-schema instead", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@isaacs/cliui": { "version": "8.0.2", @@ -3469,6 +3632,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz", "integrity": "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==", + "license": "ISC", "engines": { "node": ">=12" } @@ -3477,6 +3641,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz", "integrity": "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==", + "license": "MIT", "dependencies": { "@jest/types": "^29.6.3" }, @@ -3488,6 +3653,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -3504,6 +3670,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -3512,6 +3679,7 @@ "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -3520,6 +3688,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "license": "MIT", "dependencies": { "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", @@ -3534,6 +3703,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -3550,6 +3720,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -3558,6 +3729,7 @@ "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -3566,6 +3738,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", @@ -3582,6 +3755,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -3598,6 +3772,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -3606,6 +3781,7 @@ "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -3614,6 +3790,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "license": "MIT", "dependencies": { "@sinclair/typebox": "^0.27.8" }, @@ -3625,6 +3802,7 @@ "version": "24.9.0", "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^1.1.1", @@ -3635,9 +3813,10 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -3651,6 +3830,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", "engines": { "node": ">=6.0.0" } @@ -3659,6 +3839,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "license": "MIT", "engines": { "node": ">=6.0.0" } @@ -3667,6 +3848,7 @@ "version": "0.3.6", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" @@ -3675,12 +3857,14 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -3690,16 +3874,18 @@ "version": "1.0.0-11713907881.1", "resolved": "https://registry.npmjs.org/@literate.ink/utilities/-/utilities-1.0.0-11713907881.1.tgz", "integrity": "sha512-+RdZqu5dor5YQfL0CRSGPb9Gl6qCsXusziJZt84KxmJsL55ElPSc35AR4CNITzsYg7rAXVnoUCf8fXpaSeWJsg==", + "license": "MIT", "dependencies": { "set-cookie-parser": "^2.7.0" } }, "node_modules/@noble/curves": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.6.0.tgz", - "integrity": "sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz", + "integrity": "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==", + "license": "MIT", "dependencies": { - "@noble/hashes": "1.5.0" + "@noble/hashes": "1.7.1" }, "engines": { "node": "^14.21.3 || >=16" @@ -3709,9 +3895,10 @@ } }, "node_modules/@noble/hashes": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.5.0.tgz", - "integrity": "sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", + "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", + "license": "MIT", "engines": { "node": "^14.21.3 || >=16" }, @@ -3723,6 +3910,7 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -3735,6 +3923,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", "engines": { "node": ">= 8" } @@ -3743,6 +3932,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -3755,6 +3945,7 @@ "version": "7.9.0", "resolved": "https://registry.npmjs.org/@notifee/react-native/-/react-native-7.9.0.tgz", "integrity": "sha512-r4iFcAkvfFV/iNwGF50y9uAYS8x0x6+t9gmBsZczWxFHzBvg5nBjwFjshnuC24+oNnlNWIbB03heNmFjrFArJQ==", + "license": "Apache-2.0", "peerDependencies": { "react-native": "*" } @@ -3772,9 +3963,9 @@ } }, "node_modules/@npmcli/fs/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -3784,47 +3975,50 @@ } }, "node_modules/@peculiar/asn1-ecc": { - "version": "2.3.14", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.3.14.tgz", - "integrity": "sha512-zWPyI7QZto6rnLv6zPniTqbGaLh6zBpJyI46r1yS/bVHJXT2amdMHCRRnbV5yst2H8+ppXG6uXu/M6lKakiQ8w==", + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.3.15.tgz", + "integrity": "sha512-/HtR91dvgog7z/WhCVdxZJ/jitJuIu8iTqiyWVgRE9Ac5imt2sT/E4obqIVGKQw7PIy+X6i8lVBoT6wC73XUgA==", + "license": "MIT", "dependencies": { - "@peculiar/asn1-schema": "^2.3.13", - "@peculiar/asn1-x509": "^2.3.13", + "@peculiar/asn1-schema": "^2.3.15", + "@peculiar/asn1-x509": "^2.3.15", "asn1js": "^3.0.5", - "tslib": "^2.6.2" + "tslib": "^2.8.1" } }, "node_modules/@peculiar/asn1-pkcs8": { - "version": "2.3.13", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.3.13.tgz", - "integrity": "sha512-VP3PQzbeSSjPjKET5K37pxyf2qCdM0dz3DJ56ZCsol3FqAXGekb4sDcpoL9uTLGxAh975WcdvUms9UcdZTuGyQ==", + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.3.15.tgz", + "integrity": "sha512-/PuQj2BIAw1/v76DV1LUOA6YOqh/UvptKLJHtec/DQwruXOCFlUo7k6llegn8N5BTeZTWMwz5EXruBw0Q10TMg==", + "license": "MIT", "dependencies": { - "@peculiar/asn1-schema": "^2.3.13", - "@peculiar/asn1-x509": "^2.3.13", + "@peculiar/asn1-schema": "^2.3.15", + "@peculiar/asn1-x509": "^2.3.15", "asn1js": "^3.0.5", - "tslib": "^2.6.2" + "tslib": "^2.8.1" } }, "node_modules/@peculiar/asn1-schema": { - "version": "2.3.13", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.13.tgz", - "integrity": "sha512-3Xq3a01WkHRZL8X04Zsfg//mGaA21xlL4tlVn4v2xGT0JStiztATRkMwa5b+f/HXmY2smsiLXYK46Gwgzvfg3g==", + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.15.tgz", + "integrity": "sha512-QPeD8UA8axQREpgR5UTAfu2mqQmm97oUqahDtNdBcfj3qAnoXzFdQW+aNf/tD2WVXF8Fhmftxoj0eMIT++gX2w==", + "license": "MIT", "dependencies": { "asn1js": "^3.0.5", - "pvtsutils": "^1.3.5", - "tslib": "^2.6.2" + "pvtsutils": "^1.3.6", + "tslib": "^2.8.1" } }, "node_modules/@peculiar/asn1-x509": { - "version": "2.3.13", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.3.13.tgz", - "integrity": "sha512-PfeLQl2skXmxX2/AFFCVaWU8U6FKW1Db43mgBhShCOFS1bVxqtvusq1hVjfuEcuSQGedrLdCSvTgabluwN/M9A==", + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.3.15.tgz", + "integrity": "sha512-0dK5xqTqSLaxv1FHXIcd4Q/BZNuopg+u1l23hT9rOmQ1g4dNtw0g/RnEi+TboB0gOwGtrWn269v27cMgchFIIg==", + "license": "MIT", "dependencies": { - "@peculiar/asn1-schema": "^2.3.13", + "@peculiar/asn1-schema": "^2.3.15", "asn1js": "^3.0.5", - "ipaddr.js": "^2.1.0", - "pvtsutils": "^1.3.5", - "tslib": "^2.6.2" + "pvtsutils": "^1.3.6", + "tslib": "^2.8.1" } }, "node_modules/@pkgjs/parseargs": { @@ -3841,6 +4035,7 @@ "version": "1.23.1", "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.23.1.tgz", "integrity": "sha512-Qd2kQ3yi6Y3+AcUlrHxSLlnBvpdCEMVGFlVBneVOjaFaPU61g1huc38g339ysXspwY1QZA2aNhrk/KlHGO+ewA==", + "license": "MIT", "dependencies": { "merge-options": "^3.0.4" }, @@ -3852,6 +4047,7 @@ "version": "13.6.9", "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-13.6.9.tgz", "integrity": "sha512-hFJL4cgLPxncJJd/epQ4dHnMg5Jy/7Q56jFvA3MHViuKpzzfTCJCB+pGY54maZbtym53UJON9WTGpM3S81UfjQ==", + "license": "MIT", "dependencies": { "@react-native-community/cli-clean": "13.6.9", "@react-native-community/cli-config": "13.6.9", @@ -3882,6 +4078,7 @@ "version": "13.6.9", "resolved": "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-13.6.9.tgz", "integrity": "sha512-7Dj5+4p9JggxuVNOjPbduZBAP1SUgNhLKVw5noBUzT/3ZpUZkDM+RCSwyoyg8xKWoE4OrdUAXwAFlMcFDPKykA==", + "license": "MIT", "dependencies": { "@react-native-community/cli-tools": "13.6.9", "chalk": "^4.1.2", @@ -3893,6 +4090,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -3915,6 +4113,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -3926,6 +4125,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", "engines": { "node": ">=8" }, @@ -3937,6 +4137,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", "engines": { "node": ">=6" } @@ -3945,6 +4146,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", "dependencies": { "path-key": "^3.0.0" }, @@ -3956,6 +4158,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -3970,6 +4173,7 @@ "version": "13.6.9", "resolved": "https://registry.npmjs.org/@react-native-community/cli-config/-/cli-config-13.6.9.tgz", "integrity": "sha512-rFfVBcNojcMm+KKHE/xqpqXg8HoKl4EC7bFHUrahMJ+y/tZll55+oX/PGG37rzB8QzP2UbMQ19DYQKC1G7kXeg==", + "license": "MIT", "dependencies": { "@react-native-community/cli-tools": "13.6.9", "chalk": "^4.1.2", @@ -3983,6 +4187,7 @@ "version": "13.6.9", "resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-13.6.9.tgz", "integrity": "sha512-TkN7IdFmGPPvTpAo3nCAH9uwGCPxWBEAwpqEZDrq0NWllI7Tdie8vDpGdrcuCcKalmhq6OYnkXzeBah7O1Ztpw==", + "license": "MIT", "dependencies": { "serve-static": "^1.13.1" } @@ -3991,6 +4196,7 @@ "version": "13.6.9", "resolved": "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-13.6.9.tgz", "integrity": "sha512-5quFaLdWFQB+677GXh5dGU9I5eg2z6Vg4jOX9vKnc9IffwyIFAyJfCZHrxLSRPDGNXD7biDQUdoezXYGwb6P/A==", + "license": "MIT", "dependencies": { "@react-native-community/cli-config": "13.6.9", "@react-native-community/cli-platform-android": "13.6.9", @@ -4015,6 +4221,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "license": "MIT", "dependencies": { "restore-cursor": "^3.1.0" }, @@ -4026,6 +4233,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -4048,6 +4256,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -4059,6 +4268,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", "engines": { "node": ">=8" }, @@ -4070,6 +4280,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "license": "MIT", "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -4085,6 +4296,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", "engines": { "node": ">=6" } @@ -4093,6 +4305,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", "dependencies": { "path-key": "^3.0.0" }, @@ -4104,6 +4317,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -4118,6 +4332,7 @@ "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "license": "MIT", "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", @@ -4140,6 +4355,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -4151,6 +4367,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "license": "MIT", "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -4160,9 +4377,10 @@ } }, "node_modules/@react-native-community/cli-doctor/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -4174,6 +4392,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "license": "MIT", "dependencies": { "ansi-regex": "^4.1.0" }, @@ -4185,6 +4404,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "license": "MIT", "engines": { "node": ">=6" } @@ -4193,6 +4413,7 @@ "version": "13.6.9", "resolved": "https://registry.npmjs.org/@react-native-community/cli-hermes/-/cli-hermes-13.6.9.tgz", "integrity": "sha512-GvwiwgvFw4Ws+krg2+gYj8sR3g05evmNjAHkKIKMkDTJjZ8EdyxbkifRUs1ZCq3TMZy2oeblZBXCJVOH4W7ZbA==", + "license": "MIT", "dependencies": { "@react-native-community/cli-platform-android": "13.6.9", "@react-native-community/cli-tools": "13.6.9", @@ -4204,6 +4425,7 @@ "version": "13.6.9", "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-13.6.9.tgz", "integrity": "sha512-9KsYGdr08QhdvT3Ht7e8phQB3gDX9Fs427NJe0xnoBh+PDPTI2BD5ks5ttsH8CzEw8/P6H8tJCHq6hf2nxd9cw==", + "license": "MIT", "dependencies": { "@react-native-community/cli-tools": "13.6.9", "chalk": "^4.1.2", @@ -4217,6 +4439,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -4239,6 +4462,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -4250,6 +4474,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", "engines": { "node": ">=8" }, @@ -4261,6 +4486,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", "engines": { "node": ">=6" } @@ -4269,6 +4495,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", "dependencies": { "path-key": "^3.0.0" }, @@ -4280,6 +4507,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -4294,6 +4522,7 @@ "version": "13.6.9", "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-apple/-/cli-platform-apple-13.6.9.tgz", "integrity": "sha512-KoeIHfhxMhKXZPXmhQdl6EE+jGKWwoO9jUVWgBvibpVmsNjo7woaG/tfJMEWfWF3najX1EkQAoJWpCDBMYWtlA==", + "license": "MIT", "dependencies": { "@react-native-community/cli-tools": "13.6.9", "chalk": "^4.1.2", @@ -4307,6 +4536,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "license": "MIT", "dependencies": { "restore-cursor": "^3.1.0" }, @@ -4318,6 +4548,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -4340,6 +4571,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -4351,6 +4583,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", "engines": { "node": ">=8" }, @@ -4362,6 +4595,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "license": "MIT", "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -4377,6 +4611,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", "engines": { "node": ">=6" } @@ -4385,6 +4620,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", "dependencies": { "path-key": "^3.0.0" }, @@ -4396,6 +4632,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -4410,6 +4647,7 @@ "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "license": "MIT", "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", @@ -4432,6 +4670,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "license": "MIT", "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -4444,6 +4683,7 @@ "version": "13.6.9", "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-13.6.9.tgz", "integrity": "sha512-CiUcHlGs8vE0CAB4oi1f+dzniqfGuhWPNrDvae2nm8dewlahTBwIcK5CawyGezjcJoeQhjBflh9vloska+nlnw==", + "license": "MIT", "dependencies": { "@react-native-community/cli-platform-apple": "13.6.9" } @@ -4452,6 +4692,7 @@ "version": "13.6.9", "resolved": "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-13.6.9.tgz", "integrity": "sha512-W8FSlCPWymO+tlQfM3E0JmM8Oei5HZsIk5S0COOl0MRi8h0NmHI4WSTF2GCfbFZkcr2VI/fRsocoN8Au4EZAug==", + "license": "MIT", "dependencies": { "@react-native-community/cli-debugger-ui": "13.6.9", "@react-native-community/cli-tools": "13.6.9", @@ -4468,6 +4709,7 @@ "version": "26.6.2", "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", @@ -4483,6 +4725,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -4491,6 +4734,7 @@ "version": "15.0.19", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.19.tgz", "integrity": "sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -4499,6 +4743,7 @@ "version": "26.6.2", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "license": "MIT", "dependencies": { "@jest/types": "^26.6.2", "ansi-regex": "^5.0.0", @@ -4512,12 +4757,14 @@ "node_modules/@react-native-community/cli-server-api/node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "license": "MIT" }, "node_modules/@react-native-community/cli-server-api/node_modules/ws": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "license": "MIT", "dependencies": { "async-limiter": "~1.0.0" } @@ -4526,6 +4773,7 @@ "version": "13.6.9", "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-13.6.9.tgz", "integrity": "sha512-OXaSjoN0mZVw3nrAwcY1PC0uMfyTd9fz7Cy06dh+EJc+h0wikABsVRzV8cIOPrVV+PPEEXE0DBrH20T2puZzgQ==", + "license": "MIT", "dependencies": { "appdirsjs": "^1.2.4", "chalk": "^4.1.2", @@ -4544,6 +4792,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "license": "MIT", "dependencies": { "restore-cursor": "^3.1.0" }, @@ -4555,6 +4804,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -4577,6 +4827,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -4588,6 +4839,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", "engines": { "node": ">=8" }, @@ -4599,6 +4851,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "license": "MIT", "engines": { "node": ">=4" } @@ -4607,6 +4860,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "license": "MIT", "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -4622,6 +4876,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", "engines": { "node": ">=6" } @@ -4630,6 +4885,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", "dependencies": { "path-key": "^3.0.0" }, @@ -4641,6 +4897,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -4655,6 +4912,7 @@ "version": "6.4.0", "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "license": "MIT", "dependencies": { "is-wsl": "^1.1.0" }, @@ -4666,6 +4924,7 @@ "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "license": "MIT", "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", @@ -4688,6 +4947,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "license": "MIT", "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -4697,9 +4957,10 @@ } }, "node_modules/@react-native-community/cli-tools/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -4710,12 +4971,15 @@ "node_modules/@react-native-community/cli-tools/node_modules/sudo-prompt": { "version": "9.2.1", "resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz", - "integrity": "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==" + "integrity": "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT" }, "node_modules/@react-native-community/cli-types": { "version": "13.6.9", "resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-13.6.9.tgz", "integrity": "sha512-RLxDppvRxXfs3hxceW/mShi+6o5yS+kFPnPqZTaMKKR5aSg7LwDpLQW4K2D22irEG8e6RKDkZUeH9aL3vO2O0w==", + "license": "MIT", "dependencies": { "joi": "^17.2.1" } @@ -4724,6 +4988,7 @@ "version": "9.5.0", "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "license": "MIT", "engines": { "node": "^12.20.0 || >=14" } @@ -4732,6 +4997,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -4754,6 +5020,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -4766,6 +5033,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -4777,6 +5045,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", "engines": { "node": ">=8" }, @@ -4788,6 +5057,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -4799,6 +5069,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", "engines": { "node": ">=6" } @@ -4807,6 +5078,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", "dependencies": { "path-key": "^3.0.0" }, @@ -4818,6 +5090,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -4832,6 +5105,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -4846,6 +5120,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -4854,9 +5129,10 @@ } }, "node_modules/@react-native-community/cli/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -4868,6 +5144,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/@react-native-community/datetimepicker/-/datetimepicker-8.0.1.tgz", "integrity": "sha512-4BO0t3geMNNw9cIIm9p9FNUzwMXexdzD4pAH0AaUAycs3BS71HLrX8jHbrI7nzq/+8O7cLAXn5Gudte+YpTV8Q==", + "license": "MIT", "dependencies": { "invariant": "^2.2.4" }, @@ -4886,6 +5163,7 @@ "version": "11.3.1", "resolved": "https://registry.npmjs.org/@react-native-community/netinfo/-/netinfo-11.3.1.tgz", "integrity": "sha512-UBnJxyV0b7i9Moa97Av+HKho1ByzX0DtbJXzUQS5E3xhQs6P2D/Os0iw3ouy7joY1TVd6uIhplPbr7l1SJNaNQ==", + "license": "MIT", "peerDependencies": { "react-native": ">=0.59" } @@ -4894,6 +5172,7 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/@react-native-cookies/cookies/-/cookies-6.2.1.tgz", "integrity": "sha512-D17wCA0DXJkGJIxkL74Qs9sZ3sA+c+kCoGmXVknW7bVw/W+Vv1m/7mWTNi9DLBZSRddhzYw8SU0aJapIaM/g5w==", + "license": "MIT", "dependencies": { "invariant": "^2.2.4" }, @@ -4905,15 +5184,17 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/@react-native-masked-view/masked-view/-/masked-view-0.3.1.tgz", "integrity": "sha512-uVm8U6nwFIlUd1iDIB5cS+lDadApKR+l8k4k84d9hn+GN4lzAIJhUZ9syYX7c022MxNgAlbxoFLt0pqKoyaAGg==", + "license": "MIT", "peerDependencies": { "react": ">=16", "react-native": ">=0.57" } }, "node_modules/@react-native/assets-registry": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.74.88.tgz", - "integrity": "sha512-tOvA+ikxa0Yxk3gLWR4+Pp4Y6Se+JEs6XXabX4/jgxIDnDfhT/czFNhqH/hdk4uOT8uVJGnilvevsia2TCFMiw==", + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.74.89.tgz", + "integrity": "sha512-woHMQQ6h8+Uw7ULKbhp4XsualYyeb2yhsl3Y14D0s40Rt+qeGy74t1wwhOu/BCV13mHM3o5vRahCr0LRTUSXTQ==", + "license": "MIT", "engines": { "node": ">=18" } @@ -4922,6 +5203,7 @@ "version": "0.74.87", "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.74.87.tgz", "integrity": "sha512-+vJYpMnENFrwtgvDfUj+CtVJRJuUnzAUYT0/Pb68Sq9RfcZ5xdcCuUgyf7JO+akW2VTBoJY427wkcxU30qrWWw==", + "license": "MIT", "dependencies": { "@react-native/codegen": "0.74.87" }, @@ -4933,6 +5215,7 @@ "version": "0.74.87", "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.74.87.tgz", "integrity": "sha512-hyKpfqzN2nxZmYYJ0tQIHG99FQO0OWXp/gVggAfEUgiT+yNKas1C60LuofUsK7cd+2o9jrpqgqW4WzEDZoBlTg==", + "license": "MIT", "dependencies": { "@babel/core": "^7.20.0", "@babel/plugin-proposal-async-generator-functions": "^7.0.0", @@ -4989,6 +5272,7 @@ "version": "0.74.87", "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.74.87.tgz", "integrity": "sha512-GMSYDiD+86zLKgMMgz9z0k6FxmRn+z6cimYZKkucW4soGbxWsbjUAZoZ56sJwt2FJ3XVRgXCrnOCgXoH/Bkhcg==", + "license": "MIT", "dependencies": { "@babel/parser": "^7.20.0", "glob": "^7.1.1", @@ -5006,14 +5290,15 @@ } }, "node_modules/@react-native/community-cli-plugin": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.74.88.tgz", - "integrity": "sha512-O8zz784kksa36nBNiULHh0rYFGr4mwtBB95YvvBOEYiYnMjFkEOUe7BPKvYmX8W29MgskXcIGNrNvfre59o4xw==", + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.74.89.tgz", + "integrity": "sha512-1/LpkO7CM95btG8BVeQcn0WjlKZ4nghsUtcYIYD3TMCkRjRluYzzmpZrVm5hiam57X/n39PjdJhUoEz9CUMobw==", + "license": "MIT", "dependencies": { "@react-native-community/cli-server-api": "13.6.9", "@react-native-community/cli-tools": "13.6.9", - "@react-native/dev-middleware": "0.74.88", - "@react-native/metro-babel-transformer": "0.74.88", + "@react-native/dev-middleware": "0.74.89", + "@react-native/metro-babel-transformer": "0.74.89", "chalk": "^4.0.0", "execa": "^5.1.1", "metro": "^0.80.3", @@ -5028,20 +5313,22 @@ } }, "node_modules/@react-native/community-cli-plugin/node_modules/@react-native/debugger-frontend": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.74.88.tgz", - "integrity": "sha512-3xUR/uJza241ya0UFxxaxQiB/gkUx1gynMxhlgc6zFxz/zSrLG1/AcA6hpua2ZvmOMabpo09XOOR1Hqvf2qPEQ==", + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.74.89.tgz", + "integrity": "sha512-2kk5+tz2SaidkVBnAlpDyN3wMVRrsthtj/fxx2Jf5+P/xqbUJ2kZBzF066fAMONCFE/IHfStMfnpTxTKWOGs/Q==", + "license": "BSD-3-Clause", "engines": { "node": ">=18" } }, "node_modules/@react-native/community-cli-plugin/node_modules/@react-native/dev-middleware": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.74.88.tgz", - "integrity": "sha512-RYaQ72j9ggeGI712UlAfWtuY0rD4WllArlYtEybT0x1zmUtLgq5lgJcSkwg501yfG/g10XB69Q2MM8gCWK8NAw==", + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.74.89.tgz", + "integrity": "sha512-cv+cHfJwzY2QD27A95ETWviXWpG0poLWU5VECQkCQQdIPteJY0xY49GYK/Um0hSuM/2PgchAkty1wds9o+dbKg==", + "license": "MIT", "dependencies": { "@isaacs/ttlcache": "^1.4.1", - "@react-native/debugger-frontend": "0.74.88", + "@react-native/debugger-frontend": "0.74.89", "@rnx-kit/chromium-edge-launcher": "^1.0.0", "chrome-launcher": "^0.15.2", "connect": "^3.6.5", @@ -5062,6 +5349,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -5070,6 +5358,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -5092,6 +5381,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -5103,6 +5393,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", "engines": { "node": ">=8" }, @@ -5114,6 +5405,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", "engines": { "node": ">=6" } @@ -5121,12 +5413,14 @@ "node_modules/@react-native/community-cli-plugin/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/@react-native/community-cli-plugin/node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", "dependencies": { "path-key": "^3.0.0" }, @@ -5138,6 +5432,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -5152,6 +5447,7 @@ "version": "7.4.2", "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "license": "MIT", "dependencies": { "is-docker": "^2.0.0", "is-wsl": "^2.1.1" @@ -5167,6 +5463,7 @@ "version": "6.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "license": "MIT", "dependencies": { "async-limiter": "~1.0.0" } @@ -5245,28 +5542,31 @@ } }, "node_modules/@react-native/gradle-plugin": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.74.88.tgz", - "integrity": "sha512-cUu4gVLFTkHe0e5/IxSycRfbBhZs/5QF8AqYcoUBsZ5o+22Im9+M4DuGFv4U5Sa2NTy2VXOCpbBTepzKsdXlgw==", + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.74.89.tgz", + "integrity": "sha512-lLGmG8Ti6RyyMmULOH5M3aDD0Q1HXPdYSm/3VPqJTxtRONbnyWpl1hC/NsbgwpUHeJ/DUCY8DFZIArtaXkhExA==", + "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@react-native/js-polyfills": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.74.88.tgz", - "integrity": "sha512-6KljxfNKAz2b2uXqxagKbytb3MvUujAmfvuubKOoCLAiLbs8CYKW0OV1FqVLYUEXXw5GEDhXcVzQxxFuDlMafQ==", + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.74.89.tgz", + "integrity": "sha512-MT609lh6SnZYWZVIFTTtL37nu5UOK4Y9CpXw9K6DoUndhkejYY/dBsJ159WNuIFv2xCOtJDYiNPNFOmnRQwYvw==", + "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@react-native/metro-babel-transformer": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.74.88.tgz", - "integrity": "sha512-r7Er162iLpQce3ODQzNVS+PnjglJoHZ4l0NeaVMB4w45DIgKM4hC2vI6a/fzyFm9C6N+QY4P2i2RSkwjXVuBlQ==", + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.74.89.tgz", + "integrity": "sha512-rGKSkXLwsYRFCfBku0ZVODqMVAI6mm2yFdYUhKu5U0qIL9bffn4Ow8lHxzdyXMiEROE0jsnN31BOP19cbVI/HA==", + "license": "MIT", "dependencies": { "@babel/core": "^7.20.0", - "@react-native/babel-preset": "0.74.88", + "@react-native/babel-preset": "0.74.89", "hermes-parser": "0.19.1", "nullthrows": "^1.1.1" }, @@ -5278,20 +5578,22 @@ } }, "node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/babel-plugin-codegen": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.74.88.tgz", - "integrity": "sha512-hul4gPU09q7K0amhzhZnG3EVxeCXjP2l1x/zdgtliRRB8Nq7Za8YkM7dy84X+Vv4UC9G1nzxIbibsKeLsY1N4A==", + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.74.89.tgz", + "integrity": "sha512-E1SF2eHf2AZ0JPZZC54v6xlL2ZonMoUk0wvo3NtllvMDGn6LqlO5i4rphz3QOtX5OZa6/PhvadqLd0otmKXgIg==", + "license": "MIT", "dependencies": { - "@react-native/codegen": "0.74.88" + "@react-native/codegen": "0.74.89" }, "engines": { "node": ">=18" } }, "node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/babel-preset": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.74.88.tgz", - "integrity": "sha512-SQODiFGlyblFTvdvePUDrQ+qlSzhcOm7It/yW2CVKxw5zRUf50+Cj3DBkRFhQDqF3ri2EnWsLnJ3oNE7hqDUxg==", + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.74.89.tgz", + "integrity": "sha512-JVI3sjnQxOjqVhERX19XYEc2HPmf0nFFmhF3CAvnxo+11GrP/eOqa1q+mAE0sXueVy+/rVjwohOxKWgwoQqtIA==", + "license": "MIT", "dependencies": { "@babel/core": "^7.20.0", "@babel/plugin-proposal-async-generator-functions": "^7.0.0", @@ -5333,7 +5635,7 @@ "@babel/plugin-transform-typescript": "^7.5.0", "@babel/plugin-transform-unicode-regex": "^7.0.0", "@babel/template": "^7.0.0", - "@react-native/babel-plugin-codegen": "0.74.88", + "@react-native/babel-plugin-codegen": "0.74.89", "babel-plugin-transform-flow-enums": "^0.0.2", "react-refresh": "^0.14.0" }, @@ -5345,9 +5647,10 @@ } }, "node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/codegen": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.74.88.tgz", - "integrity": "sha512-HMk/LCrSdUof9DZFaB2bK0soKyAF6XiCg2LG7WFjEkUDXayeiB4p7IsHISJWY4bYg7cMPZ0fiZMRaBP2vXJxgg==", + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.74.89.tgz", + "integrity": "sha512-xbcpnvsAjHrnYWnuoLdr5782dlR4LfkaWPityka/gWmdRDrE69ByAC9m0/vijMXAcMoHv6DSMh5x7gm6gW/3mA==", + "license": "MIT", "dependencies": { "@babel/parser": "^7.20.0", "glob": "^7.1.1", @@ -5368,34 +5671,14 @@ "node_modules/@react-native/normalize-colors": { "version": "0.74.85", "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.85.tgz", - "integrity": "sha512-pcE4i0X7y3hsAE0SpIl7t6dUc0B0NZLd1yv7ssm4FrLhWG+CGyIq4eFDXpmPU1XHmL5PPySxTAjEMiwv6tAmOw==" - }, - "node_modules/@react-native/virtualized-lists": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.74.88.tgz", - "integrity": "sha512-nZn4X9zuyinRJoE/WcgB1e/X6b3J3QPRSsNC0LOjHzP97tvW6xvBacjbCAJAaZQwD9KaqZyK86eCi61ksr350g==", - "dependencies": { - "invariant": "^2.2.4", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/react": "^18.2.6", - "react": "*", - "react-native": "*" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } + "integrity": "sha512-pcE4i0X7y3hsAE0SpIl7t6dUc0B0NZLd1yv7ssm4FrLhWG+CGyIq4eFDXpmPU1XHmL5PPySxTAjEMiwv6tAmOw==", + "license": "MIT" }, "node_modules/@react-navigation/bottom-tabs": { "version": "6.6.1", "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-6.6.1.tgz", "integrity": "sha512-9oD4cypEBjPuaMiu9tevWGiQ4w/d6l3HNhcJ1IjXZ24xvYDSs0mqjUcdt8SWUolCvRrYc/DmNBLlT83bk0bHTw==", + "license": "MIT", "dependencies": { "@react-navigation/elements": "^1.3.31", "color": "^4.2.3", @@ -5413,6 +5696,7 @@ "version": "6.4.17", "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-6.4.17.tgz", "integrity": "sha512-Nd76EpomzChWAosGqWOYE3ItayhDzIEzzZsT7PfGcRFDgW5miHV2t4MZcq9YIK4tzxZjVVpYbIynOOQQd1e0Cg==", + "license": "MIT", "dependencies": { "@react-navigation/routers": "^6.1.9", "escape-string-regexp": "^4.0.0", @@ -5429,6 +5713,7 @@ "version": "1.3.31", "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-1.3.31.tgz", "integrity": "sha512-bUzP4Awlljx5RKEExw8WYtif8EuQni2glDaieYROKTnaxsu9kEIA515sXQgUDZU4Ob12VoL7+z70uO3qrlfXcQ==", + "license": "MIT", "peerDependencies": { "@react-navigation/native": "^6.0.0", "react": "*", @@ -5440,6 +5725,7 @@ "version": "6.1.18", "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-6.1.18.tgz", "integrity": "sha512-mIT9MiL/vMm4eirLcmw2h6h/Nm5FICtnYSdohq4vTLA2FF/6PNhByM7s8ffqoVfE5L0uAa6Xda1B7oddolUiGg==", + "license": "MIT", "dependencies": { "@react-navigation/core": "^6.4.17", "escape-string-regexp": "^4.0.0", @@ -5455,6 +5741,7 @@ "version": "6.11.0", "resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-6.11.0.tgz", "integrity": "sha512-U5EcUB9Q2NQspCFwYGGNJm0h6wBCOv7T30QjndmvlawLkNt7S7KWbpWyxS9XBHSIKF57RgWjfxuJNTgTstpXxw==", + "license": "MIT", "dependencies": { "@react-navigation/elements": "^1.3.31", "warn-once": "^0.1.0" @@ -5471,6 +5758,7 @@ "version": "6.1.9", "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-6.1.9.tgz", "integrity": "sha512-lTM8gSFHSfkJvQkxacGM6VJtBt61ip2XO54aNfswD+KMw6eeZ4oehl7m0me3CR9hnDE4+60iAZR8sAhvCiI3NA==", + "license": "MIT", "dependencies": { "nanoid": "^3.1.23" } @@ -5479,6 +5767,7 @@ "version": "6.4.1", "resolved": "https://registry.npmjs.org/@react-navigation/stack/-/stack-6.4.1.tgz", "integrity": "sha512-upMEHOKMtuMu4c9gmoPlO/JqI6mDlSqwXg1aXKOTQLXAF8H5koOLRfrmi7AkdiE9A7lDXWUAZoGuD9O88cYvDQ==", + "license": "MIT", "dependencies": { "@react-navigation/elements": "^1.3.31", "color": "^4.2.3", @@ -5497,6 +5786,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/@rnx-kit/chromium-edge-launcher/-/chromium-edge-launcher-1.0.0.tgz", "integrity": "sha512-lzD84av1ZQhYUS+jsGqJiCMaJO2dn9u+RTT9n9q6D3SaKVwWqv+7AoRKqBu19bkwyE+iFRl1ymr40QS90jVFYg==", + "license": "Apache-2.0", "dependencies": { "@types/node": "^18.0.0", "escape-string-regexp": "^4.0.0", @@ -5513,6 +5803,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" }, @@ -5521,9 +5812,10 @@ } }, "node_modules/@scure/base": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", - "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.4.tgz", + "integrity": "sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ==", + "license": "MIT", "funding": { "url": "https://paulmillr.com/funding/" } @@ -5541,6 +5833,7 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", "integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==", + "license": "MIT", "dependencies": { "domhandler": "^5.0.3", "selderee": "^0.11.0" @@ -5553,6 +5846,7 @@ "version": "4.1.5", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "license": "BSD-3-Clause", "dependencies": { "@hapi/hoek": "^9.0.0" } @@ -5560,22 +5854,26 @@ "node_modules/@sideway/formula": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "license": "BSD-3-Clause" }, "node_modules/@sideway/pinpoint": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "license": "BSD-3-Clause" }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "license": "MIT" }, "node_modules/@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "license": "BSD-3-Clause", "dependencies": { "type-detect": "4.0.8" } @@ -5584,17 +5882,19 @@ "version": "10.3.0", "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "license": "BSD-3-Clause", "dependencies": { "@sinonjs/commons": "^3.0.0" } }, "node_modules/@stylistic/eslint-plugin": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.10.1.tgz", - "integrity": "sha512-U+4yzNXElTf9q0kEfnloI9XbOyD4cnEQCxjUI94q0+W++0GAEQvJ/slwEj9lwjDHfGADRSr+Tco/z0XJvmDfCQ==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.13.0.tgz", + "integrity": "sha512-RnO1SaiCFHn666wNz2QfZEFxvmiNRqhzaMXHXxXXKt+MEP7aajlPxUSMIQpKAaJfverpovEYqjBOXDq6dDcaOQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "^8.12.2", + "@typescript-eslint/utils": "^8.13.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "estraverse": "^5.3.0", @@ -5612,6 +5912,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -5628,6 +5929,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -5644,6 +5946,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -5660,6 +5963,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -5676,6 +5980,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -5692,6 +5997,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -5708,6 +6014,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -5724,6 +6031,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -5740,6 +6048,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", "dev": true, + "license": "MIT", "dependencies": { "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", @@ -5766,6 +6075,7 @@ "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -5786,6 +6096,7 @@ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dev": true, + "license": "MIT", "dependencies": { "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", @@ -5812,6 +6123,7 @@ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -5830,6 +6142,7 @@ "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.21.3", "entities": "^4.4.0" @@ -5847,6 +6160,7 @@ "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -5869,6 +6183,7 @@ "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", "dev": true, + "license": "MIT", "dependencies": { "cosmiconfig": "^8.1.3", "deepmerge": "^4.3.1", @@ -5890,6 +6205,7 @@ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dev": true, + "license": "MIT", "dependencies": { "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", @@ -5916,6 +6232,7 @@ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -5934,6 +6251,7 @@ "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10.13.0" } @@ -5942,28 +6260,33 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/@types/base-64/-/base-64-1.0.2.tgz", "integrity": "sha512-uPgKMmM9fmn7I+Zi6YBqctOye4SlJsHKcisjHIMWpb2YKZRc36GpKyNuQ03JcT+oNXg1m7Uv4wU94EVltn8/cw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/hammerjs": { "version": "2.0.46", "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.46.tgz", - "integrity": "sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==" + "integrity": "sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==", + "license": "MIT" }, "node_modules/@types/html-to-text": { "version": "9.0.4", "resolved": "https://registry.npmjs.org/@types/html-to-text/-/html-to-text-9.0.4.tgz", "integrity": "sha512-pUY3cKH/Nm2yYrEmDlPR1mR7yszjGx4DrwPjQ702C4/D5CwHuZTgZdIdwPkRbcuhs7BAh2L5rg3CL5cbRiGTCQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==" + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "license": "MIT" }, "node_modules/@types/istanbul-lib-report": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "*" } @@ -5972,21 +6295,24 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "*", "@types/istanbul-lib-report": "*" } }, "node_modules/@types/lodash": { - "version": "4.17.13", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.13.tgz", - "integrity": "sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==", - "dev": true + "version": "4.17.16", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.16.tgz", + "integrity": "sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==", + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { - "version": "18.19.64", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.64.tgz", - "integrity": "sha512-955mDqvO2vFf/oL7V3WiUtiz+BugyX8uVbaT2H8oj3+8dRyH2FLiNdowe7eNqRM7IOIZvzDH76EoAT+gwm6aIQ==", + "version": "18.19.80", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.80.tgz", + "integrity": "sha512-kEWeMwMeIvxYkeg1gTc01awpwLbfMRZXdIhwRcakd/KlK53jmRC26LqcbIt7fnAQTu5GzlnWmzA3H6+l1u6xxQ==", + "license": "MIT", "dependencies": { "undici-types": "~5.26.4" } @@ -5995,21 +6321,24 @@ "version": "1.3.11", "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/prop-types": { - "version": "15.7.13", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", - "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", - "devOptional": true + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "devOptional": true, + "license": "MIT" }, "node_modules/@types/react": { "version": "18.2.79", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.79.tgz", "integrity": "sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==", "devOptional": true, + "license": "MIT", "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -6029,12 +6358,14 @@ "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==" + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "license": "MIT" }, "node_modules/@types/yargs": { "version": "13.0.12", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -6042,13 +6373,15 @@ "node_modules/@types/yargs-parser": { "version": "21.0.3", "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "7.18.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "7.18.0", @@ -6082,6 +6415,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@typescript-eslint/scope-manager": "7.18.0", @@ -6104,6 +6438,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/scope-manager": "7.18.0", "@typescript-eslint/types": "7.18.0", @@ -6132,6 +6467,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/types": "7.18.0", "@typescript-eslint/visitor-keys": "7.18.0" @@ -6149,6 +6485,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/typescript-estree": "7.18.0", "@typescript-eslint/utils": "7.18.0", @@ -6176,6 +6513,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@typescript-eslint/scope-manager": "7.18.0", @@ -6198,6 +6536,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || >=20.0.0" }, @@ -6211,6 +6550,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/types": "7.18.0", "@typescript-eslint/visitor-keys": "7.18.0", @@ -6235,10 +6575,11 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -6247,15 +6588,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.13.0.tgz", - "integrity": "sha512-A1EeYOND6Uv250nybnLZapeXpYMl8tkzYUxqmoKAWnI4sei3ihf2XdZVd+vVOmHGcp3t+P7yRrNsyyiXTvShFQ==", + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.1.tgz", + "integrity": "sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.13.0", - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/typescript-estree": "8.13.0" + "@typescript-eslint/scope-manager": "8.26.1", + "@typescript-eslint/types": "8.26.1", + "@typescript-eslint/typescript-estree": "8.26.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6265,17 +6607,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.13.0.tgz", - "integrity": "sha512-XsGWww0odcUT0gJoBZ1DeulY1+jkaHUciUq4jKNv4cpInbvvrtDoyBH9rE/n2V29wQJPk8iCH1wipra9BhmiMA==", + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.26.1.tgz", + "integrity": "sha512-6EIvbE5cNER8sqBu6V7+KeMZIC1664d2Yjt+B9EWUXrsyWpxx4lEZrmvxgSKRC6gX+efDL/UY9OpPZ267io3mg==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/visitor-keys": "8.13.0" + "@typescript-eslint/types": "8.26.1", + "@typescript-eslint/visitor-keys": "8.26.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6286,10 +6630,11 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.13.0.tgz", - "integrity": "sha512-4cyFErJetFLckcThRUFdReWJjVsPCqyBlJTi6IDEpc1GWCIIZRFxVppjWLIMcQhNGhdWJJRYFHpHoDWvMlDzng==", + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.26.1.tgz", + "integrity": "sha512-n4THUQW27VmQMx+3P+B0Yptl7ydfceUj4ON/AQILAASwgYdZ/2dhfymRMh5egRUrvK5lSmaOm77Ry+lmXPOgBQ==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -6299,19 +6644,20 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.13.0.tgz", - "integrity": "sha512-v7SCIGmVsRK2Cy/LTLGN22uea6SaUIlpBcO/gnMGT/7zPtxp90bphcGf4fyrCQl3ZtiBKqVTG32hb668oIYy1g==", + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.26.1.tgz", + "integrity": "sha512-yUwPpUHDgdrv1QJ7YQal3cMVBGWfnuCdKbXw1yyjArax3353rEJP1ZA+4F8nOlQ3RfS2hUN/wze3nlY+ZOhvoA==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/visitor-keys": "8.13.0", + "@typescript-eslint/types": "8.26.1", + "@typescript-eslint/visitor-keys": "8.26.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6320,20 +6666,19 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.13.0.tgz", - "integrity": "sha512-7N/+lztJqH4Mrf0lb10R/CbI1EaAMMGyF5y0oJvFoAhafwgiRA7TXyd8TFn8FC8k5y2dTsYogg238qavRGNnlw==", + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.26.1.tgz", + "integrity": "sha512-AjOC3zfnxd6S4Eiy3jwktJPclqhFHNyd8L6Gycf9WUPoKZpgM5PjkxY1X7uSy61xVpiJDhhk7XT2NVsN3ALTWg==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.13.0", - "eslint-visitor-keys": "^3.4.3" + "@typescript-eslint/types": "8.26.1", + "eslint-visitor-keys": "^4.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6343,23 +6688,12 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -6367,11 +6701,25 @@ "node": ">=10" } }, + "node_modules/@typescript-eslint/utils/node_modules/ts-api-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", + "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/@typescript-eslint/visitor-keys": { "version": "7.18.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/types": "7.18.0", "eslint-visitor-keys": "^3.4.3" @@ -6389,6 +6737,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -6397,10 +6746,11 @@ } }, "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" }, "node_modules/@urql/core": { "version": "2.3.6", @@ -6433,6 +6783,7 @@ "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.13.tgz", "integrity": "sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g==", "deprecated": "this version is no longer supported, please update to at least 0.8.*", + "license": "MIT", "engines": { "node": ">=10.0.0" } @@ -6441,6 +6792,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", "dependencies": { "event-target-shim": "^5.0.0" }, @@ -6452,6 +6804,7 @@ "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" @@ -6461,9 +6814,10 @@ } }, "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -6476,6 +6830,7 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -6510,6 +6865,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -6533,7 +6889,8 @@ "node_modules/anser": { "version": "1.4.10", "resolved": "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz", - "integrity": "sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==" + "integrity": "sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==", + "license": "MIT" }, "node_modules/ansi-escapes": { "version": "4.3.2", @@ -6554,6 +6911,7 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/ansi-fragments/-/ansi-fragments-0.2.1.tgz", "integrity": "sha512-DykbNHxuXQwUDRv5ibc2b0x7uw7wmwOGLBUd5RmaQ5z8Lhx19vwvKV+FAsM5rEA6dEcHxX+/Ad5s9eF2k2bB+w==", + "license": "MIT", "dependencies": { "colorette": "^1.0.7", "slice-ansi": "^2.0.0", @@ -6564,6 +6922,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "license": "MIT", "engines": { "node": ">=6" } @@ -6572,6 +6931,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "license": "MIT", "dependencies": { "ansi-regex": "^4.1.0" }, @@ -6583,6 +6943,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -6591,6 +6952,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -6604,12 +6966,14 @@ "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -6622,6 +6986,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -6632,7 +6997,8 @@ "node_modules/appdirsjs": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/appdirsjs/-/appdirsjs-1.2.7.tgz", - "integrity": "sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw==" + "integrity": "sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw==", + "license": "MIT" }, "node_modules/application-config-path": { "version": "0.1.1", @@ -6649,15 +7015,17 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" }, "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" }, "engines": { "node": ">= 0.4" @@ -6671,6 +7039,7 @@ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -6690,6 +7059,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "license": "MIT", "engines": { "node": ">=8" } @@ -6699,6 +7069,7 @@ "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -6715,15 +7086,16 @@ } }, "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -6733,15 +7105,16 @@ } }, "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -6755,6 +7128,7 @@ "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -6767,18 +7141,18 @@ } }, "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" }, "engines": { "node": ">= 0.4" @@ -6790,12 +7164,14 @@ "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "license": "MIT" }, "node_modules/asn1js": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.5.tgz", "integrity": "sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==", + "license": "BSD-3-Clause", "dependencies": { "pvtsutils": "^1.3.2", "pvutils": "^1.1.3", @@ -6809,6 +7185,7 @@ "version": "0.15.2", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.15.2.tgz", "integrity": "sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg==", + "license": "MIT", "dependencies": { "tslib": "^2.0.1" }, @@ -6820,24 +7197,37 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "license": "MIT", "engines": { "node": ">=4" } }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/async-limiter": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "license": "MIT" }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" }, "node_modules/at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "license": "ISC", "engines": { "node": ">= 4.0.0" } @@ -6846,6 +7236,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" }, @@ -6871,17 +7262,19 @@ "version": "7.0.0-bridge.0", "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", "integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==", + "license": "MIT", "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", - "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", + "version": "0.4.12", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz", + "integrity": "sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==", + "license": "MIT", "dependencies": { "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.2", + "@babel/helper-define-polyfill-provider": "^0.6.3", "semver": "^6.3.1" }, "peerDependencies": { @@ -6889,23 +7282,25 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.10.6", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", - "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", + "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", + "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2", - "core-js-compat": "^3.38.0" + "@babel/helper-define-polyfill-provider": "^0.6.3", + "core-js-compat": "^3.40.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", - "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz", + "integrity": "sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==", + "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2" + "@babel/helper-define-polyfill-provider": "^0.6.3" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -6915,6 +7310,7 @@ "version": "0.0.0-experimental-592953e-20240517", "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-0.0.0-experimental-592953e-20240517.tgz", "integrity": "sha512-OjG1SVaeQZaJrqkMFJatg8W/MTow8Ak5rx2SI0ETQBO1XvOk/XZGMbltNCPdFJLKghBYoBjC+Y3Ap/Xr7B01mA==", + "license": "MIT", "dependencies": { "@babel/generator": "7.2.0", "@babel/types": "^7.19.0", @@ -6929,6 +7325,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.2.0.tgz", "integrity": "sha512-BA75MVfRlFQG2EZgFYIwyT1r6xSkwfP2bdkY/kLZusEYWiJs4xCowab/alaEaT0wSvmVuXGqiefeBlP+7V1yKg==", + "license": "MIT", "dependencies": { "@babel/types": "^7.2.0", "jsesc": "^2.5.1", @@ -6941,6 +7338,7 @@ "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, @@ -6952,6 +7350,7 @@ "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -6959,12 +7358,14 @@ "node_modules/babel-plugin-react-native-web": { "version": "0.19.13", "resolved": "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.19.13.tgz", - "integrity": "sha512-4hHoto6xaN23LCyZgL9LJZc3olmAxd7b6jDzlZnKXAh4rRAbZRKNBJoOOdp46OBqgy+K0t0guTj5/mhA8inymQ==" + "integrity": "sha512-4hHoto6xaN23LCyZgL9LJZc3olmAxd7b6jDzlZnKXAh4rRAbZRKNBJoOOdp46OBqgy+K0t0guTj5/mhA8inymQ==", + "license": "MIT" }, "node_modules/babel-plugin-transform-flow-enums": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-enums/-/babel-plugin-transform-flow-enums-0.0.2.tgz", "integrity": "sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==", + "license": "MIT", "dependencies": { "@babel/plugin-syntax-flow": "^7.12.1" } @@ -6973,6 +7374,7 @@ "version": "11.0.15", "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-11.0.15.tgz", "integrity": "sha512-rgiMTYwqIPULaO7iZdqyL7aAff9QLOX6OWUtLZBlOrOTreGY1yHah/5+l8MvI6NVc/8Zj5LY4Y5uMSnJIuzTLw==", + "license": "MIT", "dependencies": { "@babel/plugin-proposal-decorators": "^7.12.9", "@babel/plugin-transform-export-namespace-from": "^7.22.11", @@ -6989,17 +7391,20 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" }, "node_modules/base-64": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", - "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==" + "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==", + "license": "MIT" }, "node_modules/base64-arraybuffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "license": "MIT", "engines": { "node": ">= 0.6.0" } @@ -7021,7 +7426,8 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/better-opn": { "version": "3.0.2", @@ -7039,6 +7445,7 @@ "version": "1.6.52", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "license": "Unlicense", "engines": { "node": ">=0.6" } @@ -7047,6 +7454,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -7071,6 +7479,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -7080,6 +7489,7 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -7092,7 +7502,8 @@ "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" }, "node_modules/bplist-creator": { "version": "0.0.7", @@ -7119,6 +7530,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -7127,6 +7539,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -7135,9 +7548,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", - "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "funding": [ { "type": "opencollective", @@ -7152,10 +7565,11 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001669", - "electron-to-chromium": "^1.5.41", - "node-releases": "^2.0.18", + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { @@ -7169,6 +7583,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "license": "Apache-2.0", "dependencies": { "node-int64": "^0.4.0" } @@ -7191,6 +7606,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" @@ -7200,6 +7616,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "license": "MIT", "dependencies": { "buffer-alloc-unsafe": "^1.1.0", "buffer-fill": "^1.0.0" @@ -7208,17 +7625,20 @@ "node_modules/buffer-alloc-unsafe": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "license": "MIT" }, "node_modules/buffer-fill": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==" + "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", + "license": "MIT" }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" }, "node_modules/builtins": { "version": "1.0.3", @@ -7230,6 +7650,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -7287,21 +7708,51 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/cal-parser/-/cal-parser-1.0.2.tgz", "integrity": "sha512-wlQwcF0fl4eLclyGdncF9rcNNq0ipRYZGagG6h3LVgRXvCWE1fdMUaCLXwfC9YWoz9jKKbjQAq7TpO2Y3yrvmA==", + "license": "MIT", "dependencies": { "ical-date-parser": "^4.0.0", "rrule": "^2.6.8" } }, "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -7314,6 +7765,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", + "license": "MIT", "dependencies": { "callsites": "^2.0.0" }, @@ -7325,6 +7777,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==", + "license": "MIT", "engines": { "node": ">=4" } @@ -7333,6 +7786,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", + "license": "MIT", "dependencies": { "caller-callsite": "^2.0.0" }, @@ -7345,6 +7799,7 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -7353,6 +7808,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "license": "MIT", "dependencies": { "pascal-case": "^3.1.2", "tslib": "^2.0.3" @@ -7362,6 +7818,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -7370,9 +7827,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001680", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz", - "integrity": "sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==", + "version": "1.0.30001704", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001704.tgz", + "integrity": "sha512-+L2IgBbV6gXB4ETf0keSvLr7JUrRVbIaB/lrQ1+z8mRcQiisG5k+lG6O4n6Y5q6f5EuNfaYXKgymucphlEXQew==", "funding": [ { "type": "opencollective", @@ -7386,12 +7843,14 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/capital-case": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", + "license": "MIT", "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", @@ -7402,6 +7861,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -7417,6 +7877,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", + "license": "MIT", "dependencies": { "camel-case": "^4.1.2", "capital-case": "^1.0.4", @@ -7454,6 +7915,7 @@ "version": "0.15.2", "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.2.tgz", "integrity": "sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==", + "license": "Apache-2.0", "dependencies": { "@types/node": "*", "escape-string-regexp": "^4.0.0", @@ -7477,6 +7939,7 @@ "url": "https://github.com/sponsors/sibiraj-s" } ], + "license": "MIT", "engines": { "node": ">=8" } @@ -7506,6 +7969,7 @@ "version": "2.9.2", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", "engines": { "node": ">=6" }, @@ -7517,6 +7981,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -7539,6 +8004,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "license": "MIT", "dependencies": { "is-plain-object": "^2.0.4", "kind-of": "^6.0.2", @@ -7552,6 +8018,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" @@ -7564,6 +8031,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -7574,12 +8042,14 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/color-string": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" @@ -7588,12 +8058,14 @@ "node_modules/colorette": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", - "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==" + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "license": "MIT" }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -7604,12 +8076,14 @@ "node_modules/command-exists": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", - "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==", + "license": "MIT" }, "node_modules/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", "engines": { "node": ">= 10" } @@ -7617,7 +8091,8 @@ "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "license": "MIT" }, "node_modules/component-type": { "version": "1.2.2", @@ -7632,6 +8107,7 @@ "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "license": "MIT", "dependencies": { "mime-db": ">= 1.43.0 < 2" }, @@ -7640,9 +8116,10 @@ } }, "node_modules/compression": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.5.tgz", - "integrity": "sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz", + "integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==", + "license": "MIT", "dependencies": { "bytes": "3.1.2", "compressible": "~2.0.18", @@ -7660,6 +8137,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -7667,12 +8145,14 @@ "node_modules/compression/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/compression/node_modules/negotiator": { "version": "0.6.4", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -7680,12 +8160,14 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" }, "node_modules/connect": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "license": "MIT", "dependencies": { "debug": "2.6.9", "finalhandler": "1.1.2", @@ -7700,6 +8182,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -7707,12 +8190,14 @@ "node_modules/connect/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/constant-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", + "license": "MIT", "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", @@ -7722,14 +8207,16 @@ "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" }, "node_modules/core-js-compat": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", - "integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==", + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.41.0.tgz", + "integrity": "sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A==", + "license": "MIT", "dependencies": { - "browserslist": "^4.24.2" + "browserslist": "^4.24.4" }, "funding": { "type": "opencollective", @@ -7739,12 +8226,14 @@ "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" }, "node_modules/cosmiconfig": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "license": "MIT", "dependencies": { "import-fresh": "^2.0.0", "is-directory": "^0.3.1", @@ -7759,6 +8248,7 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" } @@ -7767,6 +8257,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "license": "MIT", "dependencies": { "caller-path": "^2.0.0", "resolve-from": "^3.0.0" @@ -7779,6 +8270,7 @@ "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "license": "MIT", "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -7791,22 +8283,25 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/cross-fetch": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", - "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", + "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", + "license": "MIT", "dependencies": { - "node-fetch": "^2.6.12" + "node-fetch": "^2.7.0" } }, "node_modules/cross-spawn": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz", - "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -7838,6 +8333,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "license": "MIT", "dependencies": { "utrie": "^1.0.2" } @@ -7846,6 +8342,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", @@ -7861,6 +8358,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "license": "MIT", "dependencies": { "mdn-data": "2.0.14", "source-map": "^0.6.1" @@ -7873,6 +8371,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -7881,6 +8380,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "license": "BSD-2-Clause", "engines": { "node": ">= 6" }, @@ -7893,6 +8393,7 @@ "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", "dev": true, + "license": "MIT", "dependencies": { "css-tree": "~2.2.0" }, @@ -7906,6 +8407,7 @@ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", "dev": true, + "license": "MIT", "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" @@ -7919,13 +8421,15 @@ "version": "2.0.28", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", - "dev": true + "dev": true, + "license": "CC0-1.0" }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true + "devOptional": true, + "license": "MIT" }, "node_modules/dag-map": { "version": "1.0.2", @@ -7934,13 +8438,14 @@ "license": "MIT" }, "node_modules/data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -7950,27 +8455,29 @@ } }, "node_modules/data-view-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/inspect-js" } }, "node_modules/data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" }, @@ -7994,12 +8501,14 @@ "node_modules/dayjs": { "version": "1.11.13", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", - "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" }, "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -8016,6 +8525,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -8024,6 +8534,7 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "license": "MIT", "engines": { "node": ">=0.10" } @@ -8041,12 +8552,14 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -8068,6 +8581,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "license": "MIT", "dependencies": { "clone": "^1.0.2" }, @@ -8079,6 +8593,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "license": "MIT", "engines": { "node": ">=0.8" } @@ -8087,6 +8602,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -8112,6 +8628,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "license": "MIT", "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -8150,6 +8667,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -8157,12 +8675,14 @@ "node_modules/denodeify": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/denodeify/-/denodeify-1.2.1.tgz", - "integrity": "sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg==" + "integrity": "sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg==", + "license": "MIT" }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -8191,6 +8711,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" @@ -8211,12 +8732,14 @@ "node_modules/dijkstrajs": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", - "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==" + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "license": "MIT" }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "license": "MIT", "dependencies": { "path-type": "^4.0.0" }, @@ -8229,6 +8752,7 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, + "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -8240,6 +8764,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -8258,12 +8783,14 @@ "type": "github", "url": "https://github.com/sponsors/fb55" } - ] + ], + "license": "BSD-2-Clause" }, "node_modules/domhandler": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", "dependencies": { "domelementtype": "^2.3.0" }, @@ -8275,9 +8802,10 @@ } }, "node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -8291,15 +8819,17 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "license": "MIT", "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, "node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "license": "BSD-2-Clause", "engines": { "node": ">=12" }, @@ -8308,11 +8838,12 @@ } }, "node_modules/dotenv-expand": { - "version": "11.0.6", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.6.tgz", - "integrity": "sha512-8NHi73otpWsZGBSZwwknTXS5pqMOrk9+Ssrna8xCaxkzEpU9OTf9R5ArQGVw03//Zmk9MOwLPng9WwndvpAJ5g==", + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", + "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", + "license": "BSD-2-Clause", "dependencies": { - "dotenv": "^16.4.4" + "dotenv": "^16.4.5" }, "engines": { "node": ">=12" @@ -8321,6 +8852,20 @@ "url": "https://dotenvx.com" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -8330,22 +8875,26 @@ "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.55", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.55.tgz", - "integrity": "sha512-6maZ2ASDOTBtjt9FhqYPRnbvKU5tjG0IN9SztUOWYw2AzNDNpKJYLJmlK0/En4Hs/aiWnB+JZ+gW19PIGszgKg==" + "version": "1.5.118", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.118.tgz", + "integrity": "sha512-yNDUus0iultYyVoEFLnQeei7LOQkL8wg8GQpkPCRrOlJXlcCwa6eGKZkxQ9ciHsqZyYbj8Jd94X1CTPzGm+uIA==", + "license": "ISC" }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" }, "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -8363,6 +8912,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", "engines": { "node": ">=0.12" }, @@ -8383,6 +8933,7 @@ "version": "7.14.0", "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz", "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==", + "license": "MIT", "bin": { "envinfo": "dist/cli.js" }, @@ -8400,6 +8951,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" } @@ -8408,6 +8960,7 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "license": "MIT", "dependencies": { "stackframe": "^1.3.4" } @@ -8416,6 +8969,7 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.1.tgz", "integrity": "sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A==", + "license": "MIT", "dependencies": { "accepts": "~1.3.7", "escape-html": "~1.0.3" @@ -8425,56 +8979,62 @@ } }, "node_modules/es-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" }, "engines": { "node": ">= 0.4" @@ -8484,12 +9044,10 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -8498,40 +9056,44 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/es-iterator-helpers": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.0.tgz", - "integrity": "sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", + "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.0.3", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", + "get-intrinsic": "^1.2.6", "globalthis": "^1.0.4", - "gopd": "^1.0.1", + "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "iterator.prototype": "^1.1.3", - "safe-array-concat": "^1.1.2" + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.4", + "safe-array-concat": "^1.1.3" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0" }, @@ -8540,35 +9102,42 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.4", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dev": true, + "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "license": "MIT", "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" }, "engines": { "node": ">= 0.4" @@ -8581,6 +9150,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", "engines": { "node": ">=6" } @@ -8588,12 +9158,14 @@ "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -8607,6 +9179,7 @@ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -8658,28 +9231,29 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.37.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz", - "integrity": "sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w==", + "version": "7.37.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz", + "integrity": "sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ==", "dev": true, + "license": "MIT", "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.2", + "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.1.0", + "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.8", "object.fromentries": "^2.0.8", - "object.values": "^1.2.0", + "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.11", + "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "engines": { @@ -8694,6 +9268,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-react-native/-/eslint-plugin-react-native-4.1.0.tgz", "integrity": "sha512-QLo7rzTBOl43FvVqDdq5Ql9IoElIuTdjrz9SKAXCvULvBoRZ44JGSkx9z4999ZusCsb4rK3gjS8gOGyeYqZv2Q==", "dev": true, + "license": "MIT", "dependencies": { "eslint-plugin-react-native-globals": "^0.1.1" }, @@ -8705,13 +9280,15 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/eslint-plugin-react-native-globals/-/eslint-plugin-react-native-globals-0.1.2.tgz", "integrity": "sha512-9aEPf1JEpiTjcFAmmyw8eiIXmcNZOqaZyHO77wgm0/dWfT/oxC1SrIq8ET38pMxHYrcB6Uew+TzUVsBeczF88g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/eslint-plugin-react/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -8722,6 +9299,7 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -8734,6 +9312,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -8762,6 +9341,7 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -8778,6 +9358,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -8790,6 +9371,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -8800,6 +9382,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -8812,6 +9395,7 @@ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -8829,6 +9413,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, @@ -8844,6 +9429,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -8856,6 +9442,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -8868,6 +9455,7 @@ "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", @@ -8884,6 +9472,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -8897,6 +9486,7 @@ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -8909,6 +9499,7 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -8921,16 +9512,19 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, "node_modules/esup-multi.js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/esup-multi.js/-/esup-multi.js-1.0.2.tgz", - "integrity": "sha512-EedHM/Khi22qlXURVBL+2YKnkxSLuW47OfRwQkh80RnU6WnLyGZd7t3HcbjvqlVNt3ZqQp0MxLAfG5DoVCaN6w==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esup-multi.js/-/esup-multi.js-1.0.4.tgz", + "integrity": "sha512-+OjFMJxtv4gO35IlywOj3jG2mWORLsdKaQB07ZAumzlQgfXrnwo2IF1qTybnK6NXiwD5PnsQj+xW/JP0Nz3VrQ==", + "license": "CeCILL-2.1", "dependencies": { - "fetch-cookie": "^3.0.1" + "fetch-cookie": "^3.0.1", + "tslib": "^2.7.0" }, "engines": { "node": ">=18" @@ -8940,6 +9534,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } @@ -8948,6 +9543,7 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -8956,6 +9552,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", "engines": { "node": ">=6" } @@ -9087,6 +9684,7 @@ "version": "5.9.1", "resolved": "https://registry.npmjs.org/expo-application/-/expo-application-5.9.1.tgz", "integrity": "sha512-uAfLBNZNahnDZLRU41ZFmNSKtetHUT9Ua557/q189ua0AWV7pQjoVAx49E4953feuvqc9swtU3ScZ/hN1XO/FQ==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9095,6 +9693,7 @@ "version": "10.0.10", "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-10.0.10.tgz", "integrity": "sha512-0qoTIihB79k+wGus9wy0JMKq7DdenziVx3iUkGvMAy2azscSgWH6bd2gJ9CGnhC6JRd3qTMFBL0ou/fx7WZl7A==", + "license": "MIT", "dependencies": { "expo-constants": "~16.0.0", "invariant": "^2.2.4", @@ -9108,6 +9707,7 @@ "version": "5.5.2", "resolved": "https://registry.npmjs.org/expo-auth-session/-/expo-auth-session-5.5.2.tgz", "integrity": "sha512-fgqrNz9FhCl/kNyU2Vy2AmLWk+X7vmgiGN2KVUgB8yLHl/tPogYLpNOiqFl/pMLMveoKjPpVOVfbz3RTJHJoTg==", + "license": "MIT", "dependencies": { "expo-application": "~5.9.0", "expo-constants": "~16.0.0", @@ -9121,6 +9721,7 @@ "version": "14.0.7", "resolved": "https://registry.npmjs.org/expo-av/-/expo-av-14.0.7.tgz", "integrity": "sha512-FvKZxyy+2/qcCmp+e1GTK3s4zH8ZO1RfjpqNxh7ARlS1oH8HPtk1AyZAMo52tHz3yQ3UIqxQ2YbI9CFb4065lA==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9141,6 +9742,7 @@ "version": "13.0.1", "resolved": "https://registry.npmjs.org/expo-barcode-scanner/-/expo-barcode-scanner-13.0.1.tgz", "integrity": "sha512-xBGLT1An2gpAMIQRTLU3oHydKohX8r8F9/ait1Fk9Vgd0GraFZbP4IiT7nHMlaw4H6E7Muucf7vXpGV6u7d4HQ==", + "license": "MIT", "dependencies": { "expo-image-loader": "~4.7.0" }, @@ -9149,9 +9751,10 @@ } }, "node_modules/expo-blur": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/expo-blur/-/expo-blur-13.0.2.tgz", - "integrity": "sha512-t2p7BChO3Reykued++QJRMZ/og6J3aXtSQ+bU31YcBeXhZLkHwjWEhiPKPnJka7J2/yTs4+jOCNDY0kCZmcE3w==", + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/expo-blur/-/expo-blur-13.0.3.tgz", + "integrity": "sha512-z5W9ZGlG6ZiRLuoJZG1AHRvjK8j+2+nc/mtvEtyAa8T/8iTNpUnX4eC8xXDoTL/H4y2pc3cHvytDjCXJG26pcQ==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9160,6 +9763,7 @@ "version": "12.0.1", "resolved": "https://registry.npmjs.org/expo-brightness/-/expo-brightness-12.0.1.tgz", "integrity": "sha512-Jdi8+9YeixWdg0Z2A/f3YnQ86+iMLuGyqcTi8UTipGotTHZBG8UuV1Gab/2tttOhUK2cfv4Hc2zDbkKFP6F1JQ==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9168,6 +9772,7 @@ "version": "0.12.5", "resolved": "https://registry.npmjs.org/expo-build-properties/-/expo-build-properties-0.12.5.tgz", "integrity": "sha512-donC1le0PYfLKCPKRMGQoixuWuwDWCngzXSoQXUPsgHTDHQUKr8aw+lcWkTwZcItgNovcnk784I0dyfYDcxybA==", + "license": "MIT", "dependencies": { "ajv": "^8.11.0", "semver": "^7.6.0" @@ -9180,6 +9785,7 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -9194,12 +9800,14 @@ "node_modules/expo-build-properties/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" }, "node_modules/expo-build-properties/node_modules/semver": { "version": "7.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -9211,6 +9819,7 @@ "version": "15.0.16", "resolved": "https://registry.npmjs.org/expo-camera/-/expo-camera-15.0.16.tgz", "integrity": "sha512-FLE02DMqkjwsb7IugKAqQvBe6s+TCQeb5LupO1+r//wAhBwmHncOrc6zV95ZEC2f9PTPK34nFH/s8CDGiVzIAA==", + "license": "MIT", "dependencies": { "invariant": "^2.2.4" }, @@ -9222,6 +9831,7 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/expo-clipboard/-/expo-clipboard-6.0.3.tgz", "integrity": "sha512-RIKDsuHkYfaspifbFpVC8sBVFKR05L7Pj7mU2/XkbrW9m01OBNvdpGraXEMsTFCx97xMGsZpEw9pPquL4j4xVg==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9230,6 +9840,7 @@ "version": "16.0.2", "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-16.0.2.tgz", "integrity": "sha512-9tNY3OVO0jfiMzl7ngb6IOyR5VFzNoN5OOazUWoeGfmMqVB5kltTemRvKraK9JRbBKIw+SOYLEmF0sEqgFZ6OQ==", + "license": "MIT", "dependencies": { "@expo/config": "~9.0.0", "@expo/env": "~0.3.0" @@ -9242,6 +9853,7 @@ "version": "13.0.2", "resolved": "https://registry.npmjs.org/expo-crypto/-/expo-crypto-13.0.2.tgz", "integrity": "sha512-7f/IMPYJZkBM21LNEMXGrNo/0uXSVfZTwufUdpNKedJR0fm5fH4DCSN79ZddlV26nF90PuXjK2inIbI6lb0qRA==", + "license": "MIT", "dependencies": { "base64-js": "^1.3.0" }, @@ -9253,6 +9865,7 @@ "version": "4.0.29", "resolved": "https://registry.npmjs.org/expo-dev-client/-/expo-dev-client-4.0.29.tgz", "integrity": "sha512-aANlw9dC4PJEPaRNpe+X5xwyYI+aCIcbZklAAsFlkv2/05gLrsvAFgmQpRtowAzF+VggHWde1eKUOeUccAYIEg==", + "license": "MIT", "dependencies": { "expo-dev-launcher": "4.0.29", "expo-dev-menu": "5.0.23", @@ -9268,6 +9881,7 @@ "version": "4.0.29", "resolved": "https://registry.npmjs.org/expo-dev-launcher/-/expo-dev-launcher-4.0.29.tgz", "integrity": "sha512-0a0SL8mc4FrqPeGxJHe9kf0kG+Di+38Gd+HP5DEL9dcOa8m2qffKnk22UcyujCT6+Qk0OUK1s53nnfqFB26uVw==", + "license": "MIT", "dependencies": { "ajv": "8.11.0", "expo-dev-menu": "5.0.23", @@ -9283,6 +9897,7 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -9297,12 +9912,14 @@ "node_modules/expo-dev-launcher/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" }, "node_modules/expo-dev-launcher/node_modules/semver": { "version": "7.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -9314,6 +9931,7 @@ "version": "5.0.23", "resolved": "https://registry.npmjs.org/expo-dev-menu/-/expo-dev-menu-5.0.23.tgz", "integrity": "sha512-ztDvrSdFGkRbMoQlGLyKMS6CslMGylonVW4kQHUrBQApCL0c2NtRwLlr2bA1SXF0S7qYdPPg/ayLnj7DDR5X2w==", + "license": "MIT", "dependencies": { "expo-dev-menu-interface": "1.8.4", "semver": "^7.5.4" @@ -9326,14 +9944,16 @@ "version": "1.8.4", "resolved": "https://registry.npmjs.org/expo-dev-menu-interface/-/expo-dev-menu-interface-1.8.4.tgz", "integrity": "sha512-FpYI57EUu9qTSOOi+FZJ58xkCGJK7QD0mTiXK/y1I8lRdZGjCmdBqVvC4dAx2GcbIT78EPxaVf4/90tK/KRK6A==", + "license": "MIT", "peerDependencies": { "expo": "*" } }, "node_modules/expo-dev-menu/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -9345,6 +9965,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/expo-device/-/expo-device-6.0.2.tgz", "integrity": "sha512-sCt91CuTmAuMXX4SlFOn4lIos2UIr8vb0jDstDDZXys6kErcj0uynC7bQAMreU5uRUTKMAl4MAMpKt9ufCXPBw==", + "license": "MIT", "dependencies": { "ua-parser-js": "^0.7.33" }, @@ -9356,6 +9977,7 @@ "version": "17.0.1", "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-17.0.1.tgz", "integrity": "sha512-dYpnZJqTGj6HCYJyXAgpFkQWsiCH3HY1ek2cFZVHFoEc5tLz9gmdEgTF6nFHurvmvfmXqxi7a5CXyVm0aFYJBw==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9364,6 +9986,7 @@ "version": "12.0.10", "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-12.0.10.tgz", "integrity": "sha512-Q1i2NuYri3jy32zdnBaHHCya1wH1yMAsI+3CCmj9zlQzlhsS9Bdwcj2W3c5eU5FvH2hsNQy4O+O1NnM6o/pDaQ==", + "license": "MIT", "dependencies": { "fontfaceobserver": "^2.1.0" }, @@ -9375,6 +9998,7 @@ "version": "13.0.1", "resolved": "https://registry.npmjs.org/expo-haptics/-/expo-haptics-13.0.1.tgz", "integrity": "sha512-qG0EOLDE4bROVT3DtUSyV9g3iB3YFu9j3711X7SNNEnBDXc+2/p3wGDPTnJvPW0ao6HG3/McAOrBQA5hVSdWng==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9383,6 +10007,7 @@ "version": "4.7.0", "resolved": "https://registry.npmjs.org/expo-image-loader/-/expo-image-loader-4.7.0.tgz", "integrity": "sha512-cx+MxxsAMGl9AiWnQUzrkJMJH4eNOGlu7XkLGnAXSJrRoIiciGaKqzeaD326IyCTV+Z1fXvIliSgNW+DscvD8g==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9391,6 +10016,7 @@ "version": "15.0.7", "resolved": "https://registry.npmjs.org/expo-image-picker/-/expo-image-picker-15.0.7.tgz", "integrity": "sha512-u8qiPZNfDb+ap6PJ8pq2iTO7JKX+ikAUQ0K0c7gXGliKLxoXgDdDmXxz9/6QdICTshJBJlBvI0MwY5NWu7A/uw==", + "license": "MIT", "dependencies": { "expo-image-loader": "~4.7.0" }, @@ -9402,6 +10028,7 @@ "version": "11.0.1", "resolved": "https://registry.npmjs.org/expo-intent-launcher/-/expo-intent-launcher-11.0.1.tgz", "integrity": "sha512-nUmTTa/HG4jUyRc5YHngdpP5bMyGSRZPi2RX9kpILd3vbMWQeVnwzqAfC+uI34W8uKhEk+9b9Dytzmm7bBND1Q==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9409,12 +10036,14 @@ "node_modules/expo-json-utils": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/expo-json-utils/-/expo-json-utils-0.13.1.tgz", - "integrity": "sha512-mlfaSArGVb+oJmUcR22jEONlgPp0wj4iNIHfQ2je9Q8WTOqMc0Ws9tUciz3JdJnhffdHqo/k8fpvf0IRmN5HPA==" + "integrity": "sha512-mlfaSArGVb+oJmUcR22jEONlgPp0wj4iNIHfQ2je9Q8WTOqMc0Ws9tUciz3JdJnhffdHqo/k8fpvf0IRmN5HPA==", + "license": "MIT" }, "node_modules/expo-keep-awake": { "version": "13.0.2", "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-13.0.2.tgz", "integrity": "sha512-kKiwkVg/bY0AJ5q1Pxnm/GvpeB6hbNJhcFsoOWDh2NlpibhCLaHL826KHUM+WsnJRbVRxJ+K9vbPRHEMvFpVyw==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9423,6 +10052,7 @@ "version": "13.0.2", "resolved": "https://registry.npmjs.org/expo-linear-gradient/-/expo-linear-gradient-13.0.2.tgz", "integrity": "sha512-EDcILUjRKu4P1rtWcwciN6CSyGtH7Bq4ll3oTRV7h3h8oSzSilH1g6z7kTAMlacPBKvMnkkWOGzW6KtgMKEiTg==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9431,6 +10061,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-6.3.1.tgz", "integrity": "sha512-xuZCntSBGWCD/95iZ+mTUGTwHdy8Sx+immCqbUBxdvZ2TN61P02kKg7SaLS8A4a/hLrSCwrg5tMMwu5wfKr35g==", + "license": "MIT", "dependencies": { "expo-constants": "~16.0.0", "invariant": "^2.2.4" @@ -9440,6 +10071,7 @@ "version": "17.0.1", "resolved": "https://registry.npmjs.org/expo-location/-/expo-location-17.0.1.tgz", "integrity": "sha512-m+OzotzlAXO3ZZ1uqW5GC25nXW868zN+ROyBA1V4VF6jGay1ZEs4URPglCVUDzZby2F5wt24cMzqDKw2IX6nRw==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9448,6 +10080,7 @@ "version": "0.14.3", "resolved": "https://registry.npmjs.org/expo-manifests/-/expo-manifests-0.14.3.tgz", "integrity": "sha512-L3b5/qocBPiQjbW0cpOHfnqdKZbTJS7sA3mgeDJT+mWga/xYsdpma1EfNmsuvrOzjLGjStr1k1fceM9Bl49aqQ==", + "license": "MIT", "dependencies": { "@expo/config": "~9.0.0", "expo-json-utils": "~0.13.0" @@ -9460,6 +10093,7 @@ "version": "16.0.5", "resolved": "https://registry.npmjs.org/expo-media-library/-/expo-media-library-16.0.5.tgz", "integrity": "sha512-O9RUqBWgJVRF0mO6EiLSBFyfb5wR1/ZqovbT43V0TAo5sgcjrHRs+0NID/U6BWDRuiFeX2AU516JgNDutNUFSw==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9468,6 +10102,7 @@ "version": "1.11.3", "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-1.11.3.tgz", "integrity": "sha512-oYh8EZEvYF5TYppxEKUTTJmbr8j7eRRnrIxzZtMvxLTXoujThVPMFS/cbnSnf2bFm1lq50TdDNABhmEi7z0ngQ==", + "license": "MIT", "dependencies": { "chalk": "^4.1.0", "commander": "^7.2.0", @@ -9485,6 +10120,7 @@ "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -9499,6 +10135,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -9510,6 +10147,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -9518,6 +10156,7 @@ "version": "1.12.26", "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-1.12.26.tgz", "integrity": "sha512-y8yDWjOi+rQRdO+HY+LnUlz8qzHerUaw/LUjKPU/mX8PRXP4UUPEEp5fjAwBU44xjNmYSHWZDwet4IBBE+yQUA==", + "license": "MIT", "dependencies": { "invariant": "^2.2.4" } @@ -9526,6 +10165,7 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/expo-navigation-bar/-/expo-navigation-bar-3.0.7.tgz", "integrity": "sha512-KCNHyZ58zoN4xdy7D1lUdJvveCYNVQHGSX4M6xO/SZypvI6GZbLzKSN6Lx4GDGEFxG6Kb+EAckZl48tSiNeGYQ==", + "license": "MIT", "dependencies": { "@react-native/normalize-colors": "0.74.85", "debug": "^4.3.2" @@ -9538,6 +10178,7 @@ "version": "13.0.9", "resolved": "https://registry.npmjs.org/expo-sensors/-/expo-sensors-13.0.9.tgz", "integrity": "sha512-yi/TERUP8dpsJWWRgjT+UuQ7PI604PMndwXh9iBnfNO7q+dtoKZKsVSNJw61IKLNflbtCAyJxgmzPoe/JDT04Q==", + "license": "MIT", "dependencies": { "invariant": "^2.2.4" }, @@ -9549,95 +10190,34 @@ "version": "12.0.1", "resolved": "https://registry.npmjs.org/expo-sharing/-/expo-sharing-12.0.1.tgz", "integrity": "sha512-wBT+WeXwapj/9NWuLJO01vi9bdlchYu/Q/xD8slL/Ls4vVYku8CPqzkTtDFcjLrjtlJqyeHsdQXwKLvORmBIew==", + "license": "MIT", "peerDependencies": { "expo": "*" } }, "node_modules/expo-splash-screen": { - "version": "0.27.6", - "resolved": "https://registry.npmjs.org/expo-splash-screen/-/expo-splash-screen-0.27.6.tgz", - "integrity": "sha512-joUwZQS48k3VMnucQ0Y8Dle1t1FyIvluQA4kjuPx2x7l2dRrfctbo34ahTnC0p1o2go5oN2iEnSTOElY4wRQHw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/expo-splash-screen/-/expo-splash-screen-0.27.7.tgz", + "integrity": "sha512-s+eGcG185878nixlrjhhLD6UDYrvoqBUaBkIEozBVWFg3pkdsKpONPiUAco4XR3h7I/9ODq4quN28RJLFO+s0Q==", + "license": "MIT", "dependencies": { - "@expo/prebuild-config": "7.0.8" + "@expo/prebuild-config": "7.0.9" }, "peerDependencies": { "expo": "*" } }, - "node_modules/expo-splash-screen/node_modules/@expo/prebuild-config": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-7.0.8.tgz", - "integrity": "sha512-wH9NVg6HiwF5y9x0TxiMEeBF+ITPGDXy5/i6OUheSrKpPgb0lF1Mwzl/f2fLPXBEpl+ZXOQ8LlLW32b7K9lrNg==", - "dependencies": { - "@expo/config": "~9.0.0-beta.0", - "@expo/config-plugins": "~8.0.8", - "@expo/config-types": "^51.0.0-unreleased", - "@expo/image-utils": "^0.5.0", - "@expo/json-file": "^8.3.0", - "@react-native/normalize-colors": "0.74.85", - "debug": "^4.3.1", - "fs-extra": "^9.0.0", - "resolve-from": "^5.0.0", - "semver": "^7.6.0", - "xml2js": "0.6.0" - }, - "peerDependencies": { - "expo-modules-autolinking": ">=0.8.1" - } - }, - "node_modules/expo-splash-screen/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/expo-splash-screen/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/expo-splash-screen/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/expo-splash-screen/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/expo-status-bar": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/expo-status-bar/-/expo-status-bar-1.12.1.tgz", - "integrity": "sha512-/t3xdbS8KB0prj5KG5w7z+wZPFlPtkgs95BsmrP/E7Q0xHXTcDcQ6Cu2FkFuRM+PKTb17cJDnLkawyS5vDLxMA==" + "integrity": "sha512-/t3xdbS8KB0prj5KG5w7z+wZPFlPtkgs95BsmrP/E7Q0xHXTcDcQ6Cu2FkFuRM+PKTb17cJDnLkawyS5vDLxMA==", + "license": "MIT" }, "node_modules/expo-store-review": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/expo-store-review/-/expo-store-review-7.0.2.tgz", "integrity": "sha512-bXBXPv2KhfE9Ct14vof9Y1kAGlTFXmGdOLPToyg/eM6lMSALwyrLMkJ3Ba2XCI8PzKGKlx/+Yx5bImrZs4cUwg==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9646,6 +10226,7 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/expo-system-ui/-/expo-system-ui-3.0.7.tgz", "integrity": "sha512-KAs72F5JKhdIfPR9ZNVlRubTPK9uUuevPy5oYEp12xNEzSQcjZKvypH5NpwJuNWkXzrp3n3vZ+3pXsudA7J3KA==", + "license": "MIT", "dependencies": { "@react-native/normalize-colors": "0.74.85", "debug": "^4.3.2" @@ -9658,6 +10239,7 @@ "version": "11.8.2", "resolved": "https://registry.npmjs.org/expo-task-manager/-/expo-task-manager-11.8.2.tgz", "integrity": "sha512-Uhy3ol5gYeZOyeRFddYjLI1B2DGRH1gjp/YC8Hpn5p5MVENviySoKNF+wd98rRvOAokzrzElyDBHSTfX+C3tpg==", + "license": "MIT", "dependencies": { "unimodules-app-loader": "~4.6.0" }, @@ -9669,6 +10251,7 @@ "version": "0.16.2", "resolved": "https://registry.npmjs.org/expo-updates-interface/-/expo-updates-interface-0.16.2.tgz", "integrity": "sha512-929XBU70q5ELxkKADj1xL0UIm3HvhYhNAOZv5DSk7rrKvLo7QDdPyl+JVnwZm9LrkNbH4wuE2rLoKu1KMgZ+9A==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9677,19 +10260,22 @@ "version": "13.0.3", "resolved": "https://registry.npmjs.org/expo-web-browser/-/expo-web-browser-13.0.3.tgz", "integrity": "sha512-HXb7y82ApVJtqk8tManyudtTrCtx8xcUnVzmJECeHCB0SsWSQ+penVLZxJkcyATWoJOsFMnfVSVdrTcpKKGszQ==", + "license": "MIT", "peerDependencies": { "expo": "*" } }, "node_modules/exponential-backoff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", - "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz", + "integrity": "sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==", + "license": "Apache-2.0" }, "node_modules/ezly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ezly/-/ezly-1.3.0.tgz", "integrity": "sha512-kHW9+PDbAvv19kjbkXH2pUsOQ/mNVdMqTX6n3PA9yG2K7i5UHjusoN4mqARUD4oVXbDHBXcuRS/XCK7+lkBWMw==", + "license": "GPL-3.0-or-later", "dependencies": { "@literate.ink/utilities": "1.0.0-11713907881.1", "@noble/curves": "^1.6.0", @@ -9707,18 +10293,20 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -9728,6 +10316,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -9739,13 +10328,15 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-uri": { "version": "3.0.6", @@ -9760,33 +10351,32 @@ "type": "opencollective", "url": "https://opencollective.com/fastify" } - ] + ], + "license": "BSD-3-Clause" }, "node_modules/fast-xml-parser": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", - "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", + "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" - }, - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" } ], + "license": "MIT", "dependencies": { - "strnum": "^1.0.5" + "strnum": "^1.1.1" }, "bin": { "fxparser": "src/cli/cli.js" } }, "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "license": "ISC", "dependencies": { "reusify": "^1.0.4" } @@ -9795,6 +10385,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "license": "Apache-2.0", "dependencies": { "bser": "2.1.1" } @@ -9803,6 +10394,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/fbemitter/-/fbemitter-3.0.0.tgz", "integrity": "sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==", + "license": "BSD-3-Clause", "dependencies": { "fbjs": "^3.0.0" } @@ -9811,6 +10403,7 @@ "version": "3.0.5", "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz", "integrity": "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==", + "license": "MIT", "dependencies": { "cross-fetch": "^3.1.5", "fbjs-css-vars": "^1.0.0", @@ -9824,12 +10417,13 @@ "node_modules/fbjs-css-vars": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", - "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==" + "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==", + "license": "MIT" }, "node_modules/fbjs/node_modules/ua-parser-js": { - "version": "1.0.39", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.39.tgz", - "integrity": "sha512-k24RCVWlEcjkdOxYmVJgeD/0a1TiSpqLg+ZalVGV9lsnr4yqu0w7tX/x2xX6G4zpkgQnRf89lxuZ1wsbjXM8lw==", + "version": "1.0.40", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.40.tgz", + "integrity": "sha512-z6PJ8Lml+v3ichVojCiB8toQJBuwR42ySM4ezjXIqXK3M0HczmKQ3LF4rhU55PfD99KEEXQG6yb7iOMyvYuHew==", "funding": [ { "type": "opencollective", @@ -9844,6 +10438,7 @@ "url": "https://github.com/sponsors/faisalman" } ], + "license": "MIT", "bin": { "ua-parser-js": "script/cli.js" }, @@ -9852,12 +10447,13 @@ } }, "node_modules/fetch-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-3.0.1.tgz", - "integrity": "sha512-ZGXe8Y5Z/1FWqQ9q/CrJhkUD73DyBU9VF0hBQmEO/wPHe4A9PKTjplFDLeFX8aOsYypZUcX5Ji/eByn3VCVO3Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-3.1.0.tgz", + "integrity": "sha512-s/XhhreJpqH0ftkGVcQt8JE9bqk+zRn4jF5mPJXWZeQMCI5odV9K+wEWYbnzFPHgQZlvPSMjS4n4yawWE8RINw==", + "license": "Unlicense", "dependencies": { "set-cookie-parser": "^2.4.8", - "tough-cookie": "^4.0.0" + "tough-cookie": "^5.0.0" } }, "node_modules/fetch-retry": { @@ -9871,6 +10467,7 @@ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, + "license": "MIT", "dependencies": { "flat-cache": "^3.0.4" }, @@ -9882,6 +10479,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -9893,6 +10491,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -9901,6 +10500,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "license": "MIT", "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", @@ -9918,6 +10518,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -9925,12 +10526,14 @@ "node_modules/finalhandler/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/find-cache-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "license": "MIT", "dependencies": { "commondir": "^1.0.1", "make-dir": "^2.0.0", @@ -9944,6 +10547,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -9969,6 +10573,7 @@ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, + "license": "MIT", "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", @@ -9979,20 +10584,23 @@ } }, "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" }, "node_modules/flow-enums-runtime": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/flow-enums-runtime/-/flow-enums-runtime-0.0.6.tgz", - "integrity": "sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==" + "integrity": "sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==", + "license": "MIT" }, "node_modules/flow-parser": { - "version": "0.252.0", - "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.252.0.tgz", - "integrity": "sha512-z8hKPUjZ33VLn4HVntifqmEhmolUMopysnMNzazoDqo1GLUkBsreLNsxETlKJMPotUWStQnen6SGvUNe1j4Hlg==", + "version": "0.265.0", + "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.265.0.tgz", + "integrity": "sha512-C+bg/TZsDVlLMF14+q9P9FB2pjQSgWwYs0pkIMPE1FsZWS4A0kk1M28V6YphpxAPr3AISVRZ6VgpDepvCk6dGw==", + "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -10007,6 +10615,7 @@ "url": "https://github.com/sponsors/RubenVerborgh" } ], + "license": "MIT", "engines": { "node": ">=4.0" }, @@ -10019,23 +10628,31 @@ "node_modules/fontfaceobserver": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/fontfaceobserver/-/fontfaceobserver-2.3.0.tgz", - "integrity": "sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==" + "integrity": "sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==", + "license": "BSD-2-Clause" }, "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "license": "MIT", "dependencies": { - "is-callable": "^1.1.3" + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "license": "ISC", "dependencies": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { @@ -10058,12 +10675,14 @@ } }, "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" }, "engines": { @@ -10083,6 +10702,7 @@ "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -10091,6 +10711,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", @@ -10115,13 +10736,15 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -10134,19 +10757,23 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" }, "engines": { "node": ">= 0.4" @@ -10159,6 +10786,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -10167,6 +10795,7 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -10175,20 +10804,27 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -10206,6 +10842,19 @@ "node": ">=4" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -10219,13 +10868,14 @@ } }, "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -10238,6 +10888,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/getenv/-/getenv-1.0.0.tgz", "integrity": "sha512-7yetJWqbS9sbn0vIfliPsFgoXMKn/YMF+Wuiog97x+urnSRRRZ7xB+uVkwGKzRgq9CDFfMQnE9ruL5DHv9c6Xg==", + "license": "MIT", "engines": { "node": ">=6" } @@ -10247,6 +10898,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -10267,6 +10919,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -10278,6 +10931,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -10287,6 +10941,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -10298,6 +10953,7 @@ "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "license": "MIT", "engines": { "node": ">=4" } @@ -10306,6 +10962,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "license": "MIT", "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" @@ -10321,6 +10978,7 @@ "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "license": "MIT", "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", @@ -10337,11 +10995,12 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -10350,13 +11009,15 @@ "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/graphql": { "version": "15.8.0", @@ -10383,9 +11044,13 @@ } }, "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -10394,6 +11059,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -10402,6 +11068,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" }, @@ -10410,9 +11077,13 @@ } }, "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -10421,9 +11092,10 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -10435,6 +11107,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" }, @@ -10449,6 +11122,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -10460,6 +11134,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", + "license": "MIT", "dependencies": { "capital-case": "^1.0.4", "tslib": "^2.0.3" @@ -10468,12 +11143,14 @@ "node_modules/hermes-estree": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.19.1.tgz", - "integrity": "sha512-daLGV3Q2MKk8w4evNMKwS8zBE/rcpA800nu1Q5kM08IKijoSnPe9Uo1iIxzPKRkn95IxxsgBMPeYHt3VG4ej2g==" + "integrity": "sha512-daLGV3Q2MKk8w4evNMKwS8zBE/rcpA800nu1Q5kM08IKijoSnPe9Uo1iIxzPKRkn95IxxsgBMPeYHt3VG4ej2g==", + "license": "MIT" }, "node_modules/hermes-parser": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.19.1.tgz", "integrity": "sha512-Vp+bXzxYJWrpEuJ/vXxUsLnt0+y4q9zyi4zUlkLqD8FKv4LjIfOvP69R/9Lty3dCyKh0E2BU7Eypqr63/rKT/A==", + "license": "MIT", "dependencies": { "hermes-estree": "0.19.1" } @@ -10482,6 +11159,7 @@ "version": "0.0.6", "resolved": "https://registry.npmjs.org/hermes-profile-transformer/-/hermes-profile-transformer-0.0.6.tgz", "integrity": "sha512-cnN7bQUm65UWOy6cbGcCcZ3rpwW8Q/j4OP5aWRhEry4Z2t2aR1cjrbp0BS+KiBN0smvP1caBgAuxutvyvJILzQ==", + "license": "MIT", "dependencies": { "source-map": "^0.7.3" }, @@ -10493,6 +11171,7 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", "dependencies": { "react-is": "^16.7.0" } @@ -10528,12 +11207,13 @@ "license": "ISC" }, "node_modules/html-dom-parser": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/html-dom-parser/-/html-dom-parser-5.0.10.tgz", - "integrity": "sha512-GwArYL3V3V8yU/mLKoFF7HlLBv80BZ2Ey1BzfVNRpAci0cEKhFHI/Qh8o8oyt3qlAMLlK250wsxLdYX4viedvg==", + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/html-dom-parser/-/html-dom-parser-5.0.13.tgz", + "integrity": "sha512-B7JonBuAfG32I7fDouUQEogBrz3jK9gAuN1r1AaXpED6dIhtg/JwiSRhjGL7aOJwRz3HU4efowCjQBaoXiREqg==", + "license": "MIT", "dependencies": { "domhandler": "5.0.3", - "htmlparser2": "9.1.0" + "htmlparser2": "10.0.0" } }, "node_modules/html-entities": { @@ -10549,21 +11229,23 @@ "type": "patreon", "url": "https://patreon.com/mdevils" } - ] + ], + "license": "MIT" }, "node_modules/html-react-parser": { - "version": "5.1.18", - "resolved": "https://registry.npmjs.org/html-react-parser/-/html-react-parser-5.1.18.tgz", - "integrity": "sha512-65BwC0zzrdeW96jB2FRr5f1ovBhRMpLPJNvwkY5kA8Ay5xdL9t/RH2/uUTM7p+cl5iM88i6dDk4LXtfMnRmaJQ==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/html-react-parser/-/html-react-parser-5.2.2.tgz", + "integrity": "sha512-yA5012CJGSFWYZsgYzfr6HXJgDap38/AEP4ra8Cw+WHIi2ZRDXRX/QVYdumRf1P8zKyScKd6YOrWYvVEiPfGKg==", + "license": "MIT", "dependencies": { "domhandler": "5.0.3", - "html-dom-parser": "5.0.10", + "html-dom-parser": "5.0.13", "react-property": "2.0.2", "style-to-js": "1.1.16" }, "peerDependencies": { - "@types/react": "0.14 || 15 || 16 || 17 || 18", - "react": "0.14 || 15 || 16 || 17 || 18" + "@types/react": "0.14 || 15 || 16 || 17 || 18 || 19", + "react": "0.14 || 15 || 16 || 17 || 18 || 19" }, "peerDependenciesMeta": { "@types/react": { @@ -10575,6 +11257,7 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz", "integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==", + "license": "MIT", "dependencies": { "@selderee/plugin-htmlparser2": "^0.11.0", "deepmerge": "^4.3.1", @@ -10597,6 +11280,7 @@ "url": "https://github.com/sponsors/fb55" } ], + "license": "MIT", "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", @@ -10608,6 +11292,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "license": "MIT", "dependencies": { "css-line-break": "^2.1.0", "text-segmentation": "^1.0.3" @@ -10617,9 +11302,9 @@ } }, "node_modules/htmlparser2": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", - "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", + "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==", "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", { @@ -10627,11 +11312,12 @@ "url": "https://github.com/sponsors/fb55" } ], + "license": "MIT", "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", - "domutils": "^3.1.0", - "entities": "^4.5.0" + "domutils": "^3.2.1", + "entities": "^6.0.0" } }, "node_modules/htmlparser2-without-node-native": { @@ -10711,10 +11397,23 @@ "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", "license": "BSD-2-Clause" }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz", + "integrity": "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", @@ -10730,6 +11429,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -10737,7 +11437,8 @@ "node_modules/https": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz", - "integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg==" + "integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg==", + "license": "ISC" }, "node_modules/https-proxy-agent": { "version": "5.0.1", @@ -10756,6 +11457,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "license": "Apache-2.0", "engines": { "node": ">=10.17.0" } @@ -10763,7 +11465,8 @@ "node_modules/ical-date-parser": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/ical-date-parser/-/ical-date-parser-4.0.0.tgz", - "integrity": "sha512-XRCK/FU1akC2ZaJOdKIeZI6BLLgzWUuE0pegSrrkEva89GOan5mNkLVqCU4EMhCJ9nkG5TLWdMXrVX1fNAkFzw==" + "integrity": "sha512-XRCK/FU1akC2ZaJOdKIeZI6BLLgzWUuE0pegSrrkEva89GOan5mNkLVqCU4EMhCJ9nkG5TLWdMXrVX1fNAkFzw==", + "license": "MIT" }, "node_modules/ieee754": { "version": "1.2.1", @@ -10782,20 +11485,23 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "BSD-3-Clause" }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/image-size": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.1.1.tgz", - "integrity": "sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.0.tgz", + "integrity": "sha512-4S8fwbO6w3GeCVN6OPtA9I5IGKkcDMPcKndtUlpJuCwu7JLjtj7JZpwqLuyY2nrmQT3AWsCJLSKPsc2mPBSl3w==", + "license": "MIT", "dependencies": { "queue": "6.0.2" }, @@ -10807,10 +11513,11 @@ } }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, + "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -10827,6 +11534,7 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -10835,6 +11543,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", "engines": { "node": ">=0.8.19" } @@ -10853,6 +11562,7 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -10861,7 +11571,8 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" }, "node_modules/ini": { "version": "1.3.8", @@ -10872,7 +11583,8 @@ "node_modules/inline-style-parser": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", - "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==" + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", + "license": "MIT" }, "node_modules/internal-ip": { "version": "4.3.0", @@ -10887,23 +11599,15 @@ "node": ">=6" } }, - "node_modules/internal-ip/node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -10913,6 +11617,7 @@ "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", "dependencies": { "loose-envify": "^1.0.0" } @@ -10927,20 +11632,23 @@ } }, "node_modules/ipaddr.js": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", - "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", "engines": { - "node": ">= 10" + "node": ">= 0.10" } }, "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -10952,15 +11660,20 @@ "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" }, "node_modules/is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", - "dev": true, + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -10970,23 +11683,28 @@ } }, "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "license": "MIT", "dependencies": { - "has-bigints": "^1.0.1" + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -11005,6 +11723,7 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -11013,9 +11732,9 @@ } }, "node_modules/is-core-module": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.0.tgz", - "integrity": "sha512-urTSINYfAYgcbLb0yDQ6egFm6h3Mo1DcF9EkyXSRjjzdHbsulg01qhwWuXdOoUBuTkbQ80KDboXa0vFJ+BDH+g==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "license": "MIT", "dependencies": { "hasown": "^2.0.2" @@ -11028,10 +11747,13 @@ } }, "node_modules/is-data-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "license": "MIT", "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" }, "engines": { @@ -11042,11 +11764,13 @@ } }, "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -11059,6 +11783,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -11067,6 +11792,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", "bin": { "is-docker": "cli.js" }, @@ -11081,17 +11807,21 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-finalizationregistry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", - "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", - "dev": true, + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -11101,17 +11831,21 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dev": true, + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -11124,6 +11858,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -11135,6 +11870,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "license": "MIT", "engines": { "node": ">=8" } @@ -11176,18 +11912,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -11199,16 +11924,19 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", "engines": { "node": ">=0.12.0" } }, "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -11230,6 +11958,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -11238,6 +11967,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "license": "MIT", "engines": { "node": ">=8" } @@ -11246,6 +11976,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "license": "MIT", "dependencies": { "isobject": "^3.0.1" }, @@ -11254,12 +11985,15 @@ } }, "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -11272,7 +12006,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -11281,11 +12015,12 @@ } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -11304,11 +12039,13 @@ } }, "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -11318,11 +12055,14 @@ } }, "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "license": "MIT", "dependencies": { - "has-symbols": "^1.0.2" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -11332,11 +12072,12 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "license": "MIT", "dependencies": { - "which-typed-array": "^1.1.14" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -11349,6 +12090,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -11372,7 +12114,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -11381,24 +12123,28 @@ } }, "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-weakset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", - "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", - "dev": true, + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4" + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -11411,6 +12157,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", "dependencies": { "is-docker": "^2.0.0" }, @@ -11419,34 +12166,39 @@ } }, "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" }, "node_modules/isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/iterator.prototype": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.3.tgz", - "integrity": "sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", "dev": true, + "license": "MIT", "dependencies": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -11471,6 +12223,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "license": "MIT", "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", @@ -11487,6 +12240,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -11503,6 +12257,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -11511,6 +12266,7 @@ "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -11519,6 +12275,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -11527,6 +12284,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", @@ -11546,6 +12304,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -11562,6 +12321,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -11570,6 +12330,7 @@ "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -11578,6 +12339,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -11589,6 +12351,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -11601,12 +12364,14 @@ "node_modules/jest-message-util/node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" }, "node_modules/jest-mock": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", @@ -11620,6 +12385,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -11636,6 +12402,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -11644,6 +12411,7 @@ "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -11652,6 +12420,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", @@ -11668,6 +12437,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -11684,6 +12454,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -11692,6 +12463,7 @@ "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -11700,6 +12472,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -11711,6 +12484,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", "camelcase": "^6.2.0", @@ -11727,6 +12501,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -11743,6 +12518,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -11751,6 +12527,7 @@ "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -11759,6 +12536,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -11770,6 +12548,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -11782,12 +12561,14 @@ "node_modules/jest-validate/node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" }, "node_modules/jest-worker": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "license": "MIT", "dependencies": { "@types/node": "*", "jest-util": "^29.7.0", @@ -11802,6 +12583,7 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -11815,12 +12597,14 @@ "node_modules/jimp-compact": { "version": "0.16.1", "resolved": "https://registry.npmjs.org/jimp-compact/-/jimp-compact-0.16.1.tgz", - "integrity": "sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==" + "integrity": "sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==", + "license": "MIT" }, "node_modules/joi": { "version": "17.13.3", "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "license": "BSD-3-Clause", "dependencies": { "@hapi/hoek": "^9.3.0", "@hapi/topo": "^5.1.0", @@ -11839,6 +12623,7 @@ "version": "4.15.9", "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" } @@ -11846,17 +12631,20 @@ "node_modules/js-base64": { "version": "3.7.7", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.7.tgz", - "integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==" + "integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==", + "license": "BSD-3-Clause" }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -11867,22 +12655,26 @@ "node_modules/jsbarcode": { "version": "3.11.6", "resolved": "https://registry.npmjs.org/jsbarcode/-/jsbarcode-3.11.6.tgz", - "integrity": "sha512-G5TKGyKY1zJo0ZQKFM1IIMfy0nF2rs92BLlCz+cU4/TazIc4ZH+X1GYeDRt7TKjrYqmPfTjwTBkU/QnQlsYiuA==" + "integrity": "sha512-G5TKGyKY1zJo0ZQKFM1IIMfy0nF2rs92BLlCz+cU4/TazIc4ZH+X1GYeDRt7TKjrYqmPfTjwTBkU/QnQlsYiuA==", + "license": "MIT" }, "node_modules/jsc-android": { "version": "250231.0.0", "resolved": "https://registry.npmjs.org/jsc-android/-/jsc-android-250231.0.0.tgz", - "integrity": "sha512-rS46PvsjYmdmuz1OAWXY/1kCYG7pnf1TBqeTiOJr1iDz7s5DLxxC9n/ZMknLDxzYzNVfI7R95MH10emSSG1Wuw==" + "integrity": "sha512-rS46PvsjYmdmuz1OAWXY/1kCYG7pnf1TBqeTiOJr1iDz7s5DLxxC9n/ZMknLDxzYzNVfI7R95MH10emSSG1Wuw==", + "license": "BSD-2-Clause" }, "node_modules/jsc-safe-url": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/jsc-safe-url/-/jsc-safe-url-0.2.4.tgz", - "integrity": "sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==" + "integrity": "sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==", + "license": "0BSD" }, "node_modules/jscodeshift": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.14.0.tgz", "integrity": "sha512-7eCC1knD7bLUPuSCwXsMZUH51O8jIcoVyKtI6P0XM0IVzlGjckPy3FIwQlorzbN0Sg79oK+RlohN32Mqf/lrYA==", + "license": "MIT", "dependencies": { "@babel/core": "^7.13.16", "@babel/parser": "^7.13.16", @@ -11912,9 +12704,10 @@ } }, "node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, @@ -11926,18 +12719,21 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "license": "MIT" }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-schema-deref-sync": { "version": "0.13.0", @@ -11973,18 +12769,21 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -11996,6 +12795,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/jsonapi-fractal/-/jsonapi-fractal-2.3.1.tgz", "integrity": "sha512-GYL67SfDhtvUWpy5uI4v84VheQxsWeR6Qo4V/uSDBquW1zTDi99Q8tCHs/iMMPHW9tlO3bxvXx/KV+eAWUnUbg==", + "license": "MIT", "dependencies": { "change-case": "^4.1.1" } @@ -12004,6 +12804,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "license": "MIT", "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -12013,6 +12814,7 @@ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "dev": true, + "license": "MIT", "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", @@ -12028,6 +12830,7 @@ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, + "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } @@ -12036,6 +12839,7 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -12044,6 +12848,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", "engines": { "node": ">=6" } @@ -12052,6 +12857,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz", "integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==", + "license": "MIT", "funding": { "url": "https://ko-fi.com/killymxi" } @@ -12060,6 +12866,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "license": "MIT", "engines": { "node": ">=6" } @@ -12069,6 +12876,7 @@ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -12081,6 +12889,7 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz", "integrity": "sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==", + "license": "Apache-2.0", "dependencies": { "debug": "^2.6.9", "marky": "^1.2.2" @@ -12090,6 +12899,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -12097,7 +12907,8 @@ "node_modules/lighthouse-logger/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/lightningcss": { "version": "1.19.0", @@ -12288,12 +13099,14 @@ "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -12307,28 +13120,33 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" }, "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead." + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "license": "MIT" }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "license": "MIT" }, "node_modules/lodash.throttle": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==" + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "license": "MIT" }, "node_modules/log-symbols": { "version": "2.2.0", @@ -12417,6 +13235,7 @@ "version": "0.7.1", "resolved": "https://registry.npmjs.org/logkitty/-/logkitty-0.7.1.tgz", "integrity": "sha512-/3ER20CTTbahrCrpYfPn7Xavv9diBROZpoXGVZDWMw4b/X4uuUwAC0ki85tgsdMRONURyIJbcOvS94QsUBYPbQ==", + "license": "MIT", "dependencies": { "ansi-fragments": "^0.2.1", "dayjs": "^1.8.15", @@ -12430,6 +13249,7 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", "engines": { "node": ">=6" } @@ -12438,6 +13258,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -12448,6 +13269,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -12460,6 +13282,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -12471,6 +13294,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -12485,6 +13309,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -12496,6 +13321,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -12508,12 +13334,14 @@ "node_modules/logkitty/node_modules/y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" }, "node_modules/logkitty/node_modules/yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", @@ -12535,6 +13363,7 @@ "version": "18.1.3", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" @@ -12547,6 +13376,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -12558,6 +13388,7 @@ "version": "6.7.2", "resolved": "https://registry.npmjs.org/lottie-react-native/-/lottie-react-native-6.7.2.tgz", "integrity": "sha512-MZVx6N1EeO/EaSx8T44mJ0aHc5Mqee+xIfWwszni0oz8U2wlHdaWGjES44dHxaxgAp/0dRaFt3PkpZ6egTzcBg==", + "license": "Apache-2.0", "peerDependencies": { "@dotlottie/react-player": "^1.6.1", "@lottiefiles/react-lottie-player": "^3.5.3", @@ -12581,6 +13412,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "license": "MIT", "dependencies": { "tslib": "^2.0.3" } @@ -12589,6 +13421,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "license": "ISC", "dependencies": { "yallist": "^3.0.2" } @@ -12597,6 +13430,7 @@ "version": "0.378.0", "resolved": "https://registry.npmjs.org/lucide-react-native/-/lucide-react-native-0.378.0.tgz", "integrity": "sha512-Xvqxjc3N5040Ui6tZaSbpNnNjWXDa+nRzYct4rXd2mWX+g2qxKPpEHoqNumrpky9rhsIxD8w4BSbjdkpGQTMYw==", + "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0", "react-native": "*", @@ -12607,6 +13441,7 @@ "version": "3.5.0", "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", + "license": "MIT", "engines": { "node": ">=12" } @@ -12615,6 +13450,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "license": "MIT", "dependencies": { "pify": "^4.0.1", "semver": "^5.6.0" @@ -12627,6 +13463,7 @@ "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", "bin": { "semver": "bin/semver" } @@ -12635,6 +13472,7 @@ "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "license": "BSD-3-Clause", "dependencies": { "tmpl": "1.0.5" } @@ -12642,7 +13480,17 @@ "node_modules/marky": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/marky/-/marky-1.2.5.tgz", - "integrity": "sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q==" + "integrity": "sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q==", + "license": "Apache-2.0" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } }, "node_modules/md5": { "version": "2.3.0", @@ -12659,6 +13507,7 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/md5-file/-/md5-file-3.2.3.tgz", "integrity": "sha512-3Tkp1piAHaworfcCgH0jKbTvj1jWWFgbvh2cXaNCgHwyTCBxxvD1Y04rmfpvdPm1P4oXMOpm6+2H7sr7v9v8Fw==", + "license": "MIT", "dependencies": { "buffer-alloc": "^1.1.0" }, @@ -12678,12 +13527,14 @@ "node_modules/mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "license": "CC0-1.0" }, "node_modules/memoize-one": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", - "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", + "license": "MIT" }, "node_modules/memory-cache": { "version": "0.2.0", @@ -12695,6 +13546,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz", "integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==", + "license": "MIT", "dependencies": { "is-plain-obj": "^2.1.0" }, @@ -12705,12 +13557,14 @@ "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", "engines": { "node": ">= 8" } @@ -12719,6 +13573,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro/-/metro-0.80.12.tgz", "integrity": "sha512-1UsH5FzJd9quUsD1qY+zUG4JY3jo3YEMxbMYH9jT6NK3j4iORhlwTK8fYTfAUBhDKjgLfKjAh7aoazNE23oIRA==", + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", "@babel/core": "^7.20.0", @@ -12774,6 +13629,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.80.12.tgz", "integrity": "sha512-YZziRs0MgA3pzCkkvOoQRXjIoVjvrpi/yRlJnObyIvMP6lFdtyG4nUGIwGY9VXnBvxmXD6mPY2e+NSw6JAyiRg==", + "license": "MIT", "dependencies": { "@babel/core": "^7.20.0", "flow-enums-runtime": "^0.0.6", @@ -12787,12 +13643,14 @@ "node_modules/metro-babel-transformer/node_modules/hermes-estree": { "version": "0.23.1", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.23.1.tgz", - "integrity": "sha512-eT5MU3f5aVhTqsfIReZ6n41X5sYn4IdQL0nvz6yO+MMlPxw49aSARHLg/MSehQftyjnrE8X6bYregzSumqc6cg==" + "integrity": "sha512-eT5MU3f5aVhTqsfIReZ6n41X5sYn4IdQL0nvz6yO+MMlPxw49aSARHLg/MSehQftyjnrE8X6bYregzSumqc6cg==", + "license": "MIT" }, "node_modules/metro-babel-transformer/node_modules/hermes-parser": { "version": "0.23.1", "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.23.1.tgz", "integrity": "sha512-oxl5h2DkFW83hT4DAUJorpah8ou4yvmweUzLJmmr6YV2cezduCdlil1AvU/a/xSsAFo4WUcNA4GoV5Bvq6JffA==", + "license": "MIT", "dependencies": { "hermes-estree": "0.23.1" } @@ -12801,6 +13659,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.80.12.tgz", "integrity": "sha512-p5kNHh2KJ0pbQI/H7ZBPCEwkyNcSz7OUkslzsiIWBMPQGFJ/xArMwkV7I+GJcWh+b4m6zbLxE5fk6fqbVK1xGA==", + "license": "MIT", "dependencies": { "exponential-backoff": "^3.1.1", "flow-enums-runtime": "^0.0.6", @@ -12814,6 +13673,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.80.12.tgz", "integrity": "sha512-o4BspKnugg/pE45ei0LGHVuBJXwRgruW7oSFAeSZvBKA/sGr0UhOGY3uycOgWInnS3v5yTTfiBA9lHlNRhsvGA==", + "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6" }, @@ -12825,6 +13685,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.80.12.tgz", "integrity": "sha512-4rwOWwrhm62LjB12ytiuR5NgK1ZBNr24/He8mqCsC+HXZ+ATbrewLNztzbAZHtFsrxP4D4GLTGgh96pCpYLSAQ==", + "license": "MIT", "dependencies": { "connect": "^3.6.5", "cosmiconfig": "^5.0.5", @@ -12843,6 +13704,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.80.12.tgz", "integrity": "sha512-QqdJ/yAK+IpPs2HU/h5v2pKEdANBagSsc6DRSjnwSyJsCoHlmyJKCaCJ7KhWGx+N4OHxh37hoA8fc2CuZbx0Fw==", + "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6", "lodash.throttle": "^4.1.1", @@ -12856,6 +13718,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.80.12.tgz", "integrity": "sha512-sYdemWSlk66bWzW2wp79kcPMzwuG32x1ZF3otI0QZTmrnTaaTiGyhE66P1z6KR4n2Eu5QXiABa6EWbAQv0r8bw==", + "license": "MIT", "dependencies": { "anymatch": "^3.0.3", "debug": "^2.2.0", @@ -12880,6 +13743,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -12887,12 +13751,14 @@ "node_modules/metro-file-map/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/metro-minify-terser": { "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.80.12.tgz", "integrity": "sha512-muWzUw3y5k+9083ZoX9VaJLWEV2Jcgi+Oan0Mmb/fBNMPqP9xVDuy4pOMn/HOiGndgfh/MK7s4bsjkyLJKMnXQ==", + "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6", "terser": "^5.15.0" @@ -12905,6 +13771,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.80.12.tgz", "integrity": "sha512-PR24gYRZnYHM3xT9pg6BdbrGbM/Cu1TcyIFBVlAk7qDAuHkUNQ1nMzWumWs+kwSvtd9eZGzHoucGJpTUEeLZAw==", + "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6" }, @@ -12916,6 +13783,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.80.12.tgz", "integrity": "sha512-LIx7+92p5rpI0i6iB4S4GBvvLxStNt6fF0oPMaUd1Weku7jZdfkCZzmrtDD9CSQ6EPb0T9NUZoyXIxlBa3wOCw==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.0", "flow-enums-runtime": "^0.0.6" @@ -12928,6 +13796,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.80.12.tgz", "integrity": "sha512-o+AXmE7hpvM8r8MKsx7TI21/eerYYy2DCDkWfoBkv+jNkl61khvDHlQn0cXZa6lrcNZiZkl9oHSMcwLLIrFmpw==", + "license": "MIT", "dependencies": { "@babel/traverse": "^7.20.0", "@babel/types": "^7.20.0", @@ -12947,6 +13816,7 @@ "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -12955,6 +13825,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.80.12.tgz", "integrity": "sha512-/dIpNdHksXkGHZXARZpL7doUzHqSNxgQ8+kQGxwpJuHnDhGkENxB5PS2QBaTDdEcmyTMjS53CN1rl9n1gR6fmw==", + "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", @@ -12975,6 +13846,7 @@ "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -12983,6 +13855,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.80.12.tgz", "integrity": "sha512-WQWp00AcZvXuQdbjQbx1LzFR31IInlkCDYJNRs6gtEtAyhwpMMlL2KcHmdY+wjDO9RPcliZ+Xl1riOuBecVlPA==", + "license": "MIT", "dependencies": { "@babel/core": "^7.20.0", "@babel/generator": "^7.20.0", @@ -12999,6 +13872,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.80.12.tgz", "integrity": "sha512-KAPFN1y3eVqEbKLx1I8WOarHPqDMUa8WelWxaJCNKO/yHCP26zELeqTJvhsQup+8uwB6EYi/sp0b6TGoh6lOEA==", + "license": "MIT", "dependencies": { "@babel/core": "^7.20.0", "@babel/generator": "^7.20.0", @@ -13021,12 +13895,14 @@ "node_modules/metro/node_modules/ci-info": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "license": "MIT" }, "node_modules/metro/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -13034,12 +13910,14 @@ "node_modules/metro/node_modules/hermes-estree": { "version": "0.23.1", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.23.1.tgz", - "integrity": "sha512-eT5MU3f5aVhTqsfIReZ6n41X5sYn4IdQL0nvz6yO+MMlPxw49aSARHLg/MSehQftyjnrE8X6bYregzSumqc6cg==" + "integrity": "sha512-eT5MU3f5aVhTqsfIReZ6n41X5sYn4IdQL0nvz6yO+MMlPxw49aSARHLg/MSehQftyjnrE8X6bYregzSumqc6cg==", + "license": "MIT" }, "node_modules/metro/node_modules/hermes-parser": { "version": "0.23.1", "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.23.1.tgz", "integrity": "sha512-oxl5h2DkFW83hT4DAUJorpah8ou4yvmweUzLJmmr6YV2cezduCdlil1AvU/a/xSsAFo4WUcNA4GoV5Bvq6JffA==", + "license": "MIT", "dependencies": { "hermes-estree": "0.23.1" } @@ -13047,12 +13925,14 @@ "node_modules/metro/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/metro/node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -13061,6 +13941,7 @@ "version": "7.5.10", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", "engines": { "node": ">=8.3.0" }, @@ -13081,6 +13962,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -13093,6 +13975,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -13104,6 +13987,7 @@ "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -13115,6 +13999,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -13123,6 +14008,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -13143,6 +14029,7 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -13157,6 +14044,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -13277,6 +14165,7 @@ "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", "dependencies": { "minimist": "^1.2.6" }, @@ -13287,12 +14176,14 @@ "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "license": "MIT", "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", @@ -13300,9 +14191,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.9.tgz", + "integrity": "sha512-SppoicMGpZvbF1l3z4x7No3OlIjP7QJvC9XR7AhZr1kL133KHnKPztkKDc+Ir4aJ/1VhTySrtKhrsycmrMQfvg==", "funding": [ { "type": "github", @@ -13321,12 +14212,14 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -13334,7 +14227,8 @@ "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "license": "MIT" }, "node_modules/nested-error-stacks": { "version": "2.0.1", @@ -13352,6 +14246,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "license": "MIT", "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" @@ -13361,6 +14256,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/nocache/-/nocache-3.0.4.tgz", "integrity": "sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw==", + "license": "MIT", "engines": { "node": ">=12.0.0" } @@ -13368,12 +14264,14 @@ "node_modules/node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", - "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", + "license": "MIT" }, "node_modules/node-dir": { "version": "0.1.17", "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz", "integrity": "sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==", + "license": "MIT", "dependencies": { "minimatch": "^3.0.2" }, @@ -13385,6 +14283,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -13394,6 +14293,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -13405,6 +14305,7 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -13424,6 +14325,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { "node": ">= 6.13.0" } @@ -13431,17 +14333,20 @@ "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==" + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "license": "MIT" }, "node_modules/node-stream-zip": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.15.0.tgz", "integrity": "sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==", + "license": "MIT", "engines": { "node": ">=0.12.0" }, @@ -13454,6 +14359,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -13462,7 +14368,8 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/notifee/-/notifee-0.0.1.tgz", "integrity": "sha512-R5DEyfQnhBR717u66Zut+Jx5MAOS4iLNSVEdsbiVcSzPH9S1gjxHEUJNnLnUmRTbvz8L8sUzhgkNMrtgm55UHg==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info." + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "UNLICENSED" }, "node_modules/npm-package-arg": { "version": "7.0.0", @@ -13510,6 +14417,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0" }, @@ -13520,12 +14428,14 @@ "node_modules/nullthrows": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", - "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==" + "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", + "license": "MIT" }, "node_modules/ob1": { "version": "0.80.12", "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.80.12.tgz", "integrity": "sha512-VMArClVT6LkhUGpnuEoBuyjG9rzUyEzg4PDkav6wK1cLhOK02gPCYFxoiB4mqVnrMhDpIzJcrGNAMVi9P+hXrw==", + "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6" }, @@ -13537,6 +14447,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -13545,14 +14456,16 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "license": "MIT", "engines": { "node": ">= 6" } }, "node_modules/object-inspect": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", - "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -13564,18 +14477,22 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", "object-keys": "^1.1.1" }, "engines": { @@ -13590,6 +14507,7 @@ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -13604,6 +14522,7 @@ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -13618,12 +14537,14 @@ } }, "node_modules/object.values": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", - "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" }, @@ -13635,9 +14556,10 @@ } }, "node_modules/oidc-token-hash": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz", - "integrity": "sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.1.0.tgz", + "integrity": "sha512-y0W+X7Ppo7oZX6eovsRkuzcSM40Bicg2JEJkDJ4irIt1wsYAP5MLSNv+QAogO8xivMffw/9OvV3um1pxXgt1uA==", + "license": "MIT", "engines": { "node": "^10.13.0 || >=12.0.0" } @@ -13646,6 +14568,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -13657,6 +14580,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -13665,6 +14589,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", "dependencies": { "wrappy": "1" } @@ -13699,9 +14624,10 @@ } }, "node_modules/openid-client": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.0.tgz", - "integrity": "sha512-4GCCGZt1i2kTHpwvaC/sCpTpQqDnBzDzuJcJMbH+y1Q5qI8U8RBvoSh28svarXszZHR5BAMXbJPX1PGPRE3VOA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz", + "integrity": "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==", + "license": "MIT", "dependencies": { "jose": "^4.15.9", "lru-cache": "^6.0.0", @@ -13716,6 +14642,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -13726,13 +14653,15 @@ "node_modules/openid-client/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, + "license": "MIT", "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -13883,6 +14812,23 @@ "os-tmpdir": "^1.0.0" } }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -13896,6 +14842,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -13910,6 +14857,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -13939,6 +14887,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", "engines": { "node": ">=6" } @@ -13952,12 +14901,14 @@ "node_modules/pako": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", - "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "license": "(MIT AND Zlib)" }, "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "license": "MIT", "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -13968,6 +14919,7 @@ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -13979,6 +14931,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "license": "MIT", "dependencies": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" @@ -13991,6 +14944,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/parse-png/-/parse-png-2.1.0.tgz", "integrity": "sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ==", + "license": "MIT", "dependencies": { "pngjs": "^3.3.0" }, @@ -14002,6 +14956,7 @@ "version": "0.12.1", "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz", "integrity": "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==", + "license": "MIT", "dependencies": { "leac": "^0.6.0", "peberminta": "^0.9.0" @@ -14014,6 +14969,7 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -14022,6 +14978,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "license": "MIT", "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -14041,6 +14998,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", + "license": "MIT", "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -14050,12 +15008,14 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", "engines": { "node": ">=8" } @@ -14064,6 +15024,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -14072,6 +15033,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", "engines": { "node": ">=8" } @@ -14079,7 +15041,8 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" }, "node_modules/path-scurry": { "version": "1.11.1", @@ -14107,14 +15070,15 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/pawdirecte": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/pawdirecte/-/pawdirecte-1.7.1.tgz", - "integrity": "sha512-2gyR8BIl78ktLYDp1FHSkbBG93RTyaX/JgfU02w6xCvd+VxUkovcllZ7lJsg7tVMwn2EYJ4jbTdKuneTOgTRUA==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/pawdirecte/-/pawdirecte-1.8.0.tgz", + "integrity": "sha512-DXohBS59vWyZS+MWwaV9dPFTVJU3Qo2LBrD9OcnOMvrSeHvwIhp32xT8xVjAa8EWV6iITMaopsTv81iCFO+z5g==", "license": "GPL-3.0-or-later", "dependencies": { "@literate.ink/utilities": "1.0.0-11713907881.1", @@ -14128,6 +15092,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/pawnilim/-/pawnilim-0.2.0.tgz", "integrity": "sha512-RnI3erDGpkXjBNNPnQDWxRadkOkzbZZZ1vCx8KFm276nHp2BjhGUSY7617Ea3nYQvVBPT0LtV4cVfrkiGFhSJA==", + "license": "GPL-3.0", "engines": { "node": ">=18" } @@ -14136,6 +15101,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/pawnote/-/pawnote-1.4.1.tgz", "integrity": "sha512-jdFHfyZ7tIRJ03etegs/ExgW2F2KZDPbOq6atjs6Fi1DINl893QLfVIxLwADb+tGQbYifYwwOurv4QZQ1OJZ8Q==", + "license": "GPL-3.0-or-later", "dependencies": { "@literate.ink/utilities": "1.0.0-10641118381.1", "html-entities": "^2.5.2", @@ -14150,6 +15116,7 @@ "version": "1.0.0-10641118381.1", "resolved": "https://registry.npmjs.org/@literate.ink/utilities/-/utilities-1.0.0-10641118381.1.tgz", "integrity": "sha512-omhzgwfAjNXqIjt6dmWbU8pm6QfEvNYetykEXYk+vVaJ8oICglvkChxkC6FkF8zJKMM/lFrNHgD42obQ8lwolQ==", + "license": "MIT", "dependencies": { "set-cookie-parser": "^2.7.0" } @@ -14158,6 +15125,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/pawrd/-/pawrd-0.6.1.tgz", "integrity": "sha512-9/JwVtkwcRkS7Lwn79z98sVzP6dNPaIPnDbMBdtzdj7qMBbNKHuGoUXwsdHkdN57kpq1k7n8zSoUCtN0wxZyYA==", + "license": "GPL-3.0-or-later", "dependencies": { "@literate.ink/utilities": "1.0.0-10641118381.1", "node-forge": "^1.3.1" @@ -14170,6 +15138,7 @@ "version": "1.0.0-10641118381.1", "resolved": "https://registry.npmjs.org/@literate.ink/utilities/-/utilities-1.0.0-10641118381.1.tgz", "integrity": "sha512-omhzgwfAjNXqIjt6dmWbU8pm6QfEvNYetykEXYk+vVaJ8oICglvkChxkC6FkF8zJKMM/lFrNHgD42obQ8lwolQ==", + "license": "MIT", "dependencies": { "set-cookie-parser": "^2.7.0" } @@ -14178,6 +15147,7 @@ "version": "0.9.0", "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz", "integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==", + "license": "MIT", "funding": { "url": "https://ko-fi.com/killymxi" } @@ -14185,13 +15155,15 @@ "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -14203,6 +15175,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "license": "MIT", "engines": { "node": ">=6" } @@ -14211,6 +15184,7 @@ "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "license": "MIT", "engines": { "node": ">= 6" } @@ -14219,6 +15193,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "license": "MIT", "dependencies": { "find-up": "^3.0.0" }, @@ -14230,6 +15205,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "license": "MIT", "dependencies": { "locate-path": "^3.0.0" }, @@ -14241,6 +15217,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "license": "MIT", "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" @@ -14253,6 +15230,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -14267,6 +15245,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "license": "MIT", "dependencies": { "p-limit": "^2.0.0" }, @@ -14278,6 +15257,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "license": "MIT", "engines": { "node": ">=4" } @@ -14286,6 +15266,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", + "license": "MIT", "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", @@ -14299,6 +15280,7 @@ "version": "0.8.10", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "license": "MIT", "engines": { "node": ">=10.0.0" } @@ -14307,6 +15289,7 @@ "version": "15.1.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "license": "MIT", "engines": { "node": ">=8.0" } @@ -14315,14 +15298,16 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", + "license": "MIT", "engines": { "node": ">=4.0.0" } }, "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -14360,6 +15345,7 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8.0" } @@ -14380,6 +15366,7 @@ "version": "24.9.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "license": "MIT", "dependencies": { "@jest/types": "^24.9.0", "ansi-regex": "^4.0.0", @@ -14394,6 +15381,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "license": "MIT", "engines": { "node": ">=6" } @@ -14402,6 +15390,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -14413,6 +15402,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", "dependencies": { "color-name": "1.1.3" } @@ -14420,7 +15410,8 @@ "node_modules/pretty-format/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" }, "node_modules/proc-log": { "version": "4.2.0", @@ -14434,7 +15425,8 @@ "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" }, "node_modules/progress": { "version": "2.0.3", @@ -14449,6 +15441,7 @@ "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "license": "MIT", "dependencies": { "asap": "~2.0.3" } @@ -14457,6 +15450,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "license": "MIT", "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" @@ -14469,6 +15463,7 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -14478,15 +15473,8 @@ "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "node_modules/psl": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.10.0.tgz", - "integrity": "sha512-KSKHEbjAnpUuAUserOq0FxGXCUrzC3WniuSJhvdbs102rL55266ZcHBqLWOsG30spQMlPdpy7icATiAQehg/iA==", - "dependencies": { - "punycode": "^2.3.1" - } + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" }, "node_modules/pump": { "version": "3.0.2", @@ -14502,22 +15490,25 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/pvtsutils": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.5.tgz", - "integrity": "sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", + "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", + "license": "MIT", "dependencies": { - "tslib": "^2.6.1" + "tslib": "^2.8.1" } }, "node_modules/pvutils": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.3.tgz", "integrity": "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==", + "license": "MIT", "engines": { "node": ">=6.0.0" } @@ -14526,6 +15517,7 @@ "version": "1.5.4", "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "license": "MIT", "dependencies": { "dijkstrajs": "^1.0.1", "pngjs": "^5.0.0", @@ -14550,6 +15542,7 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", "engines": { "node": ">=6" } @@ -14558,6 +15551,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -14568,6 +15562,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -14580,6 +15575,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -14591,6 +15587,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -14605,6 +15602,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -14616,6 +15614,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "license": "MIT", "engines": { "node": ">=10.13.0" } @@ -14624,6 +15623,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -14636,12 +15636,14 @@ "node_modules/qrcode/node_modules/y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" }, "node_modules/qrcode/node_modules/yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", @@ -14663,6 +15665,7 @@ "version": "18.1.3", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" @@ -14675,6 +15678,7 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", + "license": "MIT", "dependencies": { "decode-uri-component": "^0.2.2", "filter-obj": "^1.1.0", @@ -14693,19 +15697,16 @@ "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz", "integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==", "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "license": "MIT", "engines": { "node": ">=0.4.x" } }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" - }, "node_modules/queue": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "license": "MIT", "dependencies": { "inherits": "~2.0.3" } @@ -14727,12 +15728,14 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -14765,6 +15768,7 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" }, @@ -14776,6 +15780,7 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-5.3.2.tgz", "integrity": "sha512-crr9HkVrDiJ0A4zot89oS0Cgv0Oa4OG1Em4jit3P3ZxZSKPMYyMjfwMqgcJna9o625g8oN87rBm8SWWrSTBZxg==", + "license": "MIT", "dependencies": { "shell-quote": "^1.6.1", "ws": "^7" @@ -14785,6 +15790,7 @@ "version": "7.5.10", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", "engines": { "node": ">=8.3.0" }, @@ -14805,6 +15811,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.4.tgz", "integrity": "sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -14815,24 +15822,26 @@ "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" }, "node_modules/react-native": { - "version": "0.74.6", - "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.74.6.tgz", - "integrity": "sha512-TZ8uLf+dH+nO5nFwjhMd4PqtraeNT5cXQ0ySAhq7qqbTBgalxO3UklsLFW3cTSedC+eLw6J3P3H62e3/MjpWNw==", + "version": "0.74.7", + "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.74.7.tgz", + "integrity": "sha512-7emqS5CcwFoIBNAtcFPhmFRKkDl6TI/Oe10QjAYEj0JJcN/7hyCGwnDTtpjnO4Ai5LRt8xKXhrUt8cKIQ5BQlQ==", + "license": "MIT", "dependencies": { "@jest/create-cache-key-function": "^29.6.3", "@react-native-community/cli": "13.6.9", "@react-native-community/cli-platform-android": "13.6.9", "@react-native-community/cli-platform-ios": "13.6.9", - "@react-native/assets-registry": "0.74.88", - "@react-native/codegen": "0.74.88", - "@react-native/community-cli-plugin": "0.74.88", - "@react-native/gradle-plugin": "0.74.88", - "@react-native/js-polyfills": "0.74.88", - "@react-native/normalize-colors": "0.74.88", - "@react-native/virtualized-lists": "0.74.88", + "@react-native/assets-registry": "0.74.89", + "@react-native/codegen": "0.74.89", + "@react-native/community-cli-plugin": "0.74.89", + "@react-native/gradle-plugin": "0.74.89", + "@react-native/js-polyfills": "0.74.89", + "@react-native/normalize-colors": "0.74.89", + "@react-native/virtualized-lists": "0.74.89", "abort-controller": "^3.0.0", "anser": "^1.4.9", "ansi-regex": "^5.0.0", @@ -14881,6 +15890,7 @@ "version": "0.0.15", "resolved": "https://registry.npmjs.org/react-native-barcode-svg/-/react-native-barcode-svg-0.0.15.tgz", "integrity": "sha512-OOCcwoM5QqXD22QP5/z/SLKjCvyaO0067BQCeHsrlMolp4Eo/uX4nvQZy1jMe0lXkeiS4CWYpakbVLlgVALEUg==", + "license": "MIT", "dependencies": { "jsbarcode": "^3.11.0", "prop-types": "^15.5.10" @@ -14895,6 +15905,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/react-native-draggable-flatlist/-/react-native-draggable-flatlist-4.0.1.tgz", "integrity": "sha512-ZO1QUTNx64KZfXGXeXcBfql67l38X7kBcJ3rxUVZzPHt5r035GnGzIC0F8rqSXp6zgnwgUYMfB6zQc5PKmPL9Q==", + "license": "MIT", "dependencies": { "@babel/preset-typescript": "^7.17.12" }, @@ -14905,18 +15916,20 @@ } }, "node_modules/react-native-draglist": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/react-native-draglist/-/react-native-draglist-3.6.2.tgz", - "integrity": "sha512-QGgA5ejlJkd9B5meDxvkhxiFSyDZ6K341U/xcx4KhkoglCEqRTPmw6uZ9bQjpBqlty/lksVpWKnetCOgFnTUYA==", + "version": "3.9.4", + "resolved": "https://registry.npmjs.org/react-native-draglist/-/react-native-draglist-3.9.4.tgz", + "integrity": "sha512-kujoocoVQ35r6P+dNFc7Ysi94+MpiCq4uE56TFMvVAwCDqa3c8iCxnCNGFVKUspguv8xboPypBcydtNb5JFuTQ==", + "license": "MIT", "peerDependencies": { "react": ">=17.0.1", "react-native": ">=0.64.0" } }, "node_modules/react-native-gesture-handler": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.22.1.tgz", - "integrity": "sha512-E0C9D+Ia2UZYevoSV9rTKjhFWEVdR/3l4Z3TUoQrI/wewgzDlmJOrYvGW5aMlPUuQF2vHQOdFfAWhVEqFu4tWw==", + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.24.0.tgz", + "integrity": "sha512-ZdWyOd1C8axKJHIfYxjJKCcxjWEpUtUWgTOVY2wynbiveSQDm8X/PDyAKXSer/GOtIpjudUbACOndZXCN3vHsw==", + "license": "MIT", "dependencies": { "@egjs/hammerjs": "^2.0.17", "hoist-non-react-statics": "^3.3.0", @@ -14948,6 +15961,7 @@ "version": "0.3.18", "resolved": "https://registry.npmjs.org/react-native-infinite-pager/-/react-native-infinite-pager-0.3.18.tgz", "integrity": "sha512-pt9r/s6FFZsqAX6GDTISvR/T5Qh/Jec94v685p1pMYXUQKgD4+lWiznx2lCRGf/hkCvy51CWLYLs6GdKFPotbw==", + "license": "MIT", "peerDependencies": { "react-native": ">=0.62.0", "react-native-gesture-handler": ">=2.0.0", @@ -14958,6 +15972,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/react-native-ios-context-menu/-/react-native-ios-context-menu-3.1.0.tgz", "integrity": "sha512-qdPSXMKUp5lDgmZeUPdv5sgBFhkFrIqma+zsnqJQYOvekb6Qs17yJy1Rqhrj0bJrwuduHzZX0aYbaA8whxqpDw==", + "license": "MIT", "workspaces": [ "example", "example-expo" @@ -14972,9 +15987,10 @@ } }, "node_modules/react-native-ios-utilities": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/react-native-ios-utilities/-/react-native-ios-utilities-5.1.1.tgz", - "integrity": "sha512-fOm7IR2KCn3MzghITbrnZfpJ3Z7wai4S46GwXwTql1fzX25eO8MXVgaUeMd5EvPwg1zAqF5I6c3T6Dby8DoF3A==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/react-native-ios-utilities/-/react-native-ios-utilities-5.1.2.tgz", + "integrity": "sha512-H566MdgC1x0vW6D8EKweno1yRj/gQHeWiK0cRmyRexnUSk7oIecy7l2uyxXLYWFs+yvB/YIdLm+s2c19lKGfaw==", + "license": "MIT", "workspaces": [ "example" ], @@ -14987,6 +16003,7 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/react-native-ios-visual-effect-view/-/react-native-ios-visual-effect-view-0.1.2.tgz", "integrity": "sha512-6fjeF+BO3rev+U3/K2CNwzkJmeaOiBNfiL7qSUftjs1oSgPIHQ+4WS6HvHdAVgAds76F1OgDWViQJrCYyVnHww==", + "license": "MIT", "workspaces": [ "example" ], @@ -15000,6 +16017,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/react-native-pager-view/-/react-native-pager-view-6.3.0.tgz", "integrity": "sha512-ufJOoVa9pFL1J/yb4hpsCqp8n1qTlcF5VvwqvCacHX//D7hSeRscsiIXg1u1pXNWwllvACb+mqxec/3Uj2mxrA==", + "license": "MIT", "peerDependencies": { "react": "*", "react-native": "*" @@ -15009,6 +16027,7 @@ "version": "0.0.22", "resolved": "https://registry.npmjs.org/react-native-parsed-text/-/react-native-parsed-text-0.0.22.tgz", "integrity": "sha512-hfD83RDXZf9Fvth3DowR7j65fMnlqM9PpxZBGWkzVcUTFtqe6/yPcIoIAgrJbKn6YmtzkivmhWE2MCE4JKBXrQ==", + "license": "MIT", "dependencies": { "prop-types": "^15.7.x" }, @@ -15021,6 +16040,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/react-native-pressable-scale/-/react-native-pressable-scale-2.1.0.tgz", "integrity": "sha512-YfdnzxphHi/2eir3MRcdF5xQpBhQkqvwubk9iwnW8EucxBQeYU8XRLJUqIhsKg6JFcp3inqzYw96+WwR/U8+0w==", + "license": "MIT", "peerDependencies": { "react": "*", "react-native": ">= 0.63", @@ -15029,24 +16049,26 @@ } }, "node_modules/react-native-qrcode-svg": { - "version": "6.3.12", - "resolved": "https://registry.npmjs.org/react-native-qrcode-svg/-/react-native-qrcode-svg-6.3.12.tgz", - "integrity": "sha512-7Bx23ZdFNJJdVXyW9BJmFWdI5kccjnpotzmL3exkV0irUKTmj51jesxpn5sqtgVdYFE4IUVoGzdS+8qg6Ua9BA==", + "version": "6.3.15", + "resolved": "https://registry.npmjs.org/react-native-qrcode-svg/-/react-native-qrcode-svg-6.3.15.tgz", + "integrity": "sha512-vLuNImGfstE8u+rlF4JfFpq65nPhmByuDG6XUPWh8yp8MgLQX11rN5eQ8nb/bf4OB+V8XoLTJB/AZF2g7jQSSQ==", + "license": "MIT", "dependencies": { "prop-types": "^15.8.0", - "qrcode": "^1.5.1", + "qrcode": "^1.5.4", "text-encoding": "^0.7.0" }, "peerDependencies": { "react": "*", "react-native": ">=0.63.4", - "react-native-svg": ">=13.2.0" + "react-native-svg": ">=14.0.0" } }, "node_modules/react-native-reanimated": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.10.1.tgz", "integrity": "sha512-sfxg6vYphrDc/g4jf/7iJ7NRi+26z2+BszPmvmk0Vnrz6FL7HYljJqTf531F1x6tFmsf+FEAmuCtTUIXFLVo9w==", + "license": "MIT", "dependencies": { "@babel/plugin-transform-arrow-functions": "^7.0.0-0", "@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0", @@ -15067,15 +16089,17 @@ "version": "4.10.5", "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.10.5.tgz", "integrity": "sha512-Wyb0Nqw2XJ6oZxW/cK8k5q7/UAhg/wbEG6UVf89rQqecDZTDA5ic//P9J6VvJRVZerzGmxWQpVuM7f+PRYUM4g==", + "license": "MIT", "peerDependencies": { "react": "*", "react-native": "*" } }, "node_modules/react-native-screens": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.9.1.tgz", - "integrity": "sha512-3pIOu1YXTDClQfkgk2t7rIr7unrV6bviaXRJsOq1APNHLeCPrJ6m5Gy3pW5Ty+XBm9jRAbMqhpjiXZIjesOR6A==", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.9.2.tgz", + "integrity": "sha512-87gR7XRIirACYxtYltEl1BbUo5r0W4AFPckUDDzATAN6LIUZ2PC3bX6UAFeoEBEqBbfaemRZTWSYHl6MCZFSgw==", + "license": "MIT", "dependencies": { "react-freeze": "^1.0.0", "warn-once": "^0.1.0" @@ -15086,18 +16110,19 @@ } }, "node_modules/react-native-share": { - "version": "12.0.3", - "resolved": "https://registry.npmjs.org/react-native-share/-/react-native-share-12.0.3.tgz", - "integrity": "sha512-Bg96AjouSbcpdlI/AzWXMBwpjyWcm4NvGr49mSF1cz8aw8E1sMXwpsHa6I841SJML8Im6sRN3aXnK+/QManrtQ==", + "version": "12.0.9", + "resolved": "https://registry.npmjs.org/react-native-share/-/react-native-share-12.0.9.tgz", + "integrity": "sha512-vSuz/9aF+/AZS3I5NC19MrB56h1Yivk2Yz8lf2d8Szv3KuRw2BnDI/AfCTjMWByJLVYr6xgzfkTkAfvbDGzxLQ==", "license": "MIT", "engines": { "node": ">=16" } }, "node_modules/react-native-svg": { - "version": "15.8.0", - "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.8.0.tgz", - "integrity": "sha512-KHJzKpgOjwj1qeZzsBjxNdoIgv2zNCO9fVcoq2TEhTRsVV5DGTZ9JzUZwybd7q4giT/H3RdtqC3u44dWdO0Ffw==", + "version": "15.11.2", + "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.11.2.tgz", + "integrity": "sha512-+YfF72IbWQUKzCIydlijV1fLuBsQNGMT6Da2kFlo1sh+LE3BIm/2Q7AR1zAAR6L0BFLi1WaQPLfFUC9bNZpOmw==", + "license": "MIT", "dependencies": { "css-select": "^5.1.0", "css-tree": "^1.1.3", @@ -15113,6 +16138,7 @@ "resolved": "https://registry.npmjs.org/react-native-svg-transformer/-/react-native-svg-transformer-1.5.0.tgz", "integrity": "sha512-RG5fSWJT7mjCQYocgYFUo1KYPLOoypPVG5LQab+pZZO7m4ciGaQIe0mhok3W4R5jLQsEXKo0u+aQGkZV/bZG7w==", "dev": true, + "license": "MIT", "dependencies": { "@svgr/core": "^8.1.0", "@svgr/plugin-jsx": "^8.1.0", @@ -15128,6 +16154,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/react-native-url-polyfill/-/react-native-url-polyfill-2.0.0.tgz", "integrity": "sha512-My330Do7/DvKnEvwQc0WdcBnFPploYKp9CYlefDXzIdEaA+PAhDYllkvGeEroEzvc4Kzzj2O4yVdz8v6fjRvhA==", + "license": "MIT", "dependencies": { "whatwg-url-without-unicode": "8.0.0-3" }, @@ -15139,6 +16166,7 @@ "version": "3.8.0", "resolved": "https://registry.npmjs.org/react-native-view-shot/-/react-native-view-shot-3.8.0.tgz", "integrity": "sha512-4cU8SOhMn3YQIrskh+5Q8VvVRxQOu8/s1M9NAL4z5BY1Rm0HXMWkQJ4N0XsZ42+Yca+y86ISF3LC5qdLPvPuiA==", + "license": "MIT", "dependencies": { "html2canvas": "^1.4.1" }, @@ -15151,6 +16179,7 @@ "version": "13.8.6", "resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.8.6.tgz", "integrity": "sha512-jtZ9OgB2AN6rhDwto6dNL3PtOtl/SI4VN93pZEPbMLvRjqHfxiUrilGllL5fKAXq5Ry5FJyfUi82A4Ii8olZ7A==", + "license": "MIT", "dependencies": { "escape-string-regexp": "2.0.0", "invariant": "2.2.4" @@ -15164,6 +16193,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "license": "MIT", "engines": { "node": ">=8" } @@ -15172,6 +16202,7 @@ "version": "26.6.2", "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", @@ -15184,9 +16215,10 @@ } }, "node_modules/react-native/node_modules/@react-native/codegen": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.74.88.tgz", - "integrity": "sha512-HMk/LCrSdUof9DZFaB2bK0soKyAF6XiCg2LG7WFjEkUDXayeiB4p7IsHISJWY4bYg7cMPZ0fiZMRaBP2vXJxgg==", + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.74.89.tgz", + "integrity": "sha512-xbcpnvsAjHrnYWnuoLdr5782dlR4LfkaWPityka/gWmdRDrE69ByAC9m0/vijMXAcMoHv6DSMh5x7gm6gW/3mA==", + "license": "MIT", "dependencies": { "@babel/parser": "^7.20.0", "glob": "^7.1.1", @@ -15205,14 +16237,39 @@ } }, "node_modules/react-native/node_modules/@react-native/normalize-colors": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.88.tgz", - "integrity": "sha512-He5oTwPBxvXrxJ91dZzpxR7P+VYmc9IkJfhuH8zUiU50ckrt+xWNjtVugPdUv4LuVjmZ36Vk2EX8bl1gVn2dVA==" + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.89.tgz", + "integrity": "sha512-qoMMXddVKVhZ8PA1AbUCk83trpd6N+1nF2A6k1i6LsQObyS92fELuk8kU/lQs6M7BsMHwqyLCpQJ1uFgNvIQXg==", + "license": "MIT" + }, + "node_modules/react-native/node_modules/@react-native/virtualized-lists": { + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.74.89.tgz", + "integrity": "sha512-E1KU/owsHRGnWVXKHgFIfAcf9NzxoDKFLoxdNaMRGXJH4i/qwT3ouENj2LW1BPL57W1G/8rj3kgn0xPW/YeI3A==", + "license": "MIT", + "dependencies": { + "invariant": "^2.2.4", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/react": "^18.2.6", + "react": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } }, "node_modules/react-native/node_modules/@types/istanbul-reports": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -15221,6 +16278,7 @@ "version": "15.0.19", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.19.tgz", "integrity": "sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -15229,6 +16287,7 @@ "version": "26.6.2", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "license": "MIT", "dependencies": { "@jest/types": "^26.6.2", "ansi-regex": "^5.0.0", @@ -15243,6 +16302,7 @@ "version": "8.3.0", "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "license": "MIT", "dependencies": { "asap": "~2.0.6" } @@ -15250,17 +16310,20 @@ "node_modules/react-native/node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "license": "MIT" }, "node_modules/react-native/node_modules/regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT" }, "node_modules/react-native/node_modules/ws": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "license": "MIT", "dependencies": { "async-limiter": "~1.0.0" } @@ -15268,12 +16331,14 @@ "node_modules/react-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/react-property/-/react-property-2.0.2.tgz", - "integrity": "sha512-+PbtI3VuDV0l6CleQMsx2gtK0JZbZKbpdu5ynr+lbsuvtmgbNcS3VM0tuY2QjFNOcWxvXeHjDpy42RO+4U2rug==" + "integrity": "sha512-+PbtI3VuDV0l6CleQMsx2gtK0JZbZKbpdu5ynr+lbsuvtmgbNcS3VM0tuY2QjFNOcWxvXeHjDpy42RO+4U2rug==", + "license": "MIT" }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -15282,6 +16347,7 @@ "version": "16.15.0", "resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz", "integrity": "sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==", + "license": "MIT", "dependencies": { "object-assign": "^4.1.1", "react-is": "^16.12.0 || ^17.0.0 || ^18.0.0" @@ -15294,6 +16360,7 @@ "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -15304,25 +16371,23 @@ "util-deprecate": "~1.0.1" } }, - "node_modules/readable-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, "node_modules/readable-stream/node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" }, "node_modules/readline": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz", - "integrity": "sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg==" + "integrity": "sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg==", + "license": "BSD" }, "node_modules/reanimated-color-picker": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/reanimated-color-picker/-/reanimated-color-picker-3.0.4.tgz", - "integrity": "sha512-p4ZBx73gHs844CEO+WJ+oVvBFZnydMxabQlGzkd2NMVqWFTEVTPYGNmCla3nj0r/L6gwaGnoeFlWO5vhpG7G3g==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/reanimated-color-picker/-/reanimated-color-picker-3.0.6.tgz", + "integrity": "sha512-ILmeR57d/DbWDy1vPdgz9vkRJyWHMIeTWEtxUgXPEH/BsiSXXhLju+hFDZY3M+bZUwpoCJWcvaAZl7p9kVwiEQ==", + "license": "MIT", "peerDependencies": { "expo": ">=44.0.0", "react": "*", @@ -15340,6 +16405,7 @@ "version": "0.21.5", "resolved": "https://registry.npmjs.org/recast/-/recast-0.21.5.tgz", "integrity": "sha512-hjMmLaUXAm1hIuTqOdeYObMslq/q+Xff6QE3Y2P+uoHAg2nmVlLBps2hzh1UJDdMtDTMXOFewK6ky51JQIeECg==", + "license": "MIT", "dependencies": { "ast-types": "0.15.2", "esprima": "~4.0.0", @@ -15354,23 +16420,25 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/reflect.getprototypeof": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", - "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", - "dev": true, + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.23.1", + "es-abstract": "^1.23.9", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -15382,12 +16450,14 @@ "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "license": "MIT" }, "node_modules/regenerate-unicode-properties": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "license": "MIT", "dependencies": { "regenerate": "^1.4.2" }, @@ -15398,25 +16468,30 @@ "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" }, "node_modules/regenerator-transform": { "version": "0.15.2", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "license": "MIT", "peer": true, "dependencies": { "@babel/runtime": "^7.8.4" } }, "node_modules/regexp.prototype.flags": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", - "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", "set-function-name": "^2.0.2" }, "engines": { @@ -15427,14 +16502,15 @@ } }, "node_modules/regexpu-core": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz", - "integrity": "sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", + "license": "MIT", "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.0", "regjsgen": "^0.8.0", - "regjsparser": "^0.11.0", + "regjsparser": "^0.12.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" }, @@ -15445,12 +16521,14 @@ "node_modules/regjsgen": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==" + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "license": "MIT" }, "node_modules/regjsparser": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.2.tgz", - "integrity": "sha512-3OGZZ4HoLJkkAZx/48mTXJNlmqTGOzc0o9OWQPuWpkOlXXPbyN6OafCcoXUnBqE2D3f/T5L+pWc1kdEmnfnRsA==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "license": "BSD-2-Clause", "dependencies": { "jsesc": "~3.0.2" }, @@ -15458,6 +16536,18 @@ "regjsparser": "bin/parser" } }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/remove-trailing-slash": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz", @@ -15468,6 +16558,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -15476,6 +16567,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -15483,7 +16575,8 @@ "node_modules/require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" }, "node_modules/requireg": { "version": "0.2.2", @@ -15507,16 +16600,12 @@ "path-parse": "^1.0.5" } }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" - }, "node_modules/resolve": { "version": "2.0.0-next.5", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, + "license": "MIT", "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -15533,6 +16622,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "license": "MIT", "engines": { "node": ">=8" } @@ -15566,9 +16656,10 @@ } }, "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -15579,6 +16670,7 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", "dependencies": { "glob": "^7.1.3" }, @@ -15593,6 +16685,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/rrule/-/rrule-2.8.1.tgz", "integrity": "sha512-hM3dHSBMeaJ0Ktp7W38BJZ7O1zOgaFEsn41PDk+yHoEtfLV+PoJt9E9xAlZiWgf/iqEqionN0ebHFZIDAp+iGw==", + "license": "BSD-3-Clause", "dependencies": { "tslib": "^2.4.0" } @@ -15615,18 +16708,21 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } }, "node_modules/safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", "isarray": "^2.0.5" }, "engines": { @@ -15636,6 +16732,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -15653,16 +16755,40 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" }, "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", - "is-regex": "^1.1.4" + "is-regex": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -15674,12 +16800,14 @@ "node_modules/sax": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "license": "ISC" }, "node_modules/scheduler": { "version": "0.24.0-canary-efb381bbf-20230505", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.24.0-canary-efb381bbf-20230505.tgz", "integrity": "sha512-ABvovCDe/k9IluqSh4/ISoq8tIJnW8euVAWYt5j/bg6dRnqwQwiGO1F/V4AyK96NGF/FB04FhOUDuWj8IKfABA==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" } @@ -15688,6 +16816,7 @@ "version": "3.0.5", "resolved": "https://registry.npmjs.org/scolengo-api/-/scolengo-api-3.0.5.tgz", "integrity": "sha512-AOAXsyP4xZCUp0uUfQSqSi2DPMXU03gxzm5faJ6XQRbK54QOlqiAlGRG2TiOqK9YqDgdG/pcEClY+NxbFzGX1A==", + "license": "GPL-3.0-or-later", "dependencies": { "axios": "^1.3.5", "base-64": "^1.0.0", @@ -15701,6 +16830,7 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz", "integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==", + "license": "MIT", "dependencies": { "parseley": "^0.12.0" }, @@ -15712,6 +16842,7 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "license": "MIT", "dependencies": { "@types/node-forge": "^1.3.0", "node-forge": "^1" @@ -15724,13 +16855,14 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "license": "MIT", "dependencies": { @@ -15804,6 +16936,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", + "license": "MIT", "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", @@ -15814,6 +16947,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz", "integrity": "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -15822,6 +16956,7 @@ "version": "1.16.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", @@ -15836,6 +16971,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -15843,12 +16979,14 @@ "node_modules/serve-static/node_modules/debug/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/serve-static/node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -15857,6 +16995,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -15868,6 +17007,7 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -15879,6 +17019,7 @@ "version": "0.19.0", "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -15902,6 +17043,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -15910,6 +17052,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -15917,17 +17060,20 @@ "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" }, "node_modules/set-cookie-parser": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", - "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==" + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -15944,6 +17090,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -15954,20 +17101,37 @@ "node": ">= 0.4" } }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" }, "node_modules/shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "license": "MIT", "dependencies": { "kind-of": "^6.0.2" }, @@ -15979,38 +17143,99 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, - "engines": { - "node": ">=8" + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", + "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, "engines": { - "node": ">=8" - } - }, - "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -16022,12 +17247,14 @@ "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" }, "node_modules/simple-plist": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/simple-plist/-/simple-plist-1.3.1.tgz", "integrity": "sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw==", + "license": "MIT", "dependencies": { "bplist-creator": "0.1.0", "bplist-parser": "0.3.1", @@ -16038,6 +17265,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz", "integrity": "sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==", + "license": "MIT", "dependencies": { "stream-buffers": "2.2.x" } @@ -16046,6 +17274,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.1.tgz", "integrity": "sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA==", + "license": "MIT", "dependencies": { "big-integer": "1.6.x" }, @@ -16057,6 +17286,7 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "license": "MIT", "dependencies": { "is-arrayish": "^0.3.1" } @@ -16064,17 +17294,20 @@ "node_modules/simple-swizzle/node_modules/is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "license": "MIT" }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "license": "MIT", "engines": { "node": ">=8" } @@ -16083,6 +17316,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "license": "MIT", "dependencies": { "ansi-styles": "^3.2.0", "astral-regex": "^1.0.0", @@ -16096,6 +17330,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -16107,6 +17342,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", "dependencies": { "color-name": "1.1.3" } @@ -16114,12 +17350,14 @@ "node_modules/slice-ansi/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" }, "node_modules/slugify": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz", "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==", + "license": "MIT", "engines": { "node": ">=8.0.0" } @@ -16128,6 +17366,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "license": "MIT", "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -16137,6 +17376,7 @@ "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "license": "BSD-3-Clause", "engines": { "node": ">= 8" } @@ -16145,6 +17385,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -16153,6 +17394,7 @@ "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -16162,6 +17404,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -16182,6 +17425,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "license": "MIT", "engines": { "node": ">=6" } @@ -16189,7 +17433,8 @@ "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" }, "node_modules/ssri": { "version": "10.0.6", @@ -16207,6 +17452,7 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "license": "MIT", "dependencies": { "escape-string-regexp": "^2.0.0" }, @@ -16218,6 +17464,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "license": "MIT", "engines": { "node": ">=8" } @@ -16225,12 +17472,14 @@ "node_modules/stackframe": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", - "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "license": "MIT" }, "node_modules/stacktrace-parser": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", - "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==", + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz", + "integrity": "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==", + "license": "MIT", "dependencies": { "type-fest": "^0.7.1" }, @@ -16242,6 +17491,7 @@ "version": "0.7.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=8" } @@ -16250,6 +17500,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -16258,6 +17509,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", "integrity": "sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==", + "license": "Unlicense", "engines": { "node": ">= 0.10.0" } @@ -16266,6 +17518,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "license": "MIT", "engines": { "node": ">=4" } @@ -16274,6 +17527,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", "dependencies": { "safe-buffer": "~5.1.0" } @@ -16281,12 +17535,14 @@ "node_modules/string_decoder/node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -16324,28 +17580,31 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/string.prototype.matchall": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", - "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", + "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "regexp.prototype.flags": "^1.5.2", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -16359,20 +17618,25 @@ "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", "dev": true, + "license": "MIT", "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" } }, "node_modules/string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -16382,14 +17646,19 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -16398,6 +17667,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -16414,6 +17684,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -16447,6 +17718,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "license": "MIT", "engines": { "node": ">=6" } @@ -16456,6 +17728,7 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -16464,9 +17737,16 @@ } }, "node_modules/strnum": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" }, "node_modules/structured-headers": { "version": "0.4.1", @@ -16478,6 +17758,7 @@ "version": "1.1.16", "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.16.tgz", "integrity": "sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw==", + "license": "MIT", "dependencies": { "style-to-object": "1.0.8" } @@ -16486,6 +17767,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz", "integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==", + "license": "MIT", "dependencies": { "inline-style-parser": "0.2.4" } @@ -16494,6 +17776,7 @@ "version": "3.34.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", "integrity": "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==", + "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", @@ -16515,6 +17798,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -16524,6 +17808,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "license": "MIT", "engines": { "node": ">= 6" } @@ -16533,6 +17818,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -16552,6 +17838,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -16563,12 +17850,14 @@ "version": "8.2.5", "resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-8.2.5.tgz", "integrity": "sha512-rlBo3HU/1zAJUrkY6jNxDOC9eVYliG6nS4JA8u8KAshITd07tafMc/Br7xQwCSseXwJ2iCcHCE8SNWX3q8Z+kw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "license": "MIT" }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -16593,6 +17882,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -16604,13 +17894,15 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/svgo": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", "dev": true, + "license": "MIT", "dependencies": { "@trysound/sax": "0.2.0", "commander": "^7.2.0", @@ -16636,6 +17928,7 @@ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", "dev": true, + "license": "MIT", "dependencies": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" @@ -16648,12 +17941,14 @@ "version": "2.0.30", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "dev": true + "dev": true, + "license": "CC0-1.0" }, "node_modules/swiftui-react-native": { "version": "6.3.3", "resolved": "https://registry.npmjs.org/swiftui-react-native/-/swiftui-react-native-6.3.3.tgz", "integrity": "sha512-/3CygnDsGkXFat+1LRujW1a7WSw/KOCl62CbGTOFLWQNOE3uGM0tylNO4PuiUDilxDqc/krOkjGORbplNfZYxw==", + "license": "MIT", "peerDependencies": { "expo": "*", "react": "*", @@ -16732,6 +18027,7 @@ "version": "0.8.4", "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.4.tgz", "integrity": "sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg==", + "license": "MIT", "dependencies": { "rimraf": "~2.6.2" }, @@ -16743,6 +18039,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "license": "MIT", "engines": { "node": ">=8" } @@ -16752,6 +18049,7 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", "dependencies": { "glob": "^7.1.3" }, @@ -16819,9 +18117,10 @@ } }, "node_modules/terser": { - "version": "5.36.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.36.0.tgz", - "integrity": "sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==", + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", + "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", + "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -16838,18 +18137,21 @@ "node_modules/terser/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" }, "node_modules/text-encoding": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==", - "deprecated": "no longer maintained" + "deprecated": "no longer maintained", + "license": "(Unlicense OR Apache-2.0)" }, "node_modules/text-segmentation": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "license": "MIT", "dependencies": { "utrie": "^1.0.2" } @@ -16857,12 +18159,14 @@ "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "license": "MIT" }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "license": "MIT", "dependencies": { "any-promise": "^1.0.0" } @@ -16871,6 +18175,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "license": "MIT", "dependencies": { "thenify": ">= 3.1.0 < 4" }, @@ -16881,7 +18186,8 @@ "node_modules/throat": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", - "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==" + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "license": "MIT" }, "node_modules/through": { "version": "2.3.8", @@ -16893,11 +18199,30 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "license": "MIT", "dependencies": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" } }, + "node_modules/tldts": { + "version": "6.1.84", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.84.tgz", + "integrity": "sha512-aRGIbCIF3teodtUFAYSdQONVmDRy21REM3o6JnqWn5ZkQBJJ4gHxhw6OfwQ+WkSAi3ASamrS4N4nyazWx6uTYg==", + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.84" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.84", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.84.tgz", + "integrity": "sha512-NaQa1W76W2aCGjXybvnMYzGSM4x8fvG2AN/pla7qxcg0ZHbooOPhA8kctmOZUDfZyhDL27OGNbwAeig8P4p1vg==", + "license": "MIT" + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -16913,12 +18238,14 @@ "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "license": "BSD-3-Clause" }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -16930,46 +18257,38 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", "engines": { "node": ">=0.6" } }, "node_modules/tough-cookie": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", - "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "license": "BSD-3-Clause", "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" + "tldts": "^6.1.32" }, "engines": { - "node": ">=6" - } - }, - "node_modules/tough-cookie/node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "engines": { - "node": ">= 4.0.0" + "node": ">=16" } }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" }, "node_modules/traverse": { - "version": "0.6.10", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.10.tgz", - "integrity": "sha512-hN4uFRxbK+PX56DxYiGHsTn2dME3TVr9vbNqlQGcGcPhJAn+tdP126iA+TArMpI4YSgnTkMWyoLl5bf81Hi5TA==", + "version": "0.6.11", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.11.tgz", + "integrity": "sha512-vxXDZg8/+p3gblxB6BhhG5yWVn1kGRlaL8O78UDXc3wRnPizB5g83dcvWV1jpDMIPnjZjOFuxlMmE82XJ4407w==", "license": "MIT", "dependencies": { - "gopd": "^1.0.1", - "typedarray.prototype.slice": "^1.0.3", - "which-typed-array": "^1.1.15" + "gopd": "^1.2.0", + "typedarray.prototype.slice": "^1.0.5", + "which-typed-array": "^1.1.18" }, "engines": { "node": ">= 0.4" @@ -16982,15 +18301,17 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", "integrity": "sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/ts-api-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", - "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", "dev": true, + "license": "MIT", "engines": { "node": ">=16" }, @@ -17001,12 +18322,14 @@ "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "license": "Apache-2.0" }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" }, "node_modules/turboself-api": { "version": "2.1.8", @@ -17022,6 +18345,7 @@ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, @@ -17033,6 +18357,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "license": "MIT", "engines": { "node": ">=4" } @@ -17050,28 +18375,30 @@ } }, "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" } }, "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -17081,16 +18408,18 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" }, "engines": { "node": ">= 0.4" @@ -17100,16 +18429,17 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-proto": "^1.0.3", "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" }, "engines": { "node": ">= 0.4" @@ -17119,17 +18449,19 @@ } }, "node_modules/typedarray.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typedarray.prototype.slice/-/typedarray.prototype.slice-1.0.3.tgz", - "integrity": "sha512-8WbVAQAUlENo1q3c3zZYuy5k9VzBQvp8AX9WOtbvyWlLM1v5JaSRmjubLjzHF4JFtptjH/5c/i95yaElvcjC0A==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/typedarray.prototype.slice/-/typedarray.prototype.slice-1.0.5.tgz", + "integrity": "sha512-q7QNVDGTdl702bVFiI5eY4l/HkgCM6at9KhcFbgUAzezHFbOVy4+0O/lCjsABEQwbZPravVfBIiBVGo89yzHFg==", "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", + "es-abstract": "^1.23.9", "es-errors": "^1.3.0", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-offset": "^1.0.2" + "get-proto": "^1.0.1", + "math-intrinsics": "^1.1.0", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-offset": "^1.0.4" }, "engines": { "node": ">= 0.4" @@ -17139,9 +18471,9 @@ } }, "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -17153,9 +18485,9 @@ } }, "node_modules/ua-parser-js": { - "version": "0.7.39", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.39.tgz", - "integrity": "sha512-IZ6acm6RhQHNibSt7+c09hhvsKy9WUr4DVbeq9U8o71qxyYtJpQeDxQnMrVqnIFMLcQjHO0I9wgfO2vIahht4w==", + "version": "0.7.40", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.40.tgz", + "integrity": "sha512-us1E3K+3jJppDBa3Tl0L3MOJiGhe1C6P0+nIvQAFYbxlMAx0h81eOwLmU57xgqToduDDPx3y5QsdjPfDu+FgOQ==", "funding": [ { "type": "opencollective", @@ -17170,6 +18502,7 @@ "url": "https://github.com/sponsors/faisalman" } ], + "license": "MIT", "bin": { "ua-parser-js": "script/cli.js" }, @@ -17178,14 +18511,18 @@ } }, "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", + "call-bound": "^1.0.3", "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -17194,12 +18531,14 @@ "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "license": "MIT", "engines": { "node": ">=4" } @@ -17208,6 +18547,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "license": "MIT", "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" @@ -17220,6 +18560,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "license": "MIT", "engines": { "node": ">=4" } @@ -17228,6 +18569,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "license": "MIT", "engines": { "node": ">=4" } @@ -17235,7 +18577,8 @@ "node_modules/unimodules-app-loader": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/unimodules-app-loader/-/unimodules-app-loader-4.6.0.tgz", - "integrity": "sha512-FRNIlx7sLBDVPG117JnEBhnzZkTIgZTEwYW2rzrY9HdvLBTpRN+k0dxY50U/CAhFHW3zMD0OP5JAlnSQRhx5HA==" + "integrity": "sha512-FRNIlx7sLBDVPG117JnEBhnzZkTIgZTEwYW2rzrY9HdvLBTpRN+k0dxY50U/CAhFHW3zMD0OP5JAlnSQRhx5HA==", + "license": "MIT" }, "node_modules/unique-filename": { "version": "3.0.0", @@ -17277,6 +18620,7 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "license": "MIT", "engines": { "node": ">= 4.0.0" } @@ -17285,14 +18629,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "funding": [ { "type": "opencollective", @@ -17307,9 +18652,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "escalade": "^3.2.0", - "picocolors": "^1.1.0" + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -17322,6 +18668,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", + "license": "MIT", "dependencies": { "tslib": "^2.0.3" } @@ -17330,6 +18677,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", + "license": "MIT", "dependencies": { "tslib": "^2.0.3" } @@ -17338,6 +18686,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } @@ -17348,40 +18697,35 @@ "integrity": "sha512-EGXjXJZhIHiQMK2pQukuFcL303nskqIRzWvPvV5O8miOfwoUb9G+a/Cld60kUyeaybEI94wvVClT10DtfeAExA==", "license": "MIT" }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "node_modules/use-latest-callback": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.1.tgz", - "integrity": "sha512-QWlq8Is8BGWBf883QOEQP5HWYX/kMI+JTbJ5rdtvJLmXTIh9XoHIO3PQcmQl8BU44VKxow1kbQUHa6mQSMALDQ==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.3.tgz", + "integrity": "sha512-7vI3fBuyRcP91pazVboc4qu+6ZqM8izPWX9k7cRnT8hbD5svslcknsh3S9BUhaK11OmgTV4oWZZVSeQAiV53SQ==", + "license": "MIT", "peerDependencies": { "react": ">=16.8" } }, "node_modules/use-sync-external-store": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", - "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", + "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", + "license": "MIT", "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", "engines": { "node": ">= 0.4.0" } @@ -17390,6 +18734,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "license": "MIT", "dependencies": { "base64-arraybuffer": "^1.0.2" } @@ -17421,6 +18766,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -17428,12 +18774,14 @@ "node_modules/vlq": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz", - "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==" + "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==", + "license": "MIT" }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "license": "Apache-2.0", "dependencies": { "makeerror": "1.0.12" } @@ -17441,12 +18789,14 @@ "node_modules/warn-once": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/warn-once/-/warn-once-0.1.1.tgz", - "integrity": "sha512-VkQZJbO8zVImzYFteBXvBOZEl1qL175WH8VmZcxF2fZAoudNhNDvHi+doCaAEdU2l2vtcIwa2zn0QK5+I1HQ3Q==" + "integrity": "sha512-VkQZJbO8zVImzYFteBXvBOZEl1qL175WH8VmZcxF2fZAoudNhNDvHi+doCaAEdU2l2vtcIwa2zn0QK5+I1HQ3Q==", + "license": "MIT" }, "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "license": "MIT", "dependencies": { "defaults": "^1.0.3" } @@ -17454,17 +18804,20 @@ "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" }, "node_modules/whatwg-fetch": { "version": "3.6.20", "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", - "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==" + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", + "license": "MIT" }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -17474,6 +18827,7 @@ "version": "8.0.0-3", "resolved": "https://registry.npmjs.org/whatwg-url-without-unicode/-/whatwg-url-without-unicode-8.0.0-3.tgz", "integrity": "sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig==", + "license": "MIT", "dependencies": { "buffer": "^5.4.3", "punycode": "^2.1.1", @@ -17501,6 +18855,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -17510,6 +18865,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "license": "BSD-2-Clause", "engines": { "node": ">=8" } @@ -17518,6 +18874,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -17529,38 +18886,43 @@ } }, "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "license": "MIT", "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-builtin-type": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.4.tgz", - "integrity": "sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==", - "dev": true, + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "license": "MIT", "dependencies": { + "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", + "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", + "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", - "which-typed-array": "^1.1.15" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -17569,11 +18931,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/which-builtin-type/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, "node_modules/which-collection": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "dev": true, + "license": "MIT", "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", @@ -17590,17 +18958,21 @@ "node_modules/which-module": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" }, "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, "engines": { @@ -17621,6 +18993,7 @@ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -17629,6 +19002,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -17662,12 +19036,14 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" }, "node_modules/write-file-atomic": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "license": "ISC", "dependencies": { "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", @@ -17675,9 +19051,9 @@ } }, "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -17699,6 +19075,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/xcode/-/xcode-3.0.1.tgz", "integrity": "sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==", + "license": "Apache-2.0", "dependencies": { "simple-plist": "^1.1.0", "uuid": "^7.0.3" @@ -17711,6 +19088,7 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } @@ -17719,6 +19097,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.0.tgz", "integrity": "sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w==", + "license": "MIT", "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" @@ -17731,6 +19110,7 @@ "version": "11.0.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "license": "MIT", "engines": { "node": ">=4.0" } @@ -17739,6 +19119,7 @@ "version": "14.0.0", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-14.0.0.tgz", "integrity": "sha512-ts+B2rSe4fIckR6iquDjsKbQFK2NlUk6iG5nf14mDEyldgoc2nEKZ3jZWMPTxGQwVgToSjt6VGIho1H8/fNFTg==", + "license": "MIT", "engines": { "node": ">=8.0" } @@ -17747,6 +19128,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", "engines": { "node": ">=0.4" } @@ -17755,6 +19137,7 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", "engines": { "node": ">=10" } @@ -17762,12 +19145,14 @@ "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" }, "node_modules/yaml": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", - "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "license": "ISC", "bin": { "yaml": "bin.mjs" }, @@ -17779,6 +19164,7 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -17796,6 +19182,7 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", "engines": { "node": ">=12" } @@ -17804,6 +19191,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -17812,9 +19200,10 @@ } }, "node_modules/zod": { - "version": "3.23.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "version": "3.24.2", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz", + "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -17823,6 +19212,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-2.1.0.tgz", "integrity": "sha512-VJh93e2wb4c3tWtGgTa0OF/dTt/zoPCPzXq4V11ZjxmEAFaPi/Zss1xIZdEB5RD8GD00U0/iVXgqkF77RV7pdQ==", + "license": "MIT", "engines": { "node": ">=18.0.0" }, @@ -17831,11 +19221,12 @@ } }, "node_modules/zustand": { - "version": "4.5.5", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.5.tgz", - "integrity": "sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==", + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.6.tgz", + "integrity": "sha512-ibr/n1hBzLLj5Y+yUcU7dYw8p6WnIVzdJbnX+1YpaScvZVF2ziugqHs+LAmHw4lWO9c/zRj+K1ncgWDQuthEdQ==", + "license": "MIT", "dependencies": { - "use-sync-external-store": "1.2.2" + "use-sync-external-store": "^1.2.2" }, "engines": { "node": ">=12.7.0" From b7e1a0285cae5ba80cb4bea737e3985efe7148eb Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Fri, 14 Mar 2025 18:56:06 +0100 Subject: [PATCH 0846/1144] feat: ajuster le style de NativeList pour les tablettes --- src/views/welcome/ChangelogScreen.tsx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/views/welcome/ChangelogScreen.tsx b/src/views/welcome/ChangelogScreen.tsx index 2058603de..6f539481e 100644 --- a/src/views/welcome/ChangelogScreen.tsx +++ b/src/views/welcome/ChangelogScreen.tsx @@ -11,7 +11,6 @@ import { useTheme } from "@react-navigation/native"; import Reanimated, { FadeInUp, FadeOutUp, LinearTransition } from "react-native-reanimated"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { animPapillon } from "@/utils/ui/animations"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; import { TouchableOpacity } from "react-native-gesture-handler"; import { PressableScale } from "react-native-pressable-scale"; @@ -19,6 +18,7 @@ import { PressableScale } from "react-native-pressable-scale"; import AsyncStorage from "@react-native-async-storage/async-storage"; import {Screen} from "@/router/helpers/types"; import { OfflineWarning, useOnlineStatus } from "@/hooks/useOnlineStatus"; +import useScreenDimensions from "@/hooks/useScreenDimensions"; interface Feature { title: string; @@ -44,7 +44,7 @@ const changelogURL = datasets.changelog.replace("[version]", currentVersion.spli const ChangelogScreen: Screen<"ChangelogScreen"> = ({ route, navigation }) => { const theme = useTheme(); - const insets = useSafeAreaInsets(); + const { isTablet } = useScreenDimensions(); const { isOnline } = useOnlineStatus(); const [changelog, setChangelog] = useState(null); @@ -162,7 +162,16 @@ const ChangelogScreen: Screen<"ChangelogScreen"> = ({ route, navigation }) => { layout={animPapillon(LinearTransition)} > - + Date: Fri, 14 Mar 2025 19:10:18 +0100 Subject: [PATCH 0847/1144] feat: adapter la largeur de l'alerte en fonction de l'appareil (tablette ou mobile) --- src/providers/AlertProvider.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index 41009753e..98744653f 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -21,6 +21,7 @@ import { } from "@/utils/ui/animations"; import { BlurView } from "expo-blur"; import NativeTouchable from "@/components/Global/NativeTouchable"; +import useScreenDimensions from "@/hooks/useScreenDimensions"; type AlertAction = { title: string; @@ -62,7 +63,8 @@ const AlertProvider = ({ children }: AlertProviderProps) => { const [visible, setVisible] = useState(false); const [delays, setDelays] = useState<{ [key: string]: number }>({}); - const { dark, colors } = useTheme(); + const { colors } = useTheme(); + const { isTablet } = useScreenDimensions(); const insets = useSafeAreaInsets(); const showAlert = ({ @@ -132,7 +134,13 @@ const AlertProvider = ({ children }: AlertProviderProps) => { From 732a7406e4bb6049c57759eff44932098bc79536 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Fri, 14 Mar 2025 19:11:17 +0100 Subject: [PATCH 0848/1144] feat: centrer le texte et ajuster l'alignement dans SettingsMultiService --- src/views/settings/SettingsMultiService.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index 4e4cabc9e..acd5c92dd 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -246,7 +246,8 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => fontFamily: "medium", fontSize: 12.5, lineHeight: 12, - color: theme.colors.text + "60" + color: theme.colors.text + "60", + textAlign: "center", }} variant="subtitle" > @@ -331,8 +332,6 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => Date: Fri, 14 Mar 2025 19:33:24 +0100 Subject: [PATCH 0849/1144] =?UTF-8?q?feat:=20mettre=20=C3=A0=20jour=20le?= =?UTF-8?q?=20tint=20de=20ReanimatedBlurView=20pour=20utiliser=20systemUlt?= =?UTF-8?q?raThinMaterialDark?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Home/AccountSwitcher.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Home/AccountSwitcher.tsx b/src/components/Home/AccountSwitcher.tsx index 78f42ce8d..4606d16f1 100644 --- a/src/components/Home/AccountSwitcher.tsx +++ b/src/components/Home/AccountSwitcher.tsx @@ -86,7 +86,7 @@ const AccountSwitcher: React.FC<{ layout={animPapillon(LinearTransition)} > Date: Fri, 14 Mar 2025 19:45:14 +0100 Subject: [PATCH 0850/1144] =?UTF-8?q?feat:=20ajout=20de=20KeyboardAvoiding?= =?UTF-8?q?View=20pour=20am=C3=A9liorer=20la=20gestion=20du=20clavier=20da?= =?UTF-8?q?ns=20SettingsSupport?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/SettingsSupport.tsx | 166 +++++++++++++------------ 1 file changed, 88 insertions(+), 78 deletions(-) diff --git a/src/views/settings/SettingsSupport.tsx b/src/views/settings/SettingsSupport.tsx index fb89880b3..4de006877 100644 --- a/src/views/settings/SettingsSupport.tsx +++ b/src/views/settings/SettingsSupport.tsx @@ -1,5 +1,5 @@ import React, { useState } from "react"; -import { ScrollView, StyleSheet, TextInput, View } from "react-native"; +import { KeyboardAvoidingView, ScrollView, StyleSheet, TextInput, View } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; @@ -64,86 +64,96 @@ const SettingsSupport: Screen<"SettingsSupport"> = ({ navigation }) => { }; return ( - - - - - }> - Adresse E-Mail - - - }> - Sujet - - - }> - Description - - - - - - { - setSendLogs(!sendLogs); - }} + + + + + }> + Adresse E-Mail + - }> - J’accepte de transmettre les journaux d'erreurs et les données du formulaire pour le traitement de ma demande - - - - handlePress()} /> - - + + }> + Sujet + + + }> + Description + + + + + + { + setSendLogs(!sendLogs); + }} + /> + }> + J’accepte de transmettre les journaux d'erreurs et les données du formulaire pour le traitement de ma demande + + + + handlePress()} /> + + + ); }; From 7fd589e033ce78b442ce1e1dc26a5886a572edfb Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Fri, 14 Mar 2025 20:45:14 +0100 Subject: [PATCH 0851/1144] fix: Changer l'orientation de l'application en portrait dans app.config.ts --- app.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.config.ts b/app.config.ts index 511d5c7ee..e80bbd594 100644 --- a/app.config.ts +++ b/app.config.ts @@ -6,7 +6,7 @@ export default (): ExpoConfig => ({ slug: PackageJSON.name, scheme: "papillon", version: PackageJSON.version, - orientation: "default", + orientation: "portrait", icon: "./assets/icon.png", userInterfaceStyle: "automatic", primaryColor: "#32AB8E", From 88caf6bce8321570dab8c7383d7695f32f279c90 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Fri, 14 Mar 2025 20:48:43 +0100 Subject: [PATCH 0852/1144] =?UTF-8?q?fix:=20Corriger=20la=20logique=20de?= =?UTF-8?q?=20date=20actuelle=20et=20augmenter=20le=20nombre=20de=20dates?= =?UTF-8?q?=20charg=C3=A9es=20dans=20le=20composant=20Lessons?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Lessons/Lessons.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index a8d0072c0..267bc5c3f 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -191,7 +191,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { = ({ route, navigation }) => { onEndReached={() => { // Charger plus de dates si nécessaire const lastDate = data[data.length - 1]; - const newDates = Array.from({ length: 30 }, (_, i) => { + const newDates = Array.from({ length: 34 }, (_, i) => { const date = new Date(lastDate); date.setDate(lastDate.getDate() + i + 1); return date; From 8474aa4bae5e7d2f6bcbe5fd4eabe59984c07c90 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Fri, 14 Mar 2025 20:55:07 +0100 Subject: [PATCH 0853/1144] =?UTF-8?q?fix:=20Ajouter=20une=20gestion=20des?= =?UTF-8?q?=20erreurs=20lors=20de=20la=20r=C3=A9cup=C3=A9ration=20des=20do?= =?UTF-8?q?nn=C3=A9es=20de=20la=20carte=20dans=20le=20composant=20CardDeta?= =?UTF-8?q?il?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../account/Restaurant/Modals/CardDetail.tsx | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index fffae0444..8d9ec7912 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -34,23 +34,28 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio const cardName = `Carte ${AccountService[route.params.card.service as AccountService]} ${account?.identity?.firstName ? "de " + account.identity.firstName : ""}`; const updateCardData = async () => { - const [balance, history] = await Promise.all([ - balanceFromExternal(route.params.card.account as ExternalAccount).catch(err => { - console.warn(`Error fetching balance for account ${account}:`, err); - return []; - }), - reservationHistoryFromExternal(route.params.card.account as ExternalAccount).catch(err => { - console.warn(`Error fetching history for account ${account}:`, err); - return []; - }) - ]); + try { + const [balance, history] = await Promise.all([ + balanceFromExternal(route.params.card.account as ExternalAccount).catch(err => { + console.warn(`Error fetching balance for account ${account}:`, err); + return []; + }), + reservationHistoryFromExternal(route.params.card.account as ExternalAccount).catch(err => { + console.warn(`Error fetching history for account ${account}:`, err); + return []; + }) + ]); - setCardData({ - ...card, - // @ts-expect-error - balance: balance, - history: history, - }); + setCardData({ + ...card, + // @ts-expect-error + balance: balance, + history: history, + }); + } + catch (e) { + console.log(e); + } }; React.useEffect(() => { From ea647c2985e07559b9f8c34cd3eaf77985012386 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Fri, 14 Mar 2025 21:06:29 +0100 Subject: [PATCH 0854/1144] =?UTF-8?q?fix:=20Corriger=20le=20texte=20d'affi?= =?UTF-8?q?chage=20pour=20les=20logs=20non=20enregistr=C3=A9s=20dans=20le?= =?UTF-8?q?=20composant=20SettingsDevLogs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/SettingsDevLogs.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/settings/SettingsDevLogs.tsx b/src/views/settings/SettingsDevLogs.tsx index 5b2478167..1cbfd3965 100644 --- a/src/views/settings/SettingsDevLogs.tsx +++ b/src/views/settings/SettingsDevLogs.tsx @@ -229,7 +229,7 @@ const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { From ca238c3d834e5da52e897b63a4724280c0aa9038 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Fri, 14 Mar 2025 21:38:11 +0100 Subject: [PATCH 0855/1144] chore(lint): semicolon and eol-last --- App.tsx | 2 +- babel.config.js | 2 +- eslint.config.js | 1 + plugins/notifee-mod.js | 2 +- src/background/BackgroundTasks.ts | 2 +- src/components/Drawables/DrawableImportRestaurant.tsx | 2 +- src/components/FirstInstallation/DuoListPressable.tsx | 2 +- src/components/FirstInstallation/MaskStars.tsx | 2 +- src/components/FirstInstallation/MaskStarsColored.tsx | 2 +- src/components/Global/LinkFavicon.tsx | 2 +- src/components/Global/NativeTouchable.tsx | 2 +- src/components/Global/PapillonCheckbox.tsx | 2 +- src/components/Home/Widget.tsx | 2 +- src/components/Home/WidgetHeader.tsx | 2 +- src/components/Modals/ModalHandle.tsx | 2 +- src/components/Settings/AboutContainerCard.tsx | 2 +- src/components/Settings/SubjectContainerCard.tsx | 2 +- src/consts/DefaultTabs.ts | 2 +- src/router/helpers/themes.ts | 2 +- src/router/screens/account/home.tsx | 2 +- src/router/screens/account/stack.tsx | 2 +- src/router/screens/settings/navigator.tsx | 2 +- src/services/alise/balance.ts | 2 +- src/services/ard/balance.ts | 2 +- src/services/balance.ts | 2 +- src/services/ecoledirecte/getFile.ts | 2 +- src/services/ecoledirecte/time-interval.ts | 2 +- src/services/izly/balance.ts | 2 +- src/services/izly/reload.ts | 2 +- src/services/local/default-personalization.ts | 2 +- src/services/pronote/attachment.ts | 2 +- src/services/pronote/attendance.ts | 2 +- src/services/pronote/dataset_geolocation.ts | 2 +- src/services/pronote/evaluations.ts | 2 +- src/services/pronote/period.ts | 2 +- src/services/pronote/timetable.ts | 2 +- src/services/qrcode.ts | 2 +- src/services/reservation-history.ts | 2 +- src/services/shared/Attachment.ts | 2 +- src/services/shared/Chat.ts | 2 +- src/services/shared/Evaluation.ts | 2 +- src/services/shared/Homework.ts | 2 +- src/services/shared/Recipient.ts | 2 +- src/services/shared/Reel.ts | 2 +- src/services/shared/ReservationHistory.ts | 2 +- src/services/shared/Timetable.ts | 2 +- src/services/skolengo/data/attachment.ts | 2 +- src/services/skolengo/data/period.ts | 2 +- src/services/skolengo/data/timetable.ts | 2 +- src/services/skolengo/data/utils.ts | 2 +- src/services/skolengo/default-personalization.ts | 2 +- src/services/skolengo/skolengo-types.ts | 2 +- src/services/timetable.ts | 2 +- src/services/turboself/balance.ts | 2 +- src/services/turboself/booking.ts | 2 +- src/stores/classSubject/index.ts | 2 +- src/utils/GetRessources/GetContribs.tsx | 2 +- src/utils/chat/themes/GetAvailableThemes.ts | 2 +- src/utils/chat/themes/GetThemeForChat.ts | 2 +- src/utils/chat/themes/Themes.types.ts | 2 +- src/utils/files/getAndOpenFile.ts | 2 +- src/utils/files/openFileInQL.ts | 2 +- src/utils/files/saveFile.ts | 2 +- src/utils/format/attendance_time.ts | 2 +- src/utils/format/course_duration.ts | 2 +- src/utils/format/format_cours_name.ts | 2 +- src/utils/format/format_date.ts | 2 +- src/utils/format/format_pronote_initials.ts | 2 +- src/utils/login/GetV6Data.ts | 2 +- src/utils/ui/animations.ts | 2 +- src/utils/ui/colors.ts | 2 +- src/views/account/Attendance/Atoms/TotalMissed.tsx | 2 +- src/views/account/Evaluation/Latest/LatestEvaluations.tsx | 2 +- src/views/account/Evaluation/Subject/Subject.tsx | 2 +- src/views/account/Grades/Latest/LatestGrades.tsx | 2 +- src/views/account/Homeworks/Atoms/Loading.tsx | 2 +- src/views/account/Homeworks/Atoms/NoHomeworks.tsx | 2 +- src/views/account/Restaurant/Cards/Card.tsx | 2 +- src/views/account/Restaurant/Cards/StoreThemes.ts | 2 +- src/views/account/Restaurant/Modals/CardDetail.tsx | 2 +- src/views/account/Restaurant/Modals/PaymentSuccess.tsx | 2 +- src/views/account/Restaurant/Modals/QrCode.tsx | 2 +- src/views/login/pronote/PronoteGeolocation.tsx | 2 +- src/views/login/skolengo/SkolengoGeolocation.tsx | 2 +- src/views/settings/SettingsAddons.tsx | 2 +- src/views/settings/SettingsIcons.tsx | 6 +++--- src/views/welcome/AccountSelector.tsx | 2 +- src/views/welcome/ColorSelector.tsx | 2 +- src/widgets/Components/LastGrade.tsx | 2 +- src/widgets/index.tsx | 2 +- 90 files changed, 92 insertions(+), 91 deletions(-) diff --git a/App.tsx b/App.tsx index c88baa930..146330649 100644 --- a/App.tsx +++ b/App.tsx @@ -109,7 +109,7 @@ export default function App () { if (!isExpoGo()) { registerBackgroundTasks(); - }; + } }, []); const applyGlobalPolyfills = useCallback(() => { diff --git a/babel.config.js b/babel.config.js index fbcf18550..777d7ff34 100644 --- a/babel.config.js +++ b/babel.config.js @@ -8,4 +8,4 @@ module.exports = function (api) { "react-native-reanimated/plugin" ], }; -}; \ No newline at end of file +}; diff --git a/eslint.config.js b/eslint.config.js index cebdc1ca8..46eb149f3 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -39,6 +39,7 @@ module.exports = [ "@stylistic/no-trailing-spaces": "error", "@stylistic/quotes": ["error", "double"], "@stylistic/semi": ["error", "always"], + "@stylistic/eol-last": ["error", "always"], "@stylistic/space-before-function-paren": ["error", "always"], "@stylistic/no-extra-semi": "error", "@stylistic/no-mixed-spaces-and-tabs": "error", diff --git a/plugins/notifee-mod.js b/plugins/notifee-mod.js index 0c957eafb..92c5db33f 100644 --- a/plugins/notifee-mod.js +++ b/plugins/notifee-mod.js @@ -14,4 +14,4 @@ module.exports = function withNotifeeRepo (config) { return config; }); -}; \ No newline at end of file +}; diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index bfac560d8..81d901725 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -38,7 +38,7 @@ const registerBackgroundTasks = async () => { backgroundFetch(); console.log("[background fetch] Registered background fetch"); - }; + } }; const unsetBackgroundFetch = async () => { diff --git a/src/components/Drawables/DrawableImportRestaurant.tsx b/src/components/Drawables/DrawableImportRestaurant.tsx index 81d697117..13261024a 100644 --- a/src/components/Drawables/DrawableImportRestaurant.tsx +++ b/src/components/Drawables/DrawableImportRestaurant.tsx @@ -38,4 +38,4 @@ const DrawableImportRestaurant = (props: any) => ( ); -export default DrawableImportRestaurant; \ No newline at end of file +export default DrawableImportRestaurant; diff --git a/src/components/FirstInstallation/DuoListPressable.tsx b/src/components/FirstInstallation/DuoListPressable.tsx index ba8baa073..e51735afa 100644 --- a/src/components/FirstInstallation/DuoListPressable.tsx +++ b/src/components/FirstInstallation/DuoListPressable.tsx @@ -163,4 +163,4 @@ const styles = StyleSheet.create({ }, }); -export default DuoListPressable; \ No newline at end of file +export default DuoListPressable; diff --git a/src/components/FirstInstallation/MaskStars.tsx b/src/components/FirstInstallation/MaskStars.tsx index e395e3a5f..211b692b3 100644 --- a/src/components/FirstInstallation/MaskStars.tsx +++ b/src/components/FirstInstallation/MaskStars.tsx @@ -28,4 +28,4 @@ const styles = StyleSheet.create({ } }); -export default MaskStars; \ No newline at end of file +export default MaskStars; diff --git a/src/components/FirstInstallation/MaskStarsColored.tsx b/src/components/FirstInstallation/MaskStarsColored.tsx index fa087eea2..9cb86854e 100644 --- a/src/components/FirstInstallation/MaskStarsColored.tsx +++ b/src/components/FirstInstallation/MaskStarsColored.tsx @@ -28,4 +28,4 @@ const styles = StyleSheet.create({ } }); -export default MaskStarsColored; \ No newline at end of file +export default MaskStarsColored; diff --git a/src/components/Global/LinkFavicon.tsx b/src/components/Global/LinkFavicon.tsx index 9bb429471..c6cf8321b 100644 --- a/src/components/Global/LinkFavicon.tsx +++ b/src/components/Global/LinkFavicon.tsx @@ -69,4 +69,4 @@ const LinkFavicon = (props: LinkFaviconProps) => { ); }; -export default LinkFavicon; \ No newline at end of file +export default LinkFavicon; diff --git a/src/components/Global/NativeTouchable.tsx b/src/components/Global/NativeTouchable.tsx index 130b62cbb..24aa765c4 100644 --- a/src/components/Global/NativeTouchable.tsx +++ b/src/components/Global/NativeTouchable.tsx @@ -30,4 +30,4 @@ const NativeTouchable: React.FC = ({ ); }; -export default NativeTouchable; \ No newline at end of file +export default NativeTouchable; diff --git a/src/components/Global/PapillonCheckbox.tsx b/src/components/Global/PapillonCheckbox.tsx index 1ba2296b2..667fa71d6 100644 --- a/src/components/Global/PapillonCheckbox.tsx +++ b/src/components/Global/PapillonCheckbox.tsx @@ -141,4 +141,4 @@ const PapillonCheckbox: React.FC = ({ ); }; -export default PapillonCheckbox; \ No newline at end of file +export default PapillonCheckbox; diff --git a/src/components/Home/Widget.tsx b/src/components/Home/Widget.tsx index e5eefbc00..2ebb567ba 100644 --- a/src/components/Home/Widget.tsx +++ b/src/components/Home/Widget.tsx @@ -144,4 +144,4 @@ const styles = StyleSheet.create({ }, }); -export default Widget; \ No newline at end of file +export default Widget; diff --git a/src/components/Home/WidgetHeader.tsx b/src/components/Home/WidgetHeader.tsx index 8d0351181..235fd2bc3 100644 --- a/src/components/Home/WidgetHeader.tsx +++ b/src/components/Home/WidgetHeader.tsx @@ -46,4 +46,4 @@ const WidgetHeader: React.FC<{ ); }; -export default WidgetHeader; \ No newline at end of file +export default WidgetHeader; diff --git a/src/components/Modals/ModalHandle.tsx b/src/components/Modals/ModalHandle.tsx index 65d109b06..4b9dec305 100644 --- a/src/components/Modals/ModalHandle.tsx +++ b/src/components/Modals/ModalHandle.tsx @@ -43,4 +43,4 @@ const styles = StyleSheet.create({ }, }); -export default ModalHandle; \ No newline at end of file +export default ModalHandle; diff --git a/src/components/Settings/AboutContainerCard.tsx b/src/components/Settings/AboutContainerCard.tsx index 1ea52f595..092504bb8 100644 --- a/src/components/Settings/AboutContainerCard.tsx +++ b/src/components/Settings/AboutContainerCard.tsx @@ -37,4 +37,4 @@ const AboutContainerCard = ({ theme }: { theme: any }) => { ); }; -export default AboutContainerCard; \ No newline at end of file +export default AboutContainerCard; diff --git a/src/components/Settings/SubjectContainerCard.tsx b/src/components/Settings/SubjectContainerCard.tsx index b0ce2d9ce..786f1a869 100644 --- a/src/components/Settings/SubjectContainerCard.tsx +++ b/src/components/Settings/SubjectContainerCard.tsx @@ -36,4 +36,4 @@ const SubjectContainerCard = ({ theme }: { theme: any }) => { ); }; -export default SubjectContainerCard; \ No newline at end of file +export default SubjectContainerCard; diff --git a/src/consts/DefaultTabs.ts b/src/consts/DefaultTabs.ts index d83bf88fe..e92971c7d 100644 --- a/src/consts/DefaultTabs.ts +++ b/src/consts/DefaultTabs.ts @@ -71,4 +71,4 @@ export const defaultTabs = [ icon: require("@/../assets/lottie/tab_calendar.json"), enabled: true, }, -] as const; \ No newline at end of file +] as const; diff --git a/src/router/helpers/themes.ts b/src/router/helpers/themes.ts index 955e3c894..9a3c4d32e 100644 --- a/src/router/helpers/themes.ts +++ b/src/router/helpers/themes.ts @@ -24,4 +24,4 @@ export const PapillonDark: Theme = { border: "#252525", notification: "#29947A", }, -}; \ No newline at end of file +}; diff --git a/src/router/screens/account/home.tsx b/src/router/screens/account/home.tsx index bf1945e64..c3eb80c1b 100644 --- a/src/router/screens/account/home.tsx +++ b/src/router/screens/account/home.tsx @@ -80,4 +80,4 @@ const HomeStackScreen = ({ accountScreens }: { ); }; -export default HomeStackScreen; \ No newline at end of file +export default HomeStackScreen; diff --git a/src/router/screens/account/stack.tsx b/src/router/screens/account/stack.tsx index d81ad1d79..cd40aab3b 100644 --- a/src/router/screens/account/stack.tsx +++ b/src/router/screens/account/stack.tsx @@ -32,4 +32,4 @@ const AccountStackScreen: Screen<"AccountStack"> = () => { ); }; -export default AccountStackScreen; \ No newline at end of file +export default AccountStackScreen; diff --git a/src/router/screens/settings/navigator.tsx b/src/router/screens/settings/navigator.tsx index c17b36868..99cc34e52 100644 --- a/src/router/screens/settings/navigator.tsx +++ b/src/router/screens/settings/navigator.tsx @@ -37,4 +37,4 @@ const ConditionnalAlertProvider = (props: any) => { {props.children} ); -}; \ No newline at end of file +}; diff --git a/src/services/alise/balance.ts b/src/services/alise/balance.ts index e00b7c8a3..eac803213 100644 --- a/src/services/alise/balance.ts +++ b/src/services/alise/balance.ts @@ -11,4 +11,4 @@ export const getBalance = async (account: AliseAccount, force = false): Promise< remaining: Math.floor(balance / (mealPrice ?? 0)), label: "Self" }]; -}; \ No newline at end of file +}; diff --git a/src/services/ard/balance.ts b/src/services/ard/balance.ts index 31e2ff436..84a43c511 100644 --- a/src/services/ard/balance.ts +++ b/src/services/ard/balance.ts @@ -14,4 +14,4 @@ export const balance = async (account: ARDAccount): Promise => { label: wallet.walletName[0].toUpperCase() + wallet.walletName.slice(1).toLowerCase() })).reverse(); -}; \ No newline at end of file +}; diff --git a/src/services/balance.ts b/src/services/balance.ts index 7e79c7a92..4267815f6 100644 --- a/src/services/balance.ts +++ b/src/services/balance.ts @@ -23,4 +23,4 @@ export const balanceFromExternal = async (account: ExternalAccount, force = fals return []; } } -}; \ No newline at end of file +}; diff --git a/src/services/ecoledirecte/getFile.ts b/src/services/ecoledirecte/getFile.ts index 986665f4e..e102e263c 100644 --- a/src/services/ecoledirecte/getFile.ts +++ b/src/services/ecoledirecte/getFile.ts @@ -8,4 +8,4 @@ export const getFile = async (account: EcoleDirecteAccount, id: string, type: st "Content-Type": "application/x-www-form-urlencoded" }, "POST", { forceDownload: 0 }); return b64Content.split("base64,")[1]; -}; \ No newline at end of file +}; diff --git a/src/services/ecoledirecte/time-interval.ts b/src/services/ecoledirecte/time-interval.ts index 7362e8ad5..e5a0210b4 100644 --- a/src/services/ecoledirecte/time-interval.ts +++ b/src/services/ecoledirecte/time-interval.ts @@ -102,4 +102,4 @@ export const getDurationInHours = (interval: Timeinterval): string => { const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); return `${hours}h${minutes.toString().padStart(2, "0")}`; -}; \ No newline at end of file +}; diff --git a/src/services/izly/balance.ts b/src/services/izly/balance.ts index 3f4978262..ddb37c659 100644 --- a/src/services/izly/balance.ts +++ b/src/services/izly/balance.ts @@ -21,4 +21,4 @@ export const balance = async (account: IzlyAccount): Promise => { label: "Cash" }]: []) ]; -}; \ No newline at end of file +}; diff --git a/src/services/izly/reload.ts b/src/services/izly/reload.ts index f44f33159..d38cbb608 100644 --- a/src/services/izly/reload.ts +++ b/src/services/izly/reload.ts @@ -9,4 +9,4 @@ export const reload = async (account: IzlyAccount): Promise => { await refresh(instance, secret); log("session refreshed", "izly"); return instance; -}; \ No newline at end of file +}; diff --git a/src/services/local/default-personalization.ts b/src/services/local/default-personalization.ts index 16beab36a..42cd54acd 100644 --- a/src/services/local/default-personalization.ts +++ b/src/services/local/default-personalization.ts @@ -52,4 +52,4 @@ export function getProfileColorByName (name: string): { bright: string, dark: st } ]; return colors[name.length % colors.length]; -} \ No newline at end of file +} diff --git a/src/services/pronote/attachment.ts b/src/services/pronote/attachment.ts index 424655cb4..c0e425b3c 100644 --- a/src/services/pronote/attachment.ts +++ b/src/services/pronote/attachment.ts @@ -7,4 +7,4 @@ export function decodeAttachment (a: pronote.Attachment): Attachment { url: a.url, type: a.kind === pronote.AttachmentKind.File ? AttachmentType.File : AttachmentType.Link }; -} \ No newline at end of file +} diff --git a/src/services/pronote/attendance.ts b/src/services/pronote/attendance.ts index 0d4526e24..cb360f3b4 100644 --- a/src/services/pronote/attendance.ts +++ b/src/services/pronote/attendance.ts @@ -112,4 +112,4 @@ export async function getAttendance (account: PronoteAccount, periodName: string }; return attendance; -} \ No newline at end of file +} diff --git a/src/services/pronote/dataset_geolocation.ts b/src/services/pronote/dataset_geolocation.ts index 9d1307a49..e243fc99e 100644 --- a/src/services/pronote/dataset_geolocation.ts +++ b/src/services/pronote/dataset_geolocation.ts @@ -62,4 +62,4 @@ const getInstancesFromDataset = async (longitude: number, latitude: number): Pro } }; -export default getInstancesFromDataset; \ No newline at end of file +export default getInstancesFromDataset; diff --git a/src/services/pronote/evaluations.ts b/src/services/pronote/evaluations.ts index be1868208..e6aa025fa 100644 --- a/src/services/pronote/evaluations.ts +++ b/src/services/pronote/evaluations.ts @@ -82,4 +82,4 @@ export const getLevel = (level: string): SkillLevel => { } }; -export const buildLocalID = (e: pronote.Evaluation): string => `${e.subject.name}:${e.date.getTime()}/${e.name}`; \ No newline at end of file +export const buildLocalID = (e: pronote.Evaluation): string => `${e.subject.name}:${e.date.getTime()}/${e.name}`; diff --git a/src/services/pronote/period.ts b/src/services/pronote/period.ts index 1a11508bf..d62870617 100644 --- a/src/services/pronote/period.ts +++ b/src/services/pronote/period.ts @@ -7,4 +7,4 @@ export function decodePeriod (p: pronote.Period): Period { startTimestamp: p.startDate.getTime(), endTimestamp: p.endDate.getTime() }; -} \ No newline at end of file +} diff --git a/src/services/pronote/timetable.ts b/src/services/pronote/timetable.ts index 0e8c86806..cab6002c9 100644 --- a/src/services/pronote/timetable.ts +++ b/src/services/pronote/timetable.ts @@ -128,4 +128,4 @@ export const getWeekFrequency = (account: PronoteAccount, weekNumber: number): W freqLabel: frequency.label, num: frequency.fortnight }; -}; \ No newline at end of file +}; diff --git a/src/services/qrcode.ts b/src/services/qrcode.ts index a73c368af..1a8b11d9e 100644 --- a/src/services/qrcode.ts +++ b/src/services/qrcode.ts @@ -21,4 +21,4 @@ export const qrcodeFromExternal = async (account: ExternalAccount): Promise; -} \ No newline at end of file +} diff --git a/src/services/shared/Homework.ts b/src/services/shared/Homework.ts index 339864413..a5a5f814c 100644 --- a/src/services/shared/Homework.ts +++ b/src/services/shared/Homework.ts @@ -17,4 +17,4 @@ export interface Homework { done: boolean returnType?: HomeworkReturnType exam?: boolean -} \ No newline at end of file +} diff --git a/src/services/shared/Recipient.ts b/src/services/shared/Recipient.ts index bc613b3e6..aa403f4d6 100644 --- a/src/services/shared/Recipient.ts +++ b/src/services/shared/Recipient.ts @@ -2,4 +2,4 @@ export interface Recipient { name: string subject?: string _handle: any -} \ No newline at end of file +} diff --git a/src/services/shared/Reel.ts b/src/services/shared/Reel.ts index 97fc642e0..499ae15c4 100644 --- a/src/services/shared/Reel.ts +++ b/src/services/shared/Reel.ts @@ -16,4 +16,4 @@ export interface Reel { outOf: string, coef: string, } -} \ No newline at end of file +} diff --git a/src/services/shared/ReservationHistory.ts b/src/services/shared/ReservationHistory.ts index e847712e2..479d1cf73 100644 --- a/src/services/shared/ReservationHistory.ts +++ b/src/services/shared/ReservationHistory.ts @@ -3,4 +3,4 @@ export interface ReservationHistory { timestamp: number currency: string label: string -} \ No newline at end of file +} diff --git a/src/services/shared/Timetable.ts b/src/services/shared/Timetable.ts index ea4f0e2fb..261a12e0c 100644 --- a/src/services/shared/Timetable.ts +++ b/src/services/shared/Timetable.ts @@ -41,4 +41,4 @@ export interface WeekFrequency { textLabel: string, freqLabel: string, num: number -} \ No newline at end of file +} diff --git a/src/services/skolengo/data/attachment.ts b/src/services/skolengo/data/attachment.ts index a80453ff2..37791df2e 100644 --- a/src/services/skolengo/data/attachment.ts +++ b/src/services/skolengo/data/attachment.ts @@ -7,4 +7,4 @@ export function decodeSkoAttachment (a: SkolengoAttachment): Attachment { url: a.url, type: AttachmentType.File }; -} \ No newline at end of file +} diff --git a/src/services/skolengo/data/period.ts b/src/services/skolengo/data/period.ts index 9d5503c5a..3fbae807e 100644 --- a/src/services/skolengo/data/period.ts +++ b/src/services/skolengo/data/period.ts @@ -13,4 +13,4 @@ export const getPeriod = async (account: SkolengoAccount) => { startTimestamp: new Date(p.startDate).getTime(), endTimestamp: new Date(p.endDate).getTime(), })); -}; \ No newline at end of file +}; diff --git a/src/services/skolengo/data/timetable.ts b/src/services/skolengo/data/timetable.ts index 9c7b2bb6e..246d26678 100644 --- a/src/services/skolengo/data/timetable.ts +++ b/src/services/skolengo/data/timetable.ts @@ -30,4 +30,4 @@ export const getTimetableForWeek = async (account: SkolengoAccount, epochWeekNum const lessons = agenda.map(e=>e.lessons).flat(); return lessons.map(decodeLesson); -}; \ No newline at end of file +}; diff --git a/src/services/skolengo/data/utils.ts b/src/services/skolengo/data/utils.ts index 1d8940e41..35534bad2 100644 --- a/src/services/skolengo/data/utils.ts +++ b/src/services/skolengo/data/utils.ts @@ -5,4 +5,4 @@ export const userToName = (user: User) => `${user.lastName} ${user.firstName}`; export const _skoUcFist = (s: string) => s.charAt(0).toUpperCase() + s.slice(1); -export const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); \ No newline at end of file +export const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); diff --git a/src/services/skolengo/default-personalization.ts b/src/services/skolengo/default-personalization.ts index 9f35930ab..406c9fd50 100644 --- a/src/services/skolengo/default-personalization.ts +++ b/src/services/skolengo/default-personalization.ts @@ -34,4 +34,4 @@ const getServiceConfig =(instance: SkolengoAccount["instance"])=> Promise.all([ export default defaultSkolengoPersonalization; export const checkIfSkoSupported = (account: SkolengoAccount, service: Tab): boolean => - account.personalization.tabs?.some(tab => tab.name === service && tab.enabled) || false; \ No newline at end of file + account.personalization.tabs?.some(tab => tab.name === service && tab.enabled) || false; diff --git a/src/services/skolengo/skolengo-types.ts b/src/services/skolengo/skolengo-types.ts index 999ffccb5..2ad3ea912 100644 --- a/src/services/skolengo/skolengo-types.ts +++ b/src/services/skolengo/skolengo-types.ts @@ -59,4 +59,4 @@ export const toSkolengoDate = (date: Date): string => (date.getMonth()+1).toString().padStart(2, "0") }-${ (date.getDate()).toString().padStart(2, "0") - }`; \ No newline at end of file + }`; diff --git a/src/services/timetable.ts b/src/services/timetable.ts index 2f553d746..df5377cff 100644 --- a/src/services/timetable.ts +++ b/src/services/timetable.ts @@ -75,4 +75,4 @@ export async function getWeekFrequency (account: T, epochWee default: return null; } -} \ No newline at end of file +} diff --git a/src/services/turboself/balance.ts b/src/services/turboself/balance.ts index ea1e8451f..2ae8ffcdf 100644 --- a/src/services/turboself/balance.ts +++ b/src/services/turboself/balance.ts @@ -16,4 +16,4 @@ export const getBalance = async (account: TurboselfAccount): Promise }); } return result; -}; \ No newline at end of file +}; diff --git a/src/services/turboself/booking.ts b/src/services/turboself/booking.ts index ae65f8912..b4d3f4bd1 100644 --- a/src/services/turboself/booking.ts +++ b/src/services/turboself/booking.ts @@ -25,4 +25,4 @@ export const bookDay = async (account: TurboselfAccount, id: string, date: Date, date: bookedDay.date, booked: bookedDay.booked, }; -}; \ No newline at end of file +}; diff --git a/src/stores/classSubject/index.ts b/src/stores/classSubject/index.ts index be482f9ce..e79c4b3f5 100644 --- a/src/stores/classSubject/index.ts +++ b/src/stores/classSubject/index.ts @@ -24,4 +24,4 @@ export const useClassSubjectStore = create()( storage: createJSONStorage(() => AsyncStorage), } ) -); \ No newline at end of file +); diff --git a/src/utils/GetRessources/GetContribs.tsx b/src/utils/GetRessources/GetContribs.tsx index b4d3a824d..51ffe0ebd 100644 --- a/src/utils/GetRessources/GetContribs.tsx +++ b/src/utils/GetRessources/GetContribs.tsx @@ -25,4 +25,4 @@ export async function getContributors (): Promise { console.error("Erreur lors de la récupération des contributeurs:", error); return []; } -} \ No newline at end of file +} diff --git a/src/utils/chat/themes/GetAvailableThemes.ts b/src/utils/chat/themes/GetAvailableThemes.ts index af6050c8e..b681607b0 100644 --- a/src/utils/chat/themes/GetAvailableThemes.ts +++ b/src/utils/chat/themes/GetAvailableThemes.ts @@ -14,4 +14,4 @@ async function GetAvailableThemes (): Promise { }); } -export default GetAvailableThemes; \ No newline at end of file +export default GetAvailableThemes; diff --git a/src/utils/chat/themes/GetThemeForChat.ts b/src/utils/chat/themes/GetThemeForChat.ts index 524516df4..d83b4e94a 100644 --- a/src/utils/chat/themes/GetThemeForChat.ts +++ b/src/utils/chat/themes/GetThemeForChat.ts @@ -89,4 +89,4 @@ async function GetThemeForChatId (chatId: string): Promise { return theme; } -export default GetThemeForChatId; \ No newline at end of file +export default GetThemeForChatId; diff --git a/src/utils/chat/themes/Themes.types.ts b/src/utils/chat/themes/Themes.types.ts index 39de9821c..f160d84a7 100644 --- a/src/utils/chat/themes/Themes.types.ts +++ b/src/utils/chat/themes/Themes.types.ts @@ -41,4 +41,4 @@ export interface Theme { lightModifier: ThemeModifier; darkModifier: ThemeModifier; exclusiveMeta: ThemeExclusiveMeta; -} \ No newline at end of file +} diff --git a/src/utils/files/getAndOpenFile.ts b/src/utils/files/getAndOpenFile.ts index 634ab9aff..090f06c12 100644 --- a/src/utils/files/getAndOpenFile.ts +++ b/src/utils/files/getAndOpenFile.ts @@ -8,4 +8,4 @@ export default async (account: EcoleDirecteAccount, fileSeed: string) => { const fileContent = await getFile(account, id, type); await saveFile(name, fileContent); await openFileInQL(name); -}; \ No newline at end of file +}; diff --git a/src/utils/files/openFileInQL.ts b/src/utils/files/openFileInQL.ts index 49187204c..a639974ea 100644 --- a/src/utils/files/openFileInQL.ts +++ b/src/utils/files/openFileInQL.ts @@ -3,4 +3,4 @@ import { cacheDirectory } from "expo-file-system"; export default async function openFileInQL (fileName: string) { await NativeModules.FilePreviewManager.openFile(cacheDirectory + fileName); -} \ No newline at end of file +} diff --git a/src/utils/files/saveFile.ts b/src/utils/files/saveFile.ts index f22cb64e9..2853069c7 100644 --- a/src/utils/files/saveFile.ts +++ b/src/utils/files/saveFile.ts @@ -2,4 +2,4 @@ import * as FileSystem from "expo-file-system"; export default async function saveFile (fileName: string, fileContent: string) { await FileSystem.writeAsStringAsync(`${FileSystem.cacheDirectory}${fileName}`, fileContent, { encoding: "base64" }); -} \ No newline at end of file +} diff --git a/src/utils/format/attendance_time.ts b/src/utils/format/attendance_time.ts index e93bd317a..41644cab0 100644 --- a/src/utils/format/attendance_time.ts +++ b/src/utils/format/attendance_time.ts @@ -14,4 +14,4 @@ const getAbsenceTime = (fromTimestamp: number, toTimestamp: number) => { }; }; -export { getAbsenceTime, leadingZero }; \ No newline at end of file +export { getAbsenceTime, leadingZero }; diff --git a/src/utils/format/course_duration.ts b/src/utils/format/course_duration.ts index 04388cf5c..5011e4d16 100644 --- a/src/utils/format/course_duration.ts +++ b/src/utils/format/course_duration.ts @@ -9,4 +9,4 @@ export const getDuration = (minutes: number): string => { } return `${durationHours}h ${lz(durationRemainingMinutes)}min`; -}; \ No newline at end of file +}; diff --git a/src/utils/format/format_cours_name.ts b/src/utils/format/format_cours_name.ts index c6f8809f0..9633b5ca0 100644 --- a/src/utils/format/format_cours_name.ts +++ b/src/utils/format/format_cours_name.ts @@ -83,4 +83,4 @@ function getCourseSpeciality (pronoteString = ""): string | null { } export default findObjectByPronoteString; -export { getCourseSpeciality }; \ No newline at end of file +export { getCourseSpeciality }; diff --git a/src/utils/format/format_date.ts b/src/utils/format/format_date.ts index 47aaa28a2..1922f388f 100644 --- a/src/utils/format/format_date.ts +++ b/src/utils/format/format_date.ts @@ -4,4 +4,4 @@ */ export const dateAsShortString = (date: Date): string => { return date.toLocaleDateString("en-GB"); -}; \ No newline at end of file +}; diff --git a/src/utils/format/format_pronote_initials.ts b/src/utils/format/format_pronote_initials.ts index 28b2a452d..d5cf486e7 100644 --- a/src/utils/format/format_pronote_initials.ts +++ b/src/utils/format/format_pronote_initials.ts @@ -10,4 +10,4 @@ function parse_initials (content: string) { return initials; } -export default parse_initials; \ No newline at end of file +export default parse_initials; diff --git a/src/utils/login/GetV6Data.ts b/src/utils/login/GetV6Data.ts index 61f2e9fc4..7c27489de 100644 --- a/src/utils/login/GetV6Data.ts +++ b/src/utils/login/GetV6Data.ts @@ -41,4 +41,4 @@ const GetV6Data = async () => { }; }; -export default GetV6Data; \ No newline at end of file +export default GetV6Data; diff --git a/src/utils/ui/animations.ts b/src/utils/ui/animations.ts index 11be7f53e..1caea48c7 100644 --- a/src/utils/ui/animations.ts +++ b/src/utils/ui/animations.ts @@ -76,4 +76,4 @@ export { anim2Papillon, PapillonContextEnter, PapillonContextExit, -}; \ No newline at end of file +}; diff --git a/src/utils/ui/colors.ts b/src/utils/ui/colors.ts index cab7f6267..0e158196c 100644 --- a/src/utils/ui/colors.ts +++ b/src/utils/ui/colors.ts @@ -22,4 +22,4 @@ export function adjustColor (hex: string, amount: number): string { catch (e) { return hex; } -} \ No newline at end of file +} diff --git a/src/views/account/Attendance/Atoms/TotalMissed.tsx b/src/views/account/Attendance/Atoms/TotalMissed.tsx index dcd7b8730..f7c1c108b 100644 --- a/src/views/account/Attendance/Atoms/TotalMissed.tsx +++ b/src/views/account/Attendance/Atoms/TotalMissed.tsx @@ -115,4 +115,4 @@ const TotalMissed = ({ totalMissed }: TotalMissedProps) => { ); }; -export default TotalMissed; \ No newline at end of file +export default TotalMissed; diff --git a/src/views/account/Evaluation/Latest/LatestEvaluations.tsx b/src/views/account/Evaluation/Latest/LatestEvaluations.tsx index 117ecc295..df19af1a0 100644 --- a/src/views/account/Evaluation/Latest/LatestEvaluations.tsx +++ b/src/views/account/Evaluation/Latest/LatestEvaluations.tsx @@ -57,4 +57,4 @@ const EvaluationsLatestList = (props: EvaluationsLatestListProps) => { ); }; -export default EvaluationsLatestList; \ No newline at end of file +export default EvaluationsLatestList; diff --git a/src/views/account/Evaluation/Subject/Subject.tsx b/src/views/account/Evaluation/Subject/Subject.tsx index 1de15fad3..5ff5c725c 100644 --- a/src/views/account/Evaluation/Subject/Subject.tsx +++ b/src/views/account/Evaluation/Subject/Subject.tsx @@ -49,4 +49,4 @@ const Subject: React.FC = ({ ); }; -export default Subject; \ No newline at end of file +export default Subject; diff --git a/src/views/account/Grades/Latest/LatestGrades.tsx b/src/views/account/Grades/Latest/LatestGrades.tsx index f97da9359..491a2a394 100644 --- a/src/views/account/Grades/Latest/LatestGrades.tsx +++ b/src/views/account/Grades/Latest/LatestGrades.tsx @@ -71,4 +71,4 @@ const GradesLatestList = (props: GradesLatestListProps) => { ); }; -export default GradesLatestList; \ No newline at end of file +export default GradesLatestList; diff --git a/src/views/account/Homeworks/Atoms/Loading.tsx b/src/views/account/Homeworks/Atoms/Loading.tsx index 3487cb610..6bf82d3d5 100644 --- a/src/views/account/Homeworks/Atoms/Loading.tsx +++ b/src/views/account/Homeworks/Atoms/Loading.tsx @@ -48,4 +48,4 @@ const HomeworksLoading = () => { ); }; -export default HomeworksLoading; \ No newline at end of file +export default HomeworksLoading; diff --git a/src/views/account/Homeworks/Atoms/NoHomeworks.tsx b/src/views/account/Homeworks/Atoms/NoHomeworks.tsx index 61da93bd5..be9b5e0c7 100644 --- a/src/views/account/Homeworks/Atoms/NoHomeworks.tsx +++ b/src/views/account/Homeworks/Atoms/NoHomeworks.tsx @@ -54,4 +54,4 @@ const HomeworksNoHomeworksItem = () => { ); }; -export default HomeworksNoHomeworksItem; \ No newline at end of file +export default HomeworksNoHomeworksItem; diff --git a/src/views/account/Restaurant/Cards/Card.tsx b/src/views/account/Restaurant/Cards/Card.tsx index 4671909ca..4c00da5f4 100644 --- a/src/views/account/Restaurant/Cards/Card.tsx +++ b/src/views/account/Restaurant/Cards/Card.tsx @@ -161,4 +161,4 @@ const styles = StyleSheet.create({ }, }); -export default MenuCard; \ No newline at end of file +export default MenuCard; diff --git a/src/views/account/Restaurant/Cards/StoreThemes.ts b/src/views/account/Restaurant/Cards/StoreThemes.ts index 42d3fc5bd..c2df7f6fa 100644 --- a/src/views/account/Restaurant/Cards/StoreThemes.ts +++ b/src/views/account/Restaurant/Cards/StoreThemes.ts @@ -61,4 +61,4 @@ export const STORE_THEMES = [ background: require("../../../../../assets/images/cards/Carte_Cover_Alise.png"), }, -]; \ No newline at end of file +]; diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index 829b22849..58bac772f 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -279,4 +279,4 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio } }; -export default RestaurantCardDetail; \ No newline at end of file +export default RestaurantCardDetail; diff --git a/src/views/account/Restaurant/Modals/PaymentSuccess.tsx b/src/views/account/Restaurant/Modals/PaymentSuccess.tsx index 536cfdb23..8440992e2 100644 --- a/src/views/account/Restaurant/Modals/PaymentSuccess.tsx +++ b/src/views/account/Restaurant/Modals/PaymentSuccess.tsx @@ -177,4 +177,4 @@ const RestaurantPaymentSuccess: Screen<"RestaurantPaymentSuccess"> = ({ route, n ); }; -export default RestaurantPaymentSuccess; \ No newline at end of file +export default RestaurantPaymentSuccess; diff --git a/src/views/account/Restaurant/Modals/QrCode.tsx b/src/views/account/Restaurant/Modals/QrCode.tsx index 914604778..7679f04f8 100644 --- a/src/views/account/Restaurant/Modals/QrCode.tsx +++ b/src/views/account/Restaurant/Modals/QrCode.tsx @@ -206,4 +206,4 @@ const RestaurantQrCode: Screen<"RestaurantQrCode"> = ({ route, navigation }) => ); }; -export default RestaurantQrCode; \ No newline at end of file +export default RestaurantQrCode; diff --git a/src/views/login/pronote/PronoteGeolocation.tsx b/src/views/login/pronote/PronoteGeolocation.tsx index 4d3961e2c..deaa58e67 100644 --- a/src/views/login/pronote/PronoteGeolocation.tsx +++ b/src/views/login/pronote/PronoteGeolocation.tsx @@ -115,4 +115,4 @@ const styles = StyleSheet.create({ }, }); -export default PronoteGeolocation; \ No newline at end of file +export default PronoteGeolocation; diff --git a/src/views/login/skolengo/SkolengoGeolocation.tsx b/src/views/login/skolengo/SkolengoGeolocation.tsx index c05dba55b..d6573cc86 100644 --- a/src/views/login/skolengo/SkolengoGeolocation.tsx +++ b/src/views/login/skolengo/SkolengoGeolocation.tsx @@ -116,4 +116,4 @@ const styles = StyleSheet.create({ }, }); -export default SkolengoGeolocation; \ No newline at end of file +export default SkolengoGeolocation; diff --git a/src/views/settings/SettingsAddons.tsx b/src/views/settings/SettingsAddons.tsx index c51bf57da..59e7deadd 100644 --- a/src/views/settings/SettingsAddons.tsx +++ b/src/views/settings/SettingsAddons.tsx @@ -370,4 +370,4 @@ const SettingsAddons: Screen<"SettingsAddons"> = () => { ); }; -export default SettingsAddons; \ No newline at end of file +export default SettingsAddons; diff --git a/src/views/settings/SettingsIcons.tsx b/src/views/settings/SettingsIcons.tsx index d8fb23402..a845c2e94 100644 --- a/src/views/settings/SettingsIcons.tsx +++ b/src/views/settings/SettingsIcons.tsx @@ -51,7 +51,7 @@ const SettingsIcons: Screen<"SettingsIcons"> = ({ navigation }) => { getIconName().then((icon) => { setIcon(icon); }); - }; + } }, []); const setNewIcon = (icon: Icon) => { @@ -66,7 +66,7 @@ const SettingsIcons: Screen<"SettingsIcons"> = ({ navigation }) => { setIcon(iconConstructName); } else { alertExpoGo(); - }; + } } else { if (!isExpoGo()) { @@ -74,7 +74,7 @@ const SettingsIcons: Screen<"SettingsIcons"> = ({ navigation }) => { setIcon(icon.id); } else { alertExpoGo(); - }; + } } }; diff --git a/src/views/welcome/AccountSelector.tsx b/src/views/welcome/AccountSelector.tsx index da24a46bc..11bb2e1ef 100644 --- a/src/views/welcome/AccountSelector.tsx +++ b/src/views/welcome/AccountSelector.tsx @@ -53,4 +53,4 @@ const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { ); }; -export default AccountSelector; \ No newline at end of file +export default AccountSelector; diff --git a/src/views/welcome/ColorSelector.tsx b/src/views/welcome/ColorSelector.tsx index af4323e0a..d5d384805 100644 --- a/src/views/welcome/ColorSelector.tsx +++ b/src/views/welcome/ColorSelector.tsx @@ -61,7 +61,7 @@ const ColorSelector: Screen<"ColorSelector"> = ({ route, navigation }) => { setIconName(iconConstructName); } }); - }; + } }; const ColorButton: React.FC<{ color: Color }> = ({ color }) => ( diff --git a/src/widgets/Components/LastGrade.tsx b/src/widgets/Components/LastGrade.tsx index 747951178..991b36727 100644 --- a/src/widgets/Components/LastGrade.tsx +++ b/src/widgets/Components/LastGrade.tsx @@ -167,4 +167,4 @@ const LastGradeWidget = forwardRef(({ ); }); -export default LastGradeWidget; \ No newline at end of file +export default LastGradeWidget; diff --git a/src/widgets/index.tsx b/src/widgets/index.tsx index b3656867d..00e1bfa18 100644 --- a/src/widgets/index.tsx +++ b/src/widgets/index.tsx @@ -10,4 +10,4 @@ export const Widgets = [ NextCourseWidget, GeneralAverageWidget, LastGradeWidget, -]; \ No newline at end of file +]; From 86295885d8b230b55a13009f3f460f31eac1b1b6 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Fri, 14 Mar 2025 21:54:03 +0100 Subject: [PATCH 0856/1144] chore(lint): imports --- App.tsx | 2 +- eslint.config.js | 1 + src/background/BackgroundTasks.ts | 2 +- src/components/Global/AccountItem.tsx | 10 +-- src/components/Global/LinkFavicon.tsx | 2 +- src/components/Global/PapillonHeader.tsx | 1 - src/components/Modals/PapillonBottomSheet.tsx | 2 +- src/components/Restaurant/AccountButton.tsx | 4 +- src/consts/DefaultTheme.ts | 2 +- src/router/helpers/types.ts | 2 +- src/services/attendance.ts | 6 +- src/services/balance.ts | 4 +- src/services/chats.ts | 6 +- src/services/ecoledirecte/attendance.ts | 10 +-- src/services/ecoledirecte/timetable.ts | 6 +- src/services/evaluation.ts | 4 +- src/services/grades.ts | 4 +- src/services/homework.ts | 6 +- src/services/izly/history.ts | 2 +- src/services/izly/qrcode.ts | 2 +- src/services/izly/reload.ts | 2 +- src/services/multi/data/timetable.ts | 2 +- src/services/news.ts | 6 +- .../determinate-authentication-view.tsx | 2 +- src/services/pronote/evaluations.ts | 12 +-- src/services/pronote/homework.ts | 2 +- src/services/reload-account.ts | 4 +- src/services/skolengo/data/attachment.ts | 2 +- src/services/skolengo/data/homework.ts | 2 +- src/services/skolengo/skolengo-account.ts | 2 +- src/services/timetable.ts | 4 +- src/stores/account/index.ts | 6 +- src/stores/account/types.ts | 4 +- src/stores/evaluation/index.ts | 2 +- src/stores/evaluation/types.ts | 2 +- src/stores/multiService/index.ts | 2 +- src/stores/multiService/types.ts | 2 +- src/utils/chat/themes/GetAvailableThemes.ts | 2 +- src/utils/chat/themes/GetThemeForChat.ts | 4 +- src/utils/multiservice/index.ts | 8 +- src/views/account/Attendance/Attendance.tsx | 35 ++++---- src/views/account/Chat/Messages.tsx | 4 +- src/views/account/Chat/Modals/Chat.tsx | 12 +-- src/views/account/Chat/Modals/ChatDetails.tsx | 14 +-- src/views/account/Chat/Modals/ChatThemes.tsx | 2 +- .../Evaluation/Atoms/SkillLevelBadge.tsx | 10 +-- src/views/account/Evaluation/Evaluation.tsx | 30 +++---- .../Evaluation/Latest/LatestEvaluations.tsx | 2 +- .../Latest/LatestEvaluationsItem.tsx | 4 +- .../Evaluation/Subject/EvaluationList.tsx | 4 +- .../account/Evaluation/Subject/Subject.tsx | 2 +- src/views/account/Grades/Atoms/GradeTitle.tsx | 2 +- src/views/account/Grades/Document.tsx | 6 +- src/views/account/Grades/Grades.tsx | 4 +- .../account/Grades/Subject/SubjectTitle.tsx | 6 +- .../Home/Elements/HomeworksElement.tsx | 6 +- src/views/account/Home/ModalContent.tsx | 26 +++--- src/views/account/Homeworks/Homeworks.tsx | 14 +-- .../Lessons/Atoms/LessonsDatePicker.tsx | 6 +- src/views/account/Lessons/Lessons.tsx | 6 +- src/views/account/Lessons/LessonsHeader.tsx | 2 +- src/views/account/News/News.tsx | 8 +- src/views/account/Restaurant/Menu.tsx | 86 ++++++++----------- src/views/account/Week/Week.tsx | 4 +- src/views/addon/AddonLogs.tsx | 10 +-- src/views/login/ServiceSelector.tsx | 1 - .../ecoledirecte/EcoleDirecteCredentials.tsx | 4 +- .../login/pronote/PronoteInstanceSelector.tsx | 2 +- .../skolengo/SkolengoInstanceSelector.tsx | 1 - src/views/settings/ExternalAccount/ARD.tsx | 2 +- .../ExternalAccount/IzlyActivation.tsx | 18 ++-- .../ExternalAccount/QrcodeScanner.tsx | 2 - .../ExternalAccount/ServiceSelector.tsx | 3 +- .../TurboselfAccountSelector.tsx | 12 +-- src/views/settings/Settings.tsx | 4 +- src/views/settings/SettingsAccessibility.tsx | 2 +- src/views/settings/SettingsAddons.tsx | 10 +-- src/views/settings/SettingsFlagsInfos.tsx | 2 +- src/views/settings/SettingsMultiService.tsx | 28 +++--- .../settings/SettingsMultiServiceSpace.tsx | 24 +++--- src/widgets/Components/RestaurantQRCode.tsx | 6 +- 81 files changed, 277 insertions(+), 299 deletions(-) diff --git a/App.tsx b/App.tsx index 146330649..5a9c8bf36 100644 --- a/App.tsx +++ b/App.tsx @@ -5,7 +5,7 @@ import { LogBox, AppState, AppStateStatus } from "react-native"; import React, { useEffect, useState, useRef, useCallback } from "react"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { useAccounts, useCurrentAccount } from "@/stores/account"; -import {AccountService, PrimaryAccount} from "@/stores/account/types"; +import { AccountService, PrimaryAccount } from "@/stores/account/types"; import { log } from "@/utils/logger/logger"; import { isExpoGo } from "@/utils/native/expoGoAlert"; import { atobPolyfill, btoaPolyfill } from "js-base64"; diff --git a/eslint.config.js b/eslint.config.js index 46eb149f3..6c87a13c2 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -44,6 +44,7 @@ module.exports = [ "@stylistic/no-extra-semi": "error", "@stylistic/no-mixed-spaces-and-tabs": "error", "unused-imports/no-unused-imports": "error", + "@stylistic/object-curly-spacing": ["error", "always"], "no-unused-vars": ["error", { "args": "none" }], diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 81d901725..c0846d6f5 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -5,7 +5,7 @@ import { isExpoGo } from "@/utils/native/expoGoAlert"; import { useAccounts, useCurrentAccount } from "@/stores/account"; import { fetchNews } from "./data/News"; -import {PrimaryAccount} from "@/stores/account/types"; +import { PrimaryAccount } from "@/stores/account/types"; /** * Background fetch function that fetches all the data diff --git a/src/components/Global/AccountItem.tsx b/src/components/Global/AccountItem.tsx index 06a027d4b..c84ae759c 100644 --- a/src/components/Global/AccountItem.tsx +++ b/src/components/Global/AccountItem.tsx @@ -1,15 +1,15 @@ -import {Image, StyleProp, Text, View, ViewStyle} from "react-native"; +import { Image, StyleProp, Text, View, ViewStyle } from "react-native"; import { animPapillon } from "@/utils/ui/animations"; import Reanimated, { FadeOut, ZoomIn } from "react-native-reanimated"; -import {PrimaryAccount, AccountService} from "@/stores/account/types"; -import {defaultProfilePicture} from "@/utils/ui/default-profile-picture"; -import {Check} from "lucide-react-native"; +import { PrimaryAccount, AccountService } from "@/stores/account/types"; +import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; +import { Check } from "lucide-react-native"; import React from "react"; -import {useTheme} from "@react-navigation/native"; +import { useTheme } from "@react-navigation/native"; const AccountItem: React.FC<{ account: PrimaryAccount, diff --git a/src/components/Global/LinkFavicon.tsx b/src/components/Global/LinkFavicon.tsx index c6cf8321b..2b286233a 100644 --- a/src/components/Global/LinkFavicon.tsx +++ b/src/components/Global/LinkFavicon.tsx @@ -1,4 +1,4 @@ -import {Image, ImageStyle, StyleProp, View} from "react-native"; +import { Image, ImageStyle, StyleProp, View } from "react-native"; import * as FileSystem from "expo-file-system"; import { useEffect, useState } from "react"; import { Link2 } from "lucide-react-native"; diff --git a/src/components/Global/PapillonHeader.tsx b/src/components/Global/PapillonHeader.tsx index ffaa633ff..e1cea6d2d 100644 --- a/src/components/Global/PapillonHeader.tsx +++ b/src/components/Global/PapillonHeader.tsx @@ -1,7 +1,6 @@ import React from "react"; import { Platform, View } from "react-native"; import { TabAnimatedTitleLeft, TabAnimatedTitleRight } from "./TabAnimatedTitle"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; import { TouchableOpacity } from "react-native-gesture-handler"; import { ArrowLeft } from "lucide-react-native"; import { type RouteProp, useTheme } from "@react-navigation/native"; diff --git a/src/components/Modals/PapillonBottomSheet.tsx b/src/components/Modals/PapillonBottomSheet.tsx index 6c1e4681a..c070868e7 100644 --- a/src/components/Modals/PapillonBottomSheet.tsx +++ b/src/components/Modals/PapillonBottomSheet.tsx @@ -1,5 +1,5 @@ import { useTheme } from "@react-navigation/native"; -import React, {useCallback} from "react"; +import React, { useCallback } from "react"; import { KeyboardAvoidingView, Modal, Pressable } from "react-native"; import { Gesture, GestureDetector } from "react-native-gesture-handler"; diff --git a/src/components/Restaurant/AccountButton.tsx b/src/components/Restaurant/AccountButton.tsx index d30e4eb47..5ef6a0691 100644 --- a/src/components/Restaurant/AccountButton.tsx +++ b/src/components/Restaurant/AccountButton.tsx @@ -2,8 +2,8 @@ import { Coffee, Utensils } from "lucide-react-native"; import React from "react"; import { View, TouchableOpacity } from "react-native"; import Reanimated from "react-native-reanimated"; -import {Balance} from "@/services/shared/Balance"; -import {Theme} from "@react-navigation/native"; +import { Balance } from "@/services/shared/Balance"; +import { Theme } from "@react-navigation/native"; const AnimatedTouchableOpacity = Reanimated.createAnimatedComponent(TouchableOpacity); const AnimatedView = Reanimated.createAnimatedComponent(View); diff --git a/src/consts/DefaultTheme.ts b/src/consts/DefaultTheme.ts index aa79fce37..01ef24999 100644 --- a/src/consts/DefaultTheme.ts +++ b/src/consts/DefaultTheme.ts @@ -1,4 +1,4 @@ -import {Theme} from "@/utils/chat/themes/Themes.types"; +import { Theme } from "@/utils/chat/themes/Themes.types"; export const DefaultTheme: Theme = { meta: { diff --git a/src/router/helpers/types.ts b/src/router/helpers/types.ts index f693832f0..8646c5217 100644 --- a/src/router/helpers/types.ts +++ b/src/router/helpers/types.ts @@ -15,7 +15,7 @@ import { Host } from "turboself-api"; import { Evaluation } from "@/services/shared/Evaluation"; import { ThemesMeta } from "@/utils/chat/themes/Themes.types"; import { ServiceCard } from "@/views/account/Restaurant/Menu"; -import {MultiServiceSpace} from "@/stores/multiService/types"; +import { MultiServiceSpace } from "@/stores/multiService/types"; export type RouteParameters = { // welcome.index diff --git a/src/services/attendance.ts b/src/services/attendance.ts index b334c63a0..6bde11b5c 100644 --- a/src/services/attendance.ts +++ b/src/services/attendance.ts @@ -3,9 +3,9 @@ import type { Period } from "./shared/Period"; import { useAttendanceStore } from "@/stores/attendance"; import { Attendance } from "./shared/Attendance"; import { checkIfSkoSupported } from "./skolengo/default-personalization"; -import {error, log} from "@/utils/logger/logger"; -import {MultiServiceFeature} from "@/stores/multiService/types"; -import {getFeatureAccount} from "@/utils/multiservice"; +import { error, log } from "@/utils/logger/logger"; +import { MultiServiceFeature } from "@/stores/multiService/types"; +import { getFeatureAccount } from "@/utils/multiservice"; export async function updateAttendancePeriodsInCache (account: T): Promise { let periods: Period[] = []; diff --git a/src/services/balance.ts b/src/services/balance.ts index 4267815f6..8c0bd0929 100644 --- a/src/services/balance.ts +++ b/src/services/balance.ts @@ -1,5 +1,5 @@ -import {AccountService, type ExternalAccount} from "@/stores/account/types"; -import type {Balance} from "./shared/Balance"; +import { AccountService, type ExternalAccount } from "@/stores/account/types"; +import type { Balance } from "./shared/Balance"; export const balanceFromExternal = async (account: ExternalAccount, force = false): Promise => { switch (account.service) { diff --git a/src/services/chats.ts b/src/services/chats.ts index 49489b602..07b06a293 100644 --- a/src/services/chats.ts +++ b/src/services/chats.ts @@ -1,9 +1,9 @@ import { type Account, AccountService } from "@/stores/account/types"; import type { Chat, ChatMessage, ChatRecipient } from "./shared/Chat"; import type { Recipient } from "./shared/Recipient"; -import {getFeatureAccount} from "@/utils/multiservice"; -import {MultiServiceFeature} from "@/stores/multiService/types"; -import {log} from "@/utils/logger/logger"; +import { getFeatureAccount } from "@/utils/multiservice"; +import { MultiServiceFeature } from "@/stores/multiService/types"; +import { log } from "@/utils/logger/logger"; export const getChats = async (account: T): Promise> => { switch (account.service) { diff --git a/src/services/ecoledirecte/attendance.ts b/src/services/ecoledirecte/attendance.ts index 882979d31..1d43b199c 100644 --- a/src/services/ecoledirecte/attendance.ts +++ b/src/services/ecoledirecte/attendance.ts @@ -1,11 +1,11 @@ -import ecoledirecte, {AttendanceItem, AttendanceItemKind} from "pawdirecte"; +import ecoledirecte, { AttendanceItem, AttendanceItemKind } from "pawdirecte"; import type { EcoleDirecteAccount } from "@/stores/account/types"; import { ErrorServiceUnauthenticated } from "../shared/errors"; import type { Attendance } from "../shared/Attendance"; -import {dateStringAsTimeInterval, getDuration, getDurationInHours} from "@/services/ecoledirecte/time-interval"; -import {Punishment} from "@/services/shared/Punishment"; -import {Absence} from "@/services/shared/Absence"; -import {Delay} from "@/services/shared/Delay"; +import { dateStringAsTimeInterval, getDuration, getDurationInHours } from "@/services/ecoledirecte/time-interval"; +import { Punishment } from "@/services/shared/Punishment"; +import { Absence } from "@/services/shared/Absence"; +import { Delay } from "@/services/shared/Delay"; const decodeDelay = (item: AttendanceItem): Delay => { const timeInterval = dateStringAsTimeInterval(item.displayDate); diff --git a/src/services/ecoledirecte/timetable.ts b/src/services/ecoledirecte/timetable.ts index b3a6ca7b0..76de5e648 100644 --- a/src/services/ecoledirecte/timetable.ts +++ b/src/services/ecoledirecte/timetable.ts @@ -1,6 +1,6 @@ -import type {EcoleDirecteAccount} from "@/stores/account/types"; -import {Timetable, TimetableClass, TimetableClassStatus} from "../shared/Timetable"; -import {ErrorServiceUnauthenticated} from "../shared/errors"; +import type { EcoleDirecteAccount } from "@/stores/account/types"; +import { Timetable, TimetableClass, TimetableClassStatus } from "../shared/Timetable"; +import { ErrorServiceUnauthenticated } from "../shared/errors"; import ecoledirecte, { TimetableItemKind } from "pawdirecte"; const decodeTimetableClass = (c: ecoledirecte.TimetableItem): TimetableClass => { diff --git a/src/services/evaluation.ts b/src/services/evaluation.ts index 669a48907..8008ae805 100644 --- a/src/services/evaluation.ts +++ b/src/services/evaluation.ts @@ -1,7 +1,7 @@ import { type Account, AccountService } from "@/stores/account/types"; import type { Period } from "./shared/Period"; -import {getFeatureAccount} from "@/utils/multiservice"; -import {MultiServiceFeature} from "@/stores/multiService/types"; +import { getFeatureAccount } from "@/utils/multiservice"; +import { MultiServiceFeature } from "@/stores/multiService/types"; import { useEvaluationStore } from "@/stores/evaluation"; import { Evaluation } from "@/services/shared/Evaluation"; import { error, log } from "@/utils/logger/logger"; diff --git a/src/services/grades.ts b/src/services/grades.ts index 602bcb677..9d05966e8 100644 --- a/src/services/grades.ts +++ b/src/services/grades.ts @@ -4,8 +4,8 @@ import type { Period } from "./shared/Period"; import type { AverageOverview, Grade } from "./shared/Grade"; import { error, log } from "@/utils/logger/logger"; import { checkIfSkoSupported } from "./skolengo/default-personalization"; -import {MultiServiceFeature} from "@/stores/multiService/types"; -import {getFeatureAccount} from "@/utils/multiservice"; +import { MultiServiceFeature } from "@/stores/multiService/types"; +import { getFeatureAccount } from "@/utils/multiservice"; const getDefaultPeriod = (periods: Period[]): string => { const now = Date.now(); diff --git a/src/services/homework.ts b/src/services/homework.ts index 447bddfde..1bc5cff6d 100644 --- a/src/services/homework.ts +++ b/src/services/homework.ts @@ -1,14 +1,14 @@ import { type Account, AccountService } from "@/stores/account/types"; import { useHomeworkStore } from "@/stores/homework"; import type { Homework } from "./shared/Homework"; -import {error, log} from "@/utils/logger/logger"; +import { error, log } from "@/utils/logger/logger"; import { translateToWeekNumber } from "pawnote"; import { pronoteFirstDate } from "./pronote/timetable"; import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; import { checkIfSkoSupported } from "./skolengo/default-personalization"; import { useClassSubjectStore } from "@/stores/classSubject"; -import {MultiServiceFeature} from "@/stores/multiService/types"; -import {getFeatureAccount} from "@/utils/multiservice"; +import { MultiServiceFeature } from "@/stores/multiService/types"; +import { getFeatureAccount } from "@/utils/multiservice"; /** * Updates the state and cache for the homework of given week number. diff --git a/src/services/izly/history.ts b/src/services/izly/history.ts index b768c9506..856b6e6d5 100644 --- a/src/services/izly/history.ts +++ b/src/services/izly/history.ts @@ -1,6 +1,6 @@ import type { IzlyAccount } from "@/stores/account/types"; import type { ReservationHistory } from "../shared/ReservationHistory"; -import { operations, OperationKind} from "ezly"; +import { operations, OperationKind } from "ezly"; export const history = async (account: IzlyAccount): Promise => { const payments = await operations(account.instance!, OperationKind.Payment, 10); diff --git a/src/services/izly/qrcode.ts b/src/services/izly/qrcode.ts index 58abdc455..354109b7c 100644 --- a/src/services/izly/qrcode.ts +++ b/src/services/izly/qrcode.ts @@ -1,5 +1,5 @@ import type { IzlyAccount } from "@/stores/account/types"; -import {qrPay} from "ezly"; +import { qrPay } from "ezly"; export const getQRCode = async (account: IzlyAccount): Promise => { const cardNumber = qrPay(account.instance!); diff --git a/src/services/izly/reload.ts b/src/services/izly/reload.ts index d38cbb608..18b6887c9 100644 --- a/src/services/izly/reload.ts +++ b/src/services/izly/reload.ts @@ -1,6 +1,6 @@ import type { IzlyAccount } from "@/stores/account/types"; import { log } from "@/utils/logger/logger"; -import {Identification, refresh} from "ezly"; +import { Identification, refresh } from "ezly"; export const reload = async (account: IzlyAccount): Promise => { const instance = account.authentication.identification; diff --git a/src/services/multi/data/timetable.ts b/src/services/multi/data/timetable.ts index 9891e4ab0..2d32e0b5c 100644 --- a/src/services/multi/data/timetable.ts +++ b/src/services/multi/data/timetable.ts @@ -1,5 +1,5 @@ import type { MultiAccount } from "@/stores/account/types"; -import { TimetableClassStatus, type Timetable, type TimetableClass} from "../../shared/Timetable"; +import { TimetableClassStatus, type Timetable, type TimetableClass } from "../../shared/Timetable"; import { weekNumberToDateRange } from "@/utils/epochWeekNumber"; import type { EventResponse } from "esup-multi.js"; import { ErrorServiceUnauthenticated } from "@/services/shared/errors"; diff --git a/src/services/news.ts b/src/services/news.ts index f76cb58ca..ef919e2dc 100644 --- a/src/services/news.ts +++ b/src/services/news.ts @@ -2,10 +2,10 @@ import { type Account, AccountService } from "@/stores/account/types"; import { useNewsStore } from "@/stores/news"; import type { Information } from "./shared/Information"; import { checkIfSkoSupported } from "./skolengo/default-personalization"; -import {error, log} from "@/utils/logger/logger"; +import { error, log } from "@/utils/logger/logger"; import { newsRead } from "pawnote"; -import {MultiServiceFeature} from "@/stores/multiService/types"; -import {getFeatureAccount} from "@/utils/multiservice"; +import { MultiServiceFeature } from "@/stores/multiService/types"; +import { getFeatureAccount } from "@/utils/multiservice"; /** diff --git a/src/services/pronote/determinate-authentication-view.tsx b/src/services/pronote/determinate-authentication-view.tsx index 8cf3bcd77..209e738d9 100644 --- a/src/services/pronote/determinate-authentication-view.tsx +++ b/src/services/pronote/determinate-authentication-view.tsx @@ -2,7 +2,7 @@ import type { NativeStackNavigationProp } from "@react-navigation/native-stack"; import type { RouteParameters } from "@/router/helpers/types"; import { KeyRound, LockKeyhole } from "lucide-react-native"; import pronote from "pawnote"; -import {info, warn} from "@/utils/logger/logger"; +import { info, warn } from "@/utils/logger/logger"; import type { Alert } from "@/providers/AlertProvider"; /** diff --git a/src/services/pronote/evaluations.ts b/src/services/pronote/evaluations.ts index e6aa025fa..3c6e0a655 100644 --- a/src/services/pronote/evaluations.ts +++ b/src/services/pronote/evaluations.ts @@ -1,10 +1,10 @@ -import type {PronoteAccount} from "@/stores/account/types"; -import type {Period} from "@/services/shared/Period"; -import {info} from "@/utils/logger/logger"; -import {decodePeriod} from "@/services/pronote/period"; +import type { PronoteAccount } from "@/stores/account/types"; +import type { Period } from "@/services/shared/Period"; +import { info } from "@/utils/logger/logger"; +import { decodePeriod } from "@/services/pronote/period"; import pronote from "pawnote"; -import {ErrorServiceUnauthenticated} from "@/services/shared/errors"; -import {Evaluation, SkillLevel} from "@/services/shared/Evaluation"; +import { ErrorServiceUnauthenticated } from "@/services/shared/errors"; +import { Evaluation, SkillLevel } from "@/services/shared/Evaluation"; const getTab = (account: PronoteAccount): pronote.Tab => { if (!account.instance) diff --git a/src/services/pronote/homework.ts b/src/services/pronote/homework.ts index 30b368869..e4db91553 100644 --- a/src/services/pronote/homework.ts +++ b/src/services/pronote/homework.ts @@ -3,7 +3,7 @@ import { type Homework, HomeworkReturnType } from "@/services/shared/Homework"; import { ErrorServiceUnauthenticated } from "../shared/errors"; import { decodeAttachment } from "./attachment"; import pronote from "pawnote"; -import {info, log} from "@/utils/logger/logger"; +import { info, log } from "@/utils/logger/logger"; const decodeHomework = (h: pronote.Assignment): Homework => { return { diff --git a/src/services/reload-account.ts b/src/services/reload-account.ts index f10dfa744..d45dad3eb 100644 --- a/src/services/reload-account.ts +++ b/src/services/reload-account.ts @@ -1,5 +1,5 @@ -import {type Account, AccountService} from "@/stores/account/types"; -import {warn} from "@/utils/logger/logger"; +import { type Account, AccountService } from "@/stores/account/types"; +import { warn } from "@/utils/logger/logger"; export interface Reconnected { instance: T["instance"] authentication: T["authentication"] diff --git a/src/services/skolengo/data/attachment.ts b/src/services/skolengo/data/attachment.ts index 37791df2e..a4e5722b6 100644 --- a/src/services/skolengo/data/attachment.ts +++ b/src/services/skolengo/data/attachment.ts @@ -1,4 +1,4 @@ -import {Attachment as SkolengoAttachment} from "scolengo-api/types/models/School"; +import { Attachment as SkolengoAttachment } from "scolengo-api/types/models/School"; import { AttachmentType, type Attachment } from "../../shared/Attachment"; export function decodeSkoAttachment (a: SkolengoAttachment): Attachment { diff --git a/src/services/skolengo/data/homework.ts b/src/services/skolengo/data/homework.ts index 5a0d6f1c1..7dba43ec5 100644 --- a/src/services/skolengo/data/homework.ts +++ b/src/services/skolengo/data/homework.ts @@ -1,6 +1,6 @@ import type { SkolengoAccount } from "@/stores/account/types"; import { type Homework, HomeworkReturnType } from "@/services/shared/Homework"; -import {info, log} from "@/utils/logger/logger"; +import { info, log } from "@/utils/logger/logger"; import { weekNumberToDateRange } from "@/utils/epochWeekNumber"; import { HomeworkAssignment } from "scolengo-api/types/models/Calendar"; import { htmlToText } from "html-to-text"; diff --git a/src/services/skolengo/skolengo-account.ts b/src/services/skolengo/skolengo-account.ts index 6d73da973..69984feca 100644 --- a/src/services/skolengo/skolengo-account.ts +++ b/src/services/skolengo/skolengo-account.ts @@ -3,7 +3,7 @@ import { SkolengoAuthConfig, SkolengoJWT, SkolengoTokenSet, authTokenToSkolengoT import { DiscoveryDocument } from "expo-auth-session"; import { SkolengoAccount, AccountService } from "@/stores/account/types"; import axios, { type AxiosResponse } from "axios"; -import { decode as b64decode, encode as b64encode} from "js-base64"; +import { decode as b64decode, encode as b64encode } from "js-base64"; import { Alert } from "react-native"; import { decode as htmlDecode } from "html-entities"; import { useCurrentAccount } from "@/stores/account"; diff --git a/src/services/timetable.ts b/src/services/timetable.ts index df5377cff..242b994fd 100644 --- a/src/services/timetable.ts +++ b/src/services/timetable.ts @@ -3,8 +3,8 @@ import { useTimetableStore } from "@/stores/timetable"; import { epochWNToPronoteWN, weekNumberToDateRange } from "@/utils/epochWeekNumber"; import { checkIfSkoSupported } from "./skolengo/default-personalization"; import { error, log } from "@/utils/logger/logger"; -import {MultiServiceFeature} from "@/stores/multiService/types"; -import {getFeatureAccount} from "@/utils/multiservice"; +import { MultiServiceFeature } from "@/stores/multiService/types"; +import { getFeatureAccount } from "@/utils/multiservice"; import { WeekFrequency } from "./shared/Timetable"; /** diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index 83280bcce..721fb31ae 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -17,9 +17,9 @@ import { useHomeworkStore } from "../homework"; import { useGradesStore } from "../grades"; import { useNewsStore } from "../news"; import { useAttendanceStore } from "../attendance"; -import {error, info, log} from "@/utils/logger/logger"; -import {useMultiService} from "@/stores/multiService"; -import {MultiServiceFeature, MultiServiceSpace} from "@/stores/multiService/types"; +import { error, info, log } from "@/utils/logger/logger"; +import { useMultiService } from "@/stores/multiService"; +import { MultiServiceFeature, MultiServiceSpace } from "@/stores/multiService/types"; /** * Store for the currently selected account. diff --git a/src/stores/account/types.ts b/src/stores/account/types.ts index cef2a5125..d0089120c 100644 --- a/src/stores/account/types.ts +++ b/src/stores/account/types.ts @@ -4,11 +4,11 @@ import type { Client as ARDClient } from "pawrd"; import { Client as TurboselfClient } from "turboself-api"; import { Client as AliseClient, BookingDay } from "alise-api"; import type ScolengoAPI from "scolengo-api"; -import {Configuration, Identification} from "ezly"; +import { Configuration, Identification } from "ezly"; import type MultiAPI from "esup-multi.js"; import { SkolengoAuthConfig } from "@/services/skolengo/skolengo-types"; import { User as ScolengoAPIUser } from "scolengo-api/types/models/Common"; -import {OnlinePayments} from "pawrd/dist"; +import { OnlinePayments } from "pawrd/dist"; export interface Tab { name: string diff --git a/src/stores/evaluation/index.ts b/src/stores/evaluation/index.ts index e38121c69..fea07ee10 100644 --- a/src/stores/evaluation/index.ts +++ b/src/stores/evaluation/index.ts @@ -1,7 +1,7 @@ import AsyncStorage from "@react-native-async-storage/async-storage"; import { createJSONStorage, persist } from "zustand/middleware"; import { create } from "zustand"; -import {EvaluationStore} from "@/stores/evaluation/types"; +import { EvaluationStore } from "@/stores/evaluation/types"; export const useEvaluationStore = create()( persist( diff --git a/src/stores/evaluation/types.ts b/src/stores/evaluation/types.ts index 0f027d2a0..854e4fecf 100644 --- a/src/stores/evaluation/types.ts +++ b/src/stores/evaluation/types.ts @@ -1,5 +1,5 @@ import type { Period } from "@/services/shared/Period"; -import {Evaluation} from "@/services/shared/Evaluation"; +import { Evaluation } from "@/services/shared/Evaluation"; export interface EvaluationStore { lastUpdated: number diff --git a/src/stores/multiService/index.ts b/src/stores/multiService/index.ts index 6653263a4..3a3e5794f 100644 --- a/src/stores/multiService/index.ts +++ b/src/stores/multiService/index.ts @@ -2,7 +2,7 @@ import AsyncStorage from "@react-native-async-storage/async-storage"; import { createJSONStorage, persist } from "zustand/middleware"; import { create } from "zustand"; import { log } from "@/utils/logger/logger"; -import {MultiServiceSpace, MultiServiceStore} from "@/stores/multiService/types"; +import { MultiServiceSpace, MultiServiceStore } from "@/stores/multiService/types"; /** diff --git a/src/stores/multiService/types.ts b/src/stores/multiService/types.ts index 156e2cfd8..cda8b5891 100644 --- a/src/stores/multiService/types.ts +++ b/src/stores/multiService/types.ts @@ -1,4 +1,4 @@ -import {PapillonMultiServiceSpace, PrimaryAccount} from "@/stores/account/types"; +import { PapillonMultiServiceSpace, PrimaryAccount } from "@/stores/account/types"; export enum MultiServiceFeature { Grades = "grades", diff --git a/src/utils/chat/themes/GetAvailableThemes.ts b/src/utils/chat/themes/GetAvailableThemes.ts index b681607b0..a4dcde222 100644 --- a/src/utils/chat/themes/GetAvailableThemes.ts +++ b/src/utils/chat/themes/GetAvailableThemes.ts @@ -1,4 +1,4 @@ -import {ThemesMeta} from "@/utils/chat/themes/Themes.types"; +import { ThemesMeta } from "@/utils/chat/themes/Themes.types"; async function GetAvailableThemes (): Promise { let f = await fetch("https://raw.githubusercontent.com/PapillonApp/datasets/refs/heads/main/themes/themes.json"); diff --git a/src/utils/chat/themes/GetThemeForChat.ts b/src/utils/chat/themes/GetThemeForChat.ts index d83b4e94a..0b9989bfa 100644 --- a/src/utils/chat/themes/GetThemeForChat.ts +++ b/src/utils/chat/themes/GetThemeForChat.ts @@ -1,5 +1,5 @@ -import {Theme} from "@/utils/chat/themes/Themes.types"; -import {DefaultTheme} from "@/consts/DefaultTheme"; +import { Theme } from "@/utils/chat/themes/Themes.types"; +import { DefaultTheme } from "@/consts/DefaultTheme"; import AsyncStorage from "@react-native-async-storage/async-storage"; async function GetThemeForChatId (chatId: string): Promise { diff --git a/src/utils/multiservice/index.ts b/src/utils/multiservice/index.ts index a865a52a9..6f5fae834 100644 --- a/src/utils/multiservice/index.ts +++ b/src/utils/multiservice/index.ts @@ -1,7 +1,7 @@ -import {MultiServiceFeature} from "@/stores/multiService/types"; -import {useCurrentAccount} from "@/stores/account"; -import {useMultiService} from "@/stores/multiService"; -import {PrimaryAccount} from "@/stores/account/types"; +import { MultiServiceFeature } from "@/stores/multiService/types"; +import { useCurrentAccount } from "@/stores/account"; +import { useMultiService } from "@/stores/multiService"; +import { PrimaryAccount } from "@/stores/account/types"; export function getFeatureAccount (feature: MultiServiceFeature, spaceLocalID: string) { const accountId = useMultiService.getState().getFeatureAccountId(feature, spaceLocalID); diff --git a/src/views/account/Attendance/Attendance.tsx b/src/views/account/Attendance/Attendance.tsx index 6a62470e1..43b5dcc24 100644 --- a/src/views/account/Attendance/Attendance.tsx +++ b/src/views/account/Attendance/Attendance.tsx @@ -1,27 +1,26 @@ -import {useTheme} from "@react-navigation/native"; -import React, {useEffect, useMemo, useState} from "react"; -import {ActivityIndicator, Platform, RefreshControl, View} from "react-native"; - -import type {Screen} from "@/router/helpers/types"; -import {useCurrentAccount} from "@/stores/account"; -import {useAttendanceStore} from "@/stores/attendance"; -import {updateAttendanceInCache, updateAttendancePeriodsInCache} from "@/services/attendance"; -import {NativeText} from "@/components/Global/NativeComponents"; -import Reanimated, {FadeIn, FadeOut, LinearTransition} from "react-native-reanimated"; +import { useTheme } from "@react-navigation/native"; +import React, { useEffect, useMemo, useState } from "react"; +import { ActivityIndicator, Platform, RefreshControl, View } from "react-native"; + +import type { Screen } from "@/router/helpers/types"; +import { useCurrentAccount } from "@/stores/account"; +import { useAttendanceStore } from "@/stores/attendance"; +import { updateAttendanceInCache, updateAttendancePeriodsInCache } from "@/services/attendance"; +import { NativeText } from "@/components/Global/NativeComponents"; +import Reanimated, { FadeIn, FadeOut, LinearTransition } from "react-native-reanimated"; import PapillonPicker from "@/components/Global/PapillonPicker"; -import {ChevronDown, Eye, Scale, Timer, UserX} from "lucide-react-native"; +import { ChevronDown, Eye, Scale, Timer, UserX } from "lucide-react-native"; import PapillonHeader, { PapillonHeaderInsetHeight } from "@/components/Global/PapillonHeader"; -import {animPapillon} from "@/utils/ui/animations"; +import { animPapillon } from "@/utils/ui/animations"; import AttendanceItem from "./Atoms/AttendanceItem"; -import {getAbsenceTime} from "@/utils/format/attendance_time"; import TotalMissed from "./Atoms/TotalMissed"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; -import {protectScreenComponent} from "@/router/helpers/protected-screen"; -import {Observation} from "@/services/shared/Observation"; +import { protectScreenComponent } from "@/router/helpers/protected-screen"; +import { Observation } from "@/services/shared/Observation"; import MissingItem from "@/components/Global/MissingItem"; -import {hasFeatureAccountSetup} from "@/utils/multiservice"; -import {MultiServiceFeature} from "@/stores/multiService/types"; -import {AccountService} from "@/stores/account/types"; +import { hasFeatureAccountSetup } from "@/utils/multiservice"; +import { MultiServiceFeature } from "@/stores/multiService/types"; +import { AccountService } from "@/stores/account/types"; const Attendance: Screen<"Attendance"> = ({ route, navigation }) => { const theme = useTheme(); diff --git a/src/views/account/Chat/Messages.tsx b/src/views/account/Chat/Messages.tsx index b507f3268..9e2b877e0 100644 --- a/src/views/account/Chat/Messages.tsx +++ b/src/views/account/Chat/Messages.tsx @@ -35,8 +35,8 @@ import { SquarePen } from "lucide-react-native"; import { TouchableOpacity } from "react-native-gesture-handler"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; import { TabLocation } from "pawnote"; -import {hasFeatureAccountSetup} from "@/utils/multiservice"; -import {MultiServiceFeature} from "@/stores/multiService/types"; +import { hasFeatureAccountSetup } from "@/utils/multiservice"; +import { MultiServiceFeature } from "@/stores/multiService/types"; import { timestampToString } from "@/utils/format/DateHelper"; // Voir la documentation de `react-navigation`. diff --git a/src/views/account/Chat/Modals/Chat.tsx b/src/views/account/Chat/Modals/Chat.tsx index 3fb563104..0b45ab377 100644 --- a/src/views/account/Chat/Modals/Chat.tsx +++ b/src/views/account/Chat/Modals/Chat.tsx @@ -111,7 +111,7 @@ const Chat: Screen<"Chat"> = ({ navigation, route }) => { {messages[0] ? ( <> - + navigation.goBack()}> = ({ navigation, route }) => { textColor={getProfileColorByName(creatorName).dark} size={38} /> - + = ({ navigation, route }) => { > {item.attachments.map((attachment: Attachment) => ( openUrl(attachment.url)}> - + {attachment.type === AttachmentType.File ? ( @@ -331,7 +331,7 @@ const Chat: Screen<"Chat"> = ({ navigation, route }) => { description="Envoie un message pour commencer la discussion." entering={animPapillon(FadeInDown)} exiting={animPapillon(FadeOut)} - style={{paddingVertical: 26}} + style={{ paddingVertical: 26 }} /> )} /> @@ -377,10 +377,10 @@ const Chat: Screen<"Chat"> = ({ navigation, route }) => { sendMessageInChat(account, route.params.handle, text); }} > - + - + ) : ( diff --git a/src/views/account/Chat/Modals/ChatDetails.tsx b/src/views/account/Chat/Modals/ChatDetails.tsx index 01ffc2523..a83e7fb68 100644 --- a/src/views/account/Chat/Modals/ChatDetails.tsx +++ b/src/views/account/Chat/Modals/ChatDetails.tsx @@ -1,14 +1,14 @@ -import React, {useEffect, useState} from "react"; +import React, { useEffect, useState } from "react"; import { ScrollView, TouchableOpacity, View, Image, StyleSheet } from "react-native"; -import {useTheme} from "@react-navigation/native"; +import { useTheme } from "@react-navigation/native"; -import type {Screen} from "@/router/helpers/types"; -import {NativeItem, NativeList, NativeListHeader, NativeText,} from "@/components/Global/NativeComponents"; -import {useCurrentAccount} from "@/stores/account"; -import {ChevronDown } from "lucide-react-native"; +import type { Screen } from "@/router/helpers/types"; +import { NativeItem, NativeList, NativeListHeader, NativeText, } from "@/components/Global/NativeComponents"; +import { useCurrentAccount } from "@/stores/account"; +import { ChevronDown } from "lucide-react-native"; import parse_initials from "@/utils/format/format_pronote_initials"; import InitialIndicator from "@/components/News/InitialIndicator"; -import {getProfileColorByName} from "@/services/local/default-personalization"; +import { getProfileColorByName } from "@/services/local/default-personalization"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; import GetAvailableThemes from "@/utils/chat/themes/GetAvailableThemes"; import { ThemesMeta } from "@/utils/chat/themes/Themes.types"; diff --git a/src/views/account/Chat/Modals/ChatThemes.tsx b/src/views/account/Chat/Modals/ChatThemes.tsx index 1583f64b3..ec1c2120e 100644 --- a/src/views/account/Chat/Modals/ChatThemes.tsx +++ b/src/views/account/Chat/Modals/ChatThemes.tsx @@ -1,6 +1,6 @@ import { NativeList, NativeItem, NativeText } from "@/components/Global/NativeComponents"; import { ScrollView, StyleSheet, View, Image } from "react-native"; -import type {Screen} from "@/router/helpers/types"; +import type { Screen } from "@/router/helpers/types"; const ChatThemes: Screen<"ChatThemes"> = ({ navigation, route }) => { const { themes, onGoBack } = route.params; diff --git a/src/views/account/Evaluation/Atoms/SkillLevelBadge.tsx b/src/views/account/Evaluation/Atoms/SkillLevelBadge.tsx index 01bfb31fd..be6055231 100644 --- a/src/views/account/Evaluation/Atoms/SkillLevelBadge.tsx +++ b/src/views/account/Evaluation/Atoms/SkillLevelBadge.tsx @@ -1,8 +1,8 @@ -import {SkillLevel} from "@/services/shared/Evaluation"; -import {View} from "react-native"; -import React, {useState} from "react"; -import {NativeText} from "@/components/Global/NativeComponents"; -import {Plus} from "lucide-react-native"; +import { SkillLevel } from "@/services/shared/Evaluation"; +import { View } from "react-native"; +import React, { useState } from "react"; +import { NativeText } from "@/components/Global/NativeComponents"; +import { Plus } from "lucide-react-native"; interface SkillLevelBadgeProps { diff --git a/src/views/account/Evaluation/Evaluation.tsx b/src/views/account/Evaluation/Evaluation.tsx index e8989d72e..4c46f5f2e 100644 --- a/src/views/account/Evaluation/Evaluation.tsx +++ b/src/views/account/Evaluation/Evaluation.tsx @@ -1,23 +1,23 @@ -import React, {Suspense, useEffect, useMemo, useRef, useState} from "react"; -import {View, ScrollView, RefreshControl, Platform, ActivityIndicator} from "react-native"; +import React, { Suspense, useEffect, useMemo, useRef, useState } from "react"; +import { View, ScrollView, RefreshControl, Platform, ActivityIndicator } from "react-native"; import type { Screen } from "@/router/helpers/types"; -import {useTheme} from "@react-navigation/native"; -import {useSafeAreaInsets} from "react-native-safe-area-context"; -import {PapillonHeaderSelector, PapillonModernHeader} from "@/components/Global/PapillonModernHeader"; -import {useCurrentAccount} from "@/stores/account"; -import Reanimated, {FadeInUp, FadeOutDown} from "react-native-reanimated"; -import {animPapillon} from "@/utils/ui/animations"; -import {ChevronDown} from "lucide-react-native"; +import { useTheme } from "@react-navigation/native"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { PapillonHeaderSelector, PapillonModernHeader } from "@/components/Global/PapillonModernHeader"; +import { useCurrentAccount } from "@/stores/account"; +import Reanimated, { FadeInUp, FadeOutDown } from "react-native-reanimated"; +import { animPapillon } from "@/utils/ui/animations"; +import { ChevronDown } from "lucide-react-native"; import PapillonPicker from "@/components/Global/PapillonPicker"; -import {updateEvaluationPeriodsInCache, updateEvaluationsInCache} from "@/services/evaluation"; -import {useEvaluationStore} from "@/stores/evaluation"; -import {EvaluationsPerSubject} from "@/services/shared/Evaluation"; +import { updateEvaluationPeriodsInCache, updateEvaluationsInCache } from "@/services/evaluation"; +import { useEvaluationStore } from "@/stores/evaluation"; +import { EvaluationsPerSubject } from "@/services/shared/Evaluation"; import MissingItem from "@/components/Global/MissingItem"; import Subject from "@/views/account/Evaluation/Subject/Subject"; import EvaluationsLatestList from "@/views/account/Evaluation/Latest/LatestEvaluations"; -import {AccountService} from "@/stores/account/types"; -import {hasFeatureAccountSetup} from "@/utils/multiservice"; -import {MultiServiceFeature} from "@/stores/multiService/types"; +import { AccountService } from "@/stores/account/types"; +import { hasFeatureAccountSetup } from "@/utils/multiservice"; +import { MultiServiceFeature } from "@/stores/multiService/types"; const Evaluation: Screen<"Evaluation"> = ({ route, navigation }) => { const theme = useTheme(); diff --git a/src/views/account/Evaluation/Latest/LatestEvaluations.tsx b/src/views/account/Evaluation/Latest/LatestEvaluations.tsx index df19af1a0..87cbffb82 100644 --- a/src/views/account/Evaluation/Latest/LatestEvaluations.tsx +++ b/src/views/account/Evaluation/Latest/LatestEvaluations.tsx @@ -5,7 +5,7 @@ import { animPapillon } from "@/utils/ui/animations"; import Reanimated, { LinearTransition } from "react-native-reanimated"; import { NativeStackNavigationProp } from "@react-navigation/native-stack"; import { RouteParameters } from "@/router/helpers/types"; -import {Evaluation} from "@/services/shared/Evaluation"; +import { Evaluation } from "@/services/shared/Evaluation"; import EvaluationLatestItem from "./LatestEvaluationsItem"; interface EvaluationsLatestListProps { diff --git a/src/views/account/Evaluation/Latest/LatestEvaluationsItem.tsx b/src/views/account/Evaluation/Latest/LatestEvaluationsItem.tsx index 009b5604b..ec499f798 100644 --- a/src/views/account/Evaluation/Latest/LatestEvaluationsItem.tsx +++ b/src/views/account/Evaluation/Latest/LatestEvaluationsItem.tsx @@ -3,8 +3,8 @@ import { getSubjectData } from "@/services/shared/Subject"; import React, { useEffect, useState } from "react"; import { View } from "react-native"; import { PressableScale } from "react-native-pressable-scale"; -import {Evaluation, Skill} from "@/services/shared/Evaluation"; -import {SkillLevelBadge} from "@/views/account/Evaluation/Atoms/SkillLevelBadge"; +import { Evaluation, Skill } from "@/services/shared/Evaluation"; +import { SkillLevelBadge } from "@/views/account/Evaluation/Atoms/SkillLevelBadge"; type EvaluationLatestItemProps = { evaluation: Evaluation; diff --git a/src/views/account/Evaluation/Subject/EvaluationList.tsx b/src/views/account/Evaluation/Subject/EvaluationList.tsx index 82df1b510..04aedbd2e 100644 --- a/src/views/account/Evaluation/Subject/EvaluationList.tsx +++ b/src/views/account/Evaluation/Subject/EvaluationList.tsx @@ -13,8 +13,8 @@ import Reanimated, { FadeOutUp, } from "react-native-reanimated"; import SubjectTitle from "./SubjectTitle"; -import {Evaluation, EvaluationsPerSubject, Skill} from "@/services/shared/Evaluation"; -import {SkillLevelBadge} from "@/views/account/Evaluation/Atoms/SkillLevelBadge"; +import { Evaluation, EvaluationsPerSubject, Skill } from "@/services/shared/Evaluation"; +import { SkillLevelBadge } from "@/views/account/Evaluation/Atoms/SkillLevelBadge"; interface SubjectItemProps { subject: EvaluationsPerSubject, diff --git a/src/views/account/Evaluation/Subject/Subject.tsx b/src/views/account/Evaluation/Subject/Subject.tsx index 5ff5c725c..f6c804a30 100644 --- a/src/views/account/Evaluation/Subject/Subject.tsx +++ b/src/views/account/Evaluation/Subject/Subject.tsx @@ -5,7 +5,7 @@ import { animPapillon } from "@/utils/ui/animations"; import Reanimated, { LinearTransition } from "react-native-reanimated"; import { FlatList } from "react-native"; import SubjectItem from "./EvaluationList"; -import {Evaluation, EvaluationsPerSubject} from "@/services/shared/Evaluation"; +import { Evaluation, EvaluationsPerSubject } from "@/services/shared/Evaluation"; interface SubjectProps { allEvaluations: Evaluation[] diff --git a/src/views/account/Grades/Atoms/GradeTitle.tsx b/src/views/account/Grades/Atoms/GradeTitle.tsx index 723926672..070dedb98 100644 --- a/src/views/account/Grades/Atoms/GradeTitle.tsx +++ b/src/views/account/Grades/Atoms/GradeTitle.tsx @@ -3,7 +3,7 @@ import { getCourseSpeciality } from "@/utils/format/format_cours_name"; import { useTheme } from "@react-navigation/native"; import React from "react"; import { View } from "react-native"; -import {Grade} from "@/services/shared/Grade"; +import { Grade } from "@/services/shared/Grade"; interface GradeTitleProps { grade: Grade diff --git a/src/views/account/Grades/Document.tsx b/src/views/account/Grades/Document.tsx index 52f7ec225..5578b6db4 100644 --- a/src/views/account/Grades/Document.tsx +++ b/src/views/account/Grades/Document.tsx @@ -1,8 +1,7 @@ -import React from "react"; import { NativeItem, NativeList, NativeListHeader, NativeText, } from "@/components/Global/NativeComponents"; import { getSubjectData } from "@/services/shared/Subject"; import { useTheme } from "@react-navigation/native"; -import { useCallback, useEffect, useLayoutEffect, useState } from "react"; +import React, { useCallback, useEffect, useLayoutEffect, useState } from "react"; import { Image, Platform, ScrollView, Text, TouchableOpacity, View } from "react-native"; import * as StoreReview from "expo-store-review"; import { @@ -15,8 +14,7 @@ import { Users, Maximize2 } from "lucide-react-native"; -import { getAverageDiffGrade } from "@/utils/grades/getAverages"; -import type { AverageDiffGrade } from "@/utils/grades/getAverages"; +import { getAverageDiffGrade, type AverageDiffGrade } from "@/utils/grades/getAverages"; import { Screen } from "@/router/helpers/types"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; import AsyncStorage from "@react-native-async-storage/async-storage"; diff --git a/src/views/account/Grades/Grades.tsx b/src/views/account/Grades/Grades.tsx index 32cf7afa9..7c2d9e485 100644 --- a/src/views/account/Grades/Grades.tsx +++ b/src/views/account/Grades/Grades.tsx @@ -31,8 +31,8 @@ import Reanimated, { } from "react-native-reanimated"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import GradesScodocUE from "./Atoms/GradesScodocUE"; -import {hasFeatureAccountSetup} from "@/utils/multiservice"; -import {MultiServiceFeature} from "@/stores/multiService/types"; +import { hasFeatureAccountSetup } from "@/utils/multiservice"; +import { MultiServiceFeature } from "@/stores/multiService/types"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; const GradesAverageGraph = lazy(() => import("./Graph/GradesAverage")); diff --git a/src/views/account/Grades/Subject/SubjectTitle.tsx b/src/views/account/Grades/Subject/SubjectTitle.tsx index c65597c0d..69344ef0b 100644 --- a/src/views/account/Grades/Subject/SubjectTitle.tsx +++ b/src/views/account/Grades/Subject/SubjectTitle.tsx @@ -4,9 +4,9 @@ import { useTheme } from "@react-navigation/native"; import React, { useEffect } from "react"; import { View } from "react-native"; import { TouchableOpacity } from "react-native-gesture-handler"; -import {type RouteParameters} from "@/router/helpers/types"; -import type {NativeStackNavigationProp} from "@react-navigation/native-stack"; -import type {Grade, GradesPerSubject} from "@/services/shared/Grade"; +import { type RouteParameters } from "@/router/helpers/types"; +import type { NativeStackNavigationProp } from "@react-navigation/native-stack"; +import type { Grade, GradesPerSubject } from "@/services/shared/Grade"; import { getSubjectAverage } from "@/utils/grades/getAverages"; import { adjustColor } from "@/utils/ui/colors"; diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index d56418121..6fa3f2cd5 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -5,12 +5,12 @@ import { useHomeworkStore } from "@/stores/homework"; import { toggleHomeworkState, updateHomeworkForWeekInCache } from "@/services/homework"; import HomeworkItem from "../../Homeworks/Atoms/Item"; import type { Homework } from "@/services/shared/Homework"; -import {debounce} from "lodash"; +import { debounce } from "lodash"; import { PapillonNavigation } from "@/router/refs"; import RedirectButton from "@/components/Home/RedirectButton"; import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; -import {NativeStackNavigationProp} from "@react-navigation/native-stack"; -import {RouteParameters} from "@/router/helpers/types"; +import { NativeStackNavigationProp } from "@react-navigation/native-stack"; +import { RouteParameters } from "@/router/helpers/types"; import { FadeInDown, FadeOut } from "react-native-reanimated"; import MissingItem from "@/components/Global/MissingItem"; diff --git a/src/views/account/Home/ModalContent.tsx b/src/views/account/Home/ModalContent.tsx index b051a581f..d4c899497 100644 --- a/src/views/account/Home/ModalContent.tsx +++ b/src/views/account/Home/ModalContent.tsx @@ -1,5 +1,5 @@ -import {NativeItem, NativeList, NativeText} from "@/components/Global/NativeComponents"; -import React, {useCallback, useEffect, useMemo, useState} from "react"; +import { NativeItem, NativeList, NativeText } from "@/components/Global/NativeComponents"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; import Reanimated, { FadeInUp, FadeOutDown, @@ -7,21 +7,21 @@ import Reanimated, { FlipInXDown, LinearTransition, } from "react-native-reanimated"; -import { Sparkles, WifiOff, X} from "lucide-react-native"; -import {useTheme} from "@react-navigation/native"; +import { Sparkles, WifiOff, X } from "lucide-react-native"; +import { useTheme } from "@react-navigation/native"; import PackageJSON from "../../../../package.json"; -import {Dimensions, View} from "react-native"; +import { Dimensions, View } from "react-native"; import NetInfo from "@react-native-community/netinfo"; -import {getErrorTitle} from "@/utils/format/get_papillon_error_title"; -import {Elements, type Element} from "./ElementIndex"; -import {animPapillon} from "@/utils/ui/animations"; -import {useFlagsStore} from "@/stores/flags"; -import {useCurrentAccount} from "@/stores/account"; +import { getErrorTitle } from "@/utils/format/get_papillon_error_title"; +import { Elements, type Element } from "./ElementIndex"; +import { animPapillon } from "@/utils/ui/animations"; +import { useFlagsStore } from "@/stores/flags"; +import { useCurrentAccount } from "@/stores/account"; import AsyncStorage from "@react-native-async-storage/async-storage"; -import {defaultTabs} from "@/consts/DefaultTabs"; -import {NativeStackNavigationProp} from "@react-navigation/native-stack"; -import {RouteParameters} from "@/router/helpers/types"; +import { defaultTabs } from "@/consts/DefaultTabs"; +import { NativeStackNavigationProp } from "@react-navigation/native-stack"; +import { RouteParameters } from "@/router/helpers/types"; import { TouchableOpacity } from "react-native-gesture-handler"; interface ModalContentProps { diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 045d96f12..c92d8bf95 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -32,14 +32,14 @@ import AnimatedNumber from "@/components/Global/AnimatedNumber"; import * as Haptics from "expo-haptics"; import MissingItem from "@/components/Global/MissingItem"; import { PapillonModernHeader } from "@/components/Global/PapillonModernHeader"; -import {Homework} from "@/services/shared/Homework"; -import {Account, AccountService} from "@/stores/account/types"; -import {Screen} from "@/router/helpers/types"; -import {NativeSyntheticEvent} from "react-native/Libraries/Types/CoreEventTypes"; -import {NativeScrollEvent, ScrollViewProps} from "react-native/Libraries/Components/ScrollView/ScrollView"; +import { Homework } from "@/services/shared/Homework"; +import { AccountService } from "@/stores/account/types"; +import { Screen } from "@/router/helpers/types"; +import { NativeSyntheticEvent } from "react-native/Libraries/Types/CoreEventTypes"; +import { NativeScrollEvent, ScrollViewProps } from "react-native/Libraries/Components/ScrollView/ScrollView"; import AsyncStorage from "@react-native-async-storage/async-storage"; -import {hasFeatureAccountSetup} from "@/utils/multiservice"; -import {MultiServiceFeature} from "@/stores/multiService/types"; +import { hasFeatureAccountSetup } from "@/utils/multiservice"; +import { MultiServiceFeature } from "@/stores/multiService/types"; import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; const formatDate = (date: string | number | Date): string => { diff --git a/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx b/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx index cc308cedb..50fe446d3 100644 --- a/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx +++ b/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx @@ -14,9 +14,9 @@ import Animated, { runOnJS, SharedValue, } from "react-native-reanimated"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import {Theme} from "@react-navigation/native/src/types"; -import {NativeScrollEvent, ScrollViewProps} from "react-native/Libraries/Components/ScrollView/ScrollView"; -import {NativeSyntheticEvent} from "react-native/Libraries/Types/CoreEventTypes"; +import { Theme } from "@react-navigation/native/src/types"; +import { NativeScrollEvent, ScrollViewProps } from "react-native/Libraries/Components/ScrollView/ScrollView"; +import { NativeSyntheticEvent } from "react-native/Libraries/Types/CoreEventTypes"; import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; const { width: SCREEN_WIDTH } = Dimensions.get("window"); diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index f63b4323c..b8b05196f 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -31,9 +31,9 @@ import { import PapillonPicker from "@/components/Global/PapillonPicker"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { WeekFrequency } from "@/services/shared/Timetable"; -import {AccountService} from "@/stores/account/types"; -import {hasFeatureAccountSetup} from "@/utils/multiservice"; -import {MultiServiceFeature} from "@/stores/multiService/types"; +import { AccountService } from "@/stores/account/types"; +import { hasFeatureAccountSetup } from "@/utils/multiservice"; +import { MultiServiceFeature } from "@/stores/multiService/types"; import { fetchIcalData } from "@/services/local/ical"; const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { diff --git a/src/views/account/Lessons/LessonsHeader.tsx b/src/views/account/Lessons/LessonsHeader.tsx index 429b3dfdf..dc1a44032 100644 --- a/src/views/account/Lessons/LessonsHeader.tsx +++ b/src/views/account/Lessons/LessonsHeader.tsx @@ -12,7 +12,7 @@ import Reanimated, { import RNDateTimePicker from "@react-native-community/datetimepicker"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import {animPapillon} from "@/utils/ui/animations"; +import { animPapillon } from "@/utils/ui/animations"; interface HeaderCalendarProps { index: number, diff --git a/src/views/account/News/News.tsx b/src/views/account/News/News.tsx index b187f484b..39e3ecf4e 100644 --- a/src/views/account/News/News.tsx +++ b/src/views/account/News/News.tsx @@ -15,10 +15,10 @@ import { animPapillon } from "@/utils/ui/animations"; import { categorizeMessages } from "@/utils/magic/categorizeMessages"; import { protectScreenComponent } from "@/router/helpers/protected-screen"; import MissingItem from "@/components/Global/MissingItem"; -import {Information} from "@/services/shared/Information"; -import {AccountService} from "@/stores/account/types"; -import {hasFeatureAccountSetup} from "@/utils/multiservice"; -import {MultiServiceFeature} from "@/stores/multiService/types"; +import { Information } from "@/services/shared/Information"; +import { AccountService } from "@/stores/account/types"; +import { hasFeatureAccountSetup } from "@/utils/multiservice"; +import { MultiServiceFeature } from "@/stores/multiService/types"; import PapillonHeader, { PapillonHeaderInsetHeight } from "@/components/Global/PapillonHeader"; type NewsItem = Omit & { date: string, important: boolean }; diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index ee8ed4c31..9d911c032 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -1,59 +1,49 @@ -import React, { useEffect, useLayoutEffect, useState } from "react"; -import { - View, - ScrollView, - StyleSheet, - Switch, - Alert, - ActivityIndicator, - RefreshControl, - Text, - Dimensions -} from "react-native"; +// React import { useTheme } from "@react-navigation/native"; -import { - AlertTriangle, - ChefHat, - CookingPot, - MapPin, - Plus, - Sprout, - Utensils, -} from "lucide-react-native"; - -import type { Screen } from "@/router/helpers/types"; -import { - NativeItem, - NativeList, - NativeListHeader, - NativeText, -} from "@/components/Global/NativeComponents"; -import { useCurrentAccount } from "@/stores/account"; -import TabAnimatedTitle from "@/components/Global/TabAnimatedTitle"; -import { balanceFromExternal } from "@/services/balance"; -import MissingItem from "@/components/Global/MissingItem"; -import { animPapillon } from "@/utils/ui/animations"; import Reanimated, { FadeIn, FadeInDown, FadeOut, LinearTransition, ZoomIn, ZoomOut } from "react-native-reanimated"; -import { reservationHistoryFromExternal } from "@/services/reservation-history"; -import { qrcodeFromExternal } from "@/services/qrcode"; -import { getMenu } from "@/services/menu"; +import React, { useEffect, useLayoutEffect, useState } from "react"; +import { ActivityIndicator, Alert, Dimensions, RefreshControl, ScrollView, StyleSheet, Switch, Text, View } from "react-native"; +import { PressableScale } from "react-native-pressable-scale"; + +// External modules +import { AlertTriangle, ChefHat, ChevronLeft, ChevronRight, CookingPot, MapPin, Plus, Sprout, Utensils } from "lucide-react-native"; import type { FoodAllergen, FoodLabel, Menu as PawnoteMenu } from "pawnote"; -import { PapillonHeaderSelector } from "@/components/Global/PapillonModernHeader"; + +// Components +import DrawableImportRestaurant from "@/components/Drawables/DrawableImportRestaurant"; +import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import AnimatedNumber from "@/components/Global/AnimatedNumber"; -import { LessonsDateModal } from "../Lessons/LessonsHeader"; -import { BookingTerminal, BookingDay } from "@/services/shared/Booking"; -import { bookDayFromExternal, getBookingsAvailableFromExternal } from "@/services/booking"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; +import MissingItem from "@/components/Global/MissingItem"; +import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; import PapillonHeader, { PapillonHeaderInsetHeight } from "@/components/Global/PapillonHeader"; -import { PressableScale } from "react-native-pressable-scale"; -import { ChevronLeft, ChevronRight} from "lucide-react-native"; -import DrawableImportRestaurant from "@/components/Drawables/DrawableImportRestaurant"; -import ButtonCta from "@/components/FirstInstallation/ButtonCta"; -import { Account, AccountService } from "@/stores/account/types"; +import { PapillonHeaderSelector } from "@/components/Global/PapillonModernHeader"; +import TabAnimatedTitle from "@/components/Global/TabAnimatedTitle"; +import { LessonsDateModal } from "@/views/account/Lessons/LessonsHeader"; +import MenuCard from "@/views/account/Restaurant/Cards/Card"; + +// Router type +import type { Screen } from "@/router/helpers/types"; + +// Services +import { balanceFromExternal } from "@/services/balance"; +import { bookDayFromExternal, getBookingsAvailableFromExternal } from "@/services/booking"; +import { getMenu } from "@/services/menu"; +import { qrcodeFromExternal } from "@/services/qrcode"; +import { reservationHistoryFromExternal } from "@/services/reservation-history"; import { Balance } from "@/services/shared/Balance"; +import { BookingDay, BookingTerminal } from "@/services/shared/Booking"; import { ReservationHistory } from "@/services/shared/ReservationHistory"; -import { STORE_THEMES, StoreTheme } from "./Cards/StoreThemes"; -import MenuCard from "./Cards/Card"; + +// Stores +import { useCurrentAccount } from "@/stores/account"; +import { Account, AccountService } from "@/stores/account/types"; + +// Animations +import { animPapillon } from "@/utils/ui/animations"; + +// Styles +import { STORE_THEMES, StoreTheme } from "@/views/account/Restaurant/Cards/StoreThemes"; export const formatCardIdentifier = (identifier: string, dots: number = 4, separator: string = " ") => { if(!identifier) { diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index 6a43485f6..9433b42d0 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -14,11 +14,11 @@ import { PapillonHeaderAction } from "@/components/Global/PapillonModernHeader"; import { getSubjectData } from "@/services/shared/Subject"; import { PapillonNavigation } from "@/router/refs"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; -import { TimetableClassStatus} from "@/services/shared/Timetable"; +import { TimetableClassStatus } from "@/services/shared/Timetable"; import { NativeText } from "@/components/Global/NativeComponents"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import type { Screen } from "@/router/helpers/types"; -import {Account} from "@/stores/account/types"; +import { Account } from "@/stores/account/types"; import { fetchIcalData } from "@/services/local/ical"; const LOCALES = { diff --git a/src/views/addon/AddonLogs.tsx b/src/views/addon/AddonLogs.tsx index 2b66fd156..90d28db4d 100644 --- a/src/views/addon/AddonLogs.tsx +++ b/src/views/addon/AddonLogs.tsx @@ -1,8 +1,8 @@ -import {NativeItem, NativeList, NativeText} from "@/components/Global/NativeComponents"; -import {ScrollView, View} from "react-native"; -import {CircleAlert, CircleX, Code, TriangleAlert} from "lucide-react-native"; -import {Screen} from "@/router/helpers/types"; -import {AddonLogs as Logs} from "@/addons/types"; +import { NativeItem, NativeList, NativeText } from "@/components/Global/NativeComponents"; +import { ScrollView, View } from "react-native"; +import { CircleAlert, CircleX, Code, TriangleAlert } from "lucide-react-native"; +import { Screen } from "@/router/helpers/types"; +import { AddonLogs as Logs } from "@/addons/types"; const AddonLogs: Screen<"AddonLogs"> = ({ navigation, route }) => { const logs: Logs[] = route.params.logs.map(l => { diff --git a/src/views/login/ServiceSelector.tsx b/src/views/login/ServiceSelector.tsx index eee2639d9..99297afe1 100644 --- a/src/views/login/ServiceSelector.tsx +++ b/src/views/login/ServiceSelector.tsx @@ -9,7 +9,6 @@ import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBub import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import MaskStars from "@/components/FirstInstallation/MaskStars"; -import { useAlert } from "@/providers/AlertProvider"; import { useTheme } from "@react-navigation/native"; import GetV6Data from "@/utils/login/GetV6Data"; import { School } from "lucide-react-native"; diff --git a/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx b/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx index 021a5e466..6b67b2abd 100644 --- a/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx +++ b/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx @@ -19,14 +19,14 @@ import defaultPersonalization from "@/services/ecoledirecte/default-personalizat import { useTheme } from "@react-navigation/native"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import BottomSheet from "@/components/Modals/PapillonBottomSheet"; -import {NativeText} from "@/components/Global/NativeComponents"; +import { NativeText } from "@/components/Global/NativeComponents"; import Reanimated, { FlipInXDown, LinearTransition, useSharedValue } from "react-native-reanimated"; import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; -import {SvgFromXml} from "react-native-svg"; +import { SvgFromXml } from "react-native-svg"; import LoginView from "@/components/Templates/LoginView"; const EcoleDirecteCredentials: Screen<"EcoleDirecteCredentials"> = ({ navigation }) => { diff --git a/src/views/login/pronote/PronoteInstanceSelector.tsx b/src/views/login/pronote/PronoteInstanceSelector.tsx index bd8c14651..a3b9c1ef4 100644 --- a/src/views/login/pronote/PronoteInstanceSelector.tsx +++ b/src/views/login/pronote/PronoteInstanceSelector.tsx @@ -30,7 +30,7 @@ import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import { LinearGradient } from "expo-linear-gradient"; import { useTheme } from "@react-navigation/native"; -import {Search, X, GraduationCap, SearchX} from "lucide-react-native"; +import { Search, X, GraduationCap, SearchX } from "lucide-react-native"; import { useAlert } from "@/providers/AlertProvider"; import { Audio } from "expo-av"; import getInstancesFromDataset from "@/services/pronote/dataset_geolocation"; diff --git a/src/views/login/skolengo/SkolengoInstanceSelector.tsx b/src/views/login/skolengo/SkolengoInstanceSelector.tsx index 6a3544cde..64f8d00b7 100644 --- a/src/views/login/skolengo/SkolengoInstanceSelector.tsx +++ b/src/views/login/skolengo/SkolengoInstanceSelector.tsx @@ -10,7 +10,6 @@ import { LinearGradient } from "expo-linear-gradient"; import { useTheme } from "@react-navigation/native"; import { Search, X, GraduationCap, } from "lucide-react-native"; -import { useAlert } from "@/providers/AlertProvider"; import type { School } from "scolengo-api/types/models/School"; import { Skolengo } from "scolengo-api"; import { useDebounce } from "@/hooks/debounce"; diff --git a/src/views/settings/ExternalAccount/ARD.tsx b/src/views/settings/ExternalAccount/ARD.tsx index 0b5a8ca71..7a325436f 100644 --- a/src/views/settings/ExternalAccount/ARD.tsx +++ b/src/views/settings/ExternalAccount/ARD.tsx @@ -1,5 +1,5 @@ import { useState } from "react"; -import {Authenticator, Client} from "pawrd"; +import { Authenticator, Client } from "pawrd"; import { AccountService, type ARDAccount } from "@/stores/account/types"; import uuid from "@/utils/uuid-v4"; import { useAccounts, useCurrentAccount } from "@/stores/account"; diff --git a/src/views/settings/ExternalAccount/IzlyActivation.tsx b/src/views/settings/ExternalAccount/IzlyActivation.tsx index 49da237a6..ae622dd99 100644 --- a/src/views/settings/ExternalAccount/IzlyActivation.tsx +++ b/src/views/settings/ExternalAccount/IzlyActivation.tsx @@ -1,14 +1,14 @@ -import React, {useState, useEffect} from "react"; -import type {Screen} from "@/router/helpers/types"; -import {useTheme} from "@react-navigation/native"; -import {SafeAreaView, useSafeAreaInsets} from "react-native-safe-area-context"; -import {Alert, Keyboard, KeyboardAvoidingView, StyleSheet, TouchableWithoutFeedback, View} from "react-native"; +import React, { useState, useEffect } from "react"; +import type { Screen } from "@/router/helpers/types"; +import { useTheme } from "@react-navigation/native"; +import { SafeAreaView, useSafeAreaInsets } from "react-native-safe-area-context"; +import { Alert, Keyboard, KeyboardAvoidingView, StyleSheet, TouchableWithoutFeedback, View } from "react-native"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; -import { NativeText,} from "@/components/Global/NativeComponents"; +import { NativeText, } from "@/components/Global/NativeComponents"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; -import { tokenize} from "ezly"; -import {AccountService, IzlyAccount} from "@/stores/account/types"; -import {useAccounts, useCurrentAccount} from "@/stores/account"; +import { tokenize } from "ezly"; +import { AccountService, IzlyAccount } from "@/stores/account/types"; +import { useAccounts, useCurrentAccount } from "@/stores/account"; import uuid from "@/utils/uuid-v4"; import * as Linking from "expo-linking"; diff --git a/src/views/settings/ExternalAccount/QrcodeScanner.tsx b/src/views/settings/ExternalAccount/QrcodeScanner.tsx index 54f3f2828..4936b8acf 100644 --- a/src/views/settings/ExternalAccount/QrcodeScanner.tsx +++ b/src/views/settings/ExternalAccount/QrcodeScanner.tsx @@ -3,8 +3,6 @@ import type { Screen } from "@/router/helpers/types"; import { QrCode } from "lucide-react-native"; import { useSafeAreaInsets, SafeAreaView } from "react-native-safe-area-context"; import { View, StyleSheet, Text } from "react-native"; -import { SafeAreaView } from "react-native-safe-area-context"; - import { ExternalAccount } from "@/stores/account/types"; import { useAccounts } from "@/stores/account"; diff --git a/src/views/settings/ExternalAccount/ServiceSelector.tsx b/src/views/settings/ExternalAccount/ServiceSelector.tsx index d16465013..4ec8abb4f 100644 --- a/src/views/settings/ExternalAccount/ServiceSelector.tsx +++ b/src/views/settings/ExternalAccount/ServiceSelector.tsx @@ -1,7 +1,6 @@ import React, { useState } from "react"; import type { Screen } from "@/router/helpers/types"; -import { useTheme } from "@react-navigation/native"; -import { SafeAreaView, useSafeAreaInsets } from "react-native-safe-area-context"; +import { SafeAreaView } from "react-native-safe-area-context"; import { Image, View, StyleSheet } from "react-native"; import Reanimated, { LinearTransition, FlipInXDown } from "react-native-reanimated"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; diff --git a/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx b/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx index f575a2c5a..c39f8bf7d 100644 --- a/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx +++ b/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx @@ -1,14 +1,14 @@ import React, { useState } from "react"; -import type {Screen} from "@/router/helpers/types"; -import {useTheme} from "@react-navigation/native"; -import {SafeAreaView, useSafeAreaInsets} from "react-native-safe-area-context"; -import {Alert, Keyboard, KeyboardAvoidingView, StyleSheet, TouchableWithoutFeedback, View} from "react-native"; +import type { Screen } from "@/router/helpers/types"; +import { useTheme } from "@react-navigation/native"; +import { SafeAreaView, useSafeAreaInsets } from "react-native-safe-area-context"; +import { Alert, Keyboard, KeyboardAvoidingView, StyleSheet, TouchableWithoutFeedback, View } from "react-native"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; import { NativeText } from "@/components/Global/NativeComponents"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import Reanimated, { FlipInXDown, LinearTransition } from "react-native-reanimated"; -import {AccountService, TurboselfAccount} from "@/stores/account/types"; -import {useAccounts, useCurrentAccount} from "@/stores/account"; +import { AccountService, TurboselfAccount } from "@/stores/account/types"; +import { useAccounts, useCurrentAccount } from "@/stores/account"; import uuid from "@/utils/uuid-v4"; import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index 72c13a94a..28f2a2bad 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -42,9 +42,9 @@ import { NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText } from import ModalHandle from "@/components/Modals/ModalHandle"; import AccountContainerCard from "@/components/Settings/AccountContainerCard"; import { useTheme } from "@react-navigation/native"; -import {get_settings_widgets} from "@/addons/addons"; +import { get_settings_widgets } from "@/addons/addons"; import AsyncStorage from "@react-native-async-storage/async-storage"; -import {AddonPlacementManifest} from "@/addons/types"; +import { AddonPlacementManifest } from "@/addons/types"; import { useFlagsStore } from "@/stores/flags"; import { useAlert } from "@/providers/AlertProvider"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; diff --git a/src/views/settings/SettingsAccessibility.tsx b/src/views/settings/SettingsAccessibility.tsx index e4a1ff346..000051411 100644 --- a/src/views/settings/SettingsAccessibility.tsx +++ b/src/views/settings/SettingsAccessibility.tsx @@ -9,8 +9,8 @@ import { NativeItem, NativeListHeader, NativeIconGradient, + NativeText } from "@/components/Global/NativeComponents"; -import { NativeText } from "@/components/Global/NativeComponents"; import PapillonCheckbox from "@/components/Global/PapillonCheckbox"; import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; import { Switch } from "react-native-gesture-handler"; diff --git a/src/views/settings/SettingsAddons.tsx b/src/views/settings/SettingsAddons.tsx index 59e7deadd..be6c80469 100644 --- a/src/views/settings/SettingsAddons.tsx +++ b/src/views/settings/SettingsAddons.tsx @@ -4,7 +4,7 @@ import { NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; -import type {Screen} from "@/router/helpers/types"; +import type { Screen } from "@/router/helpers/types"; import { View, ScrollView, @@ -16,7 +16,7 @@ import { } from "react-native"; import React from "react"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; -import {useSafeAreaInsets} from "react-native-safe-area-context"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; import { Calendar, Camera, Carrot, Clock, @@ -31,11 +31,11 @@ import { User, UserCog, } from "lucide-react-native"; -import {get_addons_list} from "@/addons/addons"; -import {PressableScale} from "react-native-pressable-scale"; +import { get_addons_list } from "@/addons/addons"; +import { PressableScale } from "react-native-pressable-scale"; import * as Linking from "expo-linking"; import * as FileSystem from "expo-file-system"; -import {AddonManifest} from "@/addons/types"; +import { AddonManifest } from "@/addons/types"; const SettingsAddons: Screen<"SettingsAddons"> = () => { let [ opened, setOpened ] = React.useState(false); diff --git a/src/views/settings/SettingsFlagsInfos.tsx b/src/views/settings/SettingsFlagsInfos.tsx index 4fe58ac43..840b3251d 100644 --- a/src/views/settings/SettingsFlagsInfos.tsx +++ b/src/views/settings/SettingsFlagsInfos.tsx @@ -3,7 +3,7 @@ import { ScrollView, StyleSheet, View } from "react-native"; import { useTheme } from "@react-navigation/native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { NativeText } from "@/components/Global/NativeComponents"; -import {Screen} from "@/router/helpers/types"; +import { Screen } from "@/router/helpers/types"; const SettingsFlagsInfos: Screen<"SettingsFlagsInfos"> = ({ route, navigation }) => { const { title, value } = route.params; diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index 93bee2f6e..8f6d1cf72 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -1,22 +1,22 @@ -import React, {useRef, useState} from "react"; -import {Alert, Image, ScrollView, Switch, TextInput, View} from "react-native"; -import {useTheme} from "@react-navigation/native"; -import type {Screen} from "@/router/helpers/types"; +import React, { useRef, useState } from "react"; +import { Alert, Image, ScrollView, Switch, TextInput, View } from "react-native"; +import { useTheme } from "@react-navigation/native"; +import type { Screen } from "@/router/helpers/types"; import MultiServiceContainerCard from "@/components/Settings/MultiServiceContainerCard"; -import {NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText} from "@/components/Global/NativeComponents"; -import {ImageIcon, PlugZap, Plus, Type} from "lucide-react-native"; -import {useAccounts, useCurrentAccount} from "@/stores/account"; -import {useMultiService} from "@/stores/multiService"; +import { NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; +import { ImageIcon, PlugZap, Plus, Type } from "lucide-react-native"; +import { useAccounts, useCurrentAccount } from "@/stores/account"; +import { useMultiService } from "@/stores/multiService"; import BottomSheet from "@/components/Modals/PapillonBottomSheet"; import * as ImagePicker from "expo-image-picker"; -import {animPapillon} from "@/utils/ui/animations"; -import {ZoomIn, ZoomOut} from "react-native-reanimated"; +import { animPapillon } from "@/utils/ui/animations"; +import { ZoomIn, ZoomOut } from "react-native-reanimated"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; -import {MultiServiceSpace} from "@/stores/multiService/types"; -import {AccountService, PapillonMultiServiceSpace} from "@/stores/account/types"; +import { MultiServiceSpace } from "@/stores/multiService/types"; +import { AccountService, PapillonMultiServiceSpace } from "@/stores/account/types"; import uuid from "@/utils/uuid-v4"; -import {defaultProfilePicture} from "@/utils/ui/default-profile-picture"; -import {defaultTabs} from "@/consts/DefaultTabs"; +import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; +import { defaultTabs } from "@/consts/DefaultTabs"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => { diff --git a/src/views/settings/SettingsMultiServiceSpace.tsx b/src/views/settings/SettingsMultiServiceSpace.tsx index 3f80aecc7..30b142bb0 100644 --- a/src/views/settings/SettingsMultiServiceSpace.tsx +++ b/src/views/settings/SettingsMultiServiceSpace.tsx @@ -1,4 +1,4 @@ -import React, {useRef, useState} from "react"; +import React, { useRef, useState } from "react"; import { ActivityIndicator, Alert, @@ -9,19 +9,19 @@ import { TextInput, View } from "react-native"; -import {useTheme} from "@react-navigation/native"; -import type {Screen} from "@/router/helpers/types"; -import {NativeItem, NativeList, NativeListHeader, NativeText} from "@/components/Global/NativeComponents"; -import {Camera, ChevronDown, CircleAlert, TextCursorInput, Trash2, Type, User2} from "lucide-react-native"; -import {useAccounts} from "@/stores/account"; -import {AccountService, PrimaryAccount} from "@/stores/account/types"; +import { useTheme } from "@react-navigation/native"; +import type { Screen } from "@/router/helpers/types"; +import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; +import { Camera, ChevronDown, CircleAlert, TextCursorInput, Trash2, Type, User2 } from "lucide-react-native"; +import { useAccounts } from "@/stores/account"; +import { AccountService, PrimaryAccount } from "@/stores/account/types"; import * as ImagePicker from "expo-image-picker"; -import {useSafeAreaInsets} from "react-native-safe-area-context"; -import {useMultiService} from "@/stores/multiService"; -import {MultiServiceFeature} from "@/stores/multiService/types"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { useMultiService } from "@/stores/multiService"; +import { MultiServiceFeature } from "@/stores/multiService/types"; import LottieView from "lottie-react-native"; -import {anim2Papillon} from "@/utils/ui/animations"; -import Reanimated, {FadeOut, ZoomIn} from "react-native-reanimated"; +import { anim2Papillon } from "@/utils/ui/animations"; +import Reanimated, { FadeOut, ZoomIn } from "react-native-reanimated"; import PapillonBottomSheet from "@/components/Modals/PapillonBottomSheet"; import * as Haptics from "expo-haptics"; import AccountItem from "@/components/Global/AccountItem"; diff --git a/src/widgets/Components/RestaurantQRCode.tsx b/src/widgets/Components/RestaurantQRCode.tsx index 6ad3da6b2..f982fc792 100644 --- a/src/widgets/Components/RestaurantQRCode.tsx +++ b/src/widgets/Components/RestaurantQRCode.tsx @@ -1,4 +1,4 @@ -import { useTheme, useNavigation } from "@react-navigation/native"; +import { useTheme } from "@react-navigation/native"; import { Pizza } from "lucide-react-native"; import React, { forwardRef, useEffect, useImperativeHandle, useState } from "react"; import { Text, View } from "react-native"; @@ -11,10 +11,6 @@ import { useCurrentAccount } from "@/stores/account"; import QRCode from "react-native-qrcode-svg"; import { AccountService } from "@/stores/account/types"; import { qrcodeFromExternal } from "@/services/qrcode"; -import { StackNavigationProp } from "@react-navigation/stack"; -import { RouteParameters } from "./../../router/helpers/types"; - -type NavigationProps = StackNavigationProp; const RestaurantQRCodeWidget = forwardRef(({ setLoading, From 7b50c97d107d769324968a26687bb19e27a728a4 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Fri, 14 Mar 2025 22:05:17 +0100 Subject: [PATCH 0857/1144] chore(lint): readability --- src/components/Addons/AddonsWebview.tsx | 26 +++--- .../FirstInstallation/ButtonCta.tsx | 2 +- src/components/Global/MissingItem.tsx | 2 +- src/components/Global/PapillonHeader.tsx | 80 +++++++++---------- .../Global/PapillonModernHeader.tsx | 6 +- src/components/Global/PapillonPicker.tsx | 6 +- src/components/Grades/GradeModal.tsx | 24 +++--- src/components/Home/Header.tsx | 12 +-- .../Settings/AccountContainerCard.tsx | 2 +- .../Settings/NotificationContainerCard.tsx | 2 +- src/components/Settings/ReelGallery.tsx | 6 +- src/consts/DefaultTheme.ts | 4 +- src/router/helpers/protected-screen.tsx | 2 +- src/router/navigator/navigator.tsx | 2 +- src/router/screens/settings/navigator.tsx | 2 +- src/services/chats.ts | 2 +- src/services/ecoledirecte/timetable.ts | 2 +- src/services/grades.ts | 2 +- src/services/multi/data/news.ts | 2 +- src/services/multi/data/timetable.ts | 2 +- src/services/skolengo/data/grades.ts | 10 +-- src/services/skolengo/data/homework.ts | 4 +- src/services/skolengo/data/timetable.ts | 2 +- src/services/skolengo/reload-skolengo.ts | 4 +- src/stores/account/index.ts | 4 +- src/views/account/Chat/Modals/ChatCreate.tsx | 2 +- src/views/account/Chat/Modals/ChatDetails.tsx | 10 +-- .../Evaluation/Atoms/SkillLevelBadge.tsx | 2 +- src/views/account/Evaluation/Document.tsx | 2 +- .../Latest/LatestEvaluationsItem.tsx | 2 +- .../Evaluation/Subject/EvaluationList.tsx | 4 +- src/views/account/Grades/Document.tsx | 50 ++++++------ .../account/Grades/Modals/GradeReaction.tsx | 10 +-- src/views/account/Grades/Subject/Subject.tsx | 2 +- src/views/account/Home/Home.tsx | 16 ++-- src/views/account/Home/ModalContent.tsx | 2 +- src/views/account/Homeworks/Atoms/Item.tsx | 2 +- src/views/account/Homeworks/Homeworks.old.tsx | 8 +- src/views/account/Lessons/Atoms/Item.tsx | 2 +- src/views/account/News/Document.tsx | 4 +- src/views/account/Restaurant/Menu.tsx | 12 +-- src/views/account/Week/Week.tsx | 10 +-- src/views/addon/AddonLogs.tsx | 4 +- src/views/addon/AddonPage.tsx | 6 +- .../IdentityProvider/providers/Multi.tsx | 16 ++-- .../providers/UnivLimoges.tsx | 4 +- .../providers/UnivRennes1.tsx | 4 +- .../providers/UnivRennes2.tsx | 4 +- .../ecoledirecte/EcoleDirecteCredentials.tsx | 10 +-- .../login/pronote/PronoteInstanceSelector.tsx | 2 +- .../login/pronote/PronoteManualLocation.tsx | 2 +- src/views/login/pronote/PronoteManualURL.tsx | 8 +- .../skolengo/SkolengoInstanceSelector.tsx | 4 +- src/views/login/skolengo/SkolengoWebview.tsx | 2 +- src/views/settings/ExternalAccount/Izly.tsx | 2 +- .../settings/ExternalAccount/PriceError.tsx | 6 +- .../settings/ExternalAccount/Turboself.tsx | 2 +- src/views/settings/Settings.tsx | 4 +- src/views/settings/SettingsAddons.tsx | 46 +++++------ src/views/settings/SettingsIcons.tsx | 4 +- src/views/settings/SettingsProfile.tsx | 4 +- src/views/welcome/AccountSelector.old.tsx | 37 ++++----- src/views/welcome/ChangelogScreen.tsx | 2 +- src/views/welcome/ColorSelector.tsx | 4 +- src/widgets/Components/RestaurantQRCode.tsx | 5 +- 65 files changed, 262 insertions(+), 272 deletions(-) diff --git a/src/components/Addons/AddonsWebview.tsx b/src/components/Addons/AddonsWebview.tsx index ac2183770..337cf41d4 100644 --- a/src/components/Addons/AddonsWebview.tsx +++ b/src/components/Addons/AddonsWebview.tsx @@ -103,7 +103,7 @@ const AddonsWebview: React.FC = ({ return file; } - function inject_css (css: string, fonts: { bold: string, semiBold: string, medium: string, regular: string, light: string}) { + function inject_css (css: string, fonts: { bold: string, semiBold: string, medium: string, regular: string, light: string }) { css = css.replace("{{FONT_BOLD}}", fonts.bold); css = css.replace("{{FONT_SEMIBOLD}}", fonts.semiBold); css = css.replace("{{FONT_MEDIUM}}", fonts.medium); @@ -145,9 +145,9 @@ const AddonsWebview: React.FC = ({ }, [content, injectedJS, error, logs, showAuthorizations]); return ( - + - + = ({ borderRadius: 5, }}> - + = ({ - + {addon.name + " requiert ta position"} L'extension indique : Nous utilisons ta position pour te manger durant ton sommeil 😈 - - setShowAuthorizations(false)} style={{minWidth: null, maxWidth: null, width: (Dimensions.get("window").width - 42) / 2}} /> - setShowAuthorizations(false)} style={{minWidth: null, maxWidth: null, width: (Dimensions.get("window").width - 42) / 2}}/> + + setShowAuthorizations(false)} style={{ minWidth: null, maxWidth: null, width: (Dimensions.get("window").width - 42) / 2 }} /> + setShowAuthorizations(false)} style={{ minWidth: null, maxWidth: null, width: (Dimensions.get("window").width - 42) / 2 }}/> { error ? ( - + - L'extension à planté... + L'extension à planté... ) : @@ -211,7 +211,7 @@ const AddonsWebview: React.FC = ({ allowUniversalAccessFromFileURLs={true} webviewDebuggingEnabled={true} ref={webview} - style={{ backgroundColor:"transparent"}} + style={{ backgroundColor:"transparent" }} source={{ html: content }} scrollEnabled={scrollEnabled} onError={() => setError(true)} @@ -278,7 +278,7 @@ const AddonsWebview: React.FC = ({ } if (data.type == "navigation_navigate") { - requestNavigate?.(data.to, {addon, data: data.params}); + requestNavigate?.(data.to, { addon, data: data.params }); } if (data.type == "get_user_location") { diff --git a/src/components/FirstInstallation/ButtonCta.tsx b/src/components/FirstInstallation/ButtonCta.tsx index cca330764..f1af3b089 100644 --- a/src/components/FirstInstallation/ButtonCta.tsx +++ b/src/components/FirstInstallation/ButtonCta.tsx @@ -77,7 +77,7 @@ const ButtonCta: React.FC<{ > {icon && newIcon} - + {value} diff --git a/src/components/Global/MissingItem.tsx b/src/components/Global/MissingItem.tsx index 00200e798..3573c4480 100644 --- a/src/components/Global/MissingItem.tsx +++ b/src/components/Global/MissingItem.tsx @@ -52,7 +52,7 @@ const MissingItem: React.FC = ({ {title} - + {description} diff --git a/src/components/Global/PapillonHeader.tsx b/src/components/Global/PapillonHeader.tsx index e1cea6d2d..235fba5fb 100644 --- a/src/components/Global/PapillonHeader.tsx +++ b/src/components/Global/PapillonHeader.tsx @@ -30,55 +30,53 @@ const PapillonHeader: React.FC = ({ const largeHeader = route.params?.outsideNav || Platform.OS !== "ios"; return ( - <> - + + + {route.params?.outsideNav && Platform.OS !== "ios" && ( + navigation.goBack()} + > + + + )} + + + - {route.params?.outsideNav && Platform.OS !== "ios" && ( - navigation.goBack()} - > - - - )} + {children} - - - - {children} - - {Platform.OS === "ios" && ( - - )} - + {Platform.OS === "ios" && ( + + )} - - + + ); }; diff --git a/src/components/Global/PapillonModernHeader.tsx b/src/components/Global/PapillonModernHeader.tsx index 13782dcfe..de1aee178 100644 --- a/src/components/Global/PapillonModernHeader.tsx +++ b/src/components/Global/PapillonModernHeader.tsx @@ -41,7 +41,7 @@ const LinearGradientModernHeader: React.FC = ({ children, out const enableBlur = Platform.OS === "ios" && !isExpoGo() && parseInt(Platform.Version) >= 18; return ( - <> + {enableBlur ? ( = ({ children, out }} /> } - + ); }; @@ -156,7 +156,7 @@ const NativeModernHeader: React.FC = ({ children, outsideNav justifyContent: "space-between", alignItems: "center", gap: 8, - backgroundColor: tint ? tint : theme.colors.card + "10", + backgroundColor: tint ?? theme.colors.card + "10", borderBottomColor: theme.colors.border, borderBottomWidth: 0.5, }]} diff --git a/src/components/Global/PapillonPicker.tsx b/src/components/Global/PapillonPicker.tsx index 9a9c0aa0c..17c04df88 100644 --- a/src/components/Global/PapillonPicker.tsx +++ b/src/components/Global/PapillonPicker.tsx @@ -87,9 +87,9 @@ const PapillonPicker: React.FC = ({ // @ts-ignore menuState: (item.checked || item === selected) ? "on" : "off", icon: { - type: typeof item !== "string" ? "IMAGE_SYSTEM" : "IMAGE_SYSTEM", + type: "IMAGE_SYSTEM", imageValue: { - systemName: typeof item !== "string" ? (item.sfSymbol ? item.sfSymbol : "") : "", + systemName: typeof item !== "string" ? (item.sfSymbol ?? "") : "", }, } }; @@ -201,7 +201,7 @@ const PapillonPicker: React.FC = ({ size={20} strokeWidth={2.5} color={theme.colors.primary} - style={{ marginRight: 10}} + style={{ marginRight: 10 }} /> )} diff --git a/src/components/Grades/GradeModal.tsx b/src/components/Grades/GradeModal.tsx index fceb209b2..5f6688447 100644 --- a/src/components/Grades/GradeModal.tsx +++ b/src/components/Grades/GradeModal.tsx @@ -98,10 +98,10 @@ const GradeModal: React.FC = ({ animationType="fade" onRequestClose={onClose} > - - + + = ({ blurRadius={200} /> - + Veux-tu vraiment supprimer cette réaction ? Cette action est irréversible. - + = ({ }} > = ({ > - Enregistrer + Enregistrer = ({ borderRadius: 100, }} /> - Instagram + Instagram = ({ > - Autre + Autre diff --git a/src/components/Home/Header.tsx b/src/components/Home/Header.tsx index b1aaa28d7..fbdb82468 100644 --- a/src/components/Home/Header.tsx +++ b/src/components/Home/Header.tsx @@ -234,7 +234,7 @@ const Header: React.FC<{ // @ts-expect-error : average Reanimated issue. entering={FadeInRight.easing(Easing.bezier(0, 0, 0, 1)).duration(500).delay(250).withInitialValues({ opacity: 0, - transform: [{translateX: 20}] + transform: [{ translateX: 20 }] })} style={{ gap: 15, @@ -254,7 +254,7 @@ const Header: React.FC<{ ( - { + { let temp = addonsTitle; temp[index] = addon.name; setAddonsTitle(temp); @@ -267,7 +267,7 @@ const Header: React.FC<{ marginBottom: 10 }}> {addonsTitle[index]} + style={{ flex: 1 }}>{addonsTitle[index]} { @@ -304,7 +304,7 @@ const HeaderButton: React.FC<{ text: string scrolled: boolean, onPress: () => void -}> = ({icon, index, text, scrolled, onPress}) => { +}> = ({ icon, index, text, scrolled, onPress }) => { const newIcon = React.cloneElement(icon, { size: 24, @@ -316,7 +316,7 @@ const HeaderButton: React.FC<{ //@ts-expect-error entering={FadeInRight.easing(Easing.bezier(0, 0, 0, 1)).duration(300).delay((50 * index)).withInitialValues({ opacity: 0, - transform: [{translateX: 20}] + transform: [{ translateX: 20 }] })} > diff --git a/src/components/Settings/AccountContainerCard.tsx b/src/components/Settings/AccountContainerCard.tsx index bc9a6dafb..6415919fc 100644 --- a/src/components/Settings/AccountContainerCard.tsx +++ b/src/components/Settings/AccountContainerCard.tsx @@ -100,7 +100,7 @@ const AccountContainerCard = ({ account, onPress }: { }} > - + diff --git a/src/components/Settings/ReelGallery.tsx b/src/components/Settings/ReelGallery.tsx index 309c3b622..25000484f 100644 --- a/src/components/Settings/ReelGallery.tsx +++ b/src/components/Settings/ReelGallery.tsx @@ -16,8 +16,8 @@ import { Reel } from "@/services/shared/Reel"; // Components const GradeIndicator = ({ value, outOf, color }: { value: number; outOf: number; color: string }) => ( - {value.toFixed(2)} - /{outOf} + {value.toFixed(2)} + /{outOf} ); @@ -45,7 +45,7 @@ const ReelThumbnail = ({ reel, onPress, width }: { reel: Reel; onPress: () => vo justifyContent: "center", alignItems: "center" }]}> - Image non disponible + Image non disponible ); } diff --git a/src/consts/DefaultTheme.ts b/src/consts/DefaultTheme.ts index 01ef24999..f670126df 100644 --- a/src/consts/DefaultTheme.ts +++ b/src/consts/DefaultTheme.ts @@ -4,8 +4,8 @@ export const DefaultTheme: Theme = { meta: { name: "Default", author: "PapillonApp", - icon: { uri: "https://play-lh.googleusercontent.com/wpV-VScxugHvexfYPURrkhpCxr1un_eJupTk9rHFf9TKfCBlYcrPoqyaJCVtWlX4Zw"}, - darkIcon: { uri: "https://play-lh.googleusercontent.com/wpV-VScxugHvexfYPURrkhpCxr1un_eJupTk9rHFf9TKfCBlYcrPoqyaJCVtWlX4Zw"}, + icon: { uri: "https://play-lh.googleusercontent.com/wpV-VScxugHvexfYPURrkhpCxr1un_eJupTk9rHFf9TKfCBlYcrPoqyaJCVtWlX4Zw" }, + darkIcon: { uri: "https://play-lh.googleusercontent.com/wpV-VScxugHvexfYPURrkhpCxr1un_eJupTk9rHFf9TKfCBlYcrPoqyaJCVtWlX4Zw" }, path: "default" }, lightModifier: { diff --git a/src/router/helpers/protected-screen.tsx b/src/router/helpers/protected-screen.tsx index fbf9394b1..ff96ece37 100644 --- a/src/router/helpers/protected-screen.tsx +++ b/src/router/helpers/protected-screen.tsx @@ -27,7 +27,7 @@ const ProtectedScreen: React.FC<{ export const protectScreenComponent = ( Component: React.ComponentType> -): Screen => ({ navigation, route}) => ( +): Screen => ({ navigation, route }) => ( = ({ screenOptions, ...rest }) => { - const {isTablet} = useScreenDimensions(); + const { isTablet } = useScreenDimensions(); const { state, diff --git a/src/router/screens/settings/navigator.tsx b/src/router/screens/settings/navigator.tsx index 99cc34e52..e58cf8ca2 100644 --- a/src/router/screens/settings/navigator.tsx +++ b/src/router/screens/settings/navigator.tsx @@ -26,7 +26,7 @@ export const SettingsScreen: Screen<"SettingStack"> = ({ route }) => { const ConditionnalAlertProvider = (props: any) => { if(Platform.OS === "android") { return ( - + {props.children} ); diff --git a/src/services/chats.ts b/src/services/chats.ts index 07b06a293..e71d4c68e 100644 --- a/src/services/chats.ts +++ b/src/services/chats.ts @@ -12,7 +12,7 @@ export const getChats = async (account: T): Promise throw new Error("ecoledirecte: unknown class type"); }; -export const getTimetableForWeek = async (account: EcoleDirecteAccount, rangeDate: { start: Date, end: Date}): Promise => { +export const getTimetableForWeek = async (account: EcoleDirecteAccount, rangeDate: { start: Date, end: Date }): Promise => { if (!account.authentication.session) throw new ErrorServiceUnauthenticated("ecoledirecte"); diff --git a/src/services/grades.ts b/src/services/grades.ts index 9d05966e8..1ce69c587 100644 --- a/src/services/grades.ts +++ b/src/services/grades.ts @@ -87,7 +87,7 @@ export async function updateGradesAndAveragesInCache (accoun let averages: AverageOverview = { subjects: [], overall: { value: null, disabled: true, status: null }, - classOverall: { value: null, disabled: true, status: null} + classOverall: { value: null, disabled: true, status: null } }; try { diff --git a/src/services/multi/data/news.ts b/src/services/multi/data/news.ts index 4c8f0d2e2..d93cc51df 100644 --- a/src/services/multi/data/news.ts +++ b/src/services/multi/data/news.ts @@ -9,7 +9,7 @@ const parseInformation = (i: ActualitiesResponse): Information => ({ title: i.title, date: new Date(i.pubDate), acknowledged: false, - attachments: [{"name": i.title,"type":"link" as AttachmentType, "url": i.link}], + attachments: [{ "name": i.title,"type":"link" as AttachmentType, "url": i.link }], content: i.content, author: "Actualités", category: "Actualités", diff --git a/src/services/multi/data/timetable.ts b/src/services/multi/data/timetable.ts index 2d32e0b5c..e827d34e2 100644 --- a/src/services/multi/data/timetable.ts +++ b/src/services/multi/data/timetable.ts @@ -26,7 +26,7 @@ export const getTimetableForWeek = async (account: MultiAccount, weekNumber: num if (!account.instance) throw new ErrorServiceUnauthenticated("Multi"); - const timetable = await account.instance.getSchedules({startDate: weekNumberToDateRange(weekNumber).start.toISOString().split("T")[0], endDate:weekNumberToDateRange(weekNumber).end.toISOString().split("T")[0]}); + const timetable = await account.instance.getSchedules({ startDate: weekNumberToDateRange(weekNumber).start.toISOString().split("T")[0], endDate:weekNumberToDateRange(weekNumber).end.toISOString().split("T")[0] }); const eventsList = timetable.plannings.flatMap((planning) => planning.events.map((event: EventResponse) => ({ id: event.id, diff --git a/src/services/skolengo/data/grades.ts b/src/services/skolengo/data/grades.ts index f8c32a6aa..5a44e8837 100644 --- a/src/services/skolengo/data/grades.ts +++ b/src/services/skolengo/data/grades.ts @@ -8,16 +8,16 @@ const SKOLENGO_DEFAULT_SCALE = 20; const decodeGradeNumber = (value?:number | null): GradeValue => typeof value === "number" ? - { value, disabled: false, status: null} + { value, disabled: false, status: null } : { value: null, disabled: true, status: null }; -const getSubjectMinMax = (evalSubj: Evaluation): {min: GradeValue, max:GradeValue, outOf: GradeValue} => { +const getSubjectMinMax = (evalSubj: Evaluation): { min: GradeValue, max:GradeValue, outOf: GradeValue } => { const outOf = decodeGradeNumber(evalSubj.scale || SKOLENGO_DEFAULT_SCALE); - if(evalSubj.evaluations.filter(e=>e.evaluationResult.mark !== null && !e.evaluationResult.nonEvaluationReason).length === 0) return {min: { value: null, disabled: true, status: null } , max: { value: null, disabled: true, status: null }, outOf}; + if(evalSubj.evaluations.filter(e=>e.evaluationResult.mark !== null && !e.evaluationResult.nonEvaluationReason).length === 0) return { min: { value: null, disabled: true, status: null } , max: { value: null, disabled: true, status: null }, outOf }; const [minimum, maximum] = evalSubj.evaluations.filter(e=>e.evaluationResult.mark !== null) .map(e=>((e.evaluationResult.mark!)/(e.scale || SKOLENGO_DEFAULT_SCALE)) * (evalSubj.scale || SKOLENGO_DEFAULT_SCALE)) .reduce(([minAcc, maxAcc], e) => [Math.min(minAcc, e), Math.max(maxAcc, e)], [evalSubj.scale || SKOLENGO_DEFAULT_SCALE, 0]); - return {min: { value: minimum, disabled: false, status: null } , max: { value: maximum, disabled: false, status: null }, outOf}; + return { min: { value: minimum, disabled: false, status: null } , max: { value: maximum, disabled: false, status: null }, outOf }; }; export const getGradesAndAverages = async (account: SkolengoAccount, periodName: string): Promise<{ @@ -46,7 +46,7 @@ export const getGradesAndAverages = async (account: SkolengoAccount, periodName: })), }; - const grades: Grade[] = evals.map(e=>e.evaluations.map(f=>({...f, evaluation: e}))).flat().map(g => ({ + const grades: Grade[] = evals.map(e=>e.evaluations.map(f=>({ ...f, evaluation: e }))).flat().map(g => ({ id: g.id, subjectName: g.evaluation.subject.label, description: g.title || g.topic || "Evaluation", diff --git a/src/services/skolengo/data/homework.ts b/src/services/skolengo/data/homework.ts index 7dba43ec5..212f61033 100644 --- a/src/services/skolengo/data/homework.ts +++ b/src/services/skolengo/data/homework.ts @@ -25,7 +25,7 @@ export const getHomeworkForWeek = async (account: SkolengoAccount, epochWeekNumb if (!account.instance) throw new ErrorServiceUnauthenticated("skolengo"); - const {start, end} = weekNumberToDateRange(epochWeekNumber); + const { start, end } = weekNumberToDateRange(epochWeekNumber); const homeworks = await account.instance.getHomeworkAssignments(undefined, toSkolengoDate(start), toSkolengoDate(end)); @@ -38,7 +38,7 @@ export const toggleHomeworkState = async (account: SkolengoAccount, h: Homework) if (!account.instance) throw new ErrorServiceUnauthenticated("skolengo"); - await account.instance?.patchHomeworkAssignment(void 0, h.id, {done: !h.done}); + await account.instance?.patchHomeworkAssignment(void 0, h.id, { done: !h.done }); //await pronote.assignmentStatus(account.instance, h.id, !h.done); log(`SKOLENGO->toggleHomeworkState(): Homework ${h.id} marked as ${h.done ? "not done" : "done"}.`, "skolengo"); diff --git a/src/services/skolengo/data/timetable.ts b/src/services/skolengo/data/timetable.ts index 246d26678..c58f37c38 100644 --- a/src/services/skolengo/data/timetable.ts +++ b/src/services/skolengo/data/timetable.ts @@ -24,7 +24,7 @@ export const getTimetableForWeek = async (account: SkolengoAccount, epochWeekNum if (!account.instance) throw new ErrorServiceUnauthenticated("skolengo"); - const {start, end} = weekNumberToDateRange(epochWeekNumber); + const { start, end } = weekNumberToDateRange(epochWeekNumber); const agenda = await account.instance.getAgenda(undefined, toSkolengoDate(start), toSkolengoDate(end)); const lessons = agenda.map(e=>e.lessons).flat(); diff --git a/src/services/skolengo/reload-skolengo.ts b/src/services/skolengo/reload-skolengo.ts index 53649320e..3615ae048 100644 --- a/src/services/skolengo/reload-skolengo.ts +++ b/src/services/skolengo/reload-skolengo.ts @@ -6,8 +6,8 @@ import { Skolengo } from "scolengo-api"; export const reload = async (account: SkolengoAccount): Promise> => { if(!account.instance || !(account.instance instanceof Skolengo)) { - const {instance, authentication} = await getSkolengoAccount(account.authentication, account.userInfo); - return {instance, authentication}; + const { instance, authentication } = await getSkolengoAccount(account.authentication, account.userInfo); + return { instance, authentication }; } return { diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index 721fb31ae..cf670a271 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -105,11 +105,11 @@ export const useCurrentAccount = create()((set, get) => ({ } const linkedAccounts = account.linkedExternalLocalIDs.map((linkedID) => { - return {...accounts.find((acc) => acc.localID === linkedID)}; + return { ...accounts.find((acc) => acc.localID === linkedID) }; }).filter(Boolean) as ExternalAccount[] ?? []; const associatedAccounts = account.associatedAccountsLocalIDs?.map((associatedID) => { - return {...accounts.find((acc) => acc.localID === associatedID)}; + return { ...accounts.find((acc) => acc.localID === associatedID) }; }).filter(Boolean) as PrimaryAccount[] ?? []; info(`found ${linkedAccounts.length} external accounts and ${associatedAccounts.length} associated accounts`, "switchTo"); diff --git a/src/views/account/Chat/Modals/ChatCreate.tsx b/src/views/account/Chat/Modals/ChatCreate.tsx index 9e35c3059..c6dae2f7f 100644 --- a/src/views/account/Chat/Modals/ChatCreate.tsx +++ b/src/views/account/Chat/Modals/ChatCreate.tsx @@ -171,7 +171,7 @@ const ChatCreate: Screen<"ChatCreate"> = ({ navigation }) => { )} - + 0)} onPress={() => { if (!subject) { Alert.alert( diff --git a/src/views/account/Chat/Modals/ChatDetails.tsx b/src/views/account/Chat/Modals/ChatDetails.tsx index a83e7fb68..6499c05b0 100644 --- a/src/views/account/Chat/Modals/ChatDetails.tsx +++ b/src/views/account/Chat/Modals/ChatDetails.tsx @@ -70,13 +70,13 @@ const ChatDetails: Screen<"ChatDetails"> = ({ navigation, route }) => { textColor={getProfileColorByName(creatorName).dark} size={55} /> - + {creatorName} - {chat.subject} + {chat.subject} - + navigation.navigate("ChatThemes", { handle: chat, @@ -103,7 +103,7 @@ const ChatDetails: Screen<"ChatDetails"> = ({ navigation, route }) => { - + {recipient.name === account.name ? ( = ({ navigation, route }) => { {recipients.length > maxRecipientsShow ? ( - + En afficher plus diff --git a/src/views/account/Evaluation/Atoms/SkillLevelBadge.tsx b/src/views/account/Evaluation/Atoms/SkillLevelBadge.tsx index be6055231..63dc22963 100644 --- a/src/views/account/Evaluation/Atoms/SkillLevelBadge.tsx +++ b/src/views/account/Evaluation/Atoms/SkillLevelBadge.tsx @@ -77,7 +77,7 @@ export const SkillLevelBadge: React.FC = ({ skillLevel }) color: "#006AB7", fontSize: 15, fontFamily: "semibold", - transform: [{ translateY: -2 }, {translateX: 0.5}], + transform: [{ translateY: -2 }, { translateX: 0.5 }], }}> a diff --git a/src/views/account/Evaluation/Document.tsx b/src/views/account/Evaluation/Document.tsx index a430c587e..c82c15b0a 100644 --- a/src/views/account/Evaluation/Document.tsx +++ b/src/views/account/Evaluation/Document.tsx @@ -199,7 +199,7 @@ const EvaluationDocument: Screen<"EvaluationDocument"> = ({ route, navigation }) } - trailing={ + trailing={ {skill.pillarPrefixes.join(", ")} } > diff --git a/src/views/account/Evaluation/Latest/LatestEvaluationsItem.tsx b/src/views/account/Evaluation/Latest/LatestEvaluationsItem.tsx index ec499f798..144920461 100644 --- a/src/views/account/Evaluation/Latest/LatestEvaluationsItem.tsx +++ b/src/views/account/Evaluation/Latest/LatestEvaluationsItem.tsx @@ -147,7 +147,7 @@ const EvaluationLatestItem: React.FC = ({ {skillLevelsMoreNumber > 0 && ( {`+${skillLevelsMoreNumber}`} diff --git a/src/views/account/Evaluation/Subject/EvaluationList.tsx b/src/views/account/Evaluation/Subject/EvaluationList.tsx index 04aedbd2e..632b07f0f 100644 --- a/src/views/account/Evaluation/Subject/EvaluationList.tsx +++ b/src/views/account/Evaluation/Subject/EvaluationList.tsx @@ -60,7 +60,7 @@ const SubjectItem: React.FC = ({ evaluation={item} index={index} onPress={() => { - navigation.navigate("EvaluationDocument", { evaluation:item, allEvaluations}); + navigation.navigate("EvaluationDocument", { evaluation:item, allEvaluations }); }} /> )} @@ -143,7 +143,7 @@ const SubjectEvaluationItem: React.FC = ({ subject, ))} {skillLevelsMoreNumber > 0 && ( - + {`+${skillLevelsMoreNumber}`} )} diff --git a/src/views/account/Grades/Document.tsx b/src/views/account/Grades/Document.tsx index 5578b6db4..095248471 100644 --- a/src/views/account/Grades/Document.tsx +++ b/src/views/account/Grades/Document.tsx @@ -177,38 +177,38 @@ const GradeDocument: Screen<"GradeDocument"> = ({ route, navigation }) => { title: "Moyenne générale", description: "Impact estimé sur la moyenne générale", value: - gradeDiff.difference === undefined - ? "???" - : (gradeDiff.difference > 0 - ? "- " - : gradeDiff.difference === 0 - ? "+/- " - : "+ ") + - gradeDiff.difference.toFixed(2).replace("-", "") + - " pts", + gradeDiff.difference === undefined + ? "???" + : (gradeDiff.difference > 0 + ? "- " + : gradeDiff.difference === 0 + ? "+/- " + : "+ ") + + gradeDiff.difference.toFixed(2).replace("-", "") + + " pts", color: - gradeDiff.difference === undefined - ? void 0 - : gradeDiff.difference < 0 - ? "#4CAF50" - : gradeDiff.difference === 0 - ? theme.colors.text - : "#F44336", + gradeDiff.difference === undefined + ? void 0 + : gradeDiff.difference < 0 + ? "#4CAF50" + : gradeDiff.difference === 0 + ? theme.colors.text + : "#F44336", }, !grade.average.disabled && { icon: , title: "Moyenne de la classe", description: "Impact de la note sur la moyenne de la classe", value: - classDiff.difference === undefined - ? "???" - : (classDiff.difference > 0 - ? "- " - : gradeDiff.difference === 0 - ? "+/- " - : "+ ") + - classDiff.difference.toFixed(2).replace("-", "") + - " pts", + classDiff.difference === undefined + ? "???" + : (classDiff.difference > 0 + ? "- " + : gradeDiff.difference === 0 + ? "+/- " + : "+ ") + + classDiff.difference.toFixed(2).replace("-", "") + + " pts", }, ], }, diff --git a/src/views/account/Grades/Modals/GradeReaction.tsx b/src/views/account/Grades/Modals/GradeReaction.tsx index 24103a474..97ae2f847 100644 --- a/src/views/account/Grades/Modals/GradeReaction.tsx +++ b/src/views/account/Grades/Modals/GradeReaction.tsx @@ -166,12 +166,12 @@ const GradeReaction: Screen<"GradeReaction"> = ({ navigation, route }) => { }; return (isCameraPermissionGranted == PermissionStatus.DENIED || isMediaLibraryPermissionGranted == PermissionStatus.DENIED) ? ( - - 🫣 - On ne te voit pas… - Pour réagir à tes notes, Papillon a besoin d'un accès à ta caméra et à ta librairie photo. + + 🫣 + On ne te voit pas… + Pour réagir à tes notes, Papillon a besoin d'un accès à ta caméra et à ta librairie photo. - + = ({ data={sortedData} renderItem={renderItem} ListHeaderComponent={ListHeaderComponent} - ListHeaderComponentStyle={{zIndex: 99}} + ListHeaderComponentStyle={{ zIndex: 99 }} keyExtractor={keyExtractor} removeClippedSubviews={false} maxToRenderPerBatch={10} diff --git a/src/views/account/Home/Home.tsx b/src/views/account/Home/Home.tsx index 7d8d004bf..27bd59391 100644 --- a/src/views/account/Home/Home.tsx +++ b/src/views/account/Home/Home.tsx @@ -69,7 +69,7 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { const focused = useIsFocused(); const { playHaptics } = useSoundHapticsWrapper(); - const {isTablet} = useScreenDimensions(); + const { isTablet } = useScreenDimensions(); let scrollRef = useAnimatedRef(); let scrollOffset = useScrollViewOffset(scrollRef); @@ -100,7 +100,7 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { [0, 265], [1, 0.9], Extrapolation.CLAMP - )}, + ) }, ] })); @@ -134,12 +134,12 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { backgroundColor: colors.card, overflow: "hidden", transform: [ - {translateY: interpolate( + { translateY: interpolate( scrollOffset.value, [-1000, 0, 125, 265 ], [-1000, 0, 105, 0], Extrapolation.CLAMP - )} + ) } ], })); @@ -163,12 +163,12 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { top: 10, left: "50%", transform: [ - {translateX: interpolate( + { translateX: interpolate( scrollOffset.value, [125, 200], [-25, -2], Extrapolation.CLAMP - )} + ) } ], width: interpolate( scrollOffset.value, @@ -194,7 +194,7 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { })); return ( - + {!modalOpen && focused && !isTablet && ( )} @@ -241,7 +241,7 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { refreshControl={ setRefreshing(true)} - style={{zIndex: 100}} + style={{ zIndex: 100 }} progressViewOffset={285 + insets.top} />} showsVerticalScrollIndicator={false} diff --git a/src/views/account/Home/ModalContent.tsx b/src/views/account/Home/ModalContent.tsx index d4c899497..6913d440b 100644 --- a/src/views/account/Home/ModalContent.tsx +++ b/src/views/account/Home/ModalContent.tsx @@ -175,7 +175,7 @@ const ModalContent: React.FC = ({ navigation, refresh, endRef strokeWidth={2} color={colors.text} /> - + Papillon vient d'être mis à jour à la version {PackageJSON.version} ! diff --git a/src/views/account/Homeworks/Atoms/Item.tsx b/src/views/account/Homeworks/Atoms/Item.tsx index cf79fa6a8..f1767ef11 100644 --- a/src/views/account/Homeworks/Atoms/Item.tsx +++ b/src/views/account/Homeworks/Atoms/Item.tsx @@ -222,7 +222,7 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total } opacity={0.6} color={theme.colors.text} /> - {timestampToString(homework.due)} + {timestampToString(homework.due)} )} diff --git a/src/views/account/Homeworks/Homeworks.old.tsx b/src/views/account/Homeworks/Homeworks.old.tsx index 61a1bf3e3..ccaa3d60d 100644 --- a/src/views/account/Homeworks/Homeworks.old.tsx +++ b/src/views/account/Homeworks/Homeworks.old.tsx @@ -78,8 +78,8 @@ const HomeworksPage: React.FC = React.memo(({ index, isActiv style={{ flex: 1, padding: 16, paddingTop: 0 }} > - - + + {index} @@ -224,7 +224,7 @@ const HomeworksScreen: Screen<"Homeworks"> = ({ navigation }) => { initialIndex={initialIndex} pageBuffer={3} PageComponent={ - ({index, isActive}) => ( + ({ index, isActive }) => ( = ({ navigation }) => { getDayName={getDayName} /> )} - style={{ flex: 1}} + style={{ flex: 1 }} onPageChange={setEpochWeekNumber} /> )} diff --git a/src/views/account/Lessons/Atoms/Item.tsx b/src/views/account/Lessons/Atoms/Item.tsx index 7341922b6..45bf0c345 100644 --- a/src/views/account/Lessons/Atoms/Item.tsx +++ b/src/views/account/Lessons/Atoms/Item.tsx @@ -58,7 +58,7 @@ export const TimetableItem: React.FC<{ {item.statusText && ( - {item.statusText} + {item.statusText} )} diff --git a/src/views/account/News/Document.tsx b/src/views/account/News/Document.tsx index 2aed7c889..6bffa11e1 100644 --- a/src/views/account/News/Document.tsx +++ b/src/views/account/News/Document.tsx @@ -73,7 +73,7 @@ const NewsItem: Screen<"NewsItem"> = ({ route, navigation }) => { - + {message.title === "" ? message.author : message.title} {message.title === "" ? formatDate(message.date.toString()) : message.author} @@ -187,7 +187,7 @@ const NewsItem: Screen<"NewsItem"> = ({ route, navigation }) => { /> - {isED && + {isED && = ({ route, navigation }) => { const [currentWeek, setCurrentWeek] = useState(0); const [showDatePicker, setShowDatePicker] = useState(false); const [pickerDate, setPickerDate] = React.useState(new Date(new Date().setHours(0, 0, 0, 0))); - const [isMenuLoading, setMenuLoading] = useState(false); + const [menuLoading, setMenuLoading] = useState(false); const [isInitialised, setIsInitialised] = useState(false); const [selectedIndex, setSelectedIndex] = useState(0); const [allCards, setAllCards] = useState | null>(null); @@ -197,7 +197,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { try { const [balance, history, cardnumber, booking] = await Promise.all([ balanceFromExternal(account, isRefreshing).catch(err => { - console.warn(`Error fetching balance for account ${account}:`, err); + console.warn(`Error fetching balance for account ${account.username}:`, err); return []; }), reservationHistoryFromExternal(account).catch(err => { @@ -370,7 +370,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { /> )} - + {(allCards ?? [])?.length > 0 && ( = ({ route, navigation }) => { )} - {(currentMenu || (allBookings && allBookings?.some((terminal) => terminal.days.some((day) => day.date?.toDateString() === pickerDate.toDateString())))) && + {(currentMenu || (allBookings?.some((terminal) => terminal.days.some((day) => day.date?.toDateString() === pickerDate.toDateString())))) && = ({ route, navigation }) => { - setShowDatePicker(true)}> + setShowDatePicker(true)}> = ({ route, navigation }) => { {currentMenu ? <> - {isMenuLoading ? ( + {menuLoading ? ( ) : currentMenu?.lunch || currentMenu?.dinner ? ( <> diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index 9433b42d0..f379090c5 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -171,7 +171,7 @@ const HeaderItem = memo(({ header }) => { > {new Date(start + i * 24 * 60 * 60 * 1000 - ).toLocaleDateString("fr-FR", {weekday: "short"})} + ).toLocaleDateString("fr-FR", { weekday: "short" })} )} (({ header }) => { {new Date(start + i * 24 * 60 * 60 * 1000 ).toLocaleDateString("fr-FR", - cols > 1 ? {day: "numeric"} : { + cols > 1 ? { day: "numeric" } : { weekday: "long", day: "numeric", month: "long", @@ -323,13 +323,13 @@ const Week: Screen<"Week"> = ({ route, navigation }) => { /> Aucun agenda externe Importez un calendrier depuis une URL de votre agenda externe tel que ADE ou Moodle. @@ -402,7 +402,7 @@ const Week: Screen<"Week"> = ({ route, navigation }) => { numberOfDays={displayMode === "Semaine" ? 5 : displayMode === "3 jours" ? 3 : 1} hideWeekDays={displayMode === "Semaine" ? [6, 7] : []} pagesPerSide={2} - scrollByDay={displayMode === "Semaine" ? false : true} + scrollByDay={displayMode !== "Semaine"} events={events} onDateChanged={handleDateChange} initialLocales={LOCALES} diff --git a/src/views/addon/AddonLogs.tsx b/src/views/addon/AddonLogs.tsx index 90d28db4d..58a1c5a38 100644 --- a/src/views/addon/AddonLogs.tsx +++ b/src/views/addon/AddonLogs.tsx @@ -14,7 +14,7 @@ const AddonLogs: Screen<"AddonLogs"> = ({ navigation, route }) => { }); return ( - + {logs.map((log, index) => ( = ({ navigation, route }) => { > {log.message} {log.date.toISOString()} diff --git a/src/views/addon/AddonPage.tsx b/src/views/addon/AddonPage.tsx index 96be898f4..487d9f5af 100644 --- a/src/views/addon/AddonPage.tsx +++ b/src/views/addon/AddonPage.tsx @@ -18,7 +18,7 @@ const AddonPage: Screen<"AddonPage"> = ({ navigation, route }) => { }); return ( - + = ({ navigation, route }) => { url={addon.manifest.placement[addon.index].main} scrollEnabled={true} inset={insets} - setTitle={(title) => navigation.setOptions({headerTitle: title})} + setTitle={(title) => navigation.setOptions({ headerTitle: title })} data={data} requestNavigate={(url, data) => { //find the placement @@ -43,7 +43,7 @@ const AddonPage: Screen<"AddonPage"> = ({ navigation, route }) => { if (index == -1) { Alert.alert("Error", "The requested page was not found."); //TODO: transfer error to webview } else { - let newAddon: AddonPlacementManifest = {manifest: addon.manifest, index: index}; + let newAddon: AddonPlacementManifest = { manifest: addon.manifest, index: index }; // @ts-ignore "Very hard to type, need to think about" navigation.push("Addon" + from + "Page", { addon: newAddon, from: from, data: data.data }); } diff --git a/src/views/login/IdentityProvider/providers/Multi.tsx b/src/views/login/IdentityProvider/providers/Multi.tsx index 5ed4953e0..d8cc036ec 100644 --- a/src/views/login/IdentityProvider/providers/Multi.tsx +++ b/src/views/login/IdentityProvider/providers/Multi.tsx @@ -85,15 +85,13 @@ const Muli_Login: Screen<"Multi_Login"> = ({ route, navigation }) => { }; return ( - <> - handleLogin(username, password)} - /> - + handleLogin(username, password)} + /> ); }; diff --git a/src/views/login/IdentityProvider/providers/UnivLimoges.tsx b/src/views/login/IdentityProvider/providers/UnivLimoges.tsx index da79aacc1..55670d19a 100644 --- a/src/views/login/IdentityProvider/providers/UnivLimoges.tsx +++ b/src/views/login/IdentityProvider/providers/UnivLimoges.tsx @@ -131,11 +131,11 @@ const UnivLimoges_Login: Screen<"UnivLimoges_Login"> = ({ navigation }) => { }} /> - + Connexion au compte Biome - + Chargement de Biome... diff --git a/src/views/login/IdentityProvider/providers/UnivRennes1.tsx b/src/views/login/IdentityProvider/providers/UnivRennes1.tsx index f2ffa5b70..56d53b8ea 100644 --- a/src/views/login/IdentityProvider/providers/UnivRennes1.tsx +++ b/src/views/login/IdentityProvider/providers/UnivRennes1.tsx @@ -103,11 +103,11 @@ const UnivRennes1_Login: Screen<"UnivRennes1_Login"> = ({ navigation }) => { }} /> - + Connexion au compte Sésame - + {isLoadingText} diff --git a/src/views/login/IdentityProvider/providers/UnivRennes2.tsx b/src/views/login/IdentityProvider/providers/UnivRennes2.tsx index 954ea3084..fe981104e 100644 --- a/src/views/login/IdentityProvider/providers/UnivRennes2.tsx +++ b/src/views/login/IdentityProvider/providers/UnivRennes2.tsx @@ -145,11 +145,11 @@ const UnivRennes2_Login: Screen<"UnivRennes2_Login"> = ({ navigation }) => { }} /> - + Connexion au compte Sésame - + {isLoadingText} diff --git a/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx b/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx index 6b67b2abd..04c01172a 100644 --- a/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx +++ b/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx @@ -48,7 +48,7 @@ const EcoleDirecteCredentials: Screen<"EcoleDirecteCredentials"> = ({ navigation setLoading(true); setError(null); if (username === "demo" && password === "demo") { - setDoubleAuthChallenge({answers: ["demo00", "demo01", "demo02", "demo03", "demo04", "demo05", "demo06", "demo07", "demo08", "demo09"], question: "Ceci est une question de démonstration"}); + setDoubleAuthChallenge({ answers: ["demo00", "demo01", "demo02", "demo03", "demo04", "demo05", "demo06", "demo07", "demo08", "demo09"], question: "Ceci est une question de démonstration" }); } if (currentSession === null) { @@ -175,7 +175,7 @@ const EcoleDirecteCredentials: Screen<"EcoleDirecteCredentials"> = ({ navigation setOpened={() => setDoubleAuthChallenge(null)} > - + {doubleAuthChallenge.question} Réponds à la question suivante pour continuer ton authentification @@ -200,10 +200,10 @@ const EcoleDirecteCredentials: Screen<"EcoleDirecteCredentials"> = ({ navigation }} /> - + {doubleAuthChallenge.answers.map((answer, index) => ( = ({ navigation ))} - + diff --git a/src/views/login/pronote/PronoteInstanceSelector.tsx b/src/views/login/pronote/PronoteInstanceSelector.tsx index a3b9c1ef4..223265915 100644 --- a/src/views/login/pronote/PronoteInstanceSelector.tsx +++ b/src/views/login/pronote/PronoteInstanceSelector.tsx @@ -393,7 +393,7 @@ const PronoteInstanceSelector: Screen<"PronoteInstanceSelector"> = ({ /> - + )} diff --git a/src/views/login/pronote/PronoteManualLocation.tsx b/src/views/login/pronote/PronoteManualLocation.tsx index 08d33159e..6ba1d135f 100644 --- a/src/views/login/pronote/PronoteManualLocation.tsx +++ b/src/views/login/pronote/PronoteManualLocation.tsx @@ -35,7 +35,7 @@ const PronoteManualLocation: Screen<"PronoteManualLocation"> = ({ navigation }) }); const insets = useSafeAreaInsets(); - const {colors} = useTheme(); + const { colors } = useTheme(); const [keyboardHeight, setKeyboardHeight] = useState(0); diff --git a/src/views/login/pronote/PronoteManualURL.tsx b/src/views/login/pronote/PronoteManualURL.tsx index 39fe3ec1e..a2aa33308 100644 --- a/src/views/login/pronote/PronoteManualURL.tsx +++ b/src/views/login/pronote/PronoteManualURL.tsx @@ -18,11 +18,11 @@ import { NativeStackNavigationProp } from "@react-navigation/native-stack"; const PronoteManualURL: Screen<"PronoteManualURL"> = ({ route, navigation }) => { const theme = useTheme(); - const {colors} = theme; + const { colors } = theme; const insets = useSafeAreaInsets(); const [instanceURL, setInstanceURL] = useState(""); - const {showAlert} = useAlert(); + const { showAlert } = useAlert(); // find url in route params useEffect(() => { @@ -86,7 +86,7 @@ const PronoteManualURL: Screen<"PronoteManualURL"> = ({ route, navigation }) => ]}> - + = ({ route, navigation }) => - + = ({ const [instances, setInstances] = useState([]); const [geoInstances, setGeoInstances] = useState(null); - const {colors} = useTheme(); + const { colors } = useTheme(); const insets = useSafeAreaInsets(); const [search, setSearch] = useState(""); @@ -101,7 +101,7 @@ const SkolengoInstanceSelector: Screen<"SkolengoInstanceSelector"> = ({ > - + {!keyboardOpen && { usePKCE: false, }); const url = await authRes.makeAuthUrlAsync(discovery); - return {url, discovery, authRes}; + return { url, discovery, authRes }; } catch (e) { console.error(e); return null; diff --git a/src/views/settings/ExternalAccount/Izly.tsx b/src/views/settings/ExternalAccount/Izly.tsx index 2a4a3a4dd..d26880ee5 100644 --- a/src/views/settings/ExternalAccount/Izly.tsx +++ b/src/views/settings/ExternalAccount/Izly.tsx @@ -11,7 +11,7 @@ const ExternalIzlyLogin: Screen<"ExternalIzlyLogin"> = ({ navigation }) => { const handleLogin = async (username: string, password: string): Promise => { try { await login(username, password); - navigation.navigate("IzlyActivation", {username, password}); + navigation.navigate("IzlyActivation", { username, password }); } catch (error) { if (error instanceof Error) { setError(error.message); diff --git a/src/views/settings/ExternalAccount/PriceError.tsx b/src/views/settings/ExternalAccount/PriceError.tsx index 39664242e..c865a1afa 100644 --- a/src/views/settings/ExternalAccount/PriceError.tsx +++ b/src/views/settings/ExternalAccount/PriceError.tsx @@ -25,13 +25,13 @@ const PriceError: Screen<"PriceError"> = ({ navigation, route }) => { { text: "Soumettre", onPress: async (input) => { if (input) { const mealPrice = parseFloat(input.replace(",", ".")) * 100; - update(accountId, "authentication", {"mealPrice": mealPrice}); + update(accountId, "authentication", { "mealPrice": mealPrice }); navigation.pop(); navigation.pop(); navigation.pop(); navigation.pop(); } - }}, + } }, ], "plain-text", "2.00" @@ -52,7 +52,7 @@ const PriceError: Screen<"PriceError"> = ({ navigation, route }) => { - + = ({ navigation } const session = await authenticateWithCredentials(username, password); const siblings = await session.getSiblings(); - if (siblings.length !== 0) return navigation.navigate("TurboselfAccountSelector", {accounts: [session.host!, ...siblings!], username, password}); + if (siblings.length !== 0) return navigation.navigate("TurboselfAccountSelector", { accounts: [session.host!, ...siblings!], username, password }); const new_account: TurboselfAccount = { instance: undefined, service: AccountService.Turboself, diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index 28f2a2bad..4980f9760 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -74,7 +74,7 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { useEffect(() => { AsyncStorage.getItem("devmode") .then((res) => { - let value = {enabled: false}; + let value = { enabled: false }; if (res) value = JSON.parse(res); setDevModeEnabled(value.enabled); @@ -363,7 +363,7 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { onPress={() => navigation.navigate("AddonSettingsPage", { addon, from: "Settings" })} leading={ = () => { - - + + - - Cette extension n'est pas signé - Les extensions non signées ne sont pas vérifiées par nos équipes. Tu es responsable de l'installation de celle-ci. + + Cette extension n'est pas signé + Les extensions non signées ne sont pas vérifiées par nos équipes. Tu es responsable de l'installation de celle-ci. = () => { paddingHorizontal: 16 }}> = () => { marginBottom: 16, }} /> - + {selectedAddons.name} - + {"v" + selectedAddons.version + " - " + (selectedAddons.license == "" ? "Licence non précisé": selectedAddons.license)} = () => { marginTop: 10, }} > - + {"par @" + selectedAddons.author} - + Capture d’écran - - + + {selectedAddons.screenshot.map((screenshot, index) => ( = () => { - - + + Description {selectedAddons.description} - - + + Autorisations - + {selectedAddons.permissions.map((permission, index) => ( = () => { } > - + { permission.name == "PERM_APP_CAMERA" ? "Accès à la caméra": permission.name == "PERM_APP_PHOTOS" ? "Accès aux photos": @@ -241,7 +241,7 @@ const SettingsAddons: Screen<"SettingsAddons"> = () => { - + @@ -278,7 +278,7 @@ const SettingsAddons: Screen<"SettingsAddons"> = () => { }} leading={ = () => { )} > - + {addon.name} diff --git a/src/views/settings/SettingsIcons.tsx b/src/views/settings/SettingsIcons.tsx index a845c2e94..cd52a6b2d 100644 --- a/src/views/settings/SettingsIcons.tsx +++ b/src/views/settings/SettingsIcons.tsx @@ -40,7 +40,7 @@ export const removeColor = (icon: string) => { const SettingsIcons: Screen<"SettingsIcons"> = ({ navigation }) => { const theme = useTheme(); const { colors } = theme; - const {showAlert} = useAlert(); + const { showAlert } = useAlert(); const insets = useSafeAreaInsets(); const data = icones as { [key: string]: Icon[] }; @@ -172,7 +172,7 @@ const SettingsIcons: Screen<"SettingsIcons"> = ({ navigation }) => { }); }} > - + ) : null} diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index 3433c7625..8f59942a8 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -299,7 +299,7 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { setHideNameOnHomeScreen(!hideNameOnHomeScreen)} - trackColor={{false: theme.colors.border, true: theme.colors.primary}} + trackColor={{ false: theme.colors.border, true: theme.colors.primary }} thumbColor={theme.colors.background} /> } @@ -320,7 +320,7 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { setHideProfilePicOnHomeScreen(!hideProfilePicOnHomeScreen)} - trackColor={{false: theme.colors.border, true: theme.colors.primary}} + trackColor={{ false: theme.colors.border, true: theme.colors.primary }} thumbColor={theme.colors.background} /> } diff --git a/src/views/welcome/AccountSelector.old.tsx b/src/views/welcome/AccountSelector.old.tsx index 217b5e13c..33e75a747 100644 --- a/src/views/welcome/AccountSelector.old.tsx +++ b/src/views/welcome/AccountSelector.old.tsx @@ -1,9 +1,9 @@ -import {NativeItem, NativeList, NativeListHeader, NativeText} from "@/components/Global/NativeComponents"; -import {useAccounts, useCurrentAccount} from "@/stores/account"; -import {defaultProfilePicture} from "@/utils/ui/default-profile-picture"; -import {useIsFocused, useTheme} from "@react-navigation/native"; -import {PlusIcon} from "lucide-react-native"; -import {useEffect, useState} from "react"; +import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; +import { useAccounts, useCurrentAccount } from "@/stores/account"; +import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; +import { useIsFocused, useTheme } from "@react-navigation/native"; +import { PlusIcon } from "lucide-react-native"; +import { useEffect, useState } from "react"; import { Alert, Dimensions, @@ -14,7 +14,7 @@ import { TouchableHighlight, View } from "react-native"; -import {useSafeAreaInsets} from "react-native-safe-area-context"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; import * as SplashScreen from "expo-splash-screen"; import PapillonAvatar from "@/components/Global/PapillonAvatar"; @@ -31,15 +31,14 @@ import Reanimated, { useScrollViewOffset, ZoomIn, } from "react-native-reanimated"; -import {LinearGradient} from "expo-linear-gradient"; -import {animPapillon} from "@/utils/ui/animations"; -import {Screen} from "@/router/helpers/types"; +import { LinearGradient } from "expo-linear-gradient"; +import { animPapillon } from "@/utils/ui/animations"; +import { Screen } from "@/router/helpers/types"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; -import {PressableScale} from "react-native-pressable-scale"; +import { PressableScale } from "react-native-pressable-scale"; import datasets from "@/consts/datasets.json"; -import Animated from "react-native-reanimated"; -import {PrimaryAccount} from "@/stores/account/types"; +import { PrimaryAccount } from "@/stores/account/types"; // https://raw.githubusercontent.com/PapillonApp/datasets/refs/heads/main/illustrations/index.json @@ -67,7 +66,7 @@ const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { const [illustration, setIllustration] = useState(undefined); const [illustrationLoaded, setIllustrationLoaded] = useState(false); - const scrollRef = useAnimatedRef(); + const scrollRef = useAnimatedRef(); const scrollOffset = useScrollViewOffset(scrollRef); const headerRatioHeight = 250; let headerAnimatedStyle = useAnimatedStyle(() => ({ @@ -120,7 +119,7 @@ const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { }, [accounts]); useEffect(() => { - if (currentAccount && currentAccount?.localID) { + if (currentAccount?.localID) { navigation.reset({ index: 0, routes: [{ name: "AccountStack" }], @@ -349,7 +348,7 @@ const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { @@ -448,11 +447,7 @@ const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { {account.studentName.first} {account.studentName.last} - {account.schoolName ? - account.schoolName : - account.identityProvider ? - account.identityProvider.name : - "Compte local" + {account.schoolName ?? account.identityProvider?.name ?? "Compte local" } diff --git a/src/views/welcome/ChangelogScreen.tsx b/src/views/welcome/ChangelogScreen.tsx index f01ec21d2..314ea8417 100644 --- a/src/views/welcome/ChangelogScreen.tsx +++ b/src/views/welcome/ChangelogScreen.tsx @@ -159,7 +159,7 @@ const ChangelogScreen: Screen<"ChangelogScreen"> = ({ route, navigation }) => { animated inline > = ({ route, navigation }) => { overflow: "hidden", alignItems: "center", justifyContent: "center", - alignSelf: "center"}]} + alignSelf: "center" }]} > = ({ route, navigation }) => { if (!settings) { playSound(LEson6); } - navigation.navigate("AccountStack", {onboard: true}); + navigation.navigate("AccountStack", { onboard: true }); }} disabled={!account?.personalization?.color} style={{ diff --git a/src/widgets/Components/RestaurantQRCode.tsx b/src/widgets/Components/RestaurantQRCode.tsx index f982fc792..59cce9367 100644 --- a/src/widgets/Components/RestaurantQRCode.tsx +++ b/src/widgets/Components/RestaurantQRCode.tsx @@ -21,8 +21,7 @@ const RestaurantQRCodeWidget = forwardRef(({ const { colors } = theme; const linkedAccounts = useCurrentAccount(store => store.linkedAccounts); - const [qrcode, setQRCodes] = useState | null>(null); - const navigation = useNavigation(); + const [QRCodes, setQRCodes] = useState | null>(null); useImperativeHandle(ref, () => ({ handlePress: () => { @@ -93,7 +92,7 @@ const RestaurantQRCodeWidget = forwardRef(({ }} layout={LinearTransition} > - {qrcode && qrcode.length > 1 ? "Toucher pour afficher les QR-Codes" : "Toucher pour afficher le QR-Code"} + {QRCodes && QRCodes.length > 1 ? "Toucher pour afficher les QR-Codes" : "Toucher pour afficher le QR-Code"} Date: Fri, 14 Mar 2025 22:06:00 +0100 Subject: [PATCH 0858/1144] chore(lint): typing --- src/stores/account/types.ts | 124 ++++++++++++++-------------- src/views/login/ServiceSelector.tsx | 11 +++ 2 files changed, 73 insertions(+), 62 deletions(-) diff --git a/src/stores/account/types.ts b/src/stores/account/types.ts index d0089120c..7b4567fe6 100644 --- a/src/stores/account/types.ts +++ b/src/stores/account/types.ts @@ -133,11 +133,11 @@ interface BaseAccount { } interface BaseExternalAccount { - localID: string - isExternal: true - username: string - linkedExternalLocalIDs?: string[] - data: Record + localID: string; + isExternal: true; + username: string; + linkedExternalLocalIDs?: string[]; + data: Record; } export interface PronoteAccount extends BaseAccount { @@ -147,10 +147,10 @@ export interface PronoteAccount extends BaseAccount { authentication: pronote.RefreshInformation & { deviceUUID: string; }; - identityProvider?: undefined; + identityProvider?: never; providers: string[]; serviceData: Record; - associatedAccountsLocalIDs?: undefined + associatedAccountsLocalIDs?: never; } export interface EcoleDirecteAccount extends BaseAccount { @@ -158,11 +158,11 @@ export interface EcoleDirecteAccount extends BaseAccount { service: AccountService.EcoleDirecte; instance: {}; authentication: { - session: PawdirecteSession - account: PawdirecteAccount + session: PawdirecteSession; + account: PawdirecteAccount; } - identityProvider?: undefined - associatedAccountsLocalIDs?: undefined + identityProvider?: never; + associatedAccountsLocalIDs?: never; providers: string[]; serviceData: Record; } @@ -172,23 +172,23 @@ export interface SkolengoAccount extends BaseAccount { instance?: ScolengoAPI.Skolengo; authentication: SkolengoAuthConfig; userInfo: ScolengoAPIUser; - identityProvider?: undefined; + identityProvider?: never; providers: string[]; serviceData: Record; - associatedAccountsLocalIDs?: undefined + associatedAccountsLocalIDs?: never; } export interface MultiAccount extends BaseAccount { - service: AccountService.Multi - instance?: MultiAPI.Multi + service: AccountService.Multi; + instance?: MultiAPI.Multi; authentication: { - instanceURL: string - refreshAuthToken: string - } - identityProvider?: undefined - associatedAccountsLocalIDs?: undefined - providers: string[] - serviceData: Record + instanceURL: string; + refreshAuthToken: string; + }; + identityProvider?: never; + associatedAccountsLocalIDs?: string[]; + providers: string[]; + serviceData: Record; } export interface LocalAccount extends BaseAccount { @@ -211,68 +211,68 @@ export interface LocalAccount extends BaseAccount { providers?: string[]; serviceData: Record; - associatedAccountsLocalIDs?: undefined + associatedAccountsLocalIDs?: never; } export interface PapillonMultiServiceSpace extends BaseAccount { - service: AccountService.PapillonMultiService - instance: null | string - authentication: null + service: AccountService.PapillonMultiService; + instance: null | string; + authentication: null; identityProvider: { - name: string, - identifier: undefined, - rawData: undefined + name: string; + identifier: undefined; + rawData: undefined; }, - associatedAccountsLocalIDs: string[] - providers: string[] - serviceData: Record + associatedAccountsLocalIDs: string[]; + providers: string[]; + serviceData: Record; } export interface TurboselfAccount extends BaseExternalAccount { - service: AccountService.Turboself - instance: undefined + service: AccountService.Turboself; + instance: undefined; authentication: { - session: TurboselfClient - username: string - password: string - } + session: TurboselfClient; + username: string; + password: string; + }; } export interface AliseAccount extends BaseExternalAccount { - service: AccountService.Alise - instance: undefined + service: AccountService.Alise; + instance: undefined; authentication: { - session: AliseClient - schoolID: string - username: string - password: string - bookings: BookingDay[] - mealPrice: number - } + session: AliseClient; + schoolID: string; + username: string; + password: string; + bookings: BookingDay[]; + mealPrice: number; + }; } export interface ARDAccount extends BaseExternalAccount { - service: AccountService.ARD - instance?: ARDClient + service: AccountService.ARD; + instance?: ARDClient; authentication: { - pid: string - username: string - password: string - schoolID: string - balances: OnlinePayments - mealPrice: number - } + pid: string; + username: string; + password: string; + schoolID: string; + balances: OnlinePayments; + mealPrice: number; + }; } export interface IzlyAccount extends BaseExternalAccount { - service: AccountService.Izly - instance?: Identification + service: AccountService.Izly; + instance?: Identification; authentication: { - secret: string - identification: Identification - configuration: Configuration - } + secret: string; + identification: Identification; + configuration: Configuration; + }; } export type PrimaryAccount = ( diff --git a/src/views/login/ServiceSelector.tsx b/src/views/login/ServiceSelector.tsx index 99297afe1..56e6f303b 100644 --- a/src/views/login/ServiceSelector.tsx +++ b/src/views/login/ServiceSelector.tsx @@ -15,6 +15,16 @@ import { School } from "lucide-react-native"; import { LinearGradient } from "expo-linear-gradient"; import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; +type V6Data = { restore: boolean; + imported: boolean; + data: { + nextTimeToken: string | null; + instanceUrl: string | null; + username: string | null; + deviceUUID: string | null; + } +}; + const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { const theme = useTheme(); const { colors } = theme; @@ -191,6 +201,7 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { {v6Data && v6Data.restore && ( navigation.navigate("PronoteV6Import", { data: v6Data.data })} /> )} From 6c28603940db6c7ea21d16bd995de584efc31397 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Fri, 14 Mar 2025 22:08:25 +0100 Subject: [PATCH 0859/1144] chore(lint): unused --- src/components/Global/PapillonHeader.tsx | 4 ---- .../Settings/MultiServiceContainerCard.tsx | 1 - src/utils/logger/logger.ts | 14 -------------- src/views/account/Restaurant/Menu.tsx | 1 - src/views/account/Restaurant/Modals/CardDetail.tsx | 1 + src/views/account/Restaurant/Modals/QrCode.tsx | 1 - src/views/account/Week/Week.tsx | 3 +-- src/views/login/ServiceSelector.tsx | 9 +-------- .../login/pronote/PronoteInstanceSelector.tsx | 3 --- .../settings/ExternalAccount/ServiceSelector.tsx | 5 ----- src/views/settings/SettingsNotifications.tsx | 1 + 11 files changed, 4 insertions(+), 39 deletions(-) diff --git a/src/components/Global/PapillonHeader.tsx b/src/components/Global/PapillonHeader.tsx index 235fba5fb..2d09a0033 100644 --- a/src/components/Global/PapillonHeader.tsx +++ b/src/components/Global/PapillonHeader.tsx @@ -24,10 +24,6 @@ const PapillonHeader: React.FC = ({ navigation }) => { const theme = useTheme(); - const insets = useSafeAreaInsets(); - - const topPadding = (Platform.OS === "ios" && route.params?.outsideNav) ? 0 : insets.top; - const largeHeader = route.params?.outsideNav || Platform.OS !== "ios"; return ( diff --git a/src/components/Settings/MultiServiceContainerCard.tsx b/src/components/Settings/MultiServiceContainerCard.tsx index 2c1f0baaa..365f33dce 100644 --- a/src/components/Settings/MultiServiceContainerCard.tsx +++ b/src/components/Settings/MultiServiceContainerCard.tsx @@ -7,7 +7,6 @@ import { LinearGradient } from "expo-linear-gradient"; import BetaIndicator from "../News/Beta"; const MultiServiceContainerCard = ({ theme }: { theme: any }) => { - const { colors } = theme; const animationref = React.useRef(null); useEffect(() => { diff --git a/src/utils/logger/logger.ts b/src/utils/logger/logger.ts index 8677345ce..249c2848e 100644 --- a/src/utils/logger/logger.ts +++ b/src/utils/logger/logger.ts @@ -24,20 +24,6 @@ function get_message (type: number, date: string, from: string, message: string) ); } -function get_file_from_stacktrace (stack: string): string -{ - let res = ""; - try { - res = stack - .split("\n")[1] - .split(/\/\/localhost:\d\d\d\d\//g)[1] - .split("//&")[0]; - } catch (e) { - res = "UNKNOWN"; - } - return (res); -} - function obtain_function_name (from?: string): string { const error = new Error(); // On génère une erreur pour obtenir la stacktrace const stack = error.stack?.split("\n") || []; diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 82b61e31b..c6932b3aa 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -84,7 +84,6 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { const [pickerDate, setPickerDate] = React.useState(new Date(new Date().setHours(0, 0, 0, 0))); const [menuLoading, setMenuLoading] = useState(false); const [isInitialised, setIsInitialised] = useState(false); - const [selectedIndex, setSelectedIndex] = useState(0); const [allCards, setAllCards] = useState | null>(null); const refreshData = async () => { diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index 58bac772f..648043057 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -22,6 +22,7 @@ import { formatCardIdentifier } from "../Menu"; const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigation }) => { try { const { card } = route.params; + // eslint-disable-next-line no-unused-vars const [cardData, setCardData] = useState(null); const theme = useTheme(); diff --git a/src/views/account/Restaurant/Modals/QrCode.tsx b/src/views/account/Restaurant/Modals/QrCode.tsx index 7679f04f8..ab85c757b 100644 --- a/src/views/account/Restaurant/Modals/QrCode.tsx +++ b/src/views/account/Restaurant/Modals/QrCode.tsx @@ -22,7 +22,6 @@ const RestaurantQrCode: Screen<"RestaurantQrCode"> = ({ route, navigation }) => const PollingBalance = async () => { balanceFromExternal(card.account as ExternalAccount).then((newBalance) => { if(card.balance[0].amount !== newBalance[0].amount) { - const diff = newBalance[0].amount - card.balance[0].amount; openFeedback(); } }); diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index f379090c5..d7bac5c42 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -217,8 +217,6 @@ const Week: Screen<"Week"> = ({ route, navigation }) => { const theme = useTheme(); const insets = useSafeAreaInsets(); - const outsideNav = route.params?.outsideNav; - const [isLoading, setIsLoading] = React.useState(false); const account = useCurrentAccount((store) => store.account); @@ -277,6 +275,7 @@ const Week: Screen<"Week"> = ({ route, navigation }) => { await loadTimetableWeek(weekNumber); }, [loadTimetableWeek]); + // eslint-disable-next-line no-unused-vars const [openedIcalModal, setOpenedIcalModal] = React.useState(false); React.useEffect(() => { diff --git a/src/views/login/ServiceSelector.tsx b/src/views/login/ServiceSelector.tsx index 56e6f303b..a6c7a35c6 100644 --- a/src/views/login/ServiceSelector.tsx +++ b/src/views/login/ServiceSelector.tsx @@ -32,7 +32,7 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { type Services = "pronote" | "ed" | "skolengo"; const [service, setService] = useState(null); - const [v6Data, setV6Data] = useState(null); + const [v6Data, setV6Data] = useState(null); const { playSound } = useSoundHapticsWrapper(); const LEson = require("@/../assets/sound/1.wav"); @@ -94,13 +94,6 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { }, ]; - const UnsupportedAlert = () => { - showAlert({ - title: "Service non supporté", - message: "Désolé, ce service n'est pas encore supporté. Réessaye dans une prochaine version." - }); - }; - return ( diff --git a/src/views/login/pronote/PronoteInstanceSelector.tsx b/src/views/login/pronote/PronoteInstanceSelector.tsx index 223265915..ec576ead2 100644 --- a/src/views/login/pronote/PronoteInstanceSelector.tsx +++ b/src/views/login/pronote/PronoteInstanceSelector.tsx @@ -66,9 +66,6 @@ const PronoteInstanceSelector: Screen<"PronoteInstanceSelector"> = ({ const [loading, setLoading] = useState(false); - const routes = navigation.getState()?.routes; - const prevRoute = routes[routes.length - 2]; - const keyboardDidShow = (event: KeyboardEvent) => { setKeyboardOpen(true); setKeyboardHeight(event.endCoordinates.height); diff --git a/src/views/settings/ExternalAccount/ServiceSelector.tsx b/src/views/settings/ExternalAccount/ServiceSelector.tsx index 4ec8abb4f..002b26a91 100644 --- a/src/views/settings/ExternalAccount/ServiceSelector.tsx +++ b/src/views/settings/ExternalAccount/ServiceSelector.tsx @@ -9,12 +9,7 @@ import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; const ExternalAccountSelector: Screen<"ExternalAccountSelector"> = ({ navigation, route }) => { - const theme = useTheme(); - const { colors } = theme; - const insets = useSafeAreaInsets(); - const account = useCurrentAccount(store => store.account!); -const ExternalAccountSelector: Screen<"ExternalAccountSelector"> = ({ navigation, route }) => { type Service = AccountService | "Other"; const [service, setService] = useState(null); diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index e80706ecd..fe92d2f4b 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -20,6 +20,7 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = () => { const notifications = account.personalization.notifications; // Global state + // eslint-disable-next-line no-unused-vars const [enabled, setEnabled] = useState(notifications?.enabled || false); // Animation states From 09e68c421cb099b619695fe8bf8e27d6b624fb9e Mon Sep 17 00:00:00 2001 From: Mael <96339570+ryzenixx@users.noreply.github.com> Date: Fri, 14 Mar 2025 23:59:33 +0100 Subject: [PATCH 0860/1144] Update package-lock.json --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index d19fdf897..1b8c845cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.9.2", + "version": "7.10.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.9.2", + "version": "7.10.1", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.4", "@candlefinance/app-icon": "^0.4.5", From 143d7079ac71f86adaec3c5ec15c262927c99b2e Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Sat, 15 Mar 2025 00:39:15 +0100 Subject: [PATCH 0861/1144] fix(skolengo): revert to native alert to avoid crash --- src/services/skolengo/skolengo-account.tsx | 54 +++++++++++++--------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/src/services/skolengo/skolengo-account.tsx b/src/services/skolengo/skolengo-account.tsx index 804d3999e..80a9f4251 100644 --- a/src/services/skolengo/skolengo-account.tsx +++ b/src/services/skolengo/skolengo-account.tsx @@ -4,42 +4,46 @@ import { DiscoveryDocument } from "expo-auth-session"; import { SkolengoAccount, AccountService } from "@/stores/account/types"; import axios, { type AxiosResponse } from "axios"; import { decode as b64decode, encode as b64encode} from "js-base64"; -import { decode as htmlDecode } from "html-entities"; import { useCurrentAccount } from "@/stores/account"; import defaultSkolengoPersonalization from "./default-personalization"; import { User } from "scolengo-api/types/models/Common"; -import { useAlert } from "@/providers/AlertProvider"; -import { BadgeX } from "lucide-react-native"; +import { Alert } from "react-native"; const getSkolengoAxiosInstance = () => { const axioss = axios.create({ baseURL: BASE_URL }); - const { showAlert } = useAlert(); + axioss.interceptors.response.use( + (r: AxiosResponse) => r, + (error) => { + if (error.response?.data?.errors?.find((e: any) => e.title.includes("PRONOTE_RESOURCES"))) { + return Promise.reject(error); + } - axioss.interceptors.response.use((r: AxiosResponse) => r, (error)=>{ - if(error.response?.data?.errors?.find((e:any)=>e.title.includes("PRONOTE_RESOURCES"))) return Promise.resolve(error); + if (__DEV__) { + console.warn( + "[SKOLENGO] ERR - ", + JSON.stringify(error, null, 2), + JSON.stringify(error.response?.data, null, 2) + ); + } - if(__DEV__) { - console.warn( - "[SKOLENGO] ERR - ", - JSON.stringify(error, null, 2), - JSON.stringify(error.response?.data, null, 2) - ); - } - error.response?.data?.errors?.forEach((e: any) => { - // if unknown error, don't display the error message - if(!e["title"] || e["title"] === "FORBIDDEN") return; + error.response?.data?.errors?.forEach((e: any) => { + if (!e["title"] || e["title"] === "FORBIDDEN") return; - showAlert({ - title: "Skolengo - " + (e["title"].toString() || "Erreur"), - message: htmlDecode(e["detail"]?.toString().replace(/<(\/)?([a-z0-9]+)>/g, "") || "Erreur inconnue")+"\n\nSi cette erreur persiste, contacte les équipes de Papillon.", - icon: , + Alert.alert( + "Skolengo - " + (e["title"].toString() || "Erreur"), + htmlDecode(e["detail"]?.toString().replace(/<(\/)?([a-z0-9]+)>/g, "") || "Erreur inconnue") + + "\n\nSi cette erreur persiste, contacte les équipes de Papillon.", + [{ text: "OK" }] + ); }); - }); - return Promise.reject(error); - }); + + return Promise.reject(error); + } + ); + return axioss; }; @@ -117,3 +121,7 @@ export const getSkolengoAccount = async (authConfig: SkolengoAuthConfig, userInf }; return account; }; +function htmlDecode (arg0: any) { + throw new Error("Function not implemented."); +} + From 93b03ffc9b310d83e556d4894ade91e1078f86f1 Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Sat, 15 Mar 2025 00:41:54 +0100 Subject: [PATCH 0862/1144] fix(skolengo): import htmlDecode --- src/services/skolengo/skolengo-account.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/services/skolengo/skolengo-account.tsx b/src/services/skolengo/skolengo-account.tsx index 80a9f4251..621292c35 100644 --- a/src/services/skolengo/skolengo-account.tsx +++ b/src/services/skolengo/skolengo-account.tsx @@ -4,6 +4,7 @@ import { DiscoveryDocument } from "expo-auth-session"; import { SkolengoAccount, AccountService } from "@/stores/account/types"; import axios, { type AxiosResponse } from "axios"; import { decode as b64decode, encode as b64encode} from "js-base64"; +import { decode as htmlDecode } from "html-entities"; import { useCurrentAccount } from "@/stores/account"; import defaultSkolengoPersonalization from "./default-personalization"; import { User } from "scolengo-api/types/models/Common"; @@ -121,7 +122,3 @@ export const getSkolengoAccount = async (authConfig: SkolengoAuthConfig, userInf }; return account; }; -function htmlDecode (arg0: any) { - throw new Error("Function not implemented."); -} - From ac1da4f046f65e91d67bfa3d41c62b5d54f88bb0 Mon Sep 17 00:00:00 2001 From: Mael <96339570+ryzenixx@users.noreply.github.com> Date: Sat, 15 Mar 2025 00:44:55 +0100 Subject: [PATCH 0863/1144] fix: Centrer le texte du bouton "Ajouter un service" --- src/views/account/Restaurant/Menu.tsx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 0992c13a3..cb28a390e 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -383,16 +383,18 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { /> } title="Commence par connecter un service externe de cantine" - description="Papillon te permet d’importer un compte depuis Turboself, ARD, Alize et Izly." + description="Papillon te permet d’importer un compte depuis Turboself, ARD, Alise et Izly." entering={animPapillon(FadeInDown)} exiting={animPapillon(FadeOut)} trailing={ - navigation.navigate("SettingStack", { view: "SettingsExternalServices" })} - style={{ marginTop: 16 }} - /> + + navigation.navigate("SettingStack", { view: "SettingsExternalServices" })} + style={{ marginTop: 16 }} + /> + } /> )} From da47c69032b517e00a02af3b483b0ad0f561a21f Mon Sep 17 00:00:00 2001 From: Mael <96339570+ryzenixx@users.noreply.github.com> Date: Sat, 15 Mar 2025 12:34:59 +0100 Subject: [PATCH 0864/1144] Correction d'une faute de frappe --- src/views/account/Lessons/Atoms/Loading.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Lessons/Atoms/Loading.tsx b/src/views/account/Lessons/Atoms/Loading.tsx index 74bec79b1..199ca13b2 100644 --- a/src/views/account/Lessons/Atoms/Loading.tsx +++ b/src/views/account/Lessons/Atoms/Loading.tsx @@ -6,7 +6,7 @@ const LessonsLoading = () => { return ( ); }; From abf321416de587fcda7faaf0e134e544e3629c04 Mon Sep 17 00:00:00 2001 From: Mael <96339570+ryzenixx@users.noreply.github.com> Date: Sat, 15 Mar 2025 17:14:28 +0100 Subject: [PATCH 0865/1144] =?UTF-8?q?fix:=20probl=C3=A8me=20d'affichage=20?= =?UTF-8?q?des=20devoirs=20sur=20les=20tablettes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/Homeworks.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index c7e2f465e..1d465fe36 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -64,10 +64,7 @@ const formatDate = (date: string | number | Date): string => { const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const flatListRef: React.MutableRefObject = useRef(null) as any as React.MutableRefObject; const { width } = Dimensions.get("window"); - const finalWidth = width - (width > 600 ? ( - 320 > width * 0.35 ? width * 0.35 : - 320 - ) : 0); + const finalWidth = width; const insets = useSafeAreaInsets(); const { playHaptics } = useSoundHapticsWrapper(); const { isOnline } = useOnlineStatus(); From bf627618a95bd43a550a7358d1f6f785a762a01b Mon Sep 17 00:00:00 2001 From: Mael <96339570+ryzenixx@users.noreply.github.com> Date: Sat, 15 Mar 2025 22:27:29 +0100 Subject: [PATCH 0866/1144] Reformulation d'une phrase --- src/views/account/Restaurant/Menu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index cb28a390e..ed113c9ec 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -383,7 +383,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { /> } title="Commence par connecter un service externe de cantine" - description="Papillon te permet d’importer un compte depuis Turboself, ARD, Alise et Izly." + description="Papillon te permet de connecter un compte Turboself, ARD, Alise ou Izly." entering={animPapillon(FadeInDown)} exiting={animPapillon(FadeOut)} trailing={ From 66c29ff0d94e3b7b9bf2ee70407d29b2fac3f337 Mon Sep 17 00:00:00 2001 From: Mael <96339570+ryzenixx@users.noreply.github.com> Date: Sat, 15 Mar 2025 23:03:50 +0100 Subject: [PATCH 0867/1144] Correction des animations --- src/router/helpers/create-screen.ts | 3 +-- src/router/screens/account/home.tsx | 4 ++-- src/router/screens/index.ts | 4 ++-- src/router/screens/views/index.ts | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/router/helpers/create-screen.ts b/src/router/helpers/create-screen.ts index 8aa0a5060..c16918ce3 100644 --- a/src/router/helpers/create-screen.ts +++ b/src/router/helpers/create-screen.ts @@ -22,8 +22,7 @@ const createScreen = ( }) = {} ) => { if (!options.animation && Platform.OS === "android") { - options.animation = - options.presentation === "modal" ? "default" : "default"; + options.animation = options.presentation === "modal" ? "slide_from_bottom" : "slide_from_right"; } return { name, diff --git a/src/router/screens/account/home.tsx b/src/router/screens/account/home.tsx index 1e5ab2357..bf1945e64 100644 --- a/src/router/screens/account/home.tsx +++ b/src/router/screens/account/home.tsx @@ -42,7 +42,7 @@ const HomeStackScreen = ({ accountScreens }: { ...tabData.options, tabEnabled: tab.enabled, presentation: "formSheet", - animation: Platform.OS === "android" ? "default" : "default", + animation: Platform.OS === "android" ? "slide_from_bottom" : "default", sheetCornerRadius: 24, }; @@ -61,7 +61,7 @@ const HomeStackScreen = ({ accountScreens }: { newAccountScreens.unshift( createScreen("HomeScreen", Home, { headerShown: false, - animation: Platform.OS === "android" ? "default" : "default", + animation: Platform.OS === "android" ? "slide_from_right" : "default", }) as ReturnType ); diff --git a/src/router/screens/index.ts b/src/router/screens/index.ts index 352a5efa9..8c7022e89 100644 --- a/src/router/screens/index.ts +++ b/src/router/screens/index.ts @@ -15,14 +15,14 @@ export default [ createScreen("SettingStack", SettingsScreen, { headerShown: false, presentation: Platform.OS === "android" ? "modal" : "formSheet", - animation: Platform.OS === "android" ? "default" : "default", + animation: Platform.OS === "android" ? "slide_from_right" : "default", animationDuration: 100, }), createScreen("AccountStack", AccountScreen, { headerShown: false, gestureEnabled: false, - animation: Platform.OS === "android" ? "default" : "none", + animation: Platform.OS === "android" ? "slide_from_right" : "none", animationDuration: 100, }), ] as const; diff --git a/src/router/screens/views/index.ts b/src/router/screens/views/index.ts index 6de23df0f..a36d85fe2 100644 --- a/src/router/screens/views/index.ts +++ b/src/router/screens/views/index.ts @@ -59,7 +59,7 @@ export default [ headerTitle: "Item", presentation: "modal", headerShown: false, - animation: "default", + animation: "slide_from_right", }), createScreen("AddonLogs", AddonLogs, { headerTitle: "Logs", From a5a04975cb279e2e0dd8d7e1db75a65a7c2d97a3 Mon Sep 17 00:00:00 2001 From: Mael <96339570+ryzenixx@users.noreply.github.com> Date: Sat, 15 Mar 2025 23:13:26 +0100 Subject: [PATCH 0868/1144] =?UTF-8?q?Remplacement=20des=20sauts=20de=20lig?= =?UTF-8?q?nes=20en=20br=20dans=20les=20details=20dans=20la=20requ=C3=AAte?= =?UTF-8?q?=20vers=20Peppermint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/SettingsSupport.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/settings/SettingsSupport.tsx b/src/views/settings/SettingsSupport.tsx index fb89880b3..09534fe79 100644 --- a/src/views/settings/SettingsSupport.tsx +++ b/src/views/settings/SettingsSupport.tsx @@ -40,7 +40,7 @@ const SettingsSupport: Screen<"SettingsSupport"> = ({ navigation }) => { const data = { email: email, title: subject, - detail: `Description de mon problème:
${description}

Journaux:
${formattedLogs}`, + detail: `Description de mon problème:
${(description ?? "").replace(/\n/g, "
")}

Journaux:
${formattedLogs}`, }; const response = await fetch("https://api-menthe-et-cristaux.papillon.bzh/api/v1/ticket/public/create", { From 1a01791112c6b1c9541d7aa845c678659e183b2a Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sun, 16 Mar 2025 00:30:06 +0100 Subject: [PATCH 0869/1144] feat(logs): use custom logger instead of console builtin and enhance logs --- package-lock.json | 4 +- src/background/BackgroundTasks.ts | 4 +- src/components/Addons/AddonsWebview.tsx | 41 ++++------------- src/components/Grades/GradeModal.tsx | 6 ++- src/services/chats.ts | 14 +++--- src/services/homework.ts | 4 +- src/services/iutlan/attendance.ts | 5 ++- src/services/iutlan/grades.ts | 6 +-- src/services/izly/reload.ts | 2 +- src/services/pronote/dataset_geolocation.ts | 17 +++---- src/services/reload-account.ts | 2 +- src/services/skolengo/skolengo-account.tsx | 13 +++--- src/stores/account/index.ts | 44 +++++++++---------- src/stores/homework/index.ts | 4 +- src/stores/multiService/index.ts | 8 ++-- src/stores/news/index.ts | 4 +- src/stores/timetable/index.ts | 16 +++---- src/utils/GetRessources/GetContribs.tsx | 7 +-- .../GetDefaultProfilePicture.tsx | 7 +-- src/utils/chat/themes/GetThemeForChat.ts | 3 +- src/utils/chat/themes/SetThemeForChat.ts | 17 +++---- src/utils/logger/logger.ts | 12 ++--- src/views/account/Chat/Messages.tsx | 3 +- src/views/account/Chat/Modals/ChatDetails.tsx | 9 ++-- .../Evaluation/Subject/EvaluationList.tsx | 3 +- .../account/Grades/Atoms/GradesScodocUE.tsx | 3 +- .../account/Grades/Graph/GradesAverage.tsx | 5 ++- .../account/Grades/Modals/GradeReaction.tsx | 9 ++-- .../Home/Elements/HomeworksElement.tsx | 3 +- src/views/account/Homeworks/Homeworks.old.tsx | 11 ++--- src/views/account/Restaurant/Menu.tsx | 15 ++++--- .../account/Restaurant/Modals/CardDetail.tsx | 11 ++--- .../account/Restaurant/Modals/QrCode.tsx | 5 ++- .../actions/BackgroundIUTLannion.tsx | 9 ++-- .../IdentityProvider/providers/Multi.tsx | 9 ++-- .../providers/UnivIUTLannion.tsx | 3 +- .../providers/UnivLimoges.tsx | 6 +-- .../providers/UnivRennes2.tsx | 5 ++- .../providers/UnivSorbonneParisNord.tsx | 5 ++- .../ecoledirecte/EcoleDirecteCredentials.tsx | 17 +++---- src/views/login/pronote/PronoteQRCode.tsx | 5 ++- src/views/login/pronote/PronoteWebview.tsx | 3 +- src/views/login/skolengo/SkolengoWebview.tsx | 3 +- .../ExternalAccount/IzlyActivation.tsx | 5 ++- src/views/settings/SettingsDevLogs.tsx | 4 +- src/widgets/Components/LastGrade.tsx | 5 ++- src/widgets/Components/RestaurantQRCode.tsx | 7 +-- 47 files changed, 204 insertions(+), 199 deletions(-) diff --git a/package-lock.json b/package-lock.json index d19fdf897..1b8c845cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.9.2", + "version": "7.10.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.9.2", + "version": "7.10.1", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.4", "@candlefinance/app-icon": "^0.4.5", diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 848559193..f420869cb 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -23,7 +23,7 @@ const notifeeEvent = async () => { switch (type) { case EventType.ACTION_PRESS: { - console.log(`[Notifee] Action press: ${pressAction?.id}`); + log(`[Notifee] Action press: ${pressAction?.id}`, "NOTIFEE/BK"); break; } case EventType.DISMISSED: { @@ -41,7 +41,7 @@ const notifeeEvent = async () => { switch (type) { case EventType.ACTION_PRESS: { - console.log(`[Notifee] Action press: ${pressAction?.id}`); + log(`[Notifee] Action press: ${pressAction?.id}`, "NOTIFEE/FW"); break; } case EventType.DISMISSED: { diff --git a/src/components/Addons/AddonsWebview.tsx b/src/components/Addons/AddonsWebview.tsx index 337cf41d4..80931262b 100644 --- a/src/components/Addons/AddonsWebview.tsx +++ b/src/components/Addons/AddonsWebview.tsx @@ -11,7 +11,7 @@ import { useTheme } from "@react-navigation/native"; import Reanimated, { Easing, useSharedValue, withTiming } from "react-native-reanimated"; import type { NativeStackNavigationProp } from "@react-navigation/native-stack"; import type { RouteParameters } from "@/router/helpers/types"; -import { get_iso_date } from "@/utils/logger/logger"; +import { get_iso_date, log } from "@/utils/logger/logger"; import { AddonLogs } from "@/addons/types"; export type AddonHomePageInfo = { @@ -228,41 +228,16 @@ const AddonsWebview: React.FC = ({ } // CONSOLE.LOG - if (data.type == "log") { - console.log(`[ADDON][${addon.name}] Log : ${data.message}`); - let log = { + if(["log", "error", "warn", "info"].indexOf(data.type) !== -1){ + let type = data.type; + let str_type = String(type[0]).toUpperCase() + String(type).slice(1); + log(`[ADDON][${addon.name}] ${str_type} : ${data.message}`, "AddonsWebview"); + let log_message = { message: data.message, - type: "log", + type, date: new Date(get_iso_date()) } satisfies AddonLogs; - setLogs([...logs, log]); - } - if (data.type == "error") { - console.log(`[ADDON][${addon.name}] Error : ${data.message}`); - let log = { - message: data.message, - type: "error", - date: new Date(get_iso_date()) - } satisfies AddonLogs; - setLogs([...logs, log]); - } - if (data.type == "warn") { - console.log(`[ADDON][${addon.name}] Warning : ${data.message}`); - let log = { - message: data.message, - type: "warn", - date: new Date(get_iso_date()) - } satisfies AddonLogs; - setLogs([...logs, log]); - } - if (data.type == "info") { - console.log(`[ADDON][${addon.name}] Info : ${data.message}`); - let log = { - message: data.message, - type: "info", - date: new Date(get_iso_date()) - } satisfies AddonLogs; - setLogs([...logs, log]); + setLogs([...logs, log_message]); } if (data.type == "open_logs") { diff --git a/src/components/Grades/GradeModal.tsx b/src/components/Grades/GradeModal.tsx index d60128973..ab9820312 100644 --- a/src/components/Grades/GradeModal.tsx +++ b/src/components/Grades/GradeModal.tsx @@ -22,6 +22,8 @@ import PapillonBottomSheet from "@/components/Modals/PapillonBottomSheet"; import { useTheme } from "@react-navigation/native"; import { useAlert } from "@/providers/AlertProvider"; import { isExpoGo } from "@/utils/native/expoGoAlert"; +import { error } from "@/utils/logger/logger"; + interface GradeModalProps { isVisible: boolean; reel: Reel; @@ -95,8 +97,8 @@ const GradeModal: React.FC = ({ message: "L'image a été sauvegardée dans ta galerie.", icon: , }); - } catch (error) { - console.error("Failed to save image:", error); + } catch (err) { + error("Failed to save image:" + err, "GradeModal/saveimage"); } }; diff --git a/src/services/chats.ts b/src/services/chats.ts index e71d4c68e..c3110a39d 100644 --- a/src/services/chats.ts +++ b/src/services/chats.ts @@ -3,7 +3,7 @@ import type { Chat, ChatMessage, ChatRecipient } from "./shared/Chat"; import type { Recipient } from "./shared/Recipient"; import { getFeatureAccount } from "@/utils/multiservice"; import { MultiServiceFeature } from "@/stores/multiService/types"; -import { log } from "@/utils/logger/logger"; +import { info, log } from "@/utils/logger/logger"; export const getChats = async (account: T): Promise> => { switch (account.service) { @@ -24,7 +24,7 @@ export const getChats = async (account: T): Promise (account: T, chat: Ch return await getChatRecipients(service, chat); } default: - console.info(`[getChatRecipients]: returning empty since ${account.service} not implemented.`); + info(`Returning empty since ${account.service} not implemented.`, "getChatRecipients"); return []; } }; @@ -73,7 +73,7 @@ export const sendMessageInChat = async (account: T, chat: Ch return await sendMessageInChat(service, chat, content); } default: - console.info("[sendMessageInChat]: Not Implementend."); + info("Not Implementend.", "sendMessageInChat"); } }; @@ -96,7 +96,7 @@ export const getChatMessages = async (account: T, chat: Chat return await getChatMessages(service, chat); } default: - console.info(`[getChatMessages]: returning empty since ${account.service} not implemented.`); + info(`Returning empty since ${account.service} not implemented.`, "getChatMessages"); return []; } }; @@ -116,7 +116,7 @@ export const createDiscussionRecipients = async (account: T) return await createDiscussionRecipients(service); } default: - console.info(`[createDiscussionRecipients]: returning empty since ${account.service} not implemented.`); + info(`Returning empty since ${account.service} not implemented.`, "createDiscussionRecipients"); return []; } }; @@ -137,6 +137,6 @@ export const createDiscussion = async (account: T, subject: return await createDiscussion(service, subject, content, recipients); } default: - console.info(`[createDiscussion]: doing nothing since ${account.service} is not implemented.`); + info(`Doing nothing since ${account.service} is not implemented.`, "createDiscussion"); } }; diff --git a/src/services/homework.ts b/src/services/homework.ts index 1bc5cff6d..68cbd8837 100644 --- a/src/services/homework.ts +++ b/src/services/homework.ts @@ -1,7 +1,7 @@ import { type Account, AccountService } from "@/stores/account/types"; import { useHomeworkStore } from "@/stores/homework"; import type { Homework } from "./shared/Homework"; -import { error, log } from "@/utils/logger/logger"; +import { error, info, log } from "@/utils/logger/logger"; import { translateToWeekNumber } from "pawnote"; import { pronoteFirstDate } from "./pronote/timetable"; import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; @@ -55,7 +55,7 @@ export async function updateHomeworkForWeekInCache (account: return updateHomeworkForWeekInCache(service, date); } default: - console.info(`[updateHomeworkForWeekInCache]: updating to empty since ${account.service} not implemented.`); + info(`Updating to empty since ${account.service} not implemented.`, "updateHomeworkForWeekInCache"); } useHomeworkStore.getState().updateHomeworks(dateToEpochWeekNumber(date), homeworks); diff --git a/src/services/iutlan/attendance.ts b/src/services/iutlan/attendance.ts index c6cf91c6d..cbc1c10d3 100644 --- a/src/services/iutlan/attendance.ts +++ b/src/services/iutlan/attendance.ts @@ -1,6 +1,7 @@ import { LocalAccount } from "@/stores/account/types"; import { Attendance } from "../shared/Attendance"; import { Absence } from "../shared/Absence"; +import { error } from "@/utils/logger/logger"; export interface scodocAbsence { idAbs: string; @@ -55,8 +56,8 @@ export const saveIUTLanAttendance = async ( punishments: [], observations: [], }; - } catch (error) { - console.error(error); + } catch (err) { + error("" + (err as Error)?.stack, "saveIUTLanAttendance"); throw new Error("Failed to save attendance data"); } }; diff --git a/src/services/iutlan/grades.ts b/src/services/iutlan/grades.ts index 28faa4d50..2429ca3a2 100644 --- a/src/services/iutlan/grades.ts +++ b/src/services/iutlan/grades.ts @@ -1,16 +1,16 @@ import { LocalAccount } from "@/stores/account/types"; -import { +import { type AverageOverview, type Grade } from "@/services/shared/Grade"; import uuid from "@/utils/uuid-v4"; +import { error } from "@/utils/logger/logger"; export const saveIUTLanGrades = async (account: LocalAccount, periodName: string): Promise<{ grades: Grade[]; averages: AverageOverview; }> => { try { - // console.log(periodName); // Il faudrait peut-être penser à typer cette partie, tous les types sont any :( const data = account.serviceData.semestres as any; @@ -171,7 +171,7 @@ export const saveIUTLanGrades = async (account: LocalAccount, periodName: string return { grades: gradesList, averages: averages }; } catch(e) { - console.error(e); + error("" + (e as Error)?.stack, "saveIUTLanGrades"); return { grades: [], averages: { diff --git a/src/services/izly/reload.ts b/src/services/izly/reload.ts index 18b6887c9..42b236e1a 100644 --- a/src/services/izly/reload.ts +++ b/src/services/izly/reload.ts @@ -7,6 +7,6 @@ export const reload = async (account: IzlyAccount): Promise => { const secret = account.authentication.secret; await refresh(instance, secret); - log("session refreshed", "izly"); + log("Session refreshed", "izly"); return instance; }; diff --git a/src/services/pronote/dataset_geolocation.ts b/src/services/pronote/dataset_geolocation.ts index e243fc99e..e49bcbcb7 100644 --- a/src/services/pronote/dataset_geolocation.ts +++ b/src/services/pronote/dataset_geolocation.ts @@ -1,5 +1,6 @@ import pronote from "pawnote"; import datasets from "../../consts/datasets.json"; +import { error, log } from "@/utils/logger/logger"; const getInstancesFromDataset = async (longitude: number, latitude: number): Promise => { let adress_api_fetch = await fetch(`https://api-adresse.data.gouv.fr/reverse/?lon=${longitude}&lat=${latitude}&limit=1`); @@ -15,7 +16,7 @@ const getInstancesFromDataset = async (longitude: number, latitude: number): Pro try { let instances = await instances_fetch.json(); - console.log("Fetched instances:", instances); + log("Fetched instances: " + instances, "getInstancesFromDataset"); const calculateHaversineDistance = (lat1: number, lon1: number, lat2: number, lon2: number): number => { const toRadians = (degrees: number) => degrees * (Math.PI / 180); @@ -40,9 +41,9 @@ const getInstancesFromDataset = async (longitude: number, latitude: number): Pro instance.long ); - console.log("User location:", { latitude, longitude }); - console.log("Instance location:", { latitude: instance.lat, longitude: instance.long }); - console.log("Calculated distance:", distance); + log(`User location: { ${latitude}, ${longitude} }`, "getInstancesFromDataset"); + log(`Instance location: { latitude: ${instance.lat}, longitude: ${instance.long} }`, "getInstancesFromDataset"); + log("Calculated distance: " + distance, "getInstancesFromDataset"); return { name: instance.name.toUpperCase(), @@ -52,12 +53,12 @@ const getInstancesFromDataset = async (longitude: number, latitude: number): Pro latitude: instance.lat, }; }); - } catch (error) { - console.error("Error fetching instances:", error); + } catch (err) { + error("Error fetching instances:" + err, "getInstancesFromDataset"); return []; } - } catch (error) { - console.error("Error fetching address:", error); + } catch (err) { + error("Error fetching address:" + err, "getInstancesFromDataset"); return []; } }; diff --git a/src/services/reload-account.ts b/src/services/reload-account.ts index d45dad3eb..b43e60510 100644 --- a/src/services/reload-account.ts +++ b/src/services/reload-account.ts @@ -66,7 +66,7 @@ export async function reload (account: T): Promise { axioss.interceptors.response.use((r: AxiosResponse) => r, (error)=>{ if(error.response?.data?.errors?.find((e:any)=>e.title.includes("PRONOTE_RESOURCES"))) return Promise.resolve(error); - if(__DEV__) { - console.warn( - "[SKOLENGO] ERR - ", - JSON.stringify(error, null, 2), - JSON.stringify(error.response?.data, null, 2) - ); - } + if(__DEV__) console.warn( + "[SKOLENGO] ERR - ", + JSON.stringify(error, null, 2), + JSON.stringify(error.response?.data, null, 2) + ); + error.response?.data?.errors?.forEach((e: any) => { // if unknown error, don't display the error message if(!e["title"] || e["title"] === "FORBIDDEN") return; diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index cf670a271..b331b3a54 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -32,7 +32,7 @@ export const useCurrentAccount = create()((set, get) => ({ associatedAccounts: [], mutateProperty: (key: T, value: PrimaryAccount[T], forceMutation = false) => { - log(`mutate property ${key} in storage`, "current:update"); + log(`Mutate property ${key} in storage`, "current:update"); // Special case to keep Papillon Space custom image if (get().account?.service === AccountService.PapillonMultiService && key === "personalization" && !forceMutation) { @@ -66,11 +66,11 @@ export const useCurrentAccount = create()((set, get) => ({ } }); } - log(`done mutating property ${key} in storage`, "[current:update]"); + log(`Done mutating property ${key} in storage`, "current:update"); }, switchTo: async (account) => { - log(`reading ${account.name}`, "switchTo"); + log(`Reading ${account.name}`, "switchTo"); set({ account }); useAccounts.setState({ lastOpenedAccountID: account.localID }); @@ -94,14 +94,14 @@ export const useCurrentAccount = create()((set, get) => ({ // Special case for spaces if (account.service === AccountService.PapillonMultiService) { - log("switching to virtual space, skipping main account reload and reloading associated accounts...", "[switchTo]"); + log("Switching to virtual space, skipping main account reload and reloading associated accounts...", "switchTo"); } else if (typeof account.instance === "undefined") { // Account is currently not authenticated, - log("instance undefined, reloading...", "switchTo"); + log("Instance undefined, reloading...", "switchTo"); // Automatically reconnect the main instance. const { instance, authentication } = await reload(account); get().mutateProperty("authentication", authentication); get().mutateProperty("instance", instance); - log("instance reload done !", "switchTo"); + log("Instance reload done !", "switchTo"); } const linkedAccounts = account.linkedExternalLocalIDs.map((linkedID) => { @@ -118,14 +118,14 @@ export const useCurrentAccount = create()((set, get) => ({ const { instance, authentication } = await reload(linkedAccount); linkedAccount.instance = instance; linkedAccount.authentication = authentication; - log("reloaded external", "switchTo"); + log("Reloaded external", "switchTo"); } for (const associatedAccount of associatedAccounts) { if (!(typeof associatedAccount.instance === "undefined")) continue; const { instance, authentication } = await reload(associatedAccount).catch(err => { - error(`failed to reload associated account: ${err} !`, "[switchTo]"); + error(`failed to reload associated account: ${err} !`, "switchTo"); return { instance: associatedAccount.instance, authentication: associatedAccount.authentication @@ -135,21 +135,21 @@ export const useCurrentAccount = create()((set, get) => ({ associatedAccount.authentication = authentication; // Persist authentification value (f.e if token was renewed, it's important to make it persistant) useAccounts.getState().update(associatedAccount.localID, "authentication", authentication); - log("reloaded associated account", "[switchTo]"); + log("Reloaded associated account", "switchTo"); } // Setting instance to a non-null value after associated accounts reload, to keep the loading icon while instances are reloading... if (account.service === AccountService.PapillonMultiService) get().mutateProperty("instance", "PapillonPrime"); // A random string, so the instance is not "undefined" or "null", to prevent creating infinite loading (an undefined instance is interpreted as a loading or disconnected account...) - log("reloaded all external and associated accounts", "[switchTo]"); + log("Reloaded all external and associated accounts", "switchTo"); set({ linkedAccounts, associatedAccounts }); - log(`done reading ${account.name} and rehydrating stores.`, "switchTo"); + log(`Done reading ${account.name} and rehydrating stores.`, "switchTo"); }, linkExistingExternalAccount: (account) => { - log("linking", "linkExistingExternalAccount"); + log("Linking", "linkExistingExternalAccount"); set((state) => ({ linkedAccounts: [...state.linkedAccounts, account] @@ -160,17 +160,17 @@ export const useCurrentAccount = create()((set, get) => ({ account.localID ]); - log("linked", "linkExistingExternalAccount"); + log("Linked", "linkExistingExternalAccount"); }, logout: () => { const account = get().account; - log(`logging out ${account?.name}`, "current:logout"); + log(`Logging out ${account?.name}`, "current:logout"); // When using PRONOTE, we should make sure to stop the background interval. if (account && account.service === AccountService.Pronote && account.instance) { pronote.clearPresenceInterval(account.instance); - log("stopped pronote presence", "current:logout"); + log("Stopped pronote presence", "current:logout"); } set({ account: null, linkedAccounts: [] }); @@ -193,17 +193,17 @@ export const useAccounts = create()( // When creating, we don't want the "instance" to be stored. create: ({ instance, ...account }) => { - log(`storing ${account.localID} (${"name" in account ? account.name : "no name"})`, "accounts:create"); + log(`Storing ${account.localID} (${"name" in account ? account.name : "no name"})`, "accounts:create"); set((state) => ({ accounts: [...state.accounts, account as Account] })); - log(`stored ${account.localID}`, "accounts:create"); + log(`Stored ${account.localID}`, "accounts:create"); }, remove: (localID) => { - log(`removing ${localID}`, "accounts:remove"); + log(`Removing ${localID}`, "accounts:remove"); set((state) => ({ accounts: state.accounts.filter( @@ -221,7 +221,7 @@ export const useAccounts = create()( // The account deleted above is associated to this space if (spaceAccount.associatedAccountsLocalIDs.includes(localID)) { - log(`found ${localID} in PapillonMultiServiceSpace ${spaceAccount.name}`, "accounts:remove"); + log(`Found ${localID} in PapillonMultiServiceSpace ${spaceAccount.name}`, "accounts:remove"); // Remove the link to the account (and to every feature to which it is linked) spaceAccount.associatedAccountsLocalIDs.splice(spaceAccount.associatedAccountsLocalIDs.indexOf(localID), 1); @@ -237,7 +237,7 @@ export const useAccounts = create()( account.localID === spaceAccount.localID ? spaceAccount : account ) })); - log(`removed ${localID} from PapillonMultiServiceSpace ${spaceAccount.name}`, "accounts:remove"); + log(`Removed ${localID} from PapillonMultiServiceSpace ${spaceAccount.name}`, "accounts:remove"); } // If the space is now empty; deleting it @@ -249,11 +249,11 @@ export const useAccounts = create()( (account) => account.localID !== spaceAccount.localID ) })); - log(`deleted PapillonMultiServiceSpace ${spaceAccount.name}`, "accounts:remove"); + log(`Deleted PapillonMultiServiceSpace ${spaceAccount.name}`, "accounts:remove"); } } - log(`removed ${localID}`, "accounts:remove"); + log(`Removed ${localID}`, "accounts:remove"); }, /** diff --git a/src/stores/homework/index.ts b/src/stores/homework/index.ts index d60f27fe2..8f4f5f3db 100644 --- a/src/stores/homework/index.ts +++ b/src/stores/homework/index.ts @@ -10,7 +10,7 @@ export const useHomeworkStore = create()( (set) => ({ homeworks: {}, updateHomeworks: (epochWeekNumber, homeworks) => { - log(`updating homeworks for week ${epochWeekNumber}`, "homework:updateHomeworks"); + log(`Updating homeworks for week ${epochWeekNumber}`, "homework:updateHomeworks"); set((state) => { return { @@ -21,7 +21,7 @@ export const useHomeworkStore = create()( }; }); - log(`updated homeworks for week ${epochWeekNumber}`, "homework:updateHomeworks"); + log(`Updated homeworks for week ${epochWeekNumber}`, "homework:updateHomeworks"); } }), { diff --git a/src/stores/multiService/index.ts b/src/stores/multiService/index.ts index 3a3e5794f..9af5f2a91 100644 --- a/src/stores/multiService/index.ts +++ b/src/stores/multiService/index.ts @@ -19,17 +19,17 @@ export const useMultiService = create()( // When creating, we don't want the "instance" to be stored. create: (space, linkAccount) => { - log(`creating a virtual MultiService space with account id ${linkAccount.localID} (${space.name})`, "multiService:create"); + log(`Creating a virtual MultiService space with account id ${linkAccount.localID} (${space.name})`, "multiService:create"); set((state) => ({ spaces: [...state.spaces, space] })); - log(`stored ${space.name}, with account ${linkAccount.localID}`, "multiService:create"); + log(`Stored ${space.name}, with account ${linkAccount.localID}`, "multiService:create"); }, remove: (localID) => { - log(`removing virtual MultiService space ${localID}`, "multiService:remove"); + log(`Removing virtual MultiService space ${localID}`, "multiService:remove"); set((state) => ({ spaces: state.spaces.filter( @@ -37,7 +37,7 @@ export const useMultiService = create()( ) })); - log(`removed ${localID}`, "multiService:remove"); + log(`Removed ${localID}`, "multiService:remove"); }, toggleEnabledState: () => { diff --git a/src/stores/news/index.ts b/src/stores/news/index.ts index 6b36c8b43..9571678db 100644 --- a/src/stores/news/index.ts +++ b/src/stores/news/index.ts @@ -10,9 +10,9 @@ export const useNewsStore = create()( (set) => ({ informations: [], updateInformations: (informations) => { - log("updating store...", "news:updateInformations"); + log("Updating store...", "news:updateInformations"); set(() => ({ informations })); - log("updated store.", "news:updateInformations"); + log("Updated store.", "news:updateInformations"); } }), { diff --git a/src/stores/timetable/index.ts b/src/stores/timetable/index.ts index b0adc2e13..4ddb91793 100644 --- a/src/stores/timetable/index.ts +++ b/src/stores/timetable/index.ts @@ -10,7 +10,7 @@ export const useTimetableStore = create()( (set) => ({ timetables: {}, updateClasses: (weekNumber, classes) => { - log(`updating classes for week ${weekNumber}`, "timetable:updateClasses"); + log(`Updating classes for week ${weekNumber}`, "timetable:updateClasses"); set((state) => { return { @@ -21,10 +21,10 @@ export const useTimetableStore = create()( }; }); - log(`[timetable:updateClasses]: updated classes for week ${weekNumber}`, "timetable:updateClasses"); + log(`Updated classes for week ${weekNumber}`, "timetable:updateClasses"); }, injectClasses: (data: any) => { - log("replacing classes", "timetable:replaceClasses"); + log("Replacing classes", "timetable:replaceClasses"); set((state) => { return { @@ -32,10 +32,10 @@ export const useTimetableStore = create()( }; }); - log(`[timetable:replaceClasses]: replaced classes for week ${data.weekNumber}`, "timetable:replaceClasses"); + log(`Replaced classes for week ${data.weekNumber}`, "timetable:replaceClasses"); }, removeClasses: (weekNumber) => { - log(`removing classes for week ${weekNumber}`, "timetable:removeClasses"); + log(`Removing classes for week ${weekNumber}`, "timetable:removeClasses"); set((state) => { const timetables = { ...state.timetables }; @@ -45,10 +45,10 @@ export const useTimetableStore = create()( }; }); - log(`[timetable:removeClasses]: removed classes for week ${weekNumber}`, "timetable:removeClasses"); + log(`Removed classes for week ${weekNumber}`, "timetable:removeClasses"); }, removeClassesFromSource: (source) => { - log(`removing classes from source ${source}`, "timetable:removeClassesFromSource"); + log(`Removing classes from source ${source}`, "timetable:removeClassesFromSource"); set((state) => { const timetables = { ...state.timetables }; @@ -60,7 +60,7 @@ export const useTimetableStore = create()( }; }); - log(`[timetable:removeClassesFromSource]: removed classes from source ${source}`, "timetable:removeClassesFromSource"); + log(`Removed classes from source ${source}`, "timetable:removeClassesFromSource"); } }), { diff --git a/src/utils/GetRessources/GetContribs.tsx b/src/utils/GetRessources/GetContribs.tsx index 51ffe0ebd..9f9a36cee 100644 --- a/src/utils/GetRessources/GetContribs.tsx +++ b/src/utils/GetRessources/GetContribs.tsx @@ -1,5 +1,6 @@ import axios from "axios"; -import teams from "../../utils/data/teams.json"; +import teams from "@/utils/data/teams.json"; +import { error } from "@/utils/logger/logger"; export interface Contributor { login: string; @@ -21,8 +22,8 @@ export async function getContributors (): Promise { return allContributors.filter(({ login }) => !teamGithubUsernames.has(login.toLowerCase()) ); - } catch (error) { - console.error("Erreur lors de la récupération des contributeurs:", error); + } catch (err) { + error("Erreur lors de la récupération des contributeurs:" + err, "getContributors"); return []; } } diff --git a/src/utils/GetRessources/GetDefaultProfilePicture.tsx b/src/utils/GetRessources/GetDefaultProfilePicture.tsx index 132a16507..36b656cc7 100644 --- a/src/utils/GetRessources/GetDefaultProfilePicture.tsx +++ b/src/utils/GetRessources/GetDefaultProfilePicture.tsx @@ -3,6 +3,7 @@ import { AccountService, PrimaryAccount } from "@/stores/account/types"; +import { error, warn } from "@/utils/logger/logger"; // Depending on your account type, download the default profile photo export async function getDefaultProfilePicture ( @@ -16,7 +17,7 @@ export async function getDefaultProfilePicture ( : undefined; } case AccountService.Skolengo: - console.warn("Skolengo does not provide a profile picture."); + warn("Skolengo does not provide a profile picture.", "getDefaultProfilePicture"); return undefined; // Skolengo does not provide profile pictures case AccountService.EcoleDirecte: @@ -30,8 +31,8 @@ export async function getDefaultProfilePicture ( default: return undefined; } - } catch (error) { - console.error("Error while retrieving default profile picture:", error); + } catch (err) { + error("Error while retrieving default profile picture:" + err, "getDefaultProfilePicture"); return undefined; // Return undefined in case of error } } diff --git a/src/utils/chat/themes/GetThemeForChat.ts b/src/utils/chat/themes/GetThemeForChat.ts index 0b9989bfa..173cffb16 100644 --- a/src/utils/chat/themes/GetThemeForChat.ts +++ b/src/utils/chat/themes/GetThemeForChat.ts @@ -1,6 +1,7 @@ import { Theme } from "@/utils/chat/themes/Themes.types"; import { DefaultTheme } from "@/consts/DefaultTheme"; import AsyncStorage from "@react-native-async-storage/async-storage"; +import { log } from "@/utils/logger/logger"; async function GetThemeForChatId (chatId: string): Promise { //Just a stupid way to clone an object @@ -32,7 +33,7 @@ async function GetThemeForChatId (chatId: string): Promise { if (theme_json.lightModifier.chatBackgroundImage) { let image = await AsyncStorage.getItem("theme_" + theme.meta.name + "_@" + theme.meta.author + "_" + theme_json.lightModifier.chatBackgroundImage); - console.log("theme_" + theme.meta.name + "_@" + theme.meta.author + "_" + theme_json.lightModifier.chatBackgroundImage); + log("Theme_" + theme.meta.name + "_@" + theme.meta.author + "_" + theme_json.lightModifier.chatBackgroundImage, "GetThemeForChatId"); if (image !== null) { theme.lightModifier.chatBackgroundImage = image; } diff --git a/src/utils/chat/themes/SetThemeForChat.ts b/src/utils/chat/themes/SetThemeForChat.ts index ebe3b9431..57f4ca65e 100644 --- a/src/utils/chat/themes/SetThemeForChat.ts +++ b/src/utils/chat/themes/SetThemeForChat.ts @@ -1,4 +1,5 @@ import { ThemesMeta } from "@/utils/chat/themes/Themes.types"; +import { error, log } from "@/utils/logger/logger"; import AsyncStorage from "@react-native-async-storage/async-storage"; async function DownloadTheme (meta: ThemesMeta): Promise { @@ -6,24 +7,24 @@ async function DownloadTheme (meta: ThemesMeta): Promise { let r_theme = await f_theme.json(); let to_download: string[] = []; - if (r_theme.darkModifier && r_theme.darkModifier.chatBackgroundImage) { + if (r_theme.darkModifier?.chatBackgroundImage) { to_download.push(r_theme.darkModifier.chatBackgroundImage); } - if (r_theme.lightModifier && r_theme.lightModifier.chatBackgroundImage) { + if (r_theme.lightModifier?.chatBackgroundImage) { to_download.push(r_theme.lightModifier.chatBackgroundImage); } - for (let i = 0; i < to_download.length; i++) { - let f = await fetch("https://raw.githubusercontent.com/PapillonApp/datasets/refs/heads/main/themes/" + meta.path + "/" + to_download[i]); + for (const element of to_download) { + let f = await fetch("https://raw.githubusercontent.com/PapillonApp/datasets/refs/heads/main/themes/" + meta.path + "/" + element); let r = await f.blob(); let reader = new FileReader(); reader.readAsDataURL(r); reader.onloadend = function () { if (reader.result && typeof reader.result === "string") { let base64data = reader.result; - AsyncStorage.setItem("theme_" + meta.name + "_@" + meta.author + "_" + to_download[i], base64data); - console.log("Asset downloaded to " + "theme_" + meta.name + "_@" + meta.author + "_" + to_download[i]); + AsyncStorage.setItem("theme_" + meta.name + "_@" + meta.author + "_" + element, base64data); + log("Asset downloaded to " + "theme_" + meta.name + "_@" + meta.author + "_" + element, "DownloadTheme"); } else { - console.error("Error: reader.result is not a string or is null."); + error("Error: reader.result is not a string or is null.", "DownloadTheme"); } }; } @@ -35,7 +36,7 @@ async function DownloadTheme (meta: ThemesMeta): Promise { async function SetThemeForChatId (chatId: string, meta: ThemesMeta): Promise { let themeDownloaded = await DownloadTheme(meta); if (!themeDownloaded) { - console.error("Failed to download the theme for chatId: " + chatId); + error("Failed to download the theme for chatId: " + chatId, "SetThemeForChatId"); return false; } diff --git a/src/utils/logger/logger.ts b/src/utils/logger/logger.ts index 249c2848e..17ee117fc 100644 --- a/src/utils/logger/logger.ts +++ b/src/utils/logger/logger.ts @@ -24,7 +24,7 @@ function get_message (type: number, date: string, from: string, message: string) ); } -function obtain_function_name (from?: string): string { +function obtain_function_name (from: string): string { const error = new Error(); // On génère une erreur pour obtenir la stacktrace const stack = error.stack?.split("\n") || []; @@ -68,25 +68,25 @@ function save_logs_to_memory (log: string) { }); } -function log (message: string, from?: string): void { +function log (message: string, from: string): void { let log = get_message(0, get_iso_date(), obtain_function_name(from), message); save_logs_to_memory(log); console.log(log); } -function error (message: string, from?: string): void { +function error (message: string, from: string): void { let log = get_message(1, get_iso_date(), obtain_function_name(from), message); save_logs_to_memory(log); console.error(log); } -function warn (message: string, from?: string): void { +function warn (message: string, from: string): void { let log = get_message(2, get_iso_date(), obtain_function_name(from), message); save_logs_to_memory(log); console.warn(log); } -function info (message: string, from?: string): void { +function info (message: string, from: string): void { let log = get_message(3, get_iso_date(), obtain_function_name(from), message); save_logs_to_memory(log); console.info(log); @@ -109,7 +109,7 @@ async function get_brute_logs (): Promise { export interface Log { type: string; date: string; - from?: string; + from: string; message: string; } diff --git a/src/views/account/Chat/Messages.tsx b/src/views/account/Chat/Messages.tsx index 2cf8b6ee2..7622089f1 100644 --- a/src/views/account/Chat/Messages.tsx +++ b/src/views/account/Chat/Messages.tsx @@ -39,6 +39,7 @@ import { hasFeatureAccountSetup } from "@/utils/multiservice"; import { MultiServiceFeature } from "@/stores/multiService/types"; import { timestampToString } from "@/utils/format/DateHelper"; import { OfflineWarning, useOnlineStatus } from "@/hooks/useOnlineStatus"; +import { error } from "@/utils/logger/logger"; // Voir la documentation de `react-navigation`. // @@ -92,7 +93,7 @@ const Discussions: Screen<"Discussions"> = ({ navigation, route }) => { const chats = await getChats(account); setChats(chats); } catch (e) { - console.error("Erreur lors du chargement des discussions :", e); + error("Erreur lors du chargement des discussions :" + e, "Discussions/fetchChats"); } }, [enabled, supported]); diff --git a/src/views/account/Chat/Modals/ChatDetails.tsx b/src/views/account/Chat/Modals/ChatDetails.tsx index 6499c05b0..d9e778745 100644 --- a/src/views/account/Chat/Modals/ChatDetails.tsx +++ b/src/views/account/Chat/Modals/ChatDetails.tsx @@ -15,6 +15,7 @@ import { ThemesMeta } from "@/utils/chat/themes/Themes.types"; import GetThemeForChatId from "@/utils/chat/themes/GetThemeForChat"; import SetThemeForChatId from "@/utils/chat/themes/SetThemeForChat"; import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; +import { error } from "@/utils/logger/logger"; const ChatDetails: Screen<"ChatDetails"> = ({ navigation, route }) => { const account = useCurrentAccount((state) => state.account!); @@ -35,8 +36,8 @@ const ChatDetails: Screen<"ChatDetails"> = ({ navigation, route }) => { .then((themes) => { setAvailableThemes(themes); }) - .catch((error) => { - console.error("Error fetching themes:", error); + .catch((err) => { + error("Error fetching themes:" + err, "ChatDetails/GetAvailableThemes"); }); }, []); @@ -45,8 +46,8 @@ const ChatDetails: Screen<"ChatDetails"> = ({ navigation, route }) => { .then((theme) => { setActualTheme(theme.meta.path); }) - .catch((error) => { - console.error("Error fetching themes:", error); + .catch((err) => { + error("Error fetching themes:" + err, "ChatDetails/GetThemeForChatId"); }); }, []); diff --git a/src/views/account/Evaluation/Subject/EvaluationList.tsx b/src/views/account/Evaluation/Subject/EvaluationList.tsx index 632b07f0f..b59fd0e2d 100644 --- a/src/views/account/Evaluation/Subject/EvaluationList.tsx +++ b/src/views/account/Evaluation/Subject/EvaluationList.tsx @@ -15,6 +15,7 @@ import Reanimated, { import SubjectTitle from "./SubjectTitle"; import { Evaluation, EvaluationsPerSubject, Skill } from "@/services/shared/Evaluation"; import { SkillLevelBadge } from "@/views/account/Evaluation/Atoms/SkillLevelBadge"; +import { log } from "@/utils/logger/logger"; interface SubjectItemProps { subject: EvaluationsPerSubject, @@ -91,7 +92,7 @@ const SubjectEvaluationItem: React.FC = ({ subject, .slice(0, 4); const skillLevelsMoreNumber = evaluation.skills.length - 4; - console.log("skillLevelsMoreNumber", skillLevelsMoreNumber); + log("SkillLevelsMoreNumber: " + skillLevelsMoreNumber, "SubjectEvaluationItem"); setSkillLevelsList(skillLevels); setSkillLevelsMoreNumber(skillLevelsMoreNumber); diff --git a/src/views/account/Grades/Atoms/GradesScodocUE.tsx b/src/views/account/Grades/Atoms/GradesScodocUE.tsx index c7f707569..4be5fdd82 100644 --- a/src/views/account/Grades/Atoms/GradesScodocUE.tsx +++ b/src/views/account/Grades/Atoms/GradesScodocUE.tsx @@ -2,6 +2,7 @@ import AnimatedNumber from "@/components/Global/AnimatedNumber"; import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; import { useAlert } from "@/providers/AlertProvider"; import { PrimaryAccount } from "@/stores/account/types"; +import { error } from "@/utils/logger/logger"; import { anim2Papillon } from "@/utils/ui/animations"; import { adjustColor } from "@/utils/ui/colors"; @@ -318,7 +319,7 @@ const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: Prim ); } catch (e) { - console.error(e); + error("" + (e as Error)?.stack, "GradesScodocUE"); return null; } }; diff --git a/src/views/account/Grades/Graph/GradesAverage.tsx b/src/views/account/Grades/Graph/GradesAverage.tsx index 7044521d0..73cfcf021 100644 --- a/src/views/account/Grades/Graph/GradesAverage.tsx +++ b/src/views/account/Grades/Graph/GradesAverage.tsx @@ -32,9 +32,10 @@ import AnimatedNumber from "@/components/Global/AnimatedNumber"; import type { Grade } from "@/services/shared/Grade"; import { AlertTriangle, Check, ExternalLink, PieChart, TrendingUp } from "lucide-react-native"; import { useAlert } from "@/providers/AlertProvider"; +import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; +import { error } from "@/utils/logger/logger"; // Using require to set custom types bc module types are broken const ReanimatedGraph: React.ForwardRefExoticComponent> = require("@birdwingo/react-native-reanimated-graph").default; -import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; interface GradesAverageGraphProps { grades: Grade[]; @@ -123,7 +124,7 @@ const GradesAverageGraph: React.FC = ({ setCurrentAvg(gradesHistoryRef.current[index].value); } catch (e) { - console.error(e); + error("" + (e as Error)?.stack, "GradesAverageGraph/updateTo"); } }, [gradesHistoryRef] diff --git a/src/views/account/Grades/Modals/GradeReaction.tsx b/src/views/account/Grades/Modals/GradeReaction.tsx index 1ed354e58..c40e60231 100644 --- a/src/views/account/Grades/Modals/GradeReaction.tsx +++ b/src/views/account/Grades/Modals/GradeReaction.tsx @@ -14,6 +14,7 @@ import { NativeText } from "@/components/Global/NativeComponents"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { isExpoGo } from "@/utils/native/expoGoAlert"; import { useAlert } from "@/providers/AlertProvider"; +import { error } from "@/utils/logger/logger"; // Types interface SubjectData { @@ -158,8 +159,8 @@ const GradeReaction: Screen<"GradeReaction"> = ({ navigation, route }) => { } })); navigation.goBack(); - } catch (error) { - console.error("Failed to save image:", error); + } catch (err) { + error("Failed to save image:" + err, "GradeReaction/handleCapture"); showAlert({ title: "Erreur", message: "Erreur lors de l'enregistrement de l'image", @@ -169,8 +170,8 @@ const GradeReaction: Screen<"GradeReaction"> = ({ navigation, route }) => { setIsLoading(false); } }, 1000); - } catch (error) { - console.error("Failed to take picture:", error); + } catch (err) { + error("Failed to take picture:" + err, "GradeReaction/handleCapture"); showAlert({ title: "Erreur", message: "Impossible de capturer l'image.", diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 4d083b18d..3e1b11126 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -14,6 +14,7 @@ import { RouteParameters } from "@/router/helpers/types"; import { FadeInDown, FadeOut } from "react-native-reanimated"; import MissingItem from "@/components/Global/MissingItem"; import PapillonLoading from "@/components/Global/PapillonLoading"; +import { error } from "@/utils/logger/logger"; interface HomeworksElementProps { onImportance: (value: number) => unknown @@ -162,7 +163,7 @@ const HomeworksElement: React.FC = ({ navigation, onImpor try { handleDonePress(hw); } catch (e) { - console.error(e); + error("" + (e as Error)?.stack, "HomeworksElement/onDonePressHandler"); } }} /> diff --git a/src/views/account/Homeworks/Homeworks.old.tsx b/src/views/account/Homeworks/Homeworks.old.tsx index ccaa3d60d..4fbb92a3b 100644 --- a/src/views/account/Homeworks/Homeworks.old.tsx +++ b/src/views/account/Homeworks/Homeworks.old.tsx @@ -1,6 +1,6 @@ import { useTheme } from "@react-navigation/native"; import React, { useEffect, useRef, useCallback, useLayoutEffect, useMemo, useState } from "react"; -import { View, ScrollView,Text } from "react-native"; +import { View, ScrollView, Text } from "react-native"; import { Screen } from "@/router/helpers/types"; import { toggleHomeworkState, updateHomeworkForWeekInCache } from "@/services/homework"; import { useHomeworkStore } from "@/stores/homework"; @@ -15,6 +15,7 @@ import { Account } from "@/stores/account/types"; import { debounce } from "lodash"; import { dateToEpochWeekNumber, epochWNToDate } from "@/utils/epochWeekNumber"; import InfinitePager from "react-native-infinite-pager"; +import { log } from "@/utils/logger/logger"; // Types pour les props du composant HomeworkList type HomeworkListProps = { @@ -88,7 +89,7 @@ const HomeworksPage: React.FC = React.memo(({ index, isActiv const homeworksInWeek = homeworks[index] ?? []; const sortedHomework = useMemo( - () => homeworksInWeek.sort((a, b) => new Date(a.due).getTime() - new Date(b.due).getTime()), + () => homeworksInWeek.toSorted((a, b) => new Date(a.due).getTime() - new Date(b.due).getTime()), [homeworksInWeek] ); @@ -160,7 +161,7 @@ const HomeworksScreen: Screen<"Homeworks"> = ({ navigation }) => { const [loading, setLoading] = useState(false); useEffect(() => { - console.log("[Homeworks]: account instance changed"); + log("Account instance changed", "Homeworks"); if (account.instance) { const WN = initialIndex; manuallyChangeWeek(WN); @@ -194,9 +195,9 @@ const HomeworksScreen: Screen<"Homeworks"> = ({ navigation }) => { const updateHomeworks = useCallback(async () => { setLoading(true); - console.log("[Homeworks]: updating cache...",epochWeekNumber, epochWNToDate(epochWeekNumber)); + log("Updating cache..." + epochWeekNumber + epochWNToDate(epochWeekNumber), "Homeworks/Update"); await updateHomeworkForWeekInCache(account, epochWNToDate(epochWeekNumber)); - console.log("[Homeworks]: updated cache !", epochWNToDate(epochWeekNumber)); + log("Updated cache !" + epochWNToDate(epochWeekNumber), "Homeworks/Update"); setLoading(false); }, [account, epochWeekNumber]); diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index fd2d8b73e..4d6895bee 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -46,6 +46,7 @@ import { animPapillon } from "@/utils/ui/animations"; import { STORE_THEMES, StoreTheme } from "@/views/account/Restaurant/Cards/StoreThemes"; import { OfflineWarning, useOnlineStatus } from "@/hooks/useOnlineStatus"; import { useAlert } from "@/providers/AlertProvider"; +import { warn } from "@/utils/logger/logger"; export const formatCardIdentifier = (identifier: string, dots: number = 4, separator: string = " ") => { if(!identifier) { @@ -204,25 +205,25 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { const dailyMenu = account ? await getMenu(account, pickerDate).catch(() => null) : null; const accountPromises = linkedAccounts.map(async (account) => { try { - if (!account || !account.service) { + if (!account?.service) { return; } const [balance, history, cardnumber, booking] = await Promise.all([ balanceFromExternal(account, isRefreshing).catch(err => { - console.warn(`Error fetching balance for account ${account.username}:`, err); + warn(`Error fetching balance for account ${account.username}:` + err, "Menu/balanceFromExternal"); return []; }), reservationHistoryFromExternal(account).catch(err => { - console.warn(`Error fetching history for account ${account}:`, err); + warn(`Error fetching history for account ${account.username}:` + err, "Menu/reservationHistoryFromExternal"); return []; }), qrcodeFromExternal(account).catch(err => { - console.warn(`Error fetching QR code for account ${account}:`, err); + warn(`Error fetching QR code for account ${account.username}:` + err, "Menu/qrcodeFromExternal"); return "0"; }), getBookingsAvailableFromExternal(account, getWeekNumber(new Date()), isRefreshing).catch(err => { - console.warn(`Error fetching bookings for account ${account}:`, err); + warn(`Error fetching bookings for account ${account.username}:` + err, "Menu/getBookingsAvailableFromExternal"); return []; }) ]); @@ -243,7 +244,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { newCards.push(newCard); } catch (error) { setIsInitialised(true); - console.warn(`An error occurred with account ${account}:`, error); + warn(`An error occurred with account ${account.username}:` + error, "Menu/fetchCardsData"); } }); @@ -254,7 +255,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { setIsInitialised(true); setIsRefreshing(false); } catch (error) { - console.warn("An error occurred while fetching data:", error); + warn("An error occurred while fetching data:" + error, "Menu/fetchCardsData"); } })(); }; diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index 98b28215e..f2ba0d944 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -20,6 +20,7 @@ import { formatCardIdentifier } from "../Menu"; import { LinearGradient } from "expo-linear-gradient"; import { TouchableOpacity } from "react-native-gesture-handler"; import PapillonPicker from "@/components/Global/PapillonPicker"; +import { error, warn } from "@/utils/logger/logger"; const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigation }) => { try { @@ -38,11 +39,11 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio try { const [balance, history] = await Promise.all([ balanceFromExternal(route.params.card.account as ExternalAccount).catch(err => { - console.warn(`Error fetching balance for account ${account}:`, err); + warn(`Error fetching balance for account ${account?.name}:` + err, "CardDetail/balanceFromExternal"); return []; }), reservationHistoryFromExternal(route.params.card.account as ExternalAccount).catch(err => { - console.warn(`Error fetching history for account ${account}:`, err); + warn(`Error fetching history for account ${account?.name}:` + err, "CardDetail/reservationHistoryFromExternal"); return []; }) ]); @@ -55,7 +56,7 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio }); } catch (e) { - console.log(e); + error("" + (e as Error)?.stack, "CardDetail/updateCardData"); } }; @@ -111,7 +112,7 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio navigation.goBack(); } catch (e) { - console.log(e); + error("" + (e as Error)?.stack, "CardDetail/removeAccount"); } } } @@ -355,7 +356,7 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio ); } catch (e) { - console.log(e); + error("" + (e as Error)?.stack, "CardDetail"); return ; } }; diff --git a/src/views/account/Restaurant/Modals/QrCode.tsx b/src/views/account/Restaurant/Modals/QrCode.tsx index acdac4929..0d62c3906 100644 --- a/src/views/account/Restaurant/Modals/QrCode.tsx +++ b/src/views/account/Restaurant/Modals/QrCode.tsx @@ -13,6 +13,7 @@ import { Screen } from "@/router/helpers/types"; import { ExternalAccount } from "@/stores/account/types"; import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; import * as Brightness from "expo-brightness"; +import { log } from "@/utils/logger/logger"; const RestaurantQrCode: Screen<"RestaurantQrCode"> = ({ route, navigation }) => { const { card } = route.params; @@ -68,13 +69,13 @@ const RestaurantQrCode: Screen<"RestaurantQrCode"> = ({ route, navigation }) => // Si Izly if(card.service === 10) { const interval = setInterval(() => { - console.log("[CANTINE >> IZLY] Demande du solde"); + log("[CANTINE >> IZLY] Demande du solde", "QrCode/Izly/PollingBalance"); PollingBalance(); }, 1000); return () => { clearInterval(interval); - console.log("[CANTINE >> IZLY] Fin du polling"); + log("[CANTINE >> IZLY] Fin du polling", "QrCode/Izly/PollingBalance"); }; } }, []); diff --git a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx index 06939c092..f39fde21c 100644 --- a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx +++ b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx @@ -13,6 +13,7 @@ import { animPapillon } from "@/utils/ui/animations"; import { FadeInDown, FadeOutUp } from "react-native-reanimated"; import { useAlert } from "@/providers/AlertProvider"; import { BadgeX, Undo2 } from "lucide-react-native"; +import { error } from "@/utils/logger/logger"; const providers = ["scodoc", "moodle", "ical"]; @@ -103,13 +104,13 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio try { const scodocData = data; - const semestres = (scodocData["semestres"] as any); + const semestres = scodocData["semestres"]; setSemestresToRetrieve(semestres); await retreiveNextSemestre(currentSemestre, semestres); } catch (e) { - console.error(e); + error("" + (e as Error)?.stack, "BackgroundIUTLannion/retreiveGrades"); showAlert({ title: "Erreur", message: "Impossible de récupérer les notes de l'IUT de Lannion. Vérifie ta connexion Internet et réessaie.", @@ -336,7 +337,7 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio }} onError={(data) => { - console.error(data); + error("" + data, "BackgroundIUTLannion/onError"); showAlert({ title: "Erreur", message: "Impossible de se connecter au portail de l'IUT de Lannion. Vérifie ta connexion Internet et réessaie.", @@ -366,7 +367,7 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio } } catch (e) { - console.error(e); + error("" + (e as Error)?.stack, "BackgroundIUTLannion/onMessage"); } }} /> diff --git a/src/views/login/IdentityProvider/providers/Multi.tsx b/src/views/login/IdentityProvider/providers/Multi.tsx index d8cc036ec..64948da9d 100644 --- a/src/views/login/IdentityProvider/providers/Multi.tsx +++ b/src/views/login/IdentityProvider/providers/Multi.tsx @@ -8,6 +8,7 @@ import { useAccounts, useCurrentAccount } from "@/stores/account"; import { AccountService, type MultiAccount } from "@/stores/account/types"; import defaultPersonalization from "@/services/multi/default-personalization"; import LoginView from "@/components/Templates/LoginView"; +import { error as error_logger } from "@/utils/logger/logger"; const Muli_Login: Screen<"Multi_Login"> = ({ route, navigation }) => { const [loading, setLoading] = useState(false); @@ -71,16 +72,16 @@ const Muli_Login: Screen<"Multi_Login"> = ({ route, navigation }) => { }); }); } - catch (error) { - if (error instanceof Error) { - setError(error.message); + catch (err) { + if (err instanceof Error) { + setError(err.message); } else { setError("Erreur inconnue"); } setLoading(false); - console.error(error); + error_logger("" + err, "Multi_Login/handleLogin"); } }; diff --git a/src/views/login/IdentityProvider/providers/UnivIUTLannion.tsx b/src/views/login/IdentityProvider/providers/UnivIUTLannion.tsx index 04aaae64d..f7ac7cc8a 100644 --- a/src/views/login/IdentityProvider/providers/UnivIUTLannion.tsx +++ b/src/views/login/IdentityProvider/providers/UnivIUTLannion.tsx @@ -2,6 +2,7 @@ import LoginView from "@/components/Templates/LoginView"; import React, { useMemo, useState } from "react"; import { View } from "react-native"; import type { Screen } from "@/router/helpers/types"; +import { error as error_logger } from "@/utils/logger/logger"; export const UnivIUTLannion_Login: Screen<"UnivIUTLannion_Login"> = ({ navigation }) => { const [error, setError] = useState(null); @@ -15,7 +16,7 @@ export const UnivIUTLannion_Login: Screen<"UnivIUTLannion_Login"> = ({ navigatio }); } catch (e) { - console.error(e); + error_logger("" + e, "UnivIUTLannion_Login/login"); // @ts-expect-error setError(e.toString()); } diff --git a/src/views/login/IdentityProvider/providers/UnivLimoges.tsx b/src/views/login/IdentityProvider/providers/UnivLimoges.tsx index 11f4ff310..82f513df9 100644 --- a/src/views/login/IdentityProvider/providers/UnivLimoges.tsx +++ b/src/views/login/IdentityProvider/providers/UnivLimoges.tsx @@ -157,7 +157,7 @@ const UnivLimoges_Login: Screen<"UnivLimoges_Login"> = ({ navigation }) => { startInLoadingState={true} incognito={true} onLoadStart={(e) => { - log("start " + e.nativeEvent.url, "biome-login"); + log("Start " + e.nativeEvent.url, "biome-login"); if (e.nativeEvent.url.includes(BIOME_ORIGIN)) setLoading(true); @@ -165,7 +165,7 @@ const UnivLimoges_Login: Screen<"UnivLimoges_Login"> = ({ navigation }) => { setLoading(false); }} onLoadEnd={(e) => { - log("end " + e.nativeEvent.url, "biome-login"); + log("End " + e.nativeEvent.url, "biome-login"); if (currentLoginStateIntervalRef.current) clearInterval(currentLoginStateIntervalRef.current); @@ -174,7 +174,7 @@ const UnivLimoges_Login: Screen<"UnivLimoges_Login"> = ({ navigation }) => { setLoading(true); currentLoginStateIntervalRef.current = setInterval(() => { - log("injecting script...", "biome-login"); + log("Injecting script...", "biome-login"); webViewRef.current?.injectJavaScript(` const tokens = sessionStorage.getItem("oidc.default:https://biome.unilim.fr/authentication/callback"); diff --git a/src/views/login/IdentityProvider/providers/UnivRennes2.tsx b/src/views/login/IdentityProvider/providers/UnivRennes2.tsx index fe981104e..fec134d63 100644 --- a/src/views/login/IdentityProvider/providers/UnivRennes2.tsx +++ b/src/views/login/IdentityProvider/providers/UnivRennes2.tsx @@ -38,8 +38,8 @@ function extractStudentDataFromHTML (htmlString: string) { extractSectionData("Formation", data.formation); extractSectionData("Compte Sésame", data.sesamAccount); - } catch (error) { - console.error("Error parsing HTML:", error); + } catch (err) { + error("" + (err as Error)?.stack, "UnivRennes2/extractStudentDataFromHTML"); } return data; @@ -53,6 +53,7 @@ import uuid from "@/utils/uuid-v4"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { NativeText } from "@/components/Global/NativeComponents"; import { useTheme } from "@react-navigation/native"; +import { error } from "@/utils/logger/logger"; const UnivRennes2_Login: Screen<"UnivRennes2_Login"> = ({ navigation }) => { const mainURL = "https://cas.univ-rennes2.fr/login?service=https%3A%2F%2Fservices.univ-rennes2.fr%2Fsesame%2Findex.php%2Flogin%2Fmon-compte-sesame%2Fchanger-mon-mot-de-passe"; diff --git a/src/views/login/IdentityProvider/providers/UnivSorbonneParisNord.tsx b/src/views/login/IdentityProvider/providers/UnivSorbonneParisNord.tsx index cfe48798e..6b0bed936 100644 --- a/src/views/login/IdentityProvider/providers/UnivSorbonneParisNord.tsx +++ b/src/views/login/IdentityProvider/providers/UnivSorbonneParisNord.tsx @@ -9,6 +9,7 @@ import { useTheme } from "@react-navigation/native"; import LoginView from "@/components/Templates/LoginView"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { NativeText } from "@/components/Global/NativeComponents"; +import { error } from "@/utils/logger/logger"; const API_BASE_URL = "https://api.univ-spn.fr"; const USER_AGENT = "USPNAPP/1.0.1 CFNetwork/1568.200.41 Darwin/24.1.0"; @@ -96,8 +97,8 @@ const UnivSorbonneParisNord_login: Screen<"UnivSorbonneParisNord_login"> = ({ na index: 0, routes: [{ name: "AccountCreated" }], }); - } catch (error) { - console.error("Error during login process:", error); + } catch (err) { + error("Error during login process: " + err, "UnivSorbonneParisNord_login/login"); // Here you might want to show an error message to the user } finally { setIsLoading(false); diff --git a/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx b/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx index 04c01172a..ca4fa7c1d 100644 --- a/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx +++ b/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx @@ -28,6 +28,7 @@ import Reanimated, { import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import { SvgFromXml } from "react-native-svg"; import LoginView from "@/components/Templates/LoginView"; +import { error as logger_error, warn } from "@/utils/logger/logger"; const EcoleDirecteCredentials: Screen<"EcoleDirecteCredentials"> = ({ navigation }) => { const [session, setSession] = useState(null); @@ -112,10 +113,10 @@ const EcoleDirecteCredentials: Screen<"EcoleDirecteCredentials"> = ({ navigation }); }); } - catch (error) { - if (error instanceof DoubleAuthRequired) { + catch (err) { + if (err instanceof DoubleAuthRequired) { const challenge = await initDoubleAuth(currentSession!).catch((e) => { - console.error(e); + logger_error("" + (e as Error)?.stack, "EcoleDirecteCredentials/DoubleAuthRequired"); setError("Une erreur est survenue lors de la récupération des questions pour la double authentification"); return null; }).finally(() => setLoading(false)); @@ -124,22 +125,22 @@ const EcoleDirecteCredentials: Screen<"EcoleDirecteCredentials"> = ({ navigation setSession(currentSession); return; } - if (error instanceof Error) { - if (error.message === "Bad credentials, no token found in response") setError("Nom d'utilisateur ou mot de passe incorrect!"); - else setError(error.message); + if (err instanceof Error) { + if (err.message === "Bad credentials, no token found in response") setError("Nom d'utilisateur ou mot de passe incorrect!"); + else setError(err.message); } else { setError("Erreur inconnue"); } setLoading(false); - console.error(error); + logger_error("" + (err as Error)?.stack, "EcoleDirecteCredentials"); } }; const handleChallenge = async (answer: string) => { if (!session) { - console.warn("No session to handle challenge"); + warn("No session to handle challenge", "EcoleDirecteCredentials/handleChallenge"); return; } diff --git a/src/views/login/pronote/PronoteQRCode.tsx b/src/views/login/pronote/PronoteQRCode.tsx index 11ca17e28..fbe2d368f 100644 --- a/src/views/login/pronote/PronoteQRCode.tsx +++ b/src/views/login/pronote/PronoteQRCode.tsx @@ -21,6 +21,7 @@ import extract_pronote_name from "@/utils/format/extract_pronote_name"; import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; import { useAlert } from "@/providers/AlertProvider"; import ResponsiveTextInput from "@/components/FirstInstallation/ResponsiveTextInput"; +import { error } from "@/utils/logger/logger"; const makeUUID = (): string => { let dt = new Date().getTime(); @@ -149,8 +150,8 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { }); }); }, 1000); - } catch (error) { - console.error(error); + } catch (err) { + error("" + (err as Error)?.stack, "PronoteQRCode/loginQR"); showAlert({ title: "Erreur", diff --git a/src/views/login/pronote/PronoteWebview.tsx b/src/views/login/pronote/PronoteWebview.tsx index 5b9a8acc4..0462a0115 100644 --- a/src/views/login/pronote/PronoteWebview.tsx +++ b/src/views/login/pronote/PronoteWebview.tsx @@ -33,6 +33,7 @@ import { animPapillon } from "@/utils/ui/animations"; import { useAlert } from "@/providers/AlertProvider"; import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; import { BadgeInfo, Undo2 } from "lucide-react-native"; +import { error } from "@/utils/logger/logger"; const PronoteWebview: Screen<"PronoteWebview"> = ({ route, navigation }) => { const theme = useTheme(); @@ -242,7 +243,7 @@ const PronoteWebview: Screen<"PronoteWebview"> = ({ route, navigation }) => { setLoadProgress(nativeEvent.progress); }} onError={(e) => { - console.error("Pronote webview error", e); + error("Pronote webview error" + e, "PronoteWebview"); }} onLoadStart={(e) => { const { url } = e.nativeEvent; diff --git a/src/views/login/skolengo/SkolengoWebview.tsx b/src/views/login/skolengo/SkolengoWebview.tsx index 97f6382a0..9f1bbdea8 100644 --- a/src/views/login/skolengo/SkolengoWebview.tsx +++ b/src/views/login/skolengo/SkolengoWebview.tsx @@ -27,6 +27,7 @@ import { getSkolengoAccount } from "@/services/skolengo/skolengo-account"; import { wait } from "@/services/skolengo/data/utils"; import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; import { BadgeX, Undo2 } from "lucide-react-native"; +import { error } from "@/utils/logger/logger"; // TODO : When the app is not started with Expo Go (so with a prebuild or a release build), use the expo auth-session module completely with the deeplink and without the webview. @@ -271,7 +272,7 @@ const getSkolengoURL = async (school: School) => { const url = await authRes.makeAuthUrlAsync(discovery); return { url, discovery, authRes }; } catch (e) { - console.error(e); + error("" + (e as Error)?.stack, "SkolengoWebview/getSkolengoURL"); return null; } }; diff --git a/src/views/settings/ExternalAccount/IzlyActivation.tsx b/src/views/settings/ExternalAccount/IzlyActivation.tsx index 21fceb3f2..0d9c301b1 100644 --- a/src/views/settings/ExternalAccount/IzlyActivation.tsx +++ b/src/views/settings/ExternalAccount/IzlyActivation.tsx @@ -14,6 +14,7 @@ import uuid from "@/utils/uuid-v4"; import * as Linking from "expo-linking"; import { useAlert } from "@/providers/AlertProvider"; import { BadgeX } from "lucide-react-native"; +import { log } from "@/utils/logger/logger"; const IzlyActivation: Screen<"IzlyActivation"> = ({ navigation, route }) => { const theme = useTheme(); @@ -34,10 +35,10 @@ const IzlyActivation: Screen<"IzlyActivation"> = ({ navigation, route }) => { const url = event.url; const scheme = url.split(":")[0]; if (scheme === "izly") { - console.log("[IzlyActivation] Activation link received:", url); + log("[IzlyActivation] Activation link received:" + url, "Izly/handleDeepLink"); handleActivation(url); } else { - console.log("[IzlyActivation] Ignoring link:", url); + log("[IzlyActivation] Ignoring link:" + url, "Izly/handleDeepLink"); } }; diff --git a/src/views/settings/SettingsDevLogs.tsx b/src/views/settings/SettingsDevLogs.tsx index 1cbfd3965..1bbeefffa 100644 --- a/src/views/settings/SettingsDevLogs.tsx +++ b/src/views/settings/SettingsDevLogs.tsx @@ -172,7 +172,7 @@ const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { ) : log.message.toLowerCase().includes("read") ? ( - ) : log.message.startsWith("[timetable:updateClasses") ? ( + ) : log.message.toLowerCase().includes("timetable:updateClasses") ? ( ) : log.message.toLowerCase().includes("folder") ? ( @@ -195,7 +195,7 @@ const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { ? "#1F618D" : log.message.toLowerCase().includes("read") ? "#D4AC02" - : log.message.startsWith("[timetable:updateClasses") + : log.message.toLowerCase().includes("timetable:updateClasses") ? "#884EA0" : log.message.toLowerCase().includes("folder") ? "#CA6F1E" diff --git a/src/widgets/Components/LastGrade.tsx b/src/widgets/Components/LastGrade.tsx index 991b36727..800260883 100644 --- a/src/widgets/Components/LastGrade.tsx +++ b/src/widgets/Components/LastGrade.tsx @@ -11,6 +11,7 @@ import { updateGradesAndAveragesInCache } from "@/services/grades"; import { getSubjectData } from "@/services/shared/Subject"; import { useCurrentAccount } from "@/stores/account"; import { useGradesStore } from "@/stores/grades"; +import { error } from "@/utils/logger/logger"; const LastGradeWidget = forwardRef(({ setLoading, @@ -47,8 +48,8 @@ const LastGradeWidget = forwardRef(({ setLoading(true); try { await updateGradesAndAveragesInCache(account, defaultPeriod); - } catch (error) { - console.error("Erreur lors de la mise à jour des notes :", error); + } catch (err) { + error("Erreur lors de la mise à jour des notes :" + err, "LastGradeWidget/fetchGrades"); } finally { setLoading(false); } diff --git a/src/widgets/Components/RestaurantQRCode.tsx b/src/widgets/Components/RestaurantQRCode.tsx index 2abbaf181..77117b0f5 100644 --- a/src/widgets/Components/RestaurantQRCode.tsx +++ b/src/widgets/Components/RestaurantQRCode.tsx @@ -15,6 +15,7 @@ import { StackNavigationProp } from "@react-navigation/stack"; import { RouteParameters } from "./../../router/helpers/types"; import { formatCardIdentifier, ServiceCard } from "@/views/account/Restaurant/Menu"; import { STORE_THEMES } from "@/views/account/Restaurant/Cards/StoreThemes"; +import { warn } from "@/utils/logger/logger"; type NavigationProps = StackNavigationProp; @@ -49,7 +50,7 @@ const RestaurantQRCodeWidget = forwardRef(({ try { const [cardnumber] = await Promise.all([ qrcodeFromExternal(account).catch(err => { - console.warn(`Error fetching QR code for account ${account}:`, err); + warn(`Error fetching QR code for account ${account.username}:` + err, "RestaurantQRCodeWidget/qrcodeFromExternal"); return "0"; }), ]); @@ -66,8 +67,8 @@ const RestaurantQRCodeWidget = forwardRef(({ }; newCards.push(newCard); - } catch (error) { - console.warn(`An error occurred with account ${account}:`, error); + } catch (err) { + warn(`An error occurred with account ${account.username}:` + err, "RestaurantQRCodeWidget/accountPromises"); } }); From b29e3e2a0599b3ffa8113245ee7f2c166452bd04 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sun, 16 Mar 2025 00:43:39 +0100 Subject: [PATCH 0870/1144] fix: package-lock.json --- package-lock.json | 133 ---------------------------------------------- 1 file changed, 133 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1b8c845cc..1cc562c4a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10117,19 +10117,6 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -12125,66 +12112,6 @@ "lightningcss-win32-x64-msvc": "1.19.0" } }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.19.0.tgz", - "integrity": "sha512-wIJmFtYX0rXHsXHSr4+sC5clwblEMji7HHQ4Ub1/CznVRxtCFha6JIt5JZaNf8vQrfdZnBxLLC6R8pC818jXqg==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.19.0.tgz", - "integrity": "sha512-Lif1wD6P4poaw9c/4Uh2z+gmrWhw/HtXFoeZ3bEsv6Ia4tt8rOJBdkfVaUJ6VXmpKHALve+iTyP2+50xY1wKPw==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.19.0.tgz", - "integrity": "sha512-P15VXY5682mTXaiDtbnLYQflc8BYb774j2R84FgDLJTN6Qp0ZjWEFyN1SPqyfTj2B2TFjRHRUvQSSZ7qN4Weig==", - "cpu": [ - "arm" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/lightningcss-linux-arm64-gnu": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.19.0.tgz", @@ -12225,66 +12152,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.19.0.tgz", - "integrity": "sha512-0AFQKvVzXf9byrXUq9z0anMGLdZJS+XSDqidyijI5njIwj6MdbvX2UZK/c4FfNmeRa2N/8ngTffoIuOUit5eIQ==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.19.0.tgz", - "integrity": "sha512-SJoM8CLPt6ECCgSuWe+g0qo8dqQYVcPiW2s19dxkmSI5+Uu1GIRzyKA0b7QqmEXolA+oSJhQqCmJpzjY4CuZAg==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.19.0.tgz", - "integrity": "sha512-C+VuUTeSUOAaBZZOPT7Etn/agx/MatzJzGRkeV+zEABmPuntv1zihncsi+AyGmjkkzq3wVedEy7h0/4S84mUtg==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", From a7eb3339cd52f5a1796e0e740f49e0ee31a647f6 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sun, 16 Mar 2025 00:47:53 +0100 Subject: [PATCH 0871/1144] fix: package-lock.json --- package-lock.json | 4204 ++++++++++++++++++++++++++++++--------------- 1 file changed, 2864 insertions(+), 1340 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1cc562c4a..5381dbe7e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -131,6 +131,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -143,6 +144,7 @@ "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", @@ -153,28 +155,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", - "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", - "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -190,12 +194,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", - "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.10.tgz", + "integrity": "sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang==", + "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.2", - "@babel/types": "^7.26.0", + "@babel/parser": "^7.26.10", + "@babel/types": "^7.26.10", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -208,6 +213,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "license": "MIT", "dependencies": { "@babel/types": "^7.25.9" }, @@ -215,25 +221,13 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz", - "integrity": "sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==", - "peer": true, - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", - "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.9", + "@babel/compat-data": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -244,16 +238,17 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", - "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.26.9.tgz", + "integrity": "sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg==", + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/helper-replace-supers": "^7.25.9", + "@babel/helper-replace-supers": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", - "@babel/traverse": "^7.25.9", + "@babel/traverse": "^7.26.9", "semver": "^6.3.1" }, "engines": { @@ -264,12 +259,13 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz", - "integrity": "sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz", + "integrity": "sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong==", + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", - "regexpu-core": "^6.1.1", + "regexpu-core": "^6.2.0", "semver": "^6.3.1" }, "engines": { @@ -280,9 +276,10 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", - "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz", + "integrity": "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==", + "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", @@ -295,17 +292,21 @@ } }, "node_modules/@babel/helper-define-polyfill-provider/node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -314,6 +315,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", + "license": "MIT", "dependencies": { "@babel/types": "^7.24.7" }, @@ -325,6 +327,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", + "license": "MIT", "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -337,6 +340,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "license": "MIT", "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -349,6 +353,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9", @@ -365,6 +370,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", + "license": "MIT", "dependencies": { "@babel/types": "^7.25.9" }, @@ -373,9 +379,10 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", - "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -384,6 +391,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-wrap-function": "^7.25.9", @@ -397,13 +405,14 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", - "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz", + "integrity": "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==", + "license": "MIT", "dependencies": { "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/traverse": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -412,22 +421,11 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz", - "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==", - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", + "license": "MIT", "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -440,6 +438,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -448,6 +447,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -456,6 +456,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -464,6 +465,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", + "license": "MIT", "dependencies": { "@babel/template": "^7.25.9", "@babel/traverse": "^7.25.9", @@ -490,6 +492,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.9.tgz", "integrity": "sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==", + "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "chalk": "^2.4.2", @@ -504,6 +507,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -515,6 +519,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -528,6 +533,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", "dependencies": { "color-name": "1.1.3" } @@ -535,12 +541,14 @@ "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", "engines": { "node": ">=0.8.0" } @@ -549,6 +557,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", "engines": { "node": ">=4" } @@ -557,6 +566,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, @@ -583,6 +593,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", @@ -599,6 +610,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -614,6 +626,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -629,6 +642,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", @@ -646,6 +660,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", @@ -663,6 +678,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-async-generator-functions instead.", + "license": "MIT", "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-plugin-utils": "^7.20.2", @@ -681,6 +697,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", + "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -696,6 +713,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.25.9.tgz", "integrity": "sha512-smkNLL/O1ezy9Nhy4CNosc4Va+1wo5w4gzSZeLe6y6dM4mmHfYOCPolXQPHQxonZCF+ZyebxN9vqOolkYrSn5g==", + "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -712,6 +730,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.25.9.tgz", "integrity": "sha512-ykqgwNfSnNOB+C8fV5X4mG3AVmvu+WVxcaU9xHHtBb7PCrPeweMmPjGsn8eMaeJg6SJuoUuZENeeSWaarWqonQ==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -727,6 +746,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-logical-assignment-operators instead.", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" @@ -743,6 +763,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" @@ -759,6 +780,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead.", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-numeric-separator": "^7.10.4" @@ -775,6 +797,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.", + "license": "MIT", "dependencies": { "@babel/compat-data": "^7.20.5", "@babel/helper-compilation-targets": "^7.20.7", @@ -794,6 +817,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-catch-binding instead.", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" @@ -810,6 +834,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", @@ -826,6 +851,7 @@ "version": "7.21.0-placeholder-for-preset-env.2", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "license": "MIT", "peer": true, "engines": { "node": ">=6.9.0" @@ -838,6 +864,7 @@ "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -849,6 +876,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.25.9.tgz", "integrity": "sha512-ryzI0McXUPJnRCvMo4lumIKZUzhYUO/ScI+Mz4YVaTLt04DHNSjEUjKVvbzQjZFLuod/cYEc07mJWhzl6v4DPg==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -863,6 +891,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -874,6 +903,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.25.9.tgz", "integrity": "sha512-9MhJ/SMTsVqsd69GyQg89lYR4o9T+oDGv5F6IsigxxqFVOyR/IflDLYP8WDI1l8fkhNGGktqkvL5qwNCtGEpgQ==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -888,6 +918,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.26.0.tgz", "integrity": "sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -902,6 +933,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -917,6 +949,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -932,6 +965,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -946,6 +980,7 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -957,6 +992,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -968,6 +1004,7 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -979,6 +1016,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -990,6 +1028,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1001,6 +1040,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1012,6 +1052,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1026,6 +1067,7 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", @@ -1042,6 +1084,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1053,14 +1096,15 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz", - "integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz", + "integrity": "sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg==", + "license": "MIT", "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-remap-async-to-generator": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/traverse": "^7.26.8" }, "engines": { "node": ">=6.9.0" @@ -1073,6 +1117,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -1086,12 +1131,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", - "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz", + "integrity": "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==", + "license": "MIT", "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1104,6 +1150,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1118,6 +1165,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", @@ -1134,6 +1182,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", + "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1149,6 +1198,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-compilation-targets": "^7.25.9", @@ -1168,6 +1218,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/template": "^7.25.9" @@ -1183,6 +1234,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1197,6 +1249,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", @@ -1213,6 +1266,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -1228,6 +1282,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", @@ -1244,6 +1299,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -1256,12 +1312,12 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz", - "integrity": "sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz", + "integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==", + "license": "MIT", "peer": true, "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { @@ -1275,6 +1331,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1286,12 +1343,13 @@ } }, "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.25.9.tgz", - "integrity": "sha512-/VVukELzPDdci7UUsWQaSkhgnjIWXnIyRpM02ldxaVoFK96c41So8JcKT3m0gYjyv7j5FNPGS5vfELrWalkbDA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.26.5.tgz", + "integrity": "sha512-eGK26RsbIkYUns3Y8qKl362juDDYK+wEdPGHGrhzUl6CewZFo55VZ7hg+CyMFU4dd5QQakBN86nBMpRsFpRvbQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/plugin-syntax-flow": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/plugin-syntax-flow": "^7.26.0" }, "engines": { "node": ">=6.9.0" @@ -1301,12 +1359,13 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", - "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.26.9.tgz", + "integrity": "sha512-Hry8AusVm8LW5BVFgiyUReuoGzPUpdHQQqJY5bZnbbf+ngOHWuCuYFKw/BqaaWlvEUrF91HMhDtEaI1hZzNbLg==", + "license": "MIT", "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { @@ -1320,6 +1379,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", + "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -1336,6 +1396,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -1351,6 +1412,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1365,6 +1427,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -1380,6 +1443,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -1395,6 +1459,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.25.9", @@ -1408,13 +1473,13 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz", - "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz", + "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==", + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-simple-access": "^7.25.9" + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1427,6 +1492,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.25.9", @@ -1445,6 +1511,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.25.9", @@ -1461,6 +1528,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1476,6 +1544,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -1488,11 +1557,12 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz", - "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==", + "version": "7.26.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz", + "integrity": "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1505,6 +1575,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -1520,6 +1591,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", + "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -1536,6 +1608,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", @@ -1552,6 +1625,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -1567,6 +1641,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" @@ -1582,6 +1657,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1596,6 +1672,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", + "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1611,6 +1688,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-create-class-features-plugin": "^7.25.9", @@ -1627,6 +1705,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -1642,6 +1721,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz", "integrity": "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1656,6 +1736,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-module-imports": "^7.25.9", @@ -1674,6 +1755,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz", "integrity": "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==", + "license": "MIT", "dependencies": { "@babel/plugin-transform-react-jsx": "^7.25.9" }, @@ -1688,6 +1770,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1702,6 +1785,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1716,6 +1800,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.9.tgz", "integrity": "sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==", + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1731,6 +1816,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", @@ -1747,6 +1833,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", @@ -1763,6 +1850,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -1775,14 +1863,15 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.9.tgz", - "integrity": "sha512-nZp7GlEl+yULJrClz0SwHPqir3lc0zsPrDHQUcxGspSL7AKrexNSEfTbfqnDNJUO13bgKyfuOLMF8Xqtu8j3YQ==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.26.10.tgz", + "integrity": "sha512-NWaL2qG6HRpONTnj4JvDU6th4jYeZOJgu3QhmFTCihib0ermtOJqktA5BduGm3suhhVe9EMP9c9+mfJ/I9slqw==", + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-corejs3": "^0.11.0", "babel-plugin-polyfill-regenerator": "^0.6.1", "semver": "^6.3.1" }, @@ -1797,6 +1886,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1811,6 +1901,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" @@ -1826,6 +1917,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1837,11 +1929,12 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", - "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.26.8.tgz", + "integrity": "sha512-OmGDL5/J0CJPJZTHZbi2XpO0tyT2Ia7fzpW5GURwdtp2X3fMmN8au/ej6peC/T33/+CRiIpA8Krse8hFGVmT5Q==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1851,12 +1944,13 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", - "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz", + "integrity": "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw==", + "license": "MIT", "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1866,13 +1960,14 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.9.tgz", - "integrity": "sha512-7PbZQZP50tzv2KGGnhh82GSyMB01yKY9scIjf1a+GfZCtInOWqUH5+1EBU4t9fyR5Oykkkc9vFTs4OHrhHXljQ==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.26.8.tgz", + "integrity": "sha512-bME5J9AC8ChwA7aEPJ6zym3w7aObZULHhbNLU0bKUhKsAkylkzUdq+0kdymh9rzi8nlNFl2bmldFBCKNJBUpuw==", + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", "@babel/plugin-syntax-typescript": "^7.25.9" }, @@ -1887,6 +1982,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -1902,6 +1998,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", @@ -1918,6 +2015,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1933,6 +2031,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", @@ -1946,14 +2045,15 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", - "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.9.tgz", + "integrity": "sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ==", + "license": "MIT", "peer": true, "dependencies": { - "@babel/compat-data": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/compat-data": "^7.26.8", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", @@ -1965,9 +2065,9 @@ "@babel/plugin-syntax-import-attributes": "^7.26.0", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.25.9", - "@babel/plugin-transform-async-generator-functions": "^7.25.9", + "@babel/plugin-transform-async-generator-functions": "^7.26.8", "@babel/plugin-transform-async-to-generator": "^7.25.9", - "@babel/plugin-transform-block-scoped-functions": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.26.5", "@babel/plugin-transform-block-scoping": "^7.25.9", "@babel/plugin-transform-class-properties": "^7.25.9", "@babel/plugin-transform-class-static-block": "^7.26.0", @@ -1978,21 +2078,21 @@ "@babel/plugin-transform-duplicate-keys": "^7.25.9", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-dynamic-import": "^7.25.9", - "@babel/plugin-transform-exponentiation-operator": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.26.3", "@babel/plugin-transform-export-namespace-from": "^7.25.9", - "@babel/plugin-transform-for-of": "^7.25.9", + "@babel/plugin-transform-for-of": "^7.26.9", "@babel/plugin-transform-function-name": "^7.25.9", "@babel/plugin-transform-json-strings": "^7.25.9", "@babel/plugin-transform-literals": "^7.25.9", "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", "@babel/plugin-transform-member-expression-literals": "^7.25.9", "@babel/plugin-transform-modules-amd": "^7.25.9", - "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.26.3", "@babel/plugin-transform-modules-systemjs": "^7.25.9", "@babel/plugin-transform-modules-umd": "^7.25.9", "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-new-target": "^7.25.9", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", "@babel/plugin-transform-numeric-separator": "^7.25.9", "@babel/plugin-transform-object-rest-spread": "^7.25.9", "@babel/plugin-transform-object-super": "^7.25.9", @@ -2008,17 +2108,17 @@ "@babel/plugin-transform-shorthand-properties": "^7.25.9", "@babel/plugin-transform-spread": "^7.25.9", "@babel/plugin-transform-sticky-regex": "^7.25.9", - "@babel/plugin-transform-template-literals": "^7.25.9", - "@babel/plugin-transform-typeof-symbol": "^7.25.9", + "@babel/plugin-transform-template-literals": "^7.26.8", + "@babel/plugin-transform-typeof-symbol": "^7.26.7", "@babel/plugin-transform-unicode-escapes": "^7.25.9", "@babel/plugin-transform-unicode-property-regex": "^7.25.9", "@babel/plugin-transform-unicode-regex": "^7.25.9", "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-corejs3": "^0.11.0", "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.38.1", + "core-js-compat": "^3.40.0", "semver": "^6.3.1" }, "engines": { @@ -2032,6 +2132,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.25.9.tgz", "integrity": "sha512-EASHsAhE+SSlEzJ4bzfusnXSHiU+JfAYzj+jbw2vgQKgq5HrUr8qs+vgtiEL5dOH6sEweI+PNt2D7AqrDSHyqQ==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-validator-option": "^7.25.9", @@ -2048,6 +2149,7 @@ "version": "0.1.6-no-external-plugins", "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", @@ -2059,9 +2161,10 @@ } }, "node_modules/@babel/preset-react": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.25.9.tgz", - "integrity": "sha512-D3to0uSPiWE7rBrdIICCd0tJSIGpLaaGptna2+w7Pft5xMqLpA1sz99DK5TZ1TjGbdQ/VI1eCSZ06dv3lT4JOw==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.26.3.tgz", + "integrity": "sha512-Nl03d6T9ky516DGK2YMxrTqvnpUW63TnJMOMonj+Zae0JiPC5BC9xPMSL6L8fiSpA5vP88qfygavVQvnLp+6Cw==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-validator-option": "^7.25.9", @@ -2081,6 +2184,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz", "integrity": "sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-validator-option": "^7.25.9", @@ -2099,6 +2203,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.25.9.tgz", "integrity": "sha512-8D43jXtGsYmEeDvm4MWHYUpWf8iiXgWYx3fW7E7Wb7Oe6FWqJPl5K6TuFW0dOwNZzEE5rjlaSJYH9JjrUKJszA==", + "license": "MIT", "dependencies": { "clone-deep": "^4.0.1", "find-cache-dir": "^2.0.0", @@ -2140,15 +2245,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.10.tgz", + "integrity": "sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.10", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2173,6 +2279,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/@birdwingo/react-native-reanimated-graph/-/react-native-reanimated-graph-1.1.4.tgz", "integrity": "sha512-kUZRnIFwfJbOMEDgaRi7ZcfXDPXzEvuU1/tSuhHHkDO5NwZcfdjz+BaNWndhZVOs0T65QRJS3AvAeWU4jw9cvA==", + "license": "MIT", "peerDependencies": { "react": ">=18.0.0", "react-native": ">=0.70.0", @@ -2185,6 +2292,7 @@ "version": "0.4.5", "resolved": "https://registry.npmjs.org/@candlefinance/app-icon/-/app-icon-0.4.5.tgz", "integrity": "sha512-8y4e+AwxX14EmBq9IGenRYIchqJ4GAyLAw4gHk+ceRnMeDuSTo7CZMMd/eJDTH6Ae2rsc/qU+90tdOwozlb0FQ==", + "license": "MIT", "engines": { "node": ">= 16.0.0" }, @@ -2196,12 +2304,14 @@ "node_modules/@dominicstop/ts-event-emitter": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@dominicstop/ts-event-emitter/-/ts-event-emitter-1.1.0.tgz", - "integrity": "sha512-CcxmJIvUb1vsFheuGGVSQf4KdPZC44XolpUT34+vlal+LyQoBUOn31pjFET5M9ctOxEpt8xa0M3/2M7uUiAoJw==" + "integrity": "sha512-CcxmJIvUb1vsFheuGGVSQf4KdPZC44XolpUT34+vlal+LyQoBUOn31pjFET5M9ctOxEpt8xa0M3/2M7uUiAoJw==", + "license": "MIT" }, "node_modules/@egjs/hammerjs": { "version": "2.0.17", "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==", + "license": "MIT", "dependencies": { "@types/hammerjs": "^2.0.36" }, @@ -2210,10 +2320,11 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", - "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz", + "integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==", "dev": true, + "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" }, @@ -2232,6 +2343,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -2244,6 +2356,7 @@ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -2253,6 +2366,7 @@ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -2276,6 +2390,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2286,6 +2401,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -2298,6 +2414,7 @@ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -2315,6 +2432,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, @@ -2330,6 +2448,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2342,6 +2461,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -2354,6 +2474,7 @@ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, + "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -2440,7 +2561,7 @@ "resolve-from": "^5.0.0", "resolve.exports": "^2.0.2", "semver": "^7.6.0", - "send": "^0.19.0", + "send": "^0.18.0", "slugify": "^1.3.4", "source-map-support": "~0.5.21", "stacktrace-parser": "^0.1.10", @@ -2478,14 +2599,15 @@ } }, "node_modules/@expo/cli/node_modules/form-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.2.tgz", - "integrity": "sha512-sJe+TQb2vIaIyO783qN6BlMYWMw3WBOHA1Ay2qxsnjuafEOQFJ2JakedOQirT6D5XPRxDvS7AHYyem9fTpb4LQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.3.tgz", + "integrity": "sha512-q5YBMeWy6E2Un0nMGWMgI65MAKtaylxfNJGJxpGh45YDciZB4epbWpaAfImil6CPAPTYB4sh0URQNDRIZG5F2w==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" @@ -2529,9 +2651,9 @@ } }, "node_modules/@expo/cli/node_modules/resolve": { - "version": "1.22.9", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.9.tgz", - "integrity": "sha512-QxrmX1DzraFIi9PxdG5VkRfRwIgjwyud+z/iBwfRRrVmHc+P9Q7u2lSSpQ6bjr2gy5lrqIiU9vb6iAeGf2400A==", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "license": "MIT", "dependencies": { "is-core-module": "^2.16.0", @@ -2541,14 +2663,17 @@ "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/@expo/cli/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -2571,6 +2696,7 @@ "version": "9.0.4", "resolved": "https://registry.npmjs.org/@expo/config/-/config-9.0.4.tgz", "integrity": "sha512-g5ns5u1JSKudHYhjo1zaSfkJ/iZIcWmUmIQptMJZ6ag1C0ShL2sj8qdfU8MmAMuKLOgcIfSaiWlQnm4X3VJVkg==", + "license": "MIT", "dependencies": { "@babel/code-frame": "~7.10.4", "@expo/config-plugins": "~8.0.8", @@ -2612,6 +2738,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2622,6 +2749,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2641,6 +2769,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2649,9 +2778,10 @@ } }, "node_modules/@expo/config-plugins/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -2662,12 +2792,14 @@ "node_modules/@expo/config-types": { "version": "51.0.3", "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-51.0.3.tgz", - "integrity": "sha512-hMfuq++b8VySb+m9uNNrlpbvGxYc8OcFCUX9yTmi9tlx6A4k8SDabWFBgmnr4ao3wEArvWrtUQIfQCVtPRdpKA==" + "integrity": "sha512-hMfuq++b8VySb+m9uNNrlpbvGxYc8OcFCUX9yTmi9tlx6A4k8SDabWFBgmnr4ao3wEArvWrtUQIfQCVtPRdpKA==", + "license": "MIT" }, "node_modules/@expo/config/node_modules/@babel/code-frame": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "license": "MIT", "dependencies": { "@babel/highlight": "^7.10.4" } @@ -2676,6 +2808,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2686,6 +2819,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2705,6 +2839,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2713,9 +2848,10 @@ } }, "node_modules/@expo/config/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -2776,6 +2912,7 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/@expo/env/-/env-0.3.0.tgz", "integrity": "sha512-OtB9XVHWaXidLbHvrVDeeXa09yvTl3+IQN884sO6PhIi2/StXfgSH/9zC7IvzrDB8kW3EBJ1PPLuCUJ2hxAT7Q==", + "license": "MIT", "dependencies": { "chalk": "^4.0.0", "debug": "^4.3.4", @@ -2788,6 +2925,7 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.5.1.tgz", "integrity": "sha512-U/GsFfFox88lXULmFJ9Shfl2aQGcwoKPF7fawSCLixIKtMCpsI+1r0h+5i0nQnmt9tHuzXZDL8+Dg1z6OhkI9A==", + "license": "MIT", "dependencies": { "@expo/spawn-async": "^1.7.2", "chalk": "^4.0.0", @@ -2805,6 +2943,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", "integrity": "sha512-GsVpkFPlycH7/fRR7Dhcmnoii54gV1nz7y4CWyeFS14N+JVBBhY+r8amRHE4BwSYal7BPTDp8isvAlCxyFt3Hg==", + "license": "MIT", "engines": { "node": ">=4" } @@ -2813,6 +2952,7 @@ "version": "9.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.0.tgz", "integrity": "sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g==", + "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -2827,6 +2967,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -2838,14 +2979,16 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", "engines": { "node": ">= 10.0.0" } }, "node_modules/@expo/image-utils/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -2857,6 +3000,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", "integrity": "sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ==", + "license": "MIT", "engines": { "node": ">=4" } @@ -2865,6 +3009,7 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.3.0.tgz", "integrity": "sha512-WrH/pui8YCwmeiAoxV+lpRH9HpRtgBhSR2ViBPgpGb/wnYDzp21R4MN45fsCGvLROvY67o3byhJRYRONJyImVQ==", + "license": "MIT", "dependencies": { "temp-dir": "^1.0.0", "type-fest": "^0.3.1", @@ -2878,6 +3023,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=6" } @@ -2886,6 +3032,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", "integrity": "sha512-ODgiYu03y5g76A1I9Gt0/chLCzQjvzDy7DsZGsLOE/1MrF6wriEskSncj1+/C58Xk/kPZDppSctDybCwOSaGAg==", + "license": "MIT", "dependencies": { "crypto-random-string": "^1.0.0" }, @@ -2897,6 +3044,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -2905,6 +3053,7 @@ "version": "8.3.3", "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-8.3.3.tgz", "integrity": "sha512-eZ5dld9AD0PrVRiIWpRkm5aIoWBw3kAyd8VkuWEy92sEthBKDDDHAnK2a0dw0Eil6j7rK7lS/Qaq/Zzngv2h5A==", + "license": "MIT", "dependencies": { "@babel/code-frame": "~7.10.4", "json5": "^2.2.2", @@ -2915,6 +3064,7 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "license": "MIT", "dependencies": { "@babel/highlight": "^7.10.4" } @@ -2982,9 +3132,9 @@ } }, "node_modules/@expo/osascript": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.1.4.tgz", - "integrity": "sha512-LcPjxJ5FOFpqPORm+5MRLV0CuYWMthJYV6eerF+lQVXKlvgSn3EOqaHC3Vf3H+vmB0f6G4kdvvFtg40vG4bIhA==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.1.6.tgz", + "integrity": "sha512-SbMp4BUwDAKiFF4zZEJf32rRYMeNnLK9u4FaPo0lQRer60F+SKd20NTSys0wgssiVeQyQz2OhGLRx3cxYowAGw==", "license": "MIT", "dependencies": { "@expo/spawn-async": "^1.7.2", @@ -2995,12 +3145,12 @@ } }, "node_modules/@expo/package-manager": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-1.6.1.tgz", - "integrity": "sha512-4rT46wP/94Ll+CWXtFKok1Lbo9XncSUtErFOo/9/3FVughGbIfdG4SKZOAWIpr9wxwEfkyhHfAP9q71ONlWODw==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-1.7.2.tgz", + "integrity": "sha512-wT/qh9ebNjl6xr00bYkSh93b6E/78J3JPlT6WzGbxbsnv5FIZKB/nr522oWqVe1E+ML7BpXs8WugErWDN9kOFg==", "license": "MIT", "dependencies": { - "@expo/json-file": "^9.0.0", + "@expo/json-file": "^9.0.2", "@expo/spawn-async": "^1.7.2", "ansi-regex": "^5.0.0", "chalk": "^4.0.0", @@ -3024,9 +3174,9 @@ } }, "node_modules/@expo/package-manager/node_modules/@expo/json-file": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-9.0.0.tgz", - "integrity": "sha512-M+55xFVrFzDcgMDf+52lPDLjKB5xwRfStWlv/b/Vu2OLgxGZLWpxoPYjlRoHqxjPbCQIi2ZCbobK+0KuNhsELg==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-9.0.2.tgz", + "integrity": "sha512-yAznIUrybOIWp3Uax7yRflB0xsEpvIwIEqIjao9SGi2Gaa+N0OamWfe0fnXBSWF+2zzF4VvqwT4W5zwelchfgw==", "license": "MIT", "dependencies": { "@babel/code-frame": "~7.10.4", @@ -3090,9 +3240,9 @@ } }, "node_modules/@expo/package-manager/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -3105,6 +3255,7 @@ "version": "9.1.1", "resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.1.1.tgz", "integrity": "sha512-es33J1g2HjMpyAhz8lOR+ICmXXAqTuKbuXuUWLhOLew20oN9oUCgCJx615U/v7aioZg7IX5lIh9x34vwneu4pA==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "license": "MIT" }, "node_modules/@expo/package-manager/node_modules/validate-npm-package-name": { @@ -3120,6 +3271,7 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.1.3.tgz", "integrity": "sha512-GW/7hVlAylYg1tUrEASclw1MMk9FP4ZwyFAY/SUTJIhPDQHtfOlXREyWV3hhrHdX/K+pS73GNgdfT6E/e+kBbg==", + "license": "MIT", "dependencies": { "@xmldom/xmldom": "~0.7.7", "base64-js": "^1.2.3", @@ -3176,9 +3328,9 @@ } }, "node_modules/@expo/prebuild-config/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -3217,12 +3369,14 @@ "node_modules/@expo/sdk-runtime-versions": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@expo/sdk-runtime-versions/-/sdk-runtime-versions-1.0.0.tgz", - "integrity": "sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==" + "integrity": "sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==", + "license": "MIT" }, "node_modules/@expo/spawn-async": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/@expo/spawn-async/-/spawn-async-1.7.2.tgz", "integrity": "sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew==", + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3" }, @@ -3234,6 +3388,7 @@ "version": "14.0.4", "resolved": "https://registry.npmjs.org/@expo/vector-icons/-/vector-icons-14.0.4.tgz", "integrity": "sha512-+yKshcbpDfbV4zoXOgHxCwh7lkE9VVTT5T03OUlBsqfze1PLy6Hi4jp1vSb1GVbY6eskvMIivGVc9SKzIv0oEQ==", + "license": "MIT", "dependencies": { "prop-types": "^15.8.1" } @@ -3274,12 +3429,14 @@ "node_modules/@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "license": "BSD-3-Clause" }, "node_modules/@hapi/topo": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "license": "BSD-3-Clause", "dependencies": { "@hapi/hoek": "^9.0.0" } @@ -3288,6 +3445,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/@howljs/calendar-kit/-/calendar-kit-2.2.1.tgz", "integrity": "sha512-3ZgPS46/14pfZ/gBbMsdrSpCy/jTfVpgeHcvOHW7itflUnFBOSCRhGc4cWRd27vD9gIAu63PdWngQ34p/5EwFw==", + "license": "MIT", "dependencies": { "lodash.debounce": "^4.0.8", "lodash.isequal": "^4.5.0", @@ -3318,6 +3476,7 @@ "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "deprecated": "Use @eslint/config-array instead", "dev": true, + "license": "Apache-2.0", "dependencies": { "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", @@ -3332,6 +3491,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3342,6 +3502,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -3354,6 +3515,7 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -3367,7 +3529,8 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "deprecated": "Use @eslint/object-schema instead", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@isaacs/cliui": { "version": "8.0.2", @@ -3469,6 +3632,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz", "integrity": "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==", + "license": "ISC", "engines": { "node": ">=12" } @@ -3477,6 +3641,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz", "integrity": "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==", + "license": "MIT", "dependencies": { "@jest/types": "^29.6.3" }, @@ -3488,6 +3653,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -3504,6 +3670,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -3512,6 +3679,7 @@ "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -3520,6 +3688,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "license": "MIT", "dependencies": { "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", @@ -3534,6 +3703,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -3550,6 +3720,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -3558,6 +3729,7 @@ "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -3566,6 +3738,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", @@ -3582,6 +3755,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -3598,6 +3772,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -3606,6 +3781,7 @@ "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -3614,6 +3790,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "license": "MIT", "dependencies": { "@sinclair/typebox": "^0.27.8" }, @@ -3625,6 +3802,7 @@ "version": "24.9.0", "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^1.1.1", @@ -3635,9 +3813,10 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -3651,6 +3830,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", "engines": { "node": ">=6.0.0" } @@ -3659,6 +3839,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "license": "MIT", "engines": { "node": ">=6.0.0" } @@ -3667,6 +3848,7 @@ "version": "0.3.6", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" @@ -3675,12 +3857,14 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -3690,16 +3874,18 @@ "version": "1.0.0-11713907881.1", "resolved": "https://registry.npmjs.org/@literate.ink/utilities/-/utilities-1.0.0-11713907881.1.tgz", "integrity": "sha512-+RdZqu5dor5YQfL0CRSGPb9Gl6qCsXusziJZt84KxmJsL55ElPSc35AR4CNITzsYg7rAXVnoUCf8fXpaSeWJsg==", + "license": "MIT", "dependencies": { "set-cookie-parser": "^2.7.0" } }, "node_modules/@noble/curves": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.6.0.tgz", - "integrity": "sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz", + "integrity": "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==", + "license": "MIT", "dependencies": { - "@noble/hashes": "1.5.0" + "@noble/hashes": "1.7.1" }, "engines": { "node": "^14.21.3 || >=16" @@ -3709,9 +3895,10 @@ } }, "node_modules/@noble/hashes": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.5.0.tgz", - "integrity": "sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", + "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", + "license": "MIT", "engines": { "node": "^14.21.3 || >=16" }, @@ -3723,6 +3910,7 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -3735,6 +3923,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", "engines": { "node": ">= 8" } @@ -3743,6 +3932,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -3755,6 +3945,7 @@ "version": "7.9.0", "resolved": "https://registry.npmjs.org/@notifee/react-native/-/react-native-7.9.0.tgz", "integrity": "sha512-r4iFcAkvfFV/iNwGF50y9uAYS8x0x6+t9gmBsZczWxFHzBvg5nBjwFjshnuC24+oNnlNWIbB03heNmFjrFArJQ==", + "license": "Apache-2.0", "peerDependencies": { "react-native": "*" } @@ -3772,9 +3963,9 @@ } }, "node_modules/@npmcli/fs/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -3784,47 +3975,50 @@ } }, "node_modules/@peculiar/asn1-ecc": { - "version": "2.3.14", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.3.14.tgz", - "integrity": "sha512-zWPyI7QZto6rnLv6zPniTqbGaLh6zBpJyI46r1yS/bVHJXT2amdMHCRRnbV5yst2H8+ppXG6uXu/M6lKakiQ8w==", + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.3.15.tgz", + "integrity": "sha512-/HtR91dvgog7z/WhCVdxZJ/jitJuIu8iTqiyWVgRE9Ac5imt2sT/E4obqIVGKQw7PIy+X6i8lVBoT6wC73XUgA==", + "license": "MIT", "dependencies": { - "@peculiar/asn1-schema": "^2.3.13", - "@peculiar/asn1-x509": "^2.3.13", + "@peculiar/asn1-schema": "^2.3.15", + "@peculiar/asn1-x509": "^2.3.15", "asn1js": "^3.0.5", - "tslib": "^2.6.2" + "tslib": "^2.8.1" } }, "node_modules/@peculiar/asn1-pkcs8": { - "version": "2.3.13", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.3.13.tgz", - "integrity": "sha512-VP3PQzbeSSjPjKET5K37pxyf2qCdM0dz3DJ56ZCsol3FqAXGekb4sDcpoL9uTLGxAh975WcdvUms9UcdZTuGyQ==", + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.3.15.tgz", + "integrity": "sha512-/PuQj2BIAw1/v76DV1LUOA6YOqh/UvptKLJHtec/DQwruXOCFlUo7k6llegn8N5BTeZTWMwz5EXruBw0Q10TMg==", + "license": "MIT", "dependencies": { - "@peculiar/asn1-schema": "^2.3.13", - "@peculiar/asn1-x509": "^2.3.13", + "@peculiar/asn1-schema": "^2.3.15", + "@peculiar/asn1-x509": "^2.3.15", "asn1js": "^3.0.5", - "tslib": "^2.6.2" + "tslib": "^2.8.1" } }, "node_modules/@peculiar/asn1-schema": { - "version": "2.3.13", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.13.tgz", - "integrity": "sha512-3Xq3a01WkHRZL8X04Zsfg//mGaA21xlL4tlVn4v2xGT0JStiztATRkMwa5b+f/HXmY2smsiLXYK46Gwgzvfg3g==", + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.15.tgz", + "integrity": "sha512-QPeD8UA8axQREpgR5UTAfu2mqQmm97oUqahDtNdBcfj3qAnoXzFdQW+aNf/tD2WVXF8Fhmftxoj0eMIT++gX2w==", + "license": "MIT", "dependencies": { "asn1js": "^3.0.5", - "pvtsutils": "^1.3.5", - "tslib": "^2.6.2" + "pvtsutils": "^1.3.6", + "tslib": "^2.8.1" } }, "node_modules/@peculiar/asn1-x509": { - "version": "2.3.13", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.3.13.tgz", - "integrity": "sha512-PfeLQl2skXmxX2/AFFCVaWU8U6FKW1Db43mgBhShCOFS1bVxqtvusq1hVjfuEcuSQGedrLdCSvTgabluwN/M9A==", + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.3.15.tgz", + "integrity": "sha512-0dK5xqTqSLaxv1FHXIcd4Q/BZNuopg+u1l23hT9rOmQ1g4dNtw0g/RnEi+TboB0gOwGtrWn269v27cMgchFIIg==", + "license": "MIT", "dependencies": { - "@peculiar/asn1-schema": "^2.3.13", + "@peculiar/asn1-schema": "^2.3.15", "asn1js": "^3.0.5", - "ipaddr.js": "^2.1.0", - "pvtsutils": "^1.3.5", - "tslib": "^2.6.2" + "pvtsutils": "^1.3.6", + "tslib": "^2.8.1" } }, "node_modules/@pkgjs/parseargs": { @@ -3841,6 +4035,7 @@ "version": "1.23.1", "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.23.1.tgz", "integrity": "sha512-Qd2kQ3yi6Y3+AcUlrHxSLlnBvpdCEMVGFlVBneVOjaFaPU61g1huc38g339ysXspwY1QZA2aNhrk/KlHGO+ewA==", + "license": "MIT", "dependencies": { "merge-options": "^3.0.4" }, @@ -3852,6 +4047,7 @@ "version": "13.6.9", "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-13.6.9.tgz", "integrity": "sha512-hFJL4cgLPxncJJd/epQ4dHnMg5Jy/7Q56jFvA3MHViuKpzzfTCJCB+pGY54maZbtym53UJON9WTGpM3S81UfjQ==", + "license": "MIT", "dependencies": { "@react-native-community/cli-clean": "13.6.9", "@react-native-community/cli-config": "13.6.9", @@ -3882,6 +4078,7 @@ "version": "13.6.9", "resolved": "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-13.6.9.tgz", "integrity": "sha512-7Dj5+4p9JggxuVNOjPbduZBAP1SUgNhLKVw5noBUzT/3ZpUZkDM+RCSwyoyg8xKWoE4OrdUAXwAFlMcFDPKykA==", + "license": "MIT", "dependencies": { "@react-native-community/cli-tools": "13.6.9", "chalk": "^4.1.2", @@ -3893,6 +4090,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -3915,6 +4113,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -3926,6 +4125,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", "engines": { "node": ">=8" }, @@ -3937,6 +4137,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", "engines": { "node": ">=6" } @@ -3945,6 +4146,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", "dependencies": { "path-key": "^3.0.0" }, @@ -3956,6 +4158,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -3970,6 +4173,7 @@ "version": "13.6.9", "resolved": "https://registry.npmjs.org/@react-native-community/cli-config/-/cli-config-13.6.9.tgz", "integrity": "sha512-rFfVBcNojcMm+KKHE/xqpqXg8HoKl4EC7bFHUrahMJ+y/tZll55+oX/PGG37rzB8QzP2UbMQ19DYQKC1G7kXeg==", + "license": "MIT", "dependencies": { "@react-native-community/cli-tools": "13.6.9", "chalk": "^4.1.2", @@ -3983,6 +4187,7 @@ "version": "13.6.9", "resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-13.6.9.tgz", "integrity": "sha512-TkN7IdFmGPPvTpAo3nCAH9uwGCPxWBEAwpqEZDrq0NWllI7Tdie8vDpGdrcuCcKalmhq6OYnkXzeBah7O1Ztpw==", + "license": "MIT", "dependencies": { "serve-static": "^1.13.1" } @@ -3991,6 +4196,7 @@ "version": "13.6.9", "resolved": "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-13.6.9.tgz", "integrity": "sha512-5quFaLdWFQB+677GXh5dGU9I5eg2z6Vg4jOX9vKnc9IffwyIFAyJfCZHrxLSRPDGNXD7biDQUdoezXYGwb6P/A==", + "license": "MIT", "dependencies": { "@react-native-community/cli-config": "13.6.9", "@react-native-community/cli-platform-android": "13.6.9", @@ -4015,6 +4221,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "license": "MIT", "dependencies": { "restore-cursor": "^3.1.0" }, @@ -4026,6 +4233,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -4048,6 +4256,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -4059,6 +4268,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", "engines": { "node": ">=8" }, @@ -4070,6 +4280,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "license": "MIT", "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -4085,6 +4296,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", "engines": { "node": ">=6" } @@ -4093,6 +4305,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", "dependencies": { "path-key": "^3.0.0" }, @@ -4104,6 +4317,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -4118,6 +4332,7 @@ "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "license": "MIT", "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", @@ -4140,6 +4355,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -4151,6 +4367,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "license": "MIT", "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -4160,9 +4377,10 @@ } }, "node_modules/@react-native-community/cli-doctor/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -4174,6 +4392,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "license": "MIT", "dependencies": { "ansi-regex": "^4.1.0" }, @@ -4185,6 +4404,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "license": "MIT", "engines": { "node": ">=6" } @@ -4193,6 +4413,7 @@ "version": "13.6.9", "resolved": "https://registry.npmjs.org/@react-native-community/cli-hermes/-/cli-hermes-13.6.9.tgz", "integrity": "sha512-GvwiwgvFw4Ws+krg2+gYj8sR3g05evmNjAHkKIKMkDTJjZ8EdyxbkifRUs1ZCq3TMZy2oeblZBXCJVOH4W7ZbA==", + "license": "MIT", "dependencies": { "@react-native-community/cli-platform-android": "13.6.9", "@react-native-community/cli-tools": "13.6.9", @@ -4204,6 +4425,7 @@ "version": "13.6.9", "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-13.6.9.tgz", "integrity": "sha512-9KsYGdr08QhdvT3Ht7e8phQB3gDX9Fs427NJe0xnoBh+PDPTI2BD5ks5ttsH8CzEw8/P6H8tJCHq6hf2nxd9cw==", + "license": "MIT", "dependencies": { "@react-native-community/cli-tools": "13.6.9", "chalk": "^4.1.2", @@ -4217,6 +4439,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -4239,6 +4462,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -4250,6 +4474,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", "engines": { "node": ">=8" }, @@ -4261,6 +4486,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", "engines": { "node": ">=6" } @@ -4269,6 +4495,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", "dependencies": { "path-key": "^3.0.0" }, @@ -4280,6 +4507,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -4294,6 +4522,7 @@ "version": "13.6.9", "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-apple/-/cli-platform-apple-13.6.9.tgz", "integrity": "sha512-KoeIHfhxMhKXZPXmhQdl6EE+jGKWwoO9jUVWgBvibpVmsNjo7woaG/tfJMEWfWF3najX1EkQAoJWpCDBMYWtlA==", + "license": "MIT", "dependencies": { "@react-native-community/cli-tools": "13.6.9", "chalk": "^4.1.2", @@ -4307,6 +4536,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "license": "MIT", "dependencies": { "restore-cursor": "^3.1.0" }, @@ -4318,6 +4548,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -4340,6 +4571,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -4351,6 +4583,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", "engines": { "node": ">=8" }, @@ -4362,6 +4595,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "license": "MIT", "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -4377,6 +4611,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", "engines": { "node": ">=6" } @@ -4385,6 +4620,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", "dependencies": { "path-key": "^3.0.0" }, @@ -4396,6 +4632,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -4410,6 +4647,7 @@ "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "license": "MIT", "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", @@ -4432,6 +4670,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "license": "MIT", "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -4444,6 +4683,7 @@ "version": "13.6.9", "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-13.6.9.tgz", "integrity": "sha512-CiUcHlGs8vE0CAB4oi1f+dzniqfGuhWPNrDvae2nm8dewlahTBwIcK5CawyGezjcJoeQhjBflh9vloska+nlnw==", + "license": "MIT", "dependencies": { "@react-native-community/cli-platform-apple": "13.6.9" } @@ -4452,6 +4692,7 @@ "version": "13.6.9", "resolved": "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-13.6.9.tgz", "integrity": "sha512-W8FSlCPWymO+tlQfM3E0JmM8Oei5HZsIk5S0COOl0MRi8h0NmHI4WSTF2GCfbFZkcr2VI/fRsocoN8Au4EZAug==", + "license": "MIT", "dependencies": { "@react-native-community/cli-debugger-ui": "13.6.9", "@react-native-community/cli-tools": "13.6.9", @@ -4468,6 +4709,7 @@ "version": "26.6.2", "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", @@ -4483,6 +4725,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -4491,6 +4734,7 @@ "version": "15.0.19", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.19.tgz", "integrity": "sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -4499,6 +4743,7 @@ "version": "26.6.2", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "license": "MIT", "dependencies": { "@jest/types": "^26.6.2", "ansi-regex": "^5.0.0", @@ -4512,12 +4757,14 @@ "node_modules/@react-native-community/cli-server-api/node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "license": "MIT" }, "node_modules/@react-native-community/cli-server-api/node_modules/ws": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "license": "MIT", "dependencies": { "async-limiter": "~1.0.0" } @@ -4526,6 +4773,7 @@ "version": "13.6.9", "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-13.6.9.tgz", "integrity": "sha512-OXaSjoN0mZVw3nrAwcY1PC0uMfyTd9fz7Cy06dh+EJc+h0wikABsVRzV8cIOPrVV+PPEEXE0DBrH20T2puZzgQ==", + "license": "MIT", "dependencies": { "appdirsjs": "^1.2.4", "chalk": "^4.1.2", @@ -4544,6 +4792,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "license": "MIT", "dependencies": { "restore-cursor": "^3.1.0" }, @@ -4555,6 +4804,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -4577,6 +4827,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -4588,6 +4839,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", "engines": { "node": ">=8" }, @@ -4599,6 +4851,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "license": "MIT", "engines": { "node": ">=4" } @@ -4607,6 +4860,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "license": "MIT", "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -4622,6 +4876,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", "engines": { "node": ">=6" } @@ -4630,6 +4885,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", "dependencies": { "path-key": "^3.0.0" }, @@ -4641,6 +4897,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -4655,6 +4912,7 @@ "version": "6.4.0", "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "license": "MIT", "dependencies": { "is-wsl": "^1.1.0" }, @@ -4666,6 +4924,7 @@ "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "license": "MIT", "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", @@ -4688,6 +4947,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "license": "MIT", "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -4697,9 +4957,10 @@ } }, "node_modules/@react-native-community/cli-tools/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -4710,12 +4971,15 @@ "node_modules/@react-native-community/cli-tools/node_modules/sudo-prompt": { "version": "9.2.1", "resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz", - "integrity": "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==" + "integrity": "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT" }, "node_modules/@react-native-community/cli-types": { "version": "13.6.9", "resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-13.6.9.tgz", "integrity": "sha512-RLxDppvRxXfs3hxceW/mShi+6o5yS+kFPnPqZTaMKKR5aSg7LwDpLQW4K2D22irEG8e6RKDkZUeH9aL3vO2O0w==", + "license": "MIT", "dependencies": { "joi": "^17.2.1" } @@ -4724,6 +4988,7 @@ "version": "9.5.0", "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "license": "MIT", "engines": { "node": "^12.20.0 || >=14" } @@ -4732,6 +4997,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -4754,6 +5020,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -4766,6 +5033,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -4777,6 +5045,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", "engines": { "node": ">=8" }, @@ -4788,6 +5057,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -4799,6 +5069,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", "engines": { "node": ">=6" } @@ -4807,6 +5078,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", "dependencies": { "path-key": "^3.0.0" }, @@ -4818,6 +5090,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -4832,6 +5105,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -4846,6 +5120,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -4854,9 +5129,10 @@ } }, "node_modules/@react-native-community/cli/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -4868,6 +5144,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/@react-native-community/datetimepicker/-/datetimepicker-8.0.1.tgz", "integrity": "sha512-4BO0t3geMNNw9cIIm9p9FNUzwMXexdzD4pAH0AaUAycs3BS71HLrX8jHbrI7nzq/+8O7cLAXn5Gudte+YpTV8Q==", + "license": "MIT", "dependencies": { "invariant": "^2.2.4" }, @@ -4886,6 +5163,7 @@ "version": "11.3.1", "resolved": "https://registry.npmjs.org/@react-native-community/netinfo/-/netinfo-11.3.1.tgz", "integrity": "sha512-UBnJxyV0b7i9Moa97Av+HKho1ByzX0DtbJXzUQS5E3xhQs6P2D/Os0iw3ouy7joY1TVd6uIhplPbr7l1SJNaNQ==", + "license": "MIT", "peerDependencies": { "react-native": ">=0.59" } @@ -4894,6 +5172,7 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/@react-native-cookies/cookies/-/cookies-6.2.1.tgz", "integrity": "sha512-D17wCA0DXJkGJIxkL74Qs9sZ3sA+c+kCoGmXVknW7bVw/W+Vv1m/7mWTNi9DLBZSRddhzYw8SU0aJapIaM/g5w==", + "license": "MIT", "dependencies": { "invariant": "^2.2.4" }, @@ -4905,15 +5184,17 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/@react-native-masked-view/masked-view/-/masked-view-0.3.1.tgz", "integrity": "sha512-uVm8U6nwFIlUd1iDIB5cS+lDadApKR+l8k4k84d9hn+GN4lzAIJhUZ9syYX7c022MxNgAlbxoFLt0pqKoyaAGg==", + "license": "MIT", "peerDependencies": { "react": ">=16", "react-native": ">=0.57" } }, "node_modules/@react-native/assets-registry": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.74.88.tgz", - "integrity": "sha512-tOvA+ikxa0Yxk3gLWR4+Pp4Y6Se+JEs6XXabX4/jgxIDnDfhT/czFNhqH/hdk4uOT8uVJGnilvevsia2TCFMiw==", + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.74.89.tgz", + "integrity": "sha512-woHMQQ6h8+Uw7ULKbhp4XsualYyeb2yhsl3Y14D0s40Rt+qeGy74t1wwhOu/BCV13mHM3o5vRahCr0LRTUSXTQ==", + "license": "MIT", "engines": { "node": ">=18" } @@ -4922,6 +5203,7 @@ "version": "0.74.87", "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.74.87.tgz", "integrity": "sha512-+vJYpMnENFrwtgvDfUj+CtVJRJuUnzAUYT0/Pb68Sq9RfcZ5xdcCuUgyf7JO+akW2VTBoJY427wkcxU30qrWWw==", + "license": "MIT", "dependencies": { "@react-native/codegen": "0.74.87" }, @@ -4933,6 +5215,7 @@ "version": "0.74.87", "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.74.87.tgz", "integrity": "sha512-hyKpfqzN2nxZmYYJ0tQIHG99FQO0OWXp/gVggAfEUgiT+yNKas1C60LuofUsK7cd+2o9jrpqgqW4WzEDZoBlTg==", + "license": "MIT", "dependencies": { "@babel/core": "^7.20.0", "@babel/plugin-proposal-async-generator-functions": "^7.0.0", @@ -4989,6 +5272,7 @@ "version": "0.74.87", "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.74.87.tgz", "integrity": "sha512-GMSYDiD+86zLKgMMgz9z0k6FxmRn+z6cimYZKkucW4soGbxWsbjUAZoZ56sJwt2FJ3XVRgXCrnOCgXoH/Bkhcg==", + "license": "MIT", "dependencies": { "@babel/parser": "^7.20.0", "glob": "^7.1.1", @@ -5006,14 +5290,15 @@ } }, "node_modules/@react-native/community-cli-plugin": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.74.88.tgz", - "integrity": "sha512-O8zz784kksa36nBNiULHh0rYFGr4mwtBB95YvvBOEYiYnMjFkEOUe7BPKvYmX8W29MgskXcIGNrNvfre59o4xw==", + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.74.89.tgz", + "integrity": "sha512-1/LpkO7CM95btG8BVeQcn0WjlKZ4nghsUtcYIYD3TMCkRjRluYzzmpZrVm5hiam57X/n39PjdJhUoEz9CUMobw==", + "license": "MIT", "dependencies": { "@react-native-community/cli-server-api": "13.6.9", "@react-native-community/cli-tools": "13.6.9", - "@react-native/dev-middleware": "0.74.88", - "@react-native/metro-babel-transformer": "0.74.88", + "@react-native/dev-middleware": "0.74.89", + "@react-native/metro-babel-transformer": "0.74.89", "chalk": "^4.0.0", "execa": "^5.1.1", "metro": "^0.80.3", @@ -5028,20 +5313,22 @@ } }, "node_modules/@react-native/community-cli-plugin/node_modules/@react-native/debugger-frontend": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.74.88.tgz", - "integrity": "sha512-3xUR/uJza241ya0UFxxaxQiB/gkUx1gynMxhlgc6zFxz/zSrLG1/AcA6hpua2ZvmOMabpo09XOOR1Hqvf2qPEQ==", + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.74.89.tgz", + "integrity": "sha512-2kk5+tz2SaidkVBnAlpDyN3wMVRrsthtj/fxx2Jf5+P/xqbUJ2kZBzF066fAMONCFE/IHfStMfnpTxTKWOGs/Q==", + "license": "BSD-3-Clause", "engines": { "node": ">=18" } }, "node_modules/@react-native/community-cli-plugin/node_modules/@react-native/dev-middleware": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.74.88.tgz", - "integrity": "sha512-RYaQ72j9ggeGI712UlAfWtuY0rD4WllArlYtEybT0x1zmUtLgq5lgJcSkwg501yfG/g10XB69Q2MM8gCWK8NAw==", + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.74.89.tgz", + "integrity": "sha512-cv+cHfJwzY2QD27A95ETWviXWpG0poLWU5VECQkCQQdIPteJY0xY49GYK/Um0hSuM/2PgchAkty1wds9o+dbKg==", + "license": "MIT", "dependencies": { "@isaacs/ttlcache": "^1.4.1", - "@react-native/debugger-frontend": "0.74.88", + "@react-native/debugger-frontend": "0.74.89", "@rnx-kit/chromium-edge-launcher": "^1.0.0", "chrome-launcher": "^0.15.2", "connect": "^3.6.5", @@ -5062,6 +5349,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -5070,6 +5358,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -5092,6 +5381,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -5103,6 +5393,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", "engines": { "node": ">=8" }, @@ -5114,6 +5405,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", "engines": { "node": ">=6" } @@ -5121,12 +5413,14 @@ "node_modules/@react-native/community-cli-plugin/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/@react-native/community-cli-plugin/node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", "dependencies": { "path-key": "^3.0.0" }, @@ -5138,6 +5432,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -5152,6 +5447,7 @@ "version": "7.4.2", "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "license": "MIT", "dependencies": { "is-docker": "^2.0.0", "is-wsl": "^2.1.1" @@ -5167,6 +5463,7 @@ "version": "6.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "license": "MIT", "dependencies": { "async-limiter": "~1.0.0" } @@ -5245,28 +5542,31 @@ } }, "node_modules/@react-native/gradle-plugin": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.74.88.tgz", - "integrity": "sha512-cUu4gVLFTkHe0e5/IxSycRfbBhZs/5QF8AqYcoUBsZ5o+22Im9+M4DuGFv4U5Sa2NTy2VXOCpbBTepzKsdXlgw==", + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.74.89.tgz", + "integrity": "sha512-lLGmG8Ti6RyyMmULOH5M3aDD0Q1HXPdYSm/3VPqJTxtRONbnyWpl1hC/NsbgwpUHeJ/DUCY8DFZIArtaXkhExA==", + "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@react-native/js-polyfills": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.74.88.tgz", - "integrity": "sha512-6KljxfNKAz2b2uXqxagKbytb3MvUujAmfvuubKOoCLAiLbs8CYKW0OV1FqVLYUEXXw5GEDhXcVzQxxFuDlMafQ==", + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.74.89.tgz", + "integrity": "sha512-MT609lh6SnZYWZVIFTTtL37nu5UOK4Y9CpXw9K6DoUndhkejYY/dBsJ159WNuIFv2xCOtJDYiNPNFOmnRQwYvw==", + "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@react-native/metro-babel-transformer": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.74.88.tgz", - "integrity": "sha512-r7Er162iLpQce3ODQzNVS+PnjglJoHZ4l0NeaVMB4w45DIgKM4hC2vI6a/fzyFm9C6N+QY4P2i2RSkwjXVuBlQ==", + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.74.89.tgz", + "integrity": "sha512-rGKSkXLwsYRFCfBku0ZVODqMVAI6mm2yFdYUhKu5U0qIL9bffn4Ow8lHxzdyXMiEROE0jsnN31BOP19cbVI/HA==", + "license": "MIT", "dependencies": { "@babel/core": "^7.20.0", - "@react-native/babel-preset": "0.74.88", + "@react-native/babel-preset": "0.74.89", "hermes-parser": "0.19.1", "nullthrows": "^1.1.1" }, @@ -5278,20 +5578,22 @@ } }, "node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/babel-plugin-codegen": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.74.88.tgz", - "integrity": "sha512-hul4gPU09q7K0amhzhZnG3EVxeCXjP2l1x/zdgtliRRB8Nq7Za8YkM7dy84X+Vv4UC9G1nzxIbibsKeLsY1N4A==", + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.74.89.tgz", + "integrity": "sha512-E1SF2eHf2AZ0JPZZC54v6xlL2ZonMoUk0wvo3NtllvMDGn6LqlO5i4rphz3QOtX5OZa6/PhvadqLd0otmKXgIg==", + "license": "MIT", "dependencies": { - "@react-native/codegen": "0.74.88" + "@react-native/codegen": "0.74.89" }, "engines": { "node": ">=18" } }, "node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/babel-preset": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.74.88.tgz", - "integrity": "sha512-SQODiFGlyblFTvdvePUDrQ+qlSzhcOm7It/yW2CVKxw5zRUf50+Cj3DBkRFhQDqF3ri2EnWsLnJ3oNE7hqDUxg==", + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.74.89.tgz", + "integrity": "sha512-JVI3sjnQxOjqVhERX19XYEc2HPmf0nFFmhF3CAvnxo+11GrP/eOqa1q+mAE0sXueVy+/rVjwohOxKWgwoQqtIA==", + "license": "MIT", "dependencies": { "@babel/core": "^7.20.0", "@babel/plugin-proposal-async-generator-functions": "^7.0.0", @@ -5333,7 +5635,7 @@ "@babel/plugin-transform-typescript": "^7.5.0", "@babel/plugin-transform-unicode-regex": "^7.0.0", "@babel/template": "^7.0.0", - "@react-native/babel-plugin-codegen": "0.74.88", + "@react-native/babel-plugin-codegen": "0.74.89", "babel-plugin-transform-flow-enums": "^0.0.2", "react-refresh": "^0.14.0" }, @@ -5345,9 +5647,10 @@ } }, "node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/codegen": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.74.88.tgz", - "integrity": "sha512-HMk/LCrSdUof9DZFaB2bK0soKyAF6XiCg2LG7WFjEkUDXayeiB4p7IsHISJWY4bYg7cMPZ0fiZMRaBP2vXJxgg==", + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.74.89.tgz", + "integrity": "sha512-xbcpnvsAjHrnYWnuoLdr5782dlR4LfkaWPityka/gWmdRDrE69ByAC9m0/vijMXAcMoHv6DSMh5x7gm6gW/3mA==", + "license": "MIT", "dependencies": { "@babel/parser": "^7.20.0", "glob": "^7.1.1", @@ -5368,34 +5671,14 @@ "node_modules/@react-native/normalize-colors": { "version": "0.74.85", "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.85.tgz", - "integrity": "sha512-pcE4i0X7y3hsAE0SpIl7t6dUc0B0NZLd1yv7ssm4FrLhWG+CGyIq4eFDXpmPU1XHmL5PPySxTAjEMiwv6tAmOw==" - }, - "node_modules/@react-native/virtualized-lists": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.74.88.tgz", - "integrity": "sha512-nZn4X9zuyinRJoE/WcgB1e/X6b3J3QPRSsNC0LOjHzP97tvW6xvBacjbCAJAaZQwD9KaqZyK86eCi61ksr350g==", - "dependencies": { - "invariant": "^2.2.4", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/react": "^18.2.6", - "react": "*", - "react-native": "*" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } + "integrity": "sha512-pcE4i0X7y3hsAE0SpIl7t6dUc0B0NZLd1yv7ssm4FrLhWG+CGyIq4eFDXpmPU1XHmL5PPySxTAjEMiwv6tAmOw==", + "license": "MIT" }, "node_modules/@react-navigation/bottom-tabs": { "version": "6.6.1", "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-6.6.1.tgz", "integrity": "sha512-9oD4cypEBjPuaMiu9tevWGiQ4w/d6l3HNhcJ1IjXZ24xvYDSs0mqjUcdt8SWUolCvRrYc/DmNBLlT83bk0bHTw==", + "license": "MIT", "dependencies": { "@react-navigation/elements": "^1.3.31", "color": "^4.2.3", @@ -5413,6 +5696,7 @@ "version": "6.4.17", "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-6.4.17.tgz", "integrity": "sha512-Nd76EpomzChWAosGqWOYE3ItayhDzIEzzZsT7PfGcRFDgW5miHV2t4MZcq9YIK4tzxZjVVpYbIynOOQQd1e0Cg==", + "license": "MIT", "dependencies": { "@react-navigation/routers": "^6.1.9", "escape-string-regexp": "^4.0.0", @@ -5429,6 +5713,7 @@ "version": "1.3.31", "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-1.3.31.tgz", "integrity": "sha512-bUzP4Awlljx5RKEExw8WYtif8EuQni2glDaieYROKTnaxsu9kEIA515sXQgUDZU4Ob12VoL7+z70uO3qrlfXcQ==", + "license": "MIT", "peerDependencies": { "@react-navigation/native": "^6.0.0", "react": "*", @@ -5440,6 +5725,7 @@ "version": "6.1.18", "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-6.1.18.tgz", "integrity": "sha512-mIT9MiL/vMm4eirLcmw2h6h/Nm5FICtnYSdohq4vTLA2FF/6PNhByM7s8ffqoVfE5L0uAa6Xda1B7oddolUiGg==", + "license": "MIT", "dependencies": { "@react-navigation/core": "^6.4.17", "escape-string-regexp": "^4.0.0", @@ -5455,6 +5741,7 @@ "version": "6.11.0", "resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-6.11.0.tgz", "integrity": "sha512-U5EcUB9Q2NQspCFwYGGNJm0h6wBCOv7T30QjndmvlawLkNt7S7KWbpWyxS9XBHSIKF57RgWjfxuJNTgTstpXxw==", + "license": "MIT", "dependencies": { "@react-navigation/elements": "^1.3.31", "warn-once": "^0.1.0" @@ -5471,6 +5758,7 @@ "version": "6.1.9", "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-6.1.9.tgz", "integrity": "sha512-lTM8gSFHSfkJvQkxacGM6VJtBt61ip2XO54aNfswD+KMw6eeZ4oehl7m0me3CR9hnDE4+60iAZR8sAhvCiI3NA==", + "license": "MIT", "dependencies": { "nanoid": "^3.1.23" } @@ -5479,6 +5767,7 @@ "version": "6.4.1", "resolved": "https://registry.npmjs.org/@react-navigation/stack/-/stack-6.4.1.tgz", "integrity": "sha512-upMEHOKMtuMu4c9gmoPlO/JqI6mDlSqwXg1aXKOTQLXAF8H5koOLRfrmi7AkdiE9A7lDXWUAZoGuD9O88cYvDQ==", + "license": "MIT", "dependencies": { "@react-navigation/elements": "^1.3.31", "color": "^4.2.3", @@ -5497,6 +5786,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/@rnx-kit/chromium-edge-launcher/-/chromium-edge-launcher-1.0.0.tgz", "integrity": "sha512-lzD84av1ZQhYUS+jsGqJiCMaJO2dn9u+RTT9n9q6D3SaKVwWqv+7AoRKqBu19bkwyE+iFRl1ymr40QS90jVFYg==", + "license": "Apache-2.0", "dependencies": { "@types/node": "^18.0.0", "escape-string-regexp": "^4.0.0", @@ -5513,6 +5803,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" }, @@ -5521,9 +5812,10 @@ } }, "node_modules/@scure/base": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", - "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.4.tgz", + "integrity": "sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ==", + "license": "MIT", "funding": { "url": "https://paulmillr.com/funding/" } @@ -5541,6 +5833,7 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", "integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==", + "license": "MIT", "dependencies": { "domhandler": "^5.0.3", "selderee": "^0.11.0" @@ -5553,6 +5846,7 @@ "version": "4.1.5", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "license": "BSD-3-Clause", "dependencies": { "@hapi/hoek": "^9.0.0" } @@ -5560,22 +5854,26 @@ "node_modules/@sideway/formula": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "license": "BSD-3-Clause" }, "node_modules/@sideway/pinpoint": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "license": "BSD-3-Clause" }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "license": "MIT" }, "node_modules/@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "license": "BSD-3-Clause", "dependencies": { "type-detect": "4.0.8" } @@ -5584,17 +5882,19 @@ "version": "10.3.0", "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "license": "BSD-3-Clause", "dependencies": { "@sinonjs/commons": "^3.0.0" } }, "node_modules/@stylistic/eslint-plugin": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.10.1.tgz", - "integrity": "sha512-U+4yzNXElTf9q0kEfnloI9XbOyD4cnEQCxjUI94q0+W++0GAEQvJ/slwEj9lwjDHfGADRSr+Tco/z0XJvmDfCQ==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.13.0.tgz", + "integrity": "sha512-RnO1SaiCFHn666wNz2QfZEFxvmiNRqhzaMXHXxXXKt+MEP7aajlPxUSMIQpKAaJfverpovEYqjBOXDq6dDcaOQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "^8.12.2", + "@typescript-eslint/utils": "^8.13.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "estraverse": "^5.3.0", @@ -5612,6 +5912,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -5628,6 +5929,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -5644,6 +5946,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -5660,6 +5963,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -5676,6 +5980,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -5692,6 +5997,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -5708,6 +6014,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -5724,6 +6031,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -5740,6 +6048,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", "dev": true, + "license": "MIT", "dependencies": { "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", @@ -5766,6 +6075,7 @@ "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -5786,6 +6096,7 @@ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dev": true, + "license": "MIT", "dependencies": { "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", @@ -5812,6 +6123,7 @@ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -5830,6 +6142,7 @@ "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.21.3", "entities": "^4.4.0" @@ -5847,6 +6160,7 @@ "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -5869,6 +6183,7 @@ "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", "dev": true, + "license": "MIT", "dependencies": { "cosmiconfig": "^8.1.3", "deepmerge": "^4.3.1", @@ -5890,6 +6205,7 @@ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dev": true, + "license": "MIT", "dependencies": { "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", @@ -5916,6 +6232,7 @@ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -5934,6 +6251,7 @@ "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10.13.0" } @@ -5942,28 +6260,33 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/@types/base-64/-/base-64-1.0.2.tgz", "integrity": "sha512-uPgKMmM9fmn7I+Zi6YBqctOye4SlJsHKcisjHIMWpb2YKZRc36GpKyNuQ03JcT+oNXg1m7Uv4wU94EVltn8/cw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/hammerjs": { "version": "2.0.46", "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.46.tgz", - "integrity": "sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==" + "integrity": "sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==", + "license": "MIT" }, "node_modules/@types/html-to-text": { "version": "9.0.4", "resolved": "https://registry.npmjs.org/@types/html-to-text/-/html-to-text-9.0.4.tgz", "integrity": "sha512-pUY3cKH/Nm2yYrEmDlPR1mR7yszjGx4DrwPjQ702C4/D5CwHuZTgZdIdwPkRbcuhs7BAh2L5rg3CL5cbRiGTCQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==" + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "license": "MIT" }, "node_modules/@types/istanbul-lib-report": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "*" } @@ -5972,21 +6295,24 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "*", "@types/istanbul-lib-report": "*" } }, "node_modules/@types/lodash": { - "version": "4.17.13", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.13.tgz", - "integrity": "sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==", - "dev": true + "version": "4.17.16", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.16.tgz", + "integrity": "sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==", + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { - "version": "18.19.64", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.64.tgz", - "integrity": "sha512-955mDqvO2vFf/oL7V3WiUtiz+BugyX8uVbaT2H8oj3+8dRyH2FLiNdowe7eNqRM7IOIZvzDH76EoAT+gwm6aIQ==", + "version": "18.19.80", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.80.tgz", + "integrity": "sha512-kEWeMwMeIvxYkeg1gTc01awpwLbfMRZXdIhwRcakd/KlK53jmRC26LqcbIt7fnAQTu5GzlnWmzA3H6+l1u6xxQ==", + "license": "MIT", "dependencies": { "undici-types": "~5.26.4" } @@ -5995,21 +6321,24 @@ "version": "1.3.11", "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/prop-types": { - "version": "15.7.13", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", - "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", - "devOptional": true + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "devOptional": true, + "license": "MIT" }, "node_modules/@types/react": { "version": "18.2.79", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.79.tgz", "integrity": "sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==", "devOptional": true, + "license": "MIT", "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -6029,12 +6358,14 @@ "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==" + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "license": "MIT" }, "node_modules/@types/yargs": { "version": "13.0.12", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -6042,13 +6373,15 @@ "node_modules/@types/yargs-parser": { "version": "21.0.3", "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "7.18.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "7.18.0", @@ -6082,6 +6415,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@typescript-eslint/scope-manager": "7.18.0", @@ -6104,6 +6438,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/scope-manager": "7.18.0", "@typescript-eslint/types": "7.18.0", @@ -6132,6 +6467,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/types": "7.18.0", "@typescript-eslint/visitor-keys": "7.18.0" @@ -6149,6 +6485,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/typescript-estree": "7.18.0", "@typescript-eslint/utils": "7.18.0", @@ -6176,6 +6513,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@typescript-eslint/scope-manager": "7.18.0", @@ -6198,6 +6536,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || >=20.0.0" }, @@ -6211,6 +6550,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/types": "7.18.0", "@typescript-eslint/visitor-keys": "7.18.0", @@ -6235,10 +6575,11 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -6247,15 +6588,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.13.0.tgz", - "integrity": "sha512-A1EeYOND6Uv250nybnLZapeXpYMl8tkzYUxqmoKAWnI4sei3ihf2XdZVd+vVOmHGcp3t+P7yRrNsyyiXTvShFQ==", + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.1.tgz", + "integrity": "sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.13.0", - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/typescript-estree": "8.13.0" + "@typescript-eslint/scope-manager": "8.26.1", + "@typescript-eslint/types": "8.26.1", + "@typescript-eslint/typescript-estree": "8.26.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6265,17 +6607,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.13.0.tgz", - "integrity": "sha512-XsGWww0odcUT0gJoBZ1DeulY1+jkaHUciUq4jKNv4cpInbvvrtDoyBH9rE/n2V29wQJPk8iCH1wipra9BhmiMA==", + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.26.1.tgz", + "integrity": "sha512-6EIvbE5cNER8sqBu6V7+KeMZIC1664d2Yjt+B9EWUXrsyWpxx4lEZrmvxgSKRC6gX+efDL/UY9OpPZ267io3mg==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/visitor-keys": "8.13.0" + "@typescript-eslint/types": "8.26.1", + "@typescript-eslint/visitor-keys": "8.26.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6286,10 +6630,11 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.13.0.tgz", - "integrity": "sha512-4cyFErJetFLckcThRUFdReWJjVsPCqyBlJTi6IDEpc1GWCIIZRFxVppjWLIMcQhNGhdWJJRYFHpHoDWvMlDzng==", + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.26.1.tgz", + "integrity": "sha512-n4THUQW27VmQMx+3P+B0Yptl7ydfceUj4ON/AQILAASwgYdZ/2dhfymRMh5egRUrvK5lSmaOm77Ry+lmXPOgBQ==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -6299,19 +6644,20 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.13.0.tgz", - "integrity": "sha512-v7SCIGmVsRK2Cy/LTLGN22uea6SaUIlpBcO/gnMGT/7zPtxp90bphcGf4fyrCQl3ZtiBKqVTG32hb668oIYy1g==", + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.26.1.tgz", + "integrity": "sha512-yUwPpUHDgdrv1QJ7YQal3cMVBGWfnuCdKbXw1yyjArax3353rEJP1ZA+4F8nOlQ3RfS2hUN/wze3nlY+ZOhvoA==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/visitor-keys": "8.13.0", + "@typescript-eslint/types": "8.26.1", + "@typescript-eslint/visitor-keys": "8.26.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6320,20 +6666,19 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.13.0.tgz", - "integrity": "sha512-7N/+lztJqH4Mrf0lb10R/CbI1EaAMMGyF5y0oJvFoAhafwgiRA7TXyd8TFn8FC8k5y2dTsYogg238qavRGNnlw==", + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.26.1.tgz", + "integrity": "sha512-AjOC3zfnxd6S4Eiy3jwktJPclqhFHNyd8L6Gycf9WUPoKZpgM5PjkxY1X7uSy61xVpiJDhhk7XT2NVsN3ALTWg==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.13.0", - "eslint-visitor-keys": "^3.4.3" + "@typescript-eslint/types": "8.26.1", + "eslint-visitor-keys": "^4.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6343,23 +6688,12 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -6367,11 +6701,25 @@ "node": ">=10" } }, + "node_modules/@typescript-eslint/utils/node_modules/ts-api-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", + "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/@typescript-eslint/visitor-keys": { "version": "7.18.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/types": "7.18.0", "eslint-visitor-keys": "^3.4.3" @@ -6389,6 +6737,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -6397,10 +6746,11 @@ } }, "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" }, "node_modules/@urql/core": { "version": "2.3.6", @@ -6433,6 +6783,7 @@ "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.13.tgz", "integrity": "sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g==", "deprecated": "this version is no longer supported, please update to at least 0.8.*", + "license": "MIT", "engines": { "node": ">=10.0.0" } @@ -6441,6 +6792,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", "dependencies": { "event-target-shim": "^5.0.0" }, @@ -6452,6 +6804,7 @@ "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" @@ -6461,9 +6814,10 @@ } }, "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -6476,6 +6830,7 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -6510,6 +6865,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -6533,7 +6889,8 @@ "node_modules/anser": { "version": "1.4.10", "resolved": "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz", - "integrity": "sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==" + "integrity": "sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==", + "license": "MIT" }, "node_modules/ansi-escapes": { "version": "4.3.2", @@ -6554,6 +6911,7 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/ansi-fragments/-/ansi-fragments-0.2.1.tgz", "integrity": "sha512-DykbNHxuXQwUDRv5ibc2b0x7uw7wmwOGLBUd5RmaQ5z8Lhx19vwvKV+FAsM5rEA6dEcHxX+/Ad5s9eF2k2bB+w==", + "license": "MIT", "dependencies": { "colorette": "^1.0.7", "slice-ansi": "^2.0.0", @@ -6564,6 +6922,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "license": "MIT", "engines": { "node": ">=6" } @@ -6572,6 +6931,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "license": "MIT", "dependencies": { "ansi-regex": "^4.1.0" }, @@ -6583,6 +6943,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -6591,6 +6952,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -6604,12 +6966,14 @@ "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -6622,6 +6986,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -6632,7 +6997,8 @@ "node_modules/appdirsjs": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/appdirsjs/-/appdirsjs-1.2.7.tgz", - "integrity": "sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw==" + "integrity": "sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw==", + "license": "MIT" }, "node_modules/application-config-path": { "version": "0.1.1", @@ -6649,15 +7015,17 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" }, "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" }, "engines": { "node": ">= 0.4" @@ -6671,6 +7039,7 @@ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -6690,6 +7059,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "license": "MIT", "engines": { "node": ">=8" } @@ -6699,6 +7069,7 @@ "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -6715,15 +7086,16 @@ } }, "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -6733,15 +7105,16 @@ } }, "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -6755,6 +7128,7 @@ "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -6767,18 +7141,18 @@ } }, "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" }, "engines": { "node": ">= 0.4" @@ -6790,12 +7164,14 @@ "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "license": "MIT" }, "node_modules/asn1js": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.5.tgz", "integrity": "sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==", + "license": "BSD-3-Clause", "dependencies": { "pvtsutils": "^1.3.2", "pvutils": "^1.1.3", @@ -6809,6 +7185,7 @@ "version": "0.15.2", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.15.2.tgz", "integrity": "sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg==", + "license": "MIT", "dependencies": { "tslib": "^2.0.1" }, @@ -6820,24 +7197,37 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "license": "MIT", "engines": { "node": ">=4" } }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/async-limiter": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "license": "MIT" }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" }, "node_modules/at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "license": "ISC", "engines": { "node": ">= 4.0.0" } @@ -6846,6 +7236,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" }, @@ -6871,17 +7262,19 @@ "version": "7.0.0-bridge.0", "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", "integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==", + "license": "MIT", "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", - "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", + "version": "0.4.12", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz", + "integrity": "sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==", + "license": "MIT", "dependencies": { "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.2", + "@babel/helper-define-polyfill-provider": "^0.6.3", "semver": "^6.3.1" }, "peerDependencies": { @@ -6889,23 +7282,25 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.10.6", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", - "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", + "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", + "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2", - "core-js-compat": "^3.38.0" + "@babel/helper-define-polyfill-provider": "^0.6.3", + "core-js-compat": "^3.40.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", - "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz", + "integrity": "sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==", + "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2" + "@babel/helper-define-polyfill-provider": "^0.6.3" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -6915,6 +7310,7 @@ "version": "0.0.0-experimental-592953e-20240517", "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-0.0.0-experimental-592953e-20240517.tgz", "integrity": "sha512-OjG1SVaeQZaJrqkMFJatg8W/MTow8Ak5rx2SI0ETQBO1XvOk/XZGMbltNCPdFJLKghBYoBjC+Y3Ap/Xr7B01mA==", + "license": "MIT", "dependencies": { "@babel/generator": "7.2.0", "@babel/types": "^7.19.0", @@ -6929,6 +7325,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.2.0.tgz", "integrity": "sha512-BA75MVfRlFQG2EZgFYIwyT1r6xSkwfP2bdkY/kLZusEYWiJs4xCowab/alaEaT0wSvmVuXGqiefeBlP+7V1yKg==", + "license": "MIT", "dependencies": { "@babel/types": "^7.2.0", "jsesc": "^2.5.1", @@ -6941,6 +7338,7 @@ "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, @@ -6952,6 +7350,7 @@ "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -6959,12 +7358,14 @@ "node_modules/babel-plugin-react-native-web": { "version": "0.19.13", "resolved": "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.19.13.tgz", - "integrity": "sha512-4hHoto6xaN23LCyZgL9LJZc3olmAxd7b6jDzlZnKXAh4rRAbZRKNBJoOOdp46OBqgy+K0t0guTj5/mhA8inymQ==" + "integrity": "sha512-4hHoto6xaN23LCyZgL9LJZc3olmAxd7b6jDzlZnKXAh4rRAbZRKNBJoOOdp46OBqgy+K0t0guTj5/mhA8inymQ==", + "license": "MIT" }, "node_modules/babel-plugin-transform-flow-enums": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-enums/-/babel-plugin-transform-flow-enums-0.0.2.tgz", "integrity": "sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==", + "license": "MIT", "dependencies": { "@babel/plugin-syntax-flow": "^7.12.1" } @@ -6973,6 +7374,7 @@ "version": "11.0.15", "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-11.0.15.tgz", "integrity": "sha512-rgiMTYwqIPULaO7iZdqyL7aAff9QLOX6OWUtLZBlOrOTreGY1yHah/5+l8MvI6NVc/8Zj5LY4Y5uMSnJIuzTLw==", + "license": "MIT", "dependencies": { "@babel/plugin-proposal-decorators": "^7.12.9", "@babel/plugin-transform-export-namespace-from": "^7.22.11", @@ -6989,17 +7391,20 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" }, "node_modules/base-64": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", - "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==" + "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==", + "license": "MIT" }, "node_modules/base64-arraybuffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "license": "MIT", "engines": { "node": ">= 0.6.0" } @@ -7021,7 +7426,8 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/better-opn": { "version": "3.0.2", @@ -7039,6 +7445,7 @@ "version": "1.6.52", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "license": "Unlicense", "engines": { "node": ">=0.6" } @@ -7047,6 +7454,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -7071,6 +7479,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -7080,6 +7489,7 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -7092,7 +7502,8 @@ "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" }, "node_modules/bplist-creator": { "version": "0.0.7", @@ -7119,6 +7530,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -7127,6 +7539,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -7135,9 +7548,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", - "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "funding": [ { "type": "opencollective", @@ -7152,10 +7565,11 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001669", - "electron-to-chromium": "^1.5.41", - "node-releases": "^2.0.18", + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { @@ -7169,6 +7583,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "license": "Apache-2.0", "dependencies": { "node-int64": "^0.4.0" } @@ -7191,6 +7606,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" @@ -7200,6 +7616,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "license": "MIT", "dependencies": { "buffer-alloc-unsafe": "^1.1.0", "buffer-fill": "^1.0.0" @@ -7208,17 +7625,20 @@ "node_modules/buffer-alloc-unsafe": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "license": "MIT" }, "node_modules/buffer-fill": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==" + "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", + "license": "MIT" }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" }, "node_modules/builtins": { "version": "1.0.3", @@ -7230,6 +7650,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -7287,21 +7708,51 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/cal-parser/-/cal-parser-1.0.2.tgz", "integrity": "sha512-wlQwcF0fl4eLclyGdncF9rcNNq0ipRYZGagG6h3LVgRXvCWE1fdMUaCLXwfC9YWoz9jKKbjQAq7TpO2Y3yrvmA==", + "license": "MIT", "dependencies": { "ical-date-parser": "^4.0.0", "rrule": "^2.6.8" } }, "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -7314,6 +7765,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", + "license": "MIT", "dependencies": { "callsites": "^2.0.0" }, @@ -7325,6 +7777,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==", + "license": "MIT", "engines": { "node": ">=4" } @@ -7333,6 +7786,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", + "license": "MIT", "dependencies": { "caller-callsite": "^2.0.0" }, @@ -7345,6 +7799,7 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -7353,6 +7808,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "license": "MIT", "dependencies": { "pascal-case": "^3.1.2", "tslib": "^2.0.3" @@ -7362,6 +7818,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -7370,9 +7827,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001680", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz", - "integrity": "sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==", + "version": "1.0.30001704", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001704.tgz", + "integrity": "sha512-+L2IgBbV6gXB4ETf0keSvLr7JUrRVbIaB/lrQ1+z8mRcQiisG5k+lG6O4n6Y5q6f5EuNfaYXKgymucphlEXQew==", "funding": [ { "type": "opencollective", @@ -7386,12 +7843,14 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/capital-case": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", + "license": "MIT", "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", @@ -7402,6 +7861,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -7417,6 +7877,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", + "license": "MIT", "dependencies": { "camel-case": "^4.1.2", "capital-case": "^1.0.4", @@ -7454,6 +7915,7 @@ "version": "0.15.2", "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.2.tgz", "integrity": "sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==", + "license": "Apache-2.0", "dependencies": { "@types/node": "*", "escape-string-regexp": "^4.0.0", @@ -7477,6 +7939,7 @@ "url": "https://github.com/sponsors/sibiraj-s" } ], + "license": "MIT", "engines": { "node": ">=8" } @@ -7506,6 +7969,7 @@ "version": "2.9.2", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", "engines": { "node": ">=6" }, @@ -7517,6 +7981,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -7539,6 +8004,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "license": "MIT", "dependencies": { "is-plain-object": "^2.0.4", "kind-of": "^6.0.2", @@ -7552,6 +8018,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" @@ -7564,6 +8031,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -7574,12 +8042,14 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/color-string": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" @@ -7588,12 +8058,14 @@ "node_modules/colorette": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", - "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==" + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "license": "MIT" }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -7604,12 +8076,14 @@ "node_modules/command-exists": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", - "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==", + "license": "MIT" }, "node_modules/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", "engines": { "node": ">= 10" } @@ -7617,7 +8091,8 @@ "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "license": "MIT" }, "node_modules/component-type": { "version": "1.2.2", @@ -7632,6 +8107,7 @@ "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "license": "MIT", "dependencies": { "mime-db": ">= 1.43.0 < 2" }, @@ -7640,9 +8116,10 @@ } }, "node_modules/compression": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.5.tgz", - "integrity": "sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz", + "integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==", + "license": "MIT", "dependencies": { "bytes": "3.1.2", "compressible": "~2.0.18", @@ -7660,6 +8137,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -7667,12 +8145,14 @@ "node_modules/compression/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/compression/node_modules/negotiator": { "version": "0.6.4", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -7680,12 +8160,14 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" }, "node_modules/connect": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "license": "MIT", "dependencies": { "debug": "2.6.9", "finalhandler": "1.1.2", @@ -7700,6 +8182,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -7707,12 +8190,14 @@ "node_modules/connect/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/constant-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", + "license": "MIT", "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", @@ -7722,14 +8207,16 @@ "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" }, "node_modules/core-js-compat": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", - "integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==", + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.41.0.tgz", + "integrity": "sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A==", + "license": "MIT", "dependencies": { - "browserslist": "^4.24.2" + "browserslist": "^4.24.4" }, "funding": { "type": "opencollective", @@ -7739,12 +8226,14 @@ "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" }, "node_modules/cosmiconfig": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "license": "MIT", "dependencies": { "import-fresh": "^2.0.0", "is-directory": "^0.3.1", @@ -7759,6 +8248,7 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" } @@ -7767,6 +8257,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "license": "MIT", "dependencies": { "caller-path": "^2.0.0", "resolve-from": "^3.0.0" @@ -7779,6 +8270,7 @@ "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "license": "MIT", "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -7791,22 +8283,25 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/cross-fetch": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", - "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", + "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", + "license": "MIT", "dependencies": { - "node-fetch": "^2.6.12" + "node-fetch": "^2.7.0" } }, "node_modules/cross-spawn": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz", - "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -7838,6 +8333,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "license": "MIT", "dependencies": { "utrie": "^1.0.2" } @@ -7846,6 +8342,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", @@ -7861,6 +8358,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "license": "MIT", "dependencies": { "mdn-data": "2.0.14", "source-map": "^0.6.1" @@ -7873,6 +8371,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -7881,6 +8380,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "license": "BSD-2-Clause", "engines": { "node": ">= 6" }, @@ -7893,6 +8393,7 @@ "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", "dev": true, + "license": "MIT", "dependencies": { "css-tree": "~2.2.0" }, @@ -7906,6 +8407,7 @@ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", "dev": true, + "license": "MIT", "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" @@ -7919,13 +8421,15 @@ "version": "2.0.28", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", - "dev": true + "dev": true, + "license": "CC0-1.0" }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true + "devOptional": true, + "license": "MIT" }, "node_modules/dag-map": { "version": "1.0.2", @@ -7934,13 +8438,14 @@ "license": "MIT" }, "node_modules/data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -7950,27 +8455,29 @@ } }, "node_modules/data-view-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/inspect-js" } }, "node_modules/data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" }, @@ -7994,12 +8501,14 @@ "node_modules/dayjs": { "version": "1.11.13", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", - "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" }, "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -8016,6 +8525,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -8024,6 +8534,7 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "license": "MIT", "engines": { "node": ">=0.10" } @@ -8041,12 +8552,14 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -8068,6 +8581,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "license": "MIT", "dependencies": { "clone": "^1.0.2" }, @@ -8079,6 +8593,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "license": "MIT", "engines": { "node": ">=0.8" } @@ -8087,6 +8602,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -8112,6 +8628,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "license": "MIT", "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -8150,6 +8667,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -8157,12 +8675,14 @@ "node_modules/denodeify": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/denodeify/-/denodeify-1.2.1.tgz", - "integrity": "sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg==" + "integrity": "sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg==", + "license": "MIT" }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -8191,6 +8711,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" @@ -8211,12 +8732,14 @@ "node_modules/dijkstrajs": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", - "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==" + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "license": "MIT" }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "license": "MIT", "dependencies": { "path-type": "^4.0.0" }, @@ -8229,6 +8752,7 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, + "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -8240,6 +8764,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -8258,12 +8783,14 @@ "type": "github", "url": "https://github.com/sponsors/fb55" } - ] + ], + "license": "BSD-2-Clause" }, "node_modules/domhandler": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", "dependencies": { "domelementtype": "^2.3.0" }, @@ -8275,9 +8802,10 @@ } }, "node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -8291,15 +8819,17 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "license": "MIT", "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, "node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "license": "BSD-2-Clause", "engines": { "node": ">=12" }, @@ -8308,11 +8838,12 @@ } }, "node_modules/dotenv-expand": { - "version": "11.0.6", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.6.tgz", - "integrity": "sha512-8NHi73otpWsZGBSZwwknTXS5pqMOrk9+Ssrna8xCaxkzEpU9OTf9R5ArQGVw03//Zmk9MOwLPng9WwndvpAJ5g==", + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", + "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", + "license": "BSD-2-Clause", "dependencies": { - "dotenv": "^16.4.4" + "dotenv": "^16.4.5" }, "engines": { "node": ">=12" @@ -8321,6 +8852,20 @@ "url": "https://dotenvx.com" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -8330,22 +8875,26 @@ "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.55", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.55.tgz", - "integrity": "sha512-6maZ2ASDOTBtjt9FhqYPRnbvKU5tjG0IN9SztUOWYw2AzNDNpKJYLJmlK0/En4Hs/aiWnB+JZ+gW19PIGszgKg==" + "version": "1.5.118", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.118.tgz", + "integrity": "sha512-yNDUus0iultYyVoEFLnQeei7LOQkL8wg8GQpkPCRrOlJXlcCwa6eGKZkxQ9ciHsqZyYbj8Jd94X1CTPzGm+uIA==", + "license": "ISC" }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" }, "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -8363,6 +8912,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", "engines": { "node": ">=0.12" }, @@ -8383,6 +8933,7 @@ "version": "7.14.0", "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz", "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==", + "license": "MIT", "bin": { "envinfo": "dist/cli.js" }, @@ -8400,6 +8951,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" } @@ -8408,6 +8960,7 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "license": "MIT", "dependencies": { "stackframe": "^1.3.4" } @@ -8416,6 +8969,7 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.1.tgz", "integrity": "sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A==", + "license": "MIT", "dependencies": { "accepts": "~1.3.7", "escape-html": "~1.0.3" @@ -8425,56 +8979,62 @@ } }, "node_modules/es-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" }, "engines": { "node": ">= 0.4" @@ -8484,12 +9044,10 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -8498,40 +9056,44 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/es-iterator-helpers": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.0.tgz", - "integrity": "sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", + "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.0.3", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", + "get-intrinsic": "^1.2.6", "globalthis": "^1.0.4", - "gopd": "^1.0.1", + "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "iterator.prototype": "^1.1.3", - "safe-array-concat": "^1.1.2" + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.4", + "safe-array-concat": "^1.1.3" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0" }, @@ -8540,35 +9102,42 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.4", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dev": true, + "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "license": "MIT", "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" }, "engines": { "node": ">= 0.4" @@ -8581,6 +9150,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", "engines": { "node": ">=6" } @@ -8588,12 +9158,14 @@ "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -8607,6 +9179,7 @@ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -8658,28 +9231,29 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.37.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz", - "integrity": "sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w==", + "version": "7.37.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz", + "integrity": "sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ==", "dev": true, + "license": "MIT", "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.2", + "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.1.0", + "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.8", "object.fromentries": "^2.0.8", - "object.values": "^1.2.0", + "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.11", + "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "engines": { @@ -8694,6 +9268,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-react-native/-/eslint-plugin-react-native-4.1.0.tgz", "integrity": "sha512-QLo7rzTBOl43FvVqDdq5Ql9IoElIuTdjrz9SKAXCvULvBoRZ44JGSkx9z4999ZusCsb4rK3gjS8gOGyeYqZv2Q==", "dev": true, + "license": "MIT", "dependencies": { "eslint-plugin-react-native-globals": "^0.1.1" }, @@ -8705,13 +9280,15 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/eslint-plugin-react-native-globals/-/eslint-plugin-react-native-globals-0.1.2.tgz", "integrity": "sha512-9aEPf1JEpiTjcFAmmyw8eiIXmcNZOqaZyHO77wgm0/dWfT/oxC1SrIq8ET38pMxHYrcB6Uew+TzUVsBeczF88g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/eslint-plugin-react/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -8722,6 +9299,7 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -8734,6 +9312,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -8762,6 +9341,7 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -8778,6 +9358,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -8790,6 +9371,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -8800,6 +9382,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -8812,6 +9395,7 @@ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -8829,6 +9413,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, @@ -8844,6 +9429,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -8856,6 +9442,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -8868,6 +9455,7 @@ "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", @@ -8884,6 +9472,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -8897,6 +9486,7 @@ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -8909,6 +9499,7 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -8921,16 +9512,19 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, "node_modules/esup-multi.js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/esup-multi.js/-/esup-multi.js-1.0.2.tgz", - "integrity": "sha512-EedHM/Khi22qlXURVBL+2YKnkxSLuW47OfRwQkh80RnU6WnLyGZd7t3HcbjvqlVNt3ZqQp0MxLAfG5DoVCaN6w==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esup-multi.js/-/esup-multi.js-1.0.4.tgz", + "integrity": "sha512-+OjFMJxtv4gO35IlywOj3jG2mWORLsdKaQB07ZAumzlQgfXrnwo2IF1qTybnK6NXiwD5PnsQj+xW/JP0Nz3VrQ==", + "license": "CeCILL-2.1", "dependencies": { - "fetch-cookie": "^3.0.1" + "fetch-cookie": "^3.0.1", + "tslib": "^2.7.0" }, "engines": { "node": ">=18" @@ -8940,6 +9534,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } @@ -8948,6 +9543,7 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -8956,6 +9552,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", "engines": { "node": ">=6" } @@ -9087,6 +9684,7 @@ "version": "5.9.1", "resolved": "https://registry.npmjs.org/expo-application/-/expo-application-5.9.1.tgz", "integrity": "sha512-uAfLBNZNahnDZLRU41ZFmNSKtetHUT9Ua557/q189ua0AWV7pQjoVAx49E4953feuvqc9swtU3ScZ/hN1XO/FQ==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9095,6 +9693,7 @@ "version": "10.0.10", "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-10.0.10.tgz", "integrity": "sha512-0qoTIihB79k+wGus9wy0JMKq7DdenziVx3iUkGvMAy2azscSgWH6bd2gJ9CGnhC6JRd3qTMFBL0ou/fx7WZl7A==", + "license": "MIT", "dependencies": { "expo-constants": "~16.0.0", "invariant": "^2.2.4", @@ -9108,6 +9707,7 @@ "version": "5.5.2", "resolved": "https://registry.npmjs.org/expo-auth-session/-/expo-auth-session-5.5.2.tgz", "integrity": "sha512-fgqrNz9FhCl/kNyU2Vy2AmLWk+X7vmgiGN2KVUgB8yLHl/tPogYLpNOiqFl/pMLMveoKjPpVOVfbz3RTJHJoTg==", + "license": "MIT", "dependencies": { "expo-application": "~5.9.0", "expo-constants": "~16.0.0", @@ -9121,6 +9721,7 @@ "version": "14.0.7", "resolved": "https://registry.npmjs.org/expo-av/-/expo-av-14.0.7.tgz", "integrity": "sha512-FvKZxyy+2/qcCmp+e1GTK3s4zH8ZO1RfjpqNxh7ARlS1oH8HPtk1AyZAMo52tHz3yQ3UIqxQ2YbI9CFb4065lA==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9141,6 +9742,7 @@ "version": "13.0.1", "resolved": "https://registry.npmjs.org/expo-barcode-scanner/-/expo-barcode-scanner-13.0.1.tgz", "integrity": "sha512-xBGLT1An2gpAMIQRTLU3oHydKohX8r8F9/ait1Fk9Vgd0GraFZbP4IiT7nHMlaw4H6E7Muucf7vXpGV6u7d4HQ==", + "license": "MIT", "dependencies": { "expo-image-loader": "~4.7.0" }, @@ -9149,9 +9751,10 @@ } }, "node_modules/expo-blur": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/expo-blur/-/expo-blur-13.0.2.tgz", - "integrity": "sha512-t2p7BChO3Reykued++QJRMZ/og6J3aXtSQ+bU31YcBeXhZLkHwjWEhiPKPnJka7J2/yTs4+jOCNDY0kCZmcE3w==", + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/expo-blur/-/expo-blur-13.0.3.tgz", + "integrity": "sha512-z5W9ZGlG6ZiRLuoJZG1AHRvjK8j+2+nc/mtvEtyAa8T/8iTNpUnX4eC8xXDoTL/H4y2pc3cHvytDjCXJG26pcQ==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9160,6 +9763,7 @@ "version": "12.0.1", "resolved": "https://registry.npmjs.org/expo-brightness/-/expo-brightness-12.0.1.tgz", "integrity": "sha512-Jdi8+9YeixWdg0Z2A/f3YnQ86+iMLuGyqcTi8UTipGotTHZBG8UuV1Gab/2tttOhUK2cfv4Hc2zDbkKFP6F1JQ==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9168,6 +9772,7 @@ "version": "0.12.5", "resolved": "https://registry.npmjs.org/expo-build-properties/-/expo-build-properties-0.12.5.tgz", "integrity": "sha512-donC1le0PYfLKCPKRMGQoixuWuwDWCngzXSoQXUPsgHTDHQUKr8aw+lcWkTwZcItgNovcnk784I0dyfYDcxybA==", + "license": "MIT", "dependencies": { "ajv": "^8.11.0", "semver": "^7.6.0" @@ -9180,6 +9785,7 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -9194,12 +9800,14 @@ "node_modules/expo-build-properties/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" }, "node_modules/expo-build-properties/node_modules/semver": { "version": "7.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -9211,6 +9819,7 @@ "version": "15.0.16", "resolved": "https://registry.npmjs.org/expo-camera/-/expo-camera-15.0.16.tgz", "integrity": "sha512-FLE02DMqkjwsb7IugKAqQvBe6s+TCQeb5LupO1+r//wAhBwmHncOrc6zV95ZEC2f9PTPK34nFH/s8CDGiVzIAA==", + "license": "MIT", "dependencies": { "invariant": "^2.2.4" }, @@ -9222,6 +9831,7 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/expo-clipboard/-/expo-clipboard-6.0.3.tgz", "integrity": "sha512-RIKDsuHkYfaspifbFpVC8sBVFKR05L7Pj7mU2/XkbrW9m01OBNvdpGraXEMsTFCx97xMGsZpEw9pPquL4j4xVg==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9230,6 +9840,7 @@ "version": "16.0.2", "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-16.0.2.tgz", "integrity": "sha512-9tNY3OVO0jfiMzl7ngb6IOyR5VFzNoN5OOazUWoeGfmMqVB5kltTemRvKraK9JRbBKIw+SOYLEmF0sEqgFZ6OQ==", + "license": "MIT", "dependencies": { "@expo/config": "~9.0.0", "@expo/env": "~0.3.0" @@ -9242,6 +9853,7 @@ "version": "13.0.2", "resolved": "https://registry.npmjs.org/expo-crypto/-/expo-crypto-13.0.2.tgz", "integrity": "sha512-7f/IMPYJZkBM21LNEMXGrNo/0uXSVfZTwufUdpNKedJR0fm5fH4DCSN79ZddlV26nF90PuXjK2inIbI6lb0qRA==", + "license": "MIT", "dependencies": { "base64-js": "^1.3.0" }, @@ -9253,6 +9865,7 @@ "version": "4.0.29", "resolved": "https://registry.npmjs.org/expo-dev-client/-/expo-dev-client-4.0.29.tgz", "integrity": "sha512-aANlw9dC4PJEPaRNpe+X5xwyYI+aCIcbZklAAsFlkv2/05gLrsvAFgmQpRtowAzF+VggHWde1eKUOeUccAYIEg==", + "license": "MIT", "dependencies": { "expo-dev-launcher": "4.0.29", "expo-dev-menu": "5.0.23", @@ -9268,6 +9881,7 @@ "version": "4.0.29", "resolved": "https://registry.npmjs.org/expo-dev-launcher/-/expo-dev-launcher-4.0.29.tgz", "integrity": "sha512-0a0SL8mc4FrqPeGxJHe9kf0kG+Di+38Gd+HP5DEL9dcOa8m2qffKnk22UcyujCT6+Qk0OUK1s53nnfqFB26uVw==", + "license": "MIT", "dependencies": { "ajv": "8.11.0", "expo-dev-menu": "5.0.23", @@ -9283,6 +9897,7 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -9297,12 +9912,14 @@ "node_modules/expo-dev-launcher/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" }, "node_modules/expo-dev-launcher/node_modules/semver": { "version": "7.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -9314,6 +9931,7 @@ "version": "5.0.23", "resolved": "https://registry.npmjs.org/expo-dev-menu/-/expo-dev-menu-5.0.23.tgz", "integrity": "sha512-ztDvrSdFGkRbMoQlGLyKMS6CslMGylonVW4kQHUrBQApCL0c2NtRwLlr2bA1SXF0S7qYdPPg/ayLnj7DDR5X2w==", + "license": "MIT", "dependencies": { "expo-dev-menu-interface": "1.8.4", "semver": "^7.5.4" @@ -9326,14 +9944,16 @@ "version": "1.8.4", "resolved": "https://registry.npmjs.org/expo-dev-menu-interface/-/expo-dev-menu-interface-1.8.4.tgz", "integrity": "sha512-FpYI57EUu9qTSOOi+FZJ58xkCGJK7QD0mTiXK/y1I8lRdZGjCmdBqVvC4dAx2GcbIT78EPxaVf4/90tK/KRK6A==", + "license": "MIT", "peerDependencies": { "expo": "*" } }, "node_modules/expo-dev-menu/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -9345,6 +9965,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/expo-device/-/expo-device-6.0.2.tgz", "integrity": "sha512-sCt91CuTmAuMXX4SlFOn4lIos2UIr8vb0jDstDDZXys6kErcj0uynC7bQAMreU5uRUTKMAl4MAMpKt9ufCXPBw==", + "license": "MIT", "dependencies": { "ua-parser-js": "^0.7.33" }, @@ -9356,6 +9977,7 @@ "version": "17.0.1", "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-17.0.1.tgz", "integrity": "sha512-dYpnZJqTGj6HCYJyXAgpFkQWsiCH3HY1ek2cFZVHFoEc5tLz9gmdEgTF6nFHurvmvfmXqxi7a5CXyVm0aFYJBw==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9364,6 +9986,7 @@ "version": "12.0.10", "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-12.0.10.tgz", "integrity": "sha512-Q1i2NuYri3jy32zdnBaHHCya1wH1yMAsI+3CCmj9zlQzlhsS9Bdwcj2W3c5eU5FvH2hsNQy4O+O1NnM6o/pDaQ==", + "license": "MIT", "dependencies": { "fontfaceobserver": "^2.1.0" }, @@ -9375,6 +9998,7 @@ "version": "13.0.1", "resolved": "https://registry.npmjs.org/expo-haptics/-/expo-haptics-13.0.1.tgz", "integrity": "sha512-qG0EOLDE4bROVT3DtUSyV9g3iB3YFu9j3711X7SNNEnBDXc+2/p3wGDPTnJvPW0ao6HG3/McAOrBQA5hVSdWng==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9383,6 +10007,7 @@ "version": "4.7.0", "resolved": "https://registry.npmjs.org/expo-image-loader/-/expo-image-loader-4.7.0.tgz", "integrity": "sha512-cx+MxxsAMGl9AiWnQUzrkJMJH4eNOGlu7XkLGnAXSJrRoIiciGaKqzeaD326IyCTV+Z1fXvIliSgNW+DscvD8g==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9391,6 +10016,7 @@ "version": "15.0.7", "resolved": "https://registry.npmjs.org/expo-image-picker/-/expo-image-picker-15.0.7.tgz", "integrity": "sha512-u8qiPZNfDb+ap6PJ8pq2iTO7JKX+ikAUQ0K0c7gXGliKLxoXgDdDmXxz9/6QdICTshJBJlBvI0MwY5NWu7A/uw==", + "license": "MIT", "dependencies": { "expo-image-loader": "~4.7.0" }, @@ -9402,6 +10028,7 @@ "version": "11.0.1", "resolved": "https://registry.npmjs.org/expo-intent-launcher/-/expo-intent-launcher-11.0.1.tgz", "integrity": "sha512-nUmTTa/HG4jUyRc5YHngdpP5bMyGSRZPi2RX9kpILd3vbMWQeVnwzqAfC+uI34W8uKhEk+9b9Dytzmm7bBND1Q==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9409,12 +10036,14 @@ "node_modules/expo-json-utils": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/expo-json-utils/-/expo-json-utils-0.13.1.tgz", - "integrity": "sha512-mlfaSArGVb+oJmUcR22jEONlgPp0wj4iNIHfQ2je9Q8WTOqMc0Ws9tUciz3JdJnhffdHqo/k8fpvf0IRmN5HPA==" + "integrity": "sha512-mlfaSArGVb+oJmUcR22jEONlgPp0wj4iNIHfQ2je9Q8WTOqMc0Ws9tUciz3JdJnhffdHqo/k8fpvf0IRmN5HPA==", + "license": "MIT" }, "node_modules/expo-keep-awake": { "version": "13.0.2", "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-13.0.2.tgz", "integrity": "sha512-kKiwkVg/bY0AJ5q1Pxnm/GvpeB6hbNJhcFsoOWDh2NlpibhCLaHL826KHUM+WsnJRbVRxJ+K9vbPRHEMvFpVyw==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9423,6 +10052,7 @@ "version": "13.0.2", "resolved": "https://registry.npmjs.org/expo-linear-gradient/-/expo-linear-gradient-13.0.2.tgz", "integrity": "sha512-EDcILUjRKu4P1rtWcwciN6CSyGtH7Bq4ll3oTRV7h3h8oSzSilH1g6z7kTAMlacPBKvMnkkWOGzW6KtgMKEiTg==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9431,6 +10061,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-6.3.1.tgz", "integrity": "sha512-xuZCntSBGWCD/95iZ+mTUGTwHdy8Sx+immCqbUBxdvZ2TN61P02kKg7SaLS8A4a/hLrSCwrg5tMMwu5wfKr35g==", + "license": "MIT", "dependencies": { "expo-constants": "~16.0.0", "invariant": "^2.2.4" @@ -9440,6 +10071,7 @@ "version": "17.0.1", "resolved": "https://registry.npmjs.org/expo-location/-/expo-location-17.0.1.tgz", "integrity": "sha512-m+OzotzlAXO3ZZ1uqW5GC25nXW868zN+ROyBA1V4VF6jGay1ZEs4URPglCVUDzZby2F5wt24cMzqDKw2IX6nRw==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9448,6 +10080,7 @@ "version": "0.14.3", "resolved": "https://registry.npmjs.org/expo-manifests/-/expo-manifests-0.14.3.tgz", "integrity": "sha512-L3b5/qocBPiQjbW0cpOHfnqdKZbTJS7sA3mgeDJT+mWga/xYsdpma1EfNmsuvrOzjLGjStr1k1fceM9Bl49aqQ==", + "license": "MIT", "dependencies": { "@expo/config": "~9.0.0", "expo-json-utils": "~0.13.0" @@ -9460,6 +10093,7 @@ "version": "16.0.5", "resolved": "https://registry.npmjs.org/expo-media-library/-/expo-media-library-16.0.5.tgz", "integrity": "sha512-O9RUqBWgJVRF0mO6EiLSBFyfb5wR1/ZqovbT43V0TAo5sgcjrHRs+0NID/U6BWDRuiFeX2AU516JgNDutNUFSw==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9468,6 +10102,7 @@ "version": "1.11.3", "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-1.11.3.tgz", "integrity": "sha512-oYh8EZEvYF5TYppxEKUTTJmbr8j7eRRnrIxzZtMvxLTXoujThVPMFS/cbnSnf2bFm1lq50TdDNABhmEi7z0ngQ==", + "license": "MIT", "dependencies": { "chalk": "^4.1.0", "commander": "^7.2.0", @@ -9485,6 +10120,7 @@ "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -9499,6 +10135,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -9510,6 +10147,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -9518,6 +10156,7 @@ "version": "1.12.26", "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-1.12.26.tgz", "integrity": "sha512-y8yDWjOi+rQRdO+HY+LnUlz8qzHerUaw/LUjKPU/mX8PRXP4UUPEEp5fjAwBU44xjNmYSHWZDwet4IBBE+yQUA==", + "license": "MIT", "dependencies": { "invariant": "^2.2.4" } @@ -9526,6 +10165,7 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/expo-navigation-bar/-/expo-navigation-bar-3.0.7.tgz", "integrity": "sha512-KCNHyZ58zoN4xdy7D1lUdJvveCYNVQHGSX4M6xO/SZypvI6GZbLzKSN6Lx4GDGEFxG6Kb+EAckZl48tSiNeGYQ==", + "license": "MIT", "dependencies": { "@react-native/normalize-colors": "0.74.85", "debug": "^4.3.2" @@ -9538,6 +10178,7 @@ "version": "13.0.9", "resolved": "https://registry.npmjs.org/expo-sensors/-/expo-sensors-13.0.9.tgz", "integrity": "sha512-yi/TERUP8dpsJWWRgjT+UuQ7PI604PMndwXh9iBnfNO7q+dtoKZKsVSNJw61IKLNflbtCAyJxgmzPoe/JDT04Q==", + "license": "MIT", "dependencies": { "invariant": "^2.2.4" }, @@ -9549,95 +10190,34 @@ "version": "12.0.1", "resolved": "https://registry.npmjs.org/expo-sharing/-/expo-sharing-12.0.1.tgz", "integrity": "sha512-wBT+WeXwapj/9NWuLJO01vi9bdlchYu/Q/xD8slL/Ls4vVYku8CPqzkTtDFcjLrjtlJqyeHsdQXwKLvORmBIew==", + "license": "MIT", "peerDependencies": { "expo": "*" } }, "node_modules/expo-splash-screen": { - "version": "0.27.6", - "resolved": "https://registry.npmjs.org/expo-splash-screen/-/expo-splash-screen-0.27.6.tgz", - "integrity": "sha512-joUwZQS48k3VMnucQ0Y8Dle1t1FyIvluQA4kjuPx2x7l2dRrfctbo34ahTnC0p1o2go5oN2iEnSTOElY4wRQHw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/expo-splash-screen/-/expo-splash-screen-0.27.7.tgz", + "integrity": "sha512-s+eGcG185878nixlrjhhLD6UDYrvoqBUaBkIEozBVWFg3pkdsKpONPiUAco4XR3h7I/9ODq4quN28RJLFO+s0Q==", + "license": "MIT", "dependencies": { - "@expo/prebuild-config": "7.0.8" + "@expo/prebuild-config": "7.0.9" }, "peerDependencies": { "expo": "*" } }, - "node_modules/expo-splash-screen/node_modules/@expo/prebuild-config": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-7.0.8.tgz", - "integrity": "sha512-wH9NVg6HiwF5y9x0TxiMEeBF+ITPGDXy5/i6OUheSrKpPgb0lF1Mwzl/f2fLPXBEpl+ZXOQ8LlLW32b7K9lrNg==", - "dependencies": { - "@expo/config": "~9.0.0-beta.0", - "@expo/config-plugins": "~8.0.8", - "@expo/config-types": "^51.0.0-unreleased", - "@expo/image-utils": "^0.5.0", - "@expo/json-file": "^8.3.0", - "@react-native/normalize-colors": "0.74.85", - "debug": "^4.3.1", - "fs-extra": "^9.0.0", - "resolve-from": "^5.0.0", - "semver": "^7.6.0", - "xml2js": "0.6.0" - }, - "peerDependencies": { - "expo-modules-autolinking": ">=0.8.1" - } - }, - "node_modules/expo-splash-screen/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/expo-splash-screen/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/expo-splash-screen/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/expo-splash-screen/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/expo-status-bar": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/expo-status-bar/-/expo-status-bar-1.12.1.tgz", - "integrity": "sha512-/t3xdbS8KB0prj5KG5w7z+wZPFlPtkgs95BsmrP/E7Q0xHXTcDcQ6Cu2FkFuRM+PKTb17cJDnLkawyS5vDLxMA==" + "integrity": "sha512-/t3xdbS8KB0prj5KG5w7z+wZPFlPtkgs95BsmrP/E7Q0xHXTcDcQ6Cu2FkFuRM+PKTb17cJDnLkawyS5vDLxMA==", + "license": "MIT" }, "node_modules/expo-store-review": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/expo-store-review/-/expo-store-review-7.0.2.tgz", "integrity": "sha512-bXBXPv2KhfE9Ct14vof9Y1kAGlTFXmGdOLPToyg/eM6lMSALwyrLMkJ3Ba2XCI8PzKGKlx/+Yx5bImrZs4cUwg==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9646,6 +10226,7 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/expo-system-ui/-/expo-system-ui-3.0.7.tgz", "integrity": "sha512-KAs72F5JKhdIfPR9ZNVlRubTPK9uUuevPy5oYEp12xNEzSQcjZKvypH5NpwJuNWkXzrp3n3vZ+3pXsudA7J3KA==", + "license": "MIT", "dependencies": { "@react-native/normalize-colors": "0.74.85", "debug": "^4.3.2" @@ -9658,6 +10239,7 @@ "version": "11.8.2", "resolved": "https://registry.npmjs.org/expo-task-manager/-/expo-task-manager-11.8.2.tgz", "integrity": "sha512-Uhy3ol5gYeZOyeRFddYjLI1B2DGRH1gjp/YC8Hpn5p5MVENviySoKNF+wd98rRvOAokzrzElyDBHSTfX+C3tpg==", + "license": "MIT", "dependencies": { "unimodules-app-loader": "~4.6.0" }, @@ -9669,6 +10251,7 @@ "version": "0.16.2", "resolved": "https://registry.npmjs.org/expo-updates-interface/-/expo-updates-interface-0.16.2.tgz", "integrity": "sha512-929XBU70q5ELxkKADj1xL0UIm3HvhYhNAOZv5DSk7rrKvLo7QDdPyl+JVnwZm9LrkNbH4wuE2rLoKu1KMgZ+9A==", + "license": "MIT", "peerDependencies": { "expo": "*" } @@ -9677,19 +10260,22 @@ "version": "13.0.3", "resolved": "https://registry.npmjs.org/expo-web-browser/-/expo-web-browser-13.0.3.tgz", "integrity": "sha512-HXb7y82ApVJtqk8tManyudtTrCtx8xcUnVzmJECeHCB0SsWSQ+penVLZxJkcyATWoJOsFMnfVSVdrTcpKKGszQ==", + "license": "MIT", "peerDependencies": { "expo": "*" } }, "node_modules/exponential-backoff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", - "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz", + "integrity": "sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==", + "license": "Apache-2.0" }, "node_modules/ezly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ezly/-/ezly-1.3.0.tgz", "integrity": "sha512-kHW9+PDbAvv19kjbkXH2pUsOQ/mNVdMqTX6n3PA9yG2K7i5UHjusoN4mqARUD4oVXbDHBXcuRS/XCK7+lkBWMw==", + "license": "GPL-3.0-or-later", "dependencies": { "@literate.ink/utilities": "1.0.0-11713907881.1", "@noble/curves": "^1.6.0", @@ -9707,18 +10293,20 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -9728,6 +10316,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -9739,13 +10328,15 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-uri": { "version": "3.0.6", @@ -9760,33 +10351,32 @@ "type": "opencollective", "url": "https://opencollective.com/fastify" } - ] + ], + "license": "BSD-3-Clause" }, "node_modules/fast-xml-parser": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", - "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", + "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" - }, - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" } ], + "license": "MIT", "dependencies": { - "strnum": "^1.0.5" + "strnum": "^1.1.1" }, "bin": { "fxparser": "src/cli/cli.js" } }, "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "license": "ISC", "dependencies": { "reusify": "^1.0.4" } @@ -9795,6 +10385,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "license": "Apache-2.0", "dependencies": { "bser": "2.1.1" } @@ -9803,6 +10394,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/fbemitter/-/fbemitter-3.0.0.tgz", "integrity": "sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==", + "license": "BSD-3-Clause", "dependencies": { "fbjs": "^3.0.0" } @@ -9811,6 +10403,7 @@ "version": "3.0.5", "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz", "integrity": "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==", + "license": "MIT", "dependencies": { "cross-fetch": "^3.1.5", "fbjs-css-vars": "^1.0.0", @@ -9824,12 +10417,13 @@ "node_modules/fbjs-css-vars": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", - "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==" + "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==", + "license": "MIT" }, "node_modules/fbjs/node_modules/ua-parser-js": { - "version": "1.0.39", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.39.tgz", - "integrity": "sha512-k24RCVWlEcjkdOxYmVJgeD/0a1TiSpqLg+ZalVGV9lsnr4yqu0w7tX/x2xX6G4zpkgQnRf89lxuZ1wsbjXM8lw==", + "version": "1.0.40", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.40.tgz", + "integrity": "sha512-z6PJ8Lml+v3ichVojCiB8toQJBuwR42ySM4ezjXIqXK3M0HczmKQ3LF4rhU55PfD99KEEXQG6yb7iOMyvYuHew==", "funding": [ { "type": "opencollective", @@ -9844,6 +10438,7 @@ "url": "https://github.com/sponsors/faisalman" } ], + "license": "MIT", "bin": { "ua-parser-js": "script/cli.js" }, @@ -9852,12 +10447,13 @@ } }, "node_modules/fetch-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-3.0.1.tgz", - "integrity": "sha512-ZGXe8Y5Z/1FWqQ9q/CrJhkUD73DyBU9VF0hBQmEO/wPHe4A9PKTjplFDLeFX8aOsYypZUcX5Ji/eByn3VCVO3Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-3.1.0.tgz", + "integrity": "sha512-s/XhhreJpqH0ftkGVcQt8JE9bqk+zRn4jF5mPJXWZeQMCI5odV9K+wEWYbnzFPHgQZlvPSMjS4n4yawWE8RINw==", + "license": "Unlicense", "dependencies": { "set-cookie-parser": "^2.4.8", - "tough-cookie": "^4.0.0" + "tough-cookie": "^5.0.0" } }, "node_modules/fetch-retry": { @@ -9871,6 +10467,7 @@ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, + "license": "MIT", "dependencies": { "flat-cache": "^3.0.4" }, @@ -9882,6 +10479,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -9893,6 +10491,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -9901,6 +10500,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "license": "MIT", "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", @@ -9918,6 +10518,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -9925,12 +10526,14 @@ "node_modules/finalhandler/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/find-cache-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "license": "MIT", "dependencies": { "commondir": "^1.0.1", "make-dir": "^2.0.0", @@ -9944,6 +10547,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -9969,6 +10573,7 @@ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, + "license": "MIT", "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", @@ -9979,20 +10584,23 @@ } }, "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" }, "node_modules/flow-enums-runtime": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/flow-enums-runtime/-/flow-enums-runtime-0.0.6.tgz", - "integrity": "sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==" + "integrity": "sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==", + "license": "MIT" }, "node_modules/flow-parser": { - "version": "0.252.0", - "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.252.0.tgz", - "integrity": "sha512-z8hKPUjZ33VLn4HVntifqmEhmolUMopysnMNzazoDqo1GLUkBsreLNsxETlKJMPotUWStQnen6SGvUNe1j4Hlg==", + "version": "0.265.0", + "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.265.0.tgz", + "integrity": "sha512-C+bg/TZsDVlLMF14+q9P9FB2pjQSgWwYs0pkIMPE1FsZWS4A0kk1M28V6YphpxAPr3AISVRZ6VgpDepvCk6dGw==", + "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -10007,6 +10615,7 @@ "url": "https://github.com/sponsors/RubenVerborgh" } ], + "license": "MIT", "engines": { "node": ">=4.0" }, @@ -10019,23 +10628,31 @@ "node_modules/fontfaceobserver": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/fontfaceobserver/-/fontfaceobserver-2.3.0.tgz", - "integrity": "sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==" + "integrity": "sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==", + "license": "BSD-2-Clause" }, "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "license": "MIT", "dependencies": { - "is-callable": "^1.1.3" + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "license": "ISC", "dependencies": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { @@ -10058,12 +10675,14 @@ } }, "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" }, "engines": { @@ -10083,6 +10702,7 @@ "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -10091,6 +10711,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", @@ -10115,25 +10736,44 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" }, "engines": { "node": ">= 0.4" @@ -10146,6 +10786,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -10154,6 +10795,7 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -10162,20 +10804,27 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -10193,6 +10842,19 @@ "node": ">=4" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -10206,13 +10868,14 @@ } }, "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -10225,6 +10888,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/getenv/-/getenv-1.0.0.tgz", "integrity": "sha512-7yetJWqbS9sbn0vIfliPsFgoXMKn/YMF+Wuiog97x+urnSRRRZ7xB+uVkwGKzRgq9CDFfMQnE9ruL5DHv9c6Xg==", + "license": "MIT", "engines": { "node": ">=6" } @@ -10234,6 +10898,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -10254,6 +10919,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -10265,6 +10931,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -10274,6 +10941,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -10285,6 +10953,7 @@ "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "license": "MIT", "engines": { "node": ">=4" } @@ -10293,6 +10962,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "license": "MIT", "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" @@ -10308,6 +10978,7 @@ "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "license": "MIT", "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", @@ -10324,11 +10995,12 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -10337,13 +11009,15 @@ "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/graphql": { "version": "15.8.0", @@ -10370,9 +11044,13 @@ } }, "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -10381,6 +11059,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -10389,6 +11068,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" }, @@ -10397,9 +11077,13 @@ } }, "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -10408,9 +11092,10 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -10422,6 +11107,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" }, @@ -10436,6 +11122,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -10447,6 +11134,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", + "license": "MIT", "dependencies": { "capital-case": "^1.0.4", "tslib": "^2.0.3" @@ -10455,12 +11143,14 @@ "node_modules/hermes-estree": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.19.1.tgz", - "integrity": "sha512-daLGV3Q2MKk8w4evNMKwS8zBE/rcpA800nu1Q5kM08IKijoSnPe9Uo1iIxzPKRkn95IxxsgBMPeYHt3VG4ej2g==" + "integrity": "sha512-daLGV3Q2MKk8w4evNMKwS8zBE/rcpA800nu1Q5kM08IKijoSnPe9Uo1iIxzPKRkn95IxxsgBMPeYHt3VG4ej2g==", + "license": "MIT" }, "node_modules/hermes-parser": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.19.1.tgz", "integrity": "sha512-Vp+bXzxYJWrpEuJ/vXxUsLnt0+y4q9zyi4zUlkLqD8FKv4LjIfOvP69R/9Lty3dCyKh0E2BU7Eypqr63/rKT/A==", + "license": "MIT", "dependencies": { "hermes-estree": "0.19.1" } @@ -10469,6 +11159,7 @@ "version": "0.0.6", "resolved": "https://registry.npmjs.org/hermes-profile-transformer/-/hermes-profile-transformer-0.0.6.tgz", "integrity": "sha512-cnN7bQUm65UWOy6cbGcCcZ3rpwW8Q/j4OP5aWRhEry4Z2t2aR1cjrbp0BS+KiBN0smvP1caBgAuxutvyvJILzQ==", + "license": "MIT", "dependencies": { "source-map": "^0.7.3" }, @@ -10480,6 +11171,7 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", "dependencies": { "react-is": "^16.7.0" } @@ -10515,12 +11207,13 @@ "license": "ISC" }, "node_modules/html-dom-parser": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/html-dom-parser/-/html-dom-parser-5.0.10.tgz", - "integrity": "sha512-GwArYL3V3V8yU/mLKoFF7HlLBv80BZ2Ey1BzfVNRpAci0cEKhFHI/Qh8o8oyt3qlAMLlK250wsxLdYX4viedvg==", + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/html-dom-parser/-/html-dom-parser-5.0.13.tgz", + "integrity": "sha512-B7JonBuAfG32I7fDouUQEogBrz3jK9gAuN1r1AaXpED6dIhtg/JwiSRhjGL7aOJwRz3HU4efowCjQBaoXiREqg==", + "license": "MIT", "dependencies": { "domhandler": "5.0.3", - "htmlparser2": "9.1.0" + "htmlparser2": "10.0.0" } }, "node_modules/html-entities": { @@ -10536,21 +11229,23 @@ "type": "patreon", "url": "https://patreon.com/mdevils" } - ] + ], + "license": "MIT" }, "node_modules/html-react-parser": { - "version": "5.1.18", - "resolved": "https://registry.npmjs.org/html-react-parser/-/html-react-parser-5.1.18.tgz", - "integrity": "sha512-65BwC0zzrdeW96jB2FRr5f1ovBhRMpLPJNvwkY5kA8Ay5xdL9t/RH2/uUTM7p+cl5iM88i6dDk4LXtfMnRmaJQ==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/html-react-parser/-/html-react-parser-5.2.2.tgz", + "integrity": "sha512-yA5012CJGSFWYZsgYzfr6HXJgDap38/AEP4ra8Cw+WHIi2ZRDXRX/QVYdumRf1P8zKyScKd6YOrWYvVEiPfGKg==", + "license": "MIT", "dependencies": { "domhandler": "5.0.3", - "html-dom-parser": "5.0.10", + "html-dom-parser": "5.0.13", "react-property": "2.0.2", "style-to-js": "1.1.16" }, "peerDependencies": { - "@types/react": "0.14 || 15 || 16 || 17 || 18", - "react": "0.14 || 15 || 16 || 17 || 18" + "@types/react": "0.14 || 15 || 16 || 17 || 18 || 19", + "react": "0.14 || 15 || 16 || 17 || 18 || 19" }, "peerDependenciesMeta": { "@types/react": { @@ -10562,6 +11257,7 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz", "integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==", + "license": "MIT", "dependencies": { "@selderee/plugin-htmlparser2": "^0.11.0", "deepmerge": "^4.3.1", @@ -10584,6 +11280,7 @@ "url": "https://github.com/sponsors/fb55" } ], + "license": "MIT", "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", @@ -10595,6 +11292,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "license": "MIT", "dependencies": { "css-line-break": "^2.1.0", "text-segmentation": "^1.0.3" @@ -10604,9 +11302,9 @@ } }, "node_modules/htmlparser2": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", - "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", + "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==", "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", { @@ -10614,11 +11312,12 @@ "url": "https://github.com/sponsors/fb55" } ], + "license": "MIT", "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", - "domutils": "^3.1.0", - "entities": "^4.5.0" + "domutils": "^3.2.1", + "entities": "^6.0.0" } }, "node_modules/htmlparser2-without-node-native": { @@ -10698,10 +11397,23 @@ "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", "license": "BSD-2-Clause" }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz", + "integrity": "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", @@ -10717,6 +11429,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -10724,7 +11437,8 @@ "node_modules/https": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz", - "integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg==" + "integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg==", + "license": "ISC" }, "node_modules/https-proxy-agent": { "version": "5.0.1", @@ -10743,6 +11457,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "license": "Apache-2.0", "engines": { "node": ">=10.17.0" } @@ -10750,7 +11465,8 @@ "node_modules/ical-date-parser": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/ical-date-parser/-/ical-date-parser-4.0.0.tgz", - "integrity": "sha512-XRCK/FU1akC2ZaJOdKIeZI6BLLgzWUuE0pegSrrkEva89GOan5mNkLVqCU4EMhCJ9nkG5TLWdMXrVX1fNAkFzw==" + "integrity": "sha512-XRCK/FU1akC2ZaJOdKIeZI6BLLgzWUuE0pegSrrkEva89GOan5mNkLVqCU4EMhCJ9nkG5TLWdMXrVX1fNAkFzw==", + "license": "MIT" }, "node_modules/ieee754": { "version": "1.2.1", @@ -10769,20 +11485,23 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "BSD-3-Clause" }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/image-size": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.1.1.tgz", - "integrity": "sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.0.tgz", + "integrity": "sha512-4S8fwbO6w3GeCVN6OPtA9I5IGKkcDMPcKndtUlpJuCwu7JLjtj7JZpwqLuyY2nrmQT3AWsCJLSKPsc2mPBSl3w==", + "license": "MIT", "dependencies": { "queue": "6.0.2" }, @@ -10794,10 +11513,11 @@ } }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, + "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -10814,6 +11534,7 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -10822,6 +11543,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", "engines": { "node": ">=0.8.19" } @@ -10840,6 +11562,7 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -10848,7 +11571,8 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" }, "node_modules/ini": { "version": "1.3.8", @@ -10859,7 +11583,8 @@ "node_modules/inline-style-parser": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", - "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==" + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", + "license": "MIT" }, "node_modules/internal-ip": { "version": "4.3.0", @@ -10874,23 +11599,15 @@ "node": ">=6" } }, - "node_modules/internal-ip/node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -10900,6 +11617,7 @@ "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", "dependencies": { "loose-envify": "^1.0.0" } @@ -10914,20 +11632,23 @@ } }, "node_modules/ipaddr.js": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", - "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", "engines": { - "node": ">= 10" + "node": ">= 0.10" } }, "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -10939,15 +11660,20 @@ "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" }, "node_modules/is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", - "dev": true, + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -10957,23 +11683,28 @@ } }, "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "license": "MIT", "dependencies": { - "has-bigints": "^1.0.1" + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -10992,6 +11723,7 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -11000,9 +11732,9 @@ } }, "node_modules/is-core-module": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.0.tgz", - "integrity": "sha512-urTSINYfAYgcbLb0yDQ6egFm6h3Mo1DcF9EkyXSRjjzdHbsulg01qhwWuXdOoUBuTkbQ80KDboXa0vFJ+BDH+g==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "license": "MIT", "dependencies": { "hasown": "^2.0.2" @@ -11015,10 +11747,13 @@ } }, "node_modules/is-data-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "license": "MIT", "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" }, "engines": { @@ -11029,11 +11764,13 @@ } }, "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -11046,6 +11783,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -11054,6 +11792,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", "bin": { "is-docker": "cli.js" }, @@ -11068,17 +11807,21 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-finalizationregistry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", - "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", - "dev": true, + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -11088,17 +11831,21 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dev": true, + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -11111,6 +11858,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -11122,6 +11870,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "license": "MIT", "engines": { "node": ">=8" } @@ -11163,18 +11912,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -11186,16 +11924,19 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", "engines": { "node": ">=0.12.0" } }, "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -11217,6 +11958,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -11225,6 +11967,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "license": "MIT", "engines": { "node": ">=8" } @@ -11233,6 +11976,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "license": "MIT", "dependencies": { "isobject": "^3.0.1" }, @@ -11241,12 +11985,15 @@ } }, "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -11259,7 +12006,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -11268,11 +12015,12 @@ } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -11291,11 +12039,13 @@ } }, "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -11305,11 +12055,14 @@ } }, "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "license": "MIT", "dependencies": { - "has-symbols": "^1.0.2" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -11319,11 +12072,12 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "license": "MIT", "dependencies": { - "which-typed-array": "^1.1.14" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -11336,6 +12090,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -11359,7 +12114,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -11368,24 +12123,28 @@ } }, "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-weakset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", - "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", - "dev": true, + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4" + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -11398,6 +12157,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", "dependencies": { "is-docker": "^2.0.0" }, @@ -11406,34 +12166,39 @@ } }, "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" }, "node_modules/isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/iterator.prototype": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.3.tgz", - "integrity": "sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", "dev": true, + "license": "MIT", "dependencies": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -11458,6 +12223,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "license": "MIT", "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", @@ -11474,6 +12240,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -11490,6 +12257,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -11498,6 +12266,7 @@ "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -11506,6 +12275,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -11514,6 +12284,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", @@ -11533,6 +12304,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -11549,6 +12321,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -11557,6 +12330,7 @@ "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -11565,6 +12339,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -11576,6 +12351,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -11588,12 +12364,14 @@ "node_modules/jest-message-util/node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" }, "node_modules/jest-mock": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", @@ -11607,6 +12385,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -11623,6 +12402,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -11631,6 +12411,7 @@ "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -11639,6 +12420,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", @@ -11655,6 +12437,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -11671,6 +12454,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -11679,6 +12463,7 @@ "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -11687,6 +12472,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -11698,6 +12484,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", "camelcase": "^6.2.0", @@ -11714,6 +12501,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -11730,6 +12518,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -11738,6 +12527,7 @@ "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -11746,6 +12536,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -11757,6 +12548,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -11769,12 +12561,14 @@ "node_modules/jest-validate/node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" }, "node_modules/jest-worker": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "license": "MIT", "dependencies": { "@types/node": "*", "jest-util": "^29.7.0", @@ -11789,6 +12583,7 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -11802,12 +12597,14 @@ "node_modules/jimp-compact": { "version": "0.16.1", "resolved": "https://registry.npmjs.org/jimp-compact/-/jimp-compact-0.16.1.tgz", - "integrity": "sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==" + "integrity": "sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==", + "license": "MIT" }, "node_modules/joi": { "version": "17.13.3", "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "license": "BSD-3-Clause", "dependencies": { "@hapi/hoek": "^9.3.0", "@hapi/topo": "^5.1.0", @@ -11826,6 +12623,7 @@ "version": "4.15.9", "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" } @@ -11833,17 +12631,20 @@ "node_modules/js-base64": { "version": "3.7.7", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.7.tgz", - "integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==" + "integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==", + "license": "BSD-3-Clause" }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -11854,22 +12655,26 @@ "node_modules/jsbarcode": { "version": "3.11.6", "resolved": "https://registry.npmjs.org/jsbarcode/-/jsbarcode-3.11.6.tgz", - "integrity": "sha512-G5TKGyKY1zJo0ZQKFM1IIMfy0nF2rs92BLlCz+cU4/TazIc4ZH+X1GYeDRt7TKjrYqmPfTjwTBkU/QnQlsYiuA==" + "integrity": "sha512-G5TKGyKY1zJo0ZQKFM1IIMfy0nF2rs92BLlCz+cU4/TazIc4ZH+X1GYeDRt7TKjrYqmPfTjwTBkU/QnQlsYiuA==", + "license": "MIT" }, "node_modules/jsc-android": { "version": "250231.0.0", "resolved": "https://registry.npmjs.org/jsc-android/-/jsc-android-250231.0.0.tgz", - "integrity": "sha512-rS46PvsjYmdmuz1OAWXY/1kCYG7pnf1TBqeTiOJr1iDz7s5DLxxC9n/ZMknLDxzYzNVfI7R95MH10emSSG1Wuw==" + "integrity": "sha512-rS46PvsjYmdmuz1OAWXY/1kCYG7pnf1TBqeTiOJr1iDz7s5DLxxC9n/ZMknLDxzYzNVfI7R95MH10emSSG1Wuw==", + "license": "BSD-2-Clause" }, "node_modules/jsc-safe-url": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/jsc-safe-url/-/jsc-safe-url-0.2.4.tgz", - "integrity": "sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==" + "integrity": "sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==", + "license": "0BSD" }, "node_modules/jscodeshift": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.14.0.tgz", "integrity": "sha512-7eCC1knD7bLUPuSCwXsMZUH51O8jIcoVyKtI6P0XM0IVzlGjckPy3FIwQlorzbN0Sg79oK+RlohN32Mqf/lrYA==", + "license": "MIT", "dependencies": { "@babel/core": "^7.13.16", "@babel/parser": "^7.13.16", @@ -11899,9 +12704,10 @@ } }, "node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, @@ -11913,18 +12719,21 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "license": "MIT" }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-schema-deref-sync": { "version": "0.13.0", @@ -11960,18 +12769,21 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -11983,6 +12795,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/jsonapi-fractal/-/jsonapi-fractal-2.3.1.tgz", "integrity": "sha512-GYL67SfDhtvUWpy5uI4v84VheQxsWeR6Qo4V/uSDBquW1zTDi99Q8tCHs/iMMPHW9tlO3bxvXx/KV+eAWUnUbg==", + "license": "MIT", "dependencies": { "change-case": "^4.1.1" } @@ -11991,6 +12804,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "license": "MIT", "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -12000,6 +12814,7 @@ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "dev": true, + "license": "MIT", "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", @@ -12015,6 +12830,7 @@ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, + "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } @@ -12023,6 +12839,7 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -12031,6 +12848,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", "engines": { "node": ">=6" } @@ -12039,6 +12857,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz", "integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==", + "license": "MIT", "funding": { "url": "https://ko-fi.com/killymxi" } @@ -12047,6 +12866,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "license": "MIT", "engines": { "node": ">=6" } @@ -12056,6 +12876,7 @@ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -12068,6 +12889,7 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz", "integrity": "sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==", + "license": "Apache-2.0", "dependencies": { "debug": "^2.6.9", "marky": "^1.2.2" @@ -12077,6 +12899,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -12084,32 +12907,93 @@ "node_modules/lighthouse-logger/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/lightningcss": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.19.0.tgz", + "integrity": "sha512-yV5UR7og+Og7lQC+70DA7a8ta1uiOPnWPJfxa0wnxylev5qfo4P+4iMpzWAdYWOca4jdNQZii+bDL/l+4hUXIA==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^1.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.19.0", + "lightningcss-darwin-x64": "1.19.0", + "lightningcss-linux-arm-gnueabihf": "1.19.0", + "lightningcss-linux-arm64-gnu": "1.19.0", + "lightningcss-linux-arm64-musl": "1.19.0", + "lightningcss-linux-x64-gnu": "1.19.0", + "lightningcss-linux-x64-musl": "1.19.0", + "lightningcss-win32-x64-msvc": "1.19.0" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.19.0.tgz", + "integrity": "sha512-wIJmFtYX0rXHsXHSr4+sC5clwblEMji7HHQ4Ub1/CznVRxtCFha6JIt5JZaNf8vQrfdZnBxLLC6R8pC818jXqg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.19.0.tgz", + "integrity": "sha512-Lif1wD6P4poaw9c/4Uh2z+gmrWhw/HtXFoeZ3bEsv6Ia4tt8rOJBdkfVaUJ6VXmpKHALve+iTyP2+50xY1wKPw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/lightningcss": { + "node_modules/lightningcss-linux-arm-gnueabihf": { "version": "1.19.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.19.0.tgz", - "integrity": "sha512-yV5UR7og+Og7lQC+70DA7a8ta1uiOPnWPJfxa0wnxylev5qfo4P+4iMpzWAdYWOca4jdNQZii+bDL/l+4hUXIA==", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.19.0.tgz", + "integrity": "sha512-P15VXY5682mTXaiDtbnLYQflc8BYb774j2R84FgDLJTN6Qp0ZjWEFyN1SPqyfTj2B2TFjRHRUvQSSZ7qN4Weig==", + "cpu": [ + "arm" + ], "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^1.0.3" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-darwin-arm64": "1.19.0", - "lightningcss-darwin-x64": "1.19.0", - "lightningcss-linux-arm-gnueabihf": "1.19.0", - "lightningcss-linux-arm64-gnu": "1.19.0", - "lightningcss-linux-arm64-musl": "1.19.0", - "lightningcss-linux-x64-gnu": "1.19.0", - "lightningcss-linux-x64-musl": "1.19.0", - "lightningcss-win32-x64-msvc": "1.19.0" } }, "node_modules/lightningcss-linux-arm64-gnu": { @@ -12152,15 +13036,77 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.19.0.tgz", + "integrity": "sha512-0AFQKvVzXf9byrXUq9z0anMGLdZJS+XSDqidyijI5njIwj6MdbvX2UZK/c4FfNmeRa2N/8ngTffoIuOUit5eIQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.19.0.tgz", + "integrity": "sha512-SJoM8CLPt6ECCgSuWe+g0qo8dqQYVcPiW2s19dxkmSI5+Uu1GIRzyKA0b7QqmEXolA+oSJhQqCmJpzjY4CuZAg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.19.0.tgz", + "integrity": "sha512-C+VuUTeSUOAaBZZOPT7Etn/agx/MatzJzGRkeV+zEABmPuntv1zihncsi+AyGmjkkzq3wVedEy7h0/4S84mUtg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -12174,28 +13120,33 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" }, "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead." + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "license": "MIT" }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "license": "MIT" }, "node_modules/lodash.throttle": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==" + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "license": "MIT" }, "node_modules/log-symbols": { "version": "2.2.0", @@ -12284,6 +13235,7 @@ "version": "0.7.1", "resolved": "https://registry.npmjs.org/logkitty/-/logkitty-0.7.1.tgz", "integrity": "sha512-/3ER20CTTbahrCrpYfPn7Xavv9diBROZpoXGVZDWMw4b/X4uuUwAC0ki85tgsdMRONURyIJbcOvS94QsUBYPbQ==", + "license": "MIT", "dependencies": { "ansi-fragments": "^0.2.1", "dayjs": "^1.8.15", @@ -12297,6 +13249,7 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", "engines": { "node": ">=6" } @@ -12305,6 +13258,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -12315,6 +13269,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -12327,6 +13282,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -12338,6 +13294,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -12352,6 +13309,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -12363,6 +13321,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -12375,12 +13334,14 @@ "node_modules/logkitty/node_modules/y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" }, "node_modules/logkitty/node_modules/yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", @@ -12402,6 +13363,7 @@ "version": "18.1.3", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" @@ -12414,6 +13376,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -12425,6 +13388,7 @@ "version": "6.7.2", "resolved": "https://registry.npmjs.org/lottie-react-native/-/lottie-react-native-6.7.2.tgz", "integrity": "sha512-MZVx6N1EeO/EaSx8T44mJ0aHc5Mqee+xIfWwszni0oz8U2wlHdaWGjES44dHxaxgAp/0dRaFt3PkpZ6egTzcBg==", + "license": "Apache-2.0", "peerDependencies": { "@dotlottie/react-player": "^1.6.1", "@lottiefiles/react-lottie-player": "^3.5.3", @@ -12448,6 +13412,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "license": "MIT", "dependencies": { "tslib": "^2.0.3" } @@ -12456,6 +13421,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "license": "ISC", "dependencies": { "yallist": "^3.0.2" } @@ -12464,6 +13430,7 @@ "version": "0.378.0", "resolved": "https://registry.npmjs.org/lucide-react-native/-/lucide-react-native-0.378.0.tgz", "integrity": "sha512-Xvqxjc3N5040Ui6tZaSbpNnNjWXDa+nRzYct4rXd2mWX+g2qxKPpEHoqNumrpky9rhsIxD8w4BSbjdkpGQTMYw==", + "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0", "react-native": "*", @@ -12474,6 +13441,7 @@ "version": "3.5.0", "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", + "license": "MIT", "engines": { "node": ">=12" } @@ -12482,6 +13450,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "license": "MIT", "dependencies": { "pify": "^4.0.1", "semver": "^5.6.0" @@ -12494,6 +13463,7 @@ "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", "bin": { "semver": "bin/semver" } @@ -12502,6 +13472,7 @@ "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "license": "BSD-3-Clause", "dependencies": { "tmpl": "1.0.5" } @@ -12509,7 +13480,17 @@ "node_modules/marky": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/marky/-/marky-1.2.5.tgz", - "integrity": "sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q==" + "integrity": "sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q==", + "license": "Apache-2.0" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } }, "node_modules/md5": { "version": "2.3.0", @@ -12526,6 +13507,7 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/md5-file/-/md5-file-3.2.3.tgz", "integrity": "sha512-3Tkp1piAHaworfcCgH0jKbTvj1jWWFgbvh2cXaNCgHwyTCBxxvD1Y04rmfpvdPm1P4oXMOpm6+2H7sr7v9v8Fw==", + "license": "MIT", "dependencies": { "buffer-alloc": "^1.1.0" }, @@ -12545,12 +13527,14 @@ "node_modules/mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "license": "CC0-1.0" }, "node_modules/memoize-one": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", - "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", + "license": "MIT" }, "node_modules/memory-cache": { "version": "0.2.0", @@ -12562,6 +13546,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz", "integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==", + "license": "MIT", "dependencies": { "is-plain-obj": "^2.1.0" }, @@ -12572,12 +13557,14 @@ "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", "engines": { "node": ">= 8" } @@ -12586,6 +13573,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro/-/metro-0.80.12.tgz", "integrity": "sha512-1UsH5FzJd9quUsD1qY+zUG4JY3jo3YEMxbMYH9jT6NK3j4iORhlwTK8fYTfAUBhDKjgLfKjAh7aoazNE23oIRA==", + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", "@babel/core": "^7.20.0", @@ -12641,6 +13629,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.80.12.tgz", "integrity": "sha512-YZziRs0MgA3pzCkkvOoQRXjIoVjvrpi/yRlJnObyIvMP6lFdtyG4nUGIwGY9VXnBvxmXD6mPY2e+NSw6JAyiRg==", + "license": "MIT", "dependencies": { "@babel/core": "^7.20.0", "flow-enums-runtime": "^0.0.6", @@ -12654,12 +13643,14 @@ "node_modules/metro-babel-transformer/node_modules/hermes-estree": { "version": "0.23.1", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.23.1.tgz", - "integrity": "sha512-eT5MU3f5aVhTqsfIReZ6n41X5sYn4IdQL0nvz6yO+MMlPxw49aSARHLg/MSehQftyjnrE8X6bYregzSumqc6cg==" + "integrity": "sha512-eT5MU3f5aVhTqsfIReZ6n41X5sYn4IdQL0nvz6yO+MMlPxw49aSARHLg/MSehQftyjnrE8X6bYregzSumqc6cg==", + "license": "MIT" }, "node_modules/metro-babel-transformer/node_modules/hermes-parser": { "version": "0.23.1", "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.23.1.tgz", "integrity": "sha512-oxl5h2DkFW83hT4DAUJorpah8ou4yvmweUzLJmmr6YV2cezduCdlil1AvU/a/xSsAFo4WUcNA4GoV5Bvq6JffA==", + "license": "MIT", "dependencies": { "hermes-estree": "0.23.1" } @@ -12668,6 +13659,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.80.12.tgz", "integrity": "sha512-p5kNHh2KJ0pbQI/H7ZBPCEwkyNcSz7OUkslzsiIWBMPQGFJ/xArMwkV7I+GJcWh+b4m6zbLxE5fk6fqbVK1xGA==", + "license": "MIT", "dependencies": { "exponential-backoff": "^3.1.1", "flow-enums-runtime": "^0.0.6", @@ -12681,6 +13673,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.80.12.tgz", "integrity": "sha512-o4BspKnugg/pE45ei0LGHVuBJXwRgruW7oSFAeSZvBKA/sGr0UhOGY3uycOgWInnS3v5yTTfiBA9lHlNRhsvGA==", + "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6" }, @@ -12692,6 +13685,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.80.12.tgz", "integrity": "sha512-4rwOWwrhm62LjB12ytiuR5NgK1ZBNr24/He8mqCsC+HXZ+ATbrewLNztzbAZHtFsrxP4D4GLTGgh96pCpYLSAQ==", + "license": "MIT", "dependencies": { "connect": "^3.6.5", "cosmiconfig": "^5.0.5", @@ -12710,6 +13704,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.80.12.tgz", "integrity": "sha512-QqdJ/yAK+IpPs2HU/h5v2pKEdANBagSsc6DRSjnwSyJsCoHlmyJKCaCJ7KhWGx+N4OHxh37hoA8fc2CuZbx0Fw==", + "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6", "lodash.throttle": "^4.1.1", @@ -12723,6 +13718,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.80.12.tgz", "integrity": "sha512-sYdemWSlk66bWzW2wp79kcPMzwuG32x1ZF3otI0QZTmrnTaaTiGyhE66P1z6KR4n2Eu5QXiABa6EWbAQv0r8bw==", + "license": "MIT", "dependencies": { "anymatch": "^3.0.3", "debug": "^2.2.0", @@ -12747,6 +13743,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -12754,12 +13751,14 @@ "node_modules/metro-file-map/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/metro-minify-terser": { "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.80.12.tgz", "integrity": "sha512-muWzUw3y5k+9083ZoX9VaJLWEV2Jcgi+Oan0Mmb/fBNMPqP9xVDuy4pOMn/HOiGndgfh/MK7s4bsjkyLJKMnXQ==", + "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6", "terser": "^5.15.0" @@ -12772,6 +13771,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.80.12.tgz", "integrity": "sha512-PR24gYRZnYHM3xT9pg6BdbrGbM/Cu1TcyIFBVlAk7qDAuHkUNQ1nMzWumWs+kwSvtd9eZGzHoucGJpTUEeLZAw==", + "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6" }, @@ -12783,6 +13783,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.80.12.tgz", "integrity": "sha512-LIx7+92p5rpI0i6iB4S4GBvvLxStNt6fF0oPMaUd1Weku7jZdfkCZzmrtDD9CSQ6EPb0T9NUZoyXIxlBa3wOCw==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.0", "flow-enums-runtime": "^0.0.6" @@ -12795,6 +13796,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.80.12.tgz", "integrity": "sha512-o+AXmE7hpvM8r8MKsx7TI21/eerYYy2DCDkWfoBkv+jNkl61khvDHlQn0cXZa6lrcNZiZkl9oHSMcwLLIrFmpw==", + "license": "MIT", "dependencies": { "@babel/traverse": "^7.20.0", "@babel/types": "^7.20.0", @@ -12814,6 +13816,7 @@ "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -12822,6 +13825,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.80.12.tgz", "integrity": "sha512-/dIpNdHksXkGHZXARZpL7doUzHqSNxgQ8+kQGxwpJuHnDhGkENxB5PS2QBaTDdEcmyTMjS53CN1rl9n1gR6fmw==", + "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", @@ -12842,6 +13846,7 @@ "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -12850,6 +13855,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.80.12.tgz", "integrity": "sha512-WQWp00AcZvXuQdbjQbx1LzFR31IInlkCDYJNRs6gtEtAyhwpMMlL2KcHmdY+wjDO9RPcliZ+Xl1riOuBecVlPA==", + "license": "MIT", "dependencies": { "@babel/core": "^7.20.0", "@babel/generator": "^7.20.0", @@ -12866,6 +13872,7 @@ "version": "0.80.12", "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.80.12.tgz", "integrity": "sha512-KAPFN1y3eVqEbKLx1I8WOarHPqDMUa8WelWxaJCNKO/yHCP26zELeqTJvhsQup+8uwB6EYi/sp0b6TGoh6lOEA==", + "license": "MIT", "dependencies": { "@babel/core": "^7.20.0", "@babel/generator": "^7.20.0", @@ -12888,12 +13895,14 @@ "node_modules/metro/node_modules/ci-info": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "license": "MIT" }, "node_modules/metro/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -12901,12 +13910,14 @@ "node_modules/metro/node_modules/hermes-estree": { "version": "0.23.1", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.23.1.tgz", - "integrity": "sha512-eT5MU3f5aVhTqsfIReZ6n41X5sYn4IdQL0nvz6yO+MMlPxw49aSARHLg/MSehQftyjnrE8X6bYregzSumqc6cg==" + "integrity": "sha512-eT5MU3f5aVhTqsfIReZ6n41X5sYn4IdQL0nvz6yO+MMlPxw49aSARHLg/MSehQftyjnrE8X6bYregzSumqc6cg==", + "license": "MIT" }, "node_modules/metro/node_modules/hermes-parser": { "version": "0.23.1", "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.23.1.tgz", "integrity": "sha512-oxl5h2DkFW83hT4DAUJorpah8ou4yvmweUzLJmmr6YV2cezduCdlil1AvU/a/xSsAFo4WUcNA4GoV5Bvq6JffA==", + "license": "MIT", "dependencies": { "hermes-estree": "0.23.1" } @@ -12914,12 +13925,14 @@ "node_modules/metro/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/metro/node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -12928,6 +13941,7 @@ "version": "7.5.10", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", "engines": { "node": ">=8.3.0" }, @@ -12948,6 +13962,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -12960,6 +13975,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -12971,6 +13987,7 @@ "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -12982,6 +13999,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -12990,6 +14008,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -13010,6 +14029,7 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -13024,6 +14044,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -13144,6 +14165,7 @@ "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", "dependencies": { "minimist": "^1.2.6" }, @@ -13154,12 +14176,14 @@ "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "license": "MIT", "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", @@ -13167,9 +14191,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.9.tgz", + "integrity": "sha512-SppoicMGpZvbF1l3z4x7No3OlIjP7QJvC9XR7AhZr1kL133KHnKPztkKDc+Ir4aJ/1VhTySrtKhrsycmrMQfvg==", "funding": [ { "type": "github", @@ -13188,12 +14212,14 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -13201,7 +14227,8 @@ "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "license": "MIT" }, "node_modules/nested-error-stacks": { "version": "2.0.1", @@ -13219,6 +14246,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "license": "MIT", "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" @@ -13228,6 +14256,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/nocache/-/nocache-3.0.4.tgz", "integrity": "sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw==", + "license": "MIT", "engines": { "node": ">=12.0.0" } @@ -13235,12 +14264,14 @@ "node_modules/node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", - "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", + "license": "MIT" }, "node_modules/node-dir": { "version": "0.1.17", "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz", "integrity": "sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==", + "license": "MIT", "dependencies": { "minimatch": "^3.0.2" }, @@ -13252,6 +14283,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -13261,6 +14293,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -13272,6 +14305,7 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -13291,6 +14325,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { "node": ">= 6.13.0" } @@ -13298,17 +14333,20 @@ "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==" + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "license": "MIT" }, "node_modules/node-stream-zip": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.15.0.tgz", "integrity": "sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==", + "license": "MIT", "engines": { "node": ">=0.12.0" }, @@ -13321,6 +14359,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -13329,7 +14368,8 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/notifee/-/notifee-0.0.1.tgz", "integrity": "sha512-R5DEyfQnhBR717u66Zut+Jx5MAOS4iLNSVEdsbiVcSzPH9S1gjxHEUJNnLnUmRTbvz8L8sUzhgkNMrtgm55UHg==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info." + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "UNLICENSED" }, "node_modules/npm-package-arg": { "version": "7.0.0", @@ -13377,6 +14417,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0" }, @@ -13387,12 +14428,14 @@ "node_modules/nullthrows": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", - "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==" + "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", + "license": "MIT" }, "node_modules/ob1": { "version": "0.80.12", "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.80.12.tgz", "integrity": "sha512-VMArClVT6LkhUGpnuEoBuyjG9rzUyEzg4PDkav6wK1cLhOK02gPCYFxoiB4mqVnrMhDpIzJcrGNAMVi9P+hXrw==", + "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6" }, @@ -13404,6 +14447,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -13412,14 +14456,16 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "license": "MIT", "engines": { "node": ">= 6" } }, "node_modules/object-inspect": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", - "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -13431,18 +14477,22 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", "object-keys": "^1.1.1" }, "engines": { @@ -13457,6 +14507,7 @@ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -13471,6 +14522,7 @@ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -13485,12 +14537,14 @@ } }, "node_modules/object.values": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", - "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" }, @@ -13502,9 +14556,10 @@ } }, "node_modules/oidc-token-hash": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz", - "integrity": "sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.1.0.tgz", + "integrity": "sha512-y0W+X7Ppo7oZX6eovsRkuzcSM40Bicg2JEJkDJ4irIt1wsYAP5MLSNv+QAogO8xivMffw/9OvV3um1pxXgt1uA==", + "license": "MIT", "engines": { "node": "^10.13.0 || >=12.0.0" } @@ -13513,6 +14568,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -13524,6 +14580,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -13532,6 +14589,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", "dependencies": { "wrappy": "1" } @@ -13566,9 +14624,10 @@ } }, "node_modules/openid-client": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.0.tgz", - "integrity": "sha512-4GCCGZt1i2kTHpwvaC/sCpTpQqDnBzDzuJcJMbH+y1Q5qI8U8RBvoSh28svarXszZHR5BAMXbJPX1PGPRE3VOA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz", + "integrity": "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==", + "license": "MIT", "dependencies": { "jose": "^4.15.9", "lru-cache": "^6.0.0", @@ -13583,6 +14642,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -13593,13 +14653,15 @@ "node_modules/openid-client/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, + "license": "MIT", "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -13750,6 +14812,23 @@ "os-tmpdir": "^1.0.0" } }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -13763,6 +14842,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -13777,6 +14857,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -13806,6 +14887,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", "engines": { "node": ">=6" } @@ -13819,12 +14901,14 @@ "node_modules/pako": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", - "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "license": "(MIT AND Zlib)" }, "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "license": "MIT", "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -13835,6 +14919,7 @@ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -13846,6 +14931,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "license": "MIT", "dependencies": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" @@ -13858,6 +14944,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/parse-png/-/parse-png-2.1.0.tgz", "integrity": "sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ==", + "license": "MIT", "dependencies": { "pngjs": "^3.3.0" }, @@ -13869,6 +14956,7 @@ "version": "0.12.1", "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz", "integrity": "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==", + "license": "MIT", "dependencies": { "leac": "^0.6.0", "peberminta": "^0.9.0" @@ -13881,6 +14969,7 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -13889,6 +14978,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "license": "MIT", "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -13908,6 +14998,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", + "license": "MIT", "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -13917,12 +15008,14 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", "engines": { "node": ">=8" } @@ -13931,6 +15024,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -13939,6 +15033,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", "engines": { "node": ">=8" } @@ -13946,7 +15041,8 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" }, "node_modules/path-scurry": { "version": "1.11.1", @@ -13974,14 +15070,15 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/pawdirecte": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/pawdirecte/-/pawdirecte-1.7.1.tgz", - "integrity": "sha512-2gyR8BIl78ktLYDp1FHSkbBG93RTyaX/JgfU02w6xCvd+VxUkovcllZ7lJsg7tVMwn2EYJ4jbTdKuneTOgTRUA==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/pawdirecte/-/pawdirecte-1.8.0.tgz", + "integrity": "sha512-DXohBS59vWyZS+MWwaV9dPFTVJU3Qo2LBrD9OcnOMvrSeHvwIhp32xT8xVjAa8EWV6iITMaopsTv81iCFO+z5g==", "license": "GPL-3.0-or-later", "dependencies": { "@literate.ink/utilities": "1.0.0-11713907881.1", @@ -13995,6 +15092,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/pawnilim/-/pawnilim-0.2.0.tgz", "integrity": "sha512-RnI3erDGpkXjBNNPnQDWxRadkOkzbZZZ1vCx8KFm276nHp2BjhGUSY7617Ea3nYQvVBPT0LtV4cVfrkiGFhSJA==", + "license": "GPL-3.0", "engines": { "node": ">=18" } @@ -14003,6 +15101,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/pawnote/-/pawnote-1.4.1.tgz", "integrity": "sha512-jdFHfyZ7tIRJ03etegs/ExgW2F2KZDPbOq6atjs6Fi1DINl893QLfVIxLwADb+tGQbYifYwwOurv4QZQ1OJZ8Q==", + "license": "GPL-3.0-or-later", "dependencies": { "@literate.ink/utilities": "1.0.0-10641118381.1", "html-entities": "^2.5.2", @@ -14017,6 +15116,7 @@ "version": "1.0.0-10641118381.1", "resolved": "https://registry.npmjs.org/@literate.ink/utilities/-/utilities-1.0.0-10641118381.1.tgz", "integrity": "sha512-omhzgwfAjNXqIjt6dmWbU8pm6QfEvNYetykEXYk+vVaJ8oICglvkChxkC6FkF8zJKMM/lFrNHgD42obQ8lwolQ==", + "license": "MIT", "dependencies": { "set-cookie-parser": "^2.7.0" } @@ -14025,6 +15125,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/pawrd/-/pawrd-0.6.1.tgz", "integrity": "sha512-9/JwVtkwcRkS7Lwn79z98sVzP6dNPaIPnDbMBdtzdj7qMBbNKHuGoUXwsdHkdN57kpq1k7n8zSoUCtN0wxZyYA==", + "license": "GPL-3.0-or-later", "dependencies": { "@literate.ink/utilities": "1.0.0-10641118381.1", "node-forge": "^1.3.1" @@ -14037,6 +15138,7 @@ "version": "1.0.0-10641118381.1", "resolved": "https://registry.npmjs.org/@literate.ink/utilities/-/utilities-1.0.0-10641118381.1.tgz", "integrity": "sha512-omhzgwfAjNXqIjt6dmWbU8pm6QfEvNYetykEXYk+vVaJ8oICglvkChxkC6FkF8zJKMM/lFrNHgD42obQ8lwolQ==", + "license": "MIT", "dependencies": { "set-cookie-parser": "^2.7.0" } @@ -14045,6 +15147,7 @@ "version": "0.9.0", "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz", "integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==", + "license": "MIT", "funding": { "url": "https://ko-fi.com/killymxi" } @@ -14052,13 +15155,15 @@ "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -14070,6 +15175,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "license": "MIT", "engines": { "node": ">=6" } @@ -14078,6 +15184,7 @@ "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "license": "MIT", "engines": { "node": ">= 6" } @@ -14086,6 +15193,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "license": "MIT", "dependencies": { "find-up": "^3.0.0" }, @@ -14097,6 +15205,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "license": "MIT", "dependencies": { "locate-path": "^3.0.0" }, @@ -14108,6 +15217,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "license": "MIT", "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" @@ -14120,6 +15230,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -14134,6 +15245,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "license": "MIT", "dependencies": { "p-limit": "^2.0.0" }, @@ -14145,6 +15257,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "license": "MIT", "engines": { "node": ">=4" } @@ -14153,6 +15266,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", + "license": "MIT", "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", @@ -14166,6 +15280,7 @@ "version": "0.8.10", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "license": "MIT", "engines": { "node": ">=10.0.0" } @@ -14174,6 +15289,7 @@ "version": "15.1.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "license": "MIT", "engines": { "node": ">=8.0" } @@ -14182,14 +15298,16 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", + "license": "MIT", "engines": { "node": ">=4.0.0" } }, "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -14227,6 +15345,7 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8.0" } @@ -14247,6 +15366,7 @@ "version": "24.9.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "license": "MIT", "dependencies": { "@jest/types": "^24.9.0", "ansi-regex": "^4.0.0", @@ -14261,6 +15381,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "license": "MIT", "engines": { "node": ">=6" } @@ -14269,6 +15390,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -14280,6 +15402,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", "dependencies": { "color-name": "1.1.3" } @@ -14287,7 +15410,8 @@ "node_modules/pretty-format/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" }, "node_modules/proc-log": { "version": "4.2.0", @@ -14301,7 +15425,8 @@ "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" }, "node_modules/progress": { "version": "2.0.3", @@ -14316,6 +15441,7 @@ "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "license": "MIT", "dependencies": { "asap": "~2.0.3" } @@ -14324,6 +15450,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "license": "MIT", "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" @@ -14336,6 +15463,7 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -14345,15 +15473,8 @@ "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "node_modules/psl": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.10.0.tgz", - "integrity": "sha512-KSKHEbjAnpUuAUserOq0FxGXCUrzC3WniuSJhvdbs102rL55266ZcHBqLWOsG30spQMlPdpy7icATiAQehg/iA==", - "dependencies": { - "punycode": "^2.3.1" - } + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" }, "node_modules/pump": { "version": "3.0.2", @@ -14369,22 +15490,25 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/pvtsutils": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.5.tgz", - "integrity": "sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", + "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", + "license": "MIT", "dependencies": { - "tslib": "^2.6.1" + "tslib": "^2.8.1" } }, "node_modules/pvutils": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.3.tgz", "integrity": "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==", + "license": "MIT", "engines": { "node": ">=6.0.0" } @@ -14393,6 +15517,7 @@ "version": "1.5.4", "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "license": "MIT", "dependencies": { "dijkstrajs": "^1.0.1", "pngjs": "^5.0.0", @@ -14417,6 +15542,7 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", "engines": { "node": ">=6" } @@ -14425,6 +15551,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -14435,6 +15562,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -14447,6 +15575,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -14458,6 +15587,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -14472,6 +15602,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -14483,6 +15614,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "license": "MIT", "engines": { "node": ">=10.13.0" } @@ -14491,6 +15623,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -14503,12 +15636,14 @@ "node_modules/qrcode/node_modules/y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" }, "node_modules/qrcode/node_modules/yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", @@ -14530,6 +15665,7 @@ "version": "18.1.3", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" @@ -14542,6 +15678,7 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", + "license": "MIT", "dependencies": { "decode-uri-component": "^0.2.2", "filter-obj": "^1.1.0", @@ -14560,19 +15697,16 @@ "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz", "integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==", "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "license": "MIT", "engines": { "node": ">=0.4.x" } }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" - }, "node_modules/queue": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "license": "MIT", "dependencies": { "inherits": "~2.0.3" } @@ -14594,12 +15728,14 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -14632,6 +15768,7 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" }, @@ -14643,6 +15780,7 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-5.3.2.tgz", "integrity": "sha512-crr9HkVrDiJ0A4zot89oS0Cgv0Oa4OG1Em4jit3P3ZxZSKPMYyMjfwMqgcJna9o625g8oN87rBm8SWWrSTBZxg==", + "license": "MIT", "dependencies": { "shell-quote": "^1.6.1", "ws": "^7" @@ -14652,6 +15790,7 @@ "version": "7.5.10", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", "engines": { "node": ">=8.3.0" }, @@ -14672,6 +15811,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.4.tgz", "integrity": "sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -14682,24 +15822,26 @@ "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" }, "node_modules/react-native": { - "version": "0.74.6", - "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.74.6.tgz", - "integrity": "sha512-TZ8uLf+dH+nO5nFwjhMd4PqtraeNT5cXQ0ySAhq7qqbTBgalxO3UklsLFW3cTSedC+eLw6J3P3H62e3/MjpWNw==", + "version": "0.74.7", + "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.74.7.tgz", + "integrity": "sha512-7emqS5CcwFoIBNAtcFPhmFRKkDl6TI/Oe10QjAYEj0JJcN/7hyCGwnDTtpjnO4Ai5LRt8xKXhrUt8cKIQ5BQlQ==", + "license": "MIT", "dependencies": { "@jest/create-cache-key-function": "^29.6.3", "@react-native-community/cli": "13.6.9", "@react-native-community/cli-platform-android": "13.6.9", "@react-native-community/cli-platform-ios": "13.6.9", - "@react-native/assets-registry": "0.74.88", - "@react-native/codegen": "0.74.88", - "@react-native/community-cli-plugin": "0.74.88", - "@react-native/gradle-plugin": "0.74.88", - "@react-native/js-polyfills": "0.74.88", - "@react-native/normalize-colors": "0.74.88", - "@react-native/virtualized-lists": "0.74.88", + "@react-native/assets-registry": "0.74.89", + "@react-native/codegen": "0.74.89", + "@react-native/community-cli-plugin": "0.74.89", + "@react-native/gradle-plugin": "0.74.89", + "@react-native/js-polyfills": "0.74.89", + "@react-native/normalize-colors": "0.74.89", + "@react-native/virtualized-lists": "0.74.89", "abort-controller": "^3.0.0", "anser": "^1.4.9", "ansi-regex": "^5.0.0", @@ -14748,6 +15890,7 @@ "version": "0.0.15", "resolved": "https://registry.npmjs.org/react-native-barcode-svg/-/react-native-barcode-svg-0.0.15.tgz", "integrity": "sha512-OOCcwoM5QqXD22QP5/z/SLKjCvyaO0067BQCeHsrlMolp4Eo/uX4nvQZy1jMe0lXkeiS4CWYpakbVLlgVALEUg==", + "license": "MIT", "dependencies": { "jsbarcode": "^3.11.0", "prop-types": "^15.5.10" @@ -14762,6 +15905,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/react-native-draggable-flatlist/-/react-native-draggable-flatlist-4.0.1.tgz", "integrity": "sha512-ZO1QUTNx64KZfXGXeXcBfql67l38X7kBcJ3rxUVZzPHt5r035GnGzIC0F8rqSXp6zgnwgUYMfB6zQc5PKmPL9Q==", + "license": "MIT", "dependencies": { "@babel/preset-typescript": "^7.17.12" }, @@ -14772,18 +15916,20 @@ } }, "node_modules/react-native-draglist": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/react-native-draglist/-/react-native-draglist-3.6.2.tgz", - "integrity": "sha512-QGgA5ejlJkd9B5meDxvkhxiFSyDZ6K341U/xcx4KhkoglCEqRTPmw6uZ9bQjpBqlty/lksVpWKnetCOgFnTUYA==", + "version": "3.9.4", + "resolved": "https://registry.npmjs.org/react-native-draglist/-/react-native-draglist-3.9.4.tgz", + "integrity": "sha512-kujoocoVQ35r6P+dNFc7Ysi94+MpiCq4uE56TFMvVAwCDqa3c8iCxnCNGFVKUspguv8xboPypBcydtNb5JFuTQ==", + "license": "MIT", "peerDependencies": { "react": ">=17.0.1", "react-native": ">=0.64.0" } }, "node_modules/react-native-gesture-handler": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.22.1.tgz", - "integrity": "sha512-E0C9D+Ia2UZYevoSV9rTKjhFWEVdR/3l4Z3TUoQrI/wewgzDlmJOrYvGW5aMlPUuQF2vHQOdFfAWhVEqFu4tWw==", + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.24.0.tgz", + "integrity": "sha512-ZdWyOd1C8axKJHIfYxjJKCcxjWEpUtUWgTOVY2wynbiveSQDm8X/PDyAKXSer/GOtIpjudUbACOndZXCN3vHsw==", + "license": "MIT", "dependencies": { "@egjs/hammerjs": "^2.0.17", "hoist-non-react-statics": "^3.3.0", @@ -14815,6 +15961,7 @@ "version": "0.3.18", "resolved": "https://registry.npmjs.org/react-native-infinite-pager/-/react-native-infinite-pager-0.3.18.tgz", "integrity": "sha512-pt9r/s6FFZsqAX6GDTISvR/T5Qh/Jec94v685p1pMYXUQKgD4+lWiznx2lCRGf/hkCvy51CWLYLs6GdKFPotbw==", + "license": "MIT", "peerDependencies": { "react-native": ">=0.62.0", "react-native-gesture-handler": ">=2.0.0", @@ -14825,6 +15972,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/react-native-ios-context-menu/-/react-native-ios-context-menu-3.1.0.tgz", "integrity": "sha512-qdPSXMKUp5lDgmZeUPdv5sgBFhkFrIqma+zsnqJQYOvekb6Qs17yJy1Rqhrj0bJrwuduHzZX0aYbaA8whxqpDw==", + "license": "MIT", "workspaces": [ "example", "example-expo" @@ -14839,9 +15987,10 @@ } }, "node_modules/react-native-ios-utilities": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/react-native-ios-utilities/-/react-native-ios-utilities-5.1.1.tgz", - "integrity": "sha512-fOm7IR2KCn3MzghITbrnZfpJ3Z7wai4S46GwXwTql1fzX25eO8MXVgaUeMd5EvPwg1zAqF5I6c3T6Dby8DoF3A==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/react-native-ios-utilities/-/react-native-ios-utilities-5.1.2.tgz", + "integrity": "sha512-H566MdgC1x0vW6D8EKweno1yRj/gQHeWiK0cRmyRexnUSk7oIecy7l2uyxXLYWFs+yvB/YIdLm+s2c19lKGfaw==", + "license": "MIT", "workspaces": [ "example" ], @@ -14854,6 +16003,7 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/react-native-ios-visual-effect-view/-/react-native-ios-visual-effect-view-0.1.2.tgz", "integrity": "sha512-6fjeF+BO3rev+U3/K2CNwzkJmeaOiBNfiL7qSUftjs1oSgPIHQ+4WS6HvHdAVgAds76F1OgDWViQJrCYyVnHww==", + "license": "MIT", "workspaces": [ "example" ], @@ -14867,6 +16017,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/react-native-pager-view/-/react-native-pager-view-6.3.0.tgz", "integrity": "sha512-ufJOoVa9pFL1J/yb4hpsCqp8n1qTlcF5VvwqvCacHX//D7hSeRscsiIXg1u1pXNWwllvACb+mqxec/3Uj2mxrA==", + "license": "MIT", "peerDependencies": { "react": "*", "react-native": "*" @@ -14876,6 +16027,7 @@ "version": "0.0.22", "resolved": "https://registry.npmjs.org/react-native-parsed-text/-/react-native-parsed-text-0.0.22.tgz", "integrity": "sha512-hfD83RDXZf9Fvth3DowR7j65fMnlqM9PpxZBGWkzVcUTFtqe6/yPcIoIAgrJbKn6YmtzkivmhWE2MCE4JKBXrQ==", + "license": "MIT", "dependencies": { "prop-types": "^15.7.x" }, @@ -14888,6 +16040,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/react-native-pressable-scale/-/react-native-pressable-scale-2.1.0.tgz", "integrity": "sha512-YfdnzxphHi/2eir3MRcdF5xQpBhQkqvwubk9iwnW8EucxBQeYU8XRLJUqIhsKg6JFcp3inqzYw96+WwR/U8+0w==", + "license": "MIT", "peerDependencies": { "react": "*", "react-native": ">= 0.63", @@ -14896,24 +16049,26 @@ } }, "node_modules/react-native-qrcode-svg": { - "version": "6.3.12", - "resolved": "https://registry.npmjs.org/react-native-qrcode-svg/-/react-native-qrcode-svg-6.3.12.tgz", - "integrity": "sha512-7Bx23ZdFNJJdVXyW9BJmFWdI5kccjnpotzmL3exkV0irUKTmj51jesxpn5sqtgVdYFE4IUVoGzdS+8qg6Ua9BA==", + "version": "6.3.15", + "resolved": "https://registry.npmjs.org/react-native-qrcode-svg/-/react-native-qrcode-svg-6.3.15.tgz", + "integrity": "sha512-vLuNImGfstE8u+rlF4JfFpq65nPhmByuDG6XUPWh8yp8MgLQX11rN5eQ8nb/bf4OB+V8XoLTJB/AZF2g7jQSSQ==", + "license": "MIT", "dependencies": { "prop-types": "^15.8.0", - "qrcode": "^1.5.1", + "qrcode": "^1.5.4", "text-encoding": "^0.7.0" }, "peerDependencies": { "react": "*", "react-native": ">=0.63.4", - "react-native-svg": ">=13.2.0" + "react-native-svg": ">=14.0.0" } }, "node_modules/react-native-reanimated": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.10.1.tgz", "integrity": "sha512-sfxg6vYphrDc/g4jf/7iJ7NRi+26z2+BszPmvmk0Vnrz6FL7HYljJqTf531F1x6tFmsf+FEAmuCtTUIXFLVo9w==", + "license": "MIT", "dependencies": { "@babel/plugin-transform-arrow-functions": "^7.0.0-0", "@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0", @@ -14934,15 +16089,17 @@ "version": "4.10.5", "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.10.5.tgz", "integrity": "sha512-Wyb0Nqw2XJ6oZxW/cK8k5q7/UAhg/wbEG6UVf89rQqecDZTDA5ic//P9J6VvJRVZerzGmxWQpVuM7f+PRYUM4g==", + "license": "MIT", "peerDependencies": { "react": "*", "react-native": "*" } }, "node_modules/react-native-screens": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.9.1.tgz", - "integrity": "sha512-3pIOu1YXTDClQfkgk2t7rIr7unrV6bviaXRJsOq1APNHLeCPrJ6m5Gy3pW5Ty+XBm9jRAbMqhpjiXZIjesOR6A==", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.9.2.tgz", + "integrity": "sha512-87gR7XRIirACYxtYltEl1BbUo5r0W4AFPckUDDzATAN6LIUZ2PC3bX6UAFeoEBEqBbfaemRZTWSYHl6MCZFSgw==", + "license": "MIT", "dependencies": { "react-freeze": "^1.0.0", "warn-once": "^0.1.0" @@ -14953,18 +16110,19 @@ } }, "node_modules/react-native-share": { - "version": "12.0.3", - "resolved": "https://registry.npmjs.org/react-native-share/-/react-native-share-12.0.3.tgz", - "integrity": "sha512-Bg96AjouSbcpdlI/AzWXMBwpjyWcm4NvGr49mSF1cz8aw8E1sMXwpsHa6I841SJML8Im6sRN3aXnK+/QManrtQ==", + "version": "12.0.9", + "resolved": "https://registry.npmjs.org/react-native-share/-/react-native-share-12.0.9.tgz", + "integrity": "sha512-vSuz/9aF+/AZS3I5NC19MrB56h1Yivk2Yz8lf2d8Szv3KuRw2BnDI/AfCTjMWByJLVYr6xgzfkTkAfvbDGzxLQ==", "license": "MIT", "engines": { "node": ">=16" } }, "node_modules/react-native-svg": { - "version": "15.8.0", - "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.8.0.tgz", - "integrity": "sha512-KHJzKpgOjwj1qeZzsBjxNdoIgv2zNCO9fVcoq2TEhTRsVV5DGTZ9JzUZwybd7q4giT/H3RdtqC3u44dWdO0Ffw==", + "version": "15.11.2", + "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.11.2.tgz", + "integrity": "sha512-+YfF72IbWQUKzCIydlijV1fLuBsQNGMT6Da2kFlo1sh+LE3BIm/2Q7AR1zAAR6L0BFLi1WaQPLfFUC9bNZpOmw==", + "license": "MIT", "dependencies": { "css-select": "^5.1.0", "css-tree": "^1.1.3", @@ -14980,6 +16138,7 @@ "resolved": "https://registry.npmjs.org/react-native-svg-transformer/-/react-native-svg-transformer-1.5.0.tgz", "integrity": "sha512-RG5fSWJT7mjCQYocgYFUo1KYPLOoypPVG5LQab+pZZO7m4ciGaQIe0mhok3W4R5jLQsEXKo0u+aQGkZV/bZG7w==", "dev": true, + "license": "MIT", "dependencies": { "@svgr/core": "^8.1.0", "@svgr/plugin-jsx": "^8.1.0", @@ -14995,6 +16154,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/react-native-url-polyfill/-/react-native-url-polyfill-2.0.0.tgz", "integrity": "sha512-My330Do7/DvKnEvwQc0WdcBnFPploYKp9CYlefDXzIdEaA+PAhDYllkvGeEroEzvc4Kzzj2O4yVdz8v6fjRvhA==", + "license": "MIT", "dependencies": { "whatwg-url-without-unicode": "8.0.0-3" }, @@ -15006,6 +16166,7 @@ "version": "3.8.0", "resolved": "https://registry.npmjs.org/react-native-view-shot/-/react-native-view-shot-3.8.0.tgz", "integrity": "sha512-4cU8SOhMn3YQIrskh+5Q8VvVRxQOu8/s1M9NAL4z5BY1Rm0HXMWkQJ4N0XsZ42+Yca+y86ISF3LC5qdLPvPuiA==", + "license": "MIT", "dependencies": { "html2canvas": "^1.4.1" }, @@ -15018,6 +16179,7 @@ "version": "13.8.6", "resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.8.6.tgz", "integrity": "sha512-jtZ9OgB2AN6rhDwto6dNL3PtOtl/SI4VN93pZEPbMLvRjqHfxiUrilGllL5fKAXq5Ry5FJyfUi82A4Ii8olZ7A==", + "license": "MIT", "dependencies": { "escape-string-regexp": "2.0.0", "invariant": "2.2.4" @@ -15031,6 +16193,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "license": "MIT", "engines": { "node": ">=8" } @@ -15039,6 +16202,7 @@ "version": "26.6.2", "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", @@ -15051,9 +16215,10 @@ } }, "node_modules/react-native/node_modules/@react-native/codegen": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.74.88.tgz", - "integrity": "sha512-HMk/LCrSdUof9DZFaB2bK0soKyAF6XiCg2LG7WFjEkUDXayeiB4p7IsHISJWY4bYg7cMPZ0fiZMRaBP2vXJxgg==", + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.74.89.tgz", + "integrity": "sha512-xbcpnvsAjHrnYWnuoLdr5782dlR4LfkaWPityka/gWmdRDrE69ByAC9m0/vijMXAcMoHv6DSMh5x7gm6gW/3mA==", + "license": "MIT", "dependencies": { "@babel/parser": "^7.20.0", "glob": "^7.1.1", @@ -15072,14 +16237,39 @@ } }, "node_modules/react-native/node_modules/@react-native/normalize-colors": { - "version": "0.74.88", - "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.88.tgz", - "integrity": "sha512-He5oTwPBxvXrxJ91dZzpxR7P+VYmc9IkJfhuH8zUiU50ckrt+xWNjtVugPdUv4LuVjmZ36Vk2EX8bl1gVn2dVA==" + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.89.tgz", + "integrity": "sha512-qoMMXddVKVhZ8PA1AbUCk83trpd6N+1nF2A6k1i6LsQObyS92fELuk8kU/lQs6M7BsMHwqyLCpQJ1uFgNvIQXg==", + "license": "MIT" + }, + "node_modules/react-native/node_modules/@react-native/virtualized-lists": { + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.74.89.tgz", + "integrity": "sha512-E1KU/owsHRGnWVXKHgFIfAcf9NzxoDKFLoxdNaMRGXJH4i/qwT3ouENj2LW1BPL57W1G/8rj3kgn0xPW/YeI3A==", + "license": "MIT", + "dependencies": { + "invariant": "^2.2.4", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/react": "^18.2.6", + "react": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } }, "node_modules/react-native/node_modules/@types/istanbul-reports": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -15088,6 +16278,7 @@ "version": "15.0.19", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.19.tgz", "integrity": "sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -15096,6 +16287,7 @@ "version": "26.6.2", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "license": "MIT", "dependencies": { "@jest/types": "^26.6.2", "ansi-regex": "^5.0.0", @@ -15110,6 +16302,7 @@ "version": "8.3.0", "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "license": "MIT", "dependencies": { "asap": "~2.0.6" } @@ -15117,17 +16310,20 @@ "node_modules/react-native/node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "license": "MIT" }, "node_modules/react-native/node_modules/regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT" }, "node_modules/react-native/node_modules/ws": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "license": "MIT", "dependencies": { "async-limiter": "~1.0.0" } @@ -15135,12 +16331,14 @@ "node_modules/react-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/react-property/-/react-property-2.0.2.tgz", - "integrity": "sha512-+PbtI3VuDV0l6CleQMsx2gtK0JZbZKbpdu5ynr+lbsuvtmgbNcS3VM0tuY2QjFNOcWxvXeHjDpy42RO+4U2rug==" + "integrity": "sha512-+PbtI3VuDV0l6CleQMsx2gtK0JZbZKbpdu5ynr+lbsuvtmgbNcS3VM0tuY2QjFNOcWxvXeHjDpy42RO+4U2rug==", + "license": "MIT" }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -15149,6 +16347,7 @@ "version": "16.15.0", "resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz", "integrity": "sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==", + "license": "MIT", "dependencies": { "object-assign": "^4.1.1", "react-is": "^16.12.0 || ^17.0.0 || ^18.0.0" @@ -15161,6 +16360,7 @@ "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -15171,25 +16371,23 @@ "util-deprecate": "~1.0.1" } }, - "node_modules/readable-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, "node_modules/readable-stream/node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" }, "node_modules/readline": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz", - "integrity": "sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg==" + "integrity": "sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg==", + "license": "BSD" }, "node_modules/reanimated-color-picker": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/reanimated-color-picker/-/reanimated-color-picker-3.0.4.tgz", - "integrity": "sha512-p4ZBx73gHs844CEO+WJ+oVvBFZnydMxabQlGzkd2NMVqWFTEVTPYGNmCla3nj0r/L6gwaGnoeFlWO5vhpG7G3g==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/reanimated-color-picker/-/reanimated-color-picker-3.0.6.tgz", + "integrity": "sha512-ILmeR57d/DbWDy1vPdgz9vkRJyWHMIeTWEtxUgXPEH/BsiSXXhLju+hFDZY3M+bZUwpoCJWcvaAZl7p9kVwiEQ==", + "license": "MIT", "peerDependencies": { "expo": ">=44.0.0", "react": "*", @@ -15207,6 +16405,7 @@ "version": "0.21.5", "resolved": "https://registry.npmjs.org/recast/-/recast-0.21.5.tgz", "integrity": "sha512-hjMmLaUXAm1hIuTqOdeYObMslq/q+Xff6QE3Y2P+uoHAg2nmVlLBps2hzh1UJDdMtDTMXOFewK6ky51JQIeECg==", + "license": "MIT", "dependencies": { "ast-types": "0.15.2", "esprima": "~4.0.0", @@ -15221,23 +16420,25 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/reflect.getprototypeof": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", - "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", - "dev": true, + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.23.1", + "es-abstract": "^1.23.9", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -15249,12 +16450,14 @@ "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "license": "MIT" }, "node_modules/regenerate-unicode-properties": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "license": "MIT", "dependencies": { "regenerate": "^1.4.2" }, @@ -15265,25 +16468,30 @@ "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" }, "node_modules/regenerator-transform": { "version": "0.15.2", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "license": "MIT", "peer": true, "dependencies": { "@babel/runtime": "^7.8.4" } }, "node_modules/regexp.prototype.flags": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", - "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", "set-function-name": "^2.0.2" }, "engines": { @@ -15294,14 +16502,15 @@ } }, "node_modules/regexpu-core": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz", - "integrity": "sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", + "license": "MIT", "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.0", "regjsgen": "^0.8.0", - "regjsparser": "^0.11.0", + "regjsparser": "^0.12.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" }, @@ -15312,12 +16521,14 @@ "node_modules/regjsgen": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==" + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "license": "MIT" }, "node_modules/regjsparser": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.2.tgz", - "integrity": "sha512-3OGZZ4HoLJkkAZx/48mTXJNlmqTGOzc0o9OWQPuWpkOlXXPbyN6OafCcoXUnBqE2D3f/T5L+pWc1kdEmnfnRsA==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "license": "BSD-2-Clause", "dependencies": { "jsesc": "~3.0.2" }, @@ -15325,6 +16536,18 @@ "regjsparser": "bin/parser" } }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/remove-trailing-slash": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz", @@ -15335,6 +16558,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -15343,6 +16567,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -15350,7 +16575,8 @@ "node_modules/require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" }, "node_modules/requireg": { "version": "0.2.2", @@ -15374,16 +16600,12 @@ "path-parse": "^1.0.5" } }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" - }, "node_modules/resolve": { "version": "2.0.0-next.5", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, + "license": "MIT", "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -15400,6 +16622,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "license": "MIT", "engines": { "node": ">=8" } @@ -15433,9 +16656,10 @@ } }, "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -15446,6 +16670,7 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", "dependencies": { "glob": "^7.1.3" }, @@ -15460,6 +16685,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/rrule/-/rrule-2.8.1.tgz", "integrity": "sha512-hM3dHSBMeaJ0Ktp7W38BJZ7O1zOgaFEsn41PDk+yHoEtfLV+PoJt9E9xAlZiWgf/iqEqionN0ebHFZIDAp+iGw==", + "license": "BSD-3-Clause", "dependencies": { "tslib": "^2.4.0" } @@ -15482,18 +16708,21 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } }, "node_modules/safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", "isarray": "^2.0.5" }, "engines": { @@ -15503,6 +16732,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -15520,16 +16755,40 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" }, "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", - "is-regex": "^1.1.4" + "is-regex": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -15541,12 +16800,14 @@ "node_modules/sax": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "license": "ISC" }, "node_modules/scheduler": { "version": "0.24.0-canary-efb381bbf-20230505", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.24.0-canary-efb381bbf-20230505.tgz", "integrity": "sha512-ABvovCDe/k9IluqSh4/ISoq8tIJnW8euVAWYt5j/bg6dRnqwQwiGO1F/V4AyK96NGF/FB04FhOUDuWj8IKfABA==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" } @@ -15555,6 +16816,7 @@ "version": "3.0.5", "resolved": "https://registry.npmjs.org/scolengo-api/-/scolengo-api-3.0.5.tgz", "integrity": "sha512-AOAXsyP4xZCUp0uUfQSqSi2DPMXU03gxzm5faJ6XQRbK54QOlqiAlGRG2TiOqK9YqDgdG/pcEClY+NxbFzGX1A==", + "license": "GPL-3.0-or-later", "dependencies": { "axios": "^1.3.5", "base-64": "^1.0.0", @@ -15568,6 +16830,7 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz", "integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==", + "license": "MIT", "dependencies": { "parseley": "^0.12.0" }, @@ -15579,6 +16842,7 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "license": "MIT", "dependencies": { "@types/node-forge": "^1.3.0", "node-forge": "^1" @@ -15591,13 +16855,14 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "license": "MIT", "dependencies": { @@ -15671,6 +16936,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", + "license": "MIT", "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", @@ -15681,6 +16947,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz", "integrity": "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -15689,6 +16956,7 @@ "version": "1.16.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", @@ -15703,6 +16971,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -15710,12 +16979,14 @@ "node_modules/serve-static/node_modules/debug/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/serve-static/node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -15724,6 +16995,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -15735,6 +17007,7 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -15746,6 +17019,7 @@ "version": "0.19.0", "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -15769,6 +17043,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -15777,6 +17052,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -15784,17 +17060,20 @@ "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" }, "node_modules/set-cookie-parser": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", - "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==" + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -15811,6 +17090,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -15821,20 +17101,37 @@ "node": ">= 0.4" } }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" }, "node_modules/shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "license": "MIT", "dependencies": { "kind-of": "^6.0.2" }, @@ -15846,38 +17143,99 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, - "engines": { - "node": ">=8" + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", + "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, "engines": { - "node": ">=8" - } - }, - "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -15889,12 +17247,14 @@ "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" }, "node_modules/simple-plist": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/simple-plist/-/simple-plist-1.3.1.tgz", "integrity": "sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw==", + "license": "MIT", "dependencies": { "bplist-creator": "0.1.0", "bplist-parser": "0.3.1", @@ -15905,6 +17265,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz", "integrity": "sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==", + "license": "MIT", "dependencies": { "stream-buffers": "2.2.x" } @@ -15913,6 +17274,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.1.tgz", "integrity": "sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA==", + "license": "MIT", "dependencies": { "big-integer": "1.6.x" }, @@ -15924,6 +17286,7 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "license": "MIT", "dependencies": { "is-arrayish": "^0.3.1" } @@ -15931,17 +17294,20 @@ "node_modules/simple-swizzle/node_modules/is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "license": "MIT" }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "license": "MIT", "engines": { "node": ">=8" } @@ -15950,6 +17316,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "license": "MIT", "dependencies": { "ansi-styles": "^3.2.0", "astral-regex": "^1.0.0", @@ -15963,6 +17330,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -15974,6 +17342,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", "dependencies": { "color-name": "1.1.3" } @@ -15981,12 +17350,14 @@ "node_modules/slice-ansi/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" }, "node_modules/slugify": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz", "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==", + "license": "MIT", "engines": { "node": ">=8.0.0" } @@ -15995,6 +17366,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "license": "MIT", "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -16004,6 +17376,7 @@ "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "license": "BSD-3-Clause", "engines": { "node": ">= 8" } @@ -16012,6 +17385,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -16020,6 +17394,7 @@ "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -16029,6 +17404,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -16049,6 +17425,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "license": "MIT", "engines": { "node": ">=6" } @@ -16056,7 +17433,8 @@ "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" }, "node_modules/ssri": { "version": "10.0.6", @@ -16074,6 +17452,7 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "license": "MIT", "dependencies": { "escape-string-regexp": "^2.0.0" }, @@ -16085,6 +17464,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "license": "MIT", "engines": { "node": ">=8" } @@ -16092,12 +17472,14 @@ "node_modules/stackframe": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", - "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "license": "MIT" }, "node_modules/stacktrace-parser": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", - "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==", + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz", + "integrity": "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==", + "license": "MIT", "dependencies": { "type-fest": "^0.7.1" }, @@ -16109,6 +17491,7 @@ "version": "0.7.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=8" } @@ -16117,6 +17500,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -16125,6 +17509,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", "integrity": "sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==", + "license": "Unlicense", "engines": { "node": ">= 0.10.0" } @@ -16133,6 +17518,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "license": "MIT", "engines": { "node": ">=4" } @@ -16141,6 +17527,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", "dependencies": { "safe-buffer": "~5.1.0" } @@ -16148,12 +17535,14 @@ "node_modules/string_decoder/node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -16191,28 +17580,31 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/string.prototype.matchall": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", - "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", + "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "regexp.prototype.flags": "^1.5.2", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -16226,20 +17618,25 @@ "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", "dev": true, + "license": "MIT", "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" } }, "node_modules/string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -16249,14 +17646,19 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -16265,6 +17667,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -16281,6 +17684,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -16314,6 +17718,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "license": "MIT", "engines": { "node": ">=6" } @@ -16323,6 +17728,7 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -16331,9 +17737,16 @@ } }, "node_modules/strnum": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" }, "node_modules/structured-headers": { "version": "0.4.1", @@ -16345,6 +17758,7 @@ "version": "1.1.16", "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.16.tgz", "integrity": "sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw==", + "license": "MIT", "dependencies": { "style-to-object": "1.0.8" } @@ -16353,6 +17767,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz", "integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==", + "license": "MIT", "dependencies": { "inline-style-parser": "0.2.4" } @@ -16361,6 +17776,7 @@ "version": "3.34.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", "integrity": "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==", + "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", @@ -16382,6 +17798,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -16391,6 +17808,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "license": "MIT", "engines": { "node": ">= 6" } @@ -16400,6 +17818,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -16419,6 +17838,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -16430,12 +17850,14 @@ "version": "8.2.5", "resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-8.2.5.tgz", "integrity": "sha512-rlBo3HU/1zAJUrkY6jNxDOC9eVYliG6nS4JA8u8KAshITd07tafMc/Br7xQwCSseXwJ2iCcHCE8SNWX3q8Z+kw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "license": "MIT" }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -16460,6 +17882,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -16471,13 +17894,15 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/svgo": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", "dev": true, + "license": "MIT", "dependencies": { "@trysound/sax": "0.2.0", "commander": "^7.2.0", @@ -16503,6 +17928,7 @@ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", "dev": true, + "license": "MIT", "dependencies": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" @@ -16515,12 +17941,14 @@ "version": "2.0.30", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "dev": true + "dev": true, + "license": "CC0-1.0" }, "node_modules/swiftui-react-native": { "version": "6.3.3", "resolved": "https://registry.npmjs.org/swiftui-react-native/-/swiftui-react-native-6.3.3.tgz", "integrity": "sha512-/3CygnDsGkXFat+1LRujW1a7WSw/KOCl62CbGTOFLWQNOE3uGM0tylNO4PuiUDilxDqc/krOkjGORbplNfZYxw==", + "license": "MIT", "peerDependencies": { "expo": "*", "react": "*", @@ -16599,6 +18027,7 @@ "version": "0.8.4", "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.4.tgz", "integrity": "sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg==", + "license": "MIT", "dependencies": { "rimraf": "~2.6.2" }, @@ -16610,6 +18039,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "license": "MIT", "engines": { "node": ">=8" } @@ -16619,6 +18049,7 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", "dependencies": { "glob": "^7.1.3" }, @@ -16686,9 +18117,10 @@ } }, "node_modules/terser": { - "version": "5.36.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.36.0.tgz", - "integrity": "sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==", + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", + "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", + "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -16705,18 +18137,21 @@ "node_modules/terser/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" }, "node_modules/text-encoding": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==", - "deprecated": "no longer maintained" + "deprecated": "no longer maintained", + "license": "(Unlicense OR Apache-2.0)" }, "node_modules/text-segmentation": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "license": "MIT", "dependencies": { "utrie": "^1.0.2" } @@ -16724,12 +18159,14 @@ "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "license": "MIT" }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "license": "MIT", "dependencies": { "any-promise": "^1.0.0" } @@ -16738,6 +18175,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "license": "MIT", "dependencies": { "thenify": ">= 3.1.0 < 4" }, @@ -16748,7 +18186,8 @@ "node_modules/throat": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", - "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==" + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "license": "MIT" }, "node_modules/through": { "version": "2.3.8", @@ -16760,11 +18199,30 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "license": "MIT", "dependencies": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" } }, + "node_modules/tldts": { + "version": "6.1.84", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.84.tgz", + "integrity": "sha512-aRGIbCIF3teodtUFAYSdQONVmDRy21REM3o6JnqWn5ZkQBJJ4gHxhw6OfwQ+WkSAi3ASamrS4N4nyazWx6uTYg==", + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.84" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.84", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.84.tgz", + "integrity": "sha512-NaQa1W76W2aCGjXybvnMYzGSM4x8fvG2AN/pla7qxcg0ZHbooOPhA8kctmOZUDfZyhDL27OGNbwAeig8P4p1vg==", + "license": "MIT" + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -16780,12 +18238,14 @@ "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "license": "BSD-3-Clause" }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -16797,46 +18257,38 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", "engines": { "node": ">=0.6" } }, "node_modules/tough-cookie": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", - "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "license": "BSD-3-Clause", "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" + "tldts": "^6.1.32" }, "engines": { - "node": ">=6" - } - }, - "node_modules/tough-cookie/node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "engines": { - "node": ">= 4.0.0" + "node": ">=16" } }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" }, "node_modules/traverse": { - "version": "0.6.10", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.10.tgz", - "integrity": "sha512-hN4uFRxbK+PX56DxYiGHsTn2dME3TVr9vbNqlQGcGcPhJAn+tdP126iA+TArMpI4YSgnTkMWyoLl5bf81Hi5TA==", + "version": "0.6.11", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.11.tgz", + "integrity": "sha512-vxXDZg8/+p3gblxB6BhhG5yWVn1kGRlaL8O78UDXc3wRnPizB5g83dcvWV1jpDMIPnjZjOFuxlMmE82XJ4407w==", "license": "MIT", "dependencies": { - "gopd": "^1.0.1", - "typedarray.prototype.slice": "^1.0.3", - "which-typed-array": "^1.1.15" + "gopd": "^1.2.0", + "typedarray.prototype.slice": "^1.0.5", + "which-typed-array": "^1.1.18" }, "engines": { "node": ">= 0.4" @@ -16849,15 +18301,17 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", "integrity": "sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/ts-api-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", - "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", "dev": true, + "license": "MIT", "engines": { "node": ">=16" }, @@ -16868,12 +18322,14 @@ "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "license": "Apache-2.0" }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" }, "node_modules/turboself-api": { "version": "2.1.8", @@ -16889,6 +18345,7 @@ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, @@ -16900,6 +18357,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "license": "MIT", "engines": { "node": ">=4" } @@ -16917,28 +18375,30 @@ } }, "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" } }, "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -16948,16 +18408,18 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" }, "engines": { "node": ">= 0.4" @@ -16967,16 +18429,17 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-proto": "^1.0.3", "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" }, "engines": { "node": ">= 0.4" @@ -16986,17 +18449,19 @@ } }, "node_modules/typedarray.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typedarray.prototype.slice/-/typedarray.prototype.slice-1.0.3.tgz", - "integrity": "sha512-8WbVAQAUlENo1q3c3zZYuy5k9VzBQvp8AX9WOtbvyWlLM1v5JaSRmjubLjzHF4JFtptjH/5c/i95yaElvcjC0A==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/typedarray.prototype.slice/-/typedarray.prototype.slice-1.0.5.tgz", + "integrity": "sha512-q7QNVDGTdl702bVFiI5eY4l/HkgCM6at9KhcFbgUAzezHFbOVy4+0O/lCjsABEQwbZPravVfBIiBVGo89yzHFg==", "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", + "es-abstract": "^1.23.9", "es-errors": "^1.3.0", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-offset": "^1.0.2" + "get-proto": "^1.0.1", + "math-intrinsics": "^1.1.0", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-offset": "^1.0.4" }, "engines": { "node": ">= 0.4" @@ -17006,9 +18471,9 @@ } }, "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -17020,9 +18485,9 @@ } }, "node_modules/ua-parser-js": { - "version": "0.7.39", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.39.tgz", - "integrity": "sha512-IZ6acm6RhQHNibSt7+c09hhvsKy9WUr4DVbeq9U8o71qxyYtJpQeDxQnMrVqnIFMLcQjHO0I9wgfO2vIahht4w==", + "version": "0.7.40", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.40.tgz", + "integrity": "sha512-us1E3K+3jJppDBa3Tl0L3MOJiGhe1C6P0+nIvQAFYbxlMAx0h81eOwLmU57xgqToduDDPx3y5QsdjPfDu+FgOQ==", "funding": [ { "type": "opencollective", @@ -17037,6 +18502,7 @@ "url": "https://github.com/sponsors/faisalman" } ], + "license": "MIT", "bin": { "ua-parser-js": "script/cli.js" }, @@ -17045,14 +18511,18 @@ } }, "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", + "call-bound": "^1.0.3", "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -17061,12 +18531,14 @@ "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "license": "MIT", "engines": { "node": ">=4" } @@ -17075,6 +18547,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "license": "MIT", "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" @@ -17087,6 +18560,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "license": "MIT", "engines": { "node": ">=4" } @@ -17095,6 +18569,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "license": "MIT", "engines": { "node": ">=4" } @@ -17102,7 +18577,8 @@ "node_modules/unimodules-app-loader": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/unimodules-app-loader/-/unimodules-app-loader-4.6.0.tgz", - "integrity": "sha512-FRNIlx7sLBDVPG117JnEBhnzZkTIgZTEwYW2rzrY9HdvLBTpRN+k0dxY50U/CAhFHW3zMD0OP5JAlnSQRhx5HA==" + "integrity": "sha512-FRNIlx7sLBDVPG117JnEBhnzZkTIgZTEwYW2rzrY9HdvLBTpRN+k0dxY50U/CAhFHW3zMD0OP5JAlnSQRhx5HA==", + "license": "MIT" }, "node_modules/unique-filename": { "version": "3.0.0", @@ -17144,6 +18620,7 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "license": "MIT", "engines": { "node": ">= 4.0.0" } @@ -17152,14 +18629,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "funding": [ { "type": "opencollective", @@ -17174,9 +18652,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "escalade": "^3.2.0", - "picocolors": "^1.1.0" + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -17189,6 +18668,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", + "license": "MIT", "dependencies": { "tslib": "^2.0.3" } @@ -17197,6 +18677,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", + "license": "MIT", "dependencies": { "tslib": "^2.0.3" } @@ -17205,6 +18686,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } @@ -17215,40 +18697,35 @@ "integrity": "sha512-EGXjXJZhIHiQMK2pQukuFcL303nskqIRzWvPvV5O8miOfwoUb9G+a/Cld60kUyeaybEI94wvVClT10DtfeAExA==", "license": "MIT" }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "node_modules/use-latest-callback": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.1.tgz", - "integrity": "sha512-QWlq8Is8BGWBf883QOEQP5HWYX/kMI+JTbJ5rdtvJLmXTIh9XoHIO3PQcmQl8BU44VKxow1kbQUHa6mQSMALDQ==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.3.tgz", + "integrity": "sha512-7vI3fBuyRcP91pazVboc4qu+6ZqM8izPWX9k7cRnT8hbD5svslcknsh3S9BUhaK11OmgTV4oWZZVSeQAiV53SQ==", + "license": "MIT", "peerDependencies": { "react": ">=16.8" } }, "node_modules/use-sync-external-store": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", - "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", + "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", + "license": "MIT", "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", "engines": { "node": ">= 0.4.0" } @@ -17257,6 +18734,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "license": "MIT", "dependencies": { "base64-arraybuffer": "^1.0.2" } @@ -17288,6 +18766,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -17295,12 +18774,14 @@ "node_modules/vlq": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz", - "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==" + "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==", + "license": "MIT" }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "license": "Apache-2.0", "dependencies": { "makeerror": "1.0.12" } @@ -17308,12 +18789,14 @@ "node_modules/warn-once": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/warn-once/-/warn-once-0.1.1.tgz", - "integrity": "sha512-VkQZJbO8zVImzYFteBXvBOZEl1qL175WH8VmZcxF2fZAoudNhNDvHi+doCaAEdU2l2vtcIwa2zn0QK5+I1HQ3Q==" + "integrity": "sha512-VkQZJbO8zVImzYFteBXvBOZEl1qL175WH8VmZcxF2fZAoudNhNDvHi+doCaAEdU2l2vtcIwa2zn0QK5+I1HQ3Q==", + "license": "MIT" }, "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "license": "MIT", "dependencies": { "defaults": "^1.0.3" } @@ -17321,17 +18804,20 @@ "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" }, "node_modules/whatwg-fetch": { "version": "3.6.20", "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", - "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==" + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", + "license": "MIT" }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -17341,6 +18827,7 @@ "version": "8.0.0-3", "resolved": "https://registry.npmjs.org/whatwg-url-without-unicode/-/whatwg-url-without-unicode-8.0.0-3.tgz", "integrity": "sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig==", + "license": "MIT", "dependencies": { "buffer": "^5.4.3", "punycode": "^2.1.1", @@ -17368,6 +18855,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -17377,6 +18865,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "license": "BSD-2-Clause", "engines": { "node": ">=8" } @@ -17385,6 +18874,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -17396,38 +18886,43 @@ } }, "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "license": "MIT", "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-builtin-type": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.4.tgz", - "integrity": "sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==", - "dev": true, + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "license": "MIT", "dependencies": { + "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", + "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", + "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", - "which-typed-array": "^1.1.15" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -17436,11 +18931,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/which-builtin-type/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, "node_modules/which-collection": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "dev": true, + "license": "MIT", "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", @@ -17457,17 +18958,21 @@ "node_modules/which-module": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" }, "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, "engines": { @@ -17488,6 +18993,7 @@ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -17496,6 +19002,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -17529,12 +19036,14 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" }, "node_modules/write-file-atomic": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "license": "ISC", "dependencies": { "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", @@ -17542,9 +19051,9 @@ } }, "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -17566,6 +19075,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/xcode/-/xcode-3.0.1.tgz", "integrity": "sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==", + "license": "Apache-2.0", "dependencies": { "simple-plist": "^1.1.0", "uuid": "^7.0.3" @@ -17578,6 +19088,7 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } @@ -17586,6 +19097,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.0.tgz", "integrity": "sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w==", + "license": "MIT", "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" @@ -17598,6 +19110,7 @@ "version": "11.0.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "license": "MIT", "engines": { "node": ">=4.0" } @@ -17606,6 +19119,7 @@ "version": "14.0.0", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-14.0.0.tgz", "integrity": "sha512-ts+B2rSe4fIckR6iquDjsKbQFK2NlUk6iG5nf14mDEyldgoc2nEKZ3jZWMPTxGQwVgToSjt6VGIho1H8/fNFTg==", + "license": "MIT", "engines": { "node": ">=8.0" } @@ -17614,6 +19128,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", "engines": { "node": ">=0.4" } @@ -17622,6 +19137,7 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", "engines": { "node": ">=10" } @@ -17629,12 +19145,14 @@ "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" }, "node_modules/yaml": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", - "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "license": "ISC", "bin": { "yaml": "bin.mjs" }, @@ -17646,6 +19164,7 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -17663,6 +19182,7 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", "engines": { "node": ">=12" } @@ -17671,6 +19191,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -17679,9 +19200,10 @@ } }, "node_modules/zod": { - "version": "3.23.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "version": "3.24.2", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz", + "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -17690,6 +19212,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-2.1.0.tgz", "integrity": "sha512-VJh93e2wb4c3tWtGgTa0OF/dTt/zoPCPzXq4V11ZjxmEAFaPi/Zss1xIZdEB5RD8GD00U0/iVXgqkF77RV7pdQ==", + "license": "MIT", "engines": { "node": ">=18.0.0" }, @@ -17698,11 +19221,12 @@ } }, "node_modules/zustand": { - "version": "4.5.5", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.5.tgz", - "integrity": "sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==", + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.6.tgz", + "integrity": "sha512-ibr/n1hBzLLj5Y+yUcU7dYw8p6WnIVzdJbnX+1YpaScvZVF2ziugqHs+LAmHw4lWO9c/zRj+K1ncgWDQuthEdQ==", + "license": "MIT", "dependencies": { - "use-sync-external-store": "1.2.2" + "use-sync-external-store": "^1.2.2" }, "engines": { "node": ">=12.7.0" From 72aecd3e2c2bf2829aae7fc512dbbefd00f09d57 Mon Sep 17 00:00:00 2001 From: Mael <96339570+ryzenixx@users.noreply.github.com> Date: Sun, 16 Mar 2025 01:01:00 +0100 Subject: [PATCH 0872/1144] =?UTF-8?q?Correction=20d'un=20probl=C3=A8me=20d?= =?UTF-8?q?'animation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/screens/login/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/router/screens/login/index.ts b/src/router/screens/login/index.ts index f35df1921..d10845109 100644 --- a/src/router/screens/login/index.ts +++ b/src/router/screens/login/index.ts @@ -12,7 +12,7 @@ export default [ headerTitle: "", headerTransparent: true, headerBackVisible: true, - animation: Platform.OS === "android" ? "default" : undefined, + animation: Platform.OS === "android" ? "slide_from_bottom" : undefined, animationDuration: 250, }), From 6dae50c662c28181e4cd248ad9d4e84de36931fb Mon Sep 17 00:00:00 2001 From: Mael <96339570+ryzenixx@users.noreply.github.com> Date: Sun, 16 Mar 2025 01:03:39 +0100 Subject: [PATCH 0873/1144] Ajout de log.from dans les formatted logs du support --- src/views/settings/SettingsSupport.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/settings/SettingsSupport.tsx b/src/views/settings/SettingsSupport.tsx index 09534fe79..9a1c1b5f4 100644 --- a/src/views/settings/SettingsSupport.tsx +++ b/src/views/settings/SettingsSupport.tsx @@ -33,7 +33,7 @@ const SettingsSupport: Screen<"SettingsSupport"> = ({ navigation }) => { const logDate = new Date(log.date); if (isNaN(logDate.getTime())) return `[${log.type}] ${log.message}`; - return `[${log.date}] [${log.type}] ${log.message}`; + return `[${log.date}] [${log.type}] [${log.from}] ${log.message}`; }) .join("
"); From fc266f23baf447f724151c75685d69110b221705 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 17 Mar 2025 10:01:10 +0100 Subject: [PATCH 0874/1144] =?UTF-8?q?fix:=20Mise=20=C3=A0=20jour=20de=20la?= =?UTF-8?q?=20version=20=C3=A0=207.10.2=20et=20ajustement=20de=20l'orienta?= =?UTF-8?q?tion=20de=20l'=C3=A9cran?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/build.gradle | 4 ++-- android/app/src/main/AndroidManifest.xml | 2 +- ios/Papillon/Info.plist | 4 +--- package.json | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index f4dd7e183..4808b3fd0 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -88,8 +88,8 @@ android { applicationId 'xyz.getpapillon.app' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 71010 - versionName "7.10.1" + versionCode 71020 + versionName "7.10.2" } signingConfigs { debug { diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 266b822cf..7d8a52236 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -28,7 +28,7 @@ - + diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index 283b7836a..18a1350db 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 7.10.1 + 7.10.2 CFBundleSignature ???? CFBundleURLTypes @@ -101,8 +101,6 @@ UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad diff --git a/package.json b/package.json index 9788f9f8c..bd41ff7a6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "papillonvex", - "version": "7.10.1", + "version": "7.10.2", "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", From 1d1fc3b87f9bdf4cdbda4dd7c69e802c6c8cd877 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 17 Mar 2025 10:51:27 +0100 Subject: [PATCH 0875/1144] =?UTF-8?q?fix:=20Activation=20de=20l'animation?= =?UTF-8?q?=20lors=20du=20d=C3=A9filement=20de=20la=20liste=20des=20le?= =?UTF-8?q?=C3=A7ons?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Lessons/Lessons.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index 267bc5c3f..044646b98 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -168,11 +168,11 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { if (index >= 0) { flatListRef.current.scrollToIndex({ index, - animated: false, + animated: true, }); } } - }, [width, height, pickerDate]); + }, [width, height]); const [data, setData] = useState(() => { const today = new Date(); From 8a32089a1bbea6da97de9f5f38f0ef040171ed1a Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 17 Mar 2025 10:53:27 +0100 Subject: [PATCH 0876/1144] =?UTF-8?q?fix:=20Ajout=20d'une=20marge=20=C3=A0?= =?UTF-8?q?=20droite=20pour=20les=20=C3=A9l=C3=A9ments=20de=20la=20liste?= =?UTF-8?q?=20des=20nouvelles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/News/Atoms/Item.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/account/News/Atoms/Item.tsx b/src/views/account/News/Atoms/Item.tsx index df682f924..db43a652e 100644 --- a/src/views/account/News/Atoms/Item.tsx +++ b/src/views/account/News/Atoms/Item.tsx @@ -39,6 +39,7 @@ const NewsListItem: React.FC = ({ index, message, navigation, gap: 10, marginBottom: 2, justifyContent: "space-between", + marginRight: 10, }}> {message.title !== "" && ( Date: Mon, 17 Mar 2025 10:53:58 +0100 Subject: [PATCH 0877/1144] =?UTF-8?q?fix:=20Ajout=20d'un=20texte=20par=20d?= =?UTF-8?q?=C3=A9faut=20pour=20les=20titres=20des=20nouvelles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/News/Atoms/Item.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/News/Atoms/Item.tsx b/src/views/account/News/Atoms/Item.tsx index db43a652e..22eb849d3 100644 --- a/src/views/account/News/Atoms/Item.tsx +++ b/src/views/account/News/Atoms/Item.tsx @@ -46,7 +46,7 @@ const NewsListItem: React.FC = ({ index, message, navigation, numberOfLines={1} variant="title" > - {message.title} + {message.title ?? "Sans titre"} ) } {!message.read && !isED && ( From 166123bd7219f08d811c5c08769ff42df089c8f1 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 17 Mar 2025 10:54:25 +0100 Subject: [PATCH 0878/1144] fix: Ajout d'un style flexible pour le titre des nouvelles --- src/views/account/News/Atoms/Item.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/views/account/News/Atoms/Item.tsx b/src/views/account/News/Atoms/Item.tsx index 22eb849d3..f3617e595 100644 --- a/src/views/account/News/Atoms/Item.tsx +++ b/src/views/account/News/Atoms/Item.tsx @@ -39,12 +39,14 @@ const NewsListItem: React.FC = ({ index, message, navigation, gap: 10, marginBottom: 2, justifyContent: "space-between", - marginRight: 10, }}> {message.title !== "" && ( {message.title ?? "Sans titre"} ) From 1d6aca18a6a5be70aa11b474bf3804b9cb295e5b Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 17 Mar 2025 10:56:55 +0100 Subject: [PATCH 0879/1144] =?UTF-8?q?fix:=20Ajout=20de=20nouvelles=20expre?= =?UTF-8?q?ssions=20r=C3=A9guli=C3=A8res=20pour=20la=20d=C3=A9tection=20de?= =?UTF-8?q?s=20examens=20et=20=C3=A9valuations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/magic/regex/evaldetection.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/magic/regex/evaldetection.json b/src/utils/magic/regex/evaldetection.json index 92a560b71..8bcd96448 100644 --- a/src/utils/magic/regex/evaldetection.json +++ b/src/utils/magic/regex/evaldetection.json @@ -23,7 +23,8 @@ "\\btest\\s(?:d'anglais|d'espagnol|d'allemand|de\\slangue)\\s(?:écrit|oral|de\\scompréhension|final|partiel|de\\sfin\\sde\\ssemestre|de\\srattrapage|de\\smi\\ssemestre|de\\smi\\strimestre|de\\sfin\\sd'année)\\b", "\\b(?:English|Spanish|German|French)\\s(?:writing|speaking|listening|reading)\\s(?:test|exam|assessment|final|partiel|de\\sfin\\sde\\ssemestre|de\\srattrapage|de\\smi\\ssemestre|de\\smi\\strimestre|de\\sfin\\sd'année)\\b", "\\bprueba\\sde\\sespañol\\s(?:escrita|oral|de\\scomprensión|de\\sgramática|final|partiel|de\\sfin\\sde\\ssemestre|de\\srattrapage|de\\smi\\ssemestre|de\\smi\\strimestre|de\\sfin\\sd'année)\\b", - "\\bDeutschtest\\s(?:schriftlich|mündlich|Hörverstehen|Leseverstehen|final|partiel|de\\sfin\\sde\\ssemestre|de\\srattrapage|de\\smi\\ssemestre|de\\smi\\strimestre|de\\sfin\\sd'année)\\b" + "\\bDeutschtest\\s(?:schriftlich|mündlich|Hörverstehen|Leseverstehen|final|partiel|de\\sfin\\sde\\ssemestre|de\\srattrapage|de\\smi\\ssemestre|de\\smi\\strimestre|de\\sfin\\sd'année)\\b", + "\\b(?:examen|prueba|evaluación|control)\\s(?:final|parcial|oral|escrito|diagnóstica|formativa|sumativa|de\\srecuperación|de\\smedio\\scurso|de\\sfin\\sde\\scurso)\\b" ], "Devoirs Maison": [ "\\bDM\\b(?:\\s+\\w+){0,3}", From 6d6de3bcb18c3c69c1f65f12457f6a08983bc429 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 17 Mar 2025 10:59:17 +0100 Subject: [PATCH 0880/1144] =?UTF-8?q?fix:=20Changement=20de=20la=20pr?= =?UTF-8?q?=C3=A9sentation=20de=20l'=C3=A9cran=20d'accueil=20en=20modal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/screens/account/home.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/router/screens/account/home.tsx b/src/router/screens/account/home.tsx index bf1945e64..f719945e3 100644 --- a/src/router/screens/account/home.tsx +++ b/src/router/screens/account/home.tsx @@ -41,7 +41,7 @@ const HomeStackScreen = ({ accountScreens }: { tabData.options = { ...tabData.options, tabEnabled: tab.enabled, - presentation: "formSheet", + presentation: "modal", animation: Platform.OS === "android" ? "slide_from_bottom" : "default", sheetCornerRadius: 24, From cf7f3cd9f9e73d42e103648048a42a1b4029b78f Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 17 Mar 2025 11:23:31 +0100 Subject: [PATCH 0881/1144] =?UTF-8?q?fix:=20Correction=20de=20la=20p=C3=A9?= =?UTF-8?q?riode=20par=20d=C3=A9faut=20pour=20afficher=20la=20derni=C3=A8r?= =?UTF-8?q?e=20note=20disponible?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/widgets/Components/LastGrade.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/widgets/Components/LastGrade.tsx b/src/widgets/Components/LastGrade.tsx index 747951178..bfef40889 100644 --- a/src/widgets/Components/LastGrade.tsx +++ b/src/widgets/Components/LastGrade.tsx @@ -24,13 +24,26 @@ const LastGradeWidget = forwardRef(({ const grades = useGradesStore((store) => store.grades); const defaultPeriod = useGradesStore((store) => store.defaultPeriod); + let lastPeriod = defaultPeriod; + + // find last period with grades + if(grades[lastPeriod].length === 0) { + const periods = Object.keys(grades); + for(let i = periods.length - 1; i >= 0; i--) { + if(grades[periods[i]].length > 0) { + lastPeriod = periods[i]; + break; + } + } + } + useImperativeHandle(ref, () => ({ handlePress: () => "Grades" })); const lastGrade = useMemo(() => { if (!grades || !defaultPeriod || !grades[defaultPeriod]) return null; - const periodGrades = grades[defaultPeriod]; + const periodGrades = grades[lastPeriod]; return periodGrades.length > 0 ? periodGrades[periodGrades.length - 1] : null; }, [grades, defaultPeriod]); From bfb4a71e0a9415c6d71b0bf548c83ff94d2c15b8 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 17 Mar 2025 11:26:04 +0100 Subject: [PATCH 0882/1144] =?UTF-8?q?fix:=20Ajout=20d'un=20d=C3=A9lai=20av?= =?UTF-8?q?ant=20la=20r=C3=A9initialisation=20de=20la=20navigation=20apr?= =?UTF-8?q?=C3=A8s=20d=C3=A9connexion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/Settings.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index 1960edc52..4ebcc3293 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -248,10 +248,12 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { title: "Déconnexion", onPress: () => { removeAccount(account.localID); - navigation.reset({ - index: 0, - routes: [{ name: "AccountSelector" }], - }); + setTimeout(() => { + navigation.reset({ + index: 0, + routes: [{ name: "AccountSelector" }], + }); + }, 100); }, danger: true, icon: , From 79708f01a4dbad6ee8489748da96c25b56dd16b5 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 17 Mar 2025 11:27:46 +0100 Subject: [PATCH 0883/1144] =?UTF-8?q?fix:=20Remplacement=20de=20l'ic=C3=B4?= =?UTF-8?q?ne=20de=20v=C3=A9rification=20par=20un=20graphique=20en=20secte?= =?UTF-8?q?urs=20dans=20le=20widget=20de=20la=20derni=C3=A8re=20note?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/widgets/Components/LastGrade.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/widgets/Components/LastGrade.tsx b/src/widgets/Components/LastGrade.tsx index bfef40889..385e9b63c 100644 --- a/src/widgets/Components/LastGrade.tsx +++ b/src/widgets/Components/LastGrade.tsx @@ -1,5 +1,5 @@ import { useTheme } from "@react-navigation/native"; -import { FileCheck } from "lucide-react-native"; +import { TrendingUp } from "lucide-react-native"; import React, { forwardRef, useEffect, useImperativeHandle, useMemo } from "react"; import { Text, View } from "react-native"; import Reanimated, { LinearTransition } from "react-native-reanimated"; @@ -93,7 +93,7 @@ const LastGradeWidget = forwardRef(({ opacity: 0.5, }} > - + Date: Mon, 17 Mar 2025 11:28:39 +0100 Subject: [PATCH 0884/1144] =?UTF-8?q?fix:=20Changement=20de=20la=20teinte?= =?UTF-8?q?=20du=20flou=20dans=20le=20s=C3=A9lecteur=20de=20compte?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Home/AccountSwitcher.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Home/AccountSwitcher.tsx b/src/components/Home/AccountSwitcher.tsx index 4606d16f1..a9051fad6 100644 --- a/src/components/Home/AccountSwitcher.tsx +++ b/src/components/Home/AccountSwitcher.tsx @@ -86,7 +86,7 @@ const AccountSwitcher: React.FC<{ layout={animPapillon(LinearTransition)} > Date: Mon, 17 Mar 2025 11:35:33 +0100 Subject: [PATCH 0885/1144] fix: Ajout de la largeur et du rayon de bordure au composant ColorIndicator --- src/components/Lessons/ColorIndicator.tsx | 10 ++++++---- src/views/account/Lessons/Atoms/Item.tsx | 4 ++-- src/widgets/Components/NextCourse.tsx | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/components/Lessons/ColorIndicator.tsx b/src/components/Lessons/ColorIndicator.tsx index 8c97f3a95..92331a011 100644 --- a/src/components/Lessons/ColorIndicator.tsx +++ b/src/components/Lessons/ColorIndicator.tsx @@ -3,18 +3,20 @@ import { View, Image, type ViewStyle } from "react-native"; interface ColorIndicatorProps { style?: ViewStyle - color: string + color: string, + width?: number, + borderRadius?: number } -const ColorIndicator: React.FC = ({ color, style }) => { +const ColorIndicator: React.FC = ({ color, style, width, borderRadius }) => { return ( diff --git a/src/views/account/Lessons/Atoms/Item.tsx b/src/views/account/Lessons/Atoms/Item.tsx index 1c9c6daf8..f453185ee 100644 --- a/src/views/account/Lessons/Atoms/Item.tsx +++ b/src/views/account/Lessons/Atoms/Item.tsx @@ -62,12 +62,12 @@ export const TimetableItem: React.FC<{ )} - + - + {subjectData.pretty || "Cours inconnu"} {item.itemType && ( diff --git a/src/widgets/Components/NextCourse.tsx b/src/widgets/Components/NextCourse.tsx index 1cbec2206..a33abd020 100644 --- a/src/widgets/Components/NextCourse.tsx +++ b/src/widgets/Components/NextCourse.tsx @@ -157,7 +157,7 @@ const NextCourseLesson: React.FC<{ return ( - + {subjectData.pretty} From 7a689843f228472e17831bbba2dbe7b76f8faded Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 17 Mar 2025 11:43:13 +0100 Subject: [PATCH 0886/1144] =?UTF-8?q?fix:=20Ajout=20de=20l'ic=C3=B4ne=20de?= =?UTF-8?q?=20localisation=20et=20am=C3=A9lioration=20du=20style=20des=20t?= =?UTF-8?q?extes=20dans=20le=20composant=20TimetableItem?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Lessons/Atoms/Item.tsx | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/views/account/Lessons/Atoms/Item.tsx b/src/views/account/Lessons/Atoms/Item.tsx index f453185ee..0bdb81aef 100644 --- a/src/views/account/Lessons/Atoms/Item.tsx +++ b/src/views/account/Lessons/Atoms/Item.tsx @@ -18,6 +18,7 @@ import NativeTouchable from "@/components/Global/NativeTouchable"; import { getSubjectData } from "@/services/shared/Subject"; import { animPapillon } from "@/utils/ui/animations"; import { getDuration } from "@/utils/format/course_duration"; +import { MapPin } from "lucide-react-native"; export const TimetableItem: React.FC<{ item: TimetableClass @@ -77,6 +78,8 @@ export const TimetableItem: React.FC<{ )} + + Date: Mon, 17 Mar 2025 11:43:57 +0100 Subject: [PATCH 0887/1144] fix: Augmentation de la taille de police et ajout d'espacement des lettres dans le composant Item --- src/views/account/Lessons/Atoms/Item.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/account/Lessons/Atoms/Item.tsx b/src/views/account/Lessons/Atoms/Item.tsx index 0bdb81aef..25091b326 100644 --- a/src/views/account/Lessons/Atoms/Item.tsx +++ b/src/views/account/Lessons/Atoms/Item.tsx @@ -203,7 +203,8 @@ const styles = StyleSheet.create({ paddingHorizontal: 10, }, statusText: { - fontSize: 14.5, + fontSize: 15, + letterSpacing: 0.2, fontFamily: "semibold", }, }); From 4863176452b37b1359becf98c2c5f37780217a55 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 17 Mar 2025 11:46:07 +0100 Subject: [PATCH 0888/1144] =?UTF-8?q?fix:=20Changement=20de=20la=20police?= =?UTF-8?q?=20de=20caract=C3=A8res=20du=20composant=20Item=20de=20"semibol?= =?UTF-8?q?d"=20=C3=A0=20"medium"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Lessons/Atoms/Item.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Lessons/Atoms/Item.tsx b/src/views/account/Lessons/Atoms/Item.tsx index 25091b326..f983f30c6 100644 --- a/src/views/account/Lessons/Atoms/Item.tsx +++ b/src/views/account/Lessons/Atoms/Item.tsx @@ -190,7 +190,7 @@ const styles = StyleSheet.create({ fontSize: 15, opacity: 0.5, flex: 1, - fontFamily: "semibold", + fontFamily: "medium", }, durationText: { fontSize: 15, From d19dcbe45adc0e797ffdf43bcf09ee8cf4f8d61f Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 17 Mar 2025 11:47:32 +0100 Subject: [PATCH 0889/1144] =?UTF-8?q?fix:=20Simplification=20des=20animati?= =?UTF-8?q?ons=20d'entr=C3=A9e=20et=20de=20sortie=20dans=20le=20composant?= =?UTF-8?q?=20GradesScodocUE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Grades/Atoms/GradesScodocUE.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views/account/Grades/Atoms/GradesScodocUE.tsx b/src/views/account/Grades/Atoms/GradesScodocUE.tsx index c7f707569..04947df8c 100644 --- a/src/views/account/Grades/Atoms/GradesScodocUE.tsx +++ b/src/views/account/Grades/Atoms/GradesScodocUE.tsx @@ -12,7 +12,7 @@ import { memo, useState } from "react"; import { Image, View } from "react-native"; import { TouchableOpacity } from "react-native-gesture-handler"; -import Reanimated, { FadeIn, FadeInDown, FadeInUp, FadeOut, FadeOutDown, FadeOutUp, LinearTransition } from "react-native-reanimated"; +import Reanimated, { FadeIn, FadeInDown, FadeOut, FadeOutUp, LinearTransition } from "react-native-reanimated"; const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: PrimaryAccount, navigation: any, selectedPeriod: string }) => { try { @@ -209,8 +209,8 @@ const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: Prim > {opened ? : } From 8a4fe065ed500fdc6470a4434453f84eb249cb78 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 17 Mar 2025 11:51:25 +0100 Subject: [PATCH 0890/1144] =?UTF-8?q?fix:=20R=C3=A9duction=20de=20la=20dur?= =?UTF-8?q?=C3=A9e=20des=20animations=20d'entr=C3=A9e=20et=20ajout=20d'opa?= =?UTF-8?q?cit=C3=A9=20aux=20ic=C3=B4nes=20dans=20le=20composant=20GradesS?= =?UTF-8?q?codocUE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Grades/Atoms/GradesScodocUE.tsx | 4 ++-- src/views/account/Grades/Subject/Subject.tsx | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/views/account/Grades/Atoms/GradesScodocUE.tsx b/src/views/account/Grades/Atoms/GradesScodocUE.tsx index 04947df8c..cf28cf720 100644 --- a/src/views/account/Grades/Atoms/GradesScodocUE.tsx +++ b/src/views/account/Grades/Atoms/GradesScodocUE.tsx @@ -209,10 +209,10 @@ const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: Prim > - {opened ? : } + {opened ? : } diff --git a/src/views/account/Grades/Subject/Subject.tsx b/src/views/account/Grades/Subject/Subject.tsx index 4e501695d..5585f7458 100644 --- a/src/views/account/Grades/Subject/Subject.tsx +++ b/src/views/account/Grades/Subject/Subject.tsx @@ -150,6 +150,7 @@ const Subject: React.FC = ({ layout={anim2Papillon(LinearTransition)} > Date: Mon, 17 Mar 2025 12:01:15 +0100 Subject: [PATCH 0891/1144] =?UTF-8?q?fix:=20Am=C3=A9lioration=20de=20la=20?= =?UTF-8?q?v=C3=A9rification=20des=20p=C3=A9riodes=20sans=20notes=20dans?= =?UTF-8?q?=20le=20composant=20LastGrade?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/widgets/Components/LastGrade.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/Components/LastGrade.tsx b/src/widgets/Components/LastGrade.tsx index 385e9b63c..cfcc69143 100644 --- a/src/widgets/Components/LastGrade.tsx +++ b/src/widgets/Components/LastGrade.tsx @@ -27,7 +27,7 @@ const LastGradeWidget = forwardRef(({ let lastPeriod = defaultPeriod; // find last period with grades - if(grades[lastPeriod].length === 0) { + if(!grades[lastPeriod] || grades[lastPeriod] && grades[lastPeriod].length === 0) { const periods = Object.keys(grades); for(let i = periods.length - 1; i >= 0; i--) { if(grades[periods[i]].length > 0) { From d64ea5ddd17dbc96ee33916896df8c5c9b1bc380 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 17 Mar 2025 13:17:02 +0100 Subject: [PATCH 0892/1144] =?UTF-8?q?fix:=20Ajout=20de=20la=20gestion=20de?= =?UTF-8?q?=20la=20couleur=20primaire=20personnalis=C3=A9e=20dans=20le=20c?= =?UTF-8?q?omposant=20Router=20et=20am=C3=A9lioration=20de=20la=20logique?= =?UTF-8?q?=20de=20couleur=20dans=20MenuItem=20et=20TabItem?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/index.tsx | 44 ++++++++++++++----------- src/router/navigator/atoms/MenuItem.tsx | 4 +-- src/router/navigator/atoms/TabItem.tsx | 4 +-- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/router/index.tsx b/src/router/index.tsx index d791c42d1..da5c31a0f 100644 --- a/src/router/index.tsx +++ b/src/router/index.tsx @@ -49,6 +49,9 @@ const Router: React.FC = () => { }; const [theme, setTheme] = React.useState(scheme === "dark" ? PapillonDark : PapillonLight); + const account = useCurrentAccount(store => store.account!); + + const [primaryColor, setPrimaryColor] = React.useState("#0000FF"); useEffect(() => { switch (whatTheme) { @@ -64,14 +67,9 @@ const Router: React.FC = () => { } }, [scheme, whatTheme]); - - const account = useCurrentAccount(store => store.account!); - if (account?.personalization?.color) { - - if (account.personalization?.color?.hex?.primary) { - theme.colors.primary = account.personalization.color.hex.primary; - } - } + useEffect(() => { + setPrimaryColor(account?.personalization?.color?.hex?.primary || theme.colors.primary); + }, [account?.personalization]); return ( @@ -96,18 +94,24 @@ const Router: React.FC = () => { - { - let str = ""; - let view: NavigationState | PartialState | undefined = state; - while (view?.routes) { - // @ts-expect-error (view is not undefined here bc of while condition, but ts think it can be) - str += "/" + view.routes[view.index].name; - // @ts-expect-error (same) - view = view.routes[view.index].state; - } - navigate(str); - }} + { + let str = ""; + let view: NavigationState | PartialState | undefined = state; + while (view?.routes) { + // @ts-expect-error (view is not undefined here bc of while condition, but ts think it can be) + str += "/" + view.routes[view.index].name; + // @ts-expect-error (same) + view = view.routes[view.index].state; + } + navigate(str); + }} > diff --git a/src/router/navigator/atoms/MenuItem.tsx b/src/router/navigator/atoms/MenuItem.tsx index 469126de2..8ba30f4c7 100644 --- a/src/router/navigator/atoms/MenuItem.tsx +++ b/src/router/navigator/atoms/MenuItem.tsx @@ -46,10 +46,10 @@ const MenuItem: React.FC<{ const lottieRef = React.useRef(null); - const autoColor = colorsList.filter(c => c.hex.primary === theme.colors.primary)[0]; + const autoColor = colorsList.filter(c => c.hex.primary === theme.colors.primary)[0] || colorsList[0]; const tabColor = isFocused ? - (theme.dark ? autoColor.hex.lighter : autoColor.hex.dark) : (theme.dark ? "#656c72" : "#8C9398"); + (autoColor?.hex?.lighter ? (theme.dark ? autoColor?.hex?.lighter : autoColor.hex.dark) : theme.colors.primary) : (theme.dark ? "#656c72" : "#8C9398"); return ( (null); - const autoColor = colorsList.filter(c => c.hex.primary === theme.colors.primary)[0]; + const autoColor = colorsList.filter(c => c.hex.primary === theme.colors.primary)[0] || colorsList[0]; const tabColor = isFocused ? - (theme.dark ? autoColor.hex.lighter : autoColor.hex.dark) : (theme.dark ? "#656c72" : "#8C9398"); + (autoColor?.hex?.lighter ? (theme.dark ? autoColor?.hex?.lighter : autoColor.hex.dark) : theme.colors.primary) : (theme.dark ? "#656c72" : "#8C9398"); return ( Date: Mon, 17 Mar 2025 13:22:15 +0100 Subject: [PATCH 0893/1144] =?UTF-8?q?fix:=20Remplacement=20des=20alertes?= =?UTF-8?q?=20personnalis=C3=A9es=20par=20des=20alertes=20natives=20dans?= =?UTF-8?q?=20le=20composant=20LessonsImportIcal=20et=20am=C3=A9lioration?= =?UTF-8?q?=20de=20la=20gestion=20des=20actions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FirstInstallation/ButtonCta.tsx | 10 ++- .../Lessons/Options/LessonsImportIcal.tsx | 72 +++++++------------ 2 files changed, 31 insertions(+), 51 deletions(-) diff --git a/src/components/FirstInstallation/ButtonCta.tsx b/src/components/FirstInstallation/ButtonCta.tsx index 21751d717..0054a8ea4 100644 --- a/src/components/FirstInstallation/ButtonCta.tsx +++ b/src/components/FirstInstallation/ButtonCta.tsx @@ -4,7 +4,6 @@ import Reanimated, { Easing, useSharedValue, withTiming } from "react-native-rea import { useTheme } from "@react-navigation/native"; import * as Haptics from "expo-haptics"; import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; -import useScreenDimensions from "@/hooks/useScreenDimensions"; const ButtonCta: React.FC<{ value: string @@ -26,8 +25,6 @@ const ButtonCta: React.FC<{ const { playHaptics } = useSoundHapticsWrapper(); const { colors } = useTheme(); - const { isTablet } = useScreenDimensions(); - const [pressed, setPressed] = useState(false); const scale = useSharedValue(1); const opacity = useSharedValue(1); @@ -65,7 +62,6 @@ const ButtonCta: React.FC<{ > = ({ route, navigation }) = fetchIcalData(account); }) .catch(() => { - showAlert({ - title: "Erreur", - message: "Impossible de récupérer les données du calendrier. Vérifie l'URL et réessaye.", - icon: , - }); + Alert.alert("Erreur", "Impossible de récupérer les données du calendrier. Vérifie l'URL et réessaye."); }) .finally(() => { setLoading(false); @@ -236,46 +232,32 @@ const LessonsImportIcal: Screen<"LessonsImportIcal"> = ({ route, navigation }) = key={index} icon={} onPress={() => { - showAlert({ - title: url.name, - message: url.url, - icon: , - actions: [ - { - title: "Annuler", - icon: , + Alert.alert(url.name, url.url, [ + { + text: "Annuler", + style: "cancel", + }, + { + text: "Copier l'URL", + onPress: () => { + Clipboard.setString(url.url); + Alert.alert("URL copiée", url.url); }, - { - title: "Copier l'URL", - icon: , - onPress: () => { - Clipboard.setString(url.url); - showAlert({ - title: "Copié", - message: "L'URL a été copiée dans le presse-papiers.", - icon: , - }); - }, - primary: true, - backgroundColor: theme.colors.primary, + }, + { + text: "Supprimer le calendrier", + style: "destructive", + onPress: () => { + useTimetableStore.getState().removeClassesFromSource("ical://"+url.url); + const urls = account.personalization.icalURLs || []; + urls.splice(index, 1); + mutateProperty("personalization", { + ...account.personalization, + icalURLs: urls, + }); }, - { - title: "Supprimer le calendrier", - icon: , - onPress: () => { - useTimetableStore.getState().removeClassesFromSource("ical://"+url.url); - const urls = account.personalization.icalURLs || []; - urls.splice(index, 1); - mutateProperty("personalization", { - ...account.personalization, - icalURLs: urls, - }); - }, - danger: true, - delayDisable: 3, - } - ] - }); + }, + ]); }} > {url.name} From 7eb513bbd42af384d71971cdb013d46c374d9981 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Mon, 17 Mar 2025 18:52:44 +0100 Subject: [PATCH 0894/1144] fix(lint): remove unused comma --- src/views/settings/ExternalAccount/IzlyActivation.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/settings/ExternalAccount/IzlyActivation.tsx b/src/views/settings/ExternalAccount/IzlyActivation.tsx index 0d9c301b1..d7662ae2d 100644 --- a/src/views/settings/ExternalAccount/IzlyActivation.tsx +++ b/src/views/settings/ExternalAccount/IzlyActivation.tsx @@ -4,7 +4,7 @@ import { useTheme } from "@react-navigation/native"; import { SafeAreaView, useSafeAreaInsets } from "react-native-safe-area-context"; import { Alert, Keyboard, KeyboardAvoidingView, StyleSheet, TouchableWithoutFeedback, View } from "react-native"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; -import { NativeText, } from "@/components/Global/NativeComponents"; +import { NativeText } from "@/components/Global/NativeComponents"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { tokenize } from "ezly"; import { AccountService, IzlyAccount } from "@/stores/account/types"; From 6abbc29b7ffb48e9b0db2377e7c73d6050a27068 Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Mon, 17 Mar 2025 20:24:07 +0100 Subject: [PATCH 0895/1144] fix(canteen): alise invalid date --- .../account/Restaurant/Modals/CardDetail.tsx | 111 +++++++++--------- 1 file changed, 57 insertions(+), 54 deletions(-) diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index 7c61e01d9..ad13486f4 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -278,60 +278,63 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio {card?.history.length > 0 && ( - {card.history.sort((a: any, b: any) => b.timestamp - a.timestamp).map((history, i) => ( - - } - trailing={ - - {history.amount > 0 ? ( - - +{history.amount.toFixed(2)} € - - ) : ( - - -{(-history.amount).toFixed(2)} € - - )} - - } - > - - {history.label} - - - il y a {formatDistance(new Date(history.timestamp), new Date(), { locale: fr })} - - - ))} + {card.history + .filter((event) => !isNaN(new Date(event.timestamp).getTime())) + .sort((a: any, b: any) => b.timestamp - a.timestamp) + .map((history, i) => ( + + } + trailing={ + + {history.amount > 0 ? ( + + +{history.amount.toFixed(2)} € + + ) : ( + + -{(-history.amount).toFixed(2)} € + + )} + + } + > + + {history.label} + + + il y a {formatDistance(new Date(history.timestamp), new Date(), { locale: fr })} + + + ))} )} From cb5789d3542b477a3f4a9ab66f172497514a9753 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 17 Mar 2025 20:36:49 +0100 Subject: [PATCH 0896/1144] =?UTF-8?q?fix:=20Ajout=20de=20l'en-t=C3=AAte=20?= =?UTF-8?q?User-Agent=20lors=20de=20la=20r=C3=A9cup=C3=A9ration=20des=20do?= =?UTF-8?q?nn=C3=A9es=20iCal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/local/ical.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/services/local/ical.ts b/src/services/local/ical.ts index 032c2f3e2..9856fb8fa 100644 --- a/src/services/local/ical.ts +++ b/src/services/local/ical.ts @@ -33,8 +33,14 @@ export const fetchIcalData = async ( } for (const ical of icalURLs) { - await fetch(ical.url) - .then((res) => res.text()) + await fetch(ical.url.trim(), { + headers: { + "User-Agent": "Papillon", + }, + }) + .then((res) => { + return res.text(); + }) .then((text) => { const parsed = icalParser.parseString(text).events; for (const event of parsed) { From 703612684c662dd88fd40eaa035725636f78ad85 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 17 Mar 2025 21:23:04 +0100 Subject: [PATCH 0897/1144] =?UTF-8?q?fix:=20Ajout=20d'une=20propri=C3=A9t?= =?UTF-8?q?=C3=A9=20paddingTop=20au=20composant=20OfflineWarning=20pour=20?= =?UTF-8?q?un=20espacement=20am=C3=A9lior=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useOnlineStatus.tsx | 5 ++++- src/views/account/Home/ModalContent.tsx | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/hooks/useOnlineStatus.tsx b/src/hooks/useOnlineStatus.tsx index 1123406e0..1b1412f6b 100644 --- a/src/hooks/useOnlineStatus.tsx +++ b/src/hooks/useOnlineStatus.tsx @@ -45,7 +45,7 @@ const useOnlineStatus = () => { return { isOnline, errorTitle }; }; -const OfflineWarning = ({ cache = false }) => { +const OfflineWarning = ({ cache = false, paddingTop = 0 }) => { const { errorTitle } = useOnlineStatus(); return ( @@ -53,6 +53,9 @@ const OfflineWarning = ({ cache = false }) => { entering={FlipInXDown.springify().mass(1).damping(20).stiffness(300)} exiting={FadeOutUp.springify().mass(1).damping(20).stiffness(300)} layout={animPapillon(LinearTransition)} + style={{ + paddingTop: paddingTop, + }} > }> diff --git a/src/views/account/Home/ModalContent.tsx b/src/views/account/Home/ModalContent.tsx index dc1c17e7d..aa4c9a63d 100644 --- a/src/views/account/Home/ModalContent.tsx +++ b/src/views/account/Home/ModalContent.tsx @@ -193,7 +193,7 @@ const ModalContent: React.FC = ({ navigation, refresh, endRef )} - {!isOnline && } + {!isOnline && } Date: Mon, 17 Mar 2025 21:23:22 +0100 Subject: [PATCH 0898/1144] fix: ajout de la mappin sur le widget NextCourse --- src/widgets/Components/NextCourse.tsx | 35 +++++++++++++++------------ 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/widgets/Components/NextCourse.tsx b/src/widgets/Components/NextCourse.tsx index a33abd020..6a1dacc63 100644 --- a/src/widgets/Components/NextCourse.tsx +++ b/src/widgets/Components/NextCourse.tsx @@ -1,7 +1,7 @@ import React, { forwardRef, useEffect, useImperativeHandle, useState, useCallback, useMemo } from "react"; import { ActivityIndicator, Text, View } from "react-native"; import { useTheme } from "@react-navigation/native"; -import { Calendar, Clock } from "lucide-react-native"; +import { Calendar, Clock, MapPin } from "lucide-react-native"; import { WidgetProps } from "@/components/Home/Widget"; import WidgetHeader from "@/components/Home/WidgetHeader"; @@ -170,20 +170,25 @@ const NextCourseLesson: React.FC<{ borderCurve: "continuous", alignSelf: "flex-start", }}> - - {nextCourse.room - ? nextCourse.room.includes(",") - ? "Plusieurs salles dispo." - : nextCourse.room - : "Salle inconnue"} - + + + + {nextCourse.room + ? nextCourse.room.includes(",") + ? "Plusieurs salles dispo." + : nextCourse.room + : "Salle inconnue"} + + + From 3875a17f4e01a144885a6317886e7a1e46245e75 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 17 Mar 2025 21:27:44 +0100 Subject: [PATCH 0899/1144] =?UTF-8?q?fix:=20Mise=20=C3=A0=20jour=20de=20la?= =?UTF-8?q?=20version=20=C3=A0=207.10.3=20dans=20package.json?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/build.gradle | 4 ++-- ios/Papillon/Info.plist | 2 +- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 4808b3fd0..a3d033c58 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -88,8 +88,8 @@ android { applicationId 'xyz.getpapillon.app' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 71020 - versionName "7.10.2" + versionCode 71030 + versionName "7.10.3" } signingConfigs { debug { diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index 18a1350db..d146778d5 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 7.10.2 + 7.10.3 CFBundleSignature ???? CFBundleURLTypes diff --git a/package.json b/package.json index bd41ff7a6..052d228d3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "papillonvex", - "version": "7.10.2", + "version": "7.10.3", "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", From 8d498278d37f762893385f8f016aefb2c86b09fa Mon Sep 17 00:00:00 2001 From: Mael <96339570+ryzenixx@users.noreply.github.com> Date: Tue, 18 Mar 2025 07:16:10 +0100 Subject: [PATCH 0900/1144] fix: retrait d'un texte en double sur la page de login de l'enseignement sup --- .../IdentityProvider/IdentityProviderSelector.tsx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/views/login/IdentityProvider/IdentityProviderSelector.tsx b/src/views/login/IdentityProvider/IdentityProviderSelector.tsx index 7f8d7777a..847ce8f91 100644 --- a/src/views/login/IdentityProvider/IdentityProviderSelector.tsx +++ b/src/views/login/IdentityProvider/IdentityProviderSelector.tsx @@ -121,16 +121,6 @@ const IdentityProviderSelector: Screen<"IdentityProviderSelector"> = ({ navigati
))}
- - - } - > - - Les founisseurs d'identité ne fournissent pas de données (calendrier, notes, etc...) mais permettent de te connecter à l'application. - - - From 9f0f64b38e004d07b44bc3051e128309d2f22161 Mon Sep 17 00:00:00 2001 From: TinAD17tin <163759571+TinAD17tin@users.noreply.github.com> Date: Tue, 18 Mar 2025 13:58:32 +0100 Subject: [PATCH 0901/1144] =?UTF-8?q?Ajout=20de=20hier,=20aujourd=E2=80=99?= =?UTF-8?q?hui,=20demain?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Afin d'éviter les "il y'a 1 jour" "dans 1 jour" "il y a environ 14 heures" --- src/utils/format/format_date_complets.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/utils/format/format_date_complets.ts b/src/utils/format/format_date_complets.ts index 2ef073229..02db97cc3 100644 --- a/src/utils/format/format_date_complets.ts +++ b/src/utils/format/format_date_complets.ts @@ -6,9 +6,21 @@ function formatDate (date: string): string { if (Number.isNaN(messageDate.getTime())) { return "Date invalide"; - } + } + let formattedDate = formatDistanceToNow(messageDate, { addSuffix: true, locale: fr }); + + if (formattedDate === "dans 1 jour") { + return "demain"; + } + if (formattedDate === "il y a 1 jour") { + return "hier"; + } + + if (isToday(messageDate)) { + return "aujourd’hui"; + } - return formatDistanceToNow(messageDate, { addSuffix: true, locale: fr }); + return formattedDate; } export default formatDate; From a10ef167b2c69e0a74d3bf40cc93e5d10c6aba0a Mon Sep 17 00:00:00 2001 From: TinAD17tin <163759571+TinAD17tin@users.noreply.github.com> Date: Tue, 18 Mar 2025 16:04:39 +0100 Subject: [PATCH 0902/1144] Importation de isToday format_date_complets.ts --- src/utils/format/format_date_complets.ts | 26 ++++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/utils/format/format_date_complets.ts b/src/utils/format/format_date_complets.ts index 02db97cc3..49aabb6d3 100644 --- a/src/utils/format/format_date_complets.ts +++ b/src/utils/format/format_date_complets.ts @@ -1,4 +1,4 @@ -import { formatDistanceToNow } from "date-fns"; +import { formatDistanceToNow, isToday } from "date-fns"; import { fr } from "date-fns/locale"; function formatDate (date: string): string { @@ -6,19 +6,19 @@ function formatDate (date: string): string { if (Number.isNaN(messageDate.getTime())) { return "Date invalide"; - } + } let formattedDate = formatDistanceToNow(messageDate, { addSuffix: true, locale: fr }); - - if (formattedDate === "dans 1 jour") { - return "demain"; - } - if (formattedDate === "il y a 1 jour") { - return "hier"; - } - - if (isToday(messageDate)) { - return "aujourd’hui"; - } + + if (formattedDate === "dans 1 jour") { + return "demain"; + } + if (formattedDate === "il y a 1 jour") { + return "hier"; + } + + if (isToday(messageDate)) { + return "aujourd’hui"; + } return formattedDate; } From 32607dc6a83a9ebe0cb49ff8446ec22b2c35117e Mon Sep 17 00:00:00 2001 From: TinAD17tin <163759571+TinAD17tin@users.noreply.github.com> Date: Tue, 18 Mar 2025 18:44:32 +0100 Subject: [PATCH 0903/1144] Utilisation de isYesterday & isTomorrow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plutôt que une comparaison de string, merci kgeek --- src/utils/format/format_date_complets.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/utils/format/format_date_complets.ts b/src/utils/format/format_date_complets.ts index 49aabb6d3..4fead6c64 100644 --- a/src/utils/format/format_date_complets.ts +++ b/src/utils/format/format_date_complets.ts @@ -1,4 +1,4 @@ -import { formatDistanceToNow, isToday } from "date-fns"; +import { formatDistanceToNow, isYesterday, isToday, isTomorrow } from "date-fns"; import { fr } from "date-fns/locale"; function formatDate (date: string): string { @@ -9,16 +9,17 @@ function formatDate (date: string): string { } let formattedDate = formatDistanceToNow(messageDate, { addSuffix: true, locale: fr }); - if (formattedDate === "dans 1 jour") { - return "demain"; - } - if (formattedDate === "il y a 1 jour") { + if (isYesterday(messageDate)) { return "hier"; } if (isToday(messageDate)) { return "aujourd’hui"; - } + } + + if (isTomorrow(messageDate)) { + return "demain"; + } return formattedDate; } From 4262b2c14edc42fd57c8d46a9ef032394a6f1a9e Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 18 Mar 2025 18:50:13 +0100 Subject: [PATCH 0904/1144] refractor: update package-lock.json --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5381dbe7e..d3b9a4295 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.10.1", + "version": "7.10.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.10.1", + "version": "7.10.3", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.4", "@candlefinance/app-icon": "^0.4.5", From 33be11d938b3bde828d7d06a48fa7cf19098b835 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 18 Mar 2025 18:50:27 +0100 Subject: [PATCH 0905/1144] =?UTF-8?q?fix:=20simplification=20du=20texte=20?= =?UTF-8?q?pour=20=C3=A9viter=20un=20d=C3=A9bordement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/widgets/Components/NextCourse.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/Components/NextCourse.tsx b/src/widgets/Components/NextCourse.tsx index 6a1dacc63..55467b600 100644 --- a/src/widgets/Components/NextCourse.tsx +++ b/src/widgets/Components/NextCourse.tsx @@ -183,7 +183,7 @@ const NextCourseLesson: React.FC<{ > {nextCourse.room ? nextCourse.room.includes(",") - ? "Plusieurs salles dispo." + ? "Plusieurs salles" : nextCourse.room : "Salle inconnue"} From e2e68561f0185d72de76b2a076569f714a6522b7 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 18 Mar 2025 19:08:07 +0100 Subject: [PATCH 0906/1144] fix: reset screen when changed account --- src/components/Home/AccountSwitcherContextMenu.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/Home/AccountSwitcherContextMenu.tsx b/src/components/Home/AccountSwitcherContextMenu.tsx index 41cbb5b64..bba704829 100644 --- a/src/components/Home/AccountSwitcherContextMenu.tsx +++ b/src/components/Home/AccountSwitcherContextMenu.tsx @@ -162,6 +162,10 @@ const ContextMenu: React.FC<{ }); setOpened(false); + navigation.reset({ + index: 0, + routes: [{ name: "AccountStack" as never }], + }); requestAnimationFrame(() => { switchTo(account); }); From 58e57229e2994a3fb092f99e66f57d679ed19c64 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 18 Mar 2025 19:09:30 +0100 Subject: [PATCH 0907/1144] fix: reset account when color configuration change --- src/views/welcome/ColorSelector.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/views/welcome/ColorSelector.tsx b/src/views/welcome/ColorSelector.tsx index e43aab104..329164de6 100644 --- a/src/views/welcome/ColorSelector.tsx +++ b/src/views/welcome/ColorSelector.tsx @@ -184,7 +184,10 @@ const ColorSelector: Screen<"ColorSelector"> = ({ route, navigation }) => { if (!settings) { playSound(LEson6); } - navigation.navigate("AccountStack", {onboard: true}); + navigation.reset({ + index: 0, + routes: [{ name: "AccountStack" }], + }); }} disabled={!account?.personalization?.color} style={{ From f9acb187c4f354fcac9b58df43fe666e2cda94c6 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 18 Mar 2025 19:15:33 +0100 Subject: [PATCH 0908/1144] =?UTF-8?q?feat:=20augmentation=20de=20la=20qual?= =?UTF-8?q?it=C3=A9=20pour=20un=20meilleur=20rendu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Grades/Modals/GradeReaction.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/account/Grades/Modals/GradeReaction.tsx b/src/views/account/Grades/Modals/GradeReaction.tsx index 77571c328..ab53ab112 100644 --- a/src/views/account/Grades/Modals/GradeReaction.tsx +++ b/src/views/account/Grades/Modals/GradeReaction.tsx @@ -137,7 +137,7 @@ const GradeReaction: Screen<"GradeReaction"> = ({ navigation, route }) => { try { const photo = await cameraRef.current?.takePictureAsync({ - quality: 0.5, + quality: 0.75, skipProcessing: true, }); if (!photo?.uri) return; @@ -147,7 +147,7 @@ const GradeReaction: Screen<"GradeReaction"> = ({ navigation, route }) => { try { const compositeUri = await captureRef(composerRef, { format: "png", - quality: 0.5, + quality: 0.75, }); const reel = await createReel(grade, compositeUri, photo.uri); useGradesStore.setState((state) => ({ From 054294bd6e27bbf55bbee118d4e84e966c4af442 Mon Sep 17 00:00:00 2001 From: Mael <96339570+ryzenixx@users.noreply.github.com> Date: Tue, 18 Mar 2025 19:23:54 +0100 Subject: [PATCH 0909/1144] =?UTF-8?q?fix:=20correction=20des=20=C3=A9mojis?= =?UTF-8?q?=20non=20centr=C3=A9s=20dans=20la=20personnalisation=20des=20ma?= =?UTF-8?q?ti=C3=A8res?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/SettingsSubjects.tsx | 29 ++++++++++++------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/views/settings/SettingsSubjects.tsx b/src/views/settings/SettingsSubjects.tsx index 70fe16191..006cbfd02 100644 --- a/src/views/settings/SettingsSubjects.tsx +++ b/src/views/settings/SettingsSubjects.tsx @@ -296,21 +296,20 @@ const SettingsSubjects: Screen<"SettingsSubjects"> = ({ navigation }) => { width: 42, }} > - handleSubjectEmojiChange(selectedSubject[0], newEmoji)} - /> + + handleSubjectEmojiChange(selectedSubject[0], newEmoji)} + /> + From 2c3e9b35c1a3313a24af6a8e092b6f0dd4adc189 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 18 Mar 2025 19:36:31 +0100 Subject: [PATCH 0910/1144] =?UTF-8?q?fix(GradeReaction):=20notes=20autre?= =?UTF-8?q?=20que=20des=20nombres=20bugg=C3=A9es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Settings/ReelGallery.tsx | 16 +++++++------- .../account/Grades/Modals/GradeReaction.tsx | 21 +++++++++---------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/components/Settings/ReelGallery.tsx b/src/components/Settings/ReelGallery.tsx index 96ec4e1f1..bf963fe45 100644 --- a/src/components/Settings/ReelGallery.tsx +++ b/src/components/Settings/ReelGallery.tsx @@ -20,9 +20,9 @@ interface ReelModalProps { } // Components -const GradeIndicator = ({ value, outOf, color }: { value: number; outOf: number; color: string }) => ( +const GradeIndicator = ({ value, outOf, color }: { value: string; outOf: number; color: string }) => ( - {value.toFixed(2)} + {value} /{outOf} ); @@ -81,7 +81,7 @@ const ReelThumbnail = ({ reel, onPress, width }: { reel: Reel; onPress: () => vo color={reel.subjectdata.color} /> @@ -175,13 +175,15 @@ const styles = StyleSheet.create({ infoContainer: { position: "absolute", bottom: 10, - left: 20, - right: 20, - padding: 5, + paddingVertical: 5, + paddingHorizontal: 10, borderRadius: 100, flexDirection: "row", alignItems: "center", - justifyContent: "space-between", + alignSelf: "center", + justifyContent: "center", + gap: 3, + maxWidth: "85%", }, subjectBadge: { borderRadius: 100, diff --git a/src/views/account/Grades/Modals/GradeReaction.tsx b/src/views/account/Grades/Modals/GradeReaction.tsx index ab53ab112..9b9520be2 100644 --- a/src/views/account/Grades/Modals/GradeReaction.tsx +++ b/src/views/account/Grades/Modals/GradeReaction.tsx @@ -14,6 +14,7 @@ import { NativeText } from "@/components/Global/NativeComponents"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { isExpoGo } from "@/utils/native/expoGoAlert"; import { useAlert } from "@/providers/AlertProvider"; +import { Grade } from "@/services/shared/Grade"; // Types interface SubjectData { @@ -22,15 +23,6 @@ interface SubjectData { emoji: string; } -interface Grade { - id: string; - student: { value: number | null }; - outOf: { value: number | null }; - coefficient: number | null; - subjectName: string; - timestamp: number; -} - // Helper Functions const convertToBase64 = async (uri: string): Promise => { const response = await fetch(uri); @@ -60,7 +52,10 @@ const createReel = async ( imagewithouteffect: imageWithoutEffect, subjectdata: getSubjectData(grade.subjectName), grade: { - value: grade.student.value?.toString() ?? "", + value: + (grade.student.disabled + ? grade.student.status + : grade.student.value?.toFixed(2)) ?? "", outOf: grade.outOf.value?.toString() ?? "", coef: grade.coefficient?.toString() ?? "", } @@ -232,7 +227,11 @@ const GradeReaction: Screen<"GradeReaction"> = ({ navigation, route }) => {
- {grade.student.value} + + {grade.student.disabled + ? grade.student.status + : grade.student.value?.toFixed(2)} + /{grade.outOf.value}
From 4a40d94d640bc7bf8ef714d49ac2389092ae61b6 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 18 Mar 2025 19:41:57 +0100 Subject: [PATCH 0911/1144] =?UTF-8?q?fix:=20Date=20Picker=20compl=C3=A8tem?= =?UTF-8?q?ent=20al=C3=A9atoire=20sur=20Android?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Lessons/LessonsHeader.tsx | 38 ++++++++++----------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/views/account/Lessons/LessonsHeader.tsx b/src/views/account/Lessons/LessonsHeader.tsx index a42a08248..082c00275 100644 --- a/src/views/account/Lessons/LessonsHeader.tsx +++ b/src/views/account/Lessons/LessonsHeader.tsx @@ -74,27 +74,25 @@ const LessonsDateModal: React.FC = ({ } }, [showDatePicker]); - if (Platform.OS === "android") { + if (Platform.OS === "android" && showDatePicker) { return ( - showDatePicker && ( - { - onDateSelect(selectedDate); - setShowDatePicker(false); - }} - onError={() => { - setShowDatePicker(false); - }} - /> - ) + { + setShowDatePicker(false); + onDateSelect(selectedDate); + }} + onError={() => { + setShowDatePicker(false); + }} + /> ); } From 6a3f44fade2f9807ad30f067eb9e3fe6dee653e0 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 18 Mar 2025 19:48:41 +0100 Subject: [PATCH 0912/1144] =?UTF-8?q?fix:=20mise=20=C3=A0=20jour=20de=20la?= =?UTF-8?q?=20version=20=C3=A0=207.10.3=20et=20optimisation=20des=20compos?= =?UTF-8?q?ants=20React?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 4 +- src/router/navigator/atoms/TabItem.tsx | 57 ++++++++++++++------------ src/router/navigator/navigator.tsx | 17 ++------ src/router/navigator/tabs.tsx | 12 +++--- src/router/screens/account/stack.tsx | 7 +--- 5 files changed, 45 insertions(+), 52 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5381dbe7e..d3b9a4295 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.10.1", + "version": "7.10.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.10.1", + "version": "7.10.3", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.4", "@candlefinance/app-icon": "^0.4.5", diff --git a/src/router/navigator/atoms/TabItem.tsx b/src/router/navigator/atoms/TabItem.tsx index dc91035d2..64380610e 100644 --- a/src/router/navigator/atoms/TabItem.tsx +++ b/src/router/navigator/atoms/TabItem.tsx @@ -14,14 +14,14 @@ const TabItem: React.FC<{ navigation: any; isFocused: boolean; settings: any; -}> = ({ route, descriptor, navigation, isFocused, settings }) => { +}> = React.memo(({ route, descriptor, navigation, isFocused, settings }) => { const theme = useTheme(); const { playHaptics } = useSoundHapticsWrapper(); const { options } = descriptor; const label: string = options.tabBarLabel ?? options.title ?? route.name; - const onPress = () => { + const onPress = React.useCallback(() => { const event = navigation.emit({ type: "tabPress", target: route.key, @@ -39,26 +39,37 @@ const TabItem: React.FC<{ playHaptics("impact", { impact: Haptics.ImpactFeedbackStyle.Light, }); - }; + }, [isFocused, navigation, playHaptics, route.key, route.name]); - const onLongPress = () => { + const onLongPress = React.useCallback(() => { navigation.emit({ type: "tabLongPress", target: route.key }); - }; + }, [navigation, route.key]); const lottieRef = React.useRef(null); - const autoColor = colorsList.filter(c => c.hex.primary === theme.colors.primary)[0] || colorsList[0]; + const autoColor = React.useMemo(() => { + return colorsList.find(c => c.hex.primary === theme.colors.primary) || colorsList[0]; + }, [theme.colors.primary]); - const tabColor = isFocused ? - (autoColor?.hex?.lighter ? (theme.dark ? autoColor?.hex?.lighter : autoColor.hex.dark) : theme.colors.primary) : (theme.dark ? "#656c72" : "#8C9398"); + const tabColor = React.useMemo(() => { + return isFocused + ? autoColor?.hex?.lighter + ? theme.dark + ? autoColor?.hex?.lighter + : autoColor.hex.dark + : theme.colors.primary + : theme.dark + ? "#656c72" + : "#8C9398"; + }, [isFocused, autoColor, theme.dark, theme.colors.primary]); return ( @@ -95,12 +103,9 @@ const TabItem: React.FC<{ right: 0, bottom: 0, backgroundColor: tabColor + "22", - borderRadius: 8, + borderRadius: settings.hideTabTitles ? 8 : 80, borderCurve: "continuous", }, - !settings.hideTabTitles && { - borderRadius: 80, - }, ]} /> )} @@ -113,18 +118,16 @@ const TabItem: React.FC<{ keypath: "*", color: tabColor, }]} - style={[ - { - width: settings.hideTabTitles ? 28 : 26, - height: settings.hideTabTitles ? 28 : 26, - } - ]} + style={{ + width: settings.hideTabTitles ? 28 : 26, + height: settings.hideTabTitles ? 28 : 26, + }} ref={lottieRef} /> )} - {settings.hideTabTitles ? null : ( + {!settings.hideTabTitles && ( ); -}; +}); const styles = StyleSheet.create({ tabItemContainer: { diff --git a/src/router/navigator/navigator.tsx b/src/router/navigator/navigator.tsx index 639dff1a2..7a8232b07 100644 --- a/src/router/navigator/navigator.tsx +++ b/src/router/navigator/navigator.tsx @@ -1,3 +1,4 @@ +import React from "react"; import { BottomTabView } from "@react-navigation/bottom-tabs"; import { createNavigatorFactory, TabRouter, useNavigationBuilder } from "@react-navigation/native"; import PapillonNavigatorTabs from "./tabs"; @@ -12,14 +13,9 @@ const BottomTabNavigator: React.ComponentType = ({ screenOptions, ...rest }) => { - const {isTablet} = useScreenDimensions(); + const { isTablet } = useScreenDimensions(); - const { - state, - descriptors, - navigation, - NavigationContent - } = useNavigationBuilder(TabRouter, { + const { state, descriptors, navigation, NavigationContent } = useNavigationBuilder(TabRouter, { initialRouteName, backBehavior, children, @@ -28,10 +24,7 @@ const BottomTabNavigator: React.ComponentType = ({ return ( - + {isTablet && ( = ({ navigation={navigation} /> )} - - {!isTablet && ( , "NavigationContent">> = ({ state, descriptors, navigation }) => { @@ -25,9 +24,12 @@ const PapillonNavigatorTabs: React.FC state.routes, [state.routes]); - const tabs = account?.personalization.tabs?.filter((tab) => tab.enabled) - ?.map((tab) => allTabs.find((route) => route.name === tab.name)) - .filter(Boolean) || allTabs.slice(0, 5); + const tabs = useMemo(() => { + const enabledTabs = account?.personalization.tabs?.filter((tab) => tab.enabled); + return enabledTabs + ?.map((tab) => allTabs.find((route) => route.name === tab.name)) + .filter(Boolean) || allTabs.slice(0, 5); + }, [account?.personalization.tabs, allTabs]); return ( ; - const AccountStackScreen: Screen<"AccountStack"> = () => { useLayoutEffect(() => { SplashScreen.hideAsync(); }, []); return ( - + null}> {screens.map(({ name, component, options }) => ( ))} @@ -32,4 +29,4 @@ const AccountStackScreen: Screen<"AccountStack"> = () => { ); }; -export default AccountStackScreen; \ No newline at end of file +export default AccountStackScreen; From a3924729b1b71c95c5de0f9a68d977ff47941c8e Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 18 Mar 2025 19:50:32 +0100 Subject: [PATCH 0913/1144] =?UTF-8?q?fix:=20am=C3=A9lioration=20de=20la=20?= =?UTF-8?q?gestion=20des=20notifications=20et=20optimisation=20de=20l'?= =?UTF-8?q?=C3=A9tat=20de=20l'application?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.tsx | 127 +++++++++-------------- android/app/src/main/AndroidManifest.xml | 4 + 2 files changed, 51 insertions(+), 80 deletions(-) diff --git a/App.tsx b/App.tsx index 1e6f0412c..0216722da 100644 --- a/App.tsx +++ b/App.tsx @@ -1,37 +1,48 @@ import "@/background/BackgroundTasks"; -import { Notification } from "@notifee/react-native"; import Router from "@/router"; import { useFonts } from "expo-font"; import * as SplashScreen from "expo-splash-screen"; -import { LogBox, AppState, AppStateStatus } from "react-native"; +import { LogBox, AppState } from "react-native"; import React, { useEffect, useState, useRef, useCallback } from "react"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { useAccounts, useCurrentAccount } from "@/stores/account"; -import {AccountService, PrimaryAccount} from "@/stores/account/types"; +import { AccountService } from "@/stores/account/types"; import { log } from "@/utils/logger/logger"; import { isExpoGo } from "@/utils/native/expoGoAlert"; import { atobPolyfill, btoaPolyfill } from "js-base64"; import { registerBackgroundTasks } from "@/background/BackgroundTasks"; import { SoundHapticsProvider } from "@/hooks/Theme_Sound_Haptics"; import { PapillonNavigation } from "@/router/refs"; -import { RouteParameters } from "@/router/helpers/types"; -import { findAccountByID, getSwitchToFunction } from "@/background/utils/accounts"; +import { findAccountByID } from "@/background/utils/accounts"; SplashScreen.preventAutoHideAsync(); const DEFAULT_BACKGROUND_TIME = 15 * 60 * 1000; // 15 minutes -const BACKGROUND_LIMITS: Partial> = { +const BACKGROUND_LIMITS = { [AccountService.EcoleDirecte]: 15 * 60 * 1000, // 15 minutes [AccountService.Pronote]: 5 * 60 * 1000, // 5 minutes - [AccountService.Skolengo]: 12 * 60 * 60 * 1000, // 12 heures + [AccountService.Skolengo]: 12 * 60 * 60 * 1000, // 12 hours }; export default function App () { - const handleNotificationPress = async (notification: Notification) => { + const [appState, setAppState] = useState(AppState.currentState); + const backgroundStartTime = useRef(null); + const currentAccount = useCurrentAccount((store) => store.account); + const switchTo = useCurrentAccount((store) => store.switchTo); + const accounts = useAccounts((store) => store.accounts).filter(account => !account.isExternal); + + const [fontsLoaded] = useFonts({ + light: require("./assets/fonts/FixelText-Light.ttf"), + regular: require("./assets/fonts/FixelText-Regular.ttf"), + medium: require("./assets/fonts/FixelText-Medium.ttf"), + semibold: require("./assets/fonts/FixelText-SemiBold.ttf"), + bold: require("./assets/fonts/FixelText-Bold.ttf"), + }); + + const handleNotificationPress = async (notification) => { if (notification?.data) { - const switchTo = getSwitchToFunction(); - const accountID = notification.data.accountID as string; + const accountID = notification.data.accountID; const account = findAccountByID(accountID); if (account) { await switchTo(account); @@ -40,8 +51,7 @@ export default function App () { routes: [{ name: "AccountStack" }], }); setTimeout(() => { - // @ts-expect-error : on ne prend pas le state des routes en compte ici. - PapillonNavigation.current?.navigate(notification.data.page as keyof RouteParameters); + PapillonNavigation.current?.navigate(notification.data.page); }, 500); } } @@ -49,93 +59,56 @@ export default function App () { const checkInitialNotification = async () => { const notifee = (await import("@notifee/react-native")).default; - const initialNotification = await notifee.getInitialNotification(); if (initialNotification) { await handleNotificationPress(initialNotification.notification); } }; - useEffect(() => { - if (!isExpoGo()) checkInitialNotification(); + const getBackgroundTimeLimit = useCallback((service) => { + return BACKGROUND_LIMITS[service] ?? DEFAULT_BACKGROUND_TIME; }, []); - const [appState, setAppState] = useState(AppState.currentState); - const backgroundStartTime = useRef(null); - const currentAccount = useCurrentAccount((store) => store.account); - const switchTo = useCurrentAccount((store) => store.switchTo); - const accounts: PrimaryAccount[] = useAccounts((store) => store.accounts) - .filter(account => !account.isExternal) as PrimaryAccount[]; + const handleBackgroundState = useCallback(async () => { + if (!backgroundStartTime.current) return; - const [fontsLoaded, fontError] = useFonts({ - light: require("./assets/fonts/FixelText-Light.ttf"), - regular: require("./assets/fonts/FixelText-Regular.ttf"), - medium: require("./assets/fonts/FixelText-Medium.ttf"), - semibold: require("./assets/fonts/FixelText-SemiBold.ttf"), - bold: require("./assets/fonts/FixelText-Bold.ttf"), - }); + const timeInBackground = Date.now() - backgroundStartTime.current; + await AsyncStorage.setItem("@background_timestamp", Date.now().toString()); - const getBackgroundTimeLimit = useCallback((service: AccountService): number => { - return BACKGROUND_LIMITS[service] ?? DEFAULT_BACKGROUND_TIME; - }, []); + for (const account of accounts) { + const timeLimit = getBackgroundTimeLimit(account.service); + const timeInBackgroundSeconds = Math.floor(timeInBackground / 1000); - const handleBackgroundState = useCallback(async () => { - try { - if (!backgroundStartTime.current) return; - - const timeInBackground = Date.now() - backgroundStartTime.current; - await AsyncStorage.setItem("@background_timestamp", Date.now().toString()); - - for (const account of accounts) { - const timeLimit = getBackgroundTimeLimit(account.service); - const timeInBackgroundSeconds = Math.floor(timeInBackground / 1000); - const serviceName = AccountService[account.service]; - - log(`Checking account ${account.studentName.first} ${account.studentName.last}:`, "RefreshToken"); - log(`Time in background: ${timeInBackgroundSeconds}s`, "RefreshToken"); - log(`Time limit: ${timeLimit / 1000}s`, "RefreshToken"); - log(`Account type: ${serviceName}`, "RefreshToken"); - log(`Using ${BACKGROUND_LIMITS[account.service] ? "specific" : "default"} time limit`, "RefreshToken"); - - if (timeInBackground >= timeLimit && currentAccount === account) { - log(`⚠️ Refreshing account ${account.studentName.first} ${account.studentName.last} after ${timeInBackgroundSeconds}s in background`, "RefreshToken"); - - // Prevent React state updates during render - setTimeout(() => { - switchTo(account).catch((error) => { - log(`Error during switchTo: ${error}`, "RefreshToken"); - }); - }, 0); - - // Wait before processing next account - await new Promise(resolve => setTimeout(resolve, 1000)); - } + if (timeInBackground >= timeLimit && currentAccount === account) { + setTimeout(() => { + switchTo(account).catch((error) => { + log(`Error during switchTo: ${error}`, "RefreshToken"); + }); + }, 0); + await new Promise(resolve => setTimeout(resolve, 1000)); } - } catch (error) { - log(`Error handling background state: ${error}`, "RefreshToken"); } - }, [accounts, switchTo, getBackgroundTimeLimit]); + }, [accounts, switchTo, getBackgroundTimeLimit, currentAccount]); useEffect(() => { - const subscription = AppState.addEventListener("change", async (nextAppState: AppStateStatus) => { + if (!isExpoGo()) checkInitialNotification(); + }, []); + + useEffect(() => { + const subscription = AppState.addEventListener("change", async (nextAppState) => { if (appState === nextAppState) return; if (nextAppState === "active") { - log("🔄 App is active", "AppState"); if (!isExpoGo()) { const notifee = (await import("@notifee/react-native")).default; - await notifee.setBadgeCount(0); await notifee.cancelAllNotifications(); } - await handleBackgroundState(); backgroundStartTime.current = null; } else if (nextAppState.match(/inactive|background/)) { - log("App in background", "AppState"); backgroundStartTime.current = Date.now(); } - setAppState(nextAppState); }); @@ -154,24 +127,18 @@ export default function App () { if (!isExpoGo()) { registerBackgroundTasks(); - }; - }, []); + } - const applyGlobalPolyfills = useCallback(() => { const encoding = require("text-encoding"); Object.assign(global, { TextDecoder: encoding.TextDecoder, TextEncoder: encoding.TextEncoder, atob: atobPolyfill, - btoa: btoaPolyfill + btoa: btoaPolyfill, }); }, []); - useEffect(() => { - applyGlobalPolyfills(); - }, [applyGlobalPolyfills]); - - if (!fontsLoaded && !fontError) { + if (!fontsLoaded) { return null; } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 7d8a52236..86e6a9881 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -14,6 +14,10 @@ + + + + From 111ebb24d7c2c1c12fd393f5d4b477af034ff964 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 18 Mar 2025 19:51:11 +0100 Subject: [PATCH 0914/1144] =?UTF-8?q?fix:=20am=C3=A9lioration=20de=20la=20?= =?UTF-8?q?gestion=20de=20la=20navigation=20et=20optimisation=20des=20th?= =?UTF-8?q?=C3=A8mes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/index.tsx | 135 +++++++++++++++++++------------------------ 1 file changed, 61 insertions(+), 74 deletions(-) diff --git a/src/router/index.tsx b/src/router/index.tsx index da5c31a0f..7fcfcc164 100644 --- a/src/router/index.tsx +++ b/src/router/index.tsx @@ -1,15 +1,13 @@ -import React, { useEffect } from "react"; +import React, { useEffect, useState } from "react"; import { createNativeStackNavigator } from "@react-navigation/native-stack"; -import { NavigationContainer, NavigationState, PartialState, Theme } from "@react-navigation/native"; +import { NavigationContainer, Theme } from "@react-navigation/native"; import { Platform, StatusBar, View, useColorScheme } from "react-native"; import * as Linking from "expo-linking"; import screens from "@/router/screens"; -import type { RouteParameters } from "@/router/helpers/types"; import { PapillonDark, PapillonLight } from "@/router/helpers/themes"; import AlertProvider from "@/providers/AlertProvider"; import { SafeAreaProvider } from "react-native-safe-area-context"; import * as NavigationBar from "expo-navigation-bar"; - import { GestureHandlerRootView } from "react-native-gesture-handler"; import { useCurrentAccount } from "@/stores/account"; import { navigatorScreenOptions } from "./helpers/create-screen"; @@ -17,42 +15,27 @@ import { navigate } from "@/utils/logger/logger"; import { PapillonNavigation } from "./refs"; import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; -export const Stack = createNativeStackNavigator(); +export const Stack = createNativeStackNavigator(); const Router: React.FC = () => { const scheme = useColorScheme(); const { whatTheme } = useThemeSoundHaptics(); + const account = useCurrentAccount((store) => store.account!); + + const [theme, setTheme] = useState(scheme === "dark" ? PapillonDark : PapillonLight); + const [primaryColor, setPrimaryColor] = useState(theme.colors.primary); useEffect(() => { async function setNavigationBar () { - await NavigationBar.setPositionAsync("absolute"); - await NavigationBar.setBackgroundColorAsync("#ffffff00"); + if (Platform.OS === "android") { + await NavigationBar.setPositionAsync("absolute"); + await NavigationBar.setBackgroundColorAsync("#ffffff00"); + } } - if (Platform.OS === "android") { - setNavigationBar(); - } + setNavigationBar(); }, []); - const prefix = Linking.createURL("/"); - - const config = { - screens: { - PronoteManualURL: "url", - DevMenu: "dev", - }, - }; - - const linking = { - prefixes: [prefix], - config, - }; - - const [theme, setTheme] = React.useState(scheme === "dark" ? PapillonDark : PapillonLight); - const account = useCurrentAccount(store => store.account!); - - const [primaryColor, setPrimaryColor] = React.useState("#0000FF"); - useEffect(() => { switch (whatTheme) { case 0: @@ -69,60 +52,66 @@ const Router: React.FC = () => { useEffect(() => { setPrimaryColor(account?.personalization?.color?.hex?.primary || theme.colors.primary); - }, [account?.personalization]); + }, [account?.personalization, theme.colors.primary]); + + const prefix = Linking.createURL("/"); + + const linking = { + prefixes: [prefix], + config: { + screens: { + PronoteManualURL: "url", + DevMenu: "dev", + }, + }, + }; return ( {Platform.OS === "android" && ( )} - - - { - let str = ""; - let view: NavigationState | PartialState | undefined = state; - while (view?.routes) { - // @ts-expect-error (view is not undefined here bc of while condition, but ts think it can be) - str += "/" + view.routes[view.index].name; - // @ts-expect-error (same) - view = view.routes[view.index].state; - } - navigate(str); - }} + + { + let str = ""; + let view = state; + while (view?.routes) { + str += "/" + view.routes[view.index].name; + view = view.routes[view.index].state; + } + navigate(str); + }} > - - - - {screens.map((screen) => ( - // @ts-expect-error : type not compatible, but it works fine. - - ))} - - - + + + {screens.map((screen) => ( + + ))} + + @@ -130,6 +119,4 @@ const Router: React.FC = () => { ); }; - - export default Router; From bcde9aa728cfb9874bd0129cb783b08121a3de1d Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 18 Mar 2025 19:52:48 +0100 Subject: [PATCH 0915/1144] =?UTF-8?q?fix:=20suppression=20de=20la=20foncti?= =?UTF-8?q?on=20de=20confirmation=20de=20suppression=20de=20compte=20et=20?= =?UTF-8?q?ajustement=20du=20d=C3=A9lai=20de=20d=C3=A9sactivation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../settings/SettingsExternalServices.tsx | 27 +++---------------- 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/src/views/settings/SettingsExternalServices.tsx b/src/views/settings/SettingsExternalServices.tsx index 65e6d5aad..30aa45069 100644 --- a/src/views/settings/SettingsExternalServices.tsx +++ b/src/views/settings/SettingsExternalServices.tsx @@ -2,7 +2,7 @@ import React from "react"; import { ScrollView, View } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; -import { GraduationCap, Utensils, BookOpen, School, BookmarkMinus, Compass, Check, Trash2, Undo2, BadgeInfo, BadgeHelp } from "lucide-react-native"; +import { GraduationCap, Utensils, BookOpen, School, BookmarkMinus, Compass, Check, Trash2, BadgeInfo } from "lucide-react-native"; import ExternalServicesContainerCard from "@/components/Settings/ExternalServicesContainerCard"; import { NativeList, @@ -67,31 +67,10 @@ const SettingsExternalServices: Screen<"SettingsExternalServices"> = ({ { title: "Supprimer", icon: , - onPress: () => confirmDeleteAccount(account), - danger: true, - }, - ] - }); - }; - - const confirmDeleteAccount = (account: any) => { - showAlert({ - title: "Supprimer le compte", - message: "Es-tu sûr de vouloir supprimer ce compte ?", - icon: , - actions: [ - { - title: "Annuler", - icon: , - primary: false, - }, - { - title: "Confirmer", - icon: , onPress: () => removeAccount(account.localID), danger: true, - delayDisable: 5, - } + delayDisable: 3, + }, ] }); }; From 78e43761c255f5fdf3adb365329673fc9456fc5c Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 18 Mar 2025 19:54:05 +0100 Subject: [PATCH 0916/1144] fix: ajout de RNSVGFilters.bundle au projet Xcode et suppression des guillemets autour du nom du produit --- ios/Papillon.xcodeproj/project.pbxproj | 6 +- ios/Podfile.lock | 484 ++++++++++++------------- src/providers/AlertProvider.tsx | 143 +++----- 3 files changed, 298 insertions(+), 335 deletions(-) diff --git a/ios/Papillon.xcodeproj/project.pbxproj b/ios/Papillon.xcodeproj/project.pbxproj index e3b19051c..bbfe9f05e 100644 --- a/ios/Papillon.xcodeproj/project.pbxproj +++ b/ios/Papillon.xcodeproj/project.pbxproj @@ -315,6 +315,7 @@ "${PODS_CONFIGURATION_BUILD_DIR}/ExpoMediaLibrary/ExpoMediaLibrary_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/RNSVG/RNSVGFilters.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-launcher/EXDevLauncher.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-menu/EXDevMenu.bundle", @@ -332,6 +333,7 @@ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoMediaLibrary_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNSVGFilters.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevLauncher.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevMenu.bundle", @@ -455,7 +457,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = "Papillon"; + PRODUCT_NAME = Papillon; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -488,7 +490,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = "Papillon"; + PRODUCT_NAME = Papillon; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 3917628a1..17c9f7b62 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -241,7 +241,7 @@ PODS: - ExpoModulesCore - ExpoBackgroundFetch (12.0.1): - ExpoModulesCore - - ExpoBlur (13.0.2): + - ExpoBlur (13.0.3): - ExpoModulesCore - ExpoBrightness (12.0.1): - ExpoModulesCore @@ -303,7 +303,7 @@ PODS: - ExpoModulesCore - ExpoWebBrowser (13.0.3): - ExpoModulesCore - - EXSplashScreen (0.27.6): + - EXSplashScreen (0.27.7): - DoubleConversion - ExpoModulesCore - glog @@ -330,7 +330,7 @@ PODS: - UMAppLoader - EXUpdatesInterface (0.16.2): - ExpoModulesCore - - FBLazyVector (0.74.6) + - FBLazyVector (0.74.7) - fmt (9.1.0) - glog (0.3.5) - hermes-engine (0.74.6): @@ -375,27 +375,27 @@ PODS: - DoubleConversion - fmt (= 9.1.0) - glog - - RCTDeprecation (0.74.6) - - RCTRequired (0.74.6) - - RCTTypeSafety (0.74.6): - - FBLazyVector (= 0.74.6) - - RCTRequired (= 0.74.6) - - React-Core (= 0.74.6) - - React (0.74.6): - - React-Core (= 0.74.6) - - React-Core/DevSupport (= 0.74.6) - - React-Core/RCTWebSocket (= 0.74.6) - - React-RCTActionSheet (= 0.74.6) - - React-RCTAnimation (= 0.74.6) - - React-RCTBlob (= 0.74.6) - - React-RCTImage (= 0.74.6) - - React-RCTLinking (= 0.74.6) - - React-RCTNetwork (= 0.74.6) - - React-RCTSettings (= 0.74.6) - - React-RCTText (= 0.74.6) - - React-RCTVibration (= 0.74.6) - - React-callinvoker (0.74.6) - - React-Codegen (0.74.6): + - RCTDeprecation (0.74.7) + - RCTRequired (0.74.7) + - RCTTypeSafety (0.74.7): + - FBLazyVector (= 0.74.7) + - RCTRequired (= 0.74.7) + - React-Core (= 0.74.7) + - React (0.74.7): + - React-Core (= 0.74.7) + - React-Core/DevSupport (= 0.74.7) + - React-Core/RCTWebSocket (= 0.74.7) + - React-RCTActionSheet (= 0.74.7) + - React-RCTAnimation (= 0.74.7) + - React-RCTBlob (= 0.74.7) + - React-RCTImage (= 0.74.7) + - React-RCTLinking (= 0.74.7) + - React-RCTNetwork (= 0.74.7) + - React-RCTSettings (= 0.74.7) + - React-RCTText (= 0.74.7) + - React-RCTVibration (= 0.74.7) + - React-callinvoker (0.74.7) + - React-Codegen (0.74.7): - DoubleConversion - glog - hermes-engine @@ -415,12 +415,12 @@ PODS: - React-utils - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - React-Core (0.74.6): + - React-Core (0.74.7): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - RCTDeprecation - - React-Core/Default (= 0.74.6) + - React-Core/Default (= 0.74.7) - React-cxxreact - React-featureflags - React-hermes @@ -432,7 +432,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/CoreModulesHeaders (0.74.6): + - React-Core/CoreModulesHeaders (0.74.7): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -449,7 +449,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/Default (0.74.6): + - React-Core/Default (0.74.7): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -465,13 +465,13 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/DevSupport (0.74.6): + - React-Core/DevSupport (0.74.7): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - RCTDeprecation - - React-Core/Default (= 0.74.6) - - React-Core/RCTWebSocket (= 0.74.6) + - React-Core/Default (= 0.74.7) + - React-Core/RCTWebSocket (= 0.74.7) - React-cxxreact - React-featureflags - React-hermes @@ -483,7 +483,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTActionSheetHeaders (0.74.6): + - React-Core/RCTActionSheetHeaders (0.74.7): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -500,7 +500,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTAnimationHeaders (0.74.6): + - React-Core/RCTAnimationHeaders (0.74.7): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -517,7 +517,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTBlobHeaders (0.74.6): + - React-Core/RCTBlobHeaders (0.74.7): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -534,7 +534,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTImageHeaders (0.74.6): + - React-Core/RCTImageHeaders (0.74.7): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -551,7 +551,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTLinkingHeaders (0.74.6): + - React-Core/RCTLinkingHeaders (0.74.7): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -568,7 +568,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTNetworkHeaders (0.74.6): + - React-Core/RCTNetworkHeaders (0.74.7): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -585,7 +585,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTSettingsHeaders (0.74.6): + - React-Core/RCTSettingsHeaders (0.74.7): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -602,7 +602,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTTextHeaders (0.74.6): + - React-Core/RCTTextHeaders (0.74.7): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -619,7 +619,7 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTVibrationHeaders (0.74.6): + - React-Core/RCTVibrationHeaders (0.74.7): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -636,12 +636,12 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-Core/RCTWebSocket (0.74.6): + - React-Core/RCTWebSocket (0.74.7): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - RCTDeprecation - - React-Core/Default (= 0.74.6) + - React-Core/Default (= 0.74.7) - React-cxxreact - React-featureflags - React-hermes @@ -653,36 +653,36 @@ PODS: - React-utils - SocketRocket (= 0.7.0) - Yoga - - React-CoreModules (0.74.6): + - React-CoreModules (0.74.7): - DoubleConversion - fmt (= 9.1.0) - RCT-Folly (= 2024.01.01.00) - - RCTTypeSafety (= 0.74.6) + - RCTTypeSafety (= 0.74.7) - React-Codegen - - React-Core/CoreModulesHeaders (= 0.74.6) - - React-jsi (= 0.74.6) + - React-Core/CoreModulesHeaders (= 0.74.7) + - React-jsi (= 0.74.7) - React-jsinspector - React-NativeModulesApple - React-RCTBlob - - React-RCTImage (= 0.74.6) + - React-RCTImage (= 0.74.7) - ReactCommon - SocketRocket (= 0.7.0) - - React-cxxreact (0.74.6): + - React-cxxreact (0.74.7): - boost (= 1.83.0) - DoubleConversion - fmt (= 9.1.0) - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - - React-callinvoker (= 0.74.6) - - React-debug (= 0.74.6) - - React-jsi (= 0.74.6) + - React-callinvoker (= 0.74.7) + - React-debug (= 0.74.7) + - React-jsi (= 0.74.7) - React-jsinspector - - React-logger (= 0.74.6) - - React-perflogger (= 0.74.6) - - React-runtimeexecutor (= 0.74.6) - - React-debug (0.74.6) - - React-Fabric (0.74.6): + - React-logger (= 0.74.7) + - React-perflogger (= 0.74.7) + - React-runtimeexecutor (= 0.74.7) + - React-debug (0.74.7) + - React-Fabric (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -693,20 +693,20 @@ PODS: - React-Core - React-cxxreact - React-debug - - React-Fabric/animations (= 0.74.6) - - React-Fabric/attributedstring (= 0.74.6) - - React-Fabric/componentregistry (= 0.74.6) - - React-Fabric/componentregistrynative (= 0.74.6) - - React-Fabric/components (= 0.74.6) - - React-Fabric/core (= 0.74.6) - - React-Fabric/imagemanager (= 0.74.6) - - React-Fabric/leakchecker (= 0.74.6) - - React-Fabric/mounting (= 0.74.6) - - React-Fabric/scheduler (= 0.74.6) - - React-Fabric/telemetry (= 0.74.6) - - React-Fabric/templateprocessor (= 0.74.6) - - React-Fabric/textlayoutmanager (= 0.74.6) - - React-Fabric/uimanager (= 0.74.6) + - React-Fabric/animations (= 0.74.7) + - React-Fabric/attributedstring (= 0.74.7) + - React-Fabric/componentregistry (= 0.74.7) + - React-Fabric/componentregistrynative (= 0.74.7) + - React-Fabric/components (= 0.74.7) + - React-Fabric/core (= 0.74.7) + - React-Fabric/imagemanager (= 0.74.7) + - React-Fabric/leakchecker (= 0.74.7) + - React-Fabric/mounting (= 0.74.7) + - React-Fabric/scheduler (= 0.74.7) + - React-Fabric/telemetry (= 0.74.7) + - React-Fabric/templateprocessor (= 0.74.7) + - React-Fabric/textlayoutmanager (= 0.74.7) + - React-Fabric/uimanager (= 0.74.7) - React-graphics - React-jsi - React-jsiexecutor @@ -715,7 +715,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/animations (0.74.6): + - React-Fabric/animations (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -734,7 +734,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/attributedstring (0.74.6): + - React-Fabric/attributedstring (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -753,7 +753,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/componentregistry (0.74.6): + - React-Fabric/componentregistry (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -772,7 +772,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/componentregistrynative (0.74.6): + - React-Fabric/componentregistrynative (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -791,7 +791,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components (0.74.6): + - React-Fabric/components (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -802,17 +802,17 @@ PODS: - React-Core - React-cxxreact - React-debug - - React-Fabric/components/inputaccessory (= 0.74.6) - - React-Fabric/components/legacyviewmanagerinterop (= 0.74.6) - - React-Fabric/components/modal (= 0.74.6) - - React-Fabric/components/rncore (= 0.74.6) - - React-Fabric/components/root (= 0.74.6) - - React-Fabric/components/safeareaview (= 0.74.6) - - React-Fabric/components/scrollview (= 0.74.6) - - React-Fabric/components/text (= 0.74.6) - - React-Fabric/components/textinput (= 0.74.6) - - React-Fabric/components/unimplementedview (= 0.74.6) - - React-Fabric/components/view (= 0.74.6) + - React-Fabric/components/inputaccessory (= 0.74.7) + - React-Fabric/components/legacyviewmanagerinterop (= 0.74.7) + - React-Fabric/components/modal (= 0.74.7) + - React-Fabric/components/rncore (= 0.74.7) + - React-Fabric/components/root (= 0.74.7) + - React-Fabric/components/safeareaview (= 0.74.7) + - React-Fabric/components/scrollview (= 0.74.7) + - React-Fabric/components/text (= 0.74.7) + - React-Fabric/components/textinput (= 0.74.7) + - React-Fabric/components/unimplementedview (= 0.74.7) + - React-Fabric/components/view (= 0.74.7) - React-graphics - React-jsi - React-jsiexecutor @@ -821,7 +821,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/inputaccessory (0.74.6): + - React-Fabric/components/inputaccessory (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -840,7 +840,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/legacyviewmanagerinterop (0.74.6): + - React-Fabric/components/legacyviewmanagerinterop (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -859,7 +859,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/modal (0.74.6): + - React-Fabric/components/modal (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -878,7 +878,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/rncore (0.74.6): + - React-Fabric/components/rncore (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -897,7 +897,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/root (0.74.6): + - React-Fabric/components/root (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -916,7 +916,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/safeareaview (0.74.6): + - React-Fabric/components/safeareaview (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -935,7 +935,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/scrollview (0.74.6): + - React-Fabric/components/scrollview (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -954,7 +954,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/text (0.74.6): + - React-Fabric/components/text (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -973,7 +973,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/textinput (0.74.6): + - React-Fabric/components/textinput (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -992,7 +992,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/unimplementedview (0.74.6): + - React-Fabric/components/unimplementedview (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -1011,7 +1011,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/view (0.74.6): + - React-Fabric/components/view (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -1031,7 +1031,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - Yoga - - React-Fabric/core (0.74.6): + - React-Fabric/core (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -1050,7 +1050,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/imagemanager (0.74.6): + - React-Fabric/imagemanager (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -1069,7 +1069,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/leakchecker (0.74.6): + - React-Fabric/leakchecker (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -1088,7 +1088,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/mounting (0.74.6): + - React-Fabric/mounting (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -1107,7 +1107,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/scheduler (0.74.6): + - React-Fabric/scheduler (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -1126,7 +1126,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/telemetry (0.74.6): + - React-Fabric/telemetry (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -1145,7 +1145,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/templateprocessor (0.74.6): + - React-Fabric/templateprocessor (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -1164,7 +1164,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/textlayoutmanager (0.74.6): + - React-Fabric/textlayoutmanager (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -1184,7 +1184,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/uimanager (0.74.6): + - React-Fabric/uimanager (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog @@ -1203,45 +1203,45 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-FabricImage (0.74.6): + - React-FabricImage (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog - hermes-engine - RCT-Folly/Fabric (= 2024.01.01.00) - - RCTRequired (= 0.74.6) - - RCTTypeSafety (= 0.74.6) + - RCTRequired (= 0.74.7) + - RCTTypeSafety (= 0.74.7) - React-Fabric - React-graphics - React-ImageManager - React-jsi - - React-jsiexecutor (= 0.74.6) + - React-jsiexecutor (= 0.74.7) - React-logger - React-rendererdebug - React-utils - ReactCommon - Yoga - - React-featureflags (0.74.6) - - React-graphics (0.74.6): + - React-featureflags (0.74.7) + - React-graphics (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog - RCT-Folly/Fabric (= 2024.01.01.00) - - React-Core/Default (= 0.74.6) + - React-Core/Default (= 0.74.7) - React-utils - - React-hermes (0.74.6): + - React-hermes (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - - React-cxxreact (= 0.74.6) + - React-cxxreact (= 0.74.7) - React-jsi - - React-jsiexecutor (= 0.74.6) + - React-jsiexecutor (= 0.74.7) - React-jsinspector - - React-perflogger (= 0.74.6) + - React-perflogger (= 0.74.7) - React-runtimeexecutor - - React-ImageManager (0.74.6): + - React-ImageManager (0.74.7): - glog - RCT-Folly/Fabric - React-Core/Default @@ -1250,41 +1250,41 @@ PODS: - React-graphics - React-rendererdebug - React-utils - - React-jserrorhandler (0.74.6): + - React-jserrorhandler (0.74.7): - RCT-Folly/Fabric (= 2024.01.01.00) - React-debug - React-jsi - React-Mapbuffer - - React-jsi (0.74.6): + - React-jsi (0.74.7): - boost (= 1.83.0) - DoubleConversion - fmt (= 9.1.0) - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - - React-jsiexecutor (0.74.6): + - React-jsiexecutor (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - - React-cxxreact (= 0.74.6) - - React-jsi (= 0.74.6) + - React-cxxreact (= 0.74.7) + - React-jsi (= 0.74.7) - React-jsinspector - - React-perflogger (= 0.74.6) - - React-jsinspector (0.74.6): + - React-perflogger (= 0.74.7) + - React-jsinspector (0.74.7): - DoubleConversion - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - React-featureflags - React-jsi - - React-runtimeexecutor (= 0.74.6) - - React-jsitracing (0.74.6): + - React-runtimeexecutor (= 0.74.7) + - React-jsitracing (0.74.7): - React-jsi - - React-logger (0.74.6): + - React-logger (0.74.7): - glog - - React-Mapbuffer (0.74.6): + - React-Mapbuffer (0.74.7): - glog - React-debug - react-native-cookies (6.2.1): @@ -1314,7 +1314,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - react-native-ios-utilities (5.1.1): + - react-native-ios-utilities (5.1.2): - ComputableLayout (~> 0.7) - DGSwiftUtilities (~> 0.46) - DoubleConversion @@ -1412,8 +1412,8 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - React-nativeconfig (0.74.6) - - React-NativeModulesApple (0.74.6): + - React-nativeconfig (0.74.7) + - React-NativeModulesApple (0.74.7): - glog - hermes-engine - React-callinvoker @@ -1424,10 +1424,10 @@ PODS: - React-runtimeexecutor - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - React-perflogger (0.74.6) - - React-RCTActionSheet (0.74.6): - - React-Core/RCTActionSheetHeaders (= 0.74.6) - - React-RCTAnimation (0.74.6): + - React-perflogger (0.74.7) + - React-RCTActionSheet (0.74.7): + - React-Core/RCTActionSheetHeaders (= 0.74.7) + - React-RCTAnimation (0.74.7): - RCT-Folly (= 2024.01.01.00) - RCTTypeSafety - React-Codegen @@ -1435,7 +1435,7 @@ PODS: - React-jsi - React-NativeModulesApple - ReactCommon - - React-RCTAppDelegate (0.74.6): + - React-RCTAppDelegate (0.74.7): - RCT-Folly (= 2024.01.01.00) - RCTRequired - RCTTypeSafety @@ -1459,7 +1459,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon - - React-RCTBlob (0.74.6): + - React-RCTBlob (0.74.7): - DoubleConversion - fmt (= 9.1.0) - hermes-engine @@ -1472,7 +1472,7 @@ PODS: - React-NativeModulesApple - React-RCTNetwork - ReactCommon - - React-RCTFabric (0.74.6): + - React-RCTFabric (0.74.7): - glog - hermes-engine - RCT-Folly/Fabric (= 2024.01.01.00) @@ -1492,7 +1492,7 @@ PODS: - React-runtimescheduler - React-utils - Yoga - - React-RCTImage (0.74.6): + - React-RCTImage (0.74.7): - RCT-Folly (= 2024.01.01.00) - RCTTypeSafety - React-Codegen @@ -1501,14 +1501,14 @@ PODS: - React-NativeModulesApple - React-RCTNetwork - ReactCommon - - React-RCTLinking (0.74.6): + - React-RCTLinking (0.74.7): - React-Codegen - - React-Core/RCTLinkingHeaders (= 0.74.6) - - React-jsi (= 0.74.6) + - React-Core/RCTLinkingHeaders (= 0.74.7) + - React-jsi (= 0.74.7) - React-NativeModulesApple - ReactCommon - - ReactCommon/turbomodule/core (= 0.74.6) - - React-RCTNetwork (0.74.6): + - ReactCommon/turbomodule/core (= 0.74.7) + - React-RCTNetwork (0.74.7): - RCT-Folly (= 2024.01.01.00) - RCTTypeSafety - React-Codegen @@ -1516,7 +1516,7 @@ PODS: - React-jsi - React-NativeModulesApple - ReactCommon - - React-RCTSettings (0.74.6): + - React-RCTSettings (0.74.7): - RCT-Folly (= 2024.01.01.00) - RCTTypeSafety - React-Codegen @@ -1524,23 +1524,23 @@ PODS: - React-jsi - React-NativeModulesApple - ReactCommon - - React-RCTText (0.74.6): - - React-Core/RCTTextHeaders (= 0.74.6) + - React-RCTText (0.74.7): + - React-Core/RCTTextHeaders (= 0.74.7) - Yoga - - React-RCTVibration (0.74.6): + - React-RCTVibration (0.74.7): - RCT-Folly (= 2024.01.01.00) - React-Codegen - React-Core/RCTVibrationHeaders - React-jsi - React-NativeModulesApple - ReactCommon - - React-rendererdebug (0.74.6): + - React-rendererdebug (0.74.7): - DoubleConversion - fmt (= 9.1.0) - RCT-Folly (= 2024.01.01.00) - React-debug - - React-rncore (0.74.6) - - React-RuntimeApple (0.74.6): + - React-rncore (0.74.7) + - React-RuntimeApple (0.74.7): - hermes-engine - RCT-Folly/Fabric (= 2024.01.01.00) - React-callinvoker @@ -1558,7 +1558,7 @@ PODS: - React-runtimeexecutor - React-RuntimeHermes - React-utils - - React-RuntimeCore (0.74.6): + - React-RuntimeCore (0.74.7): - glog - hermes-engine - RCT-Folly/Fabric (= 2024.01.01.00) @@ -1571,9 +1571,9 @@ PODS: - React-runtimeexecutor - React-runtimescheduler - React-utils - - React-runtimeexecutor (0.74.6): - - React-jsi (= 0.74.6) - - React-RuntimeHermes (0.74.6): + - React-runtimeexecutor (0.74.7): + - React-jsi (= 0.74.7) + - React-RuntimeHermes (0.74.7): - hermes-engine - RCT-Folly/Fabric (= 2024.01.01.00) - React-featureflags @@ -1584,7 +1584,7 @@ PODS: - React-nativeconfig - React-RuntimeCore - React-utils - - React-runtimescheduler (0.74.6): + - React-runtimescheduler (0.74.7): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) @@ -1596,58 +1596,58 @@ PODS: - React-rendererdebug - React-runtimeexecutor - React-utils - - React-utils (0.74.6): + - React-utils (0.74.7): - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - React-debug - - React-jsi (= 0.74.6) - - ReactCommon (0.74.6): - - ReactCommon/turbomodule (= 0.74.6) - - ReactCommon/turbomodule (0.74.6): + - React-jsi (= 0.74.7) + - ReactCommon (0.74.7): + - ReactCommon/turbomodule (= 0.74.7) + - ReactCommon/turbomodule (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - - React-callinvoker (= 0.74.6) - - React-cxxreact (= 0.74.6) - - React-jsi (= 0.74.6) - - React-logger (= 0.74.6) - - React-perflogger (= 0.74.6) - - ReactCommon/turbomodule/bridging (= 0.74.6) - - ReactCommon/turbomodule/core (= 0.74.6) - - ReactCommon/turbomodule/bridging (0.74.6): + - React-callinvoker (= 0.74.7) + - React-cxxreact (= 0.74.7) + - React-jsi (= 0.74.7) + - React-logger (= 0.74.7) + - React-perflogger (= 0.74.7) + - ReactCommon/turbomodule/bridging (= 0.74.7) + - ReactCommon/turbomodule/core (= 0.74.7) + - ReactCommon/turbomodule/bridging (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - - React-callinvoker (= 0.74.6) - - React-cxxreact (= 0.74.6) - - React-jsi (= 0.74.6) - - React-logger (= 0.74.6) - - React-perflogger (= 0.74.6) - - ReactCommon/turbomodule/core (0.74.6): + - React-callinvoker (= 0.74.7) + - React-cxxreact (= 0.74.7) + - React-jsi (= 0.74.7) + - React-logger (= 0.74.7) + - React-perflogger (= 0.74.7) + - ReactCommon/turbomodule/core (0.74.7): - DoubleConversion - fmt (= 9.1.0) - glog - hermes-engine - RCT-Folly (= 2024.01.01.00) - - React-callinvoker (= 0.74.6) - - React-cxxreact (= 0.74.6) - - React-debug (= 0.74.6) - - React-jsi (= 0.74.6) - - React-logger (= 0.74.6) - - React-perflogger (= 0.74.6) - - React-utils (= 0.74.6) + - React-callinvoker (= 0.74.7) + - React-cxxreact (= 0.74.7) + - React-debug (= 0.74.7) + - React-jsi (= 0.74.7) + - React-logger (= 0.74.7) + - React-perflogger (= 0.74.7) + - React-utils (= 0.74.7) - RNCAsyncStorage (1.23.1): - React-Core - RNCMaskedView (0.3.1): - React-Core - RNDateTimePicker (8.0.1): - React-Core - - RNGestureHandler (2.22.1): + - RNGestureHandler (2.24.0): - DoubleConversion - glog - hermes-engine @@ -1694,7 +1694,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNScreens (4.9.1): + - RNScreens (4.9.2): - DoubleConversion - glog - hermes-engine @@ -1716,7 +1716,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNShare (12.0.3): + - RNShare (12.0.9): - DoubleConversion - glog - hermes-engine @@ -1737,7 +1737,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNSVG (15.8.0): + - RNSVG (15.11.2): - React-Core - SocketRocket (0.7.0) - SwiftUIReactNative (5.0.0): @@ -2130,7 +2130,7 @@ SPEC CHECKSUMS: expo-dev-menu-interface: 5764ad537419c1a5e8f66f668e29c81e8aca290c ExpoAsset: 323700f291684f110fb55f0d4022a3362ea9f875 ExpoBackgroundFetch: a06c553ecaf0bade0acd691042d996b9ce926327 - ExpoBlur: fa53f874e7b208bc3756d1bf07903c12e790beb1 + ExpoBlur: 4d32f9e33bab18ae2ee079c7b0422e4210d49c97 ExpoBrightness: c184f0ef116a51d7f5c1dd4c253d4d9806a5e022 ExpoCamera: 929be541d1c1319fcf32f9f5d9df8b97804346b5 ExpoClipboard: 23d203f5d4843699fbc45be1cc4fe1fbd811a6fa @@ -2149,85 +2149,85 @@ SPEC CHECKSUMS: ExpoStoreReview: 15f9a636b62ff00bb21cbe9a9fe22f0239da4481 ExpoSystemUI: d4f065a016cae6721b324eb659cdee4d4cf0cb26 ExpoWebBrowser: 7595ccac6938eb65b076385fd23d035db9ecdc8e - EXSplashScreen: 17a656c08a0095be15b620c52e61dfdb665863d2 + EXSplashScreen: 3cad09949c2ca6730cbb2801b8c51cb87692425a EXTaskManager: 9c3520305c3aa1b4a12a7c6d1e3f85f2779c06e9 EXUpdatesInterface: 996527fd7d1a5d271eb523258d603f8f92038f24 - FBLazyVector: 4b1589d37c9ff4dba11a63083fe7515fad3ac111 + FBLazyVector: 04dc972982abebd96d823752c3a426bbe6ac397f fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120 glog: fdfdfe5479092de0c4bdbebedd9056951f092c4f hermes-engine: 2102c92e54a031a270fd1fe84169ec8a0901b7bd lottie-ios: e047b1d2e6239b787cc5e9755b988869cf190494 lottie-react-native: 4279da8b681e89c29a2adb9f99985d6cf372d49d RCT-Folly: 02617c592a293bd6d418e0a88ff4ee1f88329b47 - RCTDeprecation: 5f1d7e1f8ef6c53f0207e3ac0d0ca23575e8a6ab - RCTRequired: dcfd24ece09940bbf24b7c2974f4eb68a9baee55 - RCTTypeSafety: 3d65944055cc73f3bb28c3f05c7eaff2bb7ceb83 - React: c5e9f3c07a890a7e2a1ec9b79faa5c53dd7aee01 - React-callinvoker: 9ac986dbbd0e1b3463cb740b12c0b37dbcd15fed - React-Codegen: f5bd8446ba2b7d4a7bb4b25e137f44f286eb98f7 - React-Core: e8de3613460de4f02cdf6a0d726526fe273766b4 - React-CoreModules: 704fd52f83780e1870a946d5c3ea6ea1175aa523 - React-cxxreact: 75572783e7feae5a9f67157ea13968bf5ab274a3 - React-debug: 180e1bf4a97fa4404ee7fb68952cace122aa9d73 - React-Fabric: 1118712bb2dfae21b7d03d5daf419018e867f8e9 - React-FabricImage: 3878f51fa0ac860fb733f0a3c958442a7b5ec587 - React-featureflags: 05fabc5e165fa3864c879556e83a455b8a0573fa - React-graphics: 77bb68d9d913682a23c621df01ab61b2e58a5c70 - React-hermes: e4c48dab6830b282e1b41023cdae9b5c1f7ae075 - React-ImageManager: 6dbe9e5578c03d48b25e646ee65faa63b10e9544 - React-jserrorhandler: 16f7ef986fd20a2d342e5430c9dd0502a4136320 - React-jsi: b34c85593159261ce19f9c5fd0a627b3dc1483c6 - React-jsiexecutor: dcf7df38a296d104b196c193a0111d3f8a46da0b - React-jsinspector: 7dfaff7a0f57d23eeab023fe6a243f7707a14f6d - React-jsitracing: df84cc252a1f4bb0970f7fe13c470451b18c2cbb - React-logger: de9b65c8c7b71a663e6e99d347b1c445f5190c39 - React-Mapbuffer: 766bb4d8f655d816913325b353d800debbde7209 + RCTDeprecation: c4e6e3f6d44f76c45a964b40fa3eb2475259c027 + RCTRequired: c4886806a178cd895cd4a17dae1642b72e7e8233 + RCTTypeSafety: 4e9f36465ccbcca7b62f5cb8fc1ff2c997c3b92e + React: 7f6ee889aa17245726efe5c0be52389e58d9d7c1 + React-callinvoker: 0155d33f43924c206dfaa040c020d0404bbb54d6 + React-Codegen: 19dcd3c5d2418af62dcab56a09ff62accee8b60e + React-Core: da6746240394ea2bb828e6e93baed4dea3c27689 + React-CoreModules: 319cbc30aee816ea4716115b0f77035b95902a80 + React-cxxreact: b8e823d419880d779be49fc049732e8facf64fc7 + React-debug: 81d2423e256c8f0dad94f368e7a5450b3885c5b5 + React-Fabric: a96f6898862c047023f140a787289ef4111e059d + React-FabricImage: 0cb85c8a9672cf3eddf340d96806b0d57e5a4d60 + React-featureflags: c3e59ddabf0bc2b8e125aff4aab6943112f5d852 + React-graphics: 4426a34fd9695e861d644fac89ab6ba5a42e110d + React-hermes: 010c8dc210afa547991e13fc5acee7e63cf9f2e8 + React-ImageManager: 4fc40ebeac12716ed8beeadc17785b799096cae9 + React-jserrorhandler: d2621ce6fee5bf965ba8968a5315f1c6ea6fd731 + React-jsi: df1b6ca5308a888cbdf44c5035ca2e46822f1902 + React-jsiexecutor: 88e8f50d2e0cdb1531c6d956c148577698311ac8 + React-jsinspector: cbd4d9bf7d0944f662af337727eec69b9eca1db6 + React-jsitracing: 549d1177ff45058b1300652db58add8c357a11ed + React-logger: 87cddd161bd9784d60fc5528f21c57ca07d2962a + React-Mapbuffer: b6e208217a18044f0f1a183be8f6756ad67b42d7 react-native-cookies: f54fcded06bb0cda05c11d86788020b43528a26c react-native-ios-context-menu: 486ed6e4d0fd44799f1b54f866e96d56c47beb48 - react-native-ios-utilities: 1e1fa0b4e68876e37bd26312f09aa07d79268c65 + react-native-ios-utilities: 9e06932d36dc71680110fde2f84ed89a5e8c172f react-native-ios-visual-effect-view: ce2f72588dab1a38049131887a1c4660e41dc8e1 react-native-netinfo: bdb108d340cdb41875c9ced535977cac6d2ff321 react-native-pager-view: c1e29e1a6105a02807392ba822ad322447a72f55 react-native-safe-area-context: a240ad4b683349e48b1d51fed1611138d1bdad97 react-native-view-shot: 6b7ed61d77d88580fed10954d45fad0eb2d47688 react-native-webview: 05bae3a03a1e4f59568dfc05286c0ebf8954106c - React-nativeconfig: c36a079fa219a9911070cc0058b746407e1ef47d - React-NativeModulesApple: eab84dd7bda0650f3ce41c53f76ffd49d689763f - React-perflogger: 9f21c9e3d8d220833e649a141fed8e5ca08977d9 - React-RCTActionSheet: 4c1f0dc56952f21a904e9f3bf74253eebee1d1d9 - React-RCTAnimation: 2c0b963d4fd978ce35daa330986a8bc442c7517a - React-RCTAppDelegate: bfb8293aa467aae8a28050e4095b0ceff284cbd5 - React-RCTBlob: a440574d805536c58c0c409cb5058334c8d2886c - React-RCTFabric: 6ea72ddf222ea1e373d0cbac88a1c62355701995 - React-RCTImage: e63bc8abbad2c5a4eda53ff35282d83bc9df7559 - React-RCTLinking: 12c6962253fd2f2494231eb8ae2fecae71e54e2f - React-RCTNetwork: 46df47440bd2bf63b0ca0a3c640471243ed2922a - React-RCTSettings: 5e1dfa02ae2d6cf54b3fdfebaa80837540c50847 - React-RCTText: 1c045a74e4fda674523c932f53bdd15b2a3ba085 - React-RCTVibration: 2ba9de92ae71526b3e02b8b8b2fce5cbf47c393f - React-rendererdebug: acb324f4975412bb14d55b29dd5ca6961b5fa06a - React-rncore: 63db76511a92db6cf9649c9d6567e014b7eeb6f5 - React-RuntimeApple: 6ecb0a470d1ef989895a4e5d31980004378ebf71 - React-RuntimeCore: 2794fdb42f7d37f3c877f614e12a0a240b594815 - React-runtimeexecutor: bf091a7f5f5130daab6d8216aaa290374b214cb8 - React-RuntimeHermes: 73249fcc108708a137119de18c3d40ac5ab90160 - React-runtimescheduler: b63ebebd3e000e0ba4ac19ca69bdac071559ad57 - React-utils: 2955bdc1b2ed495f14dc7d3bfbbb7e3624cfc0fc - ReactCommon: 5c504a77030c7ab89eee75b1725b80d8cee7f5d7 + React-nativeconfig: 3b359be06d9ee8d64c1eacbca4f1040f331573fd + React-NativeModulesApple: 511a01d5be0fdb768f6438dd672a79808451ab00 + React-perflogger: e9ebfc705cb9f60ef5d471637350ccab7abd0444 + React-RCTActionSheet: 75cf1acf78e9c2eab4431c3bec100a6462a7f0d5 + React-RCTAnimation: 57a1a83a919ead49436ebe69a902d823f2059e61 + React-RCTAppDelegate: 7732b1367774126055f64b3154cef2ba7d28eea2 + React-RCTBlob: efb9cd99a32b22ca94989adde148a9938a8f6fcc + React-RCTFabric: 08a9ff1c3e612f6cc0b4c1cb06f748daea4073d1 + React-RCTImage: 862823815db42c847cbd875b5ccdd64d320da693 + React-RCTLinking: 682e3018e2192f2cf87ddc601cccdac53fb8b1b7 + React-RCTNetwork: 2945ab8c3ff4e2a6603b5d9a220c35b49f3a3ae0 + React-RCTSettings: 807d08082b799d79a00b9fb3ac1590183afaa241 + React-RCTText: d0e6f900dc2d9796240ff991abec9af8ef713a7b + React-RCTVibration: 4feafdb46bebf5380ab343d86b3ddfa4aadcb071 + React-rendererdebug: 3c1a0a5775f81aab7c1fdd0cb0ce0913802170ce + React-rncore: bb90d227f926d19683f9c5790d8e3687a4db051a + React-RuntimeApple: 03bc1bb50bb621d3dd5a7ad0b0fd9ae5b7aa7d03 + React-RuntimeCore: 5411916dbb5d27b2cade9781bbe210ae23906d41 + React-runtimeexecutor: 99640ce20e73e06301dd5e18cc6646fa0968347b + React-RuntimeHermes: 872b0ab24196e98f7ef6857241edaeb9c576a7fb + React-runtimescheduler: bd24ce5b35c288355514aa42cacc9dc782d12dba + React-utils: eb18bf39ea87c4d38f175f41d77b07093eaf7765 + ReactCommon: 9ed4522c478fc82fd9822fd2be960875fccb2c45 RNCAsyncStorage: 826b603ae9c0f88b5ac4e956801f755109fa4d5c RNCMaskedView: 090213d32d8b3bb83a4dcb7d12c18f0152591906 RNDateTimePicker: b6a9b35a785ecbe12b4e7d6de5439d0aa4614146 - RNGestureHandler: f7abf21d594742be28a3a72528069225a3187e26 + RNGestureHandler: 4d82e79c24b18e83040ed9d6f1a178267c5c6aa8 RNNotifee: 935f3ea8c134c88cbf8b13ea0c97c72c09ad2116 RNReanimated: 35f9ac9c3ac42d0497ebd1cce5c39d7687a8493e - RNScreens: 3cafbf1a9de9fa8ddb547ada94704cbb59540f39 - RNShare: 22717e910836a66cb7255b3b8c4ab06cbe346e27 - RNSVG: 8b1a777d54096b8c2a0fd38fc9d5a454332bbb4d + RNScreens: 2557adbeb88fe3dd34831ce269ad1880af807bba + RNShare: ddc90a41a808268fd75061da52e0239ad416df28 + RNSVG: e451741721b9fd3db71b06aafe9335d09a189692 SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d SwiftUIReactNative: 3241f6b3d9cf6dc29b3dc64f7d838770f19fd604 UMAppLoader: f17a5ee8e85b536ace0fc254b447a37ed198d57e VisualEffectBlurView: 2db84754ea90a2f4eccb0e84038653caddaa4eff - Yoga: 4f4f07a17818e76d1b04edc01b68b6d49a682100 + Yoga: 9db4b2da1ed5d8e0c94158f9ac379c8b1b529f59 ZXingObjC: 8898711ab495761b2dbbdec76d90164a6d7e14c5 PODFILE CHECKSUM: 52c2fc0f0da6749b1aa3175a834dddc6986ae26c diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index 98744653f..dd359cb02 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -1,27 +1,13 @@ import { useTheme } from "@react-navigation/native"; import { Check } from "lucide-react-native"; -import React, { - createContext, - useState, - useContext, - ReactNode, - useEffect, -} from "react"; +import React, { createContext, useState, useContext, useEffect, ReactNode } from "react"; import { Modal, View, Text, StyleSheet, Pressable } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import Reanimated, { - FadeIn, - FadeOut, - LinearTransition, -} from "react-native-reanimated"; -import { - anim2Papillon, - PapillonContextEnter, - PapillonContextExit, -} from "@/utils/ui/animations"; +import Reanimated, { FadeIn, FadeOut, LinearTransition } from "react-native-reanimated"; import { BlurView } from "expo-blur"; import NativeTouchable from "@/components/Global/NativeTouchable"; import useScreenDimensions from "@/hooks/useScreenDimensions"; +import { anim2Papillon, PapillonContextEnter, PapillonContextExit } from "@/utils/ui/animations"; type AlertAction = { title: string; @@ -83,21 +69,18 @@ const AlertProvider = ({ children }: AlertProviderProps) => { setAlert({ title, message, icon, actions }); setVisible(true); - const initialDelays: { [key: string]: number } = {}; - actions.forEach((action) => { - if (action.delayDisable) { - initialDelays[action.title] = action.delayDisable; - } else { - initialDelays[action.title] = 0; - } - }); + const initialDelays = actions.reduce((acc, action) => { + acc[action.title] = action.delayDisable || 0; + return acc; + }, {} as { [key: string]: number }); + setDelays(initialDelays); }; - function hideAlert () { + const hideAlert = () => { setVisible(false); setTimeout(() => setAlert(null), 150); - } + }; useEffect(() => { const interval = setInterval(() => { @@ -112,18 +95,15 @@ const AlertProvider = ({ children }: AlertProviderProps) => { }); }, 1000); - setTimeout(() => { - return () => clearInterval(interval); - }, 1000); + return () => clearInterval(interval); }, []); return ( {children} - {alert && ( - {visible && + {visible && ( { @@ -147,10 +124,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { { {alert.icon && - React.cloneElement(alert.icon, { - color: colors.text, - size: 24, - })} + React.cloneElement(alert.icon, { + color: colors.text, + size: 24, + })} {alert.title} @@ -177,8 +151,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { { borderColor: colors.text + "20", backgroundColor: colors.text + "06", - flexDirection: - (alert.actions ?? []).length > 2 ? "column" : "row", + flexDirection: alert.actions?.length > 2 ? "column" : "row", alignItems: "center", }, ]} @@ -197,11 +170,10 @@ const AlertProvider = ({ children }: AlertProviderProps) => { key={title} layout={anim2Papillon(LinearTransition)} style={[ - (alert.actions?.length === 1 || (alert.actions ?? []).length > 2) && styles.singleButtonContainer, - { - borderRadius: 300, - overflow: "hidden", - } + alert.actions?.length === 1 || alert.actions?.length > 2 + ? styles.singleButtonContainer + : null, + { borderRadius: 300, overflow: "hidden" }, ]} > { hideAlert(); onPress?.(); }} - contentContainerStyle={{ - borderRadius: 300, - overflow: "hidden", - }} + contentContainerStyle={{ borderRadius: 300, overflow: "hidden" }} style={[ - (alert.actions?.length === 1 || (alert.actions ?? []).length > 2) && styles.singleButtonContainer, - { - borderRadius: 300, - overflow: "hidden", - } + alert.actions?.length === 1 || alert.actions?.length > 2 + ? styles.singleButtonContainer + : null, + { borderRadius: 300, overflow: "hidden" }, ]} > { { justifyContent: "center", alignItems: "center", - }, - { borderRadius: 300, overflow: "hidden", }, - (alert.actions?.length === 1 || (alert.actions ?? []).length > 2) && styles.singleButton, + alert.actions?.length === 1 || alert.actions?.length > 2 + ? styles.singleButton + : null, primary && !danger ? { backgroundColor: - (backgroundColor ?? colors.primary) + (delays[title] > 0 ? "99" : ""), + (backgroundColor ?? colors.primary) + (delays[title] > 0 ? "99" : ""), } : danger ? { backgroundColor: "#BE0B00" + (delays[title] > 0 ? "99" : "") } @@ -246,10 +214,10 @@ const AlertProvider = ({ children }: AlertProviderProps) => { ]} > {icon && - React.cloneElement(icon, { - color: primary || danger ? "#ffffff" : colors.text, - size: 24, - })} + React.cloneElement(icon, { + color: primary || danger ? "#ffffff" : colors.text, + size: 24, + })} { ]} > {title} - {delays[title] !== undefined && delays[title] > 0 - ? ` (${delays[title]})` - : ""} + {delays[title] > 0 ? ` (${delays[title]})` : ""} - {delays[title] !== undefined && - - } + {delays[title] > 0 && ( + + )} @@ -286,7 +252,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { - } + )} )} @@ -321,6 +287,7 @@ const styles = StyleSheet.create({ borderCurve: "continuous", width: "100%", transformOrigin: "bottom center", + overflow: "hidden", }, contentContainer: { gap: 10, @@ -370,12 +337,6 @@ const styles = StyleSheet.create({ fontSize: 16, fontFamily: "medium", }, - primaryButton: { - paddingHorizontal: 16, - }, - notPrimaryButton: { - paddingHorizontal: 16, - }, primaryButtonText: { color: "#ffffff", fontFamily: "semibold", From ffbe503a110491f2297b106b534b4a67cc0b1e53 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 18 Mar 2025 19:57:20 +0100 Subject: [PATCH 0917/1144] refractor: bump `bug.yml` to the latest version of Papillon --- .github/ISSUE_TEMPLATE/bug.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index b32273982..49004ae35 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -61,7 +61,7 @@ body: attributes: label: Version utilisée description: Paramètres (de Papillon) -> Version affichée en bas de la page - placeholder: "7.10.0" + placeholder: "7.10.3" validations: required: true From 695eb58871fec50f3239b5df7b3b62fdab896ea3 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 18 Mar 2025 20:04:20 +0100 Subject: [PATCH 0918/1144] fix: alternance semaine UNIQUEMENT sur Pronote --- src/views/account/Lessons/Lessons.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index 044646b98..9b203da65 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -421,7 +421,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { navigation.navigate("LessonsImportIcal", {}); } }, - account.service !== AccountService.Pronote ? { + account.service === AccountService.Pronote ? { icon: shouldShowWeekFrequency ? : , label: shouldShowWeekFrequency ? "Masquer alternance semaine" From 61248261562caa0fec2dc15b93fdbd0fabc093d1 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 18 Mar 2025 20:18:29 +0100 Subject: [PATCH 0919/1144] =?UTF-8?q?fix:=20refactorisation=20du=20composa?= =?UTF-8?q?nt=20AccountSwitcher=20pour=20am=C3=A9liorer=20la=20lisibilit?= =?UTF-8?q?=C3=A9=20et=20la=20gestion=20des=20styles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Home/AccountSwitcher.tsx | 136 ++++---- .../Home/AccountSwitcherContextMenu.tsx | 304 ++++++++---------- src/views/account/Home/Home.tsx | 232 ++++--------- src/views/account/Home/ModalContent.tsx | 227 +++++-------- 4 files changed, 350 insertions(+), 549 deletions(-) diff --git a/src/components/Home/AccountSwitcher.tsx b/src/components/Home/AccountSwitcher.tsx index a9051fad6..39a97dd08 100644 --- a/src/components/Home/AccountSwitcher.tsx +++ b/src/components/Home/AccountSwitcher.tsx @@ -2,34 +2,32 @@ import React from "react"; import { Image, StyleSheet, View } from "react-native"; import { useTheme } from "@react-navigation/native"; import { ChevronDown } from "lucide-react-native"; - import Reanimated, { interpolateColor, - LinearTransition, useAnimatedStyle, + LinearTransition, + useAnimatedStyle, ZoomIn, ZoomOut, } from "react-native-reanimated"; - import { useCurrentAccount } from "@/stores/account"; import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; import PapillonSpinner from "../Global/PapillonSpinner"; import { animPapillon } from "@/utils/ui/animations"; -import Animated from "react-native-reanimated"; import { BlurView } from "expo-blur"; const ReanimatedBlurView = Reanimated.createAnimatedComponent(BlurView); +const AnimatedChevronDown = Reanimated.createAnimatedComponent(ChevronDown); const AccountSwitcher: React.FC<{ - small?: boolean, - opened?: boolean, - modalOpen?: boolean, - translationY?: Reanimated.SharedValue, - loading?: boolean, + small?: boolean; + opened?: boolean; + modalOpen?: boolean; + translationY?: Reanimated.SharedValue; + loading?: boolean; }> = ({ small, opened, modalOpen, translationY, loading }) => { const theme = useTheme(); const { colors } = theme; - - const account = useCurrentAccount(store => store.account!); + const account = useCurrentAccount((store) => store.account!); const shouldHideName = account.personalization.hideNameOnHomeScreen || false; const shouldHidePicture = account.personalization.hideProfilePicOnHomeScreen || false; @@ -38,46 +36,72 @@ const AccountSwitcher: React.FC<{ borderWidth: 1, borderRadius: 80, borderColor: interpolateColor( - translationY?.value || 0, // Should think to pass a default value + translationY?.value || 0, [200, 251], - ["#ffffff50", colors.border], + ["#ffffff50", colors.border] ), backgroundColor: interpolateColor( - translationY?.value || 0, // Should think to pass a default value + translationY?.value || 0, [200, 251], - ["#ffffff30", "transparent"], + ["#ffffff30", "transparent"] ), })); const textAnimatedStyle = useAnimatedStyle(() => ({ color: interpolateColor( - translationY?.value || 0, // Should think to pass a default value + translationY?.value || 0, [200, 251], - ["#FFF", colors.text], + ["#FFF", colors.text] ), fontSize: 16, fontFamily: "semibold", maxWidth: 140, })); - - const AnimatedChevronDown = Animated.createAnimatedComponent(ChevronDown); const iconAnimatedStyle = useAnimatedStyle(() => ({ color: interpolateColor( - translationY?.value || 0, // Should think to pass a default value + translationY?.value || 0, [200, 251], - ["#FFF", colors.text], + ["#FFF", colors.text] ), marginLeft: -6, })); + const renderProfilePicture = () => { + if (shouldHidePicture) { + return ; + } + return ( + + ); + }; + return ( - {!shouldHidePicture ? ( - - ) : ( - - )} - + {renderProfilePicture()} - {account.studentName ? ( - account.studentName?.first + (shouldHideName ? "" : " " + account.studentName.last) - ) : "Mon compte"} + {account.studentName + ? account.studentName.first + (shouldHideName ? "" : " " + account.studentName.last) + : "Mon compte"} - {loading && ( )} - - + @@ -202,19 +190,13 @@ const styles = StyleSheet.create({ paddingVertical: 6, gap: 6, }, - avatar: { - height: 28, aspectRatio: 1, borderRadius: 24, backgroundColor: "#00000010", borderColor: "#00000020", borderWidth: 1, }, - - accountSwitcherText: { - - }, }); -export default AccountSwitcher; \ No newline at end of file +export default AccountSwitcher; diff --git a/src/components/Home/AccountSwitcherContextMenu.tsx b/src/components/Home/AccountSwitcherContextMenu.tsx index 41cbb5b64..3a6877461 100644 --- a/src/components/Home/AccountSwitcherContextMenu.tsx +++ b/src/components/Home/AccountSwitcherContextMenu.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useState, useCallback } from "react"; import { Dimensions, Image, @@ -10,15 +10,9 @@ import { TouchableOpacity, View, } from "react-native"; - -import Reanimated, { - FadeIn, - FadeOut -} from "react-native-reanimated"; - +import Reanimated, { FadeIn, FadeOut } from "react-native-reanimated"; import { useNavigation } from "@react-navigation/native"; import * as Haptics from "expo-haptics"; - import { useAccounts, useCurrentAccount } from "@/stores/account"; import { AccountService } from "@/stores/account/types"; import { PapillonContextEnter, PapillonContextExit } from "@/utils/ui/animations"; @@ -32,7 +26,7 @@ const ContextMenu: React.FC<{ style?: any; children: React.ReactNode; transparent?: boolean; - shouldOpenContextMenu?: boolean, + shouldOpenContextMenu?: boolean; menuStyles?: any; }> = ({ children, style, shouldOpenContextMenu, transparent, menuStyles }) => { const theme = useTheme(); @@ -40,32 +34,150 @@ const ContextMenu: React.FC<{ const navigation = useNavigation(); const { playHaptics } = useSoundHapticsWrapper(); - const [opened, setOpened] = useState(false); // État pour gérer l'ouverture du menu contextuel + const [opened, setOpened] = useState(false); + const [touchLongPress, setTouchLongPress] = useState(false); const currentAccount = useCurrentAccount((store) => store.account!); const switchTo = useCurrentAccount((store) => store.switchTo); - const accounts = useAccounts((store) => store.accounts); - // Effet pour ouvrir le menu contextuel si shouldOpenContextMenu change useEffect(() => { if (shouldOpenContextMenu) { setOpened(true); } }, [shouldOpenContextMenu]); - // Fonction pour activer un effet haptique à l'ouverture du menu - const openEffects = () => { + useEffect(() => { + setTouchLongPress(false); + }, [opened]); + + const openEffects = useCallback(() => { playHaptics("impact", { impact: Haptics.ImpactFeedbackStyle.Light, }); - }; + }, [playHaptics]); - const [touchLongPress, setTouchLongPress] = useState(false); + const handlePress = useCallback(() => { + setOpened(!opened); + openEffects(); + }, [opened, openEffects]); - useEffect(() => { - setTouchLongPress(false); - }, [opened]); + const handleLongPress = useCallback(() => { + setTouchLongPress(true); + }, []); + + const handlePressOut = useCallback(() => { + if (touchLongPress) { + setOpened(false); + openEffects(); + } + }, [touchLongPress, openEffects]); + + const renderMenuItems = useCallback(() => { + return accounts.map((account, index) => !account.isExternal && ( + { + playHaptics("impact", { + impact: Haptics.ImpactFeedbackStyle.Soft, + }); + setOpened(false); + requestAnimationFrame(() => { + switchTo(account); + }); + }} + style={({ pressed }) => [ + { + backgroundColor: pressed ? "rgba(0, 0, 0, 0.1)" : colors.card, + }, + ]} + > + + + + + + + + {account.studentName?.first || "Utilisateur"} {account.studentName?.last || ""} + + + + {AccountService[account.service] !== "Local" && account.service !== AccountService.PapillonMultiService + ? AccountService[account.service] + : account.identityProvider + ? account.identityProvider.name + : "Compte local"} + + + {currentAccount.localID === account.localID && accounts.length > 1 && ( + + + + )} + + + )); + }, [accounts, colors, currentAccount.localID, playHaptics, switchTo, theme]); return ( <> @@ -78,25 +190,11 @@ const ContextMenu: React.FC<{ style, ]} > - {/* Différents comportements pour iOS et Android */} {Platform.OS === "ios" ? ( { - if (!touchLongPress) { - setOpened(!opened); - openEffects(); - } - }} - onLongPress={() => { - setTouchLongPress(true); - }} - onPressOut={() => { - if (touchLongPress) { - setOpened(false); - openEffects(); - } - }} - // @ts-expect-error + onPressIn={handlePress} + onLongPress={handleLongPress} + onPressOut={handlePressOut} pointerEvents="auto" style={{ elevation: opened ? 3 : 0, @@ -108,12 +206,7 @@ const ContextMenu: React.FC<{ ) : ( { - setOpened(!opened); - playHaptics("impact", { - impact: Haptics.ImpactFeedbackStyle.Light, - }); - }} + onPress={handlePress} useForeground={true} style={{ overflow: "hidden", @@ -133,7 +226,6 @@ const ContextMenu: React.FC<{ )} - {/* Menu contextuel */} {opened && ( - {accounts.map((account, index) => !account.isExternal && ( - { - playHaptics("impact", { - impact: Haptics.ImpactFeedbackStyle.Soft, - }); - setOpened(false); - - requestAnimationFrame(() => { - switchTo(account); - }); - }} - style={({ pressed }) => [ - { - backgroundColor: pressed ? "rgba(0, 0, 0, 0.1)" : colors.card, // Utilisation de rgba pour l'assombrissement - }, - ]} - > - - - - - - - - {account.studentName?.first || "Utilisateur"}{" "} - {account.studentName?.last || ""} - - - - - {AccountService[account.service] !== "Local" && account.service !== AccountService.PapillonMultiService ? - AccountService[account.service] : - account.identityProvider ? - account.identityProvider.name : - "Compte local" - } - - - {currentAccount.localID === account.localID && (accounts.length > 1) && ( - - - - )} - - - ))} + {renderMenuItems()} { setOpened(false); - // @ts-expect-error : TODO: https://reactnavigation.org/docs/typescript/#specifying-default-types-for-usenavigation-link-ref-etc navigation.navigate("ServiceSelector"); }} style={({ pressed }) => [ { - backgroundColor: pressed ? "rgba(0, 0, 0, 0.1)" : colors.card, // Utilisation de rgba pour l'assombrissement + backgroundColor: pressed ? "rgba(0, 0, 0, 0.1)" : colors.card, }, ]} > @@ -280,7 +265,7 @@ const ContextMenu: React.FC<{ borderStyle: "solid", borderTopWidth: 6, borderBottomColor: colors.border, - borderColor: theme.dark ? "#ffffff20" :"#00000020", + borderColor: theme.dark ? "#ffffff20" : "#00000020", alignItems: "center", gap: 10, }} @@ -293,7 +278,6 @@ const ContextMenu: React.FC<{ marginHorizontal: 3, }} /> - { setOpened(false); - // @ts-expect-error : TODO: https://reactnavigation.org/docs/typescript/#specifying-default-types-for-usenavigation-link-ref-etc navigation.navigate("SettingStack"); }} style={({ pressed }) => [ { - backgroundColor: pressed ? "rgba(0, 0, 0, 0.1)" : colors.card, // Utilisation de rgba pour l'assombrissement + backgroundColor: pressed ? "rgba(0, 0, 0, 0.1)" : colors.card, }, ]} > @@ -326,7 +309,7 @@ const ContextMenu: React.FC<{ borderStyle: "solid", borderTopWidth: 1, borderBottomColor: colors.border, - borderColor: theme.dark ? "#ffffff20" :"#00000020", + borderColor: theme.dark ? "#ffffff20" : "#00000020", alignItems: "center", gap: 10, }} @@ -339,7 +322,6 @@ const ContextMenu: React.FC<{ marginHorizontal: 3, }} /> - = ({ navigation }) => { const focused = useIsFocused(); const { playHaptics } = useSoundHapticsWrapper(); - const {isTablet} = useScreenDimensions(); + const { isTablet } = useScreenDimensions(); const { showAlert } = useAlert(); - let scrollRef = useAnimatedRef(); - let scrollOffset = useScrollViewOffset(scrollRef); + const scrollRef = useAnimatedRef(); + const scrollOffset = useScrollViewOffset(scrollRef); - let account = useCurrentAccount(store => store.account!); + const account = useCurrentAccount(store => store.account!); const accounts = useAccounts((store) => store.accounts); useEffect(() => { - void async function () { + const checkAccounts = async () => { if (!useAccounts.persist.hasHydrated()) return; - // If there are no accounts, redirect the user to the first installation page. if (accounts.filter((account) => !account.isExternal).length === 0) { - // Use the `reset` method to clear the navigation stack. navigation.reset({ index: 0, routes: [{ name: "FirstInstallation" }], }); + } else { + const url = await Linking.getInitialURL(); + manageIzlyLogin(url || ""); } - else { - Linking.getInitialURL().then((url) => { - manageIzlyLogin(url || ""); - }); - } - }(); + }; + + checkAccounts(); - Linking.addEventListener("url", (event) => { + const handleUrl = (event) => { manageIzlyLogin(event.url); - }); - }, [accounts]); + }; + + Linking.addEventListener("url", handleUrl); + }, [accounts, navigation]); const manageIzlyLogin = (url: string) => { if (url) { const scheme = url.split(":")[0]; if (scheme === "izly") { - showAlert({ - title: "Activation de compte Izly", - message: "Papillon gère la connexion au service Izly. Ouvrez les paramètres de services de cantine pour activer votre compte.", - icon:
, - actions: [ - { - title: "Annuler", - icon: , - }, - { - title: "Ajouter mon compte", - icon: , - onPress: () => navigation.navigate("SettingStack", { view: "IzlyActivation" }), - primary: true, - } - ], - }); + setTimeout(() => { + showAlert({ + title: "Activation de compte Izly", + message: "Papillon gère la connexion au service Izly. Ouvrez les paramètres de services de cantine pour activer votre compte.", + icon: , + actions: [ + { title: "Annuler", icon: }, + { + title: "Ajouter mon compte", + icon: , + onPress: () => navigation.navigate("SettingStack", { view: "IzlyActivation" }), + primary: true, + } + ], + }); + }, 1000); } } }; const [shouldOpenContextMenu, setShouldOpenContextMenu] = useState(false); - const [modalOpen, setModalOpen] = useState(false); const [modalFull, setModalFull] = useState(false); - const [canHaptics, setCanHaptics] = useState(true); const [refreshing, setRefreshing] = useState(false); @@ -151,60 +135,26 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { const widgetAnimatedStyle = useAnimatedStyle(() => ({ paddingTop: insets.top, - opacity: interpolate( - scrollOffset.value, - [0, 265 + insets.top], - [1, 0], - Extrapolation.CLAMP - ), + opacity: interpolate(scrollOffset.value, [0, 265 + insets.top], [1, 0], Extrapolation.CLAMP), transform: [ { translateY: scrollOffset.value }, - { scale: interpolate( - scrollOffset.value, - [0, 265], - [1, 0.9], - Extrapolation.CLAMP - )}, + { scale: interpolate(scrollOffset.value, [0, 265], [1, 0.9], Extrapolation.CLAMP) }, ] })); const modalAnimatedStyle = useAnimatedStyle(() => ({ ...(Platform.OS === "android" ? {} : { borderCurve: "continuous" }), - borderTopLeftRadius: interpolate( - scrollOffset.value, - [0, 100, 265 + insets.top - 0.1, 265 + insets.top], - [12, 12, corners, 0], - Extrapolation.CLAMP - ), - borderTopRightRadius: interpolate( - scrollOffset.value, - [0, 100, 265 + insets.top - 0.1, 265 + insets.top], - [12, 12, corners, 0], - Extrapolation.CLAMP - ), - + borderTopLeftRadius: interpolate(scrollOffset.value, [0, 100, 265 + insets.top - 0.1, 265 + insets.top], [12, 12, corners, 0], Extrapolation.CLAMP), + borderTopRightRadius: interpolate(scrollOffset.value, [0, 100, 265 + insets.top - 0.1, 265 + insets.top], [12, 12, corners, 0], Extrapolation.CLAMP), shadowColor: "#000", - ...(Platform.OS === "android" ? {} : { - shadowOffset: { - width: 0, - height: 2, - } - }), + ...(Platform.OS === "android" ? {} : { shadowOffset: { width: 0, height: 2 } }), shadowOpacity: 0.2, shadowRadius: 10, - flex: 1, minHeight: windowHeight - tabbarHeight - 8, backgroundColor: colors.card, overflow: "hidden", - transform: [ - {translateY: interpolate( - scrollOffset.value, - [-1000, 0, 125, 265 ], - [-1000, 0, 105, 0], - Extrapolation.CLAMP - )} - ], + transform: [{ translateY: interpolate(scrollOffset.value, [-1000, 0, 125, 265], [-1000, 0, 105, 0], Extrapolation.CLAMP) }], })); const navigationBarAnimatedStyle = useAnimatedStyle(() => ({ @@ -212,12 +162,7 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { top: scrollOffset.value - 270 - insets.top, left: 0, right: 0, - height: interpolate( - scrollOffset.value, - [125, 265], - [0, insets.top + 60], - Extrapolation.CLAMP - ), + height: interpolate(scrollOffset.value, [125, 265], [0, insets.top + 60], Extrapolation.CLAMP), zIndex: 100, backgroundColor: colors.background, borderColor: colors.border, @@ -227,46 +172,20 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { const modalContentAnimatedStyle = useAnimatedStyle(() => ({ paddingHorizontal: 16, paddingBottom: 16 + insets.top + 56, - transform: [ - { - translateY: interpolate( - scrollOffset.value, - [-1000, 0, 125, 265 + insets.top], - [1000, 0, 0, insets.top + 56], - Extrapolation.CLAMP - ) - } - ] + transform: [{ translateY: interpolate(scrollOffset.value, [-1000, 0, 125, 265 + insets.top], [1000, 0, 0, insets.top + 56], Extrapolation.CLAMP) }], })); const modalIndicatorAnimatedStyle = useAnimatedStyle(() => ({ position: "absolute", top: 10, left: "50%", - transform: [ - {translateX: interpolate( - scrollOffset.value, - [125, 200], - [-25, -2], - Extrapolation.CLAMP - )} - ], - width: interpolate( - scrollOffset.value, - [125, 200], - [50, 4], - Extrapolation.CLAMP - ), + transform: [{ translateX: interpolate(scrollOffset.value, [125, 200], [-25, -2], Extrapolation.CLAMP) }], + width: interpolate(scrollOffset.value, [125, 200], [50, 4], Extrapolation.CLAMP), height: 4, backgroundColor: colors.text + "20", zIndex: 100, borderRadius: 5, - opacity: interpolate( - scrollOffset.value, - [125, 180, 200], - [1, 0.5, 0], - Extrapolation.CLAMP - ), + opacity: interpolate(scrollOffset.value, [125, 180, 200], [1, 0.5, 0], Extrapolation.CLAMP), })); const scrollViewAnimatedStyle = useAnimatedStyle(() => ({ @@ -275,18 +194,13 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { })); return ( - + {!modalOpen && focused && !isTablet && ( )} {!isTablet && ( = ({ navigation }) => { onScroll={(e) => { const scrollY = e.nativeEvent.contentOffset.y; if (scrollY > 125 && canHaptics) { - playHaptics("impact", { - impact: Haptics.ImpactFeedbackStyle.Light, - }); + playHaptics("impact", { impact: Haptics.ImpactFeedbackStyle.Light }); setCanHaptics(false); } else if (scrollY < 125 && !canHaptics) { setCanHaptics(true); @@ -322,39 +234,17 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { setModalOpen(scrollY >= 195 + insets.top); setModalFull(scrollY >= 265 + insets.top); }} - refreshControl={ setRefreshing(true)} - style={{zIndex: 100}} - progressViewOffset={285 + insets.top} - />} + refreshControl={ setRefreshing(true)} style={{ zIndex: 100 }} progressViewOffset={285 + insets.top} />} showsVerticalScrollIndicator={false} > - -
+ +
- + - setRefreshing(false)} - /> + setRefreshing(false)} /> diff --git a/src/views/account/Home/ModalContent.tsx b/src/views/account/Home/ModalContent.tsx index aa4c9a63d..bd14f18e7 100644 --- a/src/views/account/Home/ModalContent.tsx +++ b/src/views/account/Home/ModalContent.tsx @@ -1,107 +1,89 @@ -import { NativeList, NativeText} from "@/components/Global/NativeComponents"; -import React, {useCallback, useEffect, useState} from "react"; -import Reanimated, { - FadeInUp, - FadeOutDown, - LinearTransition, -} from "react-native-reanimated"; -import { Sparkles, X} from "lucide-react-native"; -import {useTheme} from "@react-navigation/native"; +import React, { useCallback, useEffect, useState, useMemo } from "react"; +import { NativeList, NativeText } from "@/components/Global/NativeComponents"; +import Reanimated, { FadeInUp, FadeOutDown, LinearTransition } from "react-native-reanimated"; +import { Sparkles, X } from "lucide-react-native"; +import { useTheme } from "@react-navigation/native"; import PackageJSON from "../../../../package.json"; -import {Dimensions, View} from "react-native"; - -import {Elements, type Element} from "./ElementIndex"; -import {animPapillon} from "@/utils/ui/animations"; -import {useFlagsStore} from "@/stores/flags"; -import {useCurrentAccount} from "@/stores/account"; +import { Dimensions, View } from "react-native"; +import { Elements, type Element } from "./ElementIndex"; +import { animPapillon } from "@/utils/ui/animations"; +import { useFlagsStore } from "@/stores/flags"; +import { useCurrentAccount } from "@/stores/account"; import AsyncStorage from "@react-native-async-storage/async-storage"; -import {defaultTabs} from "@/consts/DefaultTabs"; -import {NativeStackNavigationProp} from "@react-navigation/native-stack"; -import {RouteParameters} from "@/router/helpers/types"; +import { defaultTabs } from "@/consts/DefaultTabs"; +import { NativeStackNavigationProp } from "@react-navigation/native-stack"; +import { RouteParameters } from "@/router/helpers/types"; import { TouchableOpacity } from "react-native-gesture-handler"; import { OfflineWarning, useOnlineStatus } from "@/hooks/useOnlineStatus"; interface ModalContentProps { - navigation: NativeStackNavigationProp - refresh: boolean - endRefresh: () => unknown + navigation: NativeStackNavigationProp; + refresh: boolean; + endRefresh: () => unknown; } const ModalContent: React.FC = ({ navigation, refresh, endRefresh }) => { const { colors } = useTheme(); const { isOnline } = useOnlineStatus(); - - const account = useCurrentAccount(store => store.account!); - const mutateProperty = useCurrentAccount(store => store.mutateProperty); + const account = useCurrentAccount((store) => store.account!); + const mutateProperty = useCurrentAccount((store) => store.mutateProperty); + const defined = useFlagsStore((state) => state.defined); const [updatedRecently, setUpdatedRecently] = useState(false); - const defined = useFlagsStore(state => state.defined); - const [elements, setElements] = useState([]); useEffect(() => { - setElements([]); - Elements.forEach((Element) => { - setElements(prevElements => [ - ...prevElements, - { - id: Element.id, - component: Element.component, - importance: undefined, - } - ]); - }); + setElements(Elements.map((Element) => ({ + id: Element.id, + component: Element.component, + importance: undefined, + }))); }, []); - function sortElementsByImportance () { - setElements(prevElements => { - const sortedElements = [...prevElements]; - sortedElements.sort((a, b) => { - let aImportance = a.importance === undefined ? -1 : a.importance; - let bImportance = b.importance === undefined ? -1 : b.importance; + const sortElementsByImportance = useCallback(() => { + setElements((prevElements) => + [...prevElements].sort((a, b) => { + const aImportance = a.importance ?? -1; + const bImportance = b.importance ?? -1; return bImportance - aImportance; - }); - return sortedElements; - }); - } - - const updateImportance = (id: string, value: number) => { - setElements(prevElements => { - const updatedElements = [...prevElements]; - const index = updatedElements.findIndex(element => element.id === id); - updatedElements[index].importance = value; - return updatedElements; - }); - }; - - const handleImportanceChange = (id: string, value: number) => { + }) + ); + }, []); + + const updateImportance = useCallback((id: string, value: number) => { + setElements((prevElements) => + prevElements.map((element) => + element.id === id ? { ...element, importance: value } : element + ) + ); + }, []); + + const handleImportanceChange = useCallback((id: string, value: number) => { updateImportance(id, value); sortElementsByImportance(); - }; - - function checkForUpdateRecently () { - AsyncStorage.getItem("changelog.lastUpdate") - .then((value) => { - const currentVersion = PackageJSON.version; - if (value == null || value !== currentVersion) - setUpdatedRecently(true); - }); - } + }, [sortElementsByImportance, updateImportance]); + + const checkForUpdateRecently = useCallback(async () => { + const value = await AsyncStorage.getItem("changelog.lastUpdate"); + if (value == null || value !== PackageJSON.version) { + setUpdatedRecently(true); + } + }, []); const checkForNewTabs = useCallback(() => { const storedTabs = account.personalization.tabs || []; - const newTabs = defaultTabs.filter(defaultTab => - !storedTabs.some(storedTab => storedTab.name === defaultTab.tab) + const newTabs = defaultTabs.filter( + (defaultTab) => !storedTabs.some((storedTab) => storedTab.name === defaultTab.tab) ); if (newTabs.length > 0) { const updatedTabs = [ ...storedTabs, - ...newTabs.map(tab => ({ + ...newTabs.map((tab) => ({ name: tab.tab, enabled: false, - installed: true - })) + installed: true, + })), ]; mutateProperty("personalization", { @@ -111,36 +93,42 @@ const ModalContent: React.FC = ({ navigation, refresh, endRef } }, [account.personalization.tabs, mutateProperty]); - async function RefreshData () { - checkForUpdateRecently(); + const refreshData = useCallback(async () => { + await checkForUpdateRecently(); checkForNewTabs(); sortElementsByImportance(); endRefresh(); - } + }, [checkForUpdateRecently, checkForNewTabs, sortElementsByImportance, endRefresh]); useEffect(() => { - if (refresh) - RefreshData(); - }, [refresh]); + if (refresh) { + refreshData(); + } + }, [refresh, refreshData]); useEffect(() => { - return navigation.addListener("focus", () => { - RefreshData(); - }); - }, []); + const unsubscribe = navigation.addListener("focus", refreshData); + return unsubscribe; + }, [navigation, refreshData]); + + const memoizedElements = useMemo(() => elements.map((Element, index) => ( + + handleImportanceChange(Element.id, value) : () => {}} + /> + + )), [elements, handleImportanceChange, navigation]); return ( - + {(defined("force_changelog") || updatedRecently) && ( - + navigation.navigate("ChangelogScreen")} style={{ @@ -152,38 +140,19 @@ const ModalContent: React.FC = ({ navigation, refresh, endRef backgroundColor: colors.primary + "20", }} > - - - + + + Papillon vient d'être mis à jour à la version {PackageJSON.version} ! - { AsyncStorage.setItem("changelog.lastUpdate", PackageJSON.version); setUpdatedRecently(false); }} - style={{ - padding: 4, - borderRadius: 100, - backgroundColor: colors.text + "20", - }} + style={{ padding: 4, borderRadius: 100, backgroundColor: colors.text + "20" }} > - + @@ -192,29 +161,9 @@ const ModalContent: React.FC = ({ navigation, refresh, endRef )} - {!isOnline && } - - - {elements.map((Element, index) => (Element && - - handleImportanceChange(Element.id, value): - () => {} - } - /> - - ))} + + {memoizedElements} ); From 466a7a1645a6c0537d75861dc4a82f4a4359c46c Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 18 Mar 2025 21:38:31 +0100 Subject: [PATCH 0920/1144] =?UTF-8?q?feat:=20ajout=20de=20la=20navigation?= =?UTF-8?q?=20vers=20le=20nouvel=20=C3=A9cran=20ProfilePic=20et=20am=C3=A9?= =?UTF-8?q?lioration=20de=20la=20gestion=20des=20photos=20de=20profil?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/helpers/types.ts | 1 + src/router/screens/welcome/index.ts | 16 +- src/views/settings/SettingsProfile.tsx | 16 +- src/views/welcome/AccountCreated.tsx | 10 +- src/views/welcome/ColorSelector.tsx | 34 ++-- src/views/welcome/ProfilePic.tsx | 219 +++++++++++++++++++++++++ 6 files changed, 274 insertions(+), 22 deletions(-) create mode 100644 src/views/welcome/ProfilePic.tsx diff --git a/src/router/helpers/types.ts b/src/router/helpers/types.ts index f7598dc41..c461095ff 100644 --- a/src/router/helpers/types.ts +++ b/src/router/helpers/types.ts @@ -26,6 +26,7 @@ export type RouteParameters = { DevMenu: undefined; AccountCreated: undefined; ChangelogScreen: undefined; + ProfilePic: undefined; // login.index ServiceSelector: undefined; diff --git a/src/router/screens/welcome/index.ts b/src/router/screens/welcome/index.ts index 32432755e..ebc53d8f2 100644 --- a/src/router/screens/welcome/index.ts +++ b/src/router/screens/welcome/index.ts @@ -6,6 +6,7 @@ import ColorSelector from "@/views/welcome/ColorSelector"; import DevMenu from "@/views/welcome/DevMenu"; import AccountCreated from "@/views/welcome/AccountCreated"; import ChangelogScreen from "@/views/welcome/ChangelogScreen"; +import ProfilePic from "@/views/welcome/ProfilePic"; export default [ createScreen("AccountSelector", AccountSelector, { @@ -13,25 +14,30 @@ export default [ headerBackVisible: false, headerTitle: "", animation: "fade", - animationDuration: 300 + animationDuration: 300, }), createScreen("FirstInstallation", FirstInstallation, { headerTransparent: true, headerBackVisible: false, - headerTitle: "" + headerTitle: "", }), createScreen("DevMenu", DevMenu, { - headerTitle: "Développement" + headerTitle: "Développement", }), createScreen("ColorSelector", ColorSelector, { headerTransparent: true, headerBackVisible: false, - headerTitle: "" + headerTitle: "", + }), + createScreen("ProfilePic", ProfilePic, { + headerTransparent: true, + headerBackVisible: false, + headerTitle: "", }), createScreen("AccountCreated", AccountCreated, { headerTransparent: true, headerBackVisible: false, - headerTitle: "" + headerTitle: "", }), createScreen("ChangelogScreen", ChangelogScreen, { headerTitle: "Quoi de neuf ?", diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index 207ee7b8a..357afe5d3 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -3,7 +3,7 @@ import { Screen } from "@/router/helpers/types"; import { useCurrentAccount } from "@/stores/account"; import { useTheme } from "@react-navigation/native"; import * as ImagePicker from "expo-image-picker"; -import { BadgeX, Camera, ChevronDown, ChevronUp, ClipboardCopy, TextCursorInput, Undo2, User2, UserCircle2, WholeWord } from "lucide-react-native"; +import { BadgeX, Camera, CameraOff, ChevronDown, ChevronUp, ClipboardCopy, TextCursorInput, Trash, Undo2, User2, UserCircle2, WholeWord } from "lucide-react-native"; import React, { useEffect, useRef, useState } from "react"; import { ActivityIndicator, Image, KeyboardAvoidingView, ScrollView, Switch, TextInput } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; @@ -70,6 +70,18 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { primary: true, icon: , }, + { + title: "Supprimer la photo", + primary: false, + onPress: () => { + setProfilePic(undefined); + mutateProperty("personalization", { + ...account.personalization, + profilePictureB64: undefined, + }); + }, + icon: , + } ], }); } else { @@ -217,7 +229,7 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { } + icon={} > Réinitialiser la photo de profil diff --git a/src/views/welcome/AccountCreated.tsx b/src/views/welcome/AccountCreated.tsx index c225cded4..ab8aa1b62 100644 --- a/src/views/welcome/AccountCreated.tsx +++ b/src/views/welcome/AccountCreated.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useState } from "react"; -import { StyleSheet, View } from "react-native"; +import { Dimensions, StyleSheet, View } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; import type { Screen } from "@/router/helpers/types"; @@ -21,7 +21,7 @@ const AccountCreated: Screen<"AccountCreated"> = ({ navigation }) => { const LEson5 = require("@/../assets/sound/5.wav"); const LEson6 = require("@/../assets/sound/6.wav"); - let name = !account.studentName?.first ? null + let name = (!account || !account.studentName?.first) ? null : account.studentName?.first; // Truncate name if over 10 characters. @@ -68,11 +68,13 @@ const AccountCreated: Screen<"AccountCreated"> = ({ navigation }) => { ref={animationRef} source={require("@/../assets/lottie/confetti_1.json")} style={{ - width: "100%", - height: "100%", + width: Dimensions.get("window").width, + height: Dimensions.get("window").height, position: "absolute", top: 0, left: 0, + right: 0, + bottom: 0, zIndex: -200, }} autoPlay diff --git a/src/views/welcome/ColorSelector.tsx b/src/views/welcome/ColorSelector.tsx index e43aab104..e22cb1a85 100644 --- a/src/views/welcome/ColorSelector.tsx +++ b/src/views/welcome/ColorSelector.tsx @@ -8,7 +8,7 @@ import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useCurrentAccount } from "@/stores/account"; import { LinearGradient } from "expo-linear-gradient"; -import Reanimated, { ZoomIn, ZoomOut, LinearTransition, FadeIn, FadeOut, FlipInXDown, FadeOutUp } from "react-native-reanimated"; +import Reanimated, { ZoomIn, LinearTransition, FadeIn, FadeOut, FadeOutUp, FadeInDown } from "react-native-reanimated"; import * as Haptics from "expo-haptics"; import { getIconName, setIconName } from "@candlefinance/app-icon"; @@ -16,6 +16,7 @@ import colorsList from "@/utils/data/colors.json"; import { removeColor } from "../settings/SettingsIcons"; import { isExpoGo } from "@/utils/native/expoGoAlert"; import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; +import { animPapillon } from "@/utils/ui/animations"; type Color = typeof colorsList[number]; @@ -30,6 +31,8 @@ const ColorSelector: Screen<"ColorSelector"> = ({ route, navigation }) => { const LEson003 = require("@/../assets/sound/click_003.wav"); const LEson6 = require("@/../assets/sound/6.wav"); + const hasProfilePic = account && account?.personalization && account?.personalization.profilePictureB64 !== undefined && account?.personalization.profilePictureB64.trim() !== ""; + useLayoutEffect(() => { navigation.setOptions({ headerShown: settings || false, @@ -92,8 +95,8 @@ const ColorSelector: Screen<"ColorSelector"> = ({ route, navigation }) => { zIndex: -99, } ]} - entering={ZoomIn.springify().mass(1).stiffness(150)} - exiting={ZoomOut} + entering={animPapillon(ZoomIn)} + exiting={FadeOut.duration(150)} /> )} @@ -142,19 +145,21 @@ const ColorSelector: Screen<"ColorSelector"> = ({ route, navigation }) => { = ({ route, navigation }) => { > { if (!settings) { - playSound(LEson6); + if(!hasProfilePic) { + navigation.navigate("ProfilePic"); + return; + } + else { + playSound(LEson6); + } } + navigation.navigate("AccountStack", {onboard: true}); }} disabled={!account?.personalization?.color} diff --git a/src/views/welcome/ProfilePic.tsx b/src/views/welcome/ProfilePic.tsx new file mode 100644 index 000000000..dafcd496b --- /dev/null +++ b/src/views/welcome/ProfilePic.tsx @@ -0,0 +1,219 @@ +import React from "react"; +import { Pressable, StyleSheet, TouchableOpacity, View } from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; + +import type { Screen } from "@/router/helpers/types"; +import Reanimated, { FadeOut, LinearTransition, ZoomIn } from "react-native-reanimated"; + +import ButtonCta from "@/components/FirstInstallation/ButtonCta"; +import MaskStars from "@/components/FirstInstallation/MaskStars"; +import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; + + +import { useCurrentAccount } from "@/stores/account"; +import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; +import { useTheme } from "@react-navigation/native"; +import { Camera, Trash } from "lucide-react-native"; +import * as ImagePicker from "expo-image-picker"; +import PapillonSpinner from "@/components/Global/PapillonSpinner"; +import { NativeText } from "@/components/Global/NativeComponents"; +import { animPapillon } from "@/utils/ui/animations"; + + +const ProfilePic: Screen<"ProfilePic"> = ({ navigation }) => { + const account = useCurrentAccount((state) => state.account!); + const { mutateProperty } = useCurrentAccount(); + const theme = useTheme(); + const { playHaptics, playSound } = useSoundHapticsWrapper(); + const LEson5 = require("@/../assets/sound/5.wav"); + const LEson6 = require("@/../assets/sound/6.wav"); + + const hasProfilePic = account && account?.personalization && account?.personalization.profilePictureB64 !== undefined && account?.personalization.profilePictureB64.trim() !== ""; + + const [loadingPic, setLoadingPic] = React.useState(false); + + const updateProfilePic = async () => { + setLoadingPic(true); + + // No permissions request is necessary for launching the image library + let result = await ImagePicker.launchImageLibraryAsync({ + mediaTypes: ImagePicker.MediaTypeOptions.Images, + allowsEditing: true, + aspect: [1, 1], + quality: 1, + base64: true, + }); + + if (!result.canceled) { + const img = "data:image/jpeg;base64," + result.assets[0].base64; + mutateProperty("personalization", { + ...account.personalization, + profilePictureB64: img, + }, true); + } + + setLoadingPic(false); + }; + + let name = (!account || !account.studentName?.first) ? null + : account.studentName?.first; + + // Truncate name if over 10 characters. + if (name && name.length > 10) { + name = name.substring(0, 10) + "..."; + } + + return ( + + + + + + + + + + {updateProfilePic();}} + style={(state) => [ + styles.profilePicContainer, + { + borderColor: theme.colors.text + "33", + borderWidth: 1, + marginTop: -100, + }, + state.pressed && { + opacity: 0.5, + }, + ]} + > + {loadingPic ? ( + + ) : hasProfilePic ? ( + + ) : ( + + + + )} + + + + {hasProfilePic && ( + + { + mutateProperty("personalization", { + ...account.personalization, + profilePictureB64: undefined, + }, true); + }} + style={{ + flexDirection: "row", + alignItems: "center", + gap: 8, + marginTop: 24, + }} + > + + + + Supprimer la photo + + + + )} + + + + { + navigation.navigate("AccountStack", {onboard: true}); + playSound(LEson6); + }} + /> + { + navigation.navigate("AccountStack", { onboard: true }); + playSound(LEson6); + }} + /> + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: "center", + gap: 20, + }, + + profilePicContainer: { + width: 180, + height: 180, + borderRadius: 200, + justifyContent: "center", + alignItems: "center", + overflow: "hidden", + }, + + buttons: { + width: "100%", + paddingHorizontal: 16, + gap: 9, + marginBottom: 16, + }, + + terms_text: { + fontSize: 12, + textAlign: "center", + fontFamily: "medium", + paddingHorizontal: 20, + }, +}); + +export default ProfilePic; From 56231ee88e43b044370950e457888cbc4f2077bd Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 18 Mar 2025 21:40:43 +0100 Subject: [PATCH 0921/1144] feat: add "expo-screen-orientation" to `package.json` --- package-lock.json | 10 ++++++++++ package.json | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index d3b9a4295..174ab0ac6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54,6 +54,7 @@ "expo-manifests": "^0.14.3", "expo-media-library": "~16.0.4", "expo-navigation-bar": "~3.0.7", + "expo-screen-orientation": "~7.0.5", "expo-sensors": "~13.0.9", "expo-sharing": "~12.0.1", "expo-splash-screen": "~0.27.6", @@ -10174,6 +10175,15 @@ "expo": "*" } }, + "node_modules/expo-screen-orientation": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/expo-screen-orientation/-/expo-screen-orientation-7.0.5.tgz", + "integrity": "sha512-1j0MzVzYpjKQo4BWowQ3ZYwC3OnddX/8k06C8VYTAxMyd8ou1k+rG4tm+GIV2n2RSzc3g7cfPlQwSYr3/SGmbg==", + "license": "MIT", + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-sensors": { "version": "13.0.9", "resolved": "https://registry.npmjs.org/expo-sensors/-/expo-sensors-13.0.9.tgz", diff --git a/package.json b/package.json index 052d228d3..8358dafd3 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,8 @@ "swiftui-react-native": "^6.3.3", "text-encoding": "^0.7.0", "turboself-api": "^2.1.8", - "zustand": "^4.5.2" + "zustand": "^4.5.2", + "expo-screen-orientation": "~7.0.5" }, "devDependencies": { "@babel/core": "^7.20.0", From db84569e5b44db8d63ece491bf55a78efb41b0ba Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 18 Mar 2025 21:41:54 +0100 Subject: [PATCH 0922/1144] =?UTF-8?q?feat:=20autoriser=20ou=20non=20la=20r?= =?UTF-8?q?otation=20de=20l'=C3=A9cran=20en=20fonction=20du=20type=20d'app?= =?UTF-8?q?areil=20utilis=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/App.tsx b/App.tsx index 0216722da..70777e219 100644 --- a/App.tsx +++ b/App.tsx @@ -14,6 +14,8 @@ import { registerBackgroundTasks } from "@/background/BackgroundTasks"; import { SoundHapticsProvider } from "@/hooks/Theme_Sound_Haptics"; import { PapillonNavigation } from "@/router/refs"; import { findAccountByID } from "@/background/utils/accounts"; +import * as Device from "expo-device"; +import * as ScreenOrientation from "expo-screen-orientation"; SplashScreen.preventAutoHideAsync(); @@ -40,6 +42,19 @@ export default function App () { bold: require("./assets/fonts/FixelText-Bold.ttf"), }); + useEffect(() => { + const configureOrientation = async () => { + const deviceType = await Device.getDeviceTypeAsync(); + if (deviceType === Device.DeviceType.TABLET) { + await ScreenOrientation.unlockAsync(); + } else { + await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT); + } + }; + + configureOrientation(); + }, []); + const handleNotificationPress = async (notification) => { if (notification?.data) { const accountID = notification.data.accountID; From 1100648a6cffe43416b7653b5e23c8ad68f6b544 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 18 Mar 2025 21:49:34 +0100 Subject: [PATCH 0923/1144] =?UTF-8?q?fix:=20affichage=20du=20header=20dans?= =?UTF-8?q?=20les=20param=C3=A8tres=20et=20mise=20=C3=A0=20jour=20des=20?= =?UTF-8?q?=C3=A9tiquettes=20pour=20plus=20de=20clart=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/screens/settings/index.ts | 2 +- src/views/settings/Settings.tsx | 104 +++++++++++++++++++-------- 2 files changed, 77 insertions(+), 29 deletions(-) diff --git a/src/router/screens/settings/index.ts b/src/router/screens/settings/index.ts index 66b1069be..d0a8c151a 100644 --- a/src/router/screens/settings/index.ts +++ b/src/router/screens/settings/index.ts @@ -36,7 +36,7 @@ const settingsScreens = [ createScreen("Settings", Settings, { presentation: "modal", headerTitle: "Paramètres", - headerShown: false, + headerShown: true, }), createScreen("SettingsNotifications", SettingsNotifications, { headerTitle: "Notifications", diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index 4ebcc3293..1b5c1f11c 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -121,6 +121,7 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { icon: , color: "#D79400", label: "Services externes", + description: "Connecte ta cantine à Papillon", onPress: () => navigation.navigate("SettingsExternalServices"), }, { @@ -165,7 +166,7 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { }, { icon: , - label: "Avancé", + label: "Accessibilité", tabs: [ { icon: , @@ -173,28 +174,36 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { label: "Accessibilité", onPress: () => navigation.navigate("SettingsAccessibility"), }, - { - icon: , - color: "#498c75", - label: "Extensions", - description: "Disponible prochainement", - onPress: () => navigation.navigate("SettingsAddons"), - disabled: !defined("enable_addons"), - }, + ], + }, + { + icon: , + label: "Expérimental", + tabs: [ { icon: , color: "#58A3C3", - label: "Papillon Magic (Bêta)", + label: "Papillon Magic", + beta: true, description: "Fonctionnalités intelligentes", onPress: () => navigation.navigate("SettingsMagic"), }, { icon: , color: "#1f76ce", - label: "Multiservice (Bêta)", + label: "Multiservice", + beta: true, description: "Connecte plusieurs services en un seul espace de travail", onPress: () => navigation.navigate("SettingsMultiService"), }, + { + icon: , + color: "#498c75", + label: "Extensions", + description: "Disponible prochainement", + onPress: () => navigation.navigate("SettingsAddons"), + disabled: !defined("enable_addons"), + }, ], }, { @@ -279,7 +288,7 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { } if (!isTablet) { - tabs[2].tabs.unshift({ + tabs[1].tabs.push({ icon: click ? ( = ({ route, navigation }) => { // show header on Android useLayoutEffect(() => { - navigation.setOptions({ - headerShown: Platform.OS === "android", - }); + navigation.setOptions( + Platform.OS === "android" ? { + headerShown: true, + } : { + headerTransparent: true, + headerTitle: () => ( + + {!scrolled && Platform.OS === "ios" && + + + + } + + ), + }); }); return ( <> - {!scrolled && Platform.OS === "ios" && - - - - } - = ({ route, navigation }) => { }} /> } + trailing={ + subtab.beta && ( + + + Bêta + + + ) + } > {subtab.label} From 8f9d50226577c04474264c1818c5ceb482753aa1 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 18 Mar 2025 21:53:13 +0100 Subject: [PATCH 0924/1144] =?UTF-8?q?fix:=20mise=20=C3=A0=20jour=20de=20la?= =?UTF-8?q?=20configuration=20de=20l'orientation=20de=20l'=C3=A9cran=20pou?= =?UTF-8?q?r=20permettre=20une=20orientation=20par=20d=C3=A9faut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/src/main/AndroidManifest.xml | 6 +++++- app.config.ts | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 86e6a9881..32031b011 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -22,6 +22,10 @@ + + + + @@ -32,7 +36,7 @@ - + diff --git a/app.config.ts b/app.config.ts index e80bbd594..511d5c7ee 100644 --- a/app.config.ts +++ b/app.config.ts @@ -6,7 +6,7 @@ export default (): ExpoConfig => ({ slug: PackageJSON.name, scheme: "papillon", version: PackageJSON.version, - orientation: "portrait", + orientation: "default", icon: "./assets/icon.png", userInterfaceStyle: "automatic", primaryColor: "#32AB8E", From a43c99cced44007d81dee4d1f5db887834eed05c Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Tue, 18 Mar 2025 21:58:52 +0100 Subject: [PATCH 0925/1144] fix: adding `flex: 1` to move button in right of input --- src/views/account/Chat/Modals/Chat.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/account/Chat/Modals/Chat.tsx b/src/views/account/Chat/Modals/Chat.tsx index ce7aa702b..d6a3dddeb 100644 --- a/src/views/account/Chat/Modals/Chat.tsx +++ b/src/views/account/Chat/Modals/Chat.tsx @@ -366,6 +366,7 @@ const Chat: Screen<"Chat"> = ({ navigation, route }) => { /> Date: Tue, 18 Mar 2025 22:02:15 +0100 Subject: [PATCH 0926/1144] fix(Evaluation): selected in PapillonPicker not working --- src/views/account/Evaluation/Evaluation.tsx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/views/account/Evaluation/Evaluation.tsx b/src/views/account/Evaluation/Evaluation.tsx index 1f81254d5..e5eb73f87 100644 --- a/src/views/account/Evaluation/Evaluation.tsx +++ b/src/views/account/Evaluation/Evaluation.tsx @@ -126,7 +126,21 @@ const Evaluation: Screen<"Evaluation"> = ({ route, navigation }) => { period.name)} + data={periods.map((period) => { + return { + label: period.name, + subtitle: + new Date(period.startTimestamp as number).toLocaleDateString( + "fr-FR", + { + month: "long", + day: "numeric", + } + ), + onPress: () => setUserSelectedPeriod(period.name), + checked: period.name === selectedPeriod, + }; + })} selected={userSelectedPeriod ?? selectedPeriod} onSelectionChange={setUserSelectedPeriod} > From c71fc45e94ac95501c9ab1034bd6e520cb2e311c Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 18 Mar 2025 22:06:39 +0100 Subject: [PATCH 0927/1144] =?UTF-8?q?feat:=20ajout=20du=20prix=20et=20mise?= =?UTF-8?q?=20=C3=A0=20jour=20du=20calcul=20des=20repas=20restants=20dans?= =?UTF-8?q?=20les=20services=20de=20solde?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/alise/balance.ts | 1 + src/services/ard/balance.ts | 1 + src/services/izly/balance.ts | 31 +++++++++++++++++-- src/services/shared/Balance.ts | 1 + src/services/turboself/balance.ts | 1 + .../account/Restaurant/Modals/CardDetail.tsx | 28 +++++++++++++++++ 6 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/services/alise/balance.ts b/src/services/alise/balance.ts index e00b7c8a3..2c2a21d1a 100644 --- a/src/services/alise/balance.ts +++ b/src/services/alise/balance.ts @@ -9,6 +9,7 @@ export const getBalance = async (account: AliseAccount, force = false): Promise< amount: balance, currency: "€", remaining: Math.floor(balance / (mealPrice ?? 0)), + price: mealPrice ?? 0, label: "Self" }]; }; \ No newline at end of file diff --git a/src/services/ard/balance.ts b/src/services/ard/balance.ts index 31e2ff436..cd2893638 100644 --- a/src/services/ard/balance.ts +++ b/src/services/ard/balance.ts @@ -11,6 +11,7 @@ export const balance = async (account: ARDAccount): Promise => { amount: wallet.walletAmount / 100, currency: "€", remaining: wallet.walletName.toLowerCase() !== "cafetaria" ? Math.floor((wallet.walletAmount / mealPrice!)) : null, + price: mealPrice!, label: wallet.walletName[0].toUpperCase() + wallet.walletName.slice(1).toLowerCase() })).reverse(); diff --git a/src/services/izly/balance.ts b/src/services/izly/balance.ts index 3f4978262..359e002a7 100644 --- a/src/services/izly/balance.ts +++ b/src/services/izly/balance.ts @@ -2,22 +2,49 @@ import type { IzlyAccount } from "@/stores/account/types"; import type { Balance } from "../shared/Balance"; import { ErrorServiceUnauthenticated } from "../shared/errors"; import * as ezly from "ezly"; +import { operations } from "ezly"; + export const balance = async (account: IzlyAccount): Promise => { if (!account.instance) throw new ErrorServiceUnauthenticated("ARD"); const balance = await ezly.balance(account.instance); const currency = account.authentication.configuration.currency; + let remainingDividedBy = 0; + + try { + const payments = await operations( + account.instance!, + ezly.OperationKind.Payment, + 10 + ); + + const paysFullPrice = payments.filter((payment) => payment.amount === 3.30).length > 5; + const paysBoursePrice = payments.filter((payment) => payment.amount === 1).length > 5; + + if (paysFullPrice) { + remainingDividedBy = 3.30; + } else if (paysBoursePrice) { + remainingDividedBy = 1; + } + } catch (e) { + console.error(e); + } + + const remaining = remainingDividedBy > 0 ? Math.round(balance.value / remainingDividedBy) : null; + return [{ amount: balance.value, currency: currency, - remaining: null, + remaining: remaining, + price: remainingDividedBy, label: "Self" }, ...(balance.cashValue > 0 ? [{ amount: balance.cashValue, currency: currency, - remaining: null, + remaining: remaining, + price: remainingDividedBy, label: "Cash" }]: []) ]; diff --git a/src/services/shared/Balance.ts b/src/services/shared/Balance.ts index e21264f46..e66744ebc 100644 --- a/src/services/shared/Balance.ts +++ b/src/services/shared/Balance.ts @@ -2,5 +2,6 @@ export interface Balance { amount: number; currency: string; remaining: number | null; + price?: number; label: string; } diff --git a/src/services/turboself/balance.ts b/src/services/turboself/balance.ts index ea1e8451f..6fe25db29 100644 --- a/src/services/turboself/balance.ts +++ b/src/services/turboself/balance.ts @@ -12,6 +12,7 @@ export const getBalance = async (account: TurboselfAccount): Promise amount: balance.estimatedAmount / 100, currency: currencySymbol ?? "€", remaining: Math.floor(balance.estimatedAmount / (lunchPrice ?? 0)), + price: lunchPrice ?? 0, label: balance.label }); } diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index ad13486f4..65820bf85 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -276,6 +276,34 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio )} + {card?.balance[0].remaining !== null && ( + + 1 ? "#00C853" : "#FF1744", + }} + > + {card.balance[0].remaining.toFixed(0)} + + } + > + + Repas restants + + + Tarif estimé à {card.balance[0].price?.toFixed(2)} € + + + + )} + {card?.history.length > 0 && ( {card.history From 0e63252e44221bff72fcc48e3fe33afa93860ead Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 18 Mar 2025 22:15:02 +0100 Subject: [PATCH 0928/1144] =?UTF-8?q?fix:=20mise=20=C3=A0=20jour=20des=20l?= =?UTF-8?q?imites=20de=20paiement=20et=20am=C3=A9lioration=20de=20l'affich?= =?UTF-8?q?age=20des=20dates=20dans=20l'historique=20des=20r=C3=A9servatio?= =?UTF-8?q?ns?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/izly/balance.ts | 6 ++--- src/services/izly/history.ts | 4 ++-- .../account/Restaurant/Modals/CardDetail.tsx | 24 +++++++++++++++---- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/services/izly/balance.ts b/src/services/izly/balance.ts index 359e002a7..262daee63 100644 --- a/src/services/izly/balance.ts +++ b/src/services/izly/balance.ts @@ -16,11 +16,11 @@ export const balance = async (account: IzlyAccount): Promise => { const payments = await operations( account.instance!, ezly.OperationKind.Payment, - 10 + 5 ); - const paysFullPrice = payments.filter((payment) => payment.amount === 3.30).length > 5; - const paysBoursePrice = payments.filter((payment) => payment.amount === 1).length > 5; + const paysFullPrice = payments.filter((payment) => payment.amount === 3.30).length > 4; + const paysBoursePrice = payments.filter((payment) => payment.amount === 1).length > 4; if (paysFullPrice) { remainingDividedBy = 3.30; diff --git a/src/services/izly/history.ts b/src/services/izly/history.ts index b768c9506..027d511d5 100644 --- a/src/services/izly/history.ts +++ b/src/services/izly/history.ts @@ -3,8 +3,8 @@ import type { ReservationHistory } from "../shared/ReservationHistory"; import { operations, OperationKind} from "ezly"; export const history = async (account: IzlyAccount): Promise => { - const payments = await operations(account.instance!, OperationKind.Payment, 10); - const topup = await operations(account.instance!, OperationKind.TopUp, 10); + const payments = await operations(account.instance!, OperationKind.Payment, 60); + const topup = await operations(account.instance!, OperationKind.TopUp, 60); const currency = account.authentication.configuration.currency; return [ diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index 65820bf85..a977e1d79 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -4,7 +4,7 @@ import Reanimated from "react-native-reanimated"; import React, { useState } from "react"; import { NativeItem, NativeList, NativeText } from "@/components/Global/NativeComponents"; -import { formatDistance } from "date-fns"; +import { differenceInDays, formatDistance } from "date-fns"; import { fr } from "date-fns/locale"; import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; import { useTheme } from "@react-navigation/native"; @@ -358,9 +358,25 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio {history.label} - - il y a {formatDistance(new Date(history.timestamp), new Date(), { locale: fr })} - + + {differenceInDays(new Date(), new Date(history.timestamp)) < 30 ? ( + + il y a {formatDistance(new Date(history.timestamp), new Date(), { locale: fr })} • {new Date(history.timestamp).toLocaleDateString("fr-FR", { + day: "numeric", + weekday: "short", + month: "short", + })} + + ) : ( + + {new Date(history.timestamp).toLocaleDateString("fr-FR", { + day: "numeric", + weekday: "long", + month: "long", + year: new Date(history.timestamp).getFullYear() !== new Date().getFullYear() ? "numeric" : undefined, + })} + + )} ))} From 2a6033ad1b774b905faa3990aa2e6e46c89d4bfb Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 18 Mar 2025 22:15:16 +0100 Subject: [PATCH 0929/1144] =?UTF-8?q?fix:=20ajout=20d'une=20v=C3=A9rificat?= =?UTF-8?q?ion=20pour=20l'affichage=20de=20la=20date=20dans=20l'historique?= =?UTF-8?q?=20des=20r=C3=A9servations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Restaurant/Modals/CardDetail.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index a977e1d79..b58dfb03b 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -359,7 +359,7 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio {history.label} - {differenceInDays(new Date(), new Date(history.timestamp)) < 30 ? ( + {new Date(history.timestamp) && differenceInDays(new Date(), new Date(history.timestamp)) < 30 ? ( il y a {formatDistance(new Date(history.timestamp), new Date(), { locale: fr })} • {new Date(history.timestamp).toLocaleDateString("fr-FR", { day: "numeric", From 1a80156f68696fb015ae042807b69c26461492a0 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 18 Mar 2025 22:17:34 +0100 Subject: [PATCH 0930/1144] =?UTF-8?q?fix:=20augmentation=20des=20limites?= =?UTF-8?q?=20de=20paiement=20=C3=A0=20200=20et=20mise=20=C3=A0=20jour=20d?= =?UTF-8?q?e=20la=20police=20dans=20les=20d=C3=A9tails=20de=20la=20carte?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/izly/history.ts | 8 ++++++-- src/views/account/Restaurant/Modals/CardDetail.tsx | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/services/izly/history.ts b/src/services/izly/history.ts index 027d511d5..c036b3099 100644 --- a/src/services/izly/history.ts +++ b/src/services/izly/history.ts @@ -3,8 +3,12 @@ import type { ReservationHistory } from "../shared/ReservationHistory"; import { operations, OperationKind} from "ezly"; export const history = async (account: IzlyAccount): Promise => { - const payments = await operations(account.instance!, OperationKind.Payment, 60); - const topup = await operations(account.instance!, OperationKind.TopUp, 60); + const payments = await operations( + account.instance!, + OperationKind.Payment, + 200 + ); + const topup = await operations(account.instance!, OperationKind.TopUp, 200); const currency = account.authentication.configuration.currency; return [ diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index b58dfb03b..2164bf33b 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -284,7 +284,7 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio variant="titleLarge" style={{ marginRight: 10, - fontFamily: "medium", + fontFamily: "semibold", fontSize: 26, lineHeight: 28, color: card.balance[0].remaining > 1 ? "#00C853" : "#FF1744", From e169414dab18ad009279cf4f48ba8a19f6eefd0a Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 18 Mar 2025 22:18:38 +0100 Subject: [PATCH 0931/1144] fix: conversion de la partie visible de l'identifiant de carte en minuscules --- src/utils/external/restaurant.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/external/restaurant.ts b/src/utils/external/restaurant.ts index d791137c8..b1192f79a 100644 --- a/src/utils/external/restaurant.ts +++ b/src/utils/external/restaurant.ts @@ -12,7 +12,7 @@ export const formatCardIdentifier = ( return ""; } - const visiblePart = identifier.slice(-4); + const visiblePart = identifier.slice(-4).toLowerCase(); const maskedPart = identifier.slice(-(4 + dots), -4).replace(/./g, "•"); return ( maskedPart + separator + (visiblePart.match(/.{1,4}/g) ?? []).join(" ") From 1dc88f07e6162223184dca4861e20d8e682a73f4 Mon Sep 17 00:00:00 2001 From: Tryon Date: Tue, 18 Mar 2025 22:22:16 +0100 Subject: [PATCH 0932/1144] =?UTF-8?q?fix:=20am=C3=A9lioration=20de=20la=20?= =?UTF-8?q?gestion=20de=20l'=C3=A9tat=20en=20arri=C3=A8re-plan=20et=20ajou?= =?UTF-8?q?t=20de=20l'ID=20local=20au=20composant=20SettingsFlags?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.tsx | 62 +++++++++++++++------------- src/views/settings/SettingsFlags.tsx | 3 +- 2 files changed, 35 insertions(+), 30 deletions(-) diff --git a/App.tsx b/App.tsx index 0216722da..ddbe16305 100644 --- a/App.tsx +++ b/App.tsx @@ -3,7 +3,7 @@ import Router from "@/router"; import { useFonts } from "expo-font"; import * as SplashScreen from "expo-splash-screen"; import { LogBox, AppState } from "react-native"; -import React, { useEffect, useState, useRef, useCallback } from "react"; +import React, { useEffect, useState, useCallback } from "react"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { useAccounts, useCurrentAccount } from "@/stores/account"; import { AccountService } from "@/stores/account/types"; @@ -21,13 +21,12 @@ const DEFAULT_BACKGROUND_TIME = 15 * 60 * 1000; // 15 minutes const BACKGROUND_LIMITS = { [AccountService.EcoleDirecte]: 15 * 60 * 1000, // 15 minutes - [AccountService.Pronote]: 5 * 60 * 1000, // 5 minutes - [AccountService.Skolengo]: 12 * 60 * 60 * 1000, // 12 hours + [AccountService.Pronote]: 3 * 1000, // 5 minutes + [AccountService.Skolengo]: 60 * 60 * 1000, // 1 heure }; export default function App () { const [appState, setAppState] = useState(AppState.currentState); - const backgroundStartTime = useRef(null); const currentAccount = useCurrentAccount((store) => store.account); const switchTo = useCurrentAccount((store) => store.switchTo); const accounts = useAccounts((store) => store.accounts).filter(account => !account.isExternal); @@ -65,30 +64,39 @@ export default function App () { } }; - const getBackgroundTimeLimit = useCallback((service) => { + const getBackgroundTimeLimit = useCallback((service: keyof typeof BACKGROUND_LIMITS) => { return BACKGROUND_LIMITS[service] ?? DEFAULT_BACKGROUND_TIME; }, []); const handleBackgroundState = useCallback(async () => { - if (!backgroundStartTime.current) return; - - const timeInBackground = Date.now() - backgroundStartTime.current; - await AsyncStorage.setItem("@background_timestamp", Date.now().toString()); - - for (const account of accounts) { - const timeLimit = getBackgroundTimeLimit(account.service); - const timeInBackgroundSeconds = Math.floor(timeInBackground / 1000); - - if (timeInBackground >= timeLimit && currentAccount === account) { - setTimeout(() => { - switchTo(account).catch((error) => { + const savedTimestamp = await AsyncStorage.getItem("@background_timestamp"); + if (!savedTimestamp || !currentAccount) return; + + const timeInBackground = Date.now() - parseInt(savedTimestamp, 10); + const timeLimit = + currentAccount.service in BACKGROUND_LIMITS + ? getBackgroundTimeLimit(currentAccount.service as keyof typeof BACKGROUND_LIMITS) + : DEFAULT_BACKGROUND_TIME; + + const timeInBackgroundSeconds = Math.floor(timeInBackground / 1000); + if (timeInBackground >= timeLimit) { + log( + `⚠️ Refreshing current account ${currentAccount.studentName.first} after ${timeInBackgroundSeconds}s in background`, + "RefreshToken" + ); + for (const account of accounts) { + if (account.localID === currentAccount.localID) { + await switchTo(account).catch((error) => { log(`Error during switchTo: ${error}`, "RefreshToken"); }); - }, 0); - await new Promise(resolve => setTimeout(resolve, 1000)); + break; + } } + await new Promise(resolve => setTimeout(resolve, 1000)); } - }, [accounts, switchTo, getBackgroundTimeLimit, currentAccount]); + await AsyncStorage.removeItem("@background_timestamp"); + }, [currentAccount, switchTo, getBackgroundTimeLimit]); + useEffect(() => { if (!isExpoGo()) checkInitialNotification(); @@ -105,9 +113,9 @@ export default function App () { await notifee.cancelAllNotifications(); } await handleBackgroundState(); - backgroundStartTime.current = null; } else if (nextAppState.match(/inactive|background/)) { - backgroundStartTime.current = Date.now(); + const now = Date.now(); + await AsyncStorage.setItem("@background_timestamp", now.toString()); } setAppState(nextAppState); }); @@ -125,9 +133,7 @@ export default function App () { "[Reanimated] Property ", ]); - if (!isExpoGo()) { - registerBackgroundTasks(); - } + if (!isExpoGo()) registerBackgroundTasks(); const encoding = require("text-encoding"); Object.assign(global, { @@ -138,13 +144,11 @@ export default function App () { }); }, []); - if (!fontsLoaded) { - return null; - } + if (!fontsLoaded) return null; return ( ); -} +} \ No newline at end of file diff --git a/src/views/settings/SettingsFlags.tsx b/src/views/settings/SettingsFlags.tsx index a945537dd..3ad52b777 100644 --- a/src/views/settings/SettingsFlags.tsx +++ b/src/views/settings/SettingsFlags.tsx @@ -128,7 +128,8 @@ const SettingsFlags: Screen<"SettingsFlags"> = ({ navigation }) => { {renderAccountSection("Informations générales", { name: account.name, schoolName: account.schoolName, - className: account.className + className: account.className, + localID: account.localID, })} {renderAccountSection("Détails de l'authentification", account.authentication)} From e29da64bd2ae67505f169f0e0437ac4e756dd718 Mon Sep 17 00:00:00 2001 From: Tryon Date: Tue, 18 Mar 2025 22:38:55 +0100 Subject: [PATCH 0933/1144] =?UTF-8?q?fix:=20correction=20de=20la=20limite?= =?UTF-8?q?=20de=20temps=20en=20arri=C3=A8re-plan=20pour=20le=20service=20?= =?UTF-8?q?Pronote?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/App.tsx b/App.tsx index ddbe16305..f53260098 100644 --- a/App.tsx +++ b/App.tsx @@ -21,7 +21,7 @@ const DEFAULT_BACKGROUND_TIME = 15 * 60 * 1000; // 15 minutes const BACKGROUND_LIMITS = { [AccountService.EcoleDirecte]: 15 * 60 * 1000, // 15 minutes - [AccountService.Pronote]: 3 * 1000, // 5 minutes + [AccountService.Pronote]: 5 * 60 * 1000, // 5 minutes [AccountService.Skolengo]: 60 * 60 * 1000, // 1 heure }; From 1de92acd4d67dc885d062042083cdd65136b0ce4 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 18 Mar 2025 22:43:31 +0100 Subject: [PATCH 0934/1144] =?UTF-8?q?fix:=20mise=20=C3=A0=20jour=20de=20la?= =?UTF-8?q?=20version=20de=20l'application=20=C3=A0=207.10.4=20et=20correc?= =?UTF-8?q?tion=20de=20la=20d=C3=A9claration=20du=20nom=20du=20produit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/build.gradle | 4 ++-- android/app/src/main/AndroidManifest.xml | 4 ++++ ios/Papillon.xcodeproj/project.pbxproj | 4 ++-- ios/Papillon/Info.plist | 2 +- package.json | 2 +- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index a3d033c58..bbe73f10c 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -88,8 +88,8 @@ android { applicationId 'xyz.getpapillon.app' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 71030 - versionName "7.10.3" + versionCode 71040 + versionName "7.10.4" } signingConfigs { debug { diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 86e6a9881..6abd3535c 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -22,6 +22,10 @@ + + + + diff --git a/ios/Papillon.xcodeproj/project.pbxproj b/ios/Papillon.xcodeproj/project.pbxproj index bbfe9f05e..a36919e50 100644 --- a/ios/Papillon.xcodeproj/project.pbxproj +++ b/ios/Papillon.xcodeproj/project.pbxproj @@ -457,7 +457,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = Papillon; + PRODUCT_NAME = "Papillon"; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -490,7 +490,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = Papillon; + PRODUCT_NAME = "Papillon"; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index d146778d5..c3afae4d1 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 7.10.3 + 7.10.4 CFBundleSignature ???? CFBundleURLTypes diff --git a/package.json b/package.json index 052d228d3..9ec46b767 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "papillonvex", - "version": "7.10.3", + "version": "7.10.4", "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", From c4c6466897dc8815cfe31c6d098beab40d9f78bd Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 18 Mar 2025 22:56:57 +0100 Subject: [PATCH 0935/1144] =?UTF-8?q?fix:=20Ajout=20de=20types=20pour=20le?= =?UTF-8?q?s=20param=C3=A8tres=20et=20corrections=20mineures=20dans=20plus?= =?UTF-8?q?ieurs=20fichiers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.tsx | 2 +- android/app/src/main/AndroidManifest.xml | 8 + ios/Papillon.xcodeproj/project.pbxproj | 4 +- ios/Papillon/Info.plist | 2 + ios/Podfile.lock | 210 ++++++++++-------- package-lock.json | 4 +- .../Home/AccountSwitcherContextMenu.tsx | 3 + src/providers/AlertProvider.tsx | 4 + src/router/index.tsx | 2 + src/views/account/Home/Home.tsx | 2 +- src/views/settings/Settings.tsx | 5 +- 11 files changed, 147 insertions(+), 99 deletions(-) diff --git a/App.tsx b/App.tsx index 5870eb94b..49e1e4a86 100644 --- a/App.tsx +++ b/App.tsx @@ -54,7 +54,7 @@ export default function App () { configureOrientation(); }, []); - const handleNotificationPress = async (notification) => { + const handleNotificationPress = async (notification: any) => { if (notification?.data) { const accountID = notification.data.accountID; const account = findAccountByID(accountID); diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 32031b011..a4aba14ba 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -26,6 +26,14 @@ + + + + + + + + diff --git a/ios/Papillon.xcodeproj/project.pbxproj b/ios/Papillon.xcodeproj/project.pbxproj index a36919e50..bbfe9f05e 100644 --- a/ios/Papillon.xcodeproj/project.pbxproj +++ b/ios/Papillon.xcodeproj/project.pbxproj @@ -457,7 +457,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = "Papillon"; + PRODUCT_NAME = Papillon; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -490,7 +490,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = "Papillon"; + PRODUCT_NAME = Papillon; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index c3afae4d1..26af44a7a 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -101,6 +101,8 @@ UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 17c9f7b62..f31f42d9a 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -293,6 +293,28 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga + - ExpoScreenOrientation (7.0.5): + - DoubleConversion + - ExpoModulesCore + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Codegen + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga - ExpoSensors (13.0.9): - ExpoModulesCore - ExpoSharing (12.0.1): @@ -1785,6 +1807,7 @@ DEPENDENCIES: - ExpoLinearGradient (from `../node_modules/expo-linear-gradient/ios`) - ExpoMediaLibrary (from `../node_modules/expo-media-library/ios`) - ExpoModulesCore (from `../node_modules/expo-modules-core`) + - ExpoScreenOrientation (from `../node_modules/expo-screen-orientation/ios`) - ExpoSensors (from `../node_modules/expo-sensors/ios`) - ExpoSharing (from `../node_modules/expo-sharing/ios`) - ExpoStoreReview (from `../node_modules/expo-store-review/ios`) @@ -1944,6 +1967,8 @@ EXTERNAL SOURCES: :path: "../node_modules/expo-media-library/ios" ExpoModulesCore: :path: "../node_modules/expo-modules-core" + ExpoScreenOrientation: + :path: "../node_modules/expo-screen-orientation/ios" ExpoSensors: :path: "../node_modules/expo-sensors/ios" ExpoSharing: @@ -2110,121 +2135,122 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: d3f49c53809116a5d38da093a8aa78bf551aed09 - candlefinance-app-icon: 9d7562e14a7a087c4ce87c262f59300a4f18106a + candlefinance-app-icon: 91095f99969a105b86ff97aab7dd60e2bfd4152c ComputableLayout: c50faffac4ed9f8f05b0ce5e6f3a60df1f6042c8 ContextMenuAuxiliaryPreview: 20be0be795b783b68f8792732eed4bed9f202c1c DGSwiftUtilities: 2f0d35d5ff3d57bd70ccc42f15971460db202c41 DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5 - EXApplication: c08200c34daca7af7fd76ac4b9d606077410e8ad - EXAV: afa491e598334bbbb92a92a2f4dd33d7149ad37f - EXBarCodeScanner: e2dd9b42c1b522a2adc9202b1dfbc64cb34456d1 - EXConstants: 409690fbfd5afea964e5e9d6c4eb2c2b59222c59 - EXImageLoader: ab589d67d6c5f2c33572afea9917304418566334 + EXApplication: ec862905fdab3a15bf6bd8ca1a99df7fc02d7762 + EXAV: 64e72329d2f8c2ba13608fed4a713af4e793242d + EXBarCodeScanner: 6415603150dd5989a139570bb5af19b7f169fe49 + EXConstants: 89d35611505a8ce02550e64e43cd05565da35f9a + EXImageLoader: 1fe96c70cdc78bedc985ec4b1fab5dd8e67dc38b EXJSONUtils: 30c17fd9cc364d722c0946a550dfbf1be92ef6a4 - EXLocation: 43e9b582ca63a23c6f0a18d8cbe2145b3a388b55 - EXManifests: c1fab4c3237675e7b0299ea8df0bcb14baca4f42 - Expo: 8c995afb875c15bf8439af0b20bcb9ed8f90d0bd - expo-dev-client: 0cec8ec81fd01c10d9afcd9f6de3768b10644aee - expo-dev-launcher: c510982f3437742353389750748520580af0ee43 - expo-dev-menu: 12b319a9bc73d76a1ed47ce52055b5356edb971d - expo-dev-menu-interface: 5764ad537419c1a5e8f66f668e29c81e8aca290c - ExpoAsset: 323700f291684f110fb55f0d4022a3362ea9f875 - ExpoBackgroundFetch: a06c553ecaf0bade0acd691042d996b9ce926327 - ExpoBlur: 4d32f9e33bab18ae2ee079c7b0422e4210d49c97 - ExpoBrightness: c184f0ef116a51d7f5c1dd4c253d4d9806a5e022 - ExpoCamera: 929be541d1c1319fcf32f9f5d9df8b97804346b5 - ExpoClipboard: 23d203f5d4843699fbc45be1cc4fe1fbd811a6fa - ExpoCrypto: 156078f266bf28f80ecf5e2a9c3a0d6ffce07a1c - ExpoDevice: fc94f0e42ecdfd897e7590f2874fc64dfa7e9b1c - ExpoFileSystem: 80bfe850b1f9922c16905822ecbf97acd711dc51 - ExpoFont: 00756e6c796d8f7ee8d211e29c8b619e75cbf238 - ExpoHaptics: 5a3a88971af384255baf2504f38b41189cec6984 - ExpoImagePicker: 12a420923383ae38dccb069847218f27a3b87816 - ExpoKeepAwake: 3b8815d9dd1d419ee474df004021c69fdd316d08 - ExpoLinearGradient: 8cec4a09426d8934c433e83cb36262d72c667fce - ExpoMediaLibrary: 81573bcbd50cbd0a3ef57216c93593157d32b558 - ExpoModulesCore: 831ece8311a489418746925820bbffdda587d6f4 - ExpoSensors: 3bc12e5186b94703464d1554ce5ebfff05f91093 - ExpoSharing: 8db05dd85081219f75989a3db2c92fe5e9741033 - ExpoStoreReview: 15f9a636b62ff00bb21cbe9a9fe22f0239da4481 - ExpoSystemUI: d4f065a016cae6721b324eb659cdee4d4cf0cb26 - ExpoWebBrowser: 7595ccac6938eb65b076385fd23d035db9ecdc8e - EXSplashScreen: 3cad09949c2ca6730cbb2801b8c51cb87692425a - EXTaskManager: 9c3520305c3aa1b4a12a7c6d1e3f85f2779c06e9 - EXUpdatesInterface: 996527fd7d1a5d271eb523258d603f8f92038f24 + EXLocation: c52e800875ce2a5974b3b796ff2f3a8b978e0b28 + EXManifests: ebb7f551c340c0d06f3ecd9ae662e418bf68417c + Expo: ed0a748eb6be0efd2c3df7f6de3f3158a14464c9 + expo-dev-client: 67bfa449de8ee3e3cd88a6945a8058819fffb0f0 + expo-dev-launcher: 5db274c8fdf72ca2f98b189d62924b014f7b90b5 + expo-dev-menu: 8b0ea59392e2dd975390ea6f0472ce350d94866a + expo-dev-menu-interface: 5c6b79875bf0ab1251ea9962f60968fe39ed2637 + ExpoAsset: 286fee7ba711ce66bf20b315e68106b13b8629fc + ExpoBackgroundFetch: 2988b27bc95f322e856dbd44060b6951fdfe8950 + ExpoBlur: 99901a4531f5d3ac4a19b362907b8f75da4ed9c8 + ExpoBrightness: 174c89604618f907ac2a22dfaa2e71d49db283fb + ExpoCamera: cf49d2d121a9f883be0f98dde15a2185a1dd42be + ExpoClipboard: 243e22ff4161bbffcd3d2db469ae860ddc1156be + ExpoCrypto: c5c052d5f9f668c21975cb4caf072cec23c823fa + ExpoDevice: 84b3ed79df1234c17edfbf335f6ecf3c636f74de + ExpoFileSystem: 2988caaf68b7cb706e36d382829d99811d9d76a5 + ExpoFont: 38dddf823e32740c2a9f37c926a33aeca736b5c4 + ExpoHaptics: 9f47be324f691b6291c17c216189ab832d1a4d69 + ExpoImagePicker: 517a47896adf5d55d0a1c159e5d1e312af12e57c + ExpoKeepAwake: dd02e65d49f1cfd9194640028ae2857e536eb1c9 + ExpoLinearGradient: 4c44b3803b441724874b232e6520b51ca6a50db1 + ExpoMediaLibrary: b30366bf8f938166b1fe2d019e5b15337349d91e + ExpoModulesCore: 9ac73e2f60e0ea1d30137ca96cfc8c2aa34ef2b2 + ExpoScreenOrientation: cd225e9e8350e34b91bfce68dc744798ab4629c2 + ExpoSensors: 805b2d91b629a08da78dcc86032a0833cb0e98c0 + ExpoSharing: 5e6b6cbc0c232084b79ffa7243459f7dcdc5b1cb + ExpoStoreReview: 5ce23b11d7cdcba23fa26b8cd9dd83765e2ac7bf + ExpoSystemUI: 2072307375696c398a5d75633bdd5143fadc3d26 + ExpoWebBrowser: cf10afe886891ab495877dada977fe6c269614a4 + EXSplashScreen: a4ce3dd5d28d48e8b9132bcd9b58ee8e340db78c + EXTaskManager: b515b853fd97286a25cb643978ff7fa456f3139a + EXUpdatesInterface: c3a9494c2173db6442c7d5ad4e5b984972760fd3 FBLazyVector: 04dc972982abebd96d823752c3a426bbe6ac397f fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120 glog: fdfdfe5479092de0c4bdbebedd9056951f092c4f hermes-engine: 2102c92e54a031a270fd1fe84169ec8a0901b7bd lottie-ios: e047b1d2e6239b787cc5e9755b988869cf190494 - lottie-react-native: 4279da8b681e89c29a2adb9f99985d6cf372d49d + lottie-react-native: 45707364bd70cffa7602fa1a1abb40dee5f3c0e0 RCT-Folly: 02617c592a293bd6d418e0a88ff4ee1f88329b47 RCTDeprecation: c4e6e3f6d44f76c45a964b40fa3eb2475259c027 RCTRequired: c4886806a178cd895cd4a17dae1642b72e7e8233 RCTTypeSafety: 4e9f36465ccbcca7b62f5cb8fc1ff2c997c3b92e React: 7f6ee889aa17245726efe5c0be52389e58d9d7c1 React-callinvoker: 0155d33f43924c206dfaa040c020d0404bbb54d6 - React-Codegen: 19dcd3c5d2418af62dcab56a09ff62accee8b60e - React-Core: da6746240394ea2bb828e6e93baed4dea3c27689 - React-CoreModules: 319cbc30aee816ea4716115b0f77035b95902a80 - React-cxxreact: b8e823d419880d779be49fc049732e8facf64fc7 + React-Codegen: 16b3c98135968de37085da126814187e6db73d7b + React-Core: 063d3e42f48c671822d16ade452b8d25793cc9fa + React-CoreModules: 46303f9f50e942fdd8763626464a8a24d9a36419 + React-cxxreact: 08e7fb41e693d0d18266c394d95501c719f81a7e React-debug: 81d2423e256c8f0dad94f368e7a5450b3885c5b5 - React-Fabric: a96f6898862c047023f140a787289ef4111e059d - React-FabricImage: 0cb85c8a9672cf3eddf340d96806b0d57e5a4d60 + React-Fabric: 87f30b642973d16bbe84c0b898cebab6a26d8b65 + React-FabricImage: 1ebed5160efb85acf9c492dfff7d21ec04225369 React-featureflags: c3e59ddabf0bc2b8e125aff4aab6943112f5d852 - React-graphics: 4426a34fd9695e861d644fac89ab6ba5a42e110d - React-hermes: 010c8dc210afa547991e13fc5acee7e63cf9f2e8 - React-ImageManager: 4fc40ebeac12716ed8beeadc17785b799096cae9 - React-jserrorhandler: d2621ce6fee5bf965ba8968a5315f1c6ea6fd731 - React-jsi: df1b6ca5308a888cbdf44c5035ca2e46822f1902 - React-jsiexecutor: 88e8f50d2e0cdb1531c6d956c148577698311ac8 - React-jsinspector: cbd4d9bf7d0944f662af337727eec69b9eca1db6 - React-jsitracing: 549d1177ff45058b1300652db58add8c357a11ed - React-logger: 87cddd161bd9784d60fc5528f21c57ca07d2962a - React-Mapbuffer: b6e208217a18044f0f1a183be8f6756ad67b42d7 - react-native-cookies: f54fcded06bb0cda05c11d86788020b43528a26c - react-native-ios-context-menu: 486ed6e4d0fd44799f1b54f866e96d56c47beb48 - react-native-ios-utilities: 9e06932d36dc71680110fde2f84ed89a5e8c172f - react-native-ios-visual-effect-view: ce2f72588dab1a38049131887a1c4660e41dc8e1 - react-native-netinfo: bdb108d340cdb41875c9ced535977cac6d2ff321 - react-native-pager-view: c1e29e1a6105a02807392ba822ad322447a72f55 - react-native-safe-area-context: a240ad4b683349e48b1d51fed1611138d1bdad97 - react-native-view-shot: 6b7ed61d77d88580fed10954d45fad0eb2d47688 - react-native-webview: 05bae3a03a1e4f59568dfc05286c0ebf8954106c + React-graphics: cd4fdf0130e3ff941044cac69b7706608f3a7d08 + React-hermes: 1313182de2921855390a3cde52e2a407a345775f + React-ImageManager: bc485f4de8fb13ebb8663e1f969fc1f28d1209cc + React-jserrorhandler: 863c218d19b34470fc3fc99872fb5175fee47356 + React-jsi: ffc343198a6e03042a205db9e149f321d356f033 + React-jsiexecutor: 50079a521784bfb675522f1c93dd741200c6d8dc + React-jsinspector: f250539d29817196179ea349ba4d475d256777a6 + React-jsitracing: a884f4eb46ba8c373d909f9a712824af65ff41b8 + React-logger: 58cd5ca2c3ab03a06b33e6d46a210d2b17ca116d + React-Mapbuffer: f245095650540b8ddd09ac907a79605f68b1f4d4 + react-native-cookies: d648ab7025833b977c0b19e142503034f5f29411 + react-native-ios-context-menu: a4f17306d7368612ece08c7bde6f43b823b26bfe + react-native-ios-utilities: 03f6cd332eb70f048e2323239cde9c342c1b077b + react-native-ios-visual-effect-view: 2275eb68ecf39299b18a0b33c70ee6dc3baac310 + react-native-netinfo: 2e3c27627db7d49ba412bfab25834e679db41e21 + react-native-pager-view: 0f50eef500ef15dfae1f95a1c945f3d2a5ec5ade + react-native-safe-area-context: df9763c5de6fa38883028e243a0b60123acb8858 + react-native-view-shot: d1a701eb0719c6dccbd20b4bb43b1069f304cb70 + react-native-webview: a4483a25c71098e407df1c1d9056ab907647d7c7 React-nativeconfig: 3b359be06d9ee8d64c1eacbca4f1040f331573fd - React-NativeModulesApple: 511a01d5be0fdb768f6438dd672a79808451ab00 + React-NativeModulesApple: 8fa1db4855dbc1d437d017bc080e75535c85d2d2 React-perflogger: e9ebfc705cb9f60ef5d471637350ccab7abd0444 React-RCTActionSheet: 75cf1acf78e9c2eab4431c3bec100a6462a7f0d5 - React-RCTAnimation: 57a1a83a919ead49436ebe69a902d823f2059e61 - React-RCTAppDelegate: 7732b1367774126055f64b3154cef2ba7d28eea2 - React-RCTBlob: efb9cd99a32b22ca94989adde148a9938a8f6fcc - React-RCTFabric: 08a9ff1c3e612f6cc0b4c1cb06f748daea4073d1 - React-RCTImage: 862823815db42c847cbd875b5ccdd64d320da693 - React-RCTLinking: 682e3018e2192f2cf87ddc601cccdac53fb8b1b7 - React-RCTNetwork: 2945ab8c3ff4e2a6603b5d9a220c35b49f3a3ae0 - React-RCTSettings: 807d08082b799d79a00b9fb3ac1590183afaa241 - React-RCTText: d0e6f900dc2d9796240ff991abec9af8ef713a7b - React-RCTVibration: 4feafdb46bebf5380ab343d86b3ddfa4aadcb071 - React-rendererdebug: 3c1a0a5775f81aab7c1fdd0cb0ce0913802170ce + React-RCTAnimation: 93c464b62848bf9eddecf592819ca76111efb542 + React-RCTAppDelegate: 2f909ac09a87c15465f8b9a5a23c522ca1c19bb3 + React-RCTBlob: 946b43b7698e04ca313f4c872d716887f9e87fd8 + React-RCTFabric: f92685634fe3ef6ec0fc08ce3b55174b0a7cfb55 + React-RCTImage: f3e09ea018d9d49908ce13b4a56949bb7a27a640 + React-RCTLinking: d808fd43aeb59a4e8851a31b82adcf86d585ac5d + React-RCTNetwork: c36cabc37ebd5b6944ad2ba6b41971d8fb273dae + React-RCTSettings: 4268e08911a0005568328fa8909f3558c8ce2a83 + React-RCTText: 82d4e9e279c9cfaed5b95bb86a1d7212a7fd6f21 + React-RCTVibration: 6a0fe57a35bf19d88a611d64563b34fce81af0a7 + React-rendererdebug: 98e83fde6f560b98727a050c602791b72eb7d792 React-rncore: bb90d227f926d19683f9c5790d8e3687a4db051a - React-RuntimeApple: 03bc1bb50bb621d3dd5a7ad0b0fd9ae5b7aa7d03 - React-RuntimeCore: 5411916dbb5d27b2cade9781bbe210ae23906d41 + React-RuntimeApple: d6a09a97e8e8b2b110df39078785afd091e9118f + React-RuntimeCore: 5473e766306c1c9036a5867544673693d1412da8 React-runtimeexecutor: 99640ce20e73e06301dd5e18cc6646fa0968347b - React-RuntimeHermes: 872b0ab24196e98f7ef6857241edaeb9c576a7fb - React-runtimescheduler: bd24ce5b35c288355514aa42cacc9dc782d12dba - React-utils: eb18bf39ea87c4d38f175f41d77b07093eaf7765 - ReactCommon: 9ed4522c478fc82fd9822fd2be960875fccb2c45 - RNCAsyncStorage: 826b603ae9c0f88b5ac4e956801f755109fa4d5c - RNCMaskedView: 090213d32d8b3bb83a4dcb7d12c18f0152591906 - RNDateTimePicker: b6a9b35a785ecbe12b4e7d6de5439d0aa4614146 - RNGestureHandler: 4d82e79c24b18e83040ed9d6f1a178267c5c6aa8 - RNNotifee: 935f3ea8c134c88cbf8b13ea0c97c72c09ad2116 - RNReanimated: 35f9ac9c3ac42d0497ebd1cce5c39d7687a8493e - RNScreens: 2557adbeb88fe3dd34831ce269ad1880af807bba - RNShare: ddc90a41a808268fd75061da52e0239ad416df28 - RNSVG: e451741721b9fd3db71b06aafe9335d09a189692 + React-RuntimeHermes: aba99ffa7ddace35e066beedb8610a8210ee074b + React-runtimescheduler: c5080bd8fd77bd9b98d9a5ec2a4a5cee523e3ec8 + React-utils: 005eaf562b377922d8cf8a5e433046185e479796 + ReactCommon: 788c996e0ae30635a67c2567db2e21f459bcd632 + RNCAsyncStorage: aa75595c1aefa18f868452091fa0c411a516ce11 + RNCMaskedView: de80352547bd4f0d607bf6bab363d826822bd126 + RNDateTimePicker: dde7ca9005d716f3efa9a63004b441679bca9a41 + RNGestureHandler: 4c50b5e4ca199cf497511d44e3fe45d54d62e279 + RNNotifee: 271cfeb505183d2cd1b858c14c3968b6ca30a642 + RNReanimated: def444e044c354f38bb0a5926a8583ba19d944c1 + RNScreens: c2ed06d1d7910f393e073c1a4bda3b258e5a0473 + RNShare: 801ea42c16b5b0fe311334998afc0901980ec06a + RNSVG: 2df577ee9762d1b6deef820173b59b064c2570fe SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d - SwiftUIReactNative: 3241f6b3d9cf6dc29b3dc64f7d838770f19fd604 + SwiftUIReactNative: 6af1ddbecf2c93011a8047f9897907b741c3f0df UMAppLoader: f17a5ee8e85b536ace0fc254b447a37ed198d57e VisualEffectBlurView: 2db84754ea90a2f4eccb0e84038653caddaa4eff Yoga: 9db4b2da1ed5d8e0c94158f9ac379c8b1b529f59 @@ -2232,4 +2258,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 52c2fc0f0da6749b1aa3175a834dddc6986ae26c -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/package-lock.json b/package-lock.json index 174ab0ac6..af2ad4291 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.10.3", + "version": "7.10.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.10.3", + "version": "7.10.4", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.4", "@candlefinance/app-icon": "^0.4.5", diff --git a/src/components/Home/AccountSwitcherContextMenu.tsx b/src/components/Home/AccountSwitcherContextMenu.tsx index ba12ba25d..94bdb224a 100644 --- a/src/components/Home/AccountSwitcherContextMenu.tsx +++ b/src/components/Home/AccountSwitcherContextMenu.tsx @@ -199,6 +199,7 @@ const ContextMenu: React.FC<{ onPressIn={handlePress} onLongPress={handleLongPress} onPressOut={handlePressOut} + // @ts-ignore pointerEvents="auto" style={{ elevation: opened ? 3 : 0, @@ -253,6 +254,7 @@ const ContextMenu: React.FC<{ { setOpened(false); + // @ts-ignore navigation.navigate("ServiceSelector"); }} style={({ pressed }) => [ @@ -297,6 +299,7 @@ const ContextMenu: React.FC<{ { setOpened(false); + // @ts-ignore navigation.navigate("SettingStack"); }} style={({ pressed }) => [ diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index dd359cb02..2f7c9dab0 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -151,6 +151,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { { borderColor: colors.text + "20", backgroundColor: colors.text + "06", + // @ts-expect-error flexDirection: alert.actions?.length > 2 ? "column" : "row", alignItems: "center", }, @@ -170,6 +171,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { key={title} layout={anim2Papillon(LinearTransition)} style={[ + // @ts-expect-error alert.actions?.length === 1 || alert.actions?.length > 2 ? styles.singleButtonContainer : null, @@ -184,6 +186,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { }} contentContainerStyle={{ borderRadius: 300, overflow: "hidden" }} style={[ + // @ts-expect-error alert.actions?.length === 1 || alert.actions?.length > 2 ? styles.singleButtonContainer : null, @@ -200,6 +203,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { borderRadius: 300, overflow: "hidden", }, + // @ts-expect-error alert.actions?.length === 1 || alert.actions?.length > 2 ? styles.singleButton : null, diff --git a/src/router/index.tsx b/src/router/index.tsx index 7fcfcc164..2ededea63 100644 --- a/src/router/index.tsx +++ b/src/router/index.tsx @@ -100,6 +100,7 @@ const Router: React.FC = () => { let view = state; while (view?.routes) { str += "/" + view.routes[view.index].name; + // @ts-ignore view = view.routes[view.index].state; } navigate(str); @@ -108,6 +109,7 @@ const Router: React.FC = () => { {screens.map((screen) => ( + // @ts-ignore ))} diff --git a/src/views/account/Home/Home.tsx b/src/views/account/Home/Home.tsx index 496c2e253..e13e26171 100644 --- a/src/views/account/Home/Home.tsx +++ b/src/views/account/Home/Home.tsx @@ -86,7 +86,7 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { checkAccounts(); - const handleUrl = (event) => { + const handleUrl = (event: any) => { manageIzlyLogin(event.url); }; diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index 1b5c1f11c..d1fd16889 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -283,7 +283,8 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { label: "Soutenir Papillon", onPress: () => openUrl("https://papillon.bzh/donate"), android: true, - description: "" + description: "", + disabled: false, }); } @@ -310,6 +311,7 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { }, 10); }, description: "", + disabled: false, }); } @@ -426,6 +428,7 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { /> } trailing={ + // @ts-expect-error : on ignore la condition subtab.beta && ( Date: Wed, 19 Mar 2025 16:49:11 +0100 Subject: [PATCH 0936/1144] =?UTF-8?q?fix:=20verrouillage=20de=20l'orientat?= =?UTF-8?q?ion=20de=20l'=C3=A9cran=20sur=20t=C3=A9l=C3=A9phone?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.tsx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/App.tsx b/App.tsx index 49e1e4a86..d236fb4b7 100644 --- a/App.tsx +++ b/App.tsx @@ -43,11 +43,16 @@ export default function App () { useEffect(() => { const configureOrientation = async () => { - const deviceType = await Device.getDeviceTypeAsync(); - if (deviceType === Device.DeviceType.TABLET) { - await ScreenOrientation.unlockAsync(); - } else { - await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT); + try { + const deviceType = await Device.getDeviceTypeAsync(); + if (deviceType === Device.DeviceType.TABLET) { + await ScreenOrientation.unlockAsync(); + } else { + await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT_UP); + } + } catch (error) { + log(`Error during orientation lock: ${error}`, "Orientation/App"); + await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT_UP); } }; From 88340915fc21062c0c35bfb92b3ff3ea4c22c45a Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 19 Mar 2025 16:51:52 +0100 Subject: [PATCH 0937/1144] refractor: ajout de majuscules --- src/utils/format/format_date_complets.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/utils/format/format_date_complets.ts b/src/utils/format/format_date_complets.ts index 4fead6c64..7ca75c46c 100644 --- a/src/utils/format/format_date_complets.ts +++ b/src/utils/format/format_date_complets.ts @@ -10,16 +10,16 @@ function formatDate (date: string): string { let formattedDate = formatDistanceToNow(messageDate, { addSuffix: true, locale: fr }); if (isYesterday(messageDate)) { - return "hier"; + return "Hier"; } if (isToday(messageDate)) { - return "aujourd’hui"; - } + return "Aujourd’hui"; + } - if (isTomorrow(messageDate)) { - return "demain"; - } + if (isTomorrow(messageDate)) { + return "Demain"; + } return formattedDate; } From 1d8ab41caed62a6b27e1a7b376c2f12b85d212ee Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 19 Mar 2025 16:55:00 +0100 Subject: [PATCH 0938/1144] fix: add `isToday` and `isTomorrow` in DateHelper --- src/utils/format/DateHelper.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/utils/format/DateHelper.ts b/src/utils/format/DateHelper.ts index c3cc216d0..6bb2b3321 100644 --- a/src/utils/format/DateHelper.ts +++ b/src/utils/format/DateHelper.ts @@ -1,10 +1,19 @@ -import { formatDistance } from "date-fns"; +import { formatDistance, isToday, isTomorrow } from "date-fns"; import { fr } from "date-fns/locale"; export const timestampToString = (timestamp: number) => { if (isNaN(timestamp)) { return; } + + if (isToday(timestamp)) { + return "Aujourd’hui"; + } + + if (isTomorrow(timestamp)) { + return "Demain"; + } + return formatDistance(new Date(timestamp), new Date(), { locale: fr, addSuffix: true, From ec7c592f5ee6042cebb5668690cba4ba47d0b1d0 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 19 Mar 2025 16:57:03 +0100 Subject: [PATCH 0939/1144] fix: format date to 00:00:00:000 to a best difference --- src/utils/format/DateHelper.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/utils/format/DateHelper.ts b/src/utils/format/DateHelper.ts index 6bb2b3321..56c9f017e 100644 --- a/src/utils/format/DateHelper.ts +++ b/src/utils/format/DateHelper.ts @@ -14,7 +14,10 @@ export const timestampToString = (timestamp: number) => { return "Demain"; } - return formatDistance(new Date(timestamp), new Date(), { + const mtn = new Date(); + mtn.setHours(0, 0, 0, 0); + + return formatDistance(new Date(timestamp), mtn, { locale: fr, addSuffix: true, }); From fab794a9fcc21675968768d320cb8a9198ad0048 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 19 Mar 2025 17:03:56 +0100 Subject: [PATCH 0940/1144] feat: show homeworks not due for today --- src/views/account/Home/Elements/HomeworksElement.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index b37e6c0d8..1b71ebf29 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -69,7 +69,10 @@ const HomeworksElement: React.FC = ({ navigation, onImpor [account, updateHomeworks] ); - const startTime = Date.now() / 1000; + const mtn = new Date(); + mtn.setHours(0, 0, 0, 0); + + const startTime = mtn.getTime() / 1000; const endTime = startTime + 7 * 24 * 60 * 60 * 1000; const hwSemaineActuelle = homeworks[dateToEpochWeekNumber(actualDay)]?.filter( From 2d83d97fc522fe6a434267f6aaa64421a08cf4f9 Mon Sep 17 00:00:00 2001 From: Mikkel ALMONTE--RINGAUD Date: Wed, 19 Mar 2025 17:07:22 +0100 Subject: [PATCH 0941/1144] =?UTF-8?q?fix(ecoledirecte):=20bump=20version?= =?UTF-8?q?=20pour=20r=C3=A9parer=20les=20changements=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 8 ++++---- package.json | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index af2ad4291..24959ff03 100644 --- a/package-lock.json +++ b/package-lock.json @@ -74,7 +74,7 @@ "lucide-react-native": "^0.378.0", "notifee": "^0.0.1", "openid-client": "^5.7.0", - "pawdirecte": "^1.7.1", + "pawdirecte": "^1.8.1", "pawnilim": "^0.2.0", "pawnote": "^1.4.1", "pawrd": "^0.6.1", @@ -15086,9 +15086,9 @@ } }, "node_modules/pawdirecte": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/pawdirecte/-/pawdirecte-1.8.0.tgz", - "integrity": "sha512-DXohBS59vWyZS+MWwaV9dPFTVJU3Qo2LBrD9OcnOMvrSeHvwIhp32xT8xVjAa8EWV6iITMaopsTv81iCFO+z5g==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/pawdirecte/-/pawdirecte-1.8.1.tgz", + "integrity": "sha512-m8sJ5FhcQ2Ht1/25TiAZT3pKuyoNv3BV3rHpgTqHUB38izEzhgepp9c/cAh1rVF6b2Tz44JdGut6CrcpV41oUQ==", "license": "GPL-3.0-or-later", "dependencies": { "@literate.ink/utilities": "1.0.0-11713907881.1", diff --git a/package.json b/package.json index c7f1ebe8b..02430a581 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "expo-manifests": "^0.14.3", "expo-media-library": "~16.0.4", "expo-navigation-bar": "~3.0.7", + "expo-screen-orientation": "~7.0.5", "expo-sensors": "~13.0.9", "expo-sharing": "~12.0.1", "expo-splash-screen": "~0.27.6", @@ -75,7 +76,7 @@ "lucide-react-native": "^0.378.0", "notifee": "^0.0.1", "openid-client": "^5.7.0", - "pawdirecte": "^1.7.1", + "pawdirecte": "^1.8.1", "pawnilim": "^0.2.0", "pawnote": "^1.4.1", "pawrd": "^0.6.1", @@ -107,8 +108,7 @@ "swiftui-react-native": "^6.3.3", "text-encoding": "^0.7.0", "turboself-api": "^2.1.8", - "zustand": "^4.5.2", - "expo-screen-orientation": "~7.0.5" + "zustand": "^4.5.2" }, "devDependencies": { "@babel/core": "^7.20.0", From 149282e71b2d4962c9dd24d63cf4c0a8014ce518 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 19 Mar 2025 17:16:56 +0100 Subject: [PATCH 0942/1144] =?UTF-8?q?feat:=20am=C3=A9lioration=20de=20l'af?= =?UTF-8?q?fichage=20pour=20les=20t=C3=A9l=C3=A9phones=20et=20tablettes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Grades/Modals/GradeReaction.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/views/account/Grades/Modals/GradeReaction.tsx b/src/views/account/Grades/Modals/GradeReaction.tsx index 9b9520be2..bbe023a03 100644 --- a/src/views/account/Grades/Modals/GradeReaction.tsx +++ b/src/views/account/Grades/Modals/GradeReaction.tsx @@ -15,6 +15,7 @@ import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { isExpoGo } from "@/utils/native/expoGoAlert"; import { useAlert } from "@/providers/AlertProvider"; import { Grade } from "@/services/shared/Grade"; +import useScreenDimensions from "@/hooks/useScreenDimensions"; // Types interface SubjectData { @@ -90,6 +91,7 @@ const GradeReaction: Screen<"GradeReaction"> = ({ navigation, route }) => { } }; + const { isTablet } = useScreenDimensions(); const { showAlert } = useAlert(); // Setup permissions @@ -201,7 +203,14 @@ const GradeReaction: Screen<"GradeReaction"> = ({ navigation, route }) => { : ( - + Date: Wed, 19 Mar 2025 17:25:30 +0100 Subject: [PATCH 0943/1144] refractor: bump Papillon to the latest version --- .github/ISSUE_TEMPLATE/bug.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 49004ae35..5f3902e3b 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -61,7 +61,7 @@ body: attributes: label: Version utilisée description: Paramètres (de Papillon) -> Version affichée en bas de la page - placeholder: "7.10.3" + placeholder: "7.10.4" validations: required: true From 7a4ea21a0bc87f669d2ca94e33f965d63eb83c5f Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 19 Mar 2025 17:26:08 +0100 Subject: [PATCH 0944/1144] =?UTF-8?q?fix:=20retour=20=C3=A0=20la=20ligne?= =?UTF-8?q?=20en=20trop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/PULL_REQUEST_TEMPLATE.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index b9231528f..a5e4da0d3 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -14,7 +14,6 @@ Tu t'assures avoir respecté les points suivants (_en rajoutant un `x` dans les - [ ] ❌ Je n'ai pas fait **d'erreurs TypeScript et ESLint** dans le code - [ ] 📝 J'ai fait **une description des changement effectués** ci-dessous - ## Résumé des changements effectués _Les modifications_ @@ -34,4 +33,3 @@ _Le(s) capture(s) d'écran_ _S'il y en a plusieurs, continuer à les lister_ - Closed #(_le numéro de l'issue_) - From cd55e80d91cc3cb82e0aaed5e0df5be56cb77dff Mon Sep 17 00:00:00 2001 From: Tryon Date: Wed, 19 Mar 2025 18:18:51 +0100 Subject: [PATCH 0945/1144] =?UTF-8?q?fix:=20suppression=20des=20ic=C3=B4ne?= =?UTF-8?q?s=20MapPin=20dans=20les=20composants=20TimetableItem=20et=20Nex?= =?UTF-8?q?tCourse=20(?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Lessons/Atoms/Item.tsx | 3 --- src/widgets/Components/NextCourse.tsx | 4 +--- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/views/account/Lessons/Atoms/Item.tsx b/src/views/account/Lessons/Atoms/Item.tsx index f983f30c6..337fca0ad 100644 --- a/src/views/account/Lessons/Atoms/Item.tsx +++ b/src/views/account/Lessons/Atoms/Item.tsx @@ -18,7 +18,6 @@ import NativeTouchable from "@/components/Global/NativeTouchable"; import { getSubjectData } from "@/services/shared/Subject"; import { animPapillon } from "@/utils/ui/animations"; import { getDuration } from "@/utils/format/course_duration"; -import { MapPin } from "lucide-react-native"; export const TimetableItem: React.FC<{ item: TimetableClass @@ -78,8 +77,6 @@ export const TimetableItem: React.FC<{ )} - - - {nextCourse.room From 01c933d28c452125fb634ad1a37c7668ce0312d9 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Wed, 19 Mar 2025 21:21:25 +0100 Subject: [PATCH 0946/1144] chore(lint) --- .vscode/settings.json | 8 +- App.tsx | 5 +- eslint.config.js | 6 +- plugins/notifee-mod.js | 2 +- src/addons/addons.ts | 2 +- src/components/Addons/AddonsWebview.tsx | 2 +- src/components/Global/NativeTouchable.tsx | 2 +- src/components/Global/PapillonHeader.tsx | 3 +- src/components/Home/Header.tsx | 6 +- .../ExternalServicesContainerCard.tsx | 2 +- .../Settings/IconsContainerCard.tsx | 2 +- .../Settings/MagicContainerCard.tsx | 2 +- .../Settings/MultiServiceContainerCard.tsx | 2 +- src/components/Settings/ReelGallery.tsx | 2 +- src/router/helpers/protected-screen.tsx | 2 +- src/router/navigator/atoms/MenuItem.tsx | 2 +- src/router/navigator/atoms/TabItem.tsx | 2 +- src/router/screens/account/home.tsx | 8 +- src/router/screens/settings/navigator.tsx | 2 +- src/services/ard/balance.ts | 2 +- src/services/attendance.ts | 6 +- .../ecoledirecte/default-personalization.ts | 2 +- src/services/ecoledirecte/time-interval.ts | 6 +- src/services/evaluation.ts | 4 +- src/services/grades.ts | 8 +- src/services/homework.ts | 2 +- src/services/iutlan/grades.ts | 10 +- src/services/local/default-personalization.ts | 2 +- src/services/local/ical.ts | 2 +- .../local/utils/reduceIcalToCourse.ts | 2 +- src/services/multi/default-personalization.ts | 2 +- src/services/news.ts | 2 +- src/services/pronote/attendance.ts | 10 +- src/services/pronote/chats.ts | 10 +- .../pronote/default-personalization.ts | 2 +- src/services/pronote/evaluations.ts | 6 +- src/services/pronote/news.ts | 2 +- src/services/skolengo/data/attendance.ts | 8 +- src/services/skolengo/data/grades.ts | 10 +- src/services/skolengo/data/period.ts | 2 +- src/services/skolengo/data/timetable.ts | 6 +- src/services/skolengo/data/utils.ts | 2 +- .../skolengo/default-personalization.ts | 4 +- src/services/skolengo/reload-skolengo.ts | 2 +- src/services/skolengo/skolengo-account.tsx | 10 +- src/services/timetable.ts | 2 +- src/stores/account/index.ts | 8 +- src/utils/epochWeekNumber.ts | 6 +- src/utils/external/download-as-base64.ts | 2 +- src/utils/format/course_duration.ts | 2 +- src/utils/format/format_cours_name.ts | 2 +- src/utils/format/format_pronote_homeworks.ts | 2 +- src/utils/magic/categorizeHomeworks.ts | 2 +- src/utils/multiservice/index.ts | 2 +- src/views/account/Attendance/Attendance.tsx | 18 +- src/views/account/Chat/Modals/ChatDetails.tsx | 4 +- src/views/account/Chat/Modals/ChatThemes.tsx | 2 +- src/views/account/Grades/Document.tsx | 2 +- src/views/account/Grades/Grades.tsx | 2 +- .../account/Grades/Graph/GradesAverage.tsx | 6 +- .../account/Grades/Subject/SubjectTitle.tsx | 3 +- .../account/Home/Elements/GradesElement.tsx | 2 +- .../Home/Elements/HomeworksElement.tsx | 8 +- .../Home/Elements/PopupRestauration.tsx | 4 +- src/views/account/Home/Home.tsx | 2 +- src/views/account/Home/ModalContent.tsx | 3 +- src/views/account/Homeworks/Homeworks.old.tsx | 249 --------- src/views/account/Homeworks/Homeworks.tsx | 20 +- .../Lessons/Atoms/LessonsDatePicker.tsx | 2 +- src/views/account/Lessons/Document.tsx | 2 +- src/views/account/Lessons/Lessons.tsx | 4 +- .../Lessons/Options/LessonsImportIcal.tsx | 10 +- src/views/account/Restaurant/Menu.tsx | 10 +- .../account/Restaurant/Modals/CardDetail.tsx | 4 +- .../account/Restaurant/Modals/QrCode.tsx | 4 +- src/views/account/Week/Week.tsx | 6 +- src/views/addon/AddonLogs.tsx | 2 +- src/views/addon/AddonPage.tsx | 5 +- .../BackgroundIdentityProvider.tsx | 8 +- .../actions/BackgroundIUTLannion.tsx | 30 +- .../IdentityProvider/providers/Multi.tsx | 4 +- .../providers/UnivLimoges.tsx | 4 +- .../providers/UnivRennes1.tsx | 4 +- .../providers/UnivRennes2.tsx | 4 +- .../providers/UnivSorbonneParisNord.tsx | 4 +- .../ecoledirecte/EcoleDirecteCredentials.tsx | 4 +- .../login/pronote/PronoteCredentials.tsx | 4 +- .../login/pronote/PronoteManualLocation.tsx | 2 +- src/views/login/pronote/PronoteManualURL.tsx | 4 +- src/views/login/pronote/PronoteQRCode.tsx | 4 +- src/views/login/pronote/PronoteV6Import.tsx | 4 +- .../skolengo/SkolengoInstanceSelector.tsx | 2 +- src/views/login/skolengo/SkolengoWebview.tsx | 14 +- src/views/settings/ExternalAccount/ARD.tsx | 4 +- src/views/settings/ExternalAccount/Alise.tsx | 4 +- .../ExternalAccount/IzlyActivation.tsx | 4 +- .../ExternalAccount/PriceBeforeScan.tsx | 6 +- .../PriceDetectionOnboarding.tsx | 2 +- .../settings/ExternalAccount/PriceError.tsx | 2 +- .../ExternalAccount/QrcodeScanner.tsx | 2 +- .../settings/ExternalAccount/SelectMethod.tsx | 2 +- .../ExternalAccount/ServiceSelector.tsx | 2 +- .../settings/ExternalAccount/Turboself.tsx | 4 +- .../TurboselfAccountSelector.tsx | 4 +- src/views/settings/Settings.tsx | 4 +- src/views/settings/SettingsAddons.tsx | 2 +- .../settings/SettingsExternalServices.tsx | 4 +- src/views/settings/SettingsFlags.tsx | 4 +- src/views/settings/SettingsIcons.tsx | 6 +- src/views/settings/SettingsMagic.tsx | 10 +- src/views/settings/SettingsMultiService.tsx | 14 +- .../settings/SettingsMultiServiceSpace.tsx | 22 +- src/views/settings/SettingsProfile.tsx | 4 +- src/views/settings/SettingsSubjects.tsx | 24 +- src/views/settings/SettingsTabs.tsx | 2 +- src/views/welcome/AccountSelector.old.tsx | 481 ------------------ src/views/welcome/ChangelogScreen.tsx | 6 +- src/views/welcome/ColorSelector.tsx | 6 +- src/widgets/Components/LastGrade.tsx | 6 +- src/widgets/Components/NextCourse.tsx | 6 +- src/widgets/Components/RestaurantBalance.tsx | 6 +- src/widgets/Components/RestaurantQRCode.tsx | 6 +- 122 files changed, 301 insertions(+), 1032 deletions(-) delete mode 100644 src/views/account/Homeworks/Homeworks.old.tsx delete mode 100644 src/views/welcome/AccountSelector.old.tsx diff --git a/.vscode/settings.json b/.vscode/settings.json index fe0c9aafe..b92a4c2b5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,5 +11,9 @@ "android", "lint" ], - "typescript.tsdk": "node_modules/typescript/lib" -} \ No newline at end of file + "typescript.tsdk": "node_modules/typescript/lib", + "sonarlint.connectedMode.project": { + "connectionId": "gabriel29306", + "projectKey": "Gabriel29306_PapillonV7" + } +} diff --git a/App.tsx b/App.tsx index 5e7a65eee..89accdef7 100644 --- a/App.tsx +++ b/App.tsx @@ -1,3 +1,4 @@ +import "@/background/BackgroundTasks"; import Router from "@/router"; import { useFonts } from "expo-font"; import * as SplashScreen from "expo-splash-screen"; @@ -30,7 +31,7 @@ export default function App () { const [appState, setAppState] = useState(AppState.currentState); const currentAccount = useCurrentAccount((store) => store.account); const switchTo = useCurrentAccount((store) => store.switchTo); - const accounts = useAccounts((store) => store.accounts).filter(account => !account.isExternal); + const accounts = useAccounts((store) => store.accounts).filter((account) => !account.isExternal); const [fontsLoaded] = useFonts({ light: require("./assets/fonts/FixelText-Light.ttf"), @@ -111,7 +112,7 @@ export default function App () { break; } } - await new Promise(resolve => setTimeout(resolve, 1000)); + await new Promise((resolve) => setTimeout(resolve, 1000)); } await AsyncStorage.removeItem("@background_timestamp"); }, [currentAccount, switchTo, getBackgroundTimeLimit]); diff --git a/eslint.config.js b/eslint.config.js index 6c87a13c2..9918a2d5b 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -43,8 +43,12 @@ module.exports = [ "@stylistic/space-before-function-paren": ["error", "always"], "@stylistic/no-extra-semi": "error", "@stylistic/no-mixed-spaces-and-tabs": "error", - "unused-imports/no-unused-imports": "error", "@stylistic/object-curly-spacing": ["error", "always"], + "@stylistic/keyword-spacing": ["error", { + "before": true + }], + "@stylistic/arrow-parens": "error", + "unused-imports/no-unused-imports": "error", "no-unused-vars": ["error", { "args": "none" }], diff --git a/plugins/notifee-mod.js b/plugins/notifee-mod.js index 92c5db33f..f6c16dcc9 100644 --- a/plugins/notifee-mod.js +++ b/plugins/notifee-mod.js @@ -1,7 +1,7 @@ const { withProjectBuildGradle } = require("expo/config-plugins"); module.exports = function withNotifeeRepo (config) { - return withProjectBuildGradle(config, async config => { + return withProjectBuildGradle(config, async (config) => { const contents = config.modResults.contents; if (!contents.includes("@notifee/react-native")) { diff --git a/src/addons/addons.ts b/src/addons/addons.ts index d8b67860d..528b04bf0 100644 --- a/src/addons/addons.ts +++ b/src/addons/addons.ts @@ -170,7 +170,7 @@ async function get_addons_list (): Promise { continue; } - manifest.placement.forEach(placement => { + manifest.placement.forEach((placement) => { placement.main = FileSystem.documentDirectory + "addons/" + addon + "/" + placement.main; }); diff --git a/src/components/Addons/AddonsWebview.tsx b/src/components/Addons/AddonsWebview.tsx index 80931262b..6640b25c3 100644 --- a/src/components/Addons/AddonsWebview.tsx +++ b/src/components/Addons/AddonsWebview.tsx @@ -228,7 +228,7 @@ const AddonsWebview: React.FC = ({ } // CONSOLE.LOG - if(["log", "error", "warn", "info"].indexOf(data.type) !== -1){ + if (["log", "error", "warn", "info"].indexOf(data.type) !== -1){ let type = data.type; let str_type = String(type[0]).toUpperCase() + String(type).slice(1); log(`[ADDON][${addon.name}] ${str_type} : ${data.message}`, "AddonsWebview"); diff --git a/src/components/Global/NativeTouchable.tsx b/src/components/Global/NativeTouchable.tsx index 007c997ff..41d9fc814 100644 --- a/src/components/Global/NativeTouchable.tsx +++ b/src/components/Global/NativeTouchable.tsx @@ -12,7 +12,7 @@ const NativeTouchable: React.FC = ({ contentContainerStyle, ...props }) => { - if(Platform.OS === "android") { + if (Platform.OS === "android") { return ( diff --git a/src/components/Global/PapillonHeader.tsx b/src/components/Global/PapillonHeader.tsx index 2d09a0033..6f123b10b 100644 --- a/src/components/Global/PapillonHeader.tsx +++ b/src/components/Global/PapillonHeader.tsx @@ -1,7 +1,6 @@ import React from "react"; -import { Platform, View } from "react-native"; +import { Platform, View, TouchableOpacity } from "react-native"; import { TabAnimatedTitleLeft, TabAnimatedTitleRight } from "./TabAnimatedTitle"; -import { TouchableOpacity } from "react-native-gesture-handler"; import { ArrowLeft } from "lucide-react-native"; import { type RouteProp, useTheme } from "@react-navigation/native"; import type { RouteParameters } from "@/router/helpers/types"; diff --git a/src/components/Home/Header.tsx b/src/components/Home/Header.tsx index e78efc213..4134c8394 100644 --- a/src/components/Home/Header.tsx +++ b/src/components/Home/Header.tsx @@ -33,7 +33,7 @@ const Header: React.FC<{ scrolled, navigation, }) => { - const account = useCurrentAccount(store => store.account!); + const account = useCurrentAccount((store) => store.account!); const [tabs, setTabs] = useState([ { name: "Attendance", enabled: true }, { name: "Discussions", enabled: true }, @@ -82,7 +82,7 @@ const Header: React.FC<{ /> {!isTablet && ( - tabs.filter(tab => !tab.enabled).length === 0 ? + tabs.filter((tab) => !tab.enabled).length === 0 ? {tabs.map((tab, index) => { if (tab.name === "Home") return null; - const defaultTab = defaultTabs.find(curr => curr.tab === tab.name); + const defaultTab = defaultTabs.find((curr) => curr.tab === tab.name); if (tab.enabled) return null; if (!defaultTab) return null; diff --git a/src/components/Settings/ExternalServicesContainerCard.tsx b/src/components/Settings/ExternalServicesContainerCard.tsx index 2e186f369..bceec8d4f 100644 --- a/src/components/Settings/ExternalServicesContainerCard.tsx +++ b/src/components/Settings/ExternalServicesContainerCard.tsx @@ -4,7 +4,7 @@ import { View } from "react-native"; import LottieView from "lottie-react-native"; import { NativeItem, NativeList, NativeText } from "../Global/NativeComponents"; -const ExternalServicesContainerCard = ({ theme }: { theme: any }) => { +const ExternalServicesContainerCard = () => { return ( diff --git a/src/components/Settings/IconsContainerCard.tsx b/src/components/Settings/IconsContainerCard.tsx index d16cf5ebc..1017871d2 100644 --- a/src/components/Settings/IconsContainerCard.tsx +++ b/src/components/Settings/IconsContainerCard.tsx @@ -4,7 +4,7 @@ import { Image, View } from "react-native"; import LottieView from "lottie-react-native"; import { NativeItem, NativeList, NativeText } from "../Global/NativeComponents"; -const IconsContainerCard = ({ theme }: { theme: any }) => { +const IconsContainerCard = () => { const animationref = React.useRef(null); useEffect(() => { diff --git a/src/components/Settings/MagicContainerCard.tsx b/src/components/Settings/MagicContainerCard.tsx index 679570676..ccebfe554 100644 --- a/src/components/Settings/MagicContainerCard.tsx +++ b/src/components/Settings/MagicContainerCard.tsx @@ -6,7 +6,7 @@ import { NativeItem, NativeList, NativeText } from "../Global/NativeComponents"; import { LinearGradient } from "expo-linear-gradient"; import BetaIndicator from "../News/Beta"; -const MagicContainerCard = ({ theme }: { theme: any }) => { +const MagicContainerCard = () => { const animationref = React.useRef(null); useEffect(() => { diff --git a/src/components/Settings/MultiServiceContainerCard.tsx b/src/components/Settings/MultiServiceContainerCard.tsx index 365f33dce..d8668c753 100644 --- a/src/components/Settings/MultiServiceContainerCard.tsx +++ b/src/components/Settings/MultiServiceContainerCard.tsx @@ -6,7 +6,7 @@ import { NativeItem, NativeList, NativeText } from "../Global/NativeComponents"; import { LinearGradient } from "expo-linear-gradient"; import BetaIndicator from "../News/Beta"; -const MultiServiceContainerCard = ({ theme }: { theme: any }) => { +const MultiServiceContainerCard = () => { const animationref = React.useRef(null); useEffect(() => { diff --git a/src/components/Settings/ReelGallery.tsx b/src/components/Settings/ReelGallery.tsx index 696c9023b..2db7a651a 100644 --- a/src/components/Settings/ReelGallery.tsx +++ b/src/components/Settings/ReelGallery.tsx @@ -97,7 +97,7 @@ const ReelGallery = ({ reels }: ReelGalleryProps) => { const numColumns = 2; // Vérification des reels valides - const validReels = reels.filter(reel => + const validReels = reels.filter((reel) => reel && typeof reel.id === "string" && reel.grade && diff --git a/src/router/helpers/protected-screen.tsx b/src/router/helpers/protected-screen.tsx index ff96ece37..e9868ff00 100644 --- a/src/router/helpers/protected-screen.tsx +++ b/src/router/helpers/protected-screen.tsx @@ -9,7 +9,7 @@ const ProtectedScreen: React.FC<{ navigation: NativeStackNavigationProp; children: React.JSX.Element; }> = ({ navigation, children }) => { - const account = useCurrentAccount(store => store.account); + const account = useCurrentAccount((store) => store.account); useEffect(() => { if (account === null) navigation.reset({ index: 0, routes: [{ name: "AccountSelector" }] }); diff --git a/src/router/navigator/atoms/MenuItem.tsx b/src/router/navigator/atoms/MenuItem.tsx index 8ba30f4c7..36495496a 100644 --- a/src/router/navigator/atoms/MenuItem.tsx +++ b/src/router/navigator/atoms/MenuItem.tsx @@ -46,7 +46,7 @@ const MenuItem: React.FC<{ const lottieRef = React.useRef(null); - const autoColor = colorsList.filter(c => c.hex.primary === theme.colors.primary)[0] || colorsList[0]; + const autoColor = colorsList.filter((c) => c.hex.primary === theme.colors.primary)[0] || colorsList[0]; const tabColor = isFocused ? (autoColor?.hex?.lighter ? (theme.dark ? autoColor?.hex?.lighter : autoColor.hex.dark) : theme.colors.primary) : (theme.dark ? "#656c72" : "#8C9398"); diff --git a/src/router/navigator/atoms/TabItem.tsx b/src/router/navigator/atoms/TabItem.tsx index 64380610e..15869abdc 100644 --- a/src/router/navigator/atoms/TabItem.tsx +++ b/src/router/navigator/atoms/TabItem.tsx @@ -51,7 +51,7 @@ const TabItem: React.FC<{ const lottieRef = React.useRef(null); const autoColor = React.useMemo(() => { - return colorsList.find(c => c.hex.primary === theme.colors.primary) || colorsList[0]; + return colorsList.find((c) => c.hex.primary === theme.colors.primary) || colorsList[0]; }, [theme.colors.primary]); const tabColor = React.useMemo(() => { diff --git a/src/router/screens/account/home.tsx b/src/router/screens/account/home.tsx index 1a5e21fce..90d3839cc 100644 --- a/src/router/screens/account/home.tsx +++ b/src/router/screens/account/home.tsx @@ -24,7 +24,7 @@ import { useEffect } from "react"; const HomeStackScreen = ({ accountScreens }: { accountScreens: Array> }) => { - const account = useCurrentAccount(store => store.account); + const account = useCurrentAccount((store) => store.account); let newAccountScreens = accountScreens; useEffect(() => { @@ -33,10 +33,10 @@ const HomeStackScreen = ({ accountScreens }: { if (account?.personalization.tabs) { let newTabs = account.personalization.tabs; - newTabs = newTabs.filter(tab => !tab.enabled); + newTabs = newTabs.filter((tab) => !tab.enabled); - newAccountScreens = newTabs.map(tab => { - const tabData = accountScreens.find(t => t.name === tab.name); + newAccountScreens = newTabs.map((tab) => { + const tabData = accountScreens.find((t) => t.name === tab.name); if (tabData) { tabData.options = { ...tabData.options, diff --git a/src/router/screens/settings/navigator.tsx b/src/router/screens/settings/navigator.tsx index e58cf8ca2..22c9d4500 100644 --- a/src/router/screens/settings/navigator.tsx +++ b/src/router/screens/settings/navigator.tsx @@ -24,7 +24,7 @@ export const SettingsScreen: Screen<"SettingStack"> = ({ route }) => { }; const ConditionnalAlertProvider = (props: any) => { - if(Platform.OS === "android") { + if (Platform.OS === "android") { return ( {props.children} diff --git a/src/services/ard/balance.ts b/src/services/ard/balance.ts index d08fab0ac..5d716b15a 100644 --- a/src/services/ard/balance.ts +++ b/src/services/ard/balance.ts @@ -7,7 +7,7 @@ export const balance = async (account: ARDAccount): Promise => { const payments = account.authentication.balances; const mealPrice = account.authentication.mealPrice; - return payments.walletData.map(wallet => ({ + return payments.walletData.map((wallet) => ({ amount: wallet.walletAmount / 100, currency: "€", remaining: wallet.walletName.toLowerCase() !== "cafetaria" ? Math.floor((wallet.walletAmount / mealPrice!)) : null, diff --git a/src/services/attendance.ts b/src/services/attendance.ts index 6bde11b5c..a65f92d49 100644 --- a/src/services/attendance.ts +++ b/src/services/attendance.ts @@ -61,8 +61,8 @@ export async function updateAttendancePeriodsInCache (accoun periods = [ { name: "Toutes", - startTimestamp: Math.min(...output.map(e=>e.startTimestamp)), - endTimestamp: Math.max(...output.map(e=>e.endTimestamp)), + startTimestamp: Math.min(...output.map((e)=>e.startTimestamp)), + endTimestamp: Math.max(...output.map((e)=>e.endTimestamp)), }, ]; @@ -124,7 +124,7 @@ export async function updateAttendanceInCache (account: T, p break; } case AccountService.Skolengo: { - if(!checkIfSkoSupported(account, "Attendance")) { + if (!checkIfSkoSupported(account, "Attendance")) { error("[updateAttendanceInCache]: This Skolengo instance doesn't support Homeworks.", "skolengo"); break; } diff --git a/src/services/ecoledirecte/default-personalization.ts b/src/services/ecoledirecte/default-personalization.ts index d2878085f..2532d8144 100644 --- a/src/services/ecoledirecte/default-personalization.ts +++ b/src/services/ecoledirecte/default-personalization.ts @@ -26,7 +26,7 @@ export default async function defaultPersonalization (account: Account): Promise Pragma: "no-cache" }) : void 0, - tabs: defaultTabs.filter(current => defaultEDTabs.includes(current.tab)).map((tab, index) => ({ + tabs: defaultTabs.filter((current) => defaultEDTabs.includes(current.tab)).map((tab, index) => ({ name: tab.tab, enabled: index <= 4 })) diff --git a/src/services/ecoledirecte/time-interval.ts b/src/services/ecoledirecte/time-interval.ts index e5a0210b4..b03ca2fdd 100644 --- a/src/services/ecoledirecte/time-interval.ts +++ b/src/services/ecoledirecte/time-interval.ts @@ -10,9 +10,9 @@ const months = ["janvier", "février", "mars", "avril", "mai", "juin", "juillet" */ export function dateAsISO860 (str: string): string { const parts = str.split(" "); - const timeIndex = parts.findIndex(part => part.includes(":")); + const timeIndex = parts.findIndex((part) => part.includes(":")); const hour = parts[timeIndex].split(":"); - const monthIndex = parts.findIndex(part => months.includes(part)); + const monthIndex = parts.findIndex((part) => months.includes(part)); return (new Date( Number(parts[monthIndex + 1]), @@ -32,7 +32,7 @@ export function dateStringAsTimeInterval ( * str is equal to "du mercredi 21 février 2024 au jeudi 22 février 2024" * or "du mercredi 27 novembre 2024 à 08:10 au vendredi 06 décembre 2024 à 08:10" */ - const [startPart, endPart] = str.split("au").map(part => part.trim()); + const [startPart, endPart] = str.split("au").map((part) => part.trim()); let start = startPart.replace("du", "").trim(); let end = endPart.trim(); diff --git a/src/services/evaluation.ts b/src/services/evaluation.ts index 8008ae805..bde7b56aa 100644 --- a/src/services/evaluation.ts +++ b/src/services/evaluation.ts @@ -37,8 +37,8 @@ export async function updateEvaluationPeriodsInCache (accoun default: throw new Error("Service not implemented"); } - if(periods.length === 0) return; - if(!defaultPeriod) defaultPeriod = getDefaultPeriod(periods); + if (periods.length === 0) return; + if (!defaultPeriod) defaultPeriod = getDefaultPeriod(periods); useEvaluationStore.getState().updatePeriods(periods, defaultPeriod); } diff --git a/src/services/grades.ts b/src/services/grades.ts index 1ce69c587..30c0319da 100644 --- a/src/services/grades.ts +++ b/src/services/grades.ts @@ -55,7 +55,7 @@ export async function updateGradesPeriodsInCache (account: T } } case AccountService.Skolengo: { - if(!checkIfSkoSupported(account, "Grades")) { + if (!checkIfSkoSupported(account, "Grades")) { error("[updateGradesPeriodsInCache]: This Skolengo instance doesn't support Grades.", "skolengo"); break; } @@ -77,8 +77,8 @@ export async function updateGradesPeriodsInCache (account: T default: throw new Error("Service not implemented"); } - if(periods.length === 0) return; - if(!defaultPeriod) defaultPeriod = getDefaultPeriod(periods); + if (periods.length === 0) return; + if (!defaultPeriod) defaultPeriod = getDefaultPeriod(periods); useGradesStore.getState().updatePeriods(periods, defaultPeriod); } @@ -128,7 +128,7 @@ export async function updateGradesAndAveragesInCache (accoun break; } case AccountService.Skolengo: { - if(!checkIfSkoSupported(account, "Grades")) { + if (!checkIfSkoSupported(account, "Grades")) { error("[updateGradesAndAveragesInCache]: This Skolengo instance doesn't support Grades.", "skolengo"); break; } diff --git a/src/services/homework.ts b/src/services/homework.ts index 68cbd8837..d042a7d09 100644 --- a/src/services/homework.ts +++ b/src/services/homework.ts @@ -25,7 +25,7 @@ export async function updateHomeworkForWeekInCache (account: break; } case AccountService.Skolengo: { - if(!checkIfSkoSupported(account, "Homeworks")) { + if (!checkIfSkoSupported(account, "Homeworks")) { error("[updateHomeworkForWeekInCache]: This Skolengo instance doesn't support Homeworks.", "skolengo"); break; } diff --git a/src/services/iutlan/grades.ts b/src/services/iutlan/grades.ts index 2429ca3a2..681bf9f8c 100644 --- a/src/services/iutlan/grades.ts +++ b/src/services/iutlan/grades.ts @@ -125,12 +125,12 @@ export const saveIUTLanGrades = async (account: LocalAccount, periodName: string }); // - const average = grades.filter(grade => grade.student.value != null && !isNaN(grade.student.value)).length > 0? grades.filter(grade => grade.student.value != null && !isNaN(grade.student.value)).reduce((acc, grade) => acc + (grade.student.value as number), 0) / grades.filter(grade => grade.student.value != null && !isNaN(grade.student.value)).length: NaN; + const average = grades.filter((grade) => grade.student.value != null && !isNaN(grade.student.value)).length > 0? grades.filter((grade) => grade.student.value != null && !isNaN(grade.student.value)).reduce((acc, grade) => acc + (grade.student.value as number), 0) / grades.filter((grade) => grade.student.value != null && !isNaN(grade.student.value)).length: NaN; - const min = grades.filter(grade => grade.min.value != null && !isNaN(grade.min.value)).length > 0 ?grades.filter(grade => grade.min.value != null && !isNaN(grade.min.value)).reduce((acc, grade) => Math.min(acc, (grade.min.value as number)), 20): NaN; - const max = grades.filter(grade => grade.max.value != null && !isNaN(grade.max.value)).length > 0 ? grades.filter(grade => grade.max.value != null && !isNaN(grade.max.value)).reduce((acc, grade) => Math.max(acc, (grade.max.value as number)), 0): NaN; + const min = grades.filter((grade) => grade.min.value != null && !isNaN(grade.min.value)).length > 0 ?grades.filter((grade) => grade.min.value != null && !isNaN(grade.min.value)).reduce((acc, grade) => Math.min(acc, (grade.min.value as number)), 20): NaN; + const max = grades.filter((grade) => grade.max.value != null && !isNaN(grade.max.value)).length > 0 ? grades.filter((grade) => grade.max.value != null && !isNaN(grade.max.value)).reduce((acc, grade) => Math.max(acc, (grade.max.value as number)), 0): NaN; - const classAverage = grades.filter(grades => grades.average.value != null && !isNaN(grades.average.value)).length > 0 ? grades.filter(grades => grades.average.value != null && !isNaN(grades.average.value)).reduce((acc, grade) => acc + (grade.average.value as number), 0) / grades.filter(grades => grades.average.value != null && !isNaN(grades.average.value)).length: NaN; + const classAverage = grades.filter((grades) => grades.average.value != null && !isNaN(grades.average.value)).length > 0 ? grades.filter((grades) => grades.average.value != null && !isNaN(grades.average.value)).reduce((acc, grade) => acc + (grade.average.value as number), 0) / grades.filter((grades) => grades.average.value != null && !isNaN(grades.average.value)).length: NaN; if (grades.length === 0) { @@ -170,7 +170,7 @@ export const saveIUTLanGrades = async (account: LocalAccount, periodName: string return { grades: gradesList, averages: averages }; } - catch(e) { + catch (e) { error("" + (e as Error)?.stack, "saveIUTLanGrades"); return { grades: [], diff --git a/src/services/local/default-personalization.ts b/src/services/local/default-personalization.ts index 42cd54acd..673950274 100644 --- a/src/services/local/default-personalization.ts +++ b/src/services/local/default-personalization.ts @@ -20,7 +20,7 @@ export default async function defaultPersonalization (customDefaults?: Partial

defaultLocalTabs.includes(current.tab)).map((tab, index) => ({ + tabs: defaultTabs.filter((current) => defaultLocalTabs.includes(current.tab)).map((tab, index) => ({ name: tab.tab, enabled: index <= 4 })), diff --git a/src/services/local/ical.ts b/src/services/local/ical.ts index 9856fb8fa..a44115559 100644 --- a/src/services/local/ical.ts +++ b/src/services/local/ical.ts @@ -28,7 +28,7 @@ export const fetchIcalData = async ( const courses: Timetable = []; const icalURLs = account.personalization.icalURLs || []; - if(icalURLs.length === 0) { + if (icalURLs.length === 0) { return []; } diff --git a/src/services/local/utils/reduceIcalToCourse.ts b/src/services/local/utils/reduceIcalToCourse.ts index 694fa8a77..bf87293d4 100644 --- a/src/services/local/utils/reduceIcalToCourse.ts +++ b/src/services/local/utils/reduceIcalToCourse.ts @@ -3,7 +3,7 @@ import { TimetableClass } from "@/services/shared/Timetable"; function extractNames (text: string) { const pattern = /\b([A-ZÀ-Ÿ]+)\s+([A-ZÀ-Ÿ][a-zà-ÿ]+)\b/g; const matches = [...text.matchAll(pattern)]; - return matches.map(match => `${match[1]} ${match[2]}`); + return matches.map((match) => `${match[1]} ${match[2]}`); } export const reduceIcalToCourse = (course: any, identityProvider: any, url: string): TimetableClass => { diff --git a/src/services/multi/default-personalization.ts b/src/services/multi/default-personalization.ts index 7c5e3d897..4598a1a03 100644 --- a/src/services/multi/default-personalization.ts +++ b/src/services/multi/default-personalization.ts @@ -13,7 +13,7 @@ const defaultPersonalization = async (instance: MultiAccount["instance"]): Promi return { color: colors[0], magicEnabled: true, - tabs: defaultTabs.filter(current => defaultUphfTabs.includes(current.tab)).map((tab, index) => ({ + tabs: defaultTabs.filter((current) => defaultUphfTabs.includes(current.tab)).map((tab, index) => ({ name: tab.tab, enabled: index <= 4 })) diff --git a/src/services/news.ts b/src/services/news.ts index ef919e2dc..92721319b 100644 --- a/src/services/news.ts +++ b/src/services/news.ts @@ -24,7 +24,7 @@ export async function updateNewsInCache (account: T): Promis break; } case AccountService.Skolengo: { - if(!checkIfSkoSupported(account, "News")) { + if (!checkIfSkoSupported(account, "News")) { error("[updateNewsInCache]: This Skolengo instance doesn't support News.", "skolengo"); break; } diff --git a/src/services/pronote/attendance.ts b/src/services/pronote/attendance.ts index cb360f3b4..c14794ec3 100644 --- a/src/services/pronote/attendance.ts +++ b/src/services/pronote/attendance.ts @@ -31,7 +31,7 @@ export const getAttendancePeriods = (account: PronoteAccount): { periods: Period export async function getAttendance (account: PronoteAccount, periodName: string): Promise { const tab = getTab(account); // Vérifie aussi la validité de `account.instance`. - const period = tab.periods.find(p => p.name === periodName); + const period = tab.periods.find((p) => p.name === periodName); if (!period) throw new Error("La période sélectionnée n'a pas été trouvée."); @@ -39,7 +39,7 @@ export async function getAttendance (account: PronoteAccount, periodName: string info(`PRONOTE->getAttendance(): OK pour ${periodName}`, "pronote"); const attendance: Attendance = { - observations: items.observations.map(observation => { + observations: items.observations.map((observation) => { let sectionType: ObservationType; switch (observation.kind) { @@ -68,7 +68,7 @@ export async function getAttendance (account: PronoteAccount, periodName: string }; }), - punishments: items.punishments.map(punishment => ({ + punishments: items.punishments.map((punishment) => ({ id: punishment.id, schedulable: false, // TODO @@ -91,7 +91,7 @@ export async function getAttendance (account: PronoteAccount, periodName: string duration: punishment.durationMinutes })), - absences: items.absences.map(absence => ({ + absences: items.absences.map((absence) => ({ id: absence.id, fromTimestamp: absence.startDate.getTime(), toTimestamp: absence.endDate.getTime(), @@ -101,7 +101,7 @@ export async function getAttendance (account: PronoteAccount, periodName: string reasons: absence.reason })), - delays: items.delays.map(delay => ({ + delays: items.delays.map((delay) => ({ id: delay.id, timestamp: delay.date.getTime(), duration: delay.minutes, diff --git a/src/services/pronote/chats.ts b/src/services/pronote/chats.ts index da552deff..64850b205 100644 --- a/src/services/pronote/chats.ts +++ b/src/services/pronote/chats.ts @@ -19,7 +19,7 @@ export const getChats = async (account: PronoteAccount): Promise> => const parseFrenchDate = (dateText: string): Date => { const days = ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"]; const parts = dateText.split(" "); - const datePart = parts.find(part => part.includes("/")); + const datePart = parts.find((part) => part.includes("/")); if (datePart) { const [day, month, year] = datePart.split("/"); @@ -103,11 +103,11 @@ export const createDiscussionRecipients = async (account: PronoteAccount): Promi throw new ErrorServiceUnauthenticated("pronote"); const recipientsALL = await Promise.all( - account.instance!.user!.resources.flatMap(resource => + account.instance!.user!.resources.flatMap((resource) => [ pronote.EntityKind.Teacher, pronote.EntityKind.Personal - ].map(kind => pronote.newDiscussionRecipients(account.instance!, kind)) + ].map((kind) => pronote.newDiscussionRecipients(account.instance!, kind)) ) ); const recipients = recipientsALL.flat(); @@ -115,7 +115,7 @@ export const createDiscussionRecipients = async (account: PronoteAccount): Promi return recipients.map((recipient) => ({ name: recipient.name, subject: recipient.subjects.length > 0 - ? recipient.subjects.map(subject => subject.name).join(", ") + ? recipient.subjects.map((subject) => subject.name).join(", ") : undefined, _handle: recipient })); @@ -125,6 +125,6 @@ export const createDiscussion = async (account: PronoteAccount, subject: string, if (!account.instance) throw new ErrorServiceUnauthenticated("pronote"); - await pronote.newDiscussion(account.instance, subject, content, recipients.map(r => r._handle)); + await pronote.newDiscussion(account.instance, subject, content, recipients.map((r) => r._handle)); info("PRONOTE->createDiscussion(): OK", "pronote"); }; diff --git a/src/services/pronote/default-personalization.ts b/src/services/pronote/default-personalization.ts index 70ce3c1e3..6ccec048b 100644 --- a/src/services/pronote/default-personalization.ts +++ b/src/services/pronote/default-personalization.ts @@ -26,7 +26,7 @@ const defaultPersonalization = async (instance: pronote.SessionHandle): Promise< ? await downloadAsBase64(user.profilePicture.url) : void 0, - tabs: defaultTabs.filter(current => defaultPronoteTabs.includes(current.tab)).map((tab, index) => ({ + tabs: defaultTabs.filter((current) => defaultPronoteTabs.includes(current.tab)).map((tab, index) => ({ name: tab.tab, enabled: index <= 4 })) diff --git a/src/services/pronote/evaluations.ts b/src/services/pronote/evaluations.ts index 3c6e0a655..a8743062a 100644 --- a/src/services/pronote/evaluations.ts +++ b/src/services/pronote/evaluations.ts @@ -29,13 +29,13 @@ export const getEvaluationsPeriods = (account: PronoteAccount): { periods: Perio export const getEvaluations = async (account: PronoteAccount, periodName: string): Promise> => { const tab = getTab(account); // Vérifie aussi la validité de `account.instance`. - const period = tab.periods.find(p => p.name === periodName); + const period = tab.periods.find((p) => p.name === periodName); if (!period) throw new Error("La période sélectionnée n'a pas été trouvée."); const overview = await pronote.evaluations(account.instance!, period); - const evaluations: Array = overview.map(e => ({ + const evaluations: Array = overview.map((e) => ({ id: buildLocalID(e), name: e.name, subjectId: e.subject.id, @@ -44,7 +44,7 @@ export const getEvaluations = async (account: PronoteAccount, periodName: string timestamp: e.date.getTime(), coefficient: e.coefficient, levels: e.levels, - skills: e.skills.map(s => ({ + skills: e.skills.map((s) => ({ coefficient: s.coefficient, level: getLevel(s.abbreviation), domainName: s.domainName, diff --git a/src/services/pronote/news.ts b/src/services/pronote/news.ts index c2883d186..25459edb0 100644 --- a/src/services/pronote/news.ts +++ b/src/services/pronote/news.ts @@ -22,6 +22,6 @@ export const getNews = async (account: PronoteAccount): Promise = throw new ErrorServiceUnauthenticated("pronote"); const news = await pronote.news(account.instance); - const informations = news.items.filter(n => n.is === "information") as pronote.NewsInformation[]; + const informations = news.items.filter((n) => n.is === "information") as pronote.NewsInformation[]; return informations.map(parseInformation); }; diff --git a/src/services/skolengo/data/attendance.ts b/src/services/skolengo/data/attendance.ts index cd21a48c3..078ce3889 100644 --- a/src/services/skolengo/data/attendance.ts +++ b/src/services/skolengo/data/attendance.ts @@ -8,7 +8,7 @@ const dateIntervalToDeltaInMin = (from: Date, to: Date) => Math.round((to.getTim const dateIntervalToTime = (from: Date, to: Date) => { const deltaInMin = dateIntervalToDeltaInMin(from, to); - if(deltaInMin < 60) { + if (deltaInMin < 60) { return `${deltaInMin}min`; } else if (deltaInMin < 1440) { return `${Math.floor(deltaInMin/60)}h${deltaInMin%60}`; @@ -17,7 +17,7 @@ const dateIntervalToTime = (from: Date, to: Date) => { } }; -const _strs = (strs: (string|null|undefined)[], defaultStr: string) => strs.filter(e=>e && e.trim().length > 0).length > 0 ? strs.filter(e=>e && e.trim().length > 0).join(" - ") : defaultStr; +const _strs = (strs: (string|null|undefined)[], defaultStr: string) => strs.filter((e)=>e && e.trim().length > 0).length > 0 ? strs.filter((e)=>e && e.trim().length > 0).join(" - ") : defaultStr; export const getAttendance = async (account: SkolengoAccount): Promise => { if (!account.instance) @@ -34,8 +34,8 @@ export const getAttendance = async (account: SkolengoAccount): Promisee.currentState).forEach(absence => { - switch(absence.absenceType as SupportedAbsenceType) { + absences.map((e)=>e.currentState).forEach((absence) => { + switch (absence.absenceType as SupportedAbsenceType) { case ("ABSENCE"): attendance.absences.push({ diff --git a/src/services/skolengo/data/grades.ts b/src/services/skolengo/data/grades.ts index 5a44e8837..52165b554 100644 --- a/src/services/skolengo/data/grades.ts +++ b/src/services/skolengo/data/grades.ts @@ -13,9 +13,9 @@ const decodeGradeNumber = (value?:number | null): GradeValue => const getSubjectMinMax = (evalSubj: Evaluation): { min: GradeValue, max:GradeValue, outOf: GradeValue } => { const outOf = decodeGradeNumber(evalSubj.scale || SKOLENGO_DEFAULT_SCALE); - if(evalSubj.evaluations.filter(e=>e.evaluationResult.mark !== null && !e.evaluationResult.nonEvaluationReason).length === 0) return { min: { value: null, disabled: true, status: null } , max: { value: null, disabled: true, status: null }, outOf }; - const [minimum, maximum] = evalSubj.evaluations.filter(e=>e.evaluationResult.mark !== null) - .map(e=>((e.evaluationResult.mark!)/(e.scale || SKOLENGO_DEFAULT_SCALE)) * (evalSubj.scale || SKOLENGO_DEFAULT_SCALE)) + if (evalSubj.evaluations.filter((e) => e.evaluationResult.mark !== null && !e.evaluationResult.nonEvaluationReason).length === 0) return { min: { value: null, disabled: true, status: null } , max: { value: null, disabled: true, status: null }, outOf }; + const [minimum, maximum] = evalSubj.evaluations.filter((e)=>e.evaluationResult.mark !== null) + .map((e)=>((e.evaluationResult.mark!)/(e.scale || SKOLENGO_DEFAULT_SCALE)) * (evalSubj.scale || SKOLENGO_DEFAULT_SCALE)) .reduce(([minAcc, maxAcc], e) => [Math.min(minAcc, e), Math.max(maxAcc, e)], [evalSubj.scale || SKOLENGO_DEFAULT_SCALE, 0]); return { min: { value: minimum, disabled: false, status: null } , max: { value: maximum, disabled: false, status: null }, outOf }; }; @@ -28,7 +28,7 @@ export const getGradesAndAverages = async (account: SkolengoAccount, periodName: throw new ErrorServiceUnauthenticated("skolengo"); const periods = await getPeriod(account); - const period = periods.find(p => p.name === periodName); + const period = periods.find((p) => p.name === periodName); if (!period) throw new Error("La période sélectionnée n'a pas été trouvée."); @@ -46,7 +46,7 @@ export const getGradesAndAverages = async (account: SkolengoAccount, periodName: })), }; - const grades: Grade[] = evals.map(e=>e.evaluations.map(f=>({ ...f, evaluation: e }))).flat().map(g => ({ + const grades: Grade[] = evals.map((e) => e.evaluations.map((f) => ({ ...f, evaluation: e }))).flat().map((g) => ({ id: g.id, subjectName: g.evaluation.subject.label, description: g.title || g.topic || "Evaluation", diff --git a/src/services/skolengo/data/period.ts b/src/services/skolengo/data/period.ts index 3fbae807e..6a40884b0 100644 --- a/src/services/skolengo/data/period.ts +++ b/src/services/skolengo/data/period.ts @@ -7,7 +7,7 @@ export const getPeriod = async (account: SkolengoAccount) => { const periods = await account.instance.getEvaluationSettings(); - return periods.map(e=>e.periods).flat().map((p) => ({ + return periods.map((e)=>e.periods).flat().map((p) => ({ id: p.id, name: p.label, startTimestamp: new Date(p.startDate).getTime(), diff --git a/src/services/skolengo/data/timetable.ts b/src/services/skolengo/data/timetable.ts index c58f37c38..be707c9ad 100644 --- a/src/services/skolengo/data/timetable.ts +++ b/src/services/skolengo/data/timetable.ts @@ -13,9 +13,9 @@ const decodeLesson = (lesson: Lesson): TimetableClass => ({ title: lesson.title, startTimestamp: new Date(lesson.startDateTime).getTime(), endTimestamp: new Date(lesson.endDateTime).getTime(), - additionalNotes: lesson.contents?.map(e=>e.title+":\n"+htmlToText(e.html)).join("\n\n\n"), + additionalNotes: lesson.contents?.map((e)=>e.title+":\n"+htmlToText(e.html)).join("\n\n\n"), room: lesson.location ? lesson.location+(lesson.locationComplement ? " - "+lesson.locationComplement : "") : void 0, - teacher: lesson.teachers?.map(e=>`${e.firstName.at(0)}. ${e.lastName}`).join("/"), + teacher: lesson.teachers?.map((e)=>`${e.firstName.at(0)}. ${e.lastName}`).join("/"), backgroundColor: lesson.subject.color || void 0, status: lesson.canceled ? TimetableClassStatus.CANCELED : void 0, }); @@ -27,7 +27,7 @@ export const getTimetableForWeek = async (account: SkolengoAccount, epochWeekNum const { start, end } = weekNumberToDateRange(epochWeekNumber); const agenda = await account.instance.getAgenda(undefined, toSkolengoDate(start), toSkolengoDate(end)); - const lessons = agenda.map(e=>e.lessons).flat(); + const lessons = agenda.map((e)=>e.lessons).flat(); return lessons.map(decodeLesson); }; diff --git a/src/services/skolengo/data/utils.ts b/src/services/skolengo/data/utils.ts index 35534bad2..75507195e 100644 --- a/src/services/skolengo/data/utils.ts +++ b/src/services/skolengo/data/utils.ts @@ -5,4 +5,4 @@ export const userToName = (user: User) => `${user.lastName} ${user.firstName}`; export const _skoUcFist = (s: string) => s.charAt(0).toUpperCase() + s.slice(1); -export const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); +export const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); diff --git a/src/services/skolengo/default-personalization.ts b/src/services/skolengo/default-personalization.ts index 406c9fd50..0eca20d74 100644 --- a/src/services/skolengo/default-personalization.ts +++ b/src/services/skolengo/default-personalization.ts @@ -14,7 +14,7 @@ const defaultSkolengoPersonalization = async (instance: SkolengoAccount["instanc MagicNews: true, profilePictureB64: void 0, - tabs: defaultTabs.filter(current => skoTabs.includes(current.tab)).map((tab, index) => ({ + tabs: defaultTabs.filter((current) => skoTabs.includes(current.tab)).map((tab, index) => ({ name: tab.tab, enabled: index <= 4 })) @@ -34,4 +34,4 @@ const getServiceConfig =(instance: SkolengoAccount["instance"])=> Promise.all([ export default defaultSkolengoPersonalization; export const checkIfSkoSupported = (account: SkolengoAccount, service: Tab): boolean => - account.personalization.tabs?.some(tab => tab.name === service && tab.enabled) || false; + account.personalization.tabs?.some((tab) => tab.name === service && tab.enabled) || false; diff --git a/src/services/skolengo/reload-skolengo.ts b/src/services/skolengo/reload-skolengo.ts index 3615ae048..039e18439 100644 --- a/src/services/skolengo/reload-skolengo.ts +++ b/src/services/skolengo/reload-skolengo.ts @@ -5,7 +5,7 @@ import { Skolengo } from "scolengo-api"; export const reload = async (account: SkolengoAccount): Promise> => { - if(!account.instance || !(account.instance instanceof Skolengo)) { + if (!account.instance || !(account.instance instanceof Skolengo)) { const { instance, authentication } = await getSkolengoAccount(account.authentication, account.userInfo); return { instance, authentication }; } diff --git a/src/services/skolengo/skolengo-account.tsx b/src/services/skolengo/skolengo-account.tsx index 95d5ffbcf..3271ecfb8 100644 --- a/src/services/skolengo/skolengo-account.tsx +++ b/src/services/skolengo/skolengo-account.tsx @@ -54,7 +54,7 @@ export const refreshSkolengoToken = async (refreshToken: string, discovery: Disc formData.append("grant_type", "refresh_token"); formData.append("refresh_token", refreshToken); - if(!discovery.tokenEndpoint) throw new Error("[SKOLENGO] ERR - No token endpoint in discovery document"); + if (!discovery.tokenEndpoint) throw new Error("[SKOLENGO] ERR - No token endpoint in discovery document"); return await fetch(discovery.tokenEndpoint, { method: "POST", @@ -62,12 +62,12 @@ export const refreshSkolengoToken = async (refreshToken: string, discovery: Disc Authorization: "Basic "+b64encode(OID_CLIENT_ID + ":" + OID_CLIENT_SECRET), }, body: formData - }).then((response) => response.json()).then(d => authTokenToSkolengoTokenSet(d)); + }).then((response) => response.json()).then((d) => authTokenToSkolengoTokenSet(d)); }; const getJWTClaims = (token: string): SkolengoJWT => { const dataPart = token.split(".")?.at(1)?.replace(/-/g, "+").replace(/_/g, "/"); - if(!dataPart) throw new Error("[SKOLENGO] ERR - No data part in token"); + if (!dataPart) throw new Error("[SKOLENGO] ERR - No data part in token"); const data = JSON.parse(b64decode(dataPart)); return data; }; @@ -80,7 +80,7 @@ export const getSkolengoAccount = async (authConfig: SkolengoAuthConfig, userInf { refreshToken: async (tokenSet): Promise => { - if(!tokenSet.refresh_token) throw new Error("[SKOLENGO] ERR - No refresh token"); + if (!tokenSet.refresh_token) throw new Error("[SKOLENGO] ERR - No refresh token"); return refreshSkolengoToken(tokenSet.refresh_token, authConfig.discovery); }, onTokenRefresh: async (tokenSet) => { @@ -94,7 +94,7 @@ export const getSkolengoAccount = async (authConfig: SkolengoAuthConfig, userInf handlePronoteError: true } ); - if(!userInfo) userInfo = await skolengoAccount.getUserInfo(); + if (!userInfo) userInfo = await skolengoAccount.getUserInfo(); const jwtDecoded = getJWTClaims(skolengoAccount.tokenSet.id_token!); const account: SkolengoAccount = { service: AccountService.Skolengo, diff --git a/src/services/timetable.ts b/src/services/timetable.ts index 242b994fd..231dcebd4 100644 --- a/src/services/timetable.ts +++ b/src/services/timetable.ts @@ -25,7 +25,7 @@ export async function updateTimetableForWeekInCache (account break; } case AccountService.Skolengo: { - if(!checkIfSkoSupported(account, "Lessons")) { + if (!checkIfSkoSupported(account, "Lessons")) { error("[updateTimetableForWeekInCache]: This Skolengo instance doesn't support Lessons.", "skolengo"); break; } diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index b331b3a54..043f761c0 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -124,7 +124,7 @@ export const useCurrentAccount = create()((set, get) => ({ for (const associatedAccount of associatedAccounts) { if (!(typeof associatedAccount.instance === "undefined")) continue; - const { instance, authentication } = await reload(associatedAccount).catch(err => { + const { instance, authentication } = await reload(associatedAccount).catch((err) => { error(`failed to reload associated account: ${err} !`, "switchTo"); return { instance: associatedAccount.instance, @@ -216,7 +216,7 @@ export const useAccounts = create()( // 2. If a multi-service has no more associated accounts, it must be deleted (because a space is like a "group" of accounts, and without any associated accounts it does not work anymore⁾ // Fetching the accounts corresponding to spaces - const spacesAccounts: PapillonMultiServiceSpace[] = get().accounts.filter(account => account.service === AccountService.PapillonMultiService) as PapillonMultiServiceSpace[]; + const spacesAccounts: PapillonMultiServiceSpace[] = get().accounts.filter((account) => account.service === AccountService.PapillonMultiService) as PapillonMultiServiceSpace[]; for (const spaceAccount of spacesAccounts) { // The account deleted above is associated to this space @@ -225,7 +225,7 @@ export const useAccounts = create()( // Remove the link to the account (and to every feature to which it is linked) spaceAccount.associatedAccountsLocalIDs.splice(spaceAccount.associatedAccountsLocalIDs.indexOf(localID), 1); - const space = useMultiService.getState().spaces.find(space => space.accountLocalID === spaceAccount.localID) as MultiServiceSpace; + const space = useMultiService.getState().spaces.find((space) => space.accountLocalID === spaceAccount.localID) as MultiServiceSpace; Object.entries(space.featuresServices).map(([key, value]) => { if (value === localID) { space.featuresServices[key as MultiServiceFeature] = undefined; @@ -233,7 +233,7 @@ export const useAccounts = create()( }); useMultiService.getState().update(spaceAccount.localID, "featuresServices", space.featuresServices); set((state) => ({ - accounts: state.accounts.map(account => + accounts: state.accounts.map((account) => account.localID === spaceAccount.localID ? spaceAccount : account ) })); diff --git a/src/utils/epochWeekNumber.ts b/src/utils/epochWeekNumber.ts index 2d661d286..793a91a47 100644 --- a/src/utils/epochWeekNumber.ts +++ b/src/utils/epochWeekNumber.ts @@ -108,9 +108,11 @@ export const epochWMToCalendarWeekNumber = (epochWeekNumber: number): number => // Set Day to Sunday and make it the 7th day of the week date.setUTCDate(date.getUTCDate() + 4 - (date.getUTCDay()||7)); // Get first day of year - let yearStart = new Date(Date.UTC(date.getUTCFullYear(),0,1)); + const yearStart = new Date(Date.UTC(date.getUTCFullYear(),0,1)); // Calculate full weeks to nearest Thursday - let weekNo = Math.ceil(( ( (date.getTime() - yearStart.getTime()) / 86400000) + 1)/7); + const weekNo = Math.ceil( + ((date.getTime() - yearStart.getTime()) / 86400000 + 1) / 7 + ); // Return array of year and week number return weekNo; }; diff --git a/src/utils/external/download-as-base64.ts b/src/utils/external/download-as-base64.ts index f316dd386..53bba7db8 100644 --- a/src/utils/external/download-as-base64.ts +++ b/src/utils/external/download-as-base64.ts @@ -6,7 +6,7 @@ export default async function downloadAsBase64 (url: string, headers?: Record(resolve => { + return new Promise((resolve) => { // Read as base64 URL. const reader = new FileReader(); reader.readAsDataURL(blob); diff --git a/src/utils/format/course_duration.ts b/src/utils/format/course_duration.ts index 5011e4d16..c877d7b67 100644 --- a/src/utils/format/course_duration.ts +++ b/src/utils/format/course_duration.ts @@ -4,7 +4,7 @@ export const getDuration = (minutes: number): string => { const durationHours = Math.floor(minutes / 60); const durationRemainingMinutes = minutes % 60; - if(durationHours === 0) { + if (durationHours === 0) { return `${durationRemainingMinutes} min`; } diff --git a/src/utils/format/format_cours_name.ts b/src/utils/format/format_cours_name.ts index 9633b5ca0..10d826050 100644 --- a/src/utils/format/format_cours_name.ts +++ b/src/utils/format/format_cours_name.ts @@ -56,7 +56,7 @@ function findObjectByPronoteString (pronoteString = "") { function formatPretty (text: string): string { const upperCaseTerms = ["CM", "TD", "TP", "LV1", "LV2", "LV3", "LVA", "LVB", "LVC", "SAE", "PPP"]; let words = text.split(" "); - words = words.map(word => { + words = words.map((word) => { if (upperCaseTerms.includes(word.toUpperCase())) { return word.toUpperCase(); } diff --git a/src/utils/format/format_pronote_homeworks.ts b/src/utils/format/format_pronote_homeworks.ts index 859cc1de8..15d854281 100644 --- a/src/utils/format/format_pronote_homeworks.ts +++ b/src/utils/format/format_pronote_homeworks.ts @@ -28,7 +28,7 @@ function parse_homeworks (content: string): string { return text .replace(/&#(\d+);/g, (_, dec) => String.fromCharCode(parseInt(dec, 10))) .replace(/&([a-zA-Z]+);/g, (_, entity) => htmlEntities[entity] || `&${entity};`) - .replace(/[\u2200-\u22FF\u03B1-\u03C9]/g, match => latexVocabulary[match] || match); + .replace(/[\u2200-\u22FF\u03B1-\u03C9]/g, (match) => latexVocabulary[match] || match); } let result = ""; diff --git a/src/utils/magic/categorizeHomeworks.ts b/src/utils/magic/categorizeHomeworks.ts index 6deb26b9a..3785536cb 100644 --- a/src/utils/magic/categorizeHomeworks.ts +++ b/src/utils/magic/categorizeHomeworks.ts @@ -6,7 +6,7 @@ const detectionData: DetectionJson = detectionJson; function detectCategory (input: string): string | null { for (const [category, patterns] of Object.entries(detectionData)) { - if (patterns.some(pattern => new RegExp(pattern, "i").test(input))) { + if (patterns.some((pattern) => new RegExp(pattern, "i").test(input))) { return category; } } diff --git a/src/utils/multiservice/index.ts b/src/utils/multiservice/index.ts index 6f5fae834..fb748db02 100644 --- a/src/utils/multiservice/index.ts +++ b/src/utils/multiservice/index.ts @@ -5,7 +5,7 @@ import { PrimaryAccount } from "@/stores/account/types"; export function getFeatureAccount (feature: MultiServiceFeature, spaceLocalID: string) { const accountId = useMultiService.getState().getFeatureAccountId(feature, spaceLocalID); - return useCurrentAccount.getState().associatedAccounts.find(account => account.localID === accountId) as PrimaryAccount | undefined; + return useCurrentAccount.getState().associatedAccounts.find((account) => account.localID === accountId) as PrimaryAccount | undefined; } export function hasFeatureAccountSetup (feature: MultiServiceFeature, spaceLocalID: string) { diff --git a/src/views/account/Attendance/Attendance.tsx b/src/views/account/Attendance/Attendance.tsx index 9fc6562b5..639345171 100644 --- a/src/views/account/Attendance/Attendance.tsx +++ b/src/views/account/Attendance/Attendance.tsx @@ -25,14 +25,14 @@ import { OfflineWarning, useOnlineStatus } from "@/hooks/useOnlineStatus"; const Attendance: Screen<"Attendance"> = ({ route, navigation }) => { const theme = useTheme(); - const account = useCurrentAccount(store => store.account!); + const account = useCurrentAccount((store) => store.account!); const { isOnline } = useOnlineStatus(); const hasServiceSetup = account.service === AccountService.PapillonMultiService ? hasFeatureAccountSetup(MultiServiceFeature.Attendance, account.localID) : true; - const defaultPeriod = useAttendanceStore(store => store.defaultPeriod); - const periods = useAttendanceStore(store => store.periods); - const attendances = useAttendanceStore(store => store.attendances); + const defaultPeriod = useAttendanceStore((store) => store.defaultPeriod); + const periods = useAttendanceStore((store) => store.periods); + const attendances = useAttendanceStore((store) => store.attendances); const [isRefreshing, setIsRefreshing] = useState(false); const [isLoading, setLoading] = useState(hasServiceSetup); @@ -108,7 +108,7 @@ const Attendance: Screen<"Attendance"> = ({ route, navigation }) => { let totalDelayHours = 0; let totalDelayMinutes = 0; - attendances[selectedPeriod]?.absences.forEach(absence => { + attendances[selectedPeriod]?.absences.forEach((absence) => { if (!absence.justified) { totalUnJustifiedHours += parseInt(absence.hours.split("h")[0]); totalUnJustifiedMinutes += parseInt(absence.hours.split("h")[1]); @@ -121,7 +121,7 @@ const Attendance: Screen<"Attendance"> = ({ route, navigation }) => { totalAbsenceMinutes += parseInt(absence.hours.split("h")[1]); }); - attendances[selectedPeriod]?.delays.forEach(delay => { + attendances[selectedPeriod]?.delays.forEach((delay) => { const origMins = delay.duration; const missed = { hours: Math.floor(origMins / 60), @@ -198,7 +198,7 @@ const Attendance: Screen<"Attendance"> = ({ route, navigation }) => { > period.name)} + data={periods.map((period) => period.name)} selected={userSelectedPeriod ?? selectedPeriod} onSelectionChange={setUserSelectedPeriod} direction="right" @@ -239,7 +239,7 @@ const Attendance: Screen<"Attendance"> = ({ route, navigation }) => { progressViewOffset={70} onRefresh={() => { setIsRefreshing(true); - if(account.identityProvider?.identifier) { + if (account.identityProvider?.identifier) { navigation.navigate("BackgroundIdentityProvider"); updateAttendanceInCache(account, selectedPeriod).then(() => setIsRefreshing(false)); } @@ -294,7 +294,7 @@ const Attendance: Screen<"Attendance"> = ({ route, navigation }) => { /> )} - {Object.keys(attendances_observations_details).map(sectionName => ( + {Object.keys(attendances_observations_details).map((sectionName) => ( = ({ navigation, route }) => { theme.path === actualTheme)?.icon} + source={availableThemes.find((theme) => theme.path === actualTheme)?.icon} style={{ width: 35, height: 35, borderRadius: 25 / 2 }} /> Thème - {availableThemes.find(theme => theme.path === actualTheme)?.name} + {availableThemes.find((theme) => theme.path === actualTheme)?.name} diff --git a/src/views/account/Chat/Modals/ChatThemes.tsx b/src/views/account/Chat/Modals/ChatThemes.tsx index ec1c2120e..37d81568a 100644 --- a/src/views/account/Chat/Modals/ChatThemes.tsx +++ b/src/views/account/Chat/Modals/ChatThemes.tsx @@ -8,7 +8,7 @@ const ChatThemes: Screen<"ChatThemes"> = ({ navigation, route }) => { return ( - {themes.map(theme => ( + {themes.map((theme) => ( { diff --git a/src/views/account/Grades/Document.tsx b/src/views/account/Grades/Document.tsx index c3dbe5661..bd1fd6d88 100644 --- a/src/views/account/Grades/Document.tsx +++ b/src/views/account/Grades/Document.tsx @@ -52,7 +52,7 @@ const GradeDocument: Screen<"GradeDocument"> = ({ route, navigation }) => { navigation.addListener("beforeRemove", () => { if (shouldShowReviewOnClose) { AsyncStorage.getItem("review_given").then((value) => { - if(!value) { + if (!value) { askForReview(); AsyncStorage.setItem("review_given", "true"); } diff --git a/src/views/account/Grades/Grades.tsx b/src/views/account/Grades/Grades.tsx index 1eb05bd3b..f2dcb8f5a 100644 --- a/src/views/account/Grades/Grades.tsx +++ b/src/views/account/Grades/Grades.tsx @@ -100,7 +100,7 @@ const Grades: Screen<"Grades"> = ({ route, navigation }) => { setIsLoading(true); await updateData(); - if(isRefreshing && account.identityProvider?.identifier) { + if (isRefreshing && account.identityProvider?.identifier) { navigation.navigate("BackgroundIdentityProvider"); } diff --git a/src/views/account/Grades/Graph/GradesAverage.tsx b/src/views/account/Grades/Graph/GradesAverage.tsx index 73cfcf021..ad71ac6dc 100644 --- a/src/views/account/Grades/Graph/GradesAverage.tsx +++ b/src/views/account/Grades/Graph/GradesAverage.tsx @@ -86,10 +86,10 @@ const GradesAverageGraph: React.FC = ({ let hst = getAveragesHistory(grades, "student", overall ?? void 0); if (hst.length === 0) return; - let cla = getAveragesHistory(grades, "average", classOverall ?? void 0); + const cla = getAveragesHistory(grades, "average", classOverall ?? void 0); - let maxAvg = getPronoteAverage(grades, "max"); - let minAvg = getPronoteAverage(grades, "min"); + const maxAvg = getPronoteAverage(grades, "max"); + const minAvg = getPronoteAverage(grades, "min"); setGradesHistory(hst); setHLength(hst.length); diff --git a/src/views/account/Grades/Subject/SubjectTitle.tsx b/src/views/account/Grades/Subject/SubjectTitle.tsx index 69344ef0b..874609a24 100644 --- a/src/views/account/Grades/Subject/SubjectTitle.tsx +++ b/src/views/account/Grades/Subject/SubjectTitle.tsx @@ -2,8 +2,7 @@ import { NativeText } from "@/components/Global/NativeComponents"; import { getCourseSpeciality } from "@/utils/format/format_cours_name"; import { useTheme } from "@react-navigation/native"; import React, { useEffect } from "react"; -import { View } from "react-native"; -import { TouchableOpacity } from "react-native-gesture-handler"; +import { View, TouchableOpacity } from "react-native"; import { type RouteParameters } from "@/router/helpers/types"; import type { NativeStackNavigationProp } from "@react-navigation/native-stack"; import type { Grade, GradesPerSubject } from "@/services/shared/Grade"; diff --git a/src/views/account/Home/Elements/GradesElement.tsx b/src/views/account/Home/Elements/GradesElement.tsx index b50ce2431..14445751b 100644 --- a/src/views/account/Home/Elements/GradesElement.tsx +++ b/src/views/account/Home/Elements/GradesElement.tsx @@ -18,7 +18,7 @@ interface GradesElementProps { const GradesElement: React.FC = ({ onImportance }) => { const account = useCurrentAccount((store) => store.account); - const defaultPeriod = useGradesStore(store => store.defaultPeriod); + const defaultPeriod = useGradesStore((store) => store.defaultPeriod); const grades = useGradesStore((store) => store.grades); const [loading, setLoading] = useState(false); diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 3e1b11126..b1bd2d22e 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -22,8 +22,8 @@ interface HomeworksElementProps { } const HomeworksElement: React.FC = ({ navigation, onImportance }) => { - const account = useCurrentAccount(store => store.account!); - const homeworks = useHomeworkStore(store => store.homeworks); + const account = useCurrentAccount((store) => store.account!); + const homeworks = useHomeworkStore((store) => store.homeworks); const [loading, setLoading] = useState(false); @@ -35,8 +35,8 @@ const HomeworksElement: React.FC = ({ navigation, onImpor let score = 0; const hw = homeworks[dateToEpochWeekNumber(actualDay)] - .filter(hw => hw.due / 1000 >= Date.now() / 1000 && hw.due / 1000 <= Date.now() / 1000 + 7 * 24 * 60 * 60) - .filter(hw => !hw.done); + .filter((hw) => hw.due / 1000 >= Date.now() / 1000 && hw.due / 1000 <= Date.now() / 1000 + 7 * 24 * 60 * 60) + .filter((hw) => !hw.done); const date = new Date(); if (date.getHours() >= 17 && date.getHours() < 22) diff --git a/src/views/account/Home/Elements/PopupRestauration.tsx b/src/views/account/Home/Elements/PopupRestauration.tsx index 731fb1cda..03bd8a070 100644 --- a/src/views/account/Home/Elements/PopupRestauration.tsx +++ b/src/views/account/Home/Elements/PopupRestauration.tsx @@ -15,8 +15,8 @@ interface PopupRestaurationProps { const PopupRestauration: React.FC = ({ onImportance }) => { const { colors } = useTheme(); - const account = useCurrentAccount(store => store.account!); - const mutateProperty = useCurrentAccount(store => store.mutateProperty); + const account = useCurrentAccount((store) => store.account!); + const mutateProperty = useCurrentAccount((store) => store.mutateProperty); const ImportanceHandler = () => { let hours = new Date().getHours(); diff --git a/src/views/account/Home/Home.tsx b/src/views/account/Home/Home.tsx index 240d2c05e..5fc178eba 100644 --- a/src/views/account/Home/Home.tsx +++ b/src/views/account/Home/Home.tsx @@ -65,7 +65,7 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { const scrollRef = useAnimatedRef(); const scrollOffset = useScrollViewOffset(scrollRef); - const account = useCurrentAccount(store => store.account!); + const account = useCurrentAccount((store) => store.account!); const accounts = useAccounts((store) => store.accounts); useEffect(() => { diff --git a/src/views/account/Home/ModalContent.tsx b/src/views/account/Home/ModalContent.tsx index 93eb69d0c..e9c09deb0 100644 --- a/src/views/account/Home/ModalContent.tsx +++ b/src/views/account/Home/ModalContent.tsx @@ -4,7 +4,7 @@ import Reanimated, { FadeInUp, FadeOutDown, LinearTransition } from "react-nativ import { Sparkles, X } from "lucide-react-native"; import { useTheme } from "@react-navigation/native"; import PackageJSON from "../../../../package.json"; -import { Dimensions, View } from "react-native"; +import { Dimensions, View, TouchableOpacity } from "react-native"; import { Elements, type Element } from "./ElementIndex"; import { animPapillon } from "@/utils/ui/animations"; @@ -14,7 +14,6 @@ import AsyncStorage from "@react-native-async-storage/async-storage"; import { defaultTabs } from "@/consts/DefaultTabs"; import { NativeStackNavigationProp } from "@react-navigation/native-stack"; import { RouteParameters } from "@/router/helpers/types"; -import { TouchableOpacity } from "react-native-gesture-handler"; import { OfflineWarning, useOnlineStatus } from "@/hooks/useOnlineStatus"; interface ModalContentProps { diff --git a/src/views/account/Homeworks/Homeworks.old.tsx b/src/views/account/Homeworks/Homeworks.old.tsx deleted file mode 100644 index 4fbb92a3b..000000000 --- a/src/views/account/Homeworks/Homeworks.old.tsx +++ /dev/null @@ -1,249 +0,0 @@ -import { useTheme } from "@react-navigation/native"; -import React, { useEffect, useRef, useCallback, useLayoutEffect, useMemo, useState } from "react"; -import { View, ScrollView, Text } from "react-native"; -import { Screen } from "@/router/helpers/types"; -import { toggleHomeworkState, updateHomeworkForWeekInCache } from "@/services/homework"; -import { useHomeworkStore } from "@/stores/homework"; -import { useCurrentAccount } from "@/stores/account"; -import { HeaderCalendar } from "./HomeworksHeader"; -import HomeworkItem from "./Atoms/Item"; -import { RefreshControl } from "react-native-gesture-handler"; -import HomeworksNoHomeworksItem from "./Atoms/NoHomeworks"; -import { Homework } from "@/services/shared/Homework"; -import { NativeList, NativeListHeader } from "@/components/Global/NativeComponents"; -import { Account } from "@/stores/account/types"; -import { debounce } from "lodash"; -import { dateToEpochWeekNumber, epochWNToDate } from "@/utils/epochWeekNumber"; -import InfinitePager from "react-native-infinite-pager"; -import { log } from "@/utils/logger/logger"; - -// Types pour les props du composant HomeworkList -type HomeworkListProps = { - groupedHomework: Record; - loading: boolean; - onDonePressHandler: (homework: Homework) => void; -}; - -const formatDate = (date: string | number | Date): string => { - return new Date(date).toLocaleDateString("fr-FR", { - day: "numeric", - month: "long" - }); -}; - -const HomeworkList: React.FC = React.memo(({ groupedHomework, loading, onDonePressHandler }) => { - if (!loading && Object.keys(groupedHomework).length === 0) { - return ; - } - - // @ts-ignore - return ( - <> - {Object.keys(groupedHomework).map((day, index) => ( - - - - {groupedHomework[day].map((homework, idx) => ( - // @ts-ignore : this is an old file, so it doesn't match with new types... This file is not supposed to be used. - onDonePressHandler(homework)} - /> - ))} - - - ))} - - ); -}, (prevProps, nextProps) => prevProps.groupedHomework === nextProps.groupedHomework && prevProps.loading === nextProps.loading); - -// Types pour les props du composant HomeworksPage -type HomeworksPageProps = { - index: number; - isActive: boolean; - loaded: boolean; - homeworks: Record; - account: Account; - updateHomeworks: () => Promise; - loading: boolean; - getDayName: (date: string | number | Date) => string; -}; - -const HomeworksPage: React.FC = React.memo(({ index, isActive, loaded, homeworks, account, updateHomeworks, loading, getDayName }) => { - const [refreshing, setRefreshing] = useState(false); - if (!loaded) { - return - - - - {index} - - - ; - } - - const homeworksInWeek = homeworks[index] ?? []; - const sortedHomework = useMemo( - () => homeworksInWeek.toSorted((a, b) => new Date(a.due).getTime() - new Date(b.due).getTime()), - [homeworksInWeek] - ); - - const groupedHomework = useMemo( - () => - sortedHomework.reduce((acc, curr) => { - const dayName = getDayName(curr.due); - const formattedDate = formatDate(curr.due); - const day = `${dayName} ${formattedDate}`; - - if (!acc[day]) { - acc[day] = [curr]; - } else { - acc[day].push(curr); - } - - return acc; - }, {} as Record), - [sortedHomework] - ); - - const handleDonePress = useCallback( - async (homework: Homework) => { - await toggleHomeworkState(account, homework); - await updateHomeworks(); - }, - [account, updateHomeworks] - ); - - const refreshAction = useCallback(async () => { - setRefreshing(true); - await updateHomeworks(); - setRefreshing(false); - }, [updateHomeworks]); - - return ( - - } - > - - - - ); -}, (prevProps, nextProps) => { - return prevProps.index === nextProps.index; -}); - -const initialIndex = dateToEpochWeekNumber(new Date()); - -const HomeworksScreen: Screen<"Homeworks"> = ({ navigation }) => { - const theme = useTheme(); - const account = useCurrentAccount(store => store.account!); - const homeworks = useHomeworkStore(store => store.homeworks); - - // NOTE: PagerRef is a pain to type, please help me... - const PagerRef = useRef(null); - - const [epochWeekNumber, setEpochWeekNumber] = useState(initialIndex); - const [loading, setLoading] = useState(false); - - useEffect(() => { - log("Account instance changed", "Homeworks"); - if (account.instance) { - const WN = initialIndex; - manuallyChangeWeek(WN); - } - }, [account.instance]); - - const manuallyChangeWeek = (index: number) => { - setEpochWeekNumber(index); - PagerRef.current?.setPage(index); - }; - - const MemoizedHeaderCalendar = useMemo( - () => ( - { - // TODO: Implement date picker logic here - }} - changeIndex={(index: number) => manuallyChangeWeek(index)} - /> - ), - [epochWeekNumber, manuallyChangeWeek] - ); - - useLayoutEffect(() => { - navigation.setOptions({ - headerTitle: () => MemoizedHeaderCalendar, - }); - }, [navigation, epochWeekNumber]); - - const updateHomeworks = useCallback(async () => { - setLoading(true); - log("Updating cache..." + epochWeekNumber + epochWNToDate(epochWeekNumber), "Homeworks/Update"); - await updateHomeworkForWeekInCache(account, epochWNToDate(epochWeekNumber)); - log("Updated cache !" + epochWNToDate(epochWeekNumber), "Homeworks/Update"); - setLoading(false); - }, [account, epochWeekNumber]); - - const debouncedUpdateHomeworks = useMemo(() => debounce(updateHomeworks, 500), [updateHomeworks]); - - const getDayName = (date: string | number | Date): string => { - const days = ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"]; - return days[new Date(date).getDay()]; - }; - - useEffect(() => { - debouncedUpdateHomeworks(); - }, [navigation, account.instance, epochWeekNumber]); - - return ( - - {account.instance && ( - ( - - )} - style={{ flex: 1 }} - onPageChange={setEpochWeekNumber} - /> - )} - - ); -}; - -export default HomeworksScreen; diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 4bd08f050..61cb4b1f3 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -61,9 +61,9 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const outsideNav = route.params?.outsideNav; const theme = useTheme(); - const account = useCurrentAccount(store => store.account!); + const account = useCurrentAccount((store) => store.account!); const hasServiceSetup = account.service === AccountService.PapillonMultiService ? hasFeatureAccountSetup(MultiServiceFeature.Homeworks, account.localID) : true; - const homeworks = useHomeworkStore(store => store.homeworks); + const homeworks = useHomeworkStore((store) => store.homeworks); // @ts-expect-error let firstDate = account?.instance?.instance?.firstDate || null; @@ -106,7 +106,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const [loadedWeeks, setLoadedWeeks] = useState([]); const updateHomeworks = useCallback(async (force = false, showRefreshing = true, showLoading = true) => { - if(!account) return; + if (!account) return; if (!force && loadedWeeks.includes(selectedWeek)) { return; @@ -122,7 +122,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { .then(() => { setLoading(false); setRefreshing(false); - setLoadedWeeks(prev => [...prev, selectedWeek]); + setLoadedWeeks((prev) => [...prev, selectedWeek]); }); }, [account, selectedWeek, loadedWeeks]); @@ -153,7 +153,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { // filter homeworks by search terms if (searchTerms.length > 0) { - acc[day] = acc[day].filter(homework => { + acc[day] = acc[day].filter((homework) => { const content = homework.content.toLowerCase().trim().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); const subject = homework.subject.toLowerCase().trim().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); return content.includes(searchTerms.toLowerCase().trim().normalize("NFD").replace(/[\u0300-\u036f]/g, "")) || @@ -163,7 +163,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { // if hideDone is enabled, filter out the done homeworks if (hideDone) { - acc[day] = acc[day].filter(homework => !homework.done); + acc[day] = acc[day].filter((homework) => !homework.done); } // homework completed downstairs @@ -198,7 +198,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { setTimeout(() => { AsyncStorage.getItem("review_given").then((value) => { - if(!value) { + if (!value) { askForReview(); AsyncStorage.setItem("review_given", "true"); } @@ -302,13 +302,13 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const onEndReached = () => { const lastWeek = data[data.length - 1]; const newWeeks = Array.from({ length: 50 }, (_, i) => lastWeek + i + 1); - setData(prevData => [...prevData, ...newWeeks]); + setData((prevData) => [...prevData, ...newWeeks]); }; const onStartReached = () => { const firstWeek = data[0]; const newWeeks = Array.from({ length: 50 }, (_, i) => firstWeek - 50 + i); - setData(prevData => [...newWeeks, ...prevData]); + setData((prevData) => [...newWeeks, ...prevData]); flatListRef.current?.scrollToIndex({ index: 50, animated: false }); }; @@ -328,7 +328,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { }, [finalWidth, data]); const goToWeek = useCallback((weekNumber: number) => { - const index = data.findIndex(week => week === weekNumber); + const index = data.findIndex((week) => week === weekNumber); if (index !== -1) { // @ts-expect-error const currentIndex = Math.round(flatListRef.current?.contentOffset?.x / finalWidth) || 0; diff --git a/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx b/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx index 50fe446d3..85a648fe4 100644 --- a/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx +++ b/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx @@ -115,7 +115,7 @@ const HorizontalDatePicker = ({ onDateSelect, onCurrentDatePress, initialDate = const insets = useSafeAreaInsets(); useEffect(() => { - const dateIndex = dates.findIndex(date => isSameDay(date, initialDate)); + const dateIndex = dates.findIndex((date) => isSameDay(date, initialDate)); if (dateIndex !== -1) { const diffFromCenter = dateIndex - centerIndex; if (Math.abs(diffFromCenter) <= SCROLL_THRESHOLD) { diff --git a/src/views/account/Lessons/Document.tsx b/src/views/account/Lessons/Document.tsx index 20dbfaed5..727c4d2a3 100644 --- a/src/views/account/Lessons/Document.tsx +++ b/src/views/account/Lessons/Document.tsx @@ -361,7 +361,7 @@ const LessonDocument: Screen<"LessonDocument"> = ({ route, navigation }) => { value={`${desc}`} stylesheet={stylesText} addLineBreaks={false} - onLinkPress={url => openUrl(url) } + onLinkPress={(url) => openUrl(url) } key={"res_html_" + index} /> } diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index 4695df12c..80b111cd7 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -252,7 +252,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { setTimeout(() => { AsyncStorage.getItem("review_given").then((value) => { - if(!value) { + if (!value) { askForReview(); AsyncStorage.setItem("review_given", "true"); } @@ -280,7 +280,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const lastDate = data[data.length - 1]; let updatedData = [...data]; - const uniqueDates = new Set(updatedData.map(d => d.getTime())); + const uniqueDates = new Set(updatedData.map((d) => d.getTime())); if (newDate < firstDate) { const dates = []; diff --git a/src/views/account/Lessons/Options/LessonsImportIcal.tsx b/src/views/account/Lessons/Options/LessonsImportIcal.tsx index c15ce96bd..df4d01edd 100644 --- a/src/views/account/Lessons/Options/LessonsImportIcal.tsx +++ b/src/views/account/Lessons/Options/LessonsImportIcal.tsx @@ -24,8 +24,8 @@ const LessonsImportIcal: Screen<"LessonsImportIcal"> = ({ route, navigation }) = const defaultIcal = route.params?.ical || ""; const autoAdd = route.params?.autoAdd || false; - const account = useCurrentAccount(store => store.account!); - const mutateProperty = useCurrentAccount(store => store.mutateProperty); + const account = useCurrentAccount((store) => store.account!); + const mutateProperty = useCurrentAccount((store) => store.mutateProperty); const [url, setUrl] = React.useState(defaultIcal); @@ -40,7 +40,7 @@ const LessonsImportIcal: Screen<"LessonsImportIcal"> = ({ route, navigation }) = useEffect(() => { if (!account.instance) return; if (defaultIcal && autoAdd) { - if(account.personalization.icalURLs?.filter(u => u.url === defaultIcal).length === 0) { + if (account.personalization.icalURLs?.filter((u) => u.url === defaultIcal).length === 0) { saveIcal().then(() => { if (autoAdd) { navigation.goBack(); @@ -59,8 +59,8 @@ const LessonsImportIcal: Screen<"LessonsImportIcal"> = ({ route, navigation }) = const oldUrls = account.personalization.icalURLs || []; await fetch(url) - .then(response => response.text()) - .then(text => { + .then((response) => response.text()) + .then((text) => { const parsed = ical.parseString(text); let newParsed = parsed; newParsed.events = []; diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 41fe23d04..48929375c 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -178,19 +178,19 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { } const [balance, history, cardnumber, booking] = await Promise.all([ - balanceFromExternal(account, isRefreshing).catch(err => { + balanceFromExternal(account, isRefreshing).catch((err) => { warn(`Error fetching balance for account ${account.username}:` + err, "Menu/balanceFromExternal"); return []; }), - reservationHistoryFromExternal(account).catch(err => { + reservationHistoryFromExternal(account).catch((err) => { warn(`Error fetching history for account ${account.username}:` + err, "Menu/reservationHistoryFromExternal"); return []; }), - qrcodeFromExternal(account).catch(err => { + qrcodeFromExternal(account).catch((err) => { warn(`Error fetching QR code for account ${account.username}:` + err, "Menu/qrcodeFromExternal"); return "0"; }), - getBookingsAvailableFromExternal(account, getWeekNumber(new Date()), isRefreshing).catch(err => { + getBookingsAvailableFromExternal(account, getWeekNumber(new Date()), isRefreshing).catch((err) => { warn(`Error fetching bookings for account ${account.username}:` + err, "Menu/getBookingsAvailableFromExternal"); return []; }) @@ -275,7 +275,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { opacity={0.6} /> - Allergènes : {allergens.map(allergen => allergen.name).join(", ")} + Allergènes : {allergens.map((allergen) => allergen.name).join(", ")} ); diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index f158c98a9..fd73f8541 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -38,11 +38,11 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio const updateCardData = async () => { try { const [balance, history] = await Promise.all([ - balanceFromExternal(route.params.card.account as ExternalAccount).catch(err => { + balanceFromExternal(route.params.card.account as ExternalAccount).catch((err) => { warn(`Error fetching balance for account ${account?.name}:` + err, "CardDetail/balanceFromExternal"); return []; }), - reservationHistoryFromExternal(route.params.card.account as ExternalAccount).catch(err => { + reservationHistoryFromExternal(route.params.card.account as ExternalAccount).catch((err) => { warn(`Error fetching history for account ${account?.name}:` + err, "CardDetail/reservationHistoryFromExternal"); return []; }) diff --git a/src/views/account/Restaurant/Modals/QrCode.tsx b/src/views/account/Restaurant/Modals/QrCode.tsx index 0d62c3906..5dcbe93d1 100644 --- a/src/views/account/Restaurant/Modals/QrCode.tsx +++ b/src/views/account/Restaurant/Modals/QrCode.tsx @@ -23,7 +23,7 @@ const RestaurantQrCode: Screen<"RestaurantQrCode"> = ({ route, navigation }) => const PollingBalance = async () => { balanceFromExternal(card.account as ExternalAccount).then((newBalance) => { - if(card.balance[0].amount !== newBalance[0].amount) { + if (card.balance[0].amount !== newBalance[0].amount) { openFeedback(); } }); @@ -67,7 +67,7 @@ const RestaurantQrCode: Screen<"RestaurantQrCode"> = ({ route, navigation }) => useEffect(() => { // Si Izly - if(card.service === 10) { + if (card.service === 10) { const interval = setInterval(() => { log("[CANTINE >> IZLY] Demande du solde", "QrCode/Izly/PollingBalance"); PollingBalance(); diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index e5ee87c70..9b59bd6f0 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -242,11 +242,11 @@ const Week: Screen<"Week"> = ({ route, navigation }) => { const [events, setEvents] = React.useState([]); useEffect(() => { - if(!timetables) return; + if (!timetables) return; const nevts = Object.values(timetables) .flat() - .map(event => ({ + .map((event) => ({ id: event.id.toString(), title: event.title, start: { dateTime: new Date(event.startTimestamp) }, @@ -283,7 +283,7 @@ const Week: Screen<"Week"> = ({ route, navigation }) => { const [openedIcalModal, setOpenedIcalModal] = React.useState(false); React.useEffect(() => { - if(events.length === 0 && (account?.personalization?.icalURLs?.length || 0) > 0) { + if (events.length === 0 && (account?.personalization?.icalURLs?.length || 0) > 0) { setIsLoading(true); requestAnimationFrame(async () => { const weekNumber = dateToEpochWeekNumber(new Date()); diff --git a/src/views/addon/AddonLogs.tsx b/src/views/addon/AddonLogs.tsx index 58a1c5a38..c812fdc63 100644 --- a/src/views/addon/AddonLogs.tsx +++ b/src/views/addon/AddonLogs.tsx @@ -5,7 +5,7 @@ import { Screen } from "@/router/helpers/types"; import { AddonLogs as Logs } from "@/addons/types"; const AddonLogs: Screen<"AddonLogs"> = ({ navigation, route }) => { - const logs: Logs[] = route.params.logs.map(l => { + const logs: Logs[] = route.params.logs.map((l) => { return { date: new Date(l.date), message: l.message, diff --git a/src/views/addon/AddonPage.tsx b/src/views/addon/AddonPage.tsx index e981db321..0db94d3e0 100644 --- a/src/views/addon/AddonPage.tsx +++ b/src/views/addon/AddonPage.tsx @@ -38,8 +38,8 @@ const AddonPage: Screen<"AddonPage"> = ({ navigation, route }) => { requestNavigate={(url, data) => { //find the placement let index = -1; - for(let i = 0; i < addon.manifest.placement.length; i++){ - if(addon.manifest.placement[i].name == url){ + for (let i = 0; i < addon.manifest.placement.length; i++){ + if (addon.manifest.placement[i].name == url){ index = i; break; } @@ -50,7 +50,6 @@ const AddonPage: Screen<"AddonPage"> = ({ navigation, route }) => { message: "La page accédée n'a pas été trouvée.", icon: , }); //TODO: transfer error to webview - return; } else { let newAddon: AddonPlacementManifest = { manifest: addon.manifest, index: index }; // @ts-ignore "Very hard to type, need to think about" diff --git a/src/views/login/IdentityProvider/BackgroundIdentityProvider.tsx b/src/views/login/IdentityProvider/BackgroundIdentityProvider.tsx index da241a964..6b1294f92 100644 --- a/src/views/login/IdentityProvider/BackgroundIdentityProvider.tsx +++ b/src/views/login/IdentityProvider/BackgroundIdentityProvider.tsx @@ -3,19 +3,19 @@ import { Screen } from "@/router/helpers/types"; import { useEffect } from "react"; const BackgroundIdentityProvider: Screen<"BackgroundIdentityProvider"> = ({ route, navigation }) => { - const account = useCurrentAccount(store => store.account); + const account = useCurrentAccount((store) => store.account); useEffect(() => { - if(!account) { + if (!account) { navigation.goBack(); } const identityProvider = account!.identityProvider; - if(identityProvider) { + if (identityProvider) { const { identifier } = identityProvider; - if(identifier === "iut-lannion") { + if (identifier === "iut-lannion") { navigation.goBack(); navigation.navigate("BackgroundIUTLannion"); } diff --git a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx index f39fde21c..bc213dddf 100644 --- a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx +++ b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx @@ -53,7 +53,7 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio let username = params?.username || null; let password = params?.password || null; - const account = useCurrentAccount(store => store.account); + const account = useCurrentAccount((store) => store.account); const url = "https://notes9.iutlan.univ-rennes1.fr/"; const firstLogin = params?.firstLogin || false; @@ -61,8 +61,8 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio const [step, setStep] = React.useState("Chargement du portail"); - if(!firstLogin) { - if(account?.service == AccountService.Local && account.credentials) { + if (!firstLogin) { + if (account?.service == AccountService.Local && account.credentials) { username = account.credentials.username; password = account.credentials.password; } @@ -71,14 +71,14 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio } } - const createStoredAccount = useAccounts(store => store.create); - const switchTo = useCurrentAccount(store => store.switchTo); - const mutateProperty = useCurrentAccount(store => store.mutateProperty); + const createStoredAccount = useAccounts((store) => store.create); + const switchTo = useCurrentAccount((store) => store.switchTo); + const mutateProperty = useCurrentAccount((store) => store.mutateProperty); const { showAlert } = useAlert(); const useData = async (data: any) => { - if(firstLogin) { + if (firstLogin) { await actionFirstLogin(data); } else { @@ -156,7 +156,7 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio await retreiveNextSemestre(newCurrentSemestre, semestresToRetrieve); } else { - if(firstLogin) { + if (firstLogin) { queueMicrotask(() => { // Reset the navigation stack to the "Home" screen. // Prevents the user from going back to the login screen. @@ -233,7 +233,7 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio const [redirectCount, setRedirectCount] = React.useState(0); const injectPassword = () => { - if(redirectCount >= 2) { + if (redirectCount >= 2) { showAlert({ title: "Erreur", message: "Impossible de se connecter au portail du l'IUT de Lannion. Vérifie tes identifiants et réessaye.", @@ -315,21 +315,21 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio onLoad={(data) => { const url = data.nativeEvent.url; - if(url.startsWith("https://sso-cas.univ-rennes.fr//login?")) { + if (url.startsWith("https://sso-cas.univ-rennes.fr//login?")) { injectPassword(); } - if(url.startsWith("https://notes9.iutlan.univ-rennes1.fr/") && canExtractJSON) { + if (url.startsWith("https://notes9.iutlan.univ-rennes1.fr/") && canExtractJSON) { redirectToData(); setCanExtractJSON(false); } - if(url.startsWith("https://notes9.iutlan.univ-rennes1.fr/services/data.php?q=relev%C3%A9Etudiant&semestre=")) { + if (url.startsWith("https://notes9.iutlan.univ-rennes1.fr/services/data.php?q=relev%C3%A9Etudiant&semestre=")) { wbref.current?.injectJavaScript(` window.ReactNativeWebView.postMessage("semestre:"+document.body.innerText); `); } - else if(url.startsWith("https://notes9.iutlan.univ-rennes1.fr/services/data.php")) { + else if (url.startsWith("https://notes9.iutlan.univ-rennes1.fr/services/data.php")) { wbref.current?.injectJavaScript(` window.ReactNativeWebView.postMessage("firstLogin:"+document.body.innerText); `); @@ -355,12 +355,12 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio onMessage={(event) => { try { - if(event.nativeEvent.data.startsWith("firstLogin:")) { + if (event.nativeEvent.data.startsWith("firstLogin:")) { const data = event.nativeEvent.data.replace("firstLogin:", ""); const parsedData = JSON.parse(data); useData(parsedData); } - else if(event.nativeEvent.data.startsWith("semestre:")) { + else if (event.nativeEvent.data.startsWith("semestre:")) { const data = event.nativeEvent.data.replace("semestre:", ""); const parsedData = JSON.parse(data); processSemestre(parsedData); diff --git a/src/views/login/IdentityProvider/providers/Multi.tsx b/src/views/login/IdentityProvider/providers/Multi.tsx index 64948da9d..94cc9b3a2 100644 --- a/src/views/login/IdentityProvider/providers/Multi.tsx +++ b/src/views/login/IdentityProvider/providers/Multi.tsx @@ -14,8 +14,8 @@ const Muli_Login: Screen<"Multi_Login"> = ({ route, navigation }) => { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); - const createStoredAccount = useAccounts(store => store.create); - const switchTo = useCurrentAccount(store => store.switchTo); + const createStoredAccount = useAccounts((store) => store.create); + const switchTo = useCurrentAccount((store) => store.switchTo); const handleLogin = async (username: string, password: string) => { try { diff --git a/src/views/login/IdentityProvider/providers/UnivLimoges.tsx b/src/views/login/IdentityProvider/providers/UnivLimoges.tsx index 82f513df9..127e6a055 100644 --- a/src/views/login/IdentityProvider/providers/UnivLimoges.tsx +++ b/src/views/login/IdentityProvider/providers/UnivLimoges.tsx @@ -15,8 +15,8 @@ import { useAlert } from "@/providers/AlertProvider"; import { BadgeX, Check } from "lucide-react-native"; const UnivLimoges_Login: Screen<"UnivLimoges_Login"> = ({ navigation }) => { - const createStoredAccount = useAccounts(store => store.create); - const switchTo = useCurrentAccount(store => store.switchTo); + const createStoredAccount = useAccounts((store) => store.create); + const switchTo = useCurrentAccount((store) => store.switchTo); const theme = useTheme(); const { showAlert } = useAlert(); diff --git a/src/views/login/IdentityProvider/providers/UnivRennes1.tsx b/src/views/login/IdentityProvider/providers/UnivRennes1.tsx index 56d53b8ea..a1fb7f35f 100644 --- a/src/views/login/IdentityProvider/providers/UnivRennes1.tsx +++ b/src/views/login/IdentityProvider/providers/UnivRennes1.tsx @@ -19,8 +19,8 @@ const UnivRennes1_Login: Screen<"UnivRennes1_Login"> = ({ navigation }) => { const webViewRef = React.useRef(null); - const createStoredAccount = useAccounts(store => store.create); - const switchTo = useCurrentAccount(store => store.switchTo); + const createStoredAccount = useAccounts((store) => store.create); + const switchTo = useCurrentAccount((store) => store.switchTo); const [isLoading, setIsLoading] = React.useState(true); const [isLoadingText, setIsLoadingText] = React.useState("Connexion en cours..."); diff --git a/src/views/login/IdentityProvider/providers/UnivRennes2.tsx b/src/views/login/IdentityProvider/providers/UnivRennes2.tsx index fec134d63..baf43e452 100644 --- a/src/views/login/IdentityProvider/providers/UnivRennes2.tsx +++ b/src/views/login/IdentityProvider/providers/UnivRennes2.tsx @@ -61,8 +61,8 @@ const UnivRennes2_Login: Screen<"UnivRennes2_Login"> = ({ navigation }) => { const webViewRef = React.useRef(null); - const createStoredAccount = useAccounts(store => store.create); - const switchTo = useCurrentAccount(store => store.switchTo); + const createStoredAccount = useAccounts((store) => store.create); + const switchTo = useCurrentAccount((store) => store.switchTo); const [isLoading, setIsLoading] = React.useState(true); const [isLoadingText, setIsLoadingText] = React.useState("Connexion en cours..."); diff --git a/src/views/login/IdentityProvider/providers/UnivSorbonneParisNord.tsx b/src/views/login/IdentityProvider/providers/UnivSorbonneParisNord.tsx index 6b0bed936..15ee50650 100644 --- a/src/views/login/IdentityProvider/providers/UnivSorbonneParisNord.tsx +++ b/src/views/login/IdentityProvider/providers/UnivSorbonneParisNord.tsx @@ -16,8 +16,8 @@ const USER_AGENT = "USPNAPP/1.0.1 CFNetwork/1568.200.41 Darwin/24.1.0"; const UnivSorbonneParisNord_login: Screen<"UnivSorbonneParisNord_login"> = ({ navigation }) => { const theme = useTheme(); - const createStoredAccount = useAccounts(store => store.create); - const switchTo = useCurrentAccount(store => store.switchTo); + const createStoredAccount = useAccounts((store) => store.create); + const switchTo = useCurrentAccount((store) => store.switchTo); const [isLoading, setIsLoading] = useState(false); const [loadingText, setLoadingText] = useState(""); diff --git a/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx b/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx index ca4fa7c1d..0a031bd7e 100644 --- a/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx +++ b/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx @@ -41,8 +41,8 @@ const EcoleDirecteCredentials: Screen<"EcoleDirecteCredentials"> = ({ navigation const theme = useTheme(); - const createStoredAccount = useAccounts(store => store.create); - const switchTo = useCurrentAccount(store => store.switchTo); + const createStoredAccount = useAccounts((store) => store.create); + const switchTo = useCurrentAccount((store) => store.switchTo); const handleLogin = async (username: string, password: string, currentSession = session) => { try { diff --git a/src/views/login/pronote/PronoteCredentials.tsx b/src/views/login/pronote/PronoteCredentials.tsx index e5abf0081..61ea8281b 100644 --- a/src/views/login/pronote/PronoteCredentials.tsx +++ b/src/views/login/pronote/PronoteCredentials.tsx @@ -14,8 +14,8 @@ const PronoteCredentials: Screen<"PronoteCredentials"> = ({ route, navigation }) const [loading, setLoading] = useState(false); const [error, setError] = useState(null); - const createStoredAccount = useAccounts(store => store.create); - const switchTo = useCurrentAccount(store => store.switchTo); + const createStoredAccount = useAccounts((store) => store.create); + const switchTo = useCurrentAccount((store) => store.switchTo); const handleLogin = async (username: string, password: string) => { try { diff --git a/src/views/login/pronote/PronoteManualLocation.tsx b/src/views/login/pronote/PronoteManualLocation.tsx index 6a4d5e9b7..9c5d44f13 100644 --- a/src/views/login/pronote/PronoteManualLocation.tsx +++ b/src/views/login/pronote/PronoteManualLocation.tsx @@ -74,7 +74,7 @@ const PronoteManualLocation: Screen<"PronoteManualLocation"> = ({ navigation }) } // We set the loading state to true. - setMunicipalities(prev => ({ + setMunicipalities((prev) => ({ loading: true, results: prev.results // Keep the previous results while it's loading. })); diff --git a/src/views/login/pronote/PronoteManualURL.tsx b/src/views/login/pronote/PronoteManualURL.tsx index 9083df185..a2782826c 100644 --- a/src/views/login/pronote/PronoteManualURL.tsx +++ b/src/views/login/pronote/PronoteManualURL.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; import type { RouteParameters, Screen } from "@/router/helpers/types"; -import { View, StyleSheet, TouchableOpacity, KeyboardEvent, Keyboard } from "react-native"; +import { View, StyleSheet, TouchableOpacity, Keyboard } from "react-native"; import { useTheme } from "@react-navigation/native"; import determinateAuthenticationView from "@/services/pronote/determinate-authentication-view"; @@ -26,7 +26,7 @@ const PronoteManualURL: Screen<"PronoteManualURL"> = ({ route, navigation }) => const { showAlert } = useAlert(); - const keyboardDidShow = (event: KeyboardEvent) => { + const keyboardDidShow = () => { setKeyboardOpen(true); }; diff --git a/src/views/login/pronote/PronoteQRCode.tsx b/src/views/login/pronote/PronoteQRCode.tsx index fbe2d368f..45d5ae5d8 100644 --- a/src/views/login/pronote/PronoteQRCode.tsx +++ b/src/views/login/pronote/PronoteQRCode.tsx @@ -40,8 +40,8 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { const theme = useTheme(); const insets = useSafeAreaInsets(); - const createStoredAccount = useAccounts(store => store.create); - const switchTo = useCurrentAccount(store => store.switchTo); + const createStoredAccount = useAccounts((store) => store.create); + const switchTo = useCurrentAccount((store) => store.switchTo); const { colors } = theme; const [hasPermission, setHasPermission] = useState(null); diff --git a/src/views/login/pronote/PronoteV6Import.tsx b/src/views/login/pronote/PronoteV6Import.tsx index 26641068f..4c6ef0f5e 100644 --- a/src/views/login/pronote/PronoteV6Import.tsx +++ b/src/views/login/pronote/PronoteV6Import.tsx @@ -16,8 +16,8 @@ import { useAlert } from "@/providers/AlertProvider"; const PronoteV6Import: Screen<"PronoteV6Import"> = ({ route, navigation }) => { const { data } = route.params; - const createStoredAccount = useAccounts(store => store.create); - const switchTo = useCurrentAccount(store => store.switchTo); + const createStoredAccount = useAccounts((store) => store.create); + const switchTo = useCurrentAccount((store) => store.switchTo); const [loading, setLoading] = React.useState(false); diff --git a/src/views/login/skolengo/SkolengoInstanceSelector.tsx b/src/views/login/skolengo/SkolengoInstanceSelector.tsx index 16a59278f..f258c66f5 100644 --- a/src/views/login/skolengo/SkolengoInstanceSelector.tsx +++ b/src/views/login/skolengo/SkolengoInstanceSelector.tsx @@ -84,7 +84,7 @@ const SkolengoInstanceSelector: Screen<"SkolengoInstanceSelector"> = ({ const newInstances = await Skolengo.searchSchool({ text: search }, 20); // On limite à 20 instances. newInstances.splice(20); - if(_debSearch !== debouncedSearch) return; // if the search has changed, we don't update the instances. + if (_debSearch !== debouncedSearch) return; // if the search has changed, we don't update the instances. setInstances(newInstances); setHasSearched(true); } diff --git a/src/views/login/skolengo/SkolengoWebview.tsx b/src/views/login/skolengo/SkolengoWebview.tsx index 9f1bbdea8..dc120d5d8 100644 --- a/src/views/login/skolengo/SkolengoWebview.tsx +++ b/src/views/login/skolengo/SkolengoWebview.tsx @@ -11,9 +11,7 @@ import { import { WebView } from "react-native-webview"; import type { Screen } from "@/router/helpers/types"; -import { - SafeAreaView -} from "react-native-safe-area-context"; +import { SafeAreaView } from "react-native-safe-area-context"; import { useTheme } from "@react-navigation/native"; import MaskStars from "@/components/FirstInstallation/MaskStars"; @@ -49,7 +47,7 @@ const SkolengoWebview: Screen<"SkolengoWebview"> = ({ route, navigation }) => { useEffect(() => { getSkolengoURL(route.params.school).then((skourl) => { - if(skourl) { + if (skourl) { setPageUrl(skourl.url); setDiscovery(skourl.discovery); } @@ -193,7 +191,7 @@ const SkolengoWebview: Screen<"SkolengoWebview"> = ({ route, navigation }) => { code: code, redirectUri: REDIRECT_URI, }, - discovery! + discovery ).then(async (token) => { setLoginStep("Initialisation du compte..."); const newToken = authTokenToSkolengoTokenSet(token); @@ -205,7 +203,7 @@ const SkolengoWebview: Screen<"SkolengoWebview"> = ({ route, navigation }) => { const skolengoAccount = await getSkolengoAccount({ school: route.params.school, tokenSet: newToken, - discovery: discovery! + discovery }); setLoginStep("Finalisation du compte..."); @@ -226,7 +224,7 @@ const SkolengoWebview: Screen<"SkolengoWebview"> = ({ route, navigation }) => { return true; }} - source={{ uri: pageUrl || "" }} + source={{ uri: pageUrl ?? "" }} setSupportMultipleWindows={false} originWhitelist={["http://*", "https://*", "skoapp-prod://*"]} incognito={true} // Prevent to keep cookies on webview load. @@ -281,7 +279,7 @@ const getSkolengoURL = async (school: School) => { // eslint-disable-next-line const loginSkolengoWorkflow = async (school: School) => { const skolengoUrl = await getSkolengoURL(school); - if(!skolengoUrl) return; + if (!skolengoUrl) return; const res = await skolengoUrl.authRes.promptAsync(skolengoUrl.discovery, { url: skolengoUrl.url, }); diff --git a/src/views/settings/ExternalAccount/ARD.tsx b/src/views/settings/ExternalAccount/ARD.tsx index 7a325436f..7a0026299 100644 --- a/src/views/settings/ExternalAccount/ARD.tsx +++ b/src/views/settings/ExternalAccount/ARD.tsx @@ -29,8 +29,8 @@ export async function detectMealPrice (account: Client): Promise } const ExternalArdLogin: Screen<"ExternalArdLogin"> = ({ navigation }) => { - const linkExistingExternalAccount = useCurrentAccount(store => store.linkExistingExternalAccount); - const create = useAccounts(store => store.create); + const linkExistingExternalAccount = useCurrentAccount((store) => store.linkExistingExternalAccount); + const create = useAccounts((store) => store.create); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); diff --git a/src/views/settings/ExternalAccount/Alise.tsx b/src/views/settings/ExternalAccount/Alise.tsx index 4ba04a354..f90bfb1db 100644 --- a/src/views/settings/ExternalAccount/Alise.tsx +++ b/src/views/settings/ExternalAccount/Alise.tsx @@ -30,8 +30,8 @@ async function detectMealPrice (account: Client): Promise { const ExternalAliseLogin: Screen<"ExternalAliseLogin"> = ({ navigation }) => { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); - const linkExistingExternalAccount = useCurrentAccount(store => store.linkExistingExternalAccount); - const create = useAccounts(store => store.create); + const linkExistingExternalAccount = useCurrentAccount((store) => store.linkExistingExternalAccount); + const create = useAccounts((store) => store.create); const handleLogin = async (username: string, password: string, customFields: Record): Promise => { setLoading(true); diff --git a/src/views/settings/ExternalAccount/IzlyActivation.tsx b/src/views/settings/ExternalAccount/IzlyActivation.tsx index d7662ae2d..a6285d256 100644 --- a/src/views/settings/ExternalAccount/IzlyActivation.tsx +++ b/src/views/settings/ExternalAccount/IzlyActivation.tsx @@ -21,8 +21,8 @@ const IzlyActivation: Screen<"IzlyActivation"> = ({ navigation, route }) => { const { colors } = theme; const insets = useSafeAreaInsets(); - const linkExistingExternalAccount = useCurrentAccount(store => store.linkExistingExternalAccount); - const create = useAccounts(store => store.create); + const linkExistingExternalAccount = useCurrentAccount((store) => store.linkExistingExternalAccount); + const create = useAccounts((store) => store.create); const [loading, setLoading] = useState(false); const secret = route.params?.password; diff --git a/src/views/settings/ExternalAccount/PriceBeforeScan.tsx b/src/views/settings/ExternalAccount/PriceBeforeScan.tsx index a4452c7a2..6fa0e6572 100644 --- a/src/views/settings/ExternalAccount/PriceBeforeScan.tsx +++ b/src/views/settings/ExternalAccount/PriceBeforeScan.tsx @@ -14,8 +14,8 @@ const PriceBeforeScan: Screen<"PriceBeforeScan"> = ({ navigation, route }) => { const { accountID } = route.params; const { mainAccount, externalAccount } = useMemo(() => { - const mainAccount = accounts.find(acc => acc.localID === accountID && !acc.isExternal) as PrimaryAccount; - const externalAccount = accounts.find(acc => acc.localID === accountID || acc.linkedExternalLocalIDs?.includes(accountID)) as ExternalAccount; + const mainAccount = accounts.find((acc) => acc.localID === accountID && !acc.isExternal) as PrimaryAccount; + const externalAccount = accounts.find((acc) => acc.localID === accountID || acc.linkedExternalLocalIDs?.includes(accountID)) as ExternalAccount; return { mainAccount, externalAccount }; }, [accounts, accountID]); @@ -29,7 +29,7 @@ const PriceBeforeScan: Screen<"PriceBeforeScan"> = ({ navigation, route }) => { }, [mainAccount]); const getBarcodeFormat = useCallback((type: string): string => { - switch(type) { + switch (type) { case "org.gs1.EAN-8": return "EAN8"; case "org.gs1.EAN-13": diff --git a/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx b/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx index a22b06d77..6acbeff2a 100644 --- a/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx +++ b/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx @@ -13,7 +13,7 @@ const PriceDetectionOnboarding: Screen<"PriceDetectionOnboarding"> = ({ navigati const theme = useTheme(); const { colors } = theme; const insets = useSafeAreaInsets(); - const mutateProperty = useCurrentAccount(store => store.mutateProperty); + const mutateProperty = useCurrentAccount((store) => store.mutateProperty); const accountID = route.params?.accountID; return ( diff --git a/src/views/settings/ExternalAccount/PriceError.tsx b/src/views/settings/ExternalAccount/PriceError.tsx index f08a77fe1..bdab14f3a 100644 --- a/src/views/settings/ExternalAccount/PriceError.tsx +++ b/src/views/settings/ExternalAccount/PriceError.tsx @@ -13,7 +13,7 @@ import { useAlert } from "@/providers/AlertProvider"; const PriceError: Screen<"PriceError"> = ({ navigation, route }) => { const theme = useTheme(); const { colors } = theme; - const update = useAccounts(store => store.update); + const update = useAccounts((store) => store.update); const account = route.params?.account; const accountId = route.params?.accountId; diff --git a/src/views/settings/ExternalAccount/QrcodeScanner.tsx b/src/views/settings/ExternalAccount/QrcodeScanner.tsx index 4936b8acf..ccc5fa048 100644 --- a/src/views/settings/ExternalAccount/QrcodeScanner.tsx +++ b/src/views/settings/ExternalAccount/QrcodeScanner.tsx @@ -13,7 +13,7 @@ import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; const QrcodeScanner: Screen<"QrcodeScanner"> = ({ navigation, route }) => { const insets = useSafeAreaInsets(); - const update = useAccounts(store => store.update); + const update = useAccounts((store) => store.update); const accountID = route.params?.accountID; const [hasPermission, setHasPermission] = React.useState(null); const [scanned, setScanned] = React.useState(false); diff --git a/src/views/settings/ExternalAccount/SelectMethod.tsx b/src/views/settings/ExternalAccount/SelectMethod.tsx index f12eef41c..f9a6510fe 100644 --- a/src/views/settings/ExternalAccount/SelectMethod.tsx +++ b/src/views/settings/ExternalAccount/SelectMethod.tsx @@ -15,7 +15,7 @@ import { useCurrentAccount } from "@/stores/account"; const ExternalAccountSelectMethod: Screen<"ExternalAccountSelectMethod"> = ({ navigation, route }) => { const theme = useTheme(); const { colors } = theme; - const account = useCurrentAccount(store => store.account!); + const account = useCurrentAccount((store) => store.account!); return ( = ({ navigation, route }) => { +const ExternalAccountSelector: Screen<"ExternalAccountSelector"> = ({ navigation }) => { const { isOnline } = useOnlineStatus(); const { showAlert } = useAlert(); diff --git a/src/views/settings/ExternalAccount/Turboself.tsx b/src/views/settings/ExternalAccount/Turboself.tsx index 9bb062afa..46e005330 100644 --- a/src/views/settings/ExternalAccount/Turboself.tsx +++ b/src/views/settings/ExternalAccount/Turboself.tsx @@ -7,8 +7,8 @@ import LoginView from "@/components/Templates/LoginView"; import { Screen } from "@/router/helpers/types"; const ExternalTurboselfLogin: Screen<"ExternalTurboselfLogin"> = ({ navigation }) => { - const linkExistingExternalAccount = useCurrentAccount(store => store.linkExistingExternalAccount); - const create = useAccounts(store => store.create); + const linkExistingExternalAccount = useCurrentAccount((store) => store.linkExistingExternalAccount); + const create = useAccounts((store) => store.create); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); diff --git a/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx b/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx index 4533ebd5f..813b796d9 100644 --- a/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx +++ b/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx @@ -21,8 +21,8 @@ const TurboselfAccountSelector: Screen<"TurboselfAccountSelector"> = ({ navigati const { colors } = theme; const insets = useSafeAreaInsets(); - const linkExistingExternalAccount = useCurrentAccount(store => store.linkExistingExternalAccount); - const create = useAccounts(store => store.create); + const linkExistingExternalAccount = useCurrentAccount((store) => store.linkExistingExternalAccount); + const create = useAccounts((store) => store.create); const [loading, setLoading] = useState(false); const account = route.params.accounts; diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index 288654bb2..b8585d7df 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -59,10 +59,10 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { const theme = useTheme(); const { colors } = theme; const insets = useSafeAreaInsets(); - const account = useCurrentAccount(store => store.account!); + const account = useCurrentAccount((store) => store.account!); const [ addons, setAddons ] = useState>([]); const [devModeEnabled, setDevModeEnabled] = useState(false); - const defined = useFlagsStore(state => state.defined); + const defined = useFlagsStore((state) => state.defined); const [click, setClick] = useState(false); const { isTablet } = useScreenDimensions(); diff --git a/src/views/settings/SettingsAddons.tsx b/src/views/settings/SettingsAddons.tsx index 2c847c1b5..50e493538 100644 --- a/src/views/settings/SettingsAddons.tsx +++ b/src/views/settings/SettingsAddons.tsx @@ -58,7 +58,7 @@ const SettingsAddons: Screen<"SettingsAddons"> = () => { }); React.useEffect(() => { - get_addons_list().then(r => { + get_addons_list().then((r) => { setStorageAddons(r); }); }, []); diff --git a/src/views/settings/SettingsExternalServices.tsx b/src/views/settings/SettingsExternalServices.tsx index 30aa45069..35be94089 100644 --- a/src/views/settings/SettingsExternalServices.tsx +++ b/src/views/settings/SettingsExternalServices.tsx @@ -1,7 +1,6 @@ import React from "react"; import { ScrollView, View } from "react-native"; import type { Screen } from "@/router/helpers/types"; -import { useTheme } from "@react-navigation/native"; import { GraduationCap, Utensils, BookOpen, School, BookmarkMinus, Compass, Check, Trash2, BadgeInfo } from "lucide-react-native"; import ExternalServicesContainerCard from "@/components/Settings/ExternalServicesContainerCard"; import { @@ -34,7 +33,6 @@ const serviceConfig = { const SettingsExternalServices: Screen<"SettingsExternalServices"> = ({ navigation, }) => { - const theme = useTheme(); const accounts = useAccounts((state) => state.accounts); const removeAccount = useAccounts((state) => state.remove); const { showAlert } = useAlert(); @@ -88,7 +86,7 @@ const SettingsExternalServices: Screen<"SettingsExternalServices"> = ({ paddingHorizontal: 16, }} > - + diff --git a/src/views/settings/SettingsFlags.tsx b/src/views/settings/SettingsFlags.tsx index 85673ef08..74158b9fe 100644 --- a/src/views/settings/SettingsFlags.tsx +++ b/src/views/settings/SettingsFlags.tsx @@ -12,8 +12,8 @@ import ResponsiveTextInput from "@/components/FirstInstallation/ResponsiveTextIn const SettingsFlags: Screen<"SettingsFlags"> = ({ navigation }) => { const { flags, remove, set } = useFlagsStore(); - const account = useCurrentAccount(store => store.account!); - const externals = useCurrentAccount(store => store.linkedAccounts); + const account = useCurrentAccount((store) => store.account!); + const externals = useCurrentAccount((store) => store.linkedAccounts); const { colors } = useTheme(); const textInputRef = useRef(null); diff --git a/src/views/settings/SettingsIcons.tsx b/src/views/settings/SettingsIcons.tsx index 66b119367..90eae80b1 100644 --- a/src/views/settings/SettingsIcons.tsx +++ b/src/views/settings/SettingsIcons.tsx @@ -85,9 +85,7 @@ const SettingsIcons: Screen<"SettingsIcons"> = ({ navigation }) => { paddingTop: 0, }} > - + {Object.keys(data).map((key, index) => ( @@ -172,7 +170,7 @@ const SettingsIcons: Screen<"SettingsIcons"> = ({ navigation }) => { }); }} > - + ) : null} diff --git a/src/views/settings/SettingsMagic.tsx b/src/views/settings/SettingsMagic.tsx index a9eda3b9e..261cd2eb2 100644 --- a/src/views/settings/SettingsMagic.tsx +++ b/src/views/settings/SettingsMagic.tsx @@ -1,6 +1,5 @@ import React from "react"; import { ScrollView, Switch } from "react-native"; -import { useTheme } from "@react-navigation/native"; import type { Screen } from "@/router/helpers/types"; // Ensure this file contains valid regex patterns import MagicContainerCard from "@/components/Settings/MagicContainerCard"; @@ -8,10 +7,9 @@ import { NativeIconGradient, NativeItem, NativeList, NativeListHeader, NativeTex import { ArrowUpNarrowWide, BookDashed } from "lucide-react-native"; import { useCurrentAccount } from "@/stores/account"; -const SettingsMagic: Screen<"SettingsMagic"> = ({ navigation }) => { - const theme = useTheme(); - const account = useCurrentAccount(store => store.account); - const mutateProperty = useCurrentAccount(store => store.mutateProperty); +const SettingsMagic: Screen<"SettingsMagic"> = () => { + const account = useCurrentAccount((store) => store.account); + const mutateProperty = useCurrentAccount((store) => store.mutateProperty); return ( = ({ navigation }) => { paddingHorizontal: 15, }} > - + diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index 37d1af806..beddbc9cd 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -23,11 +23,11 @@ import ResponsiveTextInput from "@/components/FirstInstallation/ResponsiveTextIn const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => { const theme = useTheme(); - const toggleMultiService = useMultiService(store => store.toggleEnabledState); - const multiServiceEnabled = useMultiService(store => store.enabled); - const multiServiceSpaces = useMultiService(store => store.spaces); - const createMultiServiceSpace = useMultiService(store => store.create); - const deleteMultiServiceSpace = useMultiService(store => store.remove); + const toggleMultiService = useMultiService((store) => store.toggleEnabledState); + const multiServiceEnabled = useMultiService((store) => store.enabled); + const multiServiceSpaces = useMultiService((store) => store.spaces); + const createMultiServiceSpace = useMultiService((store) => store.create); + const deleteMultiServiceSpace = useMultiService((store) => store.remove); const accounts = useAccounts(); const currentAccount = useCurrentAccount(); @@ -97,7 +97,7 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => personalization: { profilePictureB64: selectedImage || undefined, tabs: defaultTabs - .filter(current => defaultSpaceTabs.includes(current.tab)) + .filter((current) => defaultSpaceTabs.includes(current.tab)) .map((tab, index) => ({ name: tab.tab, enabled: index <= 4 @@ -145,7 +145,7 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => paddingBottom: 25 }} > - + diff --git a/src/views/settings/SettingsMultiServiceSpace.tsx b/src/views/settings/SettingsMultiServiceSpace.tsx index 60a85d248..78e766a4f 100644 --- a/src/views/settings/SettingsMultiServiceSpace.tsx +++ b/src/views/settings/SettingsMultiServiceSpace.tsx @@ -34,13 +34,13 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga const insets = useSafeAreaInsets(); const space = route.params.space; const accounts = useAccounts(); - const availableAccounts = accounts.accounts.filter(account => !account.isExternal && !(account.service == AccountService.PapillonMultiService)); - const deleteMultiServiceSpace = useMultiService(store => store.remove); - const updateMultiServiceSpace = useMultiService(store => store.update); - const setMultiServiceSpaceAccountFeature = useMultiService(store => store.setFeatureAccount); + const availableAccounts = accounts.accounts.filter((account) => !account.isExternal && !(account.service == AccountService.PapillonMultiService)); + const deleteMultiServiceSpace = useMultiService((store) => store.remove); + const updateMultiServiceSpace = useMultiService((store) => store.update); + const setMultiServiceSpaceAccountFeature = useMultiService((store) => store.setFeatureAccount); const { playHaptics } = useSoundHapticsWrapper(); - const linkedAccount = accounts.accounts.find(account => account.localID === space.accountLocalID) as PrimaryAccount | undefined; + const linkedAccount = accounts.accounts.find((account) => account.localID === space.accountLocalID) as PrimaryAccount | undefined; const firstNameRef = useRef(null); const lastNameRef = useRef(null); @@ -110,7 +110,7 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga }; const openAccountSelector = (feature: MultiServiceFeature, name: string) => { - setSelectedAccount(availableAccounts.find(account => account.localID === space.featuresServices[feature]) || null); + setSelectedAccount(availableAccounts.find((account) => account.localID === space.featuresServices[feature]) || null); setFeatureSelection(feature); setFeatureSelectionName(name); setAccountSelectorOpened(true); @@ -126,11 +126,11 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga linkedAccountsIds = linkedAccountsIds.filter((value, index) => linkedAccountsIds.indexOf(value) === index); // Remove duplicates } else { // If feature's service has been removed and service is not assigned to other feature, we remove it from "associatedAccountsLocalIDs" - const accountNoMoreUsed = !Object.keys(space.featuresServices).some(key => + const accountNoMoreUsed = !Object.keys(space.featuresServices).some((key) => (space.featuresServices[key as MultiServiceFeature] == currentSelectedAccountID && !((key as MultiServiceFeature) === feature)) ); if (accountNoMoreUsed) { - linkedAccountsIds = linkedAccountsIds.filter(localID => localID != currentSelectedAccountID); + linkedAccountsIds = linkedAccountsIds.filter((localID) => localID != currentSelectedAccountID); } } // @ts-expect-error @@ -392,12 +392,12 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga > {feature.name} - {accounts.accounts.find(account => + {accounts.accounts.find((account) => account.localID === space.featuresServices[feature.feature]) ? ( account.localID === space.featuresServices[feature.feature]) as PrimaryAccount} + (account) => account.localID === space.featuresServices[feature.feature]) as PrimaryAccount} endCheckMark={false} additionalStyles={{ paddingStart: 10, @@ -420,7 +420,7 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga ))} - setAccountSelectorOpened(opened)}> + setAccountSelectorOpened(opened)}> = ({ navigation }) => { const theme = useTheme(); const insets = useSafeAreaInsets(); - const account = useCurrentAccount(store => store.account!); - const mutateProperty = useCurrentAccount(store => store.mutateProperty); + const account = useCurrentAccount((store) => store.account!); + const mutateProperty = useCurrentAccount((store) => store.mutateProperty); const oldFirstName = account.studentName?.first ?? ""; const oldLastName = account.studentName?.last ?? ""; diff --git a/src/views/settings/SettingsSubjects.tsx b/src/views/settings/SettingsSubjects.tsx index aff7268f5..ff3140bda 100644 --- a/src/views/settings/SettingsSubjects.tsx +++ b/src/views/settings/SettingsSubjects.tsx @@ -32,8 +32,8 @@ const MemoizedSubjectContainerCard = React.memo(SubjectContainerCard); type Item = [key: string, value: { color: string; pretty: string; emoji: string; }]; const SettingsSubjects: Screen<"SettingsSubjects"> = ({ navigation }) => { - const account = useCurrentAccount(store => store.account!); - const mutateProperty = useCurrentAccount(store => store.mutateProperty); + const account = useCurrentAccount((store) => store.account!); + const mutateProperty = useCurrentAccount((store) => store.mutateProperty); const insets = useSafeAreaInsets(); const colors = useTheme().colors; @@ -64,8 +64,8 @@ const SettingsSubjects: Screen<"SettingsSubjects"> = ({ navigation }) => { }, [selectedSubject]); const updateSubject = useCallback((subjectKey: string, updates: Partial) => { - setSubjects(prevSubjects => - prevSubjects.map(subject => + setSubjects((prevSubjects) => + prevSubjects.map((subject) => subject[0] === subjectKey ? [subject[0], { ...subject[1], ...updates }] : subject ) ); @@ -85,8 +85,8 @@ const SettingsSubjects: Screen<"SettingsSubjects"> = ({ navigation }) => { const handleSubjectTitleBlur = useCallback(() => { if (selectedSubject && currentTitle.trim() !== "") { - setLocalSubjects(prevSubjects => - prevSubjects.map(subject => + setLocalSubjects((prevSubjects) => + prevSubjects.map((subject) => subject[0] === selectedSubject[0] ? [subject[0], { ...subject[1], pretty: currentTitle }] : subject ) ); @@ -96,15 +96,15 @@ const SettingsSubjects: Screen<"SettingsSubjects"> = ({ navigation }) => { const handleSubjectEmojiChange = useCallback((subjectKey: string, newEmoji: string) => { let emoji = ""; - if(newEmoji.length >= 1) { + if (newEmoji.length >= 1) { let regexp = /((\ud83c[\udde6-\uddff]){2}|([#*0-9]\u20e3)|(\u00a9|\u00ae|[\u2000-\u3300]|[\ud83c-\ud83e][\ud000-\udfff])((\ud83c[\udffb-\udfff])?(\ud83e[\uddb0-\uddb3])?(\ufe0f?\u200d([\u2000-\u3300]|[\ud83c-\ud83e][\ud000-\udfff])\ufe0f?)?)*)/g; const emojiMatch = newEmoji.match(regexp); - if(emojiMatch) { + if (emojiMatch) { emoji = emojiMatch[emojiMatch.length - 1]; } } - setLocalSubjects(prevSubjects => - prevSubjects.map(subject => + setLocalSubjects((prevSubjects) => + prevSubjects.map((subject) => subject[0] === subjectKey ? [subject[0], { ...subject[1], emoji }] : subject ) ); @@ -113,8 +113,8 @@ const SettingsSubjects: Screen<"SettingsSubjects"> = ({ navigation }) => { }, [debouncedUpdateSubject]); const handleSubjectColorChange = useCallback((subjectKey: string, newColor: string) => { - setLocalSubjects(prevSubjects => - prevSubjects.map(subject => + setLocalSubjects((prevSubjects) => + prevSubjects.map((subject) => subject[0] === subjectKey ? [subject[0], { ...subject[1], color: newColor }] : subject ) ); diff --git a/src/views/settings/SettingsTabs.tsx b/src/views/settings/SettingsTabs.tsx index fcb830d59..70201a0bb 100644 --- a/src/views/settings/SettingsTabs.tsx +++ b/src/views/settings/SettingsTabs.tsx @@ -429,7 +429,7 @@ const SettingsTabs = () => { { - if (!item.enabled && tabs.filter(t => t.enabled).length === 5) { + if (!item.enabled && tabs.filter((t) => t.enabled).length === 5) { showAlert({ title: "Information", message: "Tu ne peux pas ajouter plus de 5 onglets sur la page d'accueil.", diff --git a/src/views/welcome/AccountSelector.old.tsx b/src/views/welcome/AccountSelector.old.tsx deleted file mode 100644 index fd2fa2dd5..000000000 --- a/src/views/welcome/AccountSelector.old.tsx +++ /dev/null @@ -1,481 +0,0 @@ -import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; -import { useAccounts, useCurrentAccount } from "@/stores/account"; -import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; -import { useIsFocused, useTheme } from "@react-navigation/native"; -import { BadgeHelp, PlusIcon, Trash2, Undo2 } from "lucide-react-native"; -import { useEffect, useState } from "react"; -import { - Dimensions, - Image, - RefreshControl, - StatusBar, - Text, - TouchableHighlight, - View -} from "react-native"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; -import * as SplashScreen from "expo-splash-screen"; - -import PapillonAvatar from "@/components/Global/PapillonAvatar"; - -import PackageJSON from "@/../package.json"; - -import Reanimated, { - Extrapolation, - FadeInDown, - FadeOut, - interpolate, - LinearTransition, - useAnimatedRef, useAnimatedStyle, - useScrollViewOffset, - ZoomIn, -} from "react-native-reanimated"; -import { LinearGradient } from "expo-linear-gradient"; -import { animPapillon } from "@/utils/ui/animations"; -import { Screen } from "@/router/helpers/types"; -import PapillonSpinner from "@/components/Global/PapillonSpinner"; -import { PressableScale } from "react-native-pressable-scale"; - -import datasets from "@/consts/datasets.json"; -import { PrimaryAccount } from "@/stores/account/types"; -import { useAlert } from "@/providers/AlertProvider"; - - -// https://raw.githubusercontent.com/PapillonApp/datasets/refs/heads/main/illustrations/index.json -type Illustration = { - name: string - image: string -}; - -const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { - const theme = useTheme(); - const insets = useSafeAreaInsets(); - - const isFocused = useIsFocused(); - - const currentAccount = useCurrentAccount((store) => store.account); - const switchTo = useCurrentAccount((store) => store.switchTo); - const removeAccount = useAccounts((store) => store.remove); - - const lastOpenedAccountID = useAccounts((store) => store.lastOpenedAccountID); - const accounts = useAccounts((store) => store.accounts); - - const [loading, setLoading] = useState(null); - - const [downloadedIllustrations, setDownloadedIllustrations] = useState(false); - const [illustration, setIllustration] = useState(undefined); - const [illustrationLoaded, setIllustrationLoaded] = useState(false); - - const scrollRef = useAnimatedRef(); - const scrollOffset = useScrollViewOffset(scrollRef); - const headerRatioHeight = 250; - let headerAnimatedStyle = useAnimatedStyle(() => ({ - top: interpolate( - scrollOffset.value, - [headerRatioHeight - 1000, 0, headerRatioHeight - (insets.top + 64), headerRatioHeight + 1000], - [headerRatioHeight - 1000, 0, 0, headerRatioHeight + 1000 - (insets.top + 64)], - Extrapolation.CLAMP - ), - })); - let headerOpacity = useAnimatedStyle(() => ({ - opacity: interpolate(scrollOffset.value, [0, 100], [0, 0.75], Extrapolation.CLAMP), - })); - - const { showAlert } = useAlert(); - - useEffect(() => { - if(!downloadedIllustrations) { - updateIllustration(); - } - }, []); - - const updateIllustration = async () => { - fetch(datasets["illustrations"]) - .then((response) => response.json()) - .then((data) => { - setDownloadedIllustrations(true); - // select a random illustration - setIllustration(data[Math.floor(Math.random() * data.length)]); - }); - }; - - useEffect(() => { - void async function () { - if (!useAccounts.persist.hasHydrated()) return; - - // If there are no accounts, redirect the user to the first installation page. - if (accounts.filter((account) => !account.isExternal).length === 0) { - // Use the `reset` method to clear the navigation stack. - navigation.reset({ - index: 0, - routes: [{ name: "FirstInstallation" }], - }); - } - else { - const selectedAccount = - accounts.find((account) => account.localID === lastOpenedAccountID) as PrimaryAccount - ?? accounts.find((account) => !account.isExternal) as PrimaryAccount; - switchTo(selectedAccount); - } - }(); - }, [accounts]); - - useEffect(() => { - if (currentAccount?.localID) { - navigation.reset({ - index: 0, - routes: [{ name: "AccountStack" }], - }); - - SplashScreen.hideAsync(); - } - }, [currentAccount]); - - if (!accounts) return null; - - return ( - - - - navigation.navigate("ServiceSelector")} - > - - - - Ajouter un compte - - - - navigation.navigate("DevMenu")} - delayLongPress={2000} - > - - ver. {PackageJSON.version} - - - - - { - updateIllustration(); - }} - progressViewOffset={headerRatioHeight} - /> - } - contentContainerStyle={{ - minHeight: Dimensions.get("window").height, - }} - > - {isFocused && ( - - )} - - - {!illustrationLoaded && - - } - - setIllustrationLoaded(true)} - /> - - - - - - - Bienvenue sur Papillon ! - - - - Sélectionne un compte pour commencer. - - - - - {accounts.filter((account) => !account.isExternal).length > 0 && ( - - - - {accounts.map((account, index) => { - return !account.isExternal && ( - - } - /> - } - trailing={ - loading === account.localID && ( - - ) - } - onLongPress={async () => { - // delete account - showAlert({ - title: "Supprimer le compte", - message: "Es-tu sûr de vouloir supprimer ce compte ?", - icon: , - actions: [ - { - title: "Annuler", - icon: , - primary: true, - }, - { - title: "Supprimer", - icon: , - onPress: () => { - // setTimeout pour laisser le temps à la précédente alerte de s'enlever - setTimeout(() => { - showAlert({ - title: "Es-tu sûr ?", - message: `Veux-tu supprimer définitivement ${account.studentName.first} ${account.studentName.last} ?`, - icon: , - actions: [ - { - title: "Annuler", - icon: , - primary: false, - }, - { - title: "Supprimer", - icon: , - onPress: () => removeAccount(account.localID), - danger: true, - delayDisable: 5, - } - ] - }); - }, 500); - }, - danger: true, - } - ] - }); - }} - onPress={async () => { - if (currentAccount?.localID !== account.localID) { - setLoading(account.localID); - await switchTo(account); - setLoading(null); - } - - navigation.reset({ - index: 0, - routes: [{ name: "AccountStack" }], - }); - }} - > - - - {account.studentName.first} {account.studentName.last} - - - {account.schoolName ?? account.identityProvider?.name ?? "Compte local" - } - - - - ); - })} - - - )} - - - - - ); -}; - -export default AccountSelector; diff --git a/src/views/welcome/ChangelogScreen.tsx b/src/views/welcome/ChangelogScreen.tsx index ebb5097c7..9c56de88a 100644 --- a/src/views/welcome/ChangelogScreen.tsx +++ b/src/views/welcome/ChangelogScreen.tsx @@ -57,7 +57,7 @@ const ChangelogScreen: Screen<"ChangelogScreen"> = ({ route, navigation }) => { }; useEffect(() => { - if(!changelog) { + if (!changelog) { setLoading(true); fetch(changelogURL + "#update=" + uuid()) // #TODO : remove, it's for development .then((response) => response.json()) @@ -317,10 +317,10 @@ const ChangelogFeature: React.FC<{ feature: Feature, navigation: any, theme: any { - if(feature.href) { + if (feature.href) { Linking.openURL(feature.href); } - else if(feature.navigation) { + else if (feature.navigation) { try { navigation.goBack(); navigation.navigate(feature.navigation); diff --git a/src/views/welcome/ColorSelector.tsx b/src/views/welcome/ColorSelector.tsx index a55d42f9a..f42e5ecd2 100644 --- a/src/views/welcome/ColorSelector.tsx +++ b/src/views/welcome/ColorSelector.tsx @@ -24,8 +24,8 @@ const ColorSelector: Screen<"ColorSelector"> = ({ route, navigation }) => { const theme = useTheme(); const { colors } = theme; const insets = useSafeAreaInsets(); - const account = useCurrentAccount(store => store.account); - const mutateProperty = useCurrentAccount(store => store.mutateProperty); + const account = useCurrentAccount((store) => store.account); + const mutateProperty = useCurrentAccount((store) => store.mutateProperty); const settings = route.params?.settings || false; const { playHaptics, playSound } = useSoundHapticsWrapper(); const LEson003 = require("@/../assets/sound/click_003.wav"); @@ -187,7 +187,7 @@ const ColorSelector: Screen<"ColorSelector"> = ({ route, navigation }) => { value={settings ? "Sauvegarder" : !hasProfilePic ? "Continuer" : "Finaliser"} onPress={async () => { if (!settings) { - if(!hasProfilePic) { + if (!hasProfilePic) { navigation.navigate("ProfilePic"); return; } diff --git a/src/widgets/Components/LastGrade.tsx b/src/widgets/Components/LastGrade.tsx index cba30aa8a..09ad43506 100644 --- a/src/widgets/Components/LastGrade.tsx +++ b/src/widgets/Components/LastGrade.tsx @@ -28,10 +28,10 @@ const LastGradeWidget = forwardRef(({ let lastPeriod = defaultPeriod; // find last period with grades - if(!grades[lastPeriod] || grades[lastPeriod] && grades[lastPeriod].length === 0) { + if (!grades[lastPeriod] || grades[lastPeriod] && grades[lastPeriod].length === 0) { const periods = Object.keys(grades); - for(let i = periods.length - 1; i >= 0; i--) { - if(grades[periods[i]].length > 0) { + for (let i = periods.length - 1; i >= 0; i--) { + if (grades[periods[i]].length > 0) { lastPeriod = periods[i]; break; } diff --git a/src/widgets/Components/NextCourse.tsx b/src/widgets/Components/NextCourse.tsx index 851e660a1..415c0c641 100644 --- a/src/widgets/Components/NextCourse.tsx +++ b/src/widgets/Components/NextCourse.tsx @@ -16,8 +16,8 @@ import { updateTimetableForWeekInCache } from "@/services/timetable"; const lz = (num: number) => (num < 10 ? `0${num}` : num); const NextCourseWidget = forwardRef(({ hidden, setHidden, loading, setLoading }: WidgetProps, ref) => { - const account = useCurrentAccount(store => store.account!); - const timetables = useTimetableStore(store => store.timetables); + const account = useCurrentAccount((store) => store.account!); + const timetables = useTimetableStore((store) => store.timetables); const [nextCourse, setNextCourse] = useState(null); const [widgetTitle, setWidgetTitle] = useState("Prochain cours"); @@ -52,7 +52,7 @@ const NextCourseWidget = forwardRef(({ hidden, setHidden, loading, setLoading }: const weekCourses = timetables[currentWeekNumber]; let updatedNextCourse = weekCourses - .filter(c => c.endTimestamp > today && c.status !== TimetableClassStatus.CANCELED) + .filter((c) => c.endTimestamp > today && c.status !== TimetableClassStatus.CANCELED) .sort((a, b) => a.startTimestamp - b.startTimestamp)[0]; setNextCourse(updatedNextCourse); diff --git a/src/widgets/Components/RestaurantBalance.tsx b/src/widgets/Components/RestaurantBalance.tsx index c7cf3a51c..e611a6a68 100644 --- a/src/widgets/Components/RestaurantBalance.tsx +++ b/src/widgets/Components/RestaurantBalance.tsx @@ -18,7 +18,7 @@ const RestaurantBalanceWidget = forwardRef(({ const theme = useTheme(); const { colors } = theme; - const linkedAccounts = useCurrentAccount(store => store.linkedAccounts); + const linkedAccounts = useCurrentAccount((store) => store.linkedAccounts); const [balances, setBalances] = useState(null); const [currentBalanceIndex, setCurrentBalanceIndex] = useState(0); @@ -37,8 +37,8 @@ const RestaurantBalanceWidget = forwardRef(({ balances.push(...balance); } } - setBalances(balances.filter(balance => balance.label.toLowerCase() !== "cafetaria")); - setHidden(balances.length === 0 || balances.every(balance => balance.remaining === 0)); + setBalances(balances.filter((balance) => balance.label.toLowerCase() !== "cafetaria")); + setHidden(balances.length === 0 || balances.every((balance) => balance.remaining === 0)); setLoading(false); }(); }, [linkedAccounts, setHidden]); diff --git a/src/widgets/Components/RestaurantQRCode.tsx b/src/widgets/Components/RestaurantQRCode.tsx index a2cb27974..ba4e9a302 100644 --- a/src/widgets/Components/RestaurantQRCode.tsx +++ b/src/widgets/Components/RestaurantQRCode.tsx @@ -27,7 +27,7 @@ const RestaurantQRCodeWidget = forwardRef(({ const theme = useTheme(); const { colors } = theme; - const linkedAccounts = useCurrentAccount(store => store.linkedAccounts); + const linkedAccounts = useCurrentAccount((store) => store.linkedAccounts); const navigation = useNavigation(); const [allCards, setAllCards] = useState | null>(null); const [currentCardIndex, setCurrentCardIndex] = useState(0); @@ -49,7 +49,7 @@ const RestaurantQRCodeWidget = forwardRef(({ const accountPromises = linkedAccounts.map(async (account) => { try { const [cardnumber] = await Promise.all([ - qrcodeFromExternal(account).catch(err => { + qrcodeFromExternal(account).catch((err) => { warn(`Error fetching QR code for account ${account.username}:` + err, "RestaurantQRCodeWidget/qrcodeFromExternal"); return "0"; }), @@ -74,7 +74,7 @@ const RestaurantQRCodeWidget = forwardRef(({ await Promise.all(accountPromises); setAllCards(newCards); - setHidden(!(allCards?.some(card => card.cardnumber) && currentHour >= 11 && currentHour <= 14)); + setHidden(!(allCards?.some((card) => card.cardnumber) && currentHour >= 11 && currentHour <= 14)); setLoading(false); }(); }, [linkedAccounts, setHidden]); From 2f3dc20f24a268ccc769bc7dc66121cb327b5640 Mon Sep 17 00:00:00 2001 From: ggkervran <73659505+Gabriel29306@users.noreply.github.com> Date: Wed, 19 Mar 2025 21:25:23 +0100 Subject: [PATCH 0947/1144] fix(vscode): oups --- .vscode/settings.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index b92a4c2b5..f850d1685 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,9 +11,5 @@ "android", "lint" ], - "typescript.tsdk": "node_modules/typescript/lib", - "sonarlint.connectedMode.project": { - "connectionId": "gabriel29306", - "projectKey": "Gabriel29306_PapillonV7" - } + "typescript.tsdk": "node_modules/typescript/lib" } From e0414f50ff9c0be067af0ee414d62f896bc37539 Mon Sep 17 00:00:00 2001 From: TinAD17tin <163759571+TinAD17tin@users.noreply.github.com> Date: Thu, 20 Mar 2025 07:02:49 +0100 Subject: [PATCH 0948/1144] passage de price en euros --- src/services/ard/balance.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/ard/balance.ts b/src/services/ard/balance.ts index cd2893638..f60fb1202 100644 --- a/src/services/ard/balance.ts +++ b/src/services/ard/balance.ts @@ -11,8 +11,8 @@ export const balance = async (account: ARDAccount): Promise => { amount: wallet.walletAmount / 100, currency: "€", remaining: wallet.walletName.toLowerCase() !== "cafetaria" ? Math.floor((wallet.walletAmount / mealPrice!)) : null, - price: mealPrice!, + price: mealPrice! / 100, label: wallet.walletName[0].toUpperCase() + wallet.walletName.slice(1).toLowerCase() })).reverse(); -}; \ No newline at end of file +}; From 36e3097da35b811045ea260a0e3d1d0f45023ff8 Mon Sep 17 00:00:00 2001 From: TinAD17tin <163759571+TinAD17tin@users.noreply.github.com> Date: Thu, 20 Mar 2025 07:04:44 +0100 Subject: [PATCH 0949/1144] Passage de price en euros --- src/services/turboself/balance.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/turboself/balance.ts b/src/services/turboself/balance.ts index 6fe25db29..705c832a5 100644 --- a/src/services/turboself/balance.ts +++ b/src/services/turboself/balance.ts @@ -12,9 +12,9 @@ export const getBalance = async (account: TurboselfAccount): Promise amount: balance.estimatedAmount / 100, currency: currencySymbol ?? "€", remaining: Math.floor(balance.estimatedAmount / (lunchPrice ?? 0)), - price: lunchPrice ?? 0, + price: (lunchPrice ?? 0) / 100, label: balance.label }); } return result; -}; \ No newline at end of file +}; From 32095152e9d4a3c509209fa3d9f83eacc33c9d56 Mon Sep 17 00:00:00 2001 From: Mikkel ALMONTE--RINGAUD Date: Thu, 20 Mar 2025 08:51:10 +0100 Subject: [PATCH 0950/1144] chore(ecoledirecte): bump pawdirecte --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 24959ff03..11eaacaf0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -74,7 +74,7 @@ "lucide-react-native": "^0.378.0", "notifee": "^0.0.1", "openid-client": "^5.7.0", - "pawdirecte": "^1.8.1", + "pawdirecte": "^1.8.2", "pawnilim": "^0.2.0", "pawnote": "^1.4.1", "pawrd": "^0.6.1", @@ -15086,9 +15086,9 @@ } }, "node_modules/pawdirecte": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/pawdirecte/-/pawdirecte-1.8.1.tgz", - "integrity": "sha512-m8sJ5FhcQ2Ht1/25TiAZT3pKuyoNv3BV3rHpgTqHUB38izEzhgepp9c/cAh1rVF6b2Tz44JdGut6CrcpV41oUQ==", + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/pawdirecte/-/pawdirecte-1.8.2.tgz", + "integrity": "sha512-qX3kS0h11xReBVMMU14x8n2E6O+9oHzR75YVrTEsTg1nO3nHHqIjgPRFs5OyVKJL8cMICIf62Pu3/ZA9kQ2klg==", "license": "GPL-3.0-or-later", "dependencies": { "@literate.ink/utilities": "1.0.0-11713907881.1", diff --git a/package.json b/package.json index 02430a581..04e7f0cb7 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "lucide-react-native": "^0.378.0", "notifee": "^0.0.1", "openid-client": "^5.7.0", - "pawdirecte": "^1.8.1", + "pawdirecte": "^1.8.2", "pawnilim": "^0.2.0", "pawnote": "^1.4.1", "pawrd": "^0.6.1", From b74b36f51fdb787288bbbd23bce099aebdb90a02 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Thu, 20 Mar 2025 12:55:33 +0100 Subject: [PATCH 0951/1144] =?UTF-8?q?fix:=20Changement=20de=20couleur=20de?= =?UTF-8?q?=20l'ic=C3=B4ne=20en=20fonction=20de=20l'=C3=A9tat=20du=20modal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Home/AccountSwitcher.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Home/AccountSwitcher.tsx b/src/components/Home/AccountSwitcher.tsx index 39a97dd08..5b723cd72 100644 --- a/src/components/Home/AccountSwitcher.tsx +++ b/src/components/Home/AccountSwitcher.tsx @@ -168,6 +168,7 @@ const AccountSwitcher: React.FC<{ size={24} strokeWidth={2.3} style={iconAnimatedStyle} + color={modalOpen && !opened ? colors.text : "#FFF"} /> From 4a22a8489c4f27caf7e3bfb55f811e3ffc52b1e1 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Thu, 20 Mar 2025 12:58:15 +0100 Subject: [PATCH 0952/1144] =?UTF-8?q?refactor:=20simplification=20de=20l'a?= =?UTF-8?q?ppel=20=C3=A0=20la=20fonction=20operations=20dans=20history.ts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/izly/history.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/services/izly/history.ts b/src/services/izly/history.ts index c036b3099..a816b0887 100644 --- a/src/services/izly/history.ts +++ b/src/services/izly/history.ts @@ -3,11 +3,7 @@ import type { ReservationHistory } from "../shared/ReservationHistory"; import { operations, OperationKind} from "ezly"; export const history = async (account: IzlyAccount): Promise => { - const payments = await operations( - account.instance!, - OperationKind.Payment, - 200 - ); + const payments = await operations(account.instance!, OperationKind.Payment, 200); const topup = await operations(account.instance!, OperationKind.TopUp, 200); const currency = account.authentication.configuration.currency; From 6d20996a73dc9465cac91925fa63fb12946d0a01 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Thu, 20 Mar 2025 18:27:23 +0100 Subject: [PATCH 0953/1144] =?UTF-8?q?fix:=20mise=20=C3=A0=20jour=20du=20mo?= =?UTF-8?q?d=C3=A8le=20de=20rapport=20de=20bogue=20et=20correction=20des?= =?UTF-8?q?=20noms=20de=20produit=20dans=20le=20projet=20iOS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/bug.yml | 2 +- android/app/build.gradle | 4 ++-- android/app/src/main/AndroidManifest.xml | 8 ++++++++ ios/Papillon.xcodeproj/project.pbxproj | 4 ++-- ios/Papillon/Info.plist | 2 +- package.json | 2 +- 6 files changed, 15 insertions(+), 7 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 5f3902e3b..979f3c179 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -61,7 +61,7 @@ body: attributes: label: Version utilisée description: Paramètres (de Papillon) -> Version affichée en bas de la page - placeholder: "7.10.4" + placeholder: "7.10.5" validations: required: true diff --git a/android/app/build.gradle b/android/app/build.gradle index bbe73f10c..cbb2728fc 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -88,8 +88,8 @@ android { applicationId 'xyz.getpapillon.app' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 71040 - versionName "7.10.4" + versionCode 71050 + versionName "7.10.5" } signingConfigs { debug { diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index a4aba14ba..d3d18a18f 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -34,6 +34,14 @@ + + + + + + + + diff --git a/ios/Papillon.xcodeproj/project.pbxproj b/ios/Papillon.xcodeproj/project.pbxproj index bbfe9f05e..a36919e50 100644 --- a/ios/Papillon.xcodeproj/project.pbxproj +++ b/ios/Papillon.xcodeproj/project.pbxproj @@ -457,7 +457,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = Papillon; + PRODUCT_NAME = "Papillon"; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -490,7 +490,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = Papillon; + PRODUCT_NAME = "Papillon"; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index 26af44a7a..ed6bf14a3 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 7.10.4 + 7.10.5 CFBundleSignature ???? CFBundleURLTypes diff --git a/package.json b/package.json index 04e7f0cb7..5a0d54585 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "papillonvex", - "version": "7.10.4", + "version": "7.10.5", "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", From 603f71ea39fe51aadcf4b3b62c7e547bd076cf13 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 20 Mar 2025 19:12:13 +0100 Subject: [PATCH 0954/1144] =?UTF-8?q?renommer=20"Papillon"=20en=20"Papillo?= =?UTF-8?q?n=20Dev"=20provisoirement=20pour=20=C3=A9viter=20d'=C3=A9craser?= =?UTF-8?q?=20l'appli=20original?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/build.gradle | 4 ++-- android/app/src/main/AndroidManifest.xml | 5 +++++ .../main/java/xyz/getpapillon/app/{ => dev}/MainActivity.kt | 2 +- .../java/xyz/getpapillon/app/{ => dev}/MainApplication.kt | 2 +- android/app/src/main/res/values/strings.xml | 2 +- android/settings.gradle | 2 +- app.config.ts | 4 ++-- 7 files changed, 13 insertions(+), 8 deletions(-) rename android/app/src/main/java/xyz/getpapillon/app/{ => dev}/MainActivity.kt (98%) rename android/app/src/main/java/xyz/getpapillon/app/{ => dev}/MainApplication.kt (98%) diff --git a/android/app/build.gradle b/android/app/build.gradle index cbb2728fc..401b316b8 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -83,9 +83,9 @@ android { buildToolsVersion rootProject.ext.buildToolsVersion compileSdk rootProject.ext.compileSdkVersion - namespace 'xyz.getpapillon.app' + namespace 'xyz.getpapillon.app.dev' defaultConfig { - applicationId 'xyz.getpapillon.app' + applicationId 'xyz.getpapillon.app.dev' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 71050 diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index d3d18a18f..cc28d66ae 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -42,6 +42,10 @@ + + + + @@ -65,6 +69,7 @@ + diff --git a/android/app/src/main/java/xyz/getpapillon/app/MainActivity.kt b/android/app/src/main/java/xyz/getpapillon/app/dev/MainActivity.kt similarity index 98% rename from android/app/src/main/java/xyz/getpapillon/app/MainActivity.kt rename to android/app/src/main/java/xyz/getpapillon/app/dev/MainActivity.kt index 98ad46d29..7a7a1d810 100644 --- a/android/app/src/main/java/xyz/getpapillon/app/MainActivity.kt +++ b/android/app/src/main/java/xyz/getpapillon/app/dev/MainActivity.kt @@ -1,4 +1,4 @@ -package xyz.getpapillon.app +package xyz.getpapillon.app.dev import android.os.Build import android.os.Bundle diff --git a/android/app/src/main/java/xyz/getpapillon/app/MainApplication.kt b/android/app/src/main/java/xyz/getpapillon/app/dev/MainApplication.kt similarity index 98% rename from android/app/src/main/java/xyz/getpapillon/app/MainApplication.kt rename to android/app/src/main/java/xyz/getpapillon/app/dev/MainApplication.kt index 8f4c5ff5e..e5cd2b94d 100644 --- a/android/app/src/main/java/xyz/getpapillon/app/MainApplication.kt +++ b/android/app/src/main/java/xyz/getpapillon/app/dev/MainApplication.kt @@ -1,4 +1,4 @@ -package xyz.getpapillon.app +package xyz.getpapillon.app.dev import android.app.Application import android.content.res.Configuration diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index ce6acdfc9..9b8452b11 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ - Papillon + Papillon Dev cover false automatic diff --git a/android/settings.gradle b/android/settings.gradle index e5d26fa48..48b330697 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,4 +1,4 @@ -rootProject.name = 'Papillon' +rootProject.name = 'Papillon Dev' dependencyResolutionManagement { versionCatalogs { diff --git a/app.config.ts b/app.config.ts index 511d5c7ee..f6b8c6457 100644 --- a/app.config.ts +++ b/app.config.ts @@ -2,7 +2,7 @@ import { ExpoConfig } from "expo/config"; import PackageJSON from "./package.json"; export default (): ExpoConfig => ({ - name: "Papillon", + name: "Papillon Dev", slug: PackageJSON.name, scheme: "papillon", version: PackageJSON.version, @@ -70,7 +70,7 @@ export default (): ExpoConfig => ({ resizeMode: "cover", }, }, - package: "xyz.getpapillon.app", + package: "xyz.getpapillon.app.dev", permissions: [ "android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION", From 8742457c0bee72fdc0b7197e22aa06c6a9542e2a Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Thu, 20 Mar 2025 19:18:07 +0100 Subject: [PATCH 0955/1144] chore(lint) --- App.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/App.tsx b/App.tsx index 89accdef7..2394a85f6 100644 --- a/App.tsx +++ b/App.tsx @@ -10,6 +10,7 @@ import { AccountService } from "@/stores/account/types"; import { log } from "@/utils/logger/logger"; import { isExpoGo } from "@/utils/native/expoGoAlert"; import { atobPolyfill, btoaPolyfill } from "js-base64"; +// eslint-disable-next-line no-duplicate-imports import { registerBackgroundTasks } from "@/background/BackgroundTasks"; import { SoundHapticsProvider } from "@/hooks/Theme_Sound_Haptics"; import { PapillonNavigation } from "@/router/refs"; From 32f7d261ade7314f7ff3ef6175f5f4f2b6709f0e Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 20 Mar 2025 19:21:09 +0100 Subject: [PATCH 0956/1144] =?UTF-8?q?r=C3=A9duire=20l'intervalle=20du=20ba?= =?UTF-8?q?ckground=20provisoirement=20pour=20faciliter=20les=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/BackgroundTasks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 7fa14120a..6c00f7a78 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -124,7 +124,7 @@ const unsetBackgroundFetch = async () => const setBackgroundFetch = async () => await BackgroundFetch.registerTaskAsync("background-fetch", { - minimumInterval: 60 * 15, + minimumInterval: 60 * 1, stopOnTerminate: false, startOnBoot: true, }); From dad13b9b3376998151961a5a8e45f9f041748708 Mon Sep 17 00:00:00 2001 From: Mael <96339570+ryzenixx@users.noreply.github.com> Date: Thu, 20 Mar 2025 19:38:10 +0100 Subject: [PATCH 0957/1144] =?UTF-8?q?fix:=20am=C3=A9lioration=20du=20suppo?= =?UTF-8?q?rt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Settings/SupportContainerCard.tsx | 2 +- src/views/settings/SettingsSupport.tsx | 38 ++++++++++++++++--- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/components/Settings/SupportContainerCard.tsx b/src/components/Settings/SupportContainerCard.tsx index e956b98eb..73fba518a 100644 --- a/src/components/Settings/SupportContainerCard.tsx +++ b/src/components/Settings/SupportContainerCard.tsx @@ -26,7 +26,7 @@ const SupportContainerCard = ({ theme }: { theme: any }) => { Un problème, une question ? - Laisse-nous un message depuis cette page et nous te répondrons dans les plus brefs délais ! + Laisse-nous un message depuis cette page et nous te répondrons par e-mail dans les plus brefs délais ! diff --git a/src/views/settings/SettingsSupport.tsx b/src/views/settings/SettingsSupport.tsx index 71e21a109..79a484bfe 100644 --- a/src/views/settings/SettingsSupport.tsx +++ b/src/views/settings/SettingsSupport.tsx @@ -10,6 +10,9 @@ import { Check, Mail, Tag, Text } from "lucide-react-native"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { get_logs, Log } from "@/utils/logger/logger"; import { useAlert } from "@/providers/AlertProvider"; +import { modelName, osName, osVersion } from "expo-device"; +import { useCurrentAccount, useAccounts } from "@/stores/account"; +import { AccountService } from "@/stores/account/types"; const SettingsSupport: Screen<"SettingsSupport"> = ({ navigation }) => { const theme = useTheme(); @@ -22,6 +25,27 @@ const SettingsSupport: Screen<"SettingsSupport"> = ({ navigation }) => { const [subject, setSubject] = useState(); const [description, setDescription] = useState(); + const currentAccount = useCurrentAccount((store) => store.account!); + const AccountType = AccountService[currentAccount.service] !== "Local" && currentAccount.service !== AccountService.PapillonMultiService ? AccountService[currentAccount.service] : currentAccount.identityProvider ? currentAccount.identityProvider.name : "Compte local"; + + const cantineAccounts = useAccounts((state) => + state.accounts.filter((acc) => + [AccountService.WebResto, AccountService.Turboself, AccountService.ARD, AccountService.Izly, AccountService.Alise].includes(acc.service) + ) + ); + + const serviceNames: Partial> = { + [AccountService.Turboself]: "Turboself", + [AccountService.ARD]: "ARD", + [AccountService.Izly]: "Izly", + [AccountService.Alise]: "Alise", + }; + + const cantineServices = cantineAccounts + .map((acc) => serviceNames[acc.service] ?? "Inconnu") + .join(", ") || "Aucun"; + + const handlePress = async () => { const logs: Log[] = await get_logs(); const formattedLogs = logs @@ -37,10 +61,12 @@ const SettingsSupport: Screen<"SettingsSupport"> = ({ navigation }) => { }) .join("
"); + + const data = { email: email, title: subject, - detail: `Description de mon problème:
${(description ?? "").replace(/\n/g, "
")}

Journaux:
${formattedLogs}`, + detail: `
💬 𝗗𝗲𝘀𝗰𝗿𝗶𝗽𝘁𝗶𝗼𝗻 𝗱𝘂 𝗽𝗿𝗼𝗯𝗹𝗲̀𝗺𝗲:
${(description ?? "").replace(/\n/g, "
")}

🔒 𝗜𝗻𝗳𝗼𝗿𝗺𝗮𝘁𝗶𝗼𝗻𝘀 𝘀𝘂𝗿 𝗹'𝗮𝗽𝗽𝗮𝗿𝗲𝗶𝗹:
📱 Modèle de l'appareil: ${modelName}
🌐 OS: ${osName} ${osVersion}

⌛ 𝗦𝗲𝗿𝘃𝗶𝗰𝗲𝘀 𝘂𝘁𝗶𝗹𝗶𝘀𝗲́𝘀:
⚡ Service scolaire: ${AccountType}
🍴 Service de cantine: ${cantineServices}

❌ 𝗝𝗼𝘂𝗿𝗻𝗮𝘂𝘅 𝗱'𝗲𝗿𝗿𝗲𝘂𝗿𝘀:
${formattedLogs}
`, }; const response = await fetch("https://api-menthe-et-cristaux.papillon.bzh/api/v1/ticket/public/create", { @@ -57,8 +83,8 @@ const SettingsSupport: Screen<"SettingsSupport"> = ({ navigation }) => { setDescription(""); setSendLogs(false); showAlert({ - title: "Merci de vos retours !", - message: "Nous avons reçu votre demande et allons la regarder avec la plus grande attention.", + title: "Merci de ton retour !", + message: "Nous avons reçu ta demande et allons la regarder avec la plus grande attention.", icon: , }); }; @@ -88,7 +114,7 @@ const SettingsSupport: Screen<"SettingsSupport"> = ({ navigation }) => { /> }> - Adresse E-Mail + Adresse e-mail = ({ navigation }) => { fontSize: 16, fontFamily: "semibold", }, { color: theme.colors.text }]} - placeholder="Faites court, mais faites bien" + placeholder="Fais court, mais fais bien" placeholderTextColor={theme.colors.text + "80"} value={subject} multiline={false} @@ -146,7 +172,7 @@ const SettingsSupport: Screen<"SettingsSupport"> = ({ navigation }) => { }} /> }> - J’accepte de transmettre les journaux d'erreurs et les données du formulaire pour le traitement de ma demande + J’accepte de transmettre le modèle et la version de mon appareil, les services connectés ainsi que les données du formulaire pour le traitement de ma demande From f9ffc08b79dfca01e30a8c191b9a297b4ec82a28 Mon Sep 17 00:00:00 2001 From: Mikkel ALMONTE--RINGAUD Date: Thu, 20 Mar 2025 19:38:57 +0100 Subject: [PATCH 0958/1144] fix(ecoledirecte): bump pawdirecte --- package-lock.json | 12 ++++++------ package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 11eaacaf0..a8f585c3c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.10.4", + "version": "7.10.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.10.4", + "version": "7.10.5", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.4", "@candlefinance/app-icon": "^0.4.5", @@ -74,7 +74,7 @@ "lucide-react-native": "^0.378.0", "notifee": "^0.0.1", "openid-client": "^5.7.0", - "pawdirecte": "^1.8.2", + "pawdirecte": "^1.9.0", "pawnilim": "^0.2.0", "pawnote": "^1.4.1", "pawrd": "^0.6.1", @@ -15086,9 +15086,9 @@ } }, "node_modules/pawdirecte": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/pawdirecte/-/pawdirecte-1.8.2.tgz", - "integrity": "sha512-qX3kS0h11xReBVMMU14x8n2E6O+9oHzR75YVrTEsTg1nO3nHHqIjgPRFs5OyVKJL8cMICIf62Pu3/ZA9kQ2klg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/pawdirecte/-/pawdirecte-1.9.0.tgz", + "integrity": "sha512-mqxn2pRdNsAHn0cyg5v+/oBaOwzxGdh5sGhvSNEyOepiQAwwxptrhbJypfXfe6hTc21tBSKkJjSQthd1b7Otng==", "license": "GPL-3.0-or-later", "dependencies": { "@literate.ink/utilities": "1.0.0-11713907881.1", diff --git a/package.json b/package.json index 5a0d54585..8b682f102 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "lucide-react-native": "^0.378.0", "notifee": "^0.0.1", "openid-client": "^5.7.0", - "pawdirecte": "^1.8.2", + "pawdirecte": "^1.9.0", "pawnilim": "^0.2.0", "pawnote": "^1.4.1", "pawrd": "^0.6.1", From 883eda7bf4286aad0c060b81f76eb91ca5d4b7f6 Mon Sep 17 00:00:00 2001 From: Mael <96339570+ryzenixx@users.noreply.github.com> Date: Thu, 20 Mar 2025 19:42:40 +0100 Subject: [PATCH 0959/1144] =?UTF-8?q?Retrait=20de=20WebResto=20car=20inuti?= =?UTF-8?q?lis=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/SettingsSupport.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/settings/SettingsSupport.tsx b/src/views/settings/SettingsSupport.tsx index 79a484bfe..3abeaffbd 100644 --- a/src/views/settings/SettingsSupport.tsx +++ b/src/views/settings/SettingsSupport.tsx @@ -30,7 +30,7 @@ const SettingsSupport: Screen<"SettingsSupport"> = ({ navigation }) => { const cantineAccounts = useAccounts((state) => state.accounts.filter((acc) => - [AccountService.WebResto, AccountService.Turboself, AccountService.ARD, AccountService.Izly, AccountService.Alise].includes(acc.service) + [AccountService.Turboself, AccountService.ARD, AccountService.Izly, AccountService.Alise].includes(acc.service) ) ); From 32ab1764edfd18eec57766722b2eb7080ab9b97a Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 20 Mar 2025 19:54:13 +0100 Subject: [PATCH 0960/1144] =?UTF-8?q?fix:=20ajout=20des=20param=C3=A8tres?= =?UTF-8?q?=20de=20navigation=20pour=20la=20gestion=20des=20notifications?= =?UTF-8?q?=20cliquables?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/App.tsx b/App.tsx index d236fb4b7..685bbe55d 100644 --- a/App.tsx +++ b/App.tsx @@ -70,7 +70,10 @@ export default function App () { routes: [{ name: "AccountStack" }], }); setTimeout(() => { - PapillonNavigation.current?.navigate(notification.data.page); + PapillonNavigation.current?.navigate( + notification.data.page, + notification.data.parameters, + ); }, 500); } } From 2bc410cf40328358e6a352f8484407e861a1a6df Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 20 Mar 2025 19:57:34 +0100 Subject: [PATCH 0961/1144] =?UTF-8?q?feat:=20si=201=20nouvelle=20actualit?= =?UTF-8?q?=C3=A9,=20redirection=20directe=20dans=20la=20page=20`NewsItem`?= =?UTF-8?q?=20avec=20les=20param=C3=A8tres?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/News.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/background/data/News.ts b/src/background/data/News.ts index d291c6f03..7ce772ffa 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -3,6 +3,7 @@ import { getCurrentAccount } from "../utils/accounts"; import { papillonNotify } from "../Notifications"; import parse_news_resume from "@/utils/format/format_pronote_news"; import { Information } from "@/services/shared/Information"; +import { AccountService } from "@/stores/account/types"; const getDifferences = ( currentNews: Information[], @@ -48,7 +49,7 @@ const fetchNews = async (): Promise => { await updateNewsState(account); const updatedNews = getNews(); - const differences = getDifferences(currentNews, updatedNews); + const differences = getDifferences(currentNews.slice(0, 144), updatedNews); switch (differences.length) { case 0: @@ -68,7 +69,12 @@ const fetchNews = async (): Promise => { : "Aucun résumé disponible.", data: { accountID: account.localID, - page: "News", + page: "NewsItem", + parameters: { + important: false, + isED: account.service === AccountService.EcoleDirecte, + message: JSON.stringify(differences[0]), + } } }, "News" From e3e930d9f8056adb36812acfc71dae1084f97f59 Mon Sep 17 00:00:00 2001 From: Mael <96339570+ryzenixx@users.noreply.github.com> Date: Thu, 20 Mar 2025 20:51:45 +0100 Subject: [PATCH 0962/1144] Ajout de la version de Papillon dans le body du support --- src/views/settings/SettingsSupport.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/views/settings/SettingsSupport.tsx b/src/views/settings/SettingsSupport.tsx index 3abeaffbd..20b19013c 100644 --- a/src/views/settings/SettingsSupport.tsx +++ b/src/views/settings/SettingsSupport.tsx @@ -1,5 +1,5 @@ import React, { useState } from "react"; -import { KeyboardAvoidingView, ScrollView, StyleSheet, TextInput, View } from "react-native"; +import { KeyboardAvoidingView, Platform, ScrollView, StyleSheet, TextInput, View } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; @@ -13,6 +13,7 @@ import { useAlert } from "@/providers/AlertProvider"; import { modelName, osName, osVersion } from "expo-device"; import { useCurrentAccount, useAccounts } from "@/stores/account"; import { AccountService } from "@/stores/account/types"; +import PackageJSON from "../../../package.json"; const SettingsSupport: Screen<"SettingsSupport"> = ({ navigation }) => { const theme = useTheme(); @@ -66,7 +67,7 @@ const SettingsSupport: Screen<"SettingsSupport"> = ({ navigation }) => { const data = { email: email, title: subject, - detail: `
💬 𝗗𝗲𝘀𝗰𝗿𝗶𝗽𝘁𝗶𝗼𝗻 𝗱𝘂 𝗽𝗿𝗼𝗯𝗹𝗲̀𝗺𝗲:
${(description ?? "").replace(/\n/g, "
")}

🔒 𝗜𝗻𝗳𝗼𝗿𝗺𝗮𝘁𝗶𝗼𝗻𝘀 𝘀𝘂𝗿 𝗹'𝗮𝗽𝗽𝗮𝗿𝗲𝗶𝗹:
📱 Modèle de l'appareil: ${modelName}
🌐 OS: ${osName} ${osVersion}

⌛ 𝗦𝗲𝗿𝘃𝗶𝗰𝗲𝘀 𝘂𝘁𝗶𝗹𝗶𝘀𝗲́𝘀:
⚡ Service scolaire: ${AccountType}
🍴 Service de cantine: ${cantineServices}

❌ 𝗝𝗼𝘂𝗿𝗻𝗮𝘂𝘅 𝗱'𝗲𝗿𝗿𝗲𝘂𝗿𝘀:
${formattedLogs}
`, + detail: `
💬 𝗗𝗲𝘀𝗰𝗿𝗶𝗽𝘁𝗶𝗼𝗻 𝗱𝘂 𝗽𝗿𝗼𝗯𝗹𝗲̀𝗺𝗲:
${(description ?? "").replace(/\n/g, "
")}

🔒 𝗜𝗻𝗳𝗼𝗿𝗺𝗮𝘁𝗶𝗼𝗻𝘀 𝘀𝘂𝗿 𝗹'𝗮𝗽𝗽𝗮𝗿𝗲𝗶𝗹:
📱 Modèle de l'appareil: ${modelName}
🌐 OS: ${osName} ${osVersion}
🦋 Version de Papillon: ${PackageJSON.version} ${Platform.OS}

⌛ 𝗦𝗲𝗿𝘃𝗶𝗰𝗲𝘀 𝘂𝘁𝗶𝗹𝗶𝘀𝗲́𝘀:
⚡ Service scolaire: ${AccountType}
🍴 Service de cantine: ${cantineServices}

❌ 𝗝𝗼𝘂𝗿𝗻𝗮𝘂𝘅 𝗱'𝗲𝗿𝗿𝗲𝘂𝗿𝘀:
${formattedLogs}
`, }; const response = await fetch("https://api-menthe-et-cristaux.papillon.bzh/api/v1/ticket/public/create", { From fb186530857095df318bf164a238101d5cabff1d Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 20 Mar 2025 22:15:09 +0100 Subject: [PATCH 0963/1144] fix: disable `toggleHomeworkState` for Skolengo --- src/views/account/Homeworks/Homeworks.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 1d465fe36..3fab5d745 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -270,7 +270,9 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { total={groupedHomework[day].length} homework={homework} onDonePressHandler={async () => { - await toggleHomeworkState(account, homework); + if (account.service !== AccountService.Skolengo) { + await toggleHomeworkState(account, homework); + } await updateHomeworks(true, false, false); await countCheckForReview(); }} From 52c6ab172b1a7a8500ab92e9d2c92cb2cbf11799 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 20 Mar 2025 22:37:56 +0100 Subject: [PATCH 0964/1144] =?UTF-8?q?fix:=20mauvaises=20variables=20utilis?= =?UTF-8?q?=C3=A9es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Lessons.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index e61d6e66d..252949b59 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -111,7 +111,7 @@ const fetchLessons = async (): Promise => { day: "numeric", month: "long", }), - body: `${differencesStatus[0].subject} (${dateLessonsDebut}-${dateLessonsFin}) : Horaire du cours modifié`, + body: `${differencesTimestamp[0].subject} (${dateLessonsDebut}-${dateLessonsFin}) : Horaire du cours modifié`, data: { accountID: account.localID, page: "Lessons" @@ -129,7 +129,7 @@ const fetchLessons = async (): Promise => { let statut: string = ""; - switch (differencesTimestamp[0].status) { + switch (differencesStatus[0].status) { case TimetableClassStatus.TEST: statut = "Devoir surveillé"; break; From c5bb685ce8d87e57fe3bd4e23179e4a61cf18631 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 20 Mar 2025 22:38:13 +0100 Subject: [PATCH 0965/1144] refractor: suppression du slice pour test --- src/background/data/News.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/background/data/News.ts b/src/background/data/News.ts index 7ce772ffa..33b3ee332 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -49,7 +49,7 @@ const fetchNews = async (): Promise => { await updateNewsState(account); const updatedNews = getNews(); - const differences = getDifferences(currentNews.slice(0, 144), updatedNews); + const differences = getDifferences(currentNews, updatedNews); switch (differences.length) { case 0: From 84813e03ab1a7e80e4a163fdb571d4192fd1100d Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 20 Mar 2025 22:39:51 +0100 Subject: [PATCH 0966/1144] =?UTF-8?q?feat:=20si=201=20changement=20dans=20?= =?UTF-8?q?l'edt,=20redirection=20directe=20dans=20la=20page=20`LessonsDoc?= =?UTF-8?q?ument`=20avec=20les=20param=C3=A8tres?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Lessons.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index 252949b59..76c96550e 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -114,7 +114,10 @@ const fetchLessons = async (): Promise => { body: `${differencesTimestamp[0].subject} (${dateLessonsDebut}-${dateLessonsFin}) : Horaire du cours modifié`, data: { accountID: account.localID, - page: "Lessons" + page: "LessonsDocument", + parameters: { + lesson: differencesTimestamp[0], + } } }, "Lessons" @@ -168,7 +171,10 @@ const fetchLessons = async (): Promise => { body: `${differencesStatus[0].subject} (${dateLessonsDebut}-${dateLessonsFin}) : ${statut}`, data: { accountID: account.localID, - page: "Lessons" + page: "LessonsDocument", + parameters: { + lesson: differencesStatus[0], + } } }, "Lessons" From 666697d46cec2023a2ba25af7e646c54975a0647 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 20 Mar 2025 22:42:32 +0100 Subject: [PATCH 0967/1144] =?UTF-8?q?feat:=20si=201=20nouveau=20devoir,=20?= =?UTF-8?q?redirection=20directe=20dans=20la=20page=20`HomeworksDocument`?= =?UTF-8?q?=20avec=20les=20param=C3=A8tres?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Homeworks.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/background/data/Homeworks.ts b/src/background/data/Homeworks.ts index 4dbb93dc6..cdec51b03 100644 --- a/src/background/data/Homeworks.ts +++ b/src/background/data/Homeworks.ts @@ -90,7 +90,10 @@ const fetchHomeworks = async (): Promise => { body: parse_homeworks(differencesHwSemaineActuelle[0].content), data: { accountID: account.localID, - page: "Homeworks" + page: "HomeworksDocument", + parameters: { + homework: differencesHwSemaineActuelle[0], + } } }, "Homeworks" @@ -107,7 +110,10 @@ const fetchHomeworks = async (): Promise => { body: parse_homeworks(differencesHwSemaineProchaine[0].content), data: { accountID: account.localID, - page: "Homeworks" + page: "HomeworksDocument", + parameters: { + homework: differencesHwSemaineProchaine[0], + } } }, "Homeworks" From d0d1ee4587423f38c4bef47ffa4a0dc8e72729cc Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 20 Mar 2025 22:46:47 +0100 Subject: [PATCH 0968/1144] =?UTF-8?q?feat:=20si=201=20nouvelle=20note,=20r?= =?UTF-8?q?edirection=20directe=20dans=20la=20page=20`GradeDocument`=20ave?= =?UTF-8?q?c=20les=20param=C3=A8tres?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Grades.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/background/data/Grades.ts b/src/background/data/Grades.ts index ecf12468c..c63f4a3ec 100644 --- a/src/background/data/Grades.ts +++ b/src/background/data/Grades.ts @@ -66,7 +66,11 @@ const fetchGrade = async (): Promise => { }, Coefficient : ${differences[0].coefficient}`, data: { accountID: account.localID, - page: "Grades" + page: "GradeDocument", + parameters: { + grade: differences[0], + allGrades: grades[defaultPeriod] || [], + } } }, "Grades" From b43382c8f4ed2d0595b9be978e7ad649d4ecf4be Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Thu, 20 Mar 2025 22:48:56 +0100 Subject: [PATCH 0969/1144] =?UTF-8?q?feat:=20si=201=20nouvelle=20comp?= =?UTF-8?q?=C3=A9tence,=20redirection=20directe=20dans=20la=20page=20`Eval?= =?UTF-8?q?uationDocument`=20avec=20les=20param=C3=A8tres?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Evaluation.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/background/data/Evaluation.ts b/src/background/data/Evaluation.ts index 7338fa0e9..ab36ba8bf 100644 --- a/src/background/data/Evaluation.ts +++ b/src/background/data/Evaluation.ts @@ -66,7 +66,11 @@ const fetchEvaluation = async (): Promise => { }, Coefficient : ${differences[0].coefficient}`, data: { accountID: account.localID, - page: "Evaluation" + page: "EvaluationDocument", + parameters: { + evaluation: differences[0], + allEvaluations: evaluation[defaultPeriod] || [], + } } }, "Evaluation" From 33769a1f89000bb630ece98ee3bf4737c6f45b60 Mon Sep 17 00:00:00 2001 From: Nathan Bonnemains Date: Fri, 21 Mar 2025 10:21:50 +0100 Subject: [PATCH 0970/1144] make text more inclusive --- src/views/account/Restaurant/Modals/CardDetail.tsx | 4 ++-- src/views/settings/ExternalAccount/IzlyActivation.tsx | 2 +- .../settings/ExternalAccount/TurboselfAccountSelector.tsx | 2 +- src/views/settings/Settings.tsx | 2 +- src/views/settings/SettingsDevLogs.tsx | 2 +- src/views/settings/SettingsMultiServiceSpace.tsx | 2 +- src/views/settings/SettingsSubjects.tsx | 4 ++-- src/views/welcome/AccountSelector.old.tsx | 4 ++-- src/views/welcome/DevMenu.tsx | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index 2164bf33b..6b28e0a26 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -98,7 +98,7 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio onPress: () => { Alert.alert( "Supprimer la carte", - "Es-tu sûr de vouloir supprimer la " + (cardName ?? "carte") + " ?", + "Veux-tu vraiment supprimer la " + (cardName ?? "carte") + " ?", [ { text: "Annuler", style: "cancel" }, { @@ -406,4 +406,4 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio } }; -export default RestaurantCardDetail; \ No newline at end of file +export default RestaurantCardDetail; diff --git a/src/views/settings/ExternalAccount/IzlyActivation.tsx b/src/views/settings/ExternalAccount/IzlyActivation.tsx index 69a3cc259..f419fd012 100644 --- a/src/views/settings/ExternalAccount/IzlyActivation.tsx +++ b/src/views/settings/ExternalAccount/IzlyActivation.tsx @@ -136,7 +136,7 @@ const IzlyActivation: Screen<"IzlyActivation"> = ({ navigation, route }) => { value="Annuler" disabled={loading} onPress={() => { - Alert.alert("Annuler", "Es-tu sûr de vouloir annuler l'activation ?", [ + Alert.alert("Annuler", "Veux-tu vraiment annuler l'activation ?", [ { text: "Continuer", style: "cancel", diff --git a/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx b/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx index b818da3d2..ecaf76eb5 100644 --- a/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx +++ b/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx @@ -139,7 +139,7 @@ const TurboselfAccountSelector: Screen<"TurboselfAccountSelector"> = ({ navigati onPress={() => { showAlert({ title: "Annuler", - message: "Es-tu sûr de vouloir annuler la connexion ?", + message: "Veux-tu vraiment annuler la connexion ?", icon: , actions: [ { diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index d1fd16889..473f66fb3 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -245,7 +245,7 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { onPress: () => { showAlert({ title: "Se déconnecter", - message: "Es-tu sûr de vouloir te déconnecter ?", + message: "Veux-tu vraiment te déconnecter ?", icon: , actions: [ { diff --git a/src/views/settings/SettingsDevLogs.tsx b/src/views/settings/SettingsDevLogs.tsx index 1cbfd3965..8106809d8 100644 --- a/src/views/settings/SettingsDevLogs.tsx +++ b/src/views/settings/SettingsDevLogs.tsx @@ -93,7 +93,7 @@ const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { onPress={() => { showAlert({ title: "Supprimer les logs ?", - message: "Es-tu sûr de vouloir supprimer toutes les logs ?", + message: "Veux-tu vraiment supprimer toutes les logs ?", icon: , actions: [ { diff --git a/src/views/settings/SettingsMultiServiceSpace.tsx b/src/views/settings/SettingsMultiServiceSpace.tsx index 4c890da42..3d8449096 100644 --- a/src/views/settings/SettingsMultiServiceSpace.tsx +++ b/src/views/settings/SettingsMultiServiceSpace.tsx @@ -85,7 +85,7 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga const deleteSpace = () => { showAlert({ - title: "Es-tu sûr ?", + title: "Veux-tu vraiment continuer ?", message: "Cette action entrainera la suppression de ton espace multi-service.", icon: , actions: [ diff --git a/src/views/settings/SettingsSubjects.tsx b/src/views/settings/SettingsSubjects.tsx index 006cbfd02..3813aaceb 100644 --- a/src/views/settings/SettingsSubjects.tsx +++ b/src/views/settings/SettingsSubjects.tsx @@ -137,7 +137,7 @@ const SettingsSubjects: Screen<"SettingsSubjects"> = ({ navigation }) => { onPress={() => { showAlert({ title: "Réinitialiser les matières", - message: "Tu es sûr de vouloir réinitialiser toutes les matières ?", + message: "Veux-tu vraiment réinitialiser toutes les matières ?", icon: , actions: [ { @@ -461,4 +461,4 @@ const SettingsSubjects: Screen<"SettingsSubjects"> = ({ navigation }) => { ); }; -export default React.memo(SettingsSubjects); \ No newline at end of file +export default React.memo(SettingsSubjects); diff --git a/src/views/welcome/AccountSelector.old.tsx b/src/views/welcome/AccountSelector.old.tsx index 627ed5d47..a6275ff39 100644 --- a/src/views/welcome/AccountSelector.old.tsx +++ b/src/views/welcome/AccountSelector.old.tsx @@ -392,7 +392,7 @@ const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { // delete account showAlert({ title: "Supprimer le compte", - message: "Es-tu sûr de vouloir supprimer ce compte ?", + message: "Veux-tu vraiment supprimer ce compte ?", icon: , actions: [ { @@ -407,7 +407,7 @@ const AccountSelector: Screen<"AccountSelector"> = ({ navigation }) => { // setTimeout pour laisser le temps à la précédente alerte de s'enlever setTimeout(() => { showAlert({ - title: "Es-tu sûr ?", + title: "Veux-tu vraiment continuer ?", message: `Veux-tu supprimer définitivement ${account.studentName.first} ${account.studentName.last} ?`, icon: , actions: [ diff --git a/src/views/welcome/DevMenu.tsx b/src/views/welcome/DevMenu.tsx index d1ecf127d..7a885f66e 100644 --- a/src/views/welcome/DevMenu.tsx +++ b/src/views/welcome/DevMenu.tsx @@ -303,7 +303,7 @@ const DevMenu: Screen<"DevMenu"> = ({ navigation }) => { onPress={() => { showAlert({ title: "Réinitialisation de Papillon", - message: "Es-tu sûr de vouloir réinitialiser toutes les données de l'application ?", + message: "Veux-tu vraiment réinitialiser toutes les données de l'application ?", icon: , actions: [ { From 9791c207a600794a5706e634526ae7905a38f04d Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Fri, 21 Mar 2025 17:41:49 +0100 Subject: [PATCH 0971/1144] feat(lint): arrow-spacing --- eslint.config.js | 1 + src/components/Global/PapillonPicker.tsx | 2 +- src/services/attendance.ts | 4 ++-- src/services/skolengo/data/attendance.ts | 4 ++-- src/services/skolengo/data/grades.ts | 4 ++-- src/services/skolengo/data/period.ts | 2 +- src/services/skolengo/data/timetable.ts | 6 +++--- src/services/skolengo/default-personalization.ts | 16 ++++++++-------- src/services/skolengo/skolengo-account.tsx | 2 +- src/utils/epochWeekNumber.ts | 2 +- 10 files changed, 22 insertions(+), 21 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 9918a2d5b..e7ed314d3 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -48,6 +48,7 @@ module.exports = [ "before": true }], "@stylistic/arrow-parens": "error", + "@stylistic/arrow-spacing": "error", "unused-imports/no-unused-imports": "error", "no-unused-vars": ["error", { "args": "none" diff --git a/src/components/Global/PapillonPicker.tsx b/src/components/Global/PapillonPicker.tsx index a3b187b9f..43758d44b 100644 --- a/src/components/Global/PapillonPicker.tsx +++ b/src/components/Global/PapillonPicker.tsx @@ -116,7 +116,7 @@ const PapillonPicker: React.FC = ({ { + onLayout={(event) => { const height = event.nativeEvent.layout.height; setContentHeight(height); }} diff --git a/src/services/attendance.ts b/src/services/attendance.ts index a65f92d49..17e0d39d3 100644 --- a/src/services/attendance.ts +++ b/src/services/attendance.ts @@ -61,8 +61,8 @@ export async function updateAttendancePeriodsInCache (accoun periods = [ { name: "Toutes", - startTimestamp: Math.min(...output.map((e)=>e.startTimestamp)), - endTimestamp: Math.max(...output.map((e)=>e.endTimestamp)), + startTimestamp: Math.min(...output.map((e) => e.startTimestamp)), + endTimestamp: Math.max(...output.map((e) => e.endTimestamp)), }, ]; diff --git a/src/services/skolengo/data/attendance.ts b/src/services/skolengo/data/attendance.ts index 078ce3889..0529d9e72 100644 --- a/src/services/skolengo/data/attendance.ts +++ b/src/services/skolengo/data/attendance.ts @@ -17,7 +17,7 @@ const dateIntervalToTime = (from: Date, to: Date) => { } }; -const _strs = (strs: (string|null|undefined)[], defaultStr: string) => strs.filter((e)=>e && e.trim().length > 0).length > 0 ? strs.filter((e)=>e && e.trim().length > 0).join(" - ") : defaultStr; +const _strs = (strs: (string|null|undefined)[], defaultStr: string) => strs.filter((e) => e && e.trim().length > 0).length > 0 ? strs.filter((e) => e && e.trim().length > 0).join(" - ") : defaultStr; export const getAttendance = async (account: SkolengoAccount): Promise => { if (!account.instance) @@ -34,7 +34,7 @@ export const getAttendance = async (account: SkolengoAccount): Promisee.currentState).forEach((absence) => { + absences.map((e) => e.currentState).forEach((absence) => { switch (absence.absenceType as SupportedAbsenceType) { case ("ABSENCE"): diff --git a/src/services/skolengo/data/grades.ts b/src/services/skolengo/data/grades.ts index 52165b554..399547497 100644 --- a/src/services/skolengo/data/grades.ts +++ b/src/services/skolengo/data/grades.ts @@ -14,8 +14,8 @@ const decodeGradeNumber = (value?:number | null): GradeValue => const getSubjectMinMax = (evalSubj: Evaluation): { min: GradeValue, max:GradeValue, outOf: GradeValue } => { const outOf = decodeGradeNumber(evalSubj.scale || SKOLENGO_DEFAULT_SCALE); if (evalSubj.evaluations.filter((e) => e.evaluationResult.mark !== null && !e.evaluationResult.nonEvaluationReason).length === 0) return { min: { value: null, disabled: true, status: null } , max: { value: null, disabled: true, status: null }, outOf }; - const [minimum, maximum] = evalSubj.evaluations.filter((e)=>e.evaluationResult.mark !== null) - .map((e)=>((e.evaluationResult.mark!)/(e.scale || SKOLENGO_DEFAULT_SCALE)) * (evalSubj.scale || SKOLENGO_DEFAULT_SCALE)) + const [minimum, maximum] = evalSubj.evaluations.filter((e) => e.evaluationResult.mark !== null) + .map((e) => ((e.evaluationResult.mark!)/(e.scale || SKOLENGO_DEFAULT_SCALE)) * (evalSubj.scale || SKOLENGO_DEFAULT_SCALE)) .reduce(([minAcc, maxAcc], e) => [Math.min(minAcc, e), Math.max(maxAcc, e)], [evalSubj.scale || SKOLENGO_DEFAULT_SCALE, 0]); return { min: { value: minimum, disabled: false, status: null } , max: { value: maximum, disabled: false, status: null }, outOf }; }; diff --git a/src/services/skolengo/data/period.ts b/src/services/skolengo/data/period.ts index 6a40884b0..e837084f0 100644 --- a/src/services/skolengo/data/period.ts +++ b/src/services/skolengo/data/period.ts @@ -7,7 +7,7 @@ export const getPeriod = async (account: SkolengoAccount) => { const periods = await account.instance.getEvaluationSettings(); - return periods.map((e)=>e.periods).flat().map((p) => ({ + return periods.map((e) => e.periods).flat().map((p) => ({ id: p.id, name: p.label, startTimestamp: new Date(p.startDate).getTime(), diff --git a/src/services/skolengo/data/timetable.ts b/src/services/skolengo/data/timetable.ts index be707c9ad..a2f64d0f8 100644 --- a/src/services/skolengo/data/timetable.ts +++ b/src/services/skolengo/data/timetable.ts @@ -13,9 +13,9 @@ const decodeLesson = (lesson: Lesson): TimetableClass => ({ title: lesson.title, startTimestamp: new Date(lesson.startDateTime).getTime(), endTimestamp: new Date(lesson.endDateTime).getTime(), - additionalNotes: lesson.contents?.map((e)=>e.title+":\n"+htmlToText(e.html)).join("\n\n\n"), + additionalNotes: lesson.contents?.map((e) => e.title+":\n"+htmlToText(e.html)).join("\n\n\n"), room: lesson.location ? lesson.location+(lesson.locationComplement ? " - "+lesson.locationComplement : "") : void 0, - teacher: lesson.teachers?.map((e)=>`${e.firstName.at(0)}. ${e.lastName}`).join("/"), + teacher: lesson.teachers?.map((e) => `${e.firstName.at(0)}. ${e.lastName}`).join("/"), backgroundColor: lesson.subject.color || void 0, status: lesson.canceled ? TimetableClassStatus.CANCELED : void 0, }); @@ -27,7 +27,7 @@ export const getTimetableForWeek = async (account: SkolengoAccount, epochWeekNum const { start, end } = weekNumberToDateRange(epochWeekNumber); const agenda = await account.instance.getAgenda(undefined, toSkolengoDate(start), toSkolengoDate(end)); - const lessons = agenda.map((e)=>e.lessons).flat(); + const lessons = agenda.map((e) => e.lessons).flat(); return lessons.map(decodeLesson); }; diff --git a/src/services/skolengo/default-personalization.ts b/src/services/skolengo/default-personalization.ts index 0eca20d74..e20930797 100644 --- a/src/services/skolengo/default-personalization.ts +++ b/src/services/skolengo/default-personalization.ts @@ -21,15 +21,15 @@ const defaultSkolengoPersonalization = async (instance: SkolengoAccount["instanc }; }; -const getServiceConfig =(instance: SkolengoAccount["instance"])=> Promise.all([ +const getServiceConfig =(instance: SkolengoAccount["instance"]) => Promise.all([ Promise.resolve("Home"), - instance?.getUsersMailSettings().then(()=> "Messages").catch(()=> null), - instance?.getAbsenceFiles().then(()=> "Attendance").catch(()=> null), - instance?.getAgenda(void 0, toSkolengoDate(new Date()), toSkolengoDate(new Date(Date.now()+604800000))).then(()=> "Lessons").catch(()=> null), - instance?.getHomeworkAssignments(void 0, toSkolengoDate(new Date()), toSkolengoDate(new Date(Date.now()+604800000))).then(()=> "Homeworks").catch(()=> null), - instance?.getEvaluationSettings().then(()=> "Grades").catch(()=> null), - instance?.getSchoolInfos().then(()=> "News").catch(()=> null), -]).then((tabs)=> tabs.filter((tab): tab is Tab => tab !== null)); + instance?.getUsersMailSettings().then(() => "Messages").catch(() => null), + instance?.getAbsenceFiles().then(() => "Attendance").catch(() => null), + instance?.getAgenda(void 0, toSkolengoDate(new Date()), toSkolengoDate(new Date(Date.now()+604800000))).then(() => "Lessons").catch(() => null), + instance?.getHomeworkAssignments(void 0, toSkolengoDate(new Date()), toSkolengoDate(new Date(Date.now()+604800000))).then(() => "Homeworks").catch(() => null), + instance?.getEvaluationSettings().then(() => "Grades").catch(() => null), + instance?.getSchoolInfos().then(() => "News").catch(() => null), +]).then((tabs) => tabs.filter((tab): tab is Tab => tab !== null)); export default defaultSkolengoPersonalization; diff --git a/src/services/skolengo/skolengo-account.tsx b/src/services/skolengo/skolengo-account.tsx index 3271ecfb8..26b74e93a 100644 --- a/src/services/skolengo/skolengo-account.tsx +++ b/src/services/skolengo/skolengo-account.tsx @@ -72,7 +72,7 @@ const getJWTClaims = (token: string): SkolengoJWT => { return data; }; -export const getSkolengoAccount = async (authConfig: SkolengoAuthConfig, userInfo?: User)=>{ +export const getSkolengoAccount = async (authConfig: SkolengoAuthConfig, userInfo?: User) => { const skolengoAccount = new Skolengo( null, authConfig.school, diff --git a/src/utils/epochWeekNumber.ts b/src/utils/epochWeekNumber.ts index 793a91a47..24cd0d972 100644 --- a/src/utils/epochWeekNumber.ts +++ b/src/utils/epochWeekNumber.ts @@ -40,7 +40,7 @@ const dayToWeekCommonDay = (date: Date): Date => { return _date; }; -export const epochWNToDate = (epochWeekNumber: number)=>dayToWeekCommonDay(weekNumberToMiddleDate(epochWeekNumber)); +export const epochWNToDate = (epochWeekNumber: number) => dayToWeekCommonDay(weekNumberToMiddleDate(epochWeekNumber)); export const epochWNToPronoteWN = (epochWeekNumber: number, account: PronoteAccount) => translateToWeekNumber(epochWNToDate(epochWeekNumber), account.instance?.instance.firstMonday || pronoteFirstDate) || 1; From 363f0af52c331c1cfc9070bf78d7edd2ef47bc05 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Fri, 21 Mar 2025 18:32:02 +0100 Subject: [PATCH 0972/1144] feat: ajout de la gestion de l'ID du dernier compte ouvert dans le store des comptes (permet d'ouvrir le bon compte lors clic sur la notif) --- App.tsx | 13 ++++--------- src/stores/account/index.ts | 6 ++++++ src/stores/account/types.ts | 1 + 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/App.tsx b/App.tsx index 685bbe55d..f6466ab23 100644 --- a/App.tsx +++ b/App.tsx @@ -13,7 +13,6 @@ import { atobPolyfill, btoaPolyfill } from "js-base64"; import { registerBackgroundTasks } from "@/background/BackgroundTasks"; import { SoundHapticsProvider } from "@/hooks/Theme_Sound_Haptics"; import { PapillonNavigation } from "@/router/refs"; -import { findAccountByID } from "@/background/utils/accounts"; import * as Device from "expo-device"; import * as ScreenOrientation from "expo-screen-orientation"; @@ -62,19 +61,15 @@ export default function App () { const handleNotificationPress = async (notification: any) => { if (notification?.data) { const accountID = notification.data.accountID; - const account = findAccountByID(accountID); - if (account) { - await switchTo(account); - PapillonNavigation.current?.reset({ - index: 0, - routes: [{ name: "AccountStack" }], - }); + if (accountID) { + useAccounts.getState().setLastOpenedAccountID(accountID); + setTimeout(() => { PapillonNavigation.current?.navigate( notification.data.page, notification.data.parameters, ); - }, 500); + }, 1000); } } }; diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index 83280bcce..3fa326ebd 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -191,6 +191,12 @@ export const useAccounts = create()( // We don't need to store the localID here, as we can get it from the account store. accounts: >[], + // Update manually lastOpenedAccountID + setLastOpenedAccountID: (id: string | null) => { + set({ lastOpenedAccountID: id }); + log(`lastOpenedAccountID updated: ${id}`, "accounts:setLastOpenedAccountID"); + }, + // When creating, we don't want the "instance" to be stored. create: ({ instance, ...account }) => { log(`storing ${account.localID} (${"name" in account ? account.name : "no name"})`, "accounts:create"); diff --git a/src/stores/account/types.ts b/src/stores/account/types.ts index 770fd9f2c..aea71f86e 100644 --- a/src/stores/account/types.ts +++ b/src/stores/account/types.ts @@ -302,6 +302,7 @@ export type Account = ( export interface AccountsStore { lastOpenedAccountID: string | null accounts: Account[] + setLastOpenedAccountID: (id: string | null) => void create: (account: Account) => void remove: (localID: string) => void update:
( From fe0ce7ef2044ac80d86bba3344d6655a4c1327ed Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Fri, 21 Mar 2025 18:48:18 +0100 Subject: [PATCH 0973/1144] =?UTF-8?q?fix:=20ajout=20de=20la=20v=C3=A9rific?= =?UTF-8?q?ation=20de=20`account.instance`=20avant=20de=20mettre=20une=20a?= =?UTF-8?q?ctualit=C3=A9=20en=20lue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/News/Document.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/views/account/News/Document.tsx b/src/views/account/News/Document.tsx index 447c78f93..33301a39f 100644 --- a/src/views/account/News/Document.tsx +++ b/src/views/account/News/Document.tsx @@ -64,11 +64,13 @@ const NewsItem: Screen<"NewsItem"> = ({ route, navigation }) => { }, [navigation, message.title]); useEffect(() => { - setNewsRead(account, message, true); - setMessage((prev) => ({ - ...prev, - read: true, - })); + if (account.instance) { + setNewsRead(account, message, true); + setMessage((prev) => ({ + ...prev, + read: true, + })); + } }, [account.instance]); const tagsStyles = { From ea8091e20a7717f4b4345bb0284abefb3d7ee550 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Fri, 21 Mar 2025 23:45:16 +0100 Subject: [PATCH 0974/1144] =?UTF-8?q?Revert=20"renommer=20"Papillon"=20en?= =?UTF-8?q?=20"Papillon=20Dev"=20provisoirement=20pour=20=C3=A9viter=20d'?= =?UTF-8?q?=C3=A9craser=20l'appli=20original"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 603f71ea39fe51aadcf4b3b62c7e547bd076cf13. --- android/app/build.gradle | 4 ++-- android/app/src/main/AndroidManifest.xml | 5 ----- .../main/java/xyz/getpapillon/app/{dev => }/MainActivity.kt | 2 +- .../java/xyz/getpapillon/app/{dev => }/MainApplication.kt | 2 +- android/app/src/main/res/values/strings.xml | 2 +- android/settings.gradle | 2 +- app.config.ts | 4 ++-- 7 files changed, 8 insertions(+), 13 deletions(-) rename android/app/src/main/java/xyz/getpapillon/app/{dev => }/MainActivity.kt (98%) rename android/app/src/main/java/xyz/getpapillon/app/{dev => }/MainApplication.kt (98%) diff --git a/android/app/build.gradle b/android/app/build.gradle index 401b316b8..cbb2728fc 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -83,9 +83,9 @@ android { buildToolsVersion rootProject.ext.buildToolsVersion compileSdk rootProject.ext.compileSdkVersion - namespace 'xyz.getpapillon.app.dev' + namespace 'xyz.getpapillon.app' defaultConfig { - applicationId 'xyz.getpapillon.app.dev' + applicationId 'xyz.getpapillon.app' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 71050 diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index cc28d66ae..d3d18a18f 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -42,10 +42,6 @@ - - - - @@ -69,7 +65,6 @@ - diff --git a/android/app/src/main/java/xyz/getpapillon/app/dev/MainActivity.kt b/android/app/src/main/java/xyz/getpapillon/app/MainActivity.kt similarity index 98% rename from android/app/src/main/java/xyz/getpapillon/app/dev/MainActivity.kt rename to android/app/src/main/java/xyz/getpapillon/app/MainActivity.kt index 7a7a1d810..98ad46d29 100644 --- a/android/app/src/main/java/xyz/getpapillon/app/dev/MainActivity.kt +++ b/android/app/src/main/java/xyz/getpapillon/app/MainActivity.kt @@ -1,4 +1,4 @@ -package xyz.getpapillon.app.dev +package xyz.getpapillon.app import android.os.Build import android.os.Bundle diff --git a/android/app/src/main/java/xyz/getpapillon/app/dev/MainApplication.kt b/android/app/src/main/java/xyz/getpapillon/app/MainApplication.kt similarity index 98% rename from android/app/src/main/java/xyz/getpapillon/app/dev/MainApplication.kt rename to android/app/src/main/java/xyz/getpapillon/app/MainApplication.kt index e5cd2b94d..8f4c5ff5e 100644 --- a/android/app/src/main/java/xyz/getpapillon/app/dev/MainApplication.kt +++ b/android/app/src/main/java/xyz/getpapillon/app/MainApplication.kt @@ -1,4 +1,4 @@ -package xyz.getpapillon.app.dev +package xyz.getpapillon.app import android.app.Application import android.content.res.Configuration diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 9b8452b11..ce6acdfc9 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ - Papillon Dev + Papillon cover false automatic diff --git a/android/settings.gradle b/android/settings.gradle index 48b330697..e5d26fa48 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,4 +1,4 @@ -rootProject.name = 'Papillon Dev' +rootProject.name = 'Papillon' dependencyResolutionManagement { versionCatalogs { diff --git a/app.config.ts b/app.config.ts index f6b8c6457..511d5c7ee 100644 --- a/app.config.ts +++ b/app.config.ts @@ -2,7 +2,7 @@ import { ExpoConfig } from "expo/config"; import PackageJSON from "./package.json"; export default (): ExpoConfig => ({ - name: "Papillon Dev", + name: "Papillon", slug: PackageJSON.name, scheme: "papillon", version: PackageJSON.version, @@ -70,7 +70,7 @@ export default (): ExpoConfig => ({ resizeMode: "cover", }, }, - package: "xyz.getpapillon.app.dev", + package: "xyz.getpapillon.app", permissions: [ "android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION", From a61d6feaea82e964c67351ac802170c850a41c67 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Fri, 21 Mar 2025 23:46:43 +0100 Subject: [PATCH 0975/1144] =?UTF-8?q?Revert=20"r=C3=A9duire=20l'intervalle?= =?UTF-8?q?=20du=20background=20provisoirement=20pour=20faciliter=20les=20?= =?UTF-8?q?tests"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 32f7d261ade7314f7ff3ef6175f5f4f2b6709f0e. --- src/background/BackgroundTasks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 6c00f7a78..7fa14120a 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -124,7 +124,7 @@ const unsetBackgroundFetch = async () => const setBackgroundFetch = async () => await BackgroundFetch.registerTaskAsync("background-fetch", { - minimumInterval: 60 * 1, + minimumInterval: 60 * 15, stopOnTerminate: false, startOnBoot: true, }); From 1b12f21117d23bba636af45ae5a6a297dce26e35 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 22 Mar 2025 11:16:22 +0100 Subject: [PATCH 0976/1144] chore: use `TouchableOpacity` from react-native instead of react-native-gesture-handler --- src/components/Home/RedirectButton.tsx | 3 +-- src/views/account/Chat/Messages.tsx | 2 +- src/views/account/Grades/Atoms/GradesScodocUE.tsx | 3 +-- src/views/account/Home/Elements/PopupRestauration.tsx | 3 +-- src/views/account/Homeworks/Homeworks.tsx | 4 ++-- src/views/account/Restaurant/Modals/CardDetail.tsx | 3 +-- src/views/account/Restaurant/Modals/QrCode.tsx | 3 +-- .../settings/ExternalAccount/PriceDetectionOnboarding.tsx | 3 +-- src/views/settings/SettingsProfile.tsx | 3 +-- src/views/welcome/ChangelogScreen.tsx | 3 +-- 10 files changed, 11 insertions(+), 19 deletions(-) diff --git a/src/components/Home/RedirectButton.tsx b/src/components/Home/RedirectButton.tsx index 793929f94..718bf61c0 100644 --- a/src/components/Home/RedirectButton.tsx +++ b/src/components/Home/RedirectButton.tsx @@ -1,8 +1,7 @@ import type React from "react"; -import { View, Text } from "react-native"; +import { View, Text, TouchableOpacity } from "react-native"; import { ArrowUpRight } from "lucide-react-native"; import { NavigationContainerRef, useTheme } from "@react-navigation/native"; -import { TouchableOpacity } from "react-native-gesture-handler"; import type { RouteParameters } from "@/router/helpers/types"; interface RedirectButtonProps { diff --git a/src/views/account/Chat/Messages.tsx b/src/views/account/Chat/Messages.tsx index 7622089f1..d7fc77fa9 100644 --- a/src/views/account/Chat/Messages.tsx +++ b/src/views/account/Chat/Messages.tsx @@ -7,6 +7,7 @@ import { Platform, Text, RefreshControl, + TouchableOpacity } from "react-native"; import { useTheme } from "@react-navigation/native"; import type { Screen } from "@/router/helpers/types"; @@ -32,7 +33,6 @@ import Reanimated, { } from "react-native-reanimated"; import PapillonHeader, { PapillonHeaderInsetHeight } from "@/components/Global/PapillonHeader"; import { SquarePen } from "lucide-react-native"; -import { TouchableOpacity } from "react-native-gesture-handler"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; import { TabLocation } from "pawnote"; import { hasFeatureAccountSetup } from "@/utils/multiservice"; diff --git a/src/views/account/Grades/Atoms/GradesScodocUE.tsx b/src/views/account/Grades/Atoms/GradesScodocUE.tsx index adf1a8165..36e3306cb 100644 --- a/src/views/account/Grades/Atoms/GradesScodocUE.tsx +++ b/src/views/account/Grades/Atoms/GradesScodocUE.tsx @@ -10,8 +10,7 @@ import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; import { useTheme } from "@react-navigation/native"; import { ChevronDown, ChevronUp, Info } from "lucide-react-native"; import { memo, useState } from "react"; -import { Image, View } from "react-native"; -import { TouchableOpacity } from "react-native-gesture-handler"; +import { Image, View, TouchableOpacity } from "react-native"; import Reanimated, { FadeIn, FadeInDown, FadeOut, FadeOutUp, LinearTransition } from "react-native-reanimated"; diff --git a/src/views/account/Home/Elements/PopupRestauration.tsx b/src/views/account/Home/Elements/PopupRestauration.tsx index 03bd8a070..d1f1faab6 100644 --- a/src/views/account/Home/Elements/PopupRestauration.tsx +++ b/src/views/account/Home/Elements/PopupRestauration.tsx @@ -1,8 +1,7 @@ import React, { useEffect } from "react"; -import { View, Image, StyleSheet } from "react-native"; +import { View, Image, StyleSheet, TouchableOpacity } from "react-native"; import { NativeList, NativeText } from "@/components/Global/NativeComponents"; import { useTheme } from "@react-navigation/native"; -import { TouchableOpacity } from "react-native-gesture-handler"; import { ArrowUpRight } from "lucide-react-native"; import { useCurrentAccount } from "@/stores/account"; import { PapillonNavigation } from "@/router/refs"; diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 61cb4b1f3..7e9e9bb3f 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -12,7 +12,8 @@ import { RefreshControl, StyleSheet, TextInput, - ListRenderItem + ListRenderItem, + TouchableOpacity } from "react-native"; import { dateToEpochWeekNumber, epochWNToDate } from "@/utils/epochWeekNumber"; @@ -20,7 +21,6 @@ import * as StoreReview from "expo-store-review"; import HomeworkItem from "./Atoms/Item"; import { PressableScale } from "react-native-pressable-scale"; -import { TouchableOpacity } from "react-native-gesture-handler"; import { Book, CheckSquare, ChevronLeft, ChevronRight, CircleDashed, Search, X } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { BlurView } from "expo-blur"; diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index fd73f8541..2a17a3090 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -1,4 +1,4 @@ -import { Alert, Image, Linking, Platform, ScrollView, Text, View } from "react-native"; +import { Alert, Image, Linking, Platform, ScrollView, Text, View, TouchableOpacity } from "react-native"; import MenuCard from "../Cards/Card"; import Reanimated from "react-native-reanimated"; import React, { useState } from "react"; @@ -17,7 +17,6 @@ import { balanceFromExternal } from "@/services/balance"; import { reservationHistoryFromExternal } from "@/services/reservation-history"; import { Screen } from "@/router/helpers/types"; import { LinearGradient } from "expo-linear-gradient"; -import { TouchableOpacity } from "react-native-gesture-handler"; import PapillonPicker from "@/components/Global/PapillonPicker"; import { formatCardIdentifier } from "@/utils/external/restaurant"; import { error, warn } from "@/utils/logger/logger"; diff --git a/src/views/account/Restaurant/Modals/QrCode.tsx b/src/views/account/Restaurant/Modals/QrCode.tsx index 5dcbe93d1..fe2e01741 100644 --- a/src/views/account/Restaurant/Modals/QrCode.tsx +++ b/src/views/account/Restaurant/Modals/QrCode.tsx @@ -4,8 +4,7 @@ import { useTheme } from "@react-navigation/native"; import { BlurView } from "expo-blur"; import { QrCodeIcon, X } from "lucide-react-native"; import { useEffect, useState } from "react"; -import { View, Text, Pressable } from "react-native"; -import { TouchableOpacity } from "react-native-gesture-handler"; +import { View, Text, Pressable, TouchableOpacity } from "react-native"; import { PressableScale } from "react-native-pressable-scale"; import QRCode from "react-native-qrcode-svg"; import * as Haptics from "expo-haptics"; diff --git a/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx b/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx index 6acbeff2a..1bc224649 100644 --- a/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx +++ b/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx @@ -3,10 +3,9 @@ import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { BellRing } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import { Image, View, StyleSheet, Text } from "react-native"; +import { Image, View, StyleSheet, Text, TouchableOpacity } from "react-native"; import { NativeText, } from "@/components/Global/NativeComponents"; import { useCurrentAccount } from "@/stores/account"; -import { TouchableOpacity } from "react-native-gesture-handler"; import BetaIndicator from "@/components/News/Beta"; const PriceDetectionOnboarding: Screen<"PriceDetectionOnboarding"> = ({ navigation, route }) => { diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index 6ebcf8b75..5959df95f 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -5,11 +5,10 @@ import { useTheme } from "@react-navigation/native"; import * as ImagePicker from "expo-image-picker"; import { BadgeX, Camera, CameraOff, ChevronDown, ChevronUp, ClipboardCopy, TextCursorInput, Trash, Undo2, User2, UserCircle2, WholeWord } from "lucide-react-native"; import React, { useEffect, useRef, useState } from "react"; -import { ActivityIndicator, Image, KeyboardAvoidingView, ScrollView, Switch, TextInput } from "react-native"; +import { ActivityIndicator, Image, KeyboardAvoidingView, ScrollView, Switch, TextInput, TouchableOpacity } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useAlert } from "@/providers/AlertProvider"; import * as Clipboard from "expo-clipboard"; -import { TouchableOpacity } from "react-native-gesture-handler"; import { getDefaultProfilePicture } from "@/utils/GetRessources/GetDefaultProfilePicture"; import ResponsiveTextInput from "@/components/FirstInstallation/ResponsiveTextInput"; diff --git a/src/views/welcome/ChangelogScreen.tsx b/src/views/welcome/ChangelogScreen.tsx index 9c56de88a..bbe8bb76c 100644 --- a/src/views/welcome/ChangelogScreen.tsx +++ b/src/views/welcome/ChangelogScreen.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect, useLayoutEffect } from "react"; -import { Image, Linking, Platform, ScrollView, View } from "react-native"; +import { Image, Linking, Platform, ScrollView, View, TouchableOpacity } from "react-native"; import PackageJSON from "../../../package.json"; import datasets from "@/consts/datasets.json"; @@ -12,7 +12,6 @@ import Reanimated, { FadeInUp, FadeOutUp, LinearTransition } from "react-native- import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { animPapillon } from "@/utils/ui/animations"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; -import { TouchableOpacity } from "react-native-gesture-handler"; import { PressableScale } from "react-native-pressable-scale"; import AsyncStorage from "@react-native-async-storage/async-storage"; From 65600e181e60348a2eb248694be9394f792add68 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 22 Mar 2025 11:59:11 +0100 Subject: [PATCH 0977/1144] chore: remove unused file --- src/components/Lessons/HomeTimetableItem.tsx | 21 -------------------- 1 file changed, 21 deletions(-) delete mode 100644 src/components/Lessons/HomeTimetableItem.tsx diff --git a/src/components/Lessons/HomeTimetableItem.tsx b/src/components/Lessons/HomeTimetableItem.tsx deleted file mode 100644 index 621578e7f..000000000 --- a/src/components/Lessons/HomeTimetableItem.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from "react"; -import { View, Text, StyleSheet } from "react-native"; - -interface HomeTimetableItemProps { - item: any -} - -export const HomeTimetableItem = ({ item }: HomeTimetableItemProps) => { - return ( - - HomeTimetableItem - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: "blue", - }, -}); From 8b4b69826a3b2937e2a621c61a90c69b3bdcbc89 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 22 Mar 2025 12:10:07 +0100 Subject: [PATCH 0978/1144] feat(lint): remove unused func args --- eslint.config.js | 4 +--- src/hooks/Theme_Sound_Haptics.tsx | 1 + src/router/navigator/menu.tsx | 2 +- src/router/navigator/tabs.tsx | 2 +- src/services/local/ical.ts | 1 - src/services/multi/default-personalization.ts | 4 ++-- src/stores/account/index.ts | 2 +- src/stores/timetable/index.ts | 2 +- src/views/account/Chat/Modals/Chat.tsx | 2 +- src/views/account/Grades/Graph/GradesAverage.tsx | 8 ++++---- src/views/account/Grades/Modals/Subject.tsx | 2 +- src/views/account/Homeworks/Homeworks.tsx | 6 +++--- src/views/account/Homeworks/HomeworksHeader.tsx | 8 ++------ src/views/account/Lessons/Lessons.tsx | 2 +- src/views/account/Restaurant/Modals/PaymentSuccess.tsx | 2 +- src/views/account/Week/Week.tsx | 9 ++------- src/views/addon/AddonLogs.tsx | 2 +- .../IdentityProvider/BackgroundIdentityProvider.tsx | 2 +- src/views/login/IdentityProvider/providers/Multi.tsx | 2 +- src/views/login/pronote/PronoteQRCode.tsx | 2 -- .../ExternalAccount/TurboselfAccountSelector.tsx | 2 +- src/views/settings/SettingsExternalServices.tsx | 2 +- src/views/settings/SettingsIcons.tsx | 2 +- src/views/settings/SettingsProfile.tsx | 2 +- src/views/settings/SettingsSupport.tsx | 2 +- src/views/settings/SettingsTrophies.tsx | 2 +- src/views/welcome/ChangelogScreen.tsx | 2 +- src/widgets/Components/LastGrade.tsx | 3 +-- src/widgets/Components/RestaurantBalance.tsx | 3 +-- src/widgets/Components/RestaurantQRCode.tsx | 3 +-- 30 files changed, 36 insertions(+), 52 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index e7ed314d3..5b884adff 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -50,9 +50,7 @@ module.exports = [ "@stylistic/arrow-parens": "error", "@stylistic/arrow-spacing": "error", "unused-imports/no-unused-imports": "error", - "no-unused-vars": ["error", { - "args": "none" - }], + "no-unused-vars": "error", "no-var": "error", "no-irregular-whitespace": "error", "no-unneeded-ternary": "error", diff --git a/src/hooks/Theme_Sound_Haptics.tsx b/src/hooks/Theme_Sound_Haptics.tsx index 601ddc106..f7c5ea3e1 100644 --- a/src/hooks/Theme_Sound_Haptics.tsx +++ b/src/hooks/Theme_Sound_Haptics.tsx @@ -1,3 +1,4 @@ +/* eslint-disable no-unused-vars */ import React, { createContext, useContext, useState, useEffect } from "react"; import AsyncStorage from "@react-native-async-storage/async-storage"; diff --git a/src/router/navigator/menu.tsx b/src/router/navigator/menu.tsx index 74c3c0c3e..629526e07 100644 --- a/src/router/navigator/menu.tsx +++ b/src/router/navigator/menu.tsx @@ -58,7 +58,7 @@ const PapillonNavigatorMenu: React.FC - {tabs.map((route, index) => ( + {tabs.map((route) => ( - {tabs.map((route, index) => ( + {tabs.map((route) => ( => { if (account.isExternal) { error( diff --git a/src/services/multi/default-personalization.ts b/src/services/multi/default-personalization.ts index 4598a1a03..c75060057 100644 --- a/src/services/multi/default-personalization.ts +++ b/src/services/multi/default-personalization.ts @@ -1,4 +1,4 @@ -import type { MultiAccount, Personalization } from "@/stores/account/types"; +import type { Personalization } from "@/stores/account/types"; import { defaultTabs } from "@/consts/DefaultTabs"; import colors from "@/utils/data/colors.json"; @@ -9,7 +9,7 @@ const defaultUphfTabs = [ "News", ] as typeof defaultTabs[number]["tab"][]; -const defaultPersonalization = async (instance: MultiAccount["instance"]): Promise> => { +const defaultPersonalization = async (): Promise> => { return { color: colors[0], magicEnabled: true, diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index 043f761c0..70dbc1c11 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -192,7 +192,7 @@ export const useAccounts = create()( accounts: >[], // When creating, we don't want the "instance" to be stored. - create: ({ instance, ...account }) => { + create: ({ ...account }) => { log(`Storing ${account.localID} (${"name" in account ? account.name : "no name"})`, "accounts:create"); set((state) => ({ diff --git a/src/stores/timetable/index.ts b/src/stores/timetable/index.ts index 4ddb91793..fec6f64cc 100644 --- a/src/stores/timetable/index.ts +++ b/src/stores/timetable/index.ts @@ -26,7 +26,7 @@ export const useTimetableStore = create()( injectClasses: (data: any) => { log("Replacing classes", "timetable:replaceClasses"); - set((state) => { + set(() => { return { timetables: data, }; diff --git a/src/views/account/Chat/Modals/Chat.tsx b/src/views/account/Chat/Modals/Chat.tsx index af893d764..b7c746bdc 100644 --- a/src/views/account/Chat/Modals/Chat.tsx +++ b/src/views/account/Chat/Modals/Chat.tsx @@ -122,7 +122,7 @@ const Chat: Screen<"Chat"> = ({ navigation, route }) => { onPress={() => navigation.navigate("ChatDetails", { handle: route.params.handle, recipients: recipients, - onThemeChange: async (updatedTheme) => { + onThemeChange: async () => { const theme = await GetThemeForChatId(route.params.handle.subject); setActualTheme(theme); } diff --git a/src/views/account/Grades/Graph/GradesAverage.tsx b/src/views/account/Grades/Graph/GradesAverage.tsx index ad71ac6dc..f00350d12 100644 --- a/src/views/account/Grades/Graph/GradesAverage.tsx +++ b/src/views/account/Grades/Graph/GradesAverage.tsx @@ -109,13 +109,13 @@ const GradesAverageGraph: React.FC = ({ hst = hst.filter((p) => isNaN(p.value) === false); graphRef.current?.updateData({ - xAxis: hst.length > 0 ? hst.map((p, i) => new Date(p.date).getTime()) : [Date.now()], + xAxis: hst.length > 0 ? hst.map((p) => new Date(p.date).getTime()) : [Date.now()], yAxis: hst.length > 0 ? hst.map((p) => p.value) : [10], }); }, [grades, account.instance]); const updateTo = useCallback( - (index: number, x: number, y: number) => { + (index: number) => { try { if (index < 0 || index > gradesHistoryRef.current.length - 1) return; if (!gradesHistoryRef.current[index]?.value) return; @@ -237,7 +237,7 @@ const GradesAverageGraph: React.FC = ({ }} > + xAxis={gradesHistory.map((p) => new Date(p.date).getTime() )} yAxis={gradesHistory.map((p) => !isNaN(p.value) ? p.value : (currentAvg ?? 10))} @@ -252,7 +252,7 @@ const GradesAverageGraph: React.FC = ({ ref={graphRef} animationDuration={400} onGestureUpdate={(x, y, index) => { - updateTo(index, x, y); + updateTo(index); }} onGestureEnd={() => { resetToOriginal(); diff --git a/src/views/account/Grades/Modals/Subject.tsx b/src/views/account/Grades/Modals/Subject.tsx index d482f9e40..091ef0b50 100644 --- a/src/views/account/Grades/Modals/Subject.tsx +++ b/src/views/account/Grades/Modals/Subject.tsx @@ -13,7 +13,7 @@ import React, { useEffect, useLayoutEffect, useState } from "react"; import { View, ScrollView } from "react-native"; import { Screen } from "@/router/helpers/types"; -const GradeSubjectScreen: Screen<"GradeSubject"> = ({ route, navigation }) => { +const GradeSubjectScreen: Screen<"GradeSubject"> = ({ route }) => { const { subject, allGrades } = route.params; const theme = useTheme(); diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 7e9e9bb3f..c5e2d8c2b 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -138,7 +138,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const renderWeek: ListRenderItem = ({ item }) => { const homeworksInWeek = homeworks[item] ?? []; - const sortedHomework = homeworksInWeek.sort((a, b) => new Date(a.due).getTime() - new Date(b.due).getTime()); + const sortedHomework = homeworksInWeek.toSorted((a, b) => new Date(a.due).getTime() - new Date(b.due).getTime()); const groupedHomework = sortedHomework.reduce((acc, curr) => { const dayName = getDayName(curr.due); @@ -167,7 +167,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { } // homework completed downstairs - acc[day] = acc[day].sort((a, b) => { + acc[day] = acc[day].toSorted((a, b) => { if (a.done === b.done) { return 0; // if both have the same status, keep the original order } @@ -232,7 +232,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { > {!isOnline && } - {groupedHomework && Object.keys(groupedHomework).map((day, index) => ( + {groupedHomework && Object.keys(groupedHomework).map((day) => ( void, changeIndex: (index: number) => void }> = ({ epochWeekNumber, oldPageIndex, showPicker, changeIndex }) => { +const HeaderCalendar: React.FC<{ epochWeekNumber: number, showPicker: () => void, changeIndex: (index: number) => void }> = ({ epochWeekNumber, showPicker, changeIndex }) => { const { width, isTablet } = useScreenDimensions(); const index = epochWeekNumber; @@ -37,14 +37,12 @@ const HeaderCalendar: React.FC<{ epochWeekNumber: number, oldPageIndex: number, epochWeekNumber={epochWeekNumber - 2} active={false} key={index - 2} - location="left" onPress={() => changeIndex(epochWeekNumber - 2)} /> changeIndex(epochWeekNumber - 1)} /> changeIndex(epochWeekNumber + 1)} /> changeIndex(epochWeekNumber + 2)} /> @@ -72,7 +68,7 @@ const HeaderCalendar: React.FC<{ epochWeekNumber: number, oldPageIndex: number, ); }; -const HeaderWeekComponent: React.FC<{ epochWeekNumber: number, active: boolean, location?: string, onPress?: () => void }> = ({ epochWeekNumber, active, location, onPress }) => { +const HeaderWeekComponent: React.FC<{ epochWeekNumber: number, active: boolean, onPress?: () => void }> = ({ epochWeekNumber, active, onPress }) => { const { colors } = useTheme(); return ( diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index 80b111cd7..b802e143b 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -122,7 +122,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { try { await updateTimetableForWeekInCache(account, weekNumber, force); - await fetchIcalData(account, force); + await fetchIcalData(account); currentlyLoadingWeeks.current.add(weekNumber); } finally { currentlyLoadingWeeks.current.delete(weekNumber); diff --git a/src/views/account/Restaurant/Modals/PaymentSuccess.tsx b/src/views/account/Restaurant/Modals/PaymentSuccess.tsx index 8440992e2..da4add95a 100644 --- a/src/views/account/Restaurant/Modals/PaymentSuccess.tsx +++ b/src/views/account/Restaurant/Modals/PaymentSuccess.tsx @@ -10,7 +10,7 @@ import { useEffect, useState } from "react"; import { View, Text } from "react-native"; import { FadeInDown, FadeOutUp } from "react-native-reanimated"; -const RestaurantPaymentSuccess: Screen<"RestaurantPaymentSuccess"> = ({ route, navigation }) => { +const RestaurantPaymentSuccess: Screen<"RestaurantPaymentSuccess"> = ({ route }) => { const { card, diff } = route.params; const theme = useTheme(); diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index 9b59bd6f0..63e5ebaa2 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -214,7 +214,7 @@ const HeaderItem = memo(({ header }) => { const displayModes = ["Semaine", "3 jours", "Journée"]; -const Week: Screen<"Week"> = ({ route, navigation }) => { +const Week: Screen<"Week"> = () => { const theme = useTheme(); const insets = useSafeAreaInsets(); const { isOnline } = useOnlineStatus(); @@ -267,7 +267,7 @@ const Week: Screen<"Week"> = ({ route, navigation }) => { requestAnimationFrame(async () => { try { await updateTimetableForWeekInCache(account as Account, weekNumber, force); - await fetchIcalData(account as Account, force); + await fetchIcalData(account as Account); } finally { setIsLoading(false); } @@ -279,16 +279,12 @@ const Week: Screen<"Week"> = ({ route, navigation }) => { await loadTimetableWeek(weekNumber); }, [loadTimetableWeek]); - // eslint-disable-next-line no-unused-vars - const [openedIcalModal, setOpenedIcalModal] = React.useState(false); - React.useEffect(() => { if (events.length === 0 && (account?.personalization?.icalURLs?.length || 0) > 0) { setIsLoading(true); requestAnimationFrame(async () => { const weekNumber = dateToEpochWeekNumber(new Date()); await loadTimetableWeek(weekNumber, true); - setOpenedIcalModal(false); }); } }, [account?.personalization?.icalURLs]); @@ -349,7 +345,6 @@ const Week: Screen<"Week"> = ({ route, navigation }) => { value="Importer mes cours" primary onPress={() => { - setOpenedIcalModal(true); setTimeout(() => { PapillonNavigation.current?.navigate("LessonsImportIcal", {}); }, 100); diff --git a/src/views/addon/AddonLogs.tsx b/src/views/addon/AddonLogs.tsx index c812fdc63..5fee9834e 100644 --- a/src/views/addon/AddonLogs.tsx +++ b/src/views/addon/AddonLogs.tsx @@ -4,7 +4,7 @@ import { CircleAlert, CircleX, Code, TriangleAlert } from "lucide-react-native"; import { Screen } from "@/router/helpers/types"; import { AddonLogs as Logs } from "@/addons/types"; -const AddonLogs: Screen<"AddonLogs"> = ({ navigation, route }) => { +const AddonLogs: Screen<"AddonLogs"> = ({ route }) => { const logs: Logs[] = route.params.logs.map((l) => { return { date: new Date(l.date), diff --git a/src/views/login/IdentityProvider/BackgroundIdentityProvider.tsx b/src/views/login/IdentityProvider/BackgroundIdentityProvider.tsx index 6b1294f92..c2d49a554 100644 --- a/src/views/login/IdentityProvider/BackgroundIdentityProvider.tsx +++ b/src/views/login/IdentityProvider/BackgroundIdentityProvider.tsx @@ -2,7 +2,7 @@ import { useCurrentAccount } from "@/stores/account"; import { Screen } from "@/router/helpers/types"; import { useEffect } from "react"; -const BackgroundIdentityProvider: Screen<"BackgroundIdentityProvider"> = ({ route, navigation }) => { +const BackgroundIdentityProvider: Screen<"BackgroundIdentityProvider"> = ({ navigation }) => { const account = useCurrentAccount((store) => store.account); useEffect(() => { diff --git a/src/views/login/IdentityProvider/providers/Multi.tsx b/src/views/login/IdentityProvider/providers/Multi.tsx index 94cc9b3a2..7c22d190c 100644 --- a/src/views/login/IdentityProvider/providers/Multi.tsx +++ b/src/views/login/IdentityProvider/providers/Multi.tsx @@ -53,7 +53,7 @@ const Muli_Login: Screen<"Multi_Login"> = ({ route, navigation }) => { instanceURL: route.params.instanceURL, refreshAuthToken: account.userData.refreshAuthToken || "", }, - personalization: await defaultPersonalization(account), + personalization: await defaultPersonalization(), serviceData: {}, providers: [] }; diff --git a/src/views/login/pronote/PronoteQRCode.tsx b/src/views/login/pronote/PronoteQRCode.tsx index 45d5ae5d8..a27dcd7be 100644 --- a/src/views/login/pronote/PronoteQRCode.tsx +++ b/src/views/login/pronote/PronoteQRCode.tsx @@ -171,10 +171,8 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { }, []); const handleBarCodeScanned = ({ - type, data, }: { - type: string; data: string; }) => { setScanned(true); diff --git a/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx b/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx index 813b796d9..84b0ae47e 100644 --- a/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx +++ b/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx @@ -99,7 +99,7 @@ const TurboselfAccountSelector: Screen<"TurboselfAccountSelector"> = ({ navigati padding: 10, gap: 10 }}> - {account.map((item, index) => ( + {account.map((item) => ( = ({ }); }; - const filteredAccounts = accounts.filter((acc, index) => { + const filteredAccounts = accounts.filter((acc) => { if (acc.isExternal) { return true; } diff --git a/src/views/settings/SettingsIcons.tsx b/src/views/settings/SettingsIcons.tsx index 90eae80b1..2af5b2f97 100644 --- a/src/views/settings/SettingsIcons.tsx +++ b/src/views/settings/SettingsIcons.tsx @@ -37,7 +37,7 @@ export const removeColor = (icon: string) => { return newName; }; -const SettingsIcons: Screen<"SettingsIcons"> = ({ navigation }) => { +const SettingsIcons: Screen<"SettingsIcons"> = () => { const theme = useTheme(); const { colors } = theme; const { showAlert } = useAlert(); diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index 5959df95f..02343ed84 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -12,7 +12,7 @@ import * as Clipboard from "expo-clipboard"; import { getDefaultProfilePicture } from "@/utils/GetRessources/GetDefaultProfilePicture"; import ResponsiveTextInput from "@/components/FirstInstallation/ResponsiveTextInput"; -const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { +const SettingsProfile: Screen<"SettingsProfile"> = () => { const theme = useTheme(); const insets = useSafeAreaInsets(); const account = useCurrentAccount((store) => store.account!); diff --git a/src/views/settings/SettingsSupport.tsx b/src/views/settings/SettingsSupport.tsx index 40a4d836f..809c1e2cf 100644 --- a/src/views/settings/SettingsSupport.tsx +++ b/src/views/settings/SettingsSupport.tsx @@ -15,7 +15,7 @@ import { useCurrentAccount, useAccounts } from "@/stores/account"; import { AccountService } from "@/stores/account/types"; import PackageJSON from "../../../package.json"; -const SettingsSupport: Screen<"SettingsSupport"> = ({ navigation }) => { +const SettingsSupport: Screen<"SettingsSupport"> = () => { const theme = useTheme(); const insets = useSafeAreaInsets(); diff --git a/src/views/settings/SettingsTrophies.tsx b/src/views/settings/SettingsTrophies.tsx index 3a0232d76..4e339ab0b 100644 --- a/src/views/settings/SettingsTrophies.tsx +++ b/src/views/settings/SettingsTrophies.tsx @@ -5,7 +5,7 @@ import { useTheme } from "@react-navigation/native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -const SettingsTrophies: Screen<"SettingsTrophies"> = ({ navigation }) => { +const SettingsTrophies: Screen<"SettingsTrophies"> = () => { const theme = useTheme(); const { colors } = theme; const insets = useSafeAreaInsets(); diff --git a/src/views/welcome/ChangelogScreen.tsx b/src/views/welcome/ChangelogScreen.tsx index bbe8bb76c..6540d8353 100644 --- a/src/views/welcome/ChangelogScreen.tsx +++ b/src/views/welcome/ChangelogScreen.tsx @@ -68,7 +68,7 @@ const ChangelogScreen: Screen<"ChangelogScreen"> = ({ route, navigation }) => { acknowledgeUpdate(); } }) - .catch((err) => { + .catch(() => { setLoading(false); setNotFound(true); }); diff --git a/src/widgets/Components/LastGrade.tsx b/src/widgets/Components/LastGrade.tsx index 09ad43506..adcc65b49 100644 --- a/src/widgets/Components/LastGrade.tsx +++ b/src/widgets/Components/LastGrade.tsx @@ -15,8 +15,7 @@ import { error } from "@/utils/logger/logger"; const LastGradeWidget = forwardRef(({ setLoading, - setHidden, - loading, + setHidden }: WidgetProps, ref) => { const theme = useTheme(); const { colors } = theme; diff --git a/src/widgets/Components/RestaurantBalance.tsx b/src/widgets/Components/RestaurantBalance.tsx index e611a6a68..d60bd59bd 100644 --- a/src/widgets/Components/RestaurantBalance.tsx +++ b/src/widgets/Components/RestaurantBalance.tsx @@ -12,8 +12,7 @@ import { balanceFromExternal } from "@/services/balance"; const RestaurantBalanceWidget = forwardRef(({ setLoading, - setHidden, - loading, + setHidden }: WidgetProps, ref) => { const theme = useTheme(); const { colors } = theme; diff --git a/src/widgets/Components/RestaurantQRCode.tsx b/src/widgets/Components/RestaurantQRCode.tsx index ba4e9a302..444ffce97 100644 --- a/src/widgets/Components/RestaurantQRCode.tsx +++ b/src/widgets/Components/RestaurantQRCode.tsx @@ -21,8 +21,7 @@ type NavigationProps = StackNavigationProp; const RestaurantQRCodeWidget = forwardRef(({ setLoading, - setHidden, - loading, + setHidden }: WidgetProps, ref) => { const theme = useTheme(); const { colors } = theme; From 0e621c9b8ea742a5cb189ce13e851fb27863470c Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 22 Mar 2025 12:11:23 +0100 Subject: [PATCH 0979/1144] fi(lint): unued assertion --- src/services/pronote/chats.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/pronote/chats.ts b/src/services/pronote/chats.ts index 64850b205..50e6dd168 100644 --- a/src/services/pronote/chats.ts +++ b/src/services/pronote/chats.ts @@ -103,7 +103,7 @@ export const createDiscussionRecipients = async (account: PronoteAccount): Promi throw new ErrorServiceUnauthenticated("pronote"); const recipientsALL = await Promise.all( - account.instance!.user!.resources.flatMap((resource) => + account.instance.user.resources.flatMap(() => [ pronote.EntityKind.Teacher, pronote.EntityKind.Personal From ada94dd9bb3ee68861a53c9c60b5f711585992d1 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 22 Mar 2025 12:12:01 +0100 Subject: [PATCH 0980/1144] feat(restaurant): use hook instead of static to use actualised version --- .../account/Restaurant/Modals/CardDetail.tsx | 48 +++++++++---------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index 2a17a3090..e2c62548b 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -24,24 +24,23 @@ import { error, warn } from "@/utils/logger/logger"; const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigation }) => { try { const { card } = route.params; - // eslint-disable-next-line no-unused-vars - const [cardData, setCardData] = useState(null); + const [cardData, setCardData] = useState(card); const theme = useTheme(); const account = useCurrentAccount((store) => store.account); const removeAccount = useAccounts((state) => state.remove); - const cardName = `Carte ${AccountService[route.params.card.service as AccountService]} ${account?.identity?.firstName ? "de " + account.identity.firstName : ""}`; + const cardName = `Carte ${AccountService[cardData.service as AccountService]} ${account?.identity?.firstName ? "de " + account.identity.firstName : ""}`; const updateCardData = async () => { try { const [balance, history] = await Promise.all([ - balanceFromExternal(route.params.card.account as ExternalAccount).catch((err) => { + balanceFromExternal(cardData.account as ExternalAccount).catch((err) => { warn(`Error fetching balance for account ${account?.name}:` + err, "CardDetail/balanceFromExternal"); return []; }), - reservationHistoryFromExternal(route.params.card.account as ExternalAccount).catch((err) => { + reservationHistoryFromExternal(cardData.account as ExternalAccount).catch((err) => { warn(`Error fetching history for account ${account?.name}:` + err, "CardDetail/reservationHistoryFromExternal"); return []; }) @@ -49,9 +48,8 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio setCardData({ ...card, - // @ts-expect-error - balance: balance, - history: history, + balance, + history, }); } catch (e) { @@ -84,7 +82,7 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio headerRight: () => ( ({ + ...cardData.theme.links?.map((link) => ({ label: link.label, subtitle: link.subtitle, sfSymbol: link.sfSymbol, @@ -107,7 +105,7 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio style: "destructive", onPress: () => { try { - removeAccount(card.account?.localID as string); + removeAccount(cardData.account?.localID as string); navigation.goBack(); } catch (e) { @@ -160,7 +158,7 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio }} pointerEvents={"none"} > - + @@ -198,7 +196,7 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio letterSpacing: 3.5, }} > - {formatCardIdentifier(card.account?.localID as string, 12, "")} + {formatCardIdentifier(cardData.account?.localID as string, 12, "")} @@ -208,7 +206,7 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio gap: 10, }} > - {card?.balance[0] && ( + {cardData?.balance[0] && ( = ({ route, navigatio fontFamily: "semibold", fontSize: 28, textAlign: "center", - color: card.balance[0].amount > 0 ? "#00C853" : "#FF1744", + color: cardData.balance[0].amount > 0 ? "#00C853" : "#FF1744", }} > - {card.balance[0].amount > 0 && "+"}{card.balance[0].amount.toFixed(2)} € + {cardData.balance[0].amount > 0 && "+"}{cardData.balance[0].amount.toFixed(2)} € )} - {card?.cardnumber && ( + {cardData?.cardnumber && ( navigation.navigate("RestaurantQrCode", { card: card })} + onPress={() => navigation.navigate("RestaurantQrCode", { card: cardData })} weight="light" activeScale={0.95} > @@ -277,7 +275,7 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio )} - {card?.balance[0].remaining !== null && ( + {cardData?.balance[0].remaining !== null && ( = ({ route, navigatio fontFamily: "semibold", fontSize: 26, lineHeight: 28, - color: card.balance[0].remaining > 1 ? "#00C853" : "#FF1744", + color: cardData.balance[0].remaining > 1 ? "#00C853" : "#FF1744", }} > - {card.balance[0].remaining.toFixed(0)} + {cardData.balance[0].remaining.toFixed(0)} } > @@ -299,15 +297,15 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio Repas restants - Tarif estimé à {card.balance[0].price?.toFixed(2)} € + Tarif estimé à {cardData.balance[0].price?.toFixed(2)} € )} - {card?.history.length > 0 && ( + {cardData?.history.length > 0 && ( - {card.history + {cardData.history .filter((event) => !isNaN(new Date(event.timestamp).getTime())) .sort((a: any, b: any) => b.timestamp - a.timestamp) .map((history, i) => ( @@ -315,7 +313,7 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio key={"cardhistory-"+i} leading={ = ({ route, navigatio Date: Sat, 22 Mar 2025 12:15:00 +0100 Subject: [PATCH 0981/1144] feat(esthetic): split the two useState var into pair --- src/views/settings/SettingsIcons.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/views/settings/SettingsIcons.tsx b/src/views/settings/SettingsIcons.tsx index 2af5b2f97..1b19e2c53 100644 --- a/src/views/settings/SettingsIcons.tsx +++ b/src/views/settings/SettingsIcons.tsx @@ -44,12 +44,12 @@ const SettingsIcons: Screen<"SettingsIcons"> = () => { const insets = useSafeAreaInsets(); const data = icones as { [key: string]: Icon[] }; - const [currentIcon, setIcon] = React.useState("default"); + const [currentIcon, setCurrentIcon] = React.useState("default"); useEffect(() => { if (!isExpoGo()) { getIconName().then((icon) => { - setIcon(icon); + setCurrentIcon(icon); }); } }, []); @@ -63,7 +63,7 @@ const SettingsIcons: Screen<"SettingsIcons"> = () => { if (!isExpoGo()) { setIconName(iconConstructName); - setIcon(iconConstructName); + setCurrentIcon(iconConstructName); } else { alertExpoGo(showAlert); } @@ -71,7 +71,7 @@ const SettingsIcons: Screen<"SettingsIcons"> = () => { else { if (!isExpoGo()) { setIconName(icon.id); - setIcon(icon.id); + setCurrentIcon(icon.id); } else { alertExpoGo(showAlert); } From a8ff2b2e5a3e0e91186f1c125f5092b82d8d8ee2 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 22 Mar 2025 12:15:33 +0100 Subject: [PATCH 0982/1144] feat(lint): simplify ternary --- src/views/settings/SettingsSupport.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/settings/SettingsSupport.tsx b/src/views/settings/SettingsSupport.tsx index 809c1e2cf..b3a27ea07 100644 --- a/src/views/settings/SettingsSupport.tsx +++ b/src/views/settings/SettingsSupport.tsx @@ -27,7 +27,7 @@ const SettingsSupport: Screen<"SettingsSupport"> = () => { const [description, setDescription] = useState(); const currentAccount = useCurrentAccount((store) => store.account!); - const AccountType = AccountService[currentAccount.service] !== "Local" && currentAccount.service !== AccountService.PapillonMultiService ? AccountService[currentAccount.service] : currentAccount.identityProvider ? currentAccount.identityProvider.name : "Compte local"; + const AccountType = AccountService[currentAccount.service] !== "Local" && currentAccount.service !== AccountService.PapillonMultiService ? AccountService[currentAccount.service] : currentAccount.identityProvider?.name ?? "Compte local"; const cantineAccounts = useAccounts((state) => state.accounts.filter((acc) => From 24c818285b2667a53e8bc12142fca323b1c47504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20GUERIN?= Date: Sat, 22 Mar 2025 14:24:33 +0100 Subject: [PATCH 0983/1144] fix(CardDetail): screen presentation on Android As formSheet presentation is not currently well-supported on Android, we have to use a modal for this screen on this platform. --- src/router/screens/views/index.ts | 4 ++-- src/views/account/Restaurant/Modals/CardDetail.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/router/screens/views/index.ts b/src/router/screens/views/index.ts index a36d85fe2..3fb523951 100644 --- a/src/router/screens/views/index.ts +++ b/src/router/screens/views/index.ts @@ -42,10 +42,10 @@ export default [ }), createScreen("RestaurantCardDetail", RestaurantCardDetail, { headerTitle: "Détail de la carte", - presentation: "formSheet", + presentation: Platform.OS == "android" ? "modal" : "formSheet", headerShown: true, headerLargeTitle: true, - headerTransparent: true, + headerTransparent: Platform.OS == "android" ? false : true, sheetCornerRadius: 16, sheetGrabberVisible: true, sheetExpandsWhenScrolledToEdge: true, diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index 2164bf33b..86516e693 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -77,7 +77,7 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio backgroundColor: "transparent", }, headerStyle: { - backgroundColor: theme.colors.card + "55", + backgroundColor: Platform.OS == "android" ? theme.colors.card : theme.colors.card + "55", }, headerBlurEffect: "regular", headerRight: () => ( From f6c348d03a28803ead434e370b578c24c67f6c82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20GUERIN?= Date: Sat, 22 Mar 2025 15:22:50 +0100 Subject: [PATCH 0984/1144] fix: do not show modal grabber on android --- src/components/Global/PapillonModernHeader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Global/PapillonModernHeader.tsx b/src/components/Global/PapillonModernHeader.tsx index 79ef50e8a..7a75f80cd 100644 --- a/src/components/Global/PapillonModernHeader.tsx +++ b/src/components/Global/PapillonModernHeader.tsx @@ -126,7 +126,7 @@ const LinearGradientModernHeader: React.FC = ({ children, out {children} - {outsideNav && + {outsideNav && Platform.OS == "ios" && Date: Sat, 22 Mar 2025 17:56:34 +0100 Subject: [PATCH 0985/1144] Update src/router/screens/views/index.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 𝕂𝕪𝕝𝕚𝕒𝕟 <164187100+Kgeek33@users.noreply.github.com> --- src/router/screens/views/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/router/screens/views/index.ts b/src/router/screens/views/index.ts index 3fb523951..1f9582cfb 100644 --- a/src/router/screens/views/index.ts +++ b/src/router/screens/views/index.ts @@ -45,7 +45,7 @@ export default [ presentation: Platform.OS == "android" ? "modal" : "formSheet", headerShown: true, headerLargeTitle: true, - headerTransparent: Platform.OS == "android" ? false : true, + headerTransparent: Platform.OS !== "android", sheetCornerRadius: 16, sheetGrabberVisible: true, sheetExpandsWhenScrolledToEdge: true, From 7b6ec307467816fabc5241e2381d81246ec27750 Mon Sep 17 00:00:00 2001 From: Tryon <68423470+tryon-dev@users.noreply.github.com> Date: Sat, 22 Mar 2025 17:56:48 +0100 Subject: [PATCH 0986/1144] Update src/views/account/Restaurant/Modals/CardDetail.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 𝕂𝕪𝕝𝕚𝕒𝕟 <164187100+Kgeek33@users.noreply.github.com> --- src/views/account/Restaurant/Modals/CardDetail.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index 8cb36a051..2265211f2 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -77,7 +77,7 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio backgroundColor: "transparent", }, headerStyle: { - backgroundColor: Platform.OS == "android" ? theme.colors.card : theme.colors.card + "55", + backgroundColor: Platform.OS === "android" ? theme.colors.card : theme.colors.card + "55", }, headerBlurEffect: "regular", headerRight: () => ( From 207c38928cb7894c3318a978c49b5f5bbbefade0 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 22 Mar 2025 18:44:50 +0100 Subject: [PATCH 0987/1144] =?UTF-8?q?feat:=20Ajout=20de=20la=20gestion=20d?= =?UTF-8?q?es=20devoirs=20personnalis=C3=A9s=20pour=20les=20comptes=20prim?= =?UTF-8?q?aires?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/stores/account/index.ts | 66 +++++++++++++++++++++++++++++++++++++ src/stores/account/types.ts | 2 ++ 2 files changed, 68 insertions(+) diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index 83280bcce..39f311570 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -20,6 +20,7 @@ import { useAttendanceStore } from "../attendance"; import {error, info, log} from "@/utils/logger/logger"; import {useMultiService} from "@/stores/multiService"; import {MultiServiceFeature, MultiServiceSpace} from "@/stores/multiService/types"; +import { Homework } from "@/services/shared/Homework"; /** * Store for the currently selected account. @@ -310,6 +311,71 @@ export const useAccounts = create()( // Return the updated account (to reuse the account directly) return accountMutated; }, + + /** + * Add a personalized homework to account + * @param localID ID Account where homework must be added + * @param homework Homework create by user + */ + addHomework: (localID: string, homework: Homework) => { + set((state) => ({ + accounts: state.accounts.map((account) => + account.localID === localID && !account.isExternal + ? { + ...account, + homeworks: [...(account.homeworks || []), homework], + } + : account + ), + })); + }, + + /** + * Update a personalized homework to account + * @param localID ID Account where homework must be updated + * @param homeworkID ID Homework to modify a specific homework + * @param updatedHomework Homework updated by user + */ + updateHomework: ( + localID: string, + homeworkID: string, + updatedHomework: Homework + ) => { + set((state) => ({ + accounts: state.accounts.map((account) => + account.localID === localID && !account.isExternal + ? { + ...account, + homeworks: account.homeworks?.map((devoir) => + devoir.id === homeworkID + ? { ...devoir, ...updatedHomework } + : devoir + ), + } + : account + ), + })); + }, + + /** + * Remove a personalized homework to account + * @param localID ID Account where homework must be remove + * @param homeworkID ID Homework to remove a specific homework + */ + removeHomework: (localID: string, homeworkID: string) => { + set((state) => ({ + accounts: state.accounts.map((account) => + account.localID === localID && !account.isExternal + ? { + ...account, + homeworks: account.homeworks?.filter( + (devoir) => devoir.id !== homeworkID + ), + } + : account + ), + })); + }, }), { name: "accounts-storage", diff --git a/src/stores/account/types.ts b/src/stores/account/types.ts index 770fd9f2c..eff631a4b 100644 --- a/src/stores/account/types.ts +++ b/src/stores/account/types.ts @@ -9,6 +9,7 @@ import type MultiAPI from "esup-multi.js"; import { SkolengoAuthConfig } from "@/services/skolengo/skolengo-types"; import { User as ScolengoAPIUser } from "scolengo-api/types/models/Common"; import { OnlinePayments } from "pawrd/dist"; +import { Homework } from "@/services/shared/Homework"; export interface Tab { name: string @@ -134,6 +135,7 @@ interface BaseAccount { last: string; }; personalization: Partial; + homeworks?: Array; } interface BaseExternalAccount { From 4d28b7ed4f23851f982fbadab5a58b8c8b082602 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 22 Mar 2025 18:49:50 +0100 Subject: [PATCH 0988/1144] =?UTF-8?q?fix:=20utilisation=20de=20l'import=20?= =?UTF-8?q?TouchableOpacity=20de=20react-native,=20non=20d=C3=A9pr=C3=A9ci?= =?UTF-8?q?=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/Homeworks.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 1d465fe36..c252db05c 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -12,7 +12,8 @@ import { RefreshControl, StyleSheet, TextInput, - ListRenderItem + ListRenderItem, + TouchableOpacity } from "react-native"; import { dateToEpochWeekNumber, epochWNToDate } from "@/utils/epochWeekNumber"; @@ -20,7 +21,6 @@ import * as StoreReview from "expo-store-review"; import HomeworkItem from "./Atoms/Item"; import { PressableScale } from "react-native-pressable-scale"; -import { TouchableOpacity } from "react-native-gesture-handler"; import { Book, CheckSquare, ChevronLeft, ChevronRight, CircleDashed, Search, X } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { BlurView } from "expo-blur"; From 9b4b2bfb8d6bacab65ddc64c48675dc93886a212 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 22 Mar 2025 19:01:38 +0100 Subject: [PATCH 0989/1144] =?UTF-8?q?feat:=20ajout=20d'un=20bouton=20pour?= =?UTF-8?q?=20cr=C3=A9er=20un=20devoir=20personnalis=C3=A9=20dans=20la=20v?= =?UTF-8?q?ue=20des=20devoirs=20+=20suppression=20bouton=20de=20recherche?= =?UTF-8?q?=20si=20`showPickerButtons`=20est=20true?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/Homeworks.tsx | 41 +++++++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index c252db05c..c1f35b340 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -21,7 +21,7 @@ import * as StoreReview from "expo-store-review"; import HomeworkItem from "./Atoms/Item"; import { PressableScale } from "react-native-pressable-scale"; -import { Book, CheckSquare, ChevronLeft, ChevronRight, CircleDashed, Search, X } from "lucide-react-native"; +import { Book, BookPlus, CheckSquare, ChevronLeft, ChevronRight, CircleDashed, Search, X } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { BlurView } from "expo-blur"; @@ -546,6 +546,41 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { /> } + {!searchHasFocus && ( + + { + // Afficher la modal pour créer un devoir personnalisé + }} + > + + + + )} + {showPickerButtons && !searchHasFocus && width > 330 && = ({ route, navigation }) => { } + {!showPickerButtons && = ({ route, navigation }) => { /> - {!showPickerButtons && = ({ route, navigation }) => { ref={SearchRef} /> - } {searchTerms.length > 0 && searchHasFocus && = ({ route, navigation }) => { } + } Date: Sat, 22 Mar 2025 20:39:42 +0100 Subject: [PATCH 0990/1144] =?UTF-8?q?chore:=20Mise=20=C3=A0=20jour=20de=20?= =?UTF-8?q?la=20version=20=C3=A0=207.10.6=20dans=20plusieurs=20fichiers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/bug.yml | 2 +- android/app/build.gradle | 4 ++-- android/app/src/main/AndroidManifest.xml | 4 ++++ ios/Papillon/Info.plist | 2 +- package.json | 2 +- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 979f3c179..4e0f00956 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -61,7 +61,7 @@ body: attributes: label: Version utilisée description: Paramètres (de Papillon) -> Version affichée en bas de la page - placeholder: "7.10.5" + placeholder: "7.10.6" validations: required: true diff --git a/android/app/build.gradle b/android/app/build.gradle index cbb2728fc..ed598785b 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -88,8 +88,8 @@ android { applicationId 'xyz.getpapillon.app' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 71050 - versionName "7.10.5" + versionCode 71060 + versionName "7.10.6" } signingConfigs { debug { diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index d3d18a18f..ca946b3e9 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -42,6 +42,10 @@ + + + + diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index ed6bf14a3..08eb9b57c 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 7.10.5 + 7.10.6 CFBundleSignature ???? CFBundleURLTypes diff --git a/package.json b/package.json index 8b682f102..9e27b5996 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "papillonvex", - "version": "7.10.5", + "version": "7.10.6", "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", From d0fcdeefbe9a6125eb6579828d01341cadd1ce34 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 22 Mar 2025 20:43:15 +0100 Subject: [PATCH 0991/1144] =?UTF-8?q?feat:=20ajout=20de=20la=20d=C3=A9pend?= =?UTF-8?q?ance=20@react-native-picker/picker=20en=20version=202.11.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 14 ++++++++++++++ package.json | 1 + 2 files changed, 15 insertions(+) diff --git a/package-lock.json b/package-lock.json index a8f585c3c..dcc2678b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@react-native-community/netinfo": "11.3.1", "@react-native-cookies/cookies": "^6.2.1", "@react-native-masked-view/masked-view": "0.3.1", + "@react-native-picker/picker": "^2.11.0", "@react-navigation/bottom-tabs": "^6.6.0", "@react-navigation/native": "^6.1.17", "@react-navigation/native-stack": "^6.9.26", @@ -5191,6 +5192,19 @@ "react-native": ">=0.57" } }, + "node_modules/@react-native-picker/picker": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@react-native-picker/picker/-/picker-2.11.0.tgz", + "integrity": "sha512-QuZU6gbxmOID5zZgd/H90NgBnbJ3VV6qVzp6c7/dDrmWdX8S0X5YFYgDcQFjE3dRen9wB9FWnj2VVdPU64adSg==", + "license": "MIT", + "workspaces": [ + "example" + ], + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/@react-native/assets-registry": { "version": "0.74.89", "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.74.89.tgz", diff --git a/package.json b/package.json index 8b682f102..ec736b139 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@react-native-community/netinfo": "11.3.1", "@react-native-cookies/cookies": "^6.2.1", "@react-native-masked-view/masked-view": "0.3.1", + "@react-native-picker/picker": "^2.11.0", "@react-navigation/bottom-tabs": "^6.6.0", "@react-navigation/native": "^6.1.17", "@react-navigation/native-stack": "^6.9.26", From ac4f597e1cbb0a62648dd33737db2b30aaf7c879 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 22 Mar 2025 22:30:15 +0100 Subject: [PATCH 0992/1144] =?UTF-8?q?refactor:=20simplification=20de=20la?= =?UTF-8?q?=20structure=20de=20rendu=20pour=20l'affichage=20de=20l'horloge?= =?UTF-8?q?=20et=20de=20la=20date=20d'=C3=A9ch=C3=A9ance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/Atoms/Item.tsx | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/views/account/Homeworks/Atoms/Item.tsx b/src/views/account/Homeworks/Atoms/Item.tsx index 322ffb1eb..1088bf647 100644 --- a/src/views/account/Homeworks/Atoms/Item.tsx +++ b/src/views/account/Homeworks/Atoms/Item.tsx @@ -236,17 +236,15 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total } /> - {route.name === "HomeScreen" && ( - - - {timestampToString(homework.due)} - - )} + + + {timestampToString(homework.due)} + {homework.attachments.length > 0 && ( Date: Sat, 22 Mar 2025 23:28:06 +0100 Subject: [PATCH 0993/1144] =?UTF-8?q?refactor:=20suppression=20des=20types?= =?UTF-8?q?=20explicites=20pour=20les=20param=C3=A8tres=20des=20m=C3=A9tho?= =?UTF-8?q?des=20addHomework,=20updateHomework=20et=20removeHomework?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/stores/account/index.ts | 11 +++++------ src/stores/account/types.ts | 7 +++++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index 39f311570..32f74e784 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -20,7 +20,6 @@ import { useAttendanceStore } from "../attendance"; import {error, info, log} from "@/utils/logger/logger"; import {useMultiService} from "@/stores/multiService"; import {MultiServiceFeature, MultiServiceSpace} from "@/stores/multiService/types"; -import { Homework } from "@/services/shared/Homework"; /** * Store for the currently selected account. @@ -317,7 +316,7 @@ export const useAccounts = create()( * @param localID ID Account where homework must be added * @param homework Homework create by user */ - addHomework: (localID: string, homework: Homework) => { + addHomework: (localID, homework) => { set((state) => ({ accounts: state.accounts.map((account) => account.localID === localID && !account.isExternal @@ -337,9 +336,9 @@ export const useAccounts = create()( * @param updatedHomework Homework updated by user */ updateHomework: ( - localID: string, - homeworkID: string, - updatedHomework: Homework + localID, + homeworkID, + updatedHomework ) => { set((state) => ({ accounts: state.accounts.map((account) => @@ -362,7 +361,7 @@ export const useAccounts = create()( * @param localID ID Account where homework must be remove * @param homeworkID ID Homework to remove a specific homework */ - removeHomework: (localID: string, homeworkID: string) => { + removeHomework: (localID, homeworkID) => { set((state) => ({ accounts: state.accounts.map((account) => account.localID === localID && !account.isExternal diff --git a/src/stores/account/types.ts b/src/stores/account/types.ts index eff631a4b..47eb6c112 100644 --- a/src/stores/account/types.ts +++ b/src/stores/account/types.ts @@ -311,4 +311,11 @@ export interface AccountsStore { key: T, value: A[T] ) => Account | null + addHomework: (localID: string, homework: Homework) => void; + updateHomework: ( + localID: string, + homeworkID: string, + homework: Homework + ) => void; + removeHomework: (localID: string, homeworkID: string) => void; } From 7dcf6f7719f71f79a12919ff39e8ead63ba31ca1 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 22 Mar 2025 23:37:31 +0100 Subject: [PATCH 0994/1144] =?UTF-8?q?feat:=20ajout=20de=20la=20propri?= =?UTF-8?q?=C3=A9t=C3=A9=20personalizate=20=C3=A0=20l'interface=20Homework?= =?UTF-8?q?=20et=20int=C3=A9gration=20de=20la=20cr=C3=A9ation=20des=20devo?= =?UTF-8?q?irs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/homework.ts | 5 + src/services/shared/Homework.ts | 1 + src/views/account/Homeworks/Atoms/Item.tsx | 5 +- src/views/account/Homeworks/Homeworks.tsx | 275 +++++++++++++++++++-- 4 files changed, 267 insertions(+), 19 deletions(-) diff --git a/src/services/homework.ts b/src/services/homework.ts index 447bddfde..7a6317a3d 100644 --- a/src/services/homework.ts +++ b/src/services/homework.ts @@ -58,6 +58,11 @@ export async function updateHomeworkForWeekInCache (account: console.info(`[updateHomeworkForWeekInCache]: updating to empty since ${account.service} not implemented.`); } + homeworks = homeworks.map((homework) => ({ + ...homework, + personalizate: false, + })); + useHomeworkStore.getState().updateHomeworks(dateToEpochWeekNumber(date), homeworks); } catch (err) { diff --git a/src/services/shared/Homework.ts b/src/services/shared/Homework.ts index 339864413..282d622e1 100644 --- a/src/services/shared/Homework.ts +++ b/src/services/shared/Homework.ts @@ -17,4 +17,5 @@ export interface Homework { done: boolean returnType?: HomeworkReturnType exam?: boolean + personalizate: boolean } \ No newline at end of file diff --git a/src/views/account/Homeworks/Atoms/Item.tsx b/src/views/account/Homeworks/Atoms/Item.tsx index 1088bf647..4d7a52da3 100644 --- a/src/views/account/Homeworks/Atoms/Item.tsx +++ b/src/views/account/Homeworks/Atoms/Item.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState, useCallback } from "react"; -import {Check, Clock, Paperclip, Sparkles, WifiOff} from "lucide-react-native"; +import {Check, Clock, Paperclip, PencilLine, Sparkles, WifiOff} from "lucide-react-native"; import { getSubjectData } from "@/services/shared/Subject"; import { useRoute, useTheme} from "@react-navigation/native"; import { NativeItem, NativeText } from "@/components/Global/NativeComponents"; @@ -195,6 +195,9 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total } > + {homework.personalizate && ( + + )} {subjectData.pretty} diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index c1f35b340..24fa4d507 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -1,5 +1,5 @@ -import { NativeList, NativeListHeader } from "@/components/Global/NativeComponents"; -import { useCurrentAccount } from "@/stores/account"; +import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; +import { useAccounts, useCurrentAccount } from "@/stores/account"; import { useHomeworkStore } from "@/stores/homework"; import { useTheme } from "@react-navigation/native"; import React, { useRef, useState, useCallback, useEffect } from "react"; @@ -13,7 +13,9 @@ import { StyleSheet, TextInput, ListRenderItem, - TouchableOpacity + TouchableOpacity, + ActivityIndicator, + Alert } from "react-native"; import { dateToEpochWeekNumber, epochWNToDate } from "@/utils/epochWeekNumber"; @@ -21,7 +23,7 @@ import * as StoreReview from "expo-store-review"; import HomeworkItem from "./Atoms/Item"; import { PressableScale } from "react-native-pressable-scale"; -import { Book, BookPlus, CheckSquare, ChevronLeft, ChevronRight, CircleDashed, Search, X } from "lucide-react-native"; +import { Book, BookPlus, CalendarClock, CheckSquare, ChevronLeft, ChevronRight, CircleDashed, Search, X } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { BlurView } from "expo-blur"; @@ -33,7 +35,7 @@ import * as Haptics from "expo-haptics"; import MissingItem from "@/components/Global/MissingItem"; import { PapillonModernHeader } from "@/components/Global/PapillonModernHeader"; import {Homework} from "@/services/shared/Homework"; -import {Account, AccountService} from "@/stores/account/types"; +import {AccountService} from "@/stores/account/types"; import {Screen} from "@/router/helpers/types"; import {NativeSyntheticEvent} from "react-native/Libraries/Types/CoreEventTypes"; import {NativeScrollEvent, ScrollViewProps} from "react-native/Libraries/Components/ScrollView/ScrollView"; @@ -42,17 +44,15 @@ import {hasFeatureAccountSetup} from "@/utils/multiservice"; import {MultiServiceFeature} from "@/stores/multiService/types"; import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; import { OfflineWarning, useOnlineStatus } from "@/hooks/useOnlineStatus"; +import ResponsiveTextInput from "@/components/FirstInstallation/ResponsiveTextInput"; +import BottomSheet from "@/components/Modals/PapillonBottomSheet"; +import ButtonCta from "@/components/FirstInstallation/ButtonCta"; +import { Picker } from "@react-native-picker/picker"; +import DateTimePicker from "@react-native-community/datetimepicker"; -type HomeworksPageProps = { - index: number; - isActive: boolean; - loaded: boolean; - homeworks: Record; - account: Account; - updateHomeworks: () => Promise; - loading: boolean; - getDayName: (date: string | number | Date) => string; -}; +const MemoizedNativeItem = React.memo(NativeItem); +const MemoizedNativeList = React.memo(NativeList); +const MemoizedNativeText = React.memo(NativeText); const formatDate = (date: string | number | Date): string => { return new Date(date).toLocaleDateString("fr-FR", { @@ -75,6 +75,10 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const account = useCurrentAccount(store => store.account!); const hasServiceSetup = account.service === AccountService.PapillonMultiService ? hasFeatureAccountSetup(MultiServiceFeature.Homeworks, account.localID) : true; const homeworks = useHomeworkStore(store => store.homeworks); + const localSubjects = account.personalization.subjects ?? {}; + const [selectedPretty, setSelectedPretty] = useState( + Object.entries(localSubjects || {})[0]?.[1] ?? null + ); // @ts-expect-error let firstDate = account?.instance?.instance?.firstDate || null; @@ -94,6 +98,13 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const [hideDone, setHideDone] = useState(false); + // Création de devoirs personnalisés + const [showCreateHomework, setShowCreateHomework] = useState(false); + const [showDatePicker, setShowDatePicker] = useState(false); + const [idHomework, setIdHomework] = useState(NaN); + const [contentHomework, setContentHomework] = useState(null); + const [dateHomework, setDateHomework] = useState(Date.now()); + const getItemLayout = useCallback((_: any, index: number) => ({ length: finalWidth, offset: finalWidth * index, @@ -156,9 +167,19 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const [searchTerms, setSearchTerms] = useState(""); const renderWeek: ListRenderItem = ({ item }) => { - const homeworksInWeek = homeworks[item] ?? []; + const homeworksInWeek = [...(homeworks[item] ?? [])]; + const homeworksPersonalized = account.homeworks ?? []; + + const personalizedForWeek = homeworksPersonalized.filter((element) => { + const weekHomework = dateToEpochWeekNumber(new Date(element.due)); + return weekHomework === item; + }); + + const combinedHomeworks = [...homeworksInWeek, ...personalizedForWeek]; - const sortedHomework = homeworksInWeek.sort((a, b) => new Date(a.due).getTime() - new Date(b.due).getTime()); + const sortedHomework = combinedHomeworks.sort( + (a, b) => new Date(a.due).getTime() - new Date(b.due).getTime() + ); const groupedHomework = sortedHomework.reduce((acc, curr) => { const dayName = getDayName(curr.due); @@ -569,7 +590,8 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { > { - // Afficher la modal pour créer un devoir personnalisé + setIdHomework(Math.random() * 1000 + 1); + setShowCreateHomework(true); }} > = ({ route, navigation }) => { } + { + setShowCreateHomework(bool); + if (!bool) { + setSelectedPretty(Object.entries(localSubjects || {})[0]?.[1] ?? null); + setIdHomework(NaN); + setContentHomework(null); + setContentHomework(null); + setDateHomework(Date.now()); + setLoading(false); + } + }} + contentContainerStyle={{ + paddingHorizontal: 16, + borderColor: theme.colors.border, + borderWidth: 1, + }} + > + + + Créer un devoir + + + + + + + Aperçu + + + undefined} + total={1} + /> + + + + + + + setShowDatePicker(true)} + chevron={false} + > + + Date du devoir + + + + + {new Date(dateHomework).toLocaleDateString( + "fr-FR", + { + day: "numeric", + month: "long", + year: "numeric", + } + )} + + + + + + + + + Nom de la matière + + + { + const selectedSubject = Object.entries(localSubjects).find( + ([, subject]) => subject.pretty === itemValue + ); + + if (selectedSubject) { + setSelectedPretty(selectedSubject[1]); + } + }} + style={{ + color: theme.colors.text, + }} + > + {Object.entries(localSubjects).map(([key, subject]) => ( + + ))} + + + + + + + + + + Contenu du devoir + + { + if (input === "") { + setContentHomework(null); + } else { + setContentHomework(input); + } + }} + /> + + + + {showDatePicker && ( + { + setShowDatePicker(false); + if (selectedDate) { + selectedDate.setHours(0, 0, 0, 0); + setDateHomework(selectedDate.getTime()); + } + }} + /> + )} + + { + setLoading(true); + + if (!selectedPretty || !contentHomework) { + Alert.alert("Veuillez remplir tous les champs avant de valider."); + setLoading(false); + return; + } + + // Créez un objet représentant le devoir + const newHomework: Homework = { + id: String(idHomework), + subject: selectedPretty.pretty, + color: selectedPretty.color, + content: contentHomework, + due: dateHomework, + done: false, + personalizate: true, + attachments: [], + exam: false, + }; + + useAccounts.getState().addHomework(account.localID, newHomework); + + setShowCreateHomework(false); + setSelectedPretty(Object.entries(localSubjects || {})[0]?.[1] ?? null); + setIdHomework(NaN); + setContentHomework(null); + setDateHomework(Date.now()); + setLoading(false); + }} + primary={!loading} + icon={loading ? : void 0} + disabled={loading} + style={{ + minWidth: undefined, + maxWidth: undefined, + width: "50%", + alignSelf: "center", + marginTop: 15, + }} + /> + + Date: Sat, 22 Mar 2025 23:44:21 +0100 Subject: [PATCH 0995/1144] =?UTF-8?q?feat:=20ajout=20de=20la=20gestion=20d?= =?UTF-8?q?es=20devoirs=20personnalis=C3=A9s=20pour=20les=20semaines=20act?= =?UTF-8?q?uelle=20et=20prochaine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Home/Elements/HomeworksElement.tsx | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 1b71ebf29..97d4d6660 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -23,6 +23,7 @@ interface HomeworksElementProps { const HomeworksElement: React.FC = ({ navigation, onImportance }) => { const account = useCurrentAccount(store => store.account!); const homeworks = useHomeworkStore(store => store.homeworks); + const homeworksPersonalized = account.homeworks ?? []; const [loading, setLoading] = useState(false); @@ -75,12 +76,29 @@ const HomeworksElement: React.FC = ({ navigation, onImpor const startTime = mtn.getTime() / 1000; const endTime = startTime + 7 * 24 * 60 * 60 * 1000; - const hwSemaineActuelle = homeworks[dateToEpochWeekNumber(actualDay)]?.filter( - (hw) => hw.due / 1000 >= startTime && hw.due / 1000 <= endTime - ) ?? []; - const hwSemaineProchaine = homeworks[dateToEpochWeekNumber(actualDay) + 1]?.filter( - (hw) => hw.due / 1000 >= startTime && hw.due / 1000 <= endTime - ) ?? []; + const allHwSemaineActuelle = + homeworks[dateToEpochWeekNumber(actualDay)] ?? []; + const personalizedForSemaineActuelle = homeworksPersonalized.filter( + (element) => { + const weekHomework = dateToEpochWeekNumber(new Date(element.due)); + return weekHomework === dateToEpochWeekNumber(actualDay); + } + ); + const hwSemaineActuelle = allHwSemaineActuelle + .concat(personalizedForSemaineActuelle) + .filter((hw) => hw.due / 1000 >= startTime && hw.due / 1000 <= endTime); + + const allHwSemaineProchaine = + homeworks[dateToEpochWeekNumber(actualDay) + 1] ?? []; + const personalizedForSemaineProchaine = homeworksPersonalized.filter( + (element) => { + const weekHomework = dateToEpochWeekNumber(new Date(element.due)); + return weekHomework === dateToEpochWeekNumber(actualDay) + 1; + } + ); + const hwSemaineProchaine = allHwSemaineProchaine + .concat(personalizedForSemaineProchaine) + .filter((hw) => hw.due / 1000 >= startTime && hw.due / 1000 <= endTime); if (loading) { return ( From be7ae45d5ba8eb27399c560c59055ce1a5452ca1 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 22 Mar 2025 23:45:21 +0100 Subject: [PATCH 0996/1144] =?UTF-8?q?feat:=20tri=20des=20devoirs=20par=20d?= =?UTF-8?q?ate=20d'=C3=A9ch=C3=A9ance=20dans=20l'affichage=20des=20devoirs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Home/Elements/HomeworksElement.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 97d4d6660..c7f360a26 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -172,6 +172,7 @@ const HomeworksElement: React.FC = ({ navigation, onImpor {hw2Semaines .slice(0, 7) + .sort((a, b) => a.due - b.due) .map((hw, index) => ( Date: Sun, 23 Mar 2025 01:09:10 +0100 Subject: [PATCH 0997/1144] =?UTF-8?q?feat:=20revert=20PR=20#730=20+=20Ajou?= =?UTF-8?q?t=20de=20la=20possibilit=C3=A9=20de=20modifier=20et=20supprimer?= =?UTF-8?q?=20un=20devoir=20personnalis=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/Document.tsx | 330 ++++++++++++++++++++--- 1 file changed, 298 insertions(+), 32 deletions(-) diff --git a/src/views/account/Homeworks/Document.tsx b/src/views/account/Homeworks/Document.tsx index f04dd5433..541d33965 100644 --- a/src/views/account/Homeworks/Document.tsx +++ b/src/views/account/Homeworks/Document.tsx @@ -13,26 +13,37 @@ import { TouchableOpacity, Platform, StyleSheet, + Alert, } from "react-native"; import { Homework, HomeworkReturnType } from "@/services/shared/Homework"; -import { getSubjectData } from "@/services/shared/Subject"; import * as WebBrowser from "expo-web-browser"; import { useTheme } from "@react-navigation/native"; import HTMLView from "react-native-htmlview"; import { Screen } from "@/router/helpers/types"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { PapillonModernHeader } from "@/components/Global/PapillonModernHeader"; -import { useCurrentAccount } from "@/stores/account"; +import { useAccounts, useCurrentAccount } from "@/stores/account"; import { AccountService } from "@/stores/account/types"; import getAndOpenFile from "@/utils/files/getAndOpenFile"; import { AutoFileIcon } from "@/components/Global/FileIcon"; -import { Paperclip, CircleAlert, FileUp, School } from "lucide-react-native"; +import { Paperclip, CircleAlert, PencilLine, MoreHorizontal, Trash2, CalendarClock } from "lucide-react-native"; import LinkFavicon, { getURLDomain } from "@/components/Global/LinkFavicon"; import { timestampToString } from "@/utils/format/DateHelper"; import parse_homeworks from "@/utils/format/format_pronote_homeworks"; -import { useAlert } from "@/providers/AlertProvider"; +import PapillonPicker from "@/components/Global/PapillonPicker"; +import BottomSheet from "@/components/Modals/PapillonBottomSheet"; +import HomeworkItem from "./Atoms/Item"; +import { Picker } from "@react-native-picker/picker"; +import ResponsiveTextInput from "@/components/FirstInstallation/ResponsiveTextInput"; +import ButtonCta from "@/components/FirstInstallation/ButtonCta"; +import DateTimePicker from "@react-native-community/datetimepicker"; +import { getSubjectData } from "@/services/shared/Subject"; -const HomeworksDocument: Screen<"HomeworksDocument"> = ({ route }) => { +const MemoizedNativeItem = React.memo(NativeItem); +const MemoizedNativeList = React.memo(NativeList); +const MemoizedNativeText = React.memo(NativeText); + +const HomeworksDocument: Screen<"HomeworksDocument"> = ({ navigation, route }) => { const theme = useTheme(); const stylesText = StyleSheet.create({ body: { @@ -47,9 +58,32 @@ const HomeworksDocument: Screen<"HomeworksDocument"> = ({ route }) => { }, }); - const homework: Homework = route.params.homework || {}; - const account = useCurrentAccount((store) => store.account!); + const localSubjects = account.personalization.subjects ?? {}; + const [selectedPretty, setSelectedPretty] = useState( + Object.entries(localSubjects || {})[0]?.[1] ?? null + ); + + // Création de devoirs personnalisés + const [showCreateHomework, setShowCreateHomework] = useState(false); + const [showDatePicker, setShowDatePicker] = useState(false); + const [idHomework, setIdHomework] = useState(NaN); + const [contentHomework, setContentHomework] = useState(null); + const [dateHomework, setDateHomework] = useState(Date.now()); + + const [homework, setHomework] = useState(route.params.homework || {}); + + useEffect(() => { + const pretty = Object.entries(localSubjects || {}).find( + (element) => element[1].pretty === homework.subject + )?.[1]; + + if (pretty) setSelectedPretty(pretty); + + setIdHomework(parseInt(homework.id)); + setContentHomework(homework.content); + setDateHomework(homework.due); + }, [homework]); const openUrl = (url: string) => { @@ -81,8 +115,6 @@ const HomeworksDocument: Screen<"HomeworksDocument"> = ({ route }) => { fetchSubjectData(); }, [homework.subject]); - const { showAlert } = useAlert(); - return ( @@ -125,29 +157,18 @@ const HomeworksDocument: Screen<"HomeworksDocument"> = ({ route }) => { > { - switch (homework.returnType) { - case "file_upload": - showAlert({ - title: "Tu dois rendre ce devoir sur ton ENT", - message: "Papillon ne permet pas de rendre des devoirs sur l'ENT. Tu dois le faire sur l'ENT de ton établissement", - icon: , - }); - break; - case "paper": - showAlert({ - title: "Tu dois rendre ce devoir en classe", - message: "Ton professeur t'indiquera comment rendre ce devoir", - icon: , - }); - break; - default: - showAlert({ - title: "Ce devoir est à rendre", - message: "Ton professeur t'indiquera comment rendre ce devoir.", - icon: , - }); - break; - } + Alert.alert( + homework.returnType === "file_upload" + ? "Tu dois rendre ce devoir sur ton ENT" + : homework.returnType === "paper" + ? "Tu dois rendre ce devoir en classe" + : "Ce devoir est à rendre", + homework.returnType === "file_upload" + ? "Papillon ne permet pas de rendre des devoirs sur l'ENT. Tu dois le faire sur l'ENT de ton établissement" + : homework.returnType === "paper" + ? "Ton professeur t'indiquera comment rendre ce devoir" + : "Ton professeur t'indiquera comment rendre ce devoir", + ); }} > = ({ route }) => { )} + {homework.personalizate && ( + , + label: "Modifier le devoir", + onPress: () => setShowCreateHomework(true), + }, + { + icon: , + label: "Supprimer le devoir", + onPress: () => { + Alert.alert( + "Supprimer le devoir", + "Veux-tu vraiment supprimer ce devoir ?", + [ + { + text: "Annuler", + isPreferred: true, + }, + { + text: "Continuer", + style: "destructive", + onPress: () => { + useAccounts.getState().removeHomework(account.localID, homework.id); + navigation.goBack(); + } + } + ] + ); + }, + }, + ]} + > + + + + + )} + { + setShowCreateHomework(bool); + if (!bool) { + setSelectedPretty(Object.entries(localSubjects || {})[0]?.[1] ?? null); + setIdHomework(NaN); + setContentHomework(null); + setContentHomework(null); + setDateHomework(Date.now()); + } + setHomework(homework); + }} + contentContainerStyle={{ + paddingHorizontal: 16, + borderColor: theme.colors.border, + borderWidth: 1, + }} + > + + + Modifier un devoir + + + + + + + Aperçu + + + undefined} + total={1} + /> + + + + + + + setShowDatePicker(true)} + chevron={false} + > + + Date du devoir + + + + + {new Date(dateHomework).toLocaleDateString( + "fr-FR", + { + day: "numeric", + month: "long", + year: "numeric", + } + )} + + + + + + + + + Nom de la matière + + + { + const selectedSubject = Object.entries(localSubjects).find( + ([, subject]) => subject.pretty === itemValue + ); + + if (selectedSubject) { + setSelectedPretty(selectedSubject[1]); + } + }} + style={{ + color: theme.colors.text, + }} + > + {Object.entries(localSubjects).map(([key, subject]) => ( + + ))} + + + + + + + + + + Contenu du devoir + + { + if (input === "") { + setContentHomework(null); + } else { + setContentHomework(input); + } + }} + /> + + + + {showDatePicker && ( + { + setShowDatePicker(false); + if (selectedDate) { + selectedDate.setHours(0, 0, 0, 0); + setDateHomework(selectedDate.getTime()); + } + }} + /> + )} + + { + if (!selectedPretty || !contentHomework) { + Alert.alert("Veuillez remplir tous les champs avant de valider."); + return; + } + + // Créez un objet représentant le devoir + const newHomework: Homework = { + ...homework, + subject: selectedPretty.pretty, + color: selectedPretty.color, + content: contentHomework, + due: dateHomework, + }; + + useAccounts.getState().updateHomework(account.localID, homework.id, newHomework); + + setShowCreateHomework(false); + setSelectedPretty(Object.entries(localSubjects || {})[0]?.[1] ?? null); + setIdHomework(NaN); + setContentHomework(null); + setDateHomework(Date.now()); + setHomework(newHomework); + }} + style={{ + minWidth: undefined, + maxWidth: undefined, + width: "50%", + alignSelf: "center", + marginTop: 15, + }} + /> + + Date: Sun, 23 Mar 2025 01:09:49 +0100 Subject: [PATCH 0998/1144] fix(ts): error --- src/views/account/Homeworks/Document.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/account/Homeworks/Document.tsx b/src/views/account/Homeworks/Document.tsx index 541d33965..51e0ef15a 100644 --- a/src/views/account/Homeworks/Document.tsx +++ b/src/views/account/Homeworks/Document.tsx @@ -272,6 +272,7 @@ const HomeworksDocument: Screen<"HomeworksDocument"> = ({ navigation, route }) = }} index={idHomework} key={idHomework} + // @ts-expect-error navigation={navigation} onDonePressHandler={() => undefined} total={1} From bb1f5c7cd4bcceab670fed50264f5a0adc66a883 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 23 Mar 2025 01:11:10 +0100 Subject: [PATCH 0999/1144] =?UTF-8?q?fix:=20simplification=20de=20la=20mis?= =?UTF-8?q?e=20=C3=A0=20jour=20des=20devoirs=20dans=20le=20store=20des=20c?= =?UTF-8?q?omptes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/stores/account/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index 32f74e784..3b662023d 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -346,9 +346,7 @@ export const useAccounts = create()( ? { ...account, homeworks: account.homeworks?.map((devoir) => - devoir.id === homeworkID - ? { ...devoir, ...updatedHomework } - : devoir + devoir.id === homeworkID ? updatedHomework : devoir ), } : account From c407375a617088a7daa5d539e5b9c624d35c23ce Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 23 Mar 2025 01:27:53 +0100 Subject: [PATCH 1000/1144] =?UTF-8?q?feat:=20ajout=20de=20la=20propri?= =?UTF-8?q?=C3=A9t=C3=A9=20'personalizate'=20dans=20les=20services=20de=20?= =?UTF-8?q?devoirs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/ecoledirecte/homework.ts | 1 + src/services/homework.ts | 5 ----- src/services/pronote/homework.ts | 3 ++- src/services/skolengo/data/homework.ts | 3 ++- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/services/ecoledirecte/homework.ts b/src/services/ecoledirecte/homework.ts index 44d512944..826088f93 100644 --- a/src/services/ecoledirecte/homework.ts +++ b/src/services/ecoledirecte/homework.ts @@ -46,6 +46,7 @@ export const getHomeworkForWeek = async ( id: homework.id.toString(), subject: homework.subject, exam: homework.exam, + personalizate: false, }); } } diff --git a/src/services/homework.ts b/src/services/homework.ts index 7a6317a3d..447bddfde 100644 --- a/src/services/homework.ts +++ b/src/services/homework.ts @@ -58,11 +58,6 @@ export async function updateHomeworkForWeekInCache (account: console.info(`[updateHomeworkForWeekInCache]: updating to empty since ${account.service} not implemented.`); } - homeworks = homeworks.map((homework) => ({ - ...homework, - personalizate: false, - })); - useHomeworkStore.getState().updateHomeworks(dateToEpochWeekNumber(date), homeworks); } catch (err) { diff --git a/src/services/pronote/homework.ts b/src/services/pronote/homework.ts index 30b368869..f5263325f 100644 --- a/src/services/pronote/homework.ts +++ b/src/services/pronote/homework.ts @@ -16,7 +16,8 @@ const decodeHomework = (h: pronote.Assignment): Homework => { done: h.done, returnType: (h.return && h.return.kind !== pronote.AssignmentReturnKind.None) ? h.return.kind === pronote.AssignmentReturnKind.Paper ? HomeworkReturnType.Paper : HomeworkReturnType.FileUpload - : void 0 + : void 0, + personalizate: false, }; }; diff --git a/src/services/skolengo/data/homework.ts b/src/services/skolengo/data/homework.ts index 5a0d6f1c1..891c6161e 100644 --- a/src/services/skolengo/data/homework.ts +++ b/src/services/skolengo/data/homework.ts @@ -17,7 +17,8 @@ const decodeHomework = (h: HomeworkAssignment): Homework => { content: (h.html && htmlToText(h.html || "") !== "") ? htmlToText(h.html) : h.title ?? "", due: h.dueDateTime ? new Date(h.dueDateTime).getTime() : -1, done: h.done, - returnType: h.deliverWorkOnline ? HomeworkReturnType.FileUpload : HomeworkReturnType.Paper + returnType: h.deliverWorkOnline ? HomeworkReturnType.FileUpload : HomeworkReturnType.Paper, + personalizate: false, }; }; From c0f30524fbfc14d01d58508ce5015f7312b4b838 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 23 Mar 2025 11:58:11 +0100 Subject: [PATCH 1001/1144] =?UTF-8?q?fix:=20am=C3=A9lioration=20des=20mess?= =?UTF-8?q?ages=20d'alerte=20et=20gestion=20du=20contenu=20des=20devoirs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: raphckrman <41128238+raphckrman@users.noreply.github.com> --- src/views/account/Homeworks/Document.tsx | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/views/account/Homeworks/Document.tsx b/src/views/account/Homeworks/Document.tsx index 51e0ef15a..4bfe3e6ee 100644 --- a/src/views/account/Homeworks/Document.tsx +++ b/src/views/account/Homeworks/Document.tsx @@ -165,9 +165,7 @@ const HomeworksDocument: Screen<"HomeworksDocument"> = ({ navigation, route }) = : "Ce devoir est à rendre", homework.returnType === "file_upload" ? "Papillon ne permet pas de rendre des devoirs sur l'ENT. Tu dois le faire sur l'ENT de ton établissement" - : homework.returnType === "paper" - ? "Ton professeur t'indiquera comment rendre ce devoir" - : "Ton professeur t'indiquera comment rendre ce devoir", + : "Ton professeur t'indiquera comment rendre ce devoir", ); }} > @@ -267,7 +265,7 @@ const HomeworksDocument: Screen<"HomeworksDocument"> = ({ navigation, route }) = ...homework, subject: selectedPretty.pretty, color: selectedPretty.color, - content: contentHomework ?? "Écris le contenu du devoir juste en-dessous :)", + content: contentHomework ?? "Écris le contenu du devoir juste en-dessous", due: dateHomework, }} index={idHomework} @@ -400,18 +398,13 @@ const HomeworksDocument: Screen<"HomeworksDocument"> = ({ navigation, route }) = { - if (!selectedPretty || !contentHomework) { - Alert.alert("Veuillez remplir tous les champs avant de valider."); - return; - } - - // Créez un objet représentant le devoir const newHomework: Homework = { ...homework, subject: selectedPretty.pretty, color: selectedPretty.color, - content: contentHomework, + content: contentHomework ?? "", due: dateHomework, }; From e25a7b2be98160460d6b708d857c85a4426b4290 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 23 Mar 2025 11:58:55 +0100 Subject: [PATCH 1002/1144] refractor: update version in package-lock.json --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index dcc2678b9..42860ef98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.10.5", + "version": "7.10.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.10.5", + "version": "7.10.6", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.4", "@candlefinance/app-icon": "^0.4.5", From 6eca183a5dc1e13b674022ca44d69c6cec4c40e8 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sun, 23 Mar 2025 14:54:44 +0100 Subject: [PATCH 1003/1144] feat(lint): esthetic --- src/components/Addons/AddonsWebview.tsx | 4 +- .../Global/PapillonModernHeader.tsx | 2 +- src/components/News/InitialIndicator.tsx | 2 +- src/views/account/Chat/Modals/Chat.tsx | 4 +- src/views/account/Chat/Modals/ChatDetails.tsx | 4 +- src/views/account/Evaluation/Document.tsx | 4 +- .../account/Grades/Modals/GradeReaction.tsx | 6 +-- src/views/addon/AddonLogs.tsx | 8 ++-- src/views/settings/Settings.tsx | 6 +-- src/views/settings/SettingsAddons.tsx | 40 +++++++++---------- .../settings/SettingsExternalServices.tsx | 2 +- src/views/settings/SettingsIcons.tsx | 2 +- src/views/settings/SettingsMagic.tsx | 2 +- src/views/settings/SettingsMultiService.tsx | 12 +++--- .../settings/SettingsMultiServiceSpace.tsx | 4 +- src/views/welcome/ColorSelector.tsx | 2 +- src/views/welcome/ProfilePic.tsx | 2 +- 17 files changed, 53 insertions(+), 53 deletions(-) diff --git a/src/components/Addons/AddonsWebview.tsx b/src/components/Addons/AddonsWebview.tsx index 6640b25c3..2faf443eb 100644 --- a/src/components/Addons/AddonsWebview.tsx +++ b/src/components/Addons/AddonsWebview.tsx @@ -187,14 +187,14 @@ const AddonsWebview: React.FC = ({ setShowAuthorizations(false)} style={{ minWidth: null, maxWidth: null, width: (Dimensions.get("window").width - 42) / 2 }} /> - setShowAuthorizations(false)} style={{ minWidth: null, maxWidth: null, width: (Dimensions.get("window").width - 42) / 2 }}/> + setShowAuthorizations(false)} style={{ minWidth: null, maxWidth: null, width: (Dimensions.get("window").width - 42) / 2 }} /> { error ? ( - + L'extension à planté... ) diff --git a/src/components/Global/PapillonModernHeader.tsx b/src/components/Global/PapillonModernHeader.tsx index 4093eb7ef..21d6366e2 100644 --- a/src/components/Global/PapillonModernHeader.tsx +++ b/src/components/Global/PapillonModernHeader.tsx @@ -27,7 +27,7 @@ export const PapillonModernHeader: React.FC = (props) => { } return ( - + ); }; diff --git a/src/components/News/InitialIndicator.tsx b/src/components/News/InitialIndicator.tsx index d3894f427..86e824db9 100644 --- a/src/components/News/InitialIndicator.tsx +++ b/src/components/News/InitialIndicator.tsx @@ -15,7 +15,7 @@ const InitialIndicator = ({ initial, color, textColor = "#FFF", size = 42 }: { i backgroundColor: color, }}> {initial === "group" ? ( - + ):( = ({ navigation, route }) => { {attachment.type === AttachmentType.File ? ( - + ) : ( )} @@ -385,7 +385,7 @@ const Chat: Screen<"Chat"> = ({ navigation, route }) => { sendMessageInChat(account, route.params.handle, text); }} > - + diff --git a/src/views/account/Chat/Modals/ChatDetails.tsx b/src/views/account/Chat/Modals/ChatDetails.tsx index 70d15f290..a71c3aefd 100644 --- a/src/views/account/Chat/Modals/ChatDetails.tsx +++ b/src/views/account/Chat/Modals/ChatDetails.tsx @@ -98,7 +98,7 @@ const ChatDetails: Screen<"ChatDetails"> = ({ navigation, route }) => { - + {recipients.slice(0, maxRecipientsShow).map((recipient, index) => ( = ({ navigation, route }) => { En afficher plus - + diff --git a/src/views/account/Evaluation/Document.tsx b/src/views/account/Evaluation/Document.tsx index c82c15b0a..508c3ec8c 100644 --- a/src/views/account/Evaluation/Document.tsx +++ b/src/views/account/Evaluation/Document.tsx @@ -213,7 +213,7 @@ const EvaluationDocument: Screen<"EvaluationDocument"> = ({ route, navigation }) } + icon={} > Déposé par = ({ route, navigation }) {evaluation.description !== "" && ( } + icon={} > Détails = ({ navigation, route }) => { value={"Accès à ta caméra"} backgroundColor={"#000"} primary={true} - icon={isCameraPermissionGranted == PermissionStatus.GRANTED ? : undefined} + icon={isCameraPermissionGranted == PermissionStatus.GRANTED ? : undefined} onPress={() => {isCameraPermissionGranted != PermissionStatus.GRANTED && Linking.openSettings();}} /> : undefined} + icon={isMediaLibraryPermissionGranted == PermissionStatus.GRANTED ? : undefined} onPress={() => {isMediaLibraryPermissionGranted != PermissionStatus.GRANTED && Linking.openSettings();}} /> @@ -250,7 +250,7 @@ const GradeReaction: Screen<"GradeReaction"> = ({ navigation, route }) => { {isLoading && ( - + Enregistrement en cours... )} diff --git a/src/views/addon/AddonLogs.tsx b/src/views/addon/AddonLogs.tsx index 5fee9834e..f78ca09dd 100644 --- a/src/views/addon/AddonLogs.tsx +++ b/src/views/addon/AddonLogs.tsx @@ -22,16 +22,16 @@ const AddonLogs: Screen<"AddonLogs"> = ({ route }) => { leading={ {log.type === "log" && ( - + )} {log.type === "error" && ( - + )} {log.type === "warn" && ( - + )} {log.type === "info" && ( - + )} } diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index b8585d7df..20090aa04 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -375,7 +375,7 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { /> {addons.length > 0 && <> - + {addons.map((addon, index) => ( = ({ route, navigation }) => { {devModeEnabled && ( - + navigation.navigate("SettingsDevLogs")} leading={ } + icon={} color={"#000"} style={{ marginLeft: -6, diff --git a/src/views/settings/SettingsAddons.tsx b/src/views/settings/SettingsAddons.tsx index 50e493538..614eccd17 100644 --- a/src/views/settings/SettingsAddons.tsx +++ b/src/views/settings/SettingsAddons.tsx @@ -99,7 +99,7 @@ const SettingsAddons: Screen<"SettingsAddons"> = () => { }}> - + Cette extension n'est pas signé Les extensions non signées ne sont pas vérifiées par nos équipes. Tu es responsable de l'installation de celle-ci. @@ -186,20 +186,20 @@ const SettingsAddons: Screen<"SettingsAddons"> = () => { : - permission.name == "PERM_APP_PHOTOS" ? : - permission.name == "PERM_APP_LOCATION" ? : - permission.name == "PERM_EDIT_PREFERENCES" ? : - permission.name == "PERM_EDIT_STUDENT_INFO" ? : - permission.name == "PERM_SCHOOLDATA_CALENDAR" ? : - permission.name == "PERM_SCHOOLDATA_AUTH" ? : - permission.name == "PERM_SCHOOLDATA_GRADES" ? : - permission.name == "PERM_SCHOOLDATA_NEWS" ? : - permission.name == "PERM_SCHOOLDATA_TIMETABLE" ? : - permission.name == "PERM_STUDENT_INFO" ? : - permission.name == "PERM_APP_LOGS" ? : - permission.name == "PERM_SCHOOLDATA_SELF" ? : - + permission.name == "PERM_APP_CAMERA" ? : + permission.name == "PERM_APP_PHOTOS" ? : + permission.name == "PERM_APP_LOCATION" ? : + permission.name == "PERM_EDIT_PREFERENCES" ? : + permission.name == "PERM_EDIT_STUDENT_INFO" ? : + permission.name == "PERM_SCHOOLDATA_CALENDAR" ? : + permission.name == "PERM_SCHOOLDATA_AUTH" ? : + permission.name == "PERM_SCHOOLDATA_GRADES" ? : + permission.name == "PERM_SCHOOLDATA_NEWS" ? : + permission.name == "PERM_SCHOOLDATA_TIMETABLE" ? : + permission.name == "PERM_STUDENT_INFO" ? : + permission.name == "PERM_APP_LOGS" ? : + permission.name == "PERM_SCHOOLDATA_SELF" ? : + } > @@ -229,7 +229,7 @@ const SettingsAddons: Screen<"SettingsAddons"> = () => { + } > @@ -270,7 +270,7 @@ const SettingsAddons: Screen<"SettingsAddons"> = () => { - + {storageAddons.map((addon, index) => ( = () => { Platform.OS == "ios" && ( + } onPress={() => { Linking.openURL(FileSystem.documentDirectory?.replace("file", "shareddocuments") + "addons"); @@ -335,7 +335,7 @@ const SettingsAddons: Screen<"SettingsAddons"> = () => { ) } - + = () => { - + = ({ paddingHorizontal: 16, }} > - + diff --git a/src/views/settings/SettingsIcons.tsx b/src/views/settings/SettingsIcons.tsx index 1b19e2c53..9c450ef23 100644 --- a/src/views/settings/SettingsIcons.tsx +++ b/src/views/settings/SettingsIcons.tsx @@ -85,7 +85,7 @@ const SettingsIcons: Screen<"SettingsIcons"> = () => { paddingTop: 0, }} > - + {Object.keys(data).map((key, index) => ( diff --git a/src/views/settings/SettingsMagic.tsx b/src/views/settings/SettingsMagic.tsx index 261cd2eb2..fd3b9dceb 100644 --- a/src/views/settings/SettingsMagic.tsx +++ b/src/views/settings/SettingsMagic.tsx @@ -17,7 +17,7 @@ const SettingsMagic: Screen<"SettingsMagic"> = () => { paddingHorizontal: 15, }} > - + diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index beddbc9cd..ac60fa07c 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -145,7 +145,7 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => paddingBottom: 25 }} > - + @@ -201,7 +201,7 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => {multiServiceEnabled && ( <> - + {multiServiceSpaces.map((space, index) => ( = ({ navigation }) => ))} setSpaceCreationSheetOpened(true)} - icon={} + icon={} > Nouvel espace @@ -259,7 +259,7 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => opened={spaceCreationSheetOpened} contentContainerStyle={{ paddingHorizontal: 16 }} > - + = ({ navigation }) => spaceNameRef.current?.focus()} chevron={false} - icon={} + icon={} > Titre de l'espace @@ -301,7 +301,7 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => strokeWidth={2.8} entering={animPapillon(ZoomIn)} exiting={animPapillon(ZoomOut)} - />: } + />: } trailing={selectedImage && = ({ naviga /> } onPress={() => openAccountSelector(feature.feature, feature.name)} - trailing={} + trailing={} chevron={false} > {feature.name} @@ -426,7 +426,7 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga paddingHorizontal: 10 }} > - + {availableAccounts.map((account, index) => ( = ({ route, navigation }) => { width={280} offsetTop={"10%"} /> - + {colorsList.slice(0, 3).map((color) => )} diff --git a/src/views/welcome/ProfilePic.tsx b/src/views/welcome/ProfilePic.tsx index 1466ebb8b..8c4264b1d 100644 --- a/src/views/welcome/ProfilePic.tsx +++ b/src/views/welcome/ProfilePic.tsx @@ -67,7 +67,7 @@ const ProfilePic: Screen<"ProfilePic"> = ({ navigation }) => { - + Date: Sun, 23 Mar 2025 15:01:02 +0100 Subject: [PATCH 1004/1144] feat: bouton de filtrage (regroupant 2 options) + simplification au maximum de la page (v1) --- src/views/account/Homeworks/Homeworks.tsx | 499 +++++++++------------- 1 file changed, 211 insertions(+), 288 deletions(-) diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index a70c27b84..26f1ae6a1 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -23,7 +23,7 @@ import * as StoreReview from "expo-store-review"; import HomeworkItem from "./Atoms/Item"; import { PressableScale } from "react-native-pressable-scale"; -import { Book, BookPlus, CalendarClock, CheckSquare, ChevronLeft, ChevronRight, CircleDashed, Search, X } from "lucide-react-native"; +import { BookPlus, CalendarClock, CheckSquare, ListFilter, Search, X } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { BlurView } from "expo-blur"; @@ -50,6 +50,7 @@ import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { Picker } from "@react-native-picker/picker"; import DateTimePicker from "@react-native-community/datetimepicker"; +const MemoizedHomeworkItem = React.memo(HomeworkItem); const MemoizedNativeItem = React.memo(NativeItem); const MemoizedNativeList = React.memo(NativeList); const MemoizedNativeText = React.memo(NativeText); @@ -64,7 +65,6 @@ const formatDate = (date: string | number | Date): string => { const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const flatListRef: React.MutableRefObject = useRef(null) as any as React.MutableRefObject; const { width } = Dimensions.get("window"); - const finalWidth = width; const insets = useSafeAreaInsets(); const { playHaptics } = useSoundHapticsWrapper(); const { isOnline } = useOnlineStatus(); @@ -93,10 +93,10 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const [data, setData] = useState(Array.from({ length: 100 }, (_, i) => currentWeek - 50 + i)); const [selectedWeek, setSelectedWeek] = useState(currentWeek); - const [direction, setDirection] = useState<"left" | "right">("right"); - const [oldSelectedWeek, setOldSelectedWeek] = useState(selectedWeek); + // Filtrer les devoirs const [hideDone, setHideDone] = useState(false); + const [showFilter, setShowFilter] = useState(false); // Création de devoirs personnalisés const [showCreateHomework, setShowCreateHomework] = useState(false); @@ -106,8 +106,8 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const [dateHomework, setDateHomework] = useState(Date.now()); const getItemLayout = useCallback((_: any, index: number) => ({ - length: finalWidth, - offset: finalWidth * index, + length: width, + offset: width * index, index, }), [width]); @@ -152,16 +152,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { // on page change, load the homeworks useEffect(() => { - if (selectedWeek > oldSelectedWeek) { - setDirection("right"); - } else if (selectedWeek < oldSelectedWeek) { - setDirection("left"); - } - - setTimeout(() => { - setOldSelectedWeek(selectedWeek); - updateHomeworks(false, false); - }, 0); + updateHomeworks(false, false); }, [selectedWeek]); const [searchTerms, setSearchTerms] = useState(""); @@ -257,7 +248,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { return ( = ({ route, navigation }) => { > {!isOnline && } - {groupedHomework && Object.keys(groupedHomework).map((day, index) => ( + {groupedHomework && Object.keys(groupedHomework).map((day) => ( = ({ route, navigation }) => { {groupedHomework[day].map((homework, idx) => ( - = ({ route, navigation }) => { layout={animPapillon(LinearTransition)} key={searchTerms + hideDone} > - {searchTerms.length > 0 ? + {searchTerms.length > 0 ? ( - : - hideDone ? - - : hasServiceSetup ? - - : } + ) : hideDone ? ( + + ) : hasServiceSetup ? ( + + ) : ( + + )} } @@ -356,78 +348,27 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { }; const onScroll: ScrollViewProps["onScroll"] = useCallback(({ nativeEvent }: NativeSyntheticEvent) => { - if (nativeEvent.contentOffset.x < finalWidth) { + if (nativeEvent.contentOffset.x < width) { onStartReached(); } // Update selected week based on scroll position - const index = Math.round(nativeEvent.contentOffset.x / finalWidth); + const index = Math.round(nativeEvent.contentOffset.x / width); setSelectedWeek(data[index]); - }, [finalWidth, data]); + }, [width, data]); - const onMomentumScrollEnd: ScrollViewProps["onMomentumScrollEnd"] = useCallback(({ nativeEvent }: NativeSyntheticEvent) => { - const index = Math.round(nativeEvent.contentOffset.x / finalWidth); + const onMomentumScrollEnd = useCallback(({ nativeEvent }: NativeSyntheticEvent) => { + const index = Math.round(nativeEvent.contentOffset.x / width); setSelectedWeek(data[index]); - }, [finalWidth, data]); - - const goToWeek = useCallback((weekNumber: number) => { - const index = data.findIndex(week => week === weekNumber); - if (index !== -1) { - // @ts-expect-error - const currentIndex = Math.round(flatListRef.current?.contentOffset?.x / finalWidth) || 0; - const distance = Math.abs(index - currentIndex); - const animated = distance <= 10; // Animate if the distance is 10 weeks or less - - flatListRef.current?.scrollToIndex({ index, animated }); - setSelectedWeek(weekNumber); - } else { - // If the week is not in the current data, update the data and scroll - const newData = Array.from({ length: 100 }, (_, i) => weekNumber - 50 + i); - setData(newData); - - // Use a timeout to ensure the FlatList has updated before scrolling - setTimeout(() => { - flatListRef.current?.scrollToIndex({ index: 50, animated: false }); - setSelectedWeek(weekNumber); - }, 0); - } - }, [data, finalWidth]); + }, [width, data]); - const [showPickerButtons, setShowPickerButtons] = useState(false); const [searchHasFocus, setSearchHasFocus] = useState(false); const SearchRef: React.MutableRefObject = useRef(null) as any as React.MutableRefObject; return ( - + <> - {showPickerButtons && !searchHasFocus && - - goToWeek(selectedWeek - 1)} - activeScale={0.8} - > - - - - - - } - - {!searchHasFocus && = ({ route, navigation }) => { > setShowPickerButtons(!showPickerButtons)} + onPress={() => undefined} // Afficher DatePicker onLongPress={() => { setHideDone(!hideDone); playHaptics("notification", { @@ -447,9 +388,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { = ({ route, navigation }) => { }]} tint={theme.dark ? "dark" : "light"} > - {showPickerButtons && !loading && - - - - } - - {!showPickerButtons && hideDone && - - - - } - = ({ route, navigation }) => { value={((selectedWeek - firstDateEpoch % 52) % 52 + 1).toString()} style={[styles.weekPickerText, styles.weekPickerTextNbr, { - color: showPickerButtons ? theme.colors.primary : theme.colors.text, + color: theme.colors.text, } ]} /> @@ -519,7 +425,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { {loading && = ({ route, navigation }) => { - } - - {showPickerButtons && !searchHasFocus && - - goToWeek(selectedWeek + 1)} - activeScale={0.8} - > - - - - - - } - {showPickerButtons && !searchHasFocus && - - } - - {!searchHasFocus && ( - - { - setIdHomework(Math.random() * 1000 + 1); - setShowCreateHomework(true); - }} - > - - - - )} + - {showPickerButtons && !searchHasFocus && width > 330 && = ({ route, navigation }) => { style={{ alignItems: "center", justifyContent: "center", - backgroundColor: hideDone ? theme.colors.primary : theme.colors.background + "ff", + backgroundColor: theme.colors.background + "ff", borderColor: theme.colors.border + "dd", borderWidth: 1, borderRadius: 800, height: 40, - width: showPickerButtons ? 40 : null, - minWidth: showPickerButtons ? 40 : null, - maxWidth: showPickerButtons ? 40 : null, + width: 40, gap: 4, shadowColor: "#00000022", shadowOffset: { width: 0, height: 2 }, @@ -630,38 +468,31 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { > { - setHideDone(!hideDone); + setIdHomework(Math.random() * 1000 + 1); + setShowCreateHomework(true); }} > - - } - {!showPickerButtons && = ({ route, navigation }) => { > { - setShowPickerButtons(false); - - setTimeout(() => { - // #TODO : change timeout method or duration - SearchRef.current?.focus(); - }, 20); + setShowFilter(true); }} > - + + - - + + + Filtrer les devoirs + + + + + + + Via Recherche + + setSearchHasFocus(true)} - onBlur={() => setSearchHasFocus(false)} - ref={SearchRef} - /> - - - {searchTerms.length > 0 && searchHasFocus && - { - setSearchTerms(""); - }} - > - - + SearchRef.current?.focus()}> + + + + + setSearchHasFocus(true)} + onBlur={() => setSearchHasFocus(false)} + ref={SearchRef} + /> + + + {searchTerms.length > 0 && ( + setSearchTerms("")} + > + + + + + )} - - } - - } - + + + + + + { + setHideDone(!hideDone); + }} + > + + + + )} + > + + {hideDone ? "Afficher tous les devoirs" : "Afficher uniquement les devoirs non faits"} + + + + = ({ route, navigation }) => { Aperçu - = ({ route, navigation }) => { height: "100%", }} /> - + ); }; From 02427c7676bce05cb767ac73a74f5b799b19ad73 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 23 Mar 2025 15:38:34 +0100 Subject: [PATCH 1005/1144] =?UTF-8?q?feat:=20ajout=20d'un=20s=C3=A9lecteur?= =?UTF-8?q?=20de=20date=20pour=20filtrer=20les=20devoirs=20par=20semaine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/Homeworks.tsx | 26 +++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 26f1ae6a1..a613f8406 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -17,7 +17,7 @@ import { ActivityIndicator, Alert } from "react-native"; -import { dateToEpochWeekNumber, epochWNToDate } from "@/utils/epochWeekNumber"; +import { dateToEpochWeekNumber, epochWNToDate, weekNumberToMiddleDate } from "@/utils/epochWeekNumber"; import * as StoreReview from "expo-store-review"; @@ -95,6 +95,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const [selectedWeek, setSelectedWeek] = useState(currentWeek); // Filtrer les devoirs + const [showDatePickerWeek, setShowDatePickerWeek] = useState(false); const [hideDone, setHideDone] = useState(false); const [showFilter, setShowFilter] = useState(false); @@ -376,7 +377,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { > undefined} // Afficher DatePicker + onPress={() => setShowDatePickerWeek(true)} onLongPress={() => { setHideDone(!hideDone); playHaptics("notification", { @@ -439,6 +440,27 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { + {showDatePickerWeek && ( + { + setShowDatePickerWeek(false); + if (selectedDate) { + selectedDate.setHours(0, 0, 0, 0); + + const weekNumber = dateToEpochWeekNumber(selectedDate); + const index = data.findIndex((week) => week === weekNumber); + if (index !== -1) { + flatListRef.current?.scrollToIndex({ index, animated: true }); + setSelectedWeek(weekNumber); + } + } + }} + /> + )} + Date: Sun, 23 Mar 2025 18:28:55 +0100 Subject: [PATCH 1006/1144] =?UTF-8?q?feat:=20remplacement=20de=20NativeLis?= =?UTF-8?q?t=20par=20MemoizedNativeList=20pour=20am=C3=A9liorer=20les=20pe?= =?UTF-8?q?rformances?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/Homeworks.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index a613f8406..6de641c2f 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -274,7 +274,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { > - + {groupedHomework[day].map((homework, idx) => ( = ({ route, navigation }) => { }} /> ))} - + ))} From 017fcca4968d14f719e5d487ebb71abda5e567f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20GUERIN?= Date: Sun, 23 Mar 2025 18:43:29 +0100 Subject: [PATCH 1007/1144] feat: add custom title for `PapillonHeader` --- src/components/Global/PapillonHeader.tsx | 9 +++++--- src/components/Global/TabAnimatedTitle.tsx | 27 +++++++++++++--------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/components/Global/PapillonHeader.tsx b/src/components/Global/PapillonHeader.tsx index ffaa633ff..4893568a8 100644 --- a/src/components/Global/PapillonHeader.tsx +++ b/src/components/Global/PapillonHeader.tsx @@ -12,7 +12,8 @@ import { PapillonModernHeader } from "./PapillonModernHeader"; interface PapillonHeaderProps { children?: React.ReactNode route: RouteProp - navigation: NativeStackNavigationProp + navigation: NativeStackNavigationProp, + title?: string } interface PapillonHeaderInsetHeightProps { @@ -22,7 +23,8 @@ interface PapillonHeaderInsetHeightProps { const PapillonHeader: React.FC = ({ children, route, - navigation + navigation, + title }) => { const theme = useTheme(); const insets = useSafeAreaInsets(); @@ -39,6 +41,7 @@ const PapillonHeader: React.FC = ({ display: "flex", flexDirection: "row", justifyContent: "space-between", + alignItems: "center" }} > {route.params?.outsideNav && Platform.OS !== "ios" && ( @@ -56,6 +59,7 @@ const PapillonHeader: React.FC = ({ route={route} navigation={navigation} style={{ paddingHorizontal: 0 }} + title={title} /> = ({ flexDirection: "row", justifyContent: "flex-end", alignItems: "center", - marginRight: Platform.OS !== "ios" ? -16 : 0, }} > {children} diff --git a/src/components/Global/TabAnimatedTitle.tsx b/src/components/Global/TabAnimatedTitle.tsx index 3194cca76..c534efd03 100644 --- a/src/components/Global/TabAnimatedTitle.tsx +++ b/src/components/Global/TabAnimatedTitle.tsx @@ -10,16 +10,18 @@ import type { NativeStackNavigationProp } from "@react-navigation/native-stack"; interface TabAnimatedTitleProps { route: RouteProp; navigation?: NativeStackNavigationProp; - style?: StyleProp + style?: StyleProp, + title?: string } -const TabAnimatedTitle = ({ route, navigation }: TabAnimatedTitleProps) => { +const TabAnimatedTitle = ({ route, navigation, title }: TabAnimatedTitleProps) => { return { headerTitle: () => , headerLeft: () => ( ), headerRight: () => ( @@ -32,21 +34,24 @@ const TabAnimatedTitle = ({ route, navigation }: TabAnimatedTitleProps) => { }; }; -const TabAnimatedTitleLeft = ({ route, style }: TabAnimatedTitleProps) => { +const TabAnimatedTitleLeft = ({ route, style, title }: TabAnimatedTitleProps) => { const theme = useTheme(); + const icon = defaultTabs.find((curr) => curr.tab === route.name)?.icon; return ( - curr.tab === route.name)?.icon} - autoPlay - loop={false} - style={styles.lottieView} - colorFilters={[{ keypath: "*", color: theme.colors.text }]} - /> + {icon && + + } - {defaultTabs.find((curr) => curr.tab === route.name)?.label} + {defaultTabs.find((curr) => curr.tab === route.name)?.label ?? title} ); From fe0dc7fd50bf1e4e96a487509be3b70dfce21e73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20GUERIN?= Date: Sun, 23 Mar 2025 18:44:05 +0100 Subject: [PATCH 1008/1144] fix(CardDetails): switch from native header to `PapillonHeader` --- src/router/helpers/types.ts | 1 + src/router/screens/views/index.ts | 5 +-- src/views/account/Restaurant/Menu.tsx | 2 +- .../account/Restaurant/Modals/CardDetail.tsx | 43 +++++++------------ 4 files changed, 18 insertions(+), 33 deletions(-) diff --git a/src/router/helpers/types.ts b/src/router/helpers/types.ts index c461095ff..6d3810d93 100644 --- a/src/router/helpers/types.ts +++ b/src/router/helpers/types.ts @@ -156,6 +156,7 @@ export type RouteParameters = { }; RestaurantCardDetail: { card: ServiceCard; + outsideNav?: boolean; }; RestaurantPaymentSuccess: { card: ServiceCard; diff --git a/src/router/screens/views/index.ts b/src/router/screens/views/index.ts index 1f9582cfb..2632c3f76 100644 --- a/src/router/screens/views/index.ts +++ b/src/router/screens/views/index.ts @@ -41,11 +41,8 @@ export default [ presentation: "modal", }), createScreen("RestaurantCardDetail", RestaurantCardDetail, { - headerTitle: "Détail de la carte", + headerShown: false, presentation: Platform.OS == "android" ? "modal" : "formSheet", - headerShown: true, - headerLargeTitle: true, - headerTransparent: Platform.OS !== "android", sheetCornerRadius: 16, sheetGrabberVisible: true, sheetExpandsWhenScrolledToEdge: true, diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 8100ba031..22c7b6fce 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -407,7 +407,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { key={index} card={card} onPress={() => { - navigation.navigate("RestaurantCardDetail", { card }); + navigation.navigate("RestaurantCardDetail", { card, outsideNav: true }); }} /> diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index 2265211f2..0cccb97ec 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -12,14 +12,15 @@ import InsetsBottomView from "@/components/Global/InsetsBottomView"; import { PressableScale } from "react-native-pressable-scale"; import { useAccounts, useCurrentAccount } from "@/stores/account"; import { AccountService, ExternalAccount } from "@/stores/account/types"; -import { ExternalLink, MoreHorizontal, QrCode, Trash2 } from "lucide-react-native"; +import { ExternalLink, MoreVertical, QrCode, Trash2 } from "lucide-react-native"; import { balanceFromExternal } from "@/services/balance"; import { reservationHistoryFromExternal } from "@/services/reservation-history"; import { Screen } from "@/router/helpers/types"; import { LinearGradient } from "expo-linear-gradient"; -import { TouchableOpacity } from "react-native-gesture-handler"; -import PapillonPicker from "@/components/Global/PapillonPicker"; import { formatCardIdentifier } from "@/utils/external/restaurant"; +import PapillonHeader, { PapillonHeaderInsetHeight } from "@/components/Global/PapillonHeader"; +import PapillonPicker from "@/components/Global/PapillonPicker"; +import { PapillonHeaderAction } from "@/components/Global/PapillonModernHeader"; const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigation }) => { try { @@ -67,21 +68,13 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio return unsubscribe; }, []); - React.useLayoutEffect(() => { - navigation.setOptions({ - headerTitle: cardName ?? "Détail de la carte", - headerLargeTitleStyle: { - color: "transparent", - }, - headerLargeStyle: { - backgroundColor: "transparent", - }, - headerStyle: { - backgroundColor: Platform.OS === "android" ? theme.colors.card : theme.colors.card + "55", - }, - headerBlurEffect: "regular", - headerRight: () => ( + return ( + <> + ({ label: link.label, @@ -120,18 +113,11 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio } ]} > - - - + } + /> - ), - }); - }, [navigation, theme]); - - return ( - <> + = ({ route, navigatio padding: 16, }} > + Date: Sun, 23 Mar 2025 18:51:35 +0100 Subject: [PATCH 1009/1144] fix: do not display remaining lunches if remaining is infinity --- src/views/account/Restaurant/Modals/CardDetail.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index 0cccb97ec..f2112bfd8 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -263,7 +263,7 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio )} - {card?.balance[0].remaining !== null && ( + {card?.balance[0].remaining !== null && card?.balance[0].remaining !== Infinity && ( Date: Sun, 23 Mar 2025 18:51:37 +0100 Subject: [PATCH 1010/1144] refactor: optimisation des fonctions de formatage et de rendu dans la vue des devoirs --- src/views/account/Homeworks/Homeworks.tsx | 64 +++++++++++++---------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 6de641c2f..64fac751a 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -38,7 +38,7 @@ import {Homework} from "@/services/shared/Homework"; import {AccountService} from "@/stores/account/types"; import {Screen} from "@/router/helpers/types"; import {NativeSyntheticEvent} from "react-native/Libraries/Types/CoreEventTypes"; -import {NativeScrollEvent, ScrollViewProps} from "react-native/Libraries/Components/ScrollView/ScrollView"; +import {NativeScrollEvent} from "react-native/Libraries/Components/ScrollView/ScrollView"; import AsyncStorage from "@react-native-async-storage/async-storage"; import {hasFeatureAccountSetup} from "@/utils/multiservice"; import {MultiServiceFeature} from "@/stores/multiService/types"; @@ -55,13 +55,6 @@ const MemoizedNativeItem = React.memo(NativeItem); const MemoizedNativeList = React.memo(NativeList); const MemoizedNativeText = React.memo(NativeText); -const formatDate = (date: string | number | Date): string => { - return new Date(date).toLocaleDateString("fr-FR", { - day: "numeric", - month: "long" - }); -}; - const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const flatListRef: React.MutableRefObject = useRef(null) as any as React.MutableRefObject; const { width } = Dimensions.get("window"); @@ -95,9 +88,11 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const [selectedWeek, setSelectedWeek] = useState(currentWeek); // Filtrer les devoirs + const SearchRef: React.MutableRefObject = useRef(null) as any as React.MutableRefObject; const [showDatePickerWeek, setShowDatePickerWeek] = useState(false); const [hideDone, setHideDone] = useState(false); const [showFilter, setShowFilter] = useState(false); + const [searchHasFocus, setSearchHasFocus] = useState(false); // Création de devoirs personnalisés const [showCreateHomework, setShowCreateHomework] = useState(false); @@ -114,11 +109,6 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const keyExtractor = useCallback((item: any) => item.toString(), []); - const getDayName = (date: string | number | Date): string => { - const days = ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"]; - return days[new Date(date).getDay()]; - }; - const [loading, setLoading] = useState(false); const [refreshing, setRefreshing] = useState(false); @@ -158,7 +148,19 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const [searchTerms, setSearchTerms] = useState(""); - const renderWeek: ListRenderItem = ({ item }) => { + const getDayName = useCallback((date: string | number | Date): string => { + const days = ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"]; + return days[new Date(date).getDay()]; + }, []); + + const formatDate = useCallback((date: string | number | Date): string => { + return new Date(date).toLocaleDateString("fr-FR", { + day: "numeric", + month: "long", + }); + }, []); + + const renderWeek: ListRenderItem = useCallback(({ item }) => { const homeworksInWeek = [...(homeworks[item] ?? [])]; const homeworksPersonalized = account.homeworks ?? []; @@ -333,22 +335,34 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { } ); - }; - - const onEndReached = () => { + }, [ + homeworks, + account.homeworks, + searchTerms, + hideDone, + updateHomeworks, + navigation, + getDayName, + formatDate, + insets, + outsideNav, + isOnline, + ]); + + const onEndReached = useCallback(() => { const lastWeek = data[data.length - 1]; const newWeeks = Array.from({ length: 50 }, (_, i) => lastWeek + i + 1); - setData(prevData => [...prevData, ...newWeeks]); - }; + setData((prevData) => [...prevData, ...newWeeks]); + }, [data]); - const onStartReached = () => { + const onStartReached = useCallback(() => { const firstWeek = data[0]; const newWeeks = Array.from({ length: 50 }, (_, i) => firstWeek - 50 + i); - setData(prevData => [...newWeeks, ...prevData]); + setData((prevData) => [...newWeeks, ...prevData]); flatListRef.current?.scrollToIndex({ index: 50, animated: false }); - }; + }, [data]); - const onScroll: ScrollViewProps["onScroll"] = useCallback(({ nativeEvent }: NativeSyntheticEvent) => { + const onScroll = useCallback(({ nativeEvent }: NativeSyntheticEvent) => { if (nativeEvent.contentOffset.x < width) { onStartReached(); } @@ -363,10 +377,6 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { setSelectedWeek(data[index]); }, [width, data]); - const [searchHasFocus, setSearchHasFocus] = useState(false); - - const SearchRef: React.MutableRefObject = useRef(null) as any as React.MutableRefObject; - return ( <> From 325000f5f76186f7cfb9ace6d7575784ec8ace37 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 23 Mar 2025 20:07:57 +0100 Subject: [PATCH 1011/1144] =?UTF-8?q?feat:=20utilisation=20du=20store=20`h?= =?UTF-8?q?omework`=20plut=C3=B4t=20que=20`account`=20pour=20des=20devoirs?= =?UTF-8?q?=20personnalis=C3=A9s=20dynamiques=20entre=20les=20pages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/homework.ts | 17 ++++- src/stores/account/index.ts | 63 ------------------- src/stores/account/types.ts | 9 --- src/stores/homework/index.ts | 37 ++++++++++- src/stores/homework/types.ts | 11 +++- .../Home/Elements/HomeworksElement.tsx | 30 ++------- src/views/account/Homeworks/Document.tsx | 19 +++++- src/views/account/Homeworks/Homeworks.tsx | 20 +++--- 8 files changed, 89 insertions(+), 117 deletions(-) diff --git a/src/services/homework.ts b/src/services/homework.ts index 447bddfde..cc2e5e4c5 100644 --- a/src/services/homework.ts +++ b/src/services/homework.ts @@ -58,9 +58,20 @@ export async function updateHomeworkForWeekInCache (account: console.info(`[updateHomeworkForWeekInCache]: updating to empty since ${account.service} not implemented.`); } - useHomeworkStore.getState().updateHomeworks(dateToEpochWeekNumber(date), homeworks); - } - catch (err) { + const weekNumber = dateToEpochWeekNumber(date); + const existingHomeworks = ( + useHomeworkStore.getState().homeworks[weekNumber] || [] + ).filter((element) => element.personalizate); + + const mergedHomeworks = [ + ...homeworks, + ...existingHomeworks.filter( + (customHomework) => !homeworks.some((hw) => hw.id === customHomework.id) + ), + ]; + + useHomeworkStore.getState().updateHomeworks(weekNumber, mergedHomeworks); + } catch (err) { error(`homeworks not updated, see:${err}`, "updateHomeworkForWeekInCache"); } } diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index 911bf89be..3fa326ebd 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -316,69 +316,6 @@ export const useAccounts = create()( // Return the updated account (to reuse the account directly) return accountMutated; }, - - /** - * Add a personalized homework to account - * @param localID ID Account where homework must be added - * @param homework Homework create by user - */ - addHomework: (localID, homework) => { - set((state) => ({ - accounts: state.accounts.map((account) => - account.localID === localID && !account.isExternal - ? { - ...account, - homeworks: [...(account.homeworks || []), homework], - } - : account - ), - })); - }, - - /** - * Update a personalized homework to account - * @param localID ID Account where homework must be updated - * @param homeworkID ID Homework to modify a specific homework - * @param updatedHomework Homework updated by user - */ - updateHomework: ( - localID, - homeworkID, - updatedHomework - ) => { - set((state) => ({ - accounts: state.accounts.map((account) => - account.localID === localID && !account.isExternal - ? { - ...account, - homeworks: account.homeworks?.map((devoir) => - devoir.id === homeworkID ? updatedHomework : devoir - ), - } - : account - ), - })); - }, - - /** - * Remove a personalized homework to account - * @param localID ID Account where homework must be remove - * @param homeworkID ID Homework to remove a specific homework - */ - removeHomework: (localID, homeworkID) => { - set((state) => ({ - accounts: state.accounts.map((account) => - account.localID === localID && !account.isExternal - ? { - ...account, - homeworks: account.homeworks?.filter( - (devoir) => devoir.id !== homeworkID - ), - } - : account - ), - })); - }, }), { name: "accounts-storage", diff --git a/src/stores/account/types.ts b/src/stores/account/types.ts index d6a6a2611..aea71f86e 100644 --- a/src/stores/account/types.ts +++ b/src/stores/account/types.ts @@ -9,7 +9,6 @@ import type MultiAPI from "esup-multi.js"; import { SkolengoAuthConfig } from "@/services/skolengo/skolengo-types"; import { User as ScolengoAPIUser } from "scolengo-api/types/models/Common"; import { OnlinePayments } from "pawrd/dist"; -import { Homework } from "@/services/shared/Homework"; export interface Tab { name: string @@ -135,7 +134,6 @@ interface BaseAccount { last: string; }; personalization: Partial; - homeworks?: Array; } interface BaseExternalAccount { @@ -312,11 +310,4 @@ export interface AccountsStore { key: T, value: A[T] ) => Account | null - addHomework: (localID: string, homework: Homework) => void; - updateHomework: ( - localID: string, - homeworkID: string, - homework: Homework - ) => void; - removeHomework: (localID: string, homeworkID: string) => void; } diff --git a/src/stores/homework/index.ts b/src/stores/homework/index.ts index d60f27fe2..c34e6584c 100644 --- a/src/stores/homework/index.ts +++ b/src/stores/homework/index.ts @@ -22,7 +22,42 @@ export const useHomeworkStore = create()( }); log(`updated homeworks for week ${epochWeekNumber}`, "homework:updateHomeworks"); - } + }, + + addHomework: (epochWeekNumber, homework) => { + set((state) => ({ + homeworks: { + ...state.homeworks, + [epochWeekNumber]: [ + ...(state.homeworks[epochWeekNumber] || []), + homework, + ], + }, + })); + }, + + updateHomework: (epochWeekNumber, homeworkID, updatedHomework) => { + set((state) => ({ + homeworks: { + ...state.homeworks, + [epochWeekNumber]: state.homeworks[epochWeekNumber]?.map( + (homework) => + homework.id === homeworkID ? updatedHomework : homework + ), + }, + })); + }, + + removeHomework: (epochWeekNumber, homeworkID) => { + set((state) => ({ + homeworks: { + ...state.homeworks, + [epochWeekNumber]: state.homeworks[epochWeekNumber]?.filter( + (homework) => homework.id !== homeworkID + ), + }, + })); + }, }), { name: "-homework-storage", // will be replace to user id when using "switchTo" diff --git a/src/stores/homework/types.ts b/src/stores/homework/types.ts index 461cec1ea..6496fb5fe 100644 --- a/src/stores/homework/types.ts +++ b/src/stores/homework/types.ts @@ -1,6 +1,13 @@ import type { Homework } from "@/services/shared/Homework"; export interface HomeworkStore { - homeworks: Record - updateHomeworks: (epochWeekNumber: number, homeworks: Homework[]) => void + homeworks: Record; + updateHomeworks: (epochWeekNumber: number, homeworks: Homework[]) => void; + addHomework: (epochWeekNumber: number, homework: Homework) => void; + updateHomework: ( + epochWeekNumber: number, + homeworkID: string, + updatedHomework: Homework + ) => void; + removeHomework: (epochWeekNumber: number, homeworkID: string) => void; } diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index c7f360a26..1741e9bbb 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -23,7 +23,6 @@ interface HomeworksElementProps { const HomeworksElement: React.FC = ({ navigation, onImportance }) => { const account = useCurrentAccount(store => store.account!); const homeworks = useHomeworkStore(store => store.homeworks); - const homeworksPersonalized = account.homeworks ?? []; const [loading, setLoading] = useState(false); @@ -76,29 +75,12 @@ const HomeworksElement: React.FC = ({ navigation, onImpor const startTime = mtn.getTime() / 1000; const endTime = startTime + 7 * 24 * 60 * 60 * 1000; - const allHwSemaineActuelle = - homeworks[dateToEpochWeekNumber(actualDay)] ?? []; - const personalizedForSemaineActuelle = homeworksPersonalized.filter( - (element) => { - const weekHomework = dateToEpochWeekNumber(new Date(element.due)); - return weekHomework === dateToEpochWeekNumber(actualDay); - } - ); - const hwSemaineActuelle = allHwSemaineActuelle - .concat(personalizedForSemaineActuelle) - .filter((hw) => hw.due / 1000 >= startTime && hw.due / 1000 <= endTime); - - const allHwSemaineProchaine = - homeworks[dateToEpochWeekNumber(actualDay) + 1] ?? []; - const personalizedForSemaineProchaine = homeworksPersonalized.filter( - (element) => { - const weekHomework = dateToEpochWeekNumber(new Date(element.due)); - return weekHomework === dateToEpochWeekNumber(actualDay) + 1; - } - ); - const hwSemaineProchaine = allHwSemaineProchaine - .concat(personalizedForSemaineProchaine) - .filter((hw) => hw.due / 1000 >= startTime && hw.due / 1000 <= endTime); + const hwSemaineActuelle = homeworks[dateToEpochWeekNumber(actualDay)]?.filter( + (hw) => hw.due / 1000 >= startTime && hw.due / 1000 <= endTime + ) ?? []; + const hwSemaineProchaine = homeworks[dateToEpochWeekNumber(actualDay) + 1]?.filter( + (hw) => hw.due / 1000 >= startTime && hw.due / 1000 <= endTime + ) ?? []; if (loading) { return ( diff --git a/src/views/account/Homeworks/Document.tsx b/src/views/account/Homeworks/Document.tsx index 4bfe3e6ee..6ccef8fd2 100644 --- a/src/views/account/Homeworks/Document.tsx +++ b/src/views/account/Homeworks/Document.tsx @@ -22,7 +22,7 @@ import HTMLView from "react-native-htmlview"; import { Screen } from "@/router/helpers/types"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { PapillonModernHeader } from "@/components/Global/PapillonModernHeader"; -import { useAccounts, useCurrentAccount } from "@/stores/account"; +import { useCurrentAccount } from "@/stores/account"; import { AccountService } from "@/stores/account/types"; import getAndOpenFile from "@/utils/files/getAndOpenFile"; import { AutoFileIcon } from "@/components/Global/FileIcon"; @@ -38,6 +38,8 @@ import ResponsiveTextInput from "@/components/FirstInstallation/ResponsiveTextIn import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import DateTimePicker from "@react-native-community/datetimepicker"; import { getSubjectData } from "@/services/shared/Subject"; +import { useHomeworkStore } from "@/stores/homework"; +import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; const MemoizedNativeItem = React.memo(NativeItem); const MemoizedNativeList = React.memo(NativeList); @@ -210,7 +212,12 @@ const HomeworksDocument: Screen<"HomeworksDocument"> = ({ navigation, route }) = text: "Continuer", style: "destructive", onPress: () => { - useAccounts.getState().removeHomework(account.localID, homework.id); + useHomeworkStore + .getState() + .removeHomework( + dateToEpochWeekNumber(new Date(dateHomework)), + homework.id + ); navigation.goBack(); } } @@ -408,7 +415,13 @@ const HomeworksDocument: Screen<"HomeworksDocument"> = ({ navigation, route }) = due: dateHomework, }; - useAccounts.getState().updateHomework(account.localID, homework.id, newHomework); + useHomeworkStore + .getState() + .updateHomework( + dateToEpochWeekNumber(new Date(dateHomework)), + homework.id, + newHomework + ); setShowCreateHomework(false); setSelectedPretty(Object.entries(localSubjects || {})[0]?.[1] ?? null); diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 64fac751a..1997c0c73 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -1,5 +1,5 @@ import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; -import { useAccounts, useCurrentAccount } from "@/stores/account"; +import { useCurrentAccount } from "@/stores/account"; import { useHomeworkStore } from "@/stores/homework"; import { useTheme } from "@react-navigation/native"; import React, { useRef, useState, useCallback, useEffect } from "react"; @@ -162,16 +162,8 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const renderWeek: ListRenderItem = useCallback(({ item }) => { const homeworksInWeek = [...(homeworks[item] ?? [])]; - const homeworksPersonalized = account.homeworks ?? []; - const personalizedForWeek = homeworksPersonalized.filter((element) => { - const weekHomework = dateToEpochWeekNumber(new Date(element.due)); - return weekHomework === item; - }); - - const combinedHomeworks = [...homeworksInWeek, ...personalizedForWeek]; - - const sortedHomework = combinedHomeworks.sort( + const sortedHomework = homeworksInWeek.sort( (a, b) => new Date(a.due).getTime() - new Date(b.due).getTime() ); @@ -337,7 +329,6 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { ); }, [ homeworks, - account.homeworks, searchTerms, hideDone, updateHomeworks, @@ -894,7 +885,12 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { exam: false, }; - useAccounts.getState().addHomework(account.localID, newHomework); + useHomeworkStore + .getState() + .addHomework( + dateToEpochWeekNumber(new Date(dateHomework)), + newHomework + ); setShowCreateHomework(false); setSelectedPretty(Object.entries(localSubjects || {})[0]?.[1] ?? null); From 4c6c96834dbdc8a73c6f890c487360bd38a05574 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 23 Mar 2025 20:16:40 +0100 Subject: [PATCH 1012/1144] =?UTF-8?q?fix:=20d=C3=A9sactivation=20de=20`tog?= =?UTF-8?q?gleHomeworkState`=20pour=20les=20comptes=20Skolengo=20(voir=20#?= =?UTF-8?q?848)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Home/Elements/HomeworksElement.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 1741e9bbb..48767bfbf 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -63,7 +63,9 @@ const HomeworksElement: React.FC = ({ navigation, onImpor const handleDonePress = useCallback( async (homework: Homework) => { - await toggleHomeworkState(account, homework); + if (account.service !== AccountService.Skolengo) { + await toggleHomeworkState(account, homework); + } await updateHomeworks(); }, [account, updateHomeworks] From acaaa647d0ba76138f50b765182d64089716f709 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 23 Mar 2025 20:29:31 +0100 Subject: [PATCH 1013/1144] =?UTF-8?q?feat:=20mise=20=C3=A0=20jour=20de=20l?= =?UTF-8?q?'=C3=A9tat=20des=20devoirs=20personnalis=C3=A9s=20dans=20le=20s?= =?UTF-8?q?tore=20pour=20pouvoir=20les=20cocher?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../account/Home/Elements/HomeworksElement.tsx | 15 +++++++++++++-- src/views/account/Homeworks/Homeworks.tsx | 16 ++++++++++++---- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 48767bfbf..238d91f92 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -14,6 +14,7 @@ import {RouteParameters} from "@/router/helpers/types"; import { FadeInDown, FadeOut } from "react-native-reanimated"; import MissingItem from "@/components/Global/MissingItem"; import PapillonLoading from "@/components/Global/PapillonLoading"; +import { AccountService } from "@/stores/account/types"; interface HomeworksElementProps { onImportance: (value: number) => unknown @@ -63,8 +64,18 @@ const HomeworksElement: React.FC = ({ navigation, onImpor const handleDonePress = useCallback( async (homework: Homework) => { - if (account.service !== AccountService.Skolengo) { - await toggleHomeworkState(account, homework); + if (homework.personalizate) { + useHomeworkStore + .getState() + .updateHomework( + dateToEpochWeekNumber(new Date(homework.due)), + homework.id, + {... homework, done: !homework.done } + ); + } else { + if (account.service !== AccountService.Skolengo) { + await toggleHomeworkState(account, homework); + } } await updateHomeworks(); }, diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 1997c0c73..57a856a92 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -277,11 +277,19 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { total={groupedHomework[day].length} homework={homework} onDonePressHandler={async () => { - if (account.service !== AccountService.Skolengo) { - await toggleHomeworkState(account, homework); + if (homework.personalizate) { + useHomeworkStore + .getState() + .updateHomework(item, homework.id, + {... homework, done: !homework.done } + ); + } else { + if (account.service !== AccountService.Skolengo) { + await toggleHomeworkState(account, homework); + } + await updateHomeworks(true, false, false); + await countCheckForReview(); } - await updateHomeworks(true, false, false); - await countCheckForReview(); }} /> ))} From e89e193c186228ae14c86f36ef05b36281a74e6b Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 23 Mar 2025 20:47:54 +0100 Subject: [PATCH 1014/1144] =?UTF-8?q?chore:=20mise=20=C3=A0=20jour=20de=20?= =?UTF-8?q?la=20d=C3=A9pendance=20`lucide-react-native`=20vers=20la=20vers?= =?UTF-8?q?ion=200.483.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 42860ef98..45a63dc3d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -72,7 +72,7 @@ "js-base64": "^3.7.7", "lodash": "^4.17.21", "lottie-react-native": "^6.7.0", - "lucide-react-native": "^0.378.0", + "lucide-react-native": "^0.483.0", "notifee": "^0.0.1", "openid-client": "^5.7.0", "pawdirecte": "^1.9.0", @@ -13451,9 +13451,9 @@ } }, "node_modules/lucide-react-native": { - "version": "0.378.0", - "resolved": "https://registry.npmjs.org/lucide-react-native/-/lucide-react-native-0.378.0.tgz", - "integrity": "sha512-Xvqxjc3N5040Ui6tZaSbpNnNjWXDa+nRzYct4rXd2mWX+g2qxKPpEHoqNumrpky9rhsIxD8w4BSbjdkpGQTMYw==", + "version": "0.483.0", + "resolved": "https://registry.npmjs.org/lucide-react-native/-/lucide-react-native-0.483.0.tgz", + "integrity": "sha512-+Hef6gCid++7jC2Wh4OMSF9KJxFc+kz5/C4sUXGSL31E0Jfye+OKZsLGVGju+CfOsdcki+TMNQ7bHvgP85fANg==", "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0", diff --git a/package.json b/package.json index be3853510..44294b158 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "js-base64": "^3.7.7", "lodash": "^4.17.21", "lottie-react-native": "^6.7.0", - "lucide-react-native": "^0.378.0", + "lucide-react-native": "^0.483.0", "notifee": "^0.0.1", "openid-client": "^5.7.0", "pawdirecte": "^1.9.0", From e6eaabcff2ee74661077b31f9ce96dee186c6d4c Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 23 Mar 2025 20:52:34 +0100 Subject: [PATCH 1015/1144] =?UTF-8?q?feat:=20ajout=20de=20nouvelles=20opti?= =?UTF-8?q?ons=20et=20ic=C3=B4nes=20pour=20le=20filtrage=20des=20devoirs?= =?UTF-8?q?=20dans=20la=20vue=20hebdomadaire?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/Homeworks.tsx | 68 ++++++++++++++++++++--- 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 57a856a92..e4217a106 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -23,7 +23,7 @@ import * as StoreReview from "expo-store-review"; import HomeworkItem from "./Atoms/Item"; import { PressableScale } from "react-native-pressable-scale"; -import { BookPlus, CalendarClock, CheckSquare, ListFilter, Search, X } from "lucide-react-native"; +import { BookPlus, CalendarClock, CheckSquare, Funnel, FunnelPlus, Search, WandSparkles, X } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { BlurView } from "expo-blur"; @@ -536,11 +536,19 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { setShowFilter(true); }} > - + {searchTerms.length > 0 || hideDone ? ( + + ) : ( + + )} @@ -564,7 +572,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { - Via Recherche + Recherche = ({ route, navigation }) => { = ({ route, navigation }) => { + + + Options + + = ({ route, navigation }) => { {hideDone ? "Afficher tous les devoirs" : "Afficher uniquement les devoirs non faits"} + + + + )} + onPress={() => { + setShowFilter(false); + navigation.navigate("SettingStack", { screen: "SettingsMagic" }); + }} + > + + Configurer Papillon Magic pour les devoirs + + From 6c17206a8f80f373ca01ec93f75fffff9d8d1228 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 23 Mar 2025 20:56:18 +0100 Subject: [PATCH 1016/1144] =?UTF-8?q?fix:=20ajout=20de=20points=20=C3=A0?= =?UTF-8?q?=20la=20fin=20des=20messages=20d'instructions=20pour=20une=20me?= =?UTF-8?q?illeure=20clart=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/Document.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/account/Homeworks/Document.tsx b/src/views/account/Homeworks/Document.tsx index 6ccef8fd2..e0adf1b95 100644 --- a/src/views/account/Homeworks/Document.tsx +++ b/src/views/account/Homeworks/Document.tsx @@ -166,8 +166,8 @@ const HomeworksDocument: Screen<"HomeworksDocument"> = ({ navigation, route }) = ? "Tu dois rendre ce devoir en classe" : "Ce devoir est à rendre", homework.returnType === "file_upload" - ? "Papillon ne permet pas de rendre des devoirs sur l'ENT. Tu dois le faire sur l'ENT de ton établissement" - : "Ton professeur t'indiquera comment rendre ce devoir", + ? "Papillon ne permet pas de rendre des devoirs sur l'ENT. Tu dois le faire sur l'ENT de ton établissement." + : "Ton professeur t'indiquera comment rendre ce devoir.", ); }} > From b7fd6c366d0de0e182b44552ff8701b86bcec70c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Mon, 24 Mar 2025 08:24:30 +0100 Subject: [PATCH 1017/1144] =?UTF-8?q?fix(Homework):=20suppression=20we=20l?= =?UTF-8?q?'=C3=A9moji=20(erreur=20de=20commit=20s=C3=BBrement)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/Homeworks.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index e4217a106..cdb9e9507 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -786,7 +786,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { homework={{ attachments: [], color: selectedPretty.color, - content: contentHomework ?? "Écris le contenu du devoir juste en-dessous :)", + content: contentHomework ?? "Écris le contenu du devoir juste en-dessous", done: false, due: dateHomework, id: String(idHomework), From ccb7e77791b3fefddc4dc0d9fce3d1b1b5c6e529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20GUERIN?= Date: Mon, 24 Mar 2025 17:34:15 +0100 Subject: [PATCH 1018/1144] fix: do not display remaining lunches if there is no balance on the account --- .../account/Restaurant/Modals/CardDetail.tsx | 56 ++++++++++--------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index f2112bfd8..c184d82ed 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -263,33 +263,37 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio )} - {card?.balance[0].remaining !== null && card?.balance[0].remaining !== Infinity && ( - - 1 ? "#00C853" : "#FF1744", - }} - > - {card.balance[0].remaining.toFixed(0)} + {card?.balance[0] && + card?.balance[0].remaining !== null && + card?.balance[0].remaining !== Infinity && + ( + + 1 ? "#00C853" : "#FF1744", + }} + > + {card.balance[0].remaining.toFixed(0)} + + } + > + + Repas restants - } - > - - Repas restants - - - Tarif estimé à {card.balance[0].price?.toFixed(2)} € - - - - )} + + Tarif estimé à {card.balance[0].price?.toFixed(2)} € + + + + ) + } {card?.history.length > 0 && ( From 6e7b3c0f30c9aee4625186f612854efbab4febe2 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 24 Mar 2025 19:01:51 +0100 Subject: [PATCH 1019/1144] fix: Utiliser les minutes UTC pour le formatage des heures dans plusieurs composants --- src/background/utils/format.ts | 2 +- src/views/settings/SettingsDevLogs.tsx | 2 +- src/widgets/Components/NextCourse.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/background/utils/format.ts b/src/background/utils/format.ts index ec7880882..1595d7ae8 100644 --- a/src/background/utils/format.ts +++ b/src/background/utils/format.ts @@ -1,7 +1,7 @@ export const formatHoursMinutes = (timestamp: number) => { const LAdate = new Date(timestamp); const heures = LAdate.getHours().toString().padStart(2, "0"); - const minutes = LAdate.getMinutes().toString().padStart(2, "0"); + const minutes = LAdate.getUTCMinutes().toString().padStart(2, "0"); return `${heures}:${minutes}`; }; diff --git a/src/views/settings/SettingsDevLogs.tsx b/src/views/settings/SettingsDevLogs.tsx index 8106809d8..81f2fdc96 100644 --- a/src/views/settings/SettingsDevLogs.tsx +++ b/src/views/settings/SettingsDevLogs.tsx @@ -210,7 +210,7 @@ const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { {log.message} {formatDate(log.date)} à {new Date(log.date).getHours()}: - {new Date(log.date).getMinutes()}: + {new Date(log.date).getUTCMinutes()}: {new Date(log.date).getSeconds()} {log.from} diff --git a/src/widgets/Components/NextCourse.tsx b/src/widgets/Components/NextCourse.tsx index 0ccb4b95c..e50068ae4 100644 --- a/src/widgets/Components/NextCourse.tsx +++ b/src/widgets/Components/NextCourse.tsx @@ -145,7 +145,7 @@ const NextCourseLesson: React.FC<{ const nextMinute = new Date(now); nextMinute.setSeconds(0); nextMinute.setMilliseconds(0); - nextMinute.setMinutes(nextMinute.getMinutes() + 1); + nextMinute.setMinutes(nextMinute.getUTCMinutes() + 1); const delay = nextMinute.getTime() - now; return setTimeout(updateRemainingTime, delay); }; From c5917d38a279d61aa45dec9f58702e7877f5182b Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 24 Mar 2025 19:02:45 +0100 Subject: [PATCH 1020/1144] fix: Utiliser les dates UTC pour le traitement des dates dans plusieurs composants --- src/services/pronote/chats.ts | 2 +- src/services/skolengo/skolengo-types.ts | 2 +- src/utils/epochWeekNumber.ts | 2 +- src/views/account/Home/Elements/TimetableElement.tsx | 8 ++++---- src/views/account/Lessons/Document.tsx | 4 ++-- src/views/account/Lessons/Lessons.tsx | 10 +++++----- src/views/account/Restaurant/Menu.tsx | 6 +++--- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/services/pronote/chats.ts b/src/services/pronote/chats.ts index e52320874..8a10f7718 100644 --- a/src/services/pronote/chats.ts +++ b/src/services/pronote/chats.ts @@ -34,7 +34,7 @@ export const getChats = async (account: PronoteAccount): Promise> => if (targetIndex !== -1) { const diff = targetIndex - todayIndex; const targetDate = new Date(); - targetDate.setDate(today.getDate() + (diff <= 0 ? diff : diff - 7)); + targetDate.setDate(today.getUTCDate() + (diff <= 0 ? diff : diff - 7)); return targetDate; } diff --git a/src/services/skolengo/skolengo-types.ts b/src/services/skolengo/skolengo-types.ts index 999ffccb5..bbc105d1e 100644 --- a/src/services/skolengo/skolengo-types.ts +++ b/src/services/skolengo/skolengo-types.ts @@ -58,5 +58,5 @@ export const toSkolengoDate = (date: Date): string => }-${ (date.getMonth()+1).toString().padStart(2, "0") }-${ - (date.getDate()).toString().padStart(2, "0") + (date.getUTCDate()).toString().padStart(2, "0") }`; \ No newline at end of file diff --git a/src/utils/epochWeekNumber.ts b/src/utils/epochWeekNumber.ts index 10de59d99..dc10af52f 100644 --- a/src/utils/epochWeekNumber.ts +++ b/src/utils/epochWeekNumber.ts @@ -32,7 +32,7 @@ const EPOCH_WN_CONFIG = { const dayToWeekCommonDay = (date: Date): Date => { const _date = new Date(date); _date.setHours(EPOCH_WN_CONFIG.setHour, 0, 0, 0); - _date.setDate(_date.getDate() - ( (7 + _date.getDay() - 1) %7 ) + EPOCH_WN_CONFIG.setMiddleDay - 1); + _date.setDate(_date.getUTCDate() - ( (7 + _date.getDay() - 1) %7 ) + EPOCH_WN_CONFIG.setMiddleDay - 1); // the (7+ ... -1 ) %7 is to have Monday as the first day (0) of the week and Sunday as last day (7) cause JS start the week on Sunday ¯\_(ツ)_/¯ // In details : the 7+ is to avoid negative value, the -1 is to have Monday as the first day of the week and the %7 is to have the right day number (0 to 6) // In details : setMiddleDay - 1 is to have the middle day of the week, aka Wednesday diff --git a/src/views/account/Home/Elements/TimetableElement.tsx b/src/views/account/Home/Elements/TimetableElement.tsx index 8319ed8f5..6235a1b7f 100644 --- a/src/views/account/Home/Elements/TimetableElement.tsx +++ b/src/views/account/Home/Elements/TimetableElement.tsx @@ -43,7 +43,7 @@ const TimetableElement: React.FC = ({ onImportance }) => const today = new Date(); const date = new Date(timestamp); return ( - date.getDate() === today.getDate() && + date.getUTCDate() === today.getUTCDate() && date.getMonth() === today.getMonth() && date.getFullYear() === today.getFullYear() ); @@ -51,10 +51,10 @@ const TimetableElement: React.FC = ({ onImportance }) => const isTomorrow = (timestamp: number) => { const tomorrow = new Date(); - tomorrow.setDate(tomorrow.getDate() + 1); + tomorrow.setDate(tomorrow.getUTCDate() + 1); const date = new Date(timestamp); return ( - date.getDate() === tomorrow.getDate() && + date.getUTCDate() === tomorrow.getUTCDate() && date.getMonth() === tomorrow.getMonth() && date.getFullYear() === tomorrow.getFullYear() ); @@ -200,7 +200,7 @@ const TimetableElement: React.FC = ({ onImportance }) => const isTodayCourse = courseDate.toDateString() === today.toDateString(); const tomorrow = new Date(today); - tomorrow.setDate(today.getDate() + 1); + tomorrow.setDate(today.getUTCDate() + 1); const isTomorrowCourse = courseDate.toDateString() === tomorrow.toDateString(); diff --git a/src/views/account/Lessons/Document.tsx b/src/views/account/Lessons/Document.tsx index bf75ce37c..965f93017 100644 --- a/src/views/account/Lessons/Document.tsx +++ b/src/views/account/Lessons/Document.tsx @@ -78,8 +78,8 @@ const LessonDocument: Screen<"LessonDocument"> = ({ route, navigation }) => { setClassSubjects( subjects.subjects.filter( (b) => - new Date(b.date).getDate() === - new Date(lesson.startTimestamp).getDate() && + new Date(b.date).getUTCDate() === + new Date(lesson.startTimestamp).getUTCDate() && new Date(b.date).getMonth() === new Date(lesson.startTimestamp).getMonth() && lesson.subject === b.subject, diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index 9b203da65..146b476f3 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -178,7 +178,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const today = new Date(); return Array.from({ length: 100 }, (_, i) => { const date = new Date(today); - date.setDate(today.getDate() - 50 + i); + date.setDate(today.getUTCDate() - 50 + i); date.setHours(0, 0, 0, 0); return date; }); @@ -285,7 +285,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { if (newDate < firstDate) { const dates = []; - for (let d = new Date(firstDate); d >= newDate; d.setDate(d.getDate() - 1)) { + for (let d = new Date(firstDate); d >= newDate; d.setDate(d.getUTCDate() - 1)) { if (!uniqueDates.has(d.getTime())) { dates.unshift(new Date(d)); uniqueDates.add(d.getTime()); @@ -294,7 +294,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { updatedData = [...dates, ...data]; } else if (newDate > lastDate) { const dates = []; - for (let d = new Date(lastDate); d <= newDate; d.setDate(d.getDate() + 1)) { + for (let d = new Date(lastDate); d <= newDate; d.setDate(d.getUTCDate() + 1)) { if (!uniqueDates.has(d.getTime())) { dates.push(new Date(d)); uniqueDates.add(d.getTime()); @@ -346,7 +346,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { = ({ route, navigation }) => { const lastDate = data[data.length - 1]; const newDates = Array.from({ length: 34 }, (_, i) => { const date = new Date(lastDate); - date.setDate(lastDate.getDate() + i + 1); + date.setDate(lastDate.getUTCDate() + i + 1); return date; }); setData((prevData) => [...prevData, ...newDates]); diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 8100ba031..758d3d250 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -424,7 +424,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { > { - onDatePickerSelect(new Date(pickerDate.setDate(pickerDate.getDate() - 1))); + onDatePickerSelect(new Date(pickerDate.setDate(pickerDate.getUTCDate() - 1))); setRefreshCount(refreshCount + 1); }} activeScale={0.8} @@ -456,7 +456,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { - + {pickerDate.toLocaleDateString("fr-FR", { month: "long" })} @@ -468,7 +468,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { > { - onDatePickerSelect(new Date(pickerDate.setDate(pickerDate.getDate() + 1))); + onDatePickerSelect(new Date(pickerDate.setDate(pickerDate.getUTCDate() + 1))); setRefreshCount(refreshCount + 1); }} activeScale={0.8} From 82d0ff3e7bcfd9dcff706b43a76d930eeca4e455 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 24 Mar 2025 19:03:16 +0100 Subject: [PATCH 1021/1144] fix: Utiliser les jours de la semaine en UTC dans plusieurs composants --- src/services/pronote/chats.ts | 2 +- src/services/turboself/booking.ts | 2 +- src/utils/epochWeekNumber.ts | 4 ++-- src/views/account/Home/Elements/TimetableElement.tsx | 2 +- src/views/account/Homeworks/Homeworks.old.tsx | 2 +- src/views/account/Homeworks/Homeworks.tsx | 2 +- src/views/account/Lessons/Atoms/Page.tsx | 2 +- src/views/account/Restaurant/Menu.tsx | 2 +- src/widgets/Components/RestaurantQRCode.tsx | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/services/pronote/chats.ts b/src/services/pronote/chats.ts index 8a10f7718..ba88157ad 100644 --- a/src/services/pronote/chats.ts +++ b/src/services/pronote/chats.ts @@ -27,7 +27,7 @@ export const getChats = async (account: PronoteAccount): Promise> => } const today = new Date(); - const todayIndex = today.getDay(); + const todayIndex = today.getUTCDay(); const dayName = parts[0].toLowerCase(); const targetIndex = days.indexOf(dayName); diff --git a/src/services/turboself/booking.ts b/src/services/turboself/booking.ts index ae65f8912..a2b5aff82 100644 --- a/src/services/turboself/booking.ts +++ b/src/services/turboself/booking.ts @@ -18,7 +18,7 @@ export const getBookingWeek = async (account: TurboselfAccount, weekNumber?: num }; export const bookDay = async (account: TurboselfAccount, id: string, date: Date, booked: boolean): Promise => { - const bookedDay = await account.authentication.session.bookMeal(id, date.getDay(), booked ? 1 : 0); + const bookedDay = await account.authentication.session.bookMeal(id, date.getUTCDay(), booked ? 1 : 0); return { id: bookedDay.id, canBook: bookedDay.canBook, diff --git a/src/utils/epochWeekNumber.ts b/src/utils/epochWeekNumber.ts index dc10af52f..16a1bce59 100644 --- a/src/utils/epochWeekNumber.ts +++ b/src/utils/epochWeekNumber.ts @@ -22,7 +22,7 @@ const EPOCH_WN_CONFIG = { setMiddleDay: 3, // We set the middle day of the week to Wednesday to ensure <... same than above ...> setEndDay: 7, // We set the last day of the week to Sunday to ensure <...> numberOfMsInAWeek: 1000 /* ms */ * 60 /* s */ * 60 /* min */ * 24 /* h */ * 7, /* days */ - adjustEpochInitialDate: 259200000, // =(((new Date(0)).getDay()-1) * EPOCH_WN_CONFIG.numberOfMsInAWeek/7) // We need to substract this for having a good range cause 01/01/1970 was not a Monday and the "-1" is to have Monday as the first day of the week + adjustEpochInitialDate: 259200000, // =(((new Date(0)).getUTCDay()-1) * EPOCH_WN_CONFIG.numberOfMsInAWeek/7) // We need to substract this for having a good range cause 01/01/1970 was not a Monday and the "-1" is to have Monday as the first day of the week }; /** @@ -32,7 +32,7 @@ const EPOCH_WN_CONFIG = { const dayToWeekCommonDay = (date: Date): Date => { const _date = new Date(date); _date.setHours(EPOCH_WN_CONFIG.setHour, 0, 0, 0); - _date.setDate(_date.getUTCDate() - ( (7 + _date.getDay() - 1) %7 ) + EPOCH_WN_CONFIG.setMiddleDay - 1); + _date.setDate(_date.getUTCDate() - ( (7 + _date.getUTCDay() - 1) %7 ) + EPOCH_WN_CONFIG.setMiddleDay - 1); // the (7+ ... -1 ) %7 is to have Monday as the first day (0) of the week and Sunday as last day (7) cause JS start the week on Sunday ¯\_(ツ)_/¯ // In details : the 7+ is to avoid negative value, the -1 is to have Monday as the first day of the week and the %7 is to have the right day number (0 to 6) // In details : setMiddleDay - 1 is to have the middle day of the week, aka Wednesday diff --git a/src/views/account/Home/Elements/TimetableElement.tsx b/src/views/account/Home/Elements/TimetableElement.tsx index 6235a1b7f..56591f011 100644 --- a/src/views/account/Home/Elements/TimetableElement.tsx +++ b/src/views/account/Home/Elements/TimetableElement.tsx @@ -61,7 +61,7 @@ const TimetableElement: React.FC = ({ onImportance }) => }; const isWeekend = (courses: TimetableClass[]) => { - const today = new Date().getDay(); + const today = new Date().getUTCDay(); return (today === 6 || today === 0) && courses.length === 0; }; diff --git a/src/views/account/Homeworks/Homeworks.old.tsx b/src/views/account/Homeworks/Homeworks.old.tsx index 61a1bf3e3..495badc04 100644 --- a/src/views/account/Homeworks/Homeworks.old.tsx +++ b/src/views/account/Homeworks/Homeworks.old.tsx @@ -204,7 +204,7 @@ const HomeworksScreen: Screen<"Homeworks"> = ({ navigation }) => { const getDayName = (date: string | number | Date): string => { const days = ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"]; - return days[new Date(date).getDay()]; + return days[new Date(date).getUTCDay()]; }; useEffect(() => { diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 3fab5d745..e7bcf3c13 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -104,7 +104,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const getDayName = (date: string | number | Date): string => { const days = ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"]; - return days[new Date(date).getDay()]; + return days[new Date(date).getUTCDay()]; }; const [loading, setLoading] = useState(false); diff --git a/src/views/account/Lessons/Atoms/Page.tsx b/src/views/account/Lessons/Atoms/Page.tsx index 660e6ed08..3347f96d9 100644 --- a/src/views/account/Lessons/Atoms/Page.tsx +++ b/src/views/account/Lessons/Atoms/Page.tsx @@ -100,7 +100,7 @@ export const Page = ({ day, date, current, paddingTop, refreshAction, loading, w )} {hasServiceSetup && day && day.length === 0 && current && !loading && ( - weekExists && (new Date(date).getDay() == 6 || new Date(date).getDay() == 0) ? ( + weekExists && (new Date(date).getUTCDay() == 6 || new Date(date).getUTCDay() == 0) ? ( = ({ route, navigation }) => { const getWeekNumber = (date: Date) => { const firstDayOfYear = new Date(date.getFullYear(), 0, 1); const pastDaysOfYear = (date.getTime() - firstDayOfYear.getTime()) / 86400000; - return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7); + return Math.ceil((pastDaysOfYear + firstDayOfYear.getUTCDay() + 1) / 7); }; const onDatePickerSelect = async (date?: Date) => { diff --git a/src/widgets/Components/RestaurantQRCode.tsx b/src/widgets/Components/RestaurantQRCode.tsx index b299a9130..b5e288bd9 100644 --- a/src/widgets/Components/RestaurantQRCode.tsx +++ b/src/widgets/Components/RestaurantQRCode.tsx @@ -43,7 +43,7 @@ const RestaurantQRCodeWidget = forwardRef(({ const getWeekNumber = (date: Date) => { const firstDayOfYear = new Date(date.getFullYear(), 0, 1); const pastDaysOfYear = (date.getTime() - firstDayOfYear.getTime()) / 86400000; - return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7); + return Math.ceil((pastDaysOfYear + firstDayOfYear.getUTCDay() + 1) / 7); }; useEffect(() => { From e7be7c3f393d3326ffced69a6df6bf08416e8595 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 24 Mar 2025 19:03:42 +0100 Subject: [PATCH 1022/1144] fix: Utiliser les heures UTC dans plusieurs composants --- src/background/utils/format.ts | 2 +- src/views/account/Home/Elements/HomeworksElement.tsx | 2 +- src/views/account/Home/Elements/PopupRestauration.tsx | 2 +- src/views/account/Home/Elements/TimetableElement.tsx | 2 +- src/views/account/Lessons/Atoms/Page.tsx | 2 +- src/views/settings/SettingsDevLogs.tsx | 2 +- src/widgets/Components/RestaurantQRCode.tsx | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/background/utils/format.ts b/src/background/utils/format.ts index 1595d7ae8..49822366c 100644 --- a/src/background/utils/format.ts +++ b/src/background/utils/format.ts @@ -1,6 +1,6 @@ export const formatHoursMinutes = (timestamp: number) => { const LAdate = new Date(timestamp); - const heures = LAdate.getHours().toString().padStart(2, "0"); + const heures = LAdate.getUTCHours().toString().padStart(2, "0"); const minutes = LAdate.getUTCMinutes().toString().padStart(2, "0"); return `${heures}:${minutes}`; diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 1b71ebf29..42261d7d8 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -38,7 +38,7 @@ const HomeworksElement: React.FC = ({ navigation, onImpor .filter(hw => !hw.done); const date = new Date(); - if (date.getHours() >= 17 && date.getHours() < 22) + if (date.getUTCHours() >= 17 && date.getUTCHours() < 22) score += 4; if (hw.length > 0) score += 3; diff --git a/src/views/account/Home/Elements/PopupRestauration.tsx b/src/views/account/Home/Elements/PopupRestauration.tsx index f3b778627..b49550ee9 100644 --- a/src/views/account/Home/Elements/PopupRestauration.tsx +++ b/src/views/account/Home/Elements/PopupRestauration.tsx @@ -19,7 +19,7 @@ const PopupRestauration: React.FC = ({ onImportance }) = const mutateProperty = useCurrentAccount(store => store.mutateProperty); const ImportanceHandler = () => { - let hours = new Date().getHours(); + let hours = new Date().getUTCHours(); if (hours >= 11 && hours < 14) onImportance(10); else diff --git a/src/views/account/Home/Elements/TimetableElement.tsx b/src/views/account/Home/Elements/TimetableElement.tsx index 56591f011..e76c3a6d0 100644 --- a/src/views/account/Home/Elements/TimetableElement.tsx +++ b/src/views/account/Home/Elements/TimetableElement.tsx @@ -29,7 +29,7 @@ const TimetableElement: React.FC = ({ onImportance }) => const ImportanceHandler = (nextCourses: TimetableClass[]) => { if (nextCourses.length > 0) { - let difference = new Date(nextCourses[0].startTimestamp).getHours() - new Date().getHours(); + let difference = new Date(nextCourses[0].startTimestamp).getUTCHours() - new Date().getUTCHours(); if (difference < 0) { difference = 0; } diff --git a/src/views/account/Lessons/Atoms/Page.tsx b/src/views/account/Lessons/Atoms/Page.tsx index 3347f96d9..aadf1a331 100644 --- a/src/views/account/Lessons/Atoms/Page.tsx +++ b/src/views/account/Lessons/Atoms/Page.tsx @@ -144,7 +144,7 @@ const SeparatorCourse: React.FC<{ end: number }> = ({ i, start, end }) => { const { colors } = useTheme(); - const startHours = new Date(start).getHours(); + const startHours = new Date(start).getUTCHours(); return ( = ({ navigation }) => { > {log.message} - {formatDate(log.date)} à {new Date(log.date).getHours()}: + {formatDate(log.date)} à {new Date(log.date).getUTCHours()}: {new Date(log.date).getUTCMinutes()}: {new Date(log.date).getSeconds()} diff --git a/src/widgets/Components/RestaurantQRCode.tsx b/src/widgets/Components/RestaurantQRCode.tsx index b5e288bd9..b3e8d9aaa 100644 --- a/src/widgets/Components/RestaurantQRCode.tsx +++ b/src/widgets/Components/RestaurantQRCode.tsx @@ -51,7 +51,7 @@ const RestaurantQRCodeWidget = forwardRef(({ setHidden(true); setLoading(true); const newCards: Array = []; - const currentHour = new Date().getHours(); + const currentHour = new Date().getUTCHours(); const accountPromises = linkedAccounts.map(async (account) => { try { const [cardnumber] = await Promise.all([ From f8f4c93e0ee7c29935f3a26e29f2f67598803b75 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 24 Mar 2025 19:04:41 +0100 Subject: [PATCH 1023/1144] fix: Utiliser les mois UTC dans plusieurs composants --- src/services/skolengo/skolengo-types.ts | 2 +- src/views/account/Home/Elements/TimetableElement.tsx | 4 ++-- src/views/account/Lessons/Document.tsx | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/services/skolengo/skolengo-types.ts b/src/services/skolengo/skolengo-types.ts index bbc105d1e..44928749d 100644 --- a/src/services/skolengo/skolengo-types.ts +++ b/src/services/skolengo/skolengo-types.ts @@ -56,7 +56,7 @@ export const toSkolengoDate = (date: Date): string => `${ date.getFullYear().toString().padStart(4, "0") }-${ - (date.getMonth()+1).toString().padStart(2, "0") + (date.getUTCMonth()+1).toString().padStart(2, "0") }-${ (date.getUTCDate()).toString().padStart(2, "0") }`; \ No newline at end of file diff --git a/src/views/account/Home/Elements/TimetableElement.tsx b/src/views/account/Home/Elements/TimetableElement.tsx index e76c3a6d0..7439d5dee 100644 --- a/src/views/account/Home/Elements/TimetableElement.tsx +++ b/src/views/account/Home/Elements/TimetableElement.tsx @@ -44,7 +44,7 @@ const TimetableElement: React.FC = ({ onImportance }) => const date = new Date(timestamp); return ( date.getUTCDate() === today.getUTCDate() && - date.getMonth() === today.getMonth() && + date.getUTCMonth() === today.getUTCMonth() && date.getFullYear() === today.getFullYear() ); }; @@ -55,7 +55,7 @@ const TimetableElement: React.FC = ({ onImportance }) => const date = new Date(timestamp); return ( date.getUTCDate() === tomorrow.getUTCDate() && - date.getMonth() === tomorrow.getMonth() && + date.getUTCMonth() === tomorrow.getUTCMonth() && date.getFullYear() === tomorrow.getFullYear() ); }; diff --git a/src/views/account/Lessons/Document.tsx b/src/views/account/Lessons/Document.tsx index 965f93017..026448abe 100644 --- a/src/views/account/Lessons/Document.tsx +++ b/src/views/account/Lessons/Document.tsx @@ -80,8 +80,8 @@ const LessonDocument: Screen<"LessonDocument"> = ({ route, navigation }) => { (b) => new Date(b.date).getUTCDate() === new Date(lesson.startTimestamp).getUTCDate() && - new Date(b.date).getMonth() === - new Date(lesson.startTimestamp).getMonth() && + new Date(b.date).getUTCMonth() === + new Date(lesson.startTimestamp).getUTCMonth() && lesson.subject === b.subject, ) ?? [], ); From 6e6c1e43e93c5af235d400f8136674b4b339365b Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 24 Mar 2025 19:05:11 +0100 Subject: [PATCH 1024/1144] =?UTF-8?q?fix:=20Utiliser=20les=20secondes=20UT?= =?UTF-8?q?C=20pour=20l'affichage=20des=20journaux=20de=20d=C3=A9veloppeme?= =?UTF-8?q?nt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/SettingsDevLogs.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/settings/SettingsDevLogs.tsx b/src/views/settings/SettingsDevLogs.tsx index a955f83ed..c58b04e95 100644 --- a/src/views/settings/SettingsDevLogs.tsx +++ b/src/views/settings/SettingsDevLogs.tsx @@ -211,7 +211,7 @@ const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { {formatDate(log.date)} à {new Date(log.date).getUTCHours()}: {new Date(log.date).getUTCMinutes()}: - {new Date(log.date).getSeconds()} + {new Date(log.date).getUTCSeconds()} {log.from} From 36123b66f2e27636bfa48d8378f157aee866297f Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 24 Mar 2025 19:10:20 +0100 Subject: [PATCH 1025/1144] fix: Utiliser les dates UTC dans plusieurs composants --- src/background/data/Homeworks.ts | 2 +- src/services/pronote/chats.ts | 2 +- src/utils/epochWeekNumber.ts | 2 +- src/views/account/Home/Elements/TimetableElement.tsx | 4 ++-- src/views/account/Homeworks/Homeworks.tsx | 2 +- src/views/account/Lessons/Lessons.tsx | 8 ++++---- src/views/account/Restaurant/Menu.tsx | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/background/data/Homeworks.ts b/src/background/data/Homeworks.ts index cdec51b03..a9836a6be 100644 --- a/src/background/data/Homeworks.ts +++ b/src/background/data/Homeworks.ts @@ -27,7 +27,7 @@ const fetchHomeworks = async (): Promise => { if (!firstDate) { firstDate = new Date(); firstDate.setMonth(8); - firstDate.setDate(1); + firstDate.setUTCDate(1); } const firstDateEpoch = dateToEpochWeekNumber(firstDate); diff --git a/src/services/pronote/chats.ts b/src/services/pronote/chats.ts index ba88157ad..fa5af1fb8 100644 --- a/src/services/pronote/chats.ts +++ b/src/services/pronote/chats.ts @@ -34,7 +34,7 @@ export const getChats = async (account: PronoteAccount): Promise> => if (targetIndex !== -1) { const diff = targetIndex - todayIndex; const targetDate = new Date(); - targetDate.setDate(today.getUTCDate() + (diff <= 0 ? diff : diff - 7)); + targetDate.setUTCDate(today.getUTCDate() + (diff <= 0 ? diff : diff - 7)); return targetDate; } diff --git a/src/utils/epochWeekNumber.ts b/src/utils/epochWeekNumber.ts index 16a1bce59..d597fe36f 100644 --- a/src/utils/epochWeekNumber.ts +++ b/src/utils/epochWeekNumber.ts @@ -32,7 +32,7 @@ const EPOCH_WN_CONFIG = { const dayToWeekCommonDay = (date: Date): Date => { const _date = new Date(date); _date.setHours(EPOCH_WN_CONFIG.setHour, 0, 0, 0); - _date.setDate(_date.getUTCDate() - ( (7 + _date.getUTCDay() - 1) %7 ) + EPOCH_WN_CONFIG.setMiddleDay - 1); + _date.setUTCDate(_date.getUTCDate() - ( (7 + _date.getUTCDay() - 1) %7 ) + EPOCH_WN_CONFIG.setMiddleDay - 1); // the (7+ ... -1 ) %7 is to have Monday as the first day (0) of the week and Sunday as last day (7) cause JS start the week on Sunday ¯\_(ツ)_/¯ // In details : the 7+ is to avoid negative value, the -1 is to have Monday as the first day of the week and the %7 is to have the right day number (0 to 6) // In details : setMiddleDay - 1 is to have the middle day of the week, aka Wednesday diff --git a/src/views/account/Home/Elements/TimetableElement.tsx b/src/views/account/Home/Elements/TimetableElement.tsx index 7439d5dee..5a87e9f05 100644 --- a/src/views/account/Home/Elements/TimetableElement.tsx +++ b/src/views/account/Home/Elements/TimetableElement.tsx @@ -51,7 +51,7 @@ const TimetableElement: React.FC = ({ onImportance }) => const isTomorrow = (timestamp: number) => { const tomorrow = new Date(); - tomorrow.setDate(tomorrow.getUTCDate() + 1); + tomorrow.setUTCDate(tomorrow.getUTCDate() + 1); const date = new Date(timestamp); return ( date.getUTCDate() === tomorrow.getUTCDate() && @@ -200,7 +200,7 @@ const TimetableElement: React.FC = ({ onImportance }) => const isTodayCourse = courseDate.toDateString() === today.toDateString(); const tomorrow = new Date(today); - tomorrow.setDate(today.getUTCDate() + 1); + tomorrow.setUTCDate(today.getUTCDate() + 1); const isTomorrowCourse = courseDate.toDateString() === tomorrow.toDateString(); diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index e7bcf3c13..1c3fb1528 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -81,7 +81,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { if (!firstDate) { firstDate = new Date(); firstDate.setMonth(8); - firstDate.setDate(1); + firstDate.setUTCDate(1); } const firstDateEpoch = dateToEpochWeekNumber(firstDate); diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index 146b476f3..26dbd6ee0 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -178,7 +178,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const today = new Date(); return Array.from({ length: 100 }, (_, i) => { const date = new Date(today); - date.setDate(today.getUTCDate() - 50 + i); + date.setUTCDate(today.getUTCDate() - 50 + i); date.setHours(0, 0, 0, 0); return date; }); @@ -285,7 +285,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { if (newDate < firstDate) { const dates = []; - for (let d = new Date(firstDate); d >= newDate; d.setDate(d.getUTCDate() - 1)) { + for (let d = new Date(firstDate); d >= newDate; d.setUTCDate(d.getUTCDate() - 1)) { if (!uniqueDates.has(d.getTime())) { dates.unshift(new Date(d)); uniqueDates.add(d.getTime()); @@ -294,7 +294,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { updatedData = [...dates, ...data]; } else if (newDate > lastDate) { const dates = []; - for (let d = new Date(lastDate); d <= newDate; d.setDate(d.getUTCDate() + 1)) { + for (let d = new Date(lastDate); d <= newDate; d.setUTCDate(d.getUTCDate() + 1)) { if (!uniqueDates.has(d.getTime())) { dates.push(new Date(d)); uniqueDates.add(d.getTime()); @@ -462,7 +462,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const lastDate = data[data.length - 1]; const newDates = Array.from({ length: 34 }, (_, i) => { const date = new Date(lastDate); - date.setDate(lastDate.getUTCDate() + i + 1); + date.setUTCDate(lastDate.getUTCDate() + i + 1); return date; }); setData((prevData) => [...prevData, ...newDates]); diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index e90e4b346..96a7936e0 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -424,7 +424,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { > { - onDatePickerSelect(new Date(pickerDate.setDate(pickerDate.getUTCDate() - 1))); + onDatePickerSelect(new Date(pickerDate.setUTCDate(pickerDate.getUTCDate() - 1))); setRefreshCount(refreshCount + 1); }} activeScale={0.8} @@ -468,7 +468,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { > { - onDatePickerSelect(new Date(pickerDate.setDate(pickerDate.getUTCDate() + 1))); + onDatePickerSelect(new Date(pickerDate.setUTCDate(pickerDate.getUTCDate() + 1))); setRefreshCount(refreshCount + 1); }} activeScale={0.8} From af66ee88993bdc559c61effa8327c55f02d91180 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 24 Mar 2025 19:10:52 +0100 Subject: [PATCH 1026/1144] fix: Utiliser les heures UTC dans plusieurs composants --- src/background/data/Lessons.ts | 4 ++-- src/background/utils/homeworks.ts | 4 ++-- src/components/Global/InfiniteDatePager.tsx | 2 +- src/services/iutlan/attendance.ts | 4 ++-- src/utils/epochWeekNumber.ts | 2 +- src/utils/format/DateHelper.ts | 2 +- .../account/Home/Elements/HomeworksElement.tsx | 2 +- src/views/account/Lessons/Lessons.tsx | 14 +++++++------- src/views/account/Lessons/LessonsHeader.tsx | 2 +- src/views/account/Restaurant/Menu.tsx | 4 ++-- src/views/account/Week/Week.tsx | 2 +- 11 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index 76c96550e..80747ca51 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -11,13 +11,13 @@ import { formatHoursMinutes } from "../utils/format"; const getAllLessonsForDay = (lessons: Record) => { const date = new Date(); - date.setHours(0, 0, 0, 0); + date.setUTCHours(0, 0, 0, 0); const week = dateToEpochWeekNumber(date); const timetable = lessons[week] || []; const lessonsOfDay = timetable.filter((lesson) => { const lessonDate = new Date(lesson.startTimestamp); - lessonDate.setHours(0, 0, 0, 0); + lessonDate.setUTCHours(0, 0, 0, 0); return lessonDate.getTime() === date.getTime(); }); diff --git a/src/background/utils/homeworks.ts b/src/background/utils/homeworks.ts index ba67c7695..f039436b6 100644 --- a/src/background/utils/homeworks.ts +++ b/src/background/utils/homeworks.ts @@ -5,9 +5,9 @@ import { epochWNToDate } from "@/utils/epochWeekNumber"; const getCurrentWeekNumber = () => { const now = new Date(); - now.setHours(0, 0, 0, 0); + now.setUTCHours(0, 0, 0, 0); const start = new Date(1970, 0, 0); - start.setHours(0, 0, 0, 0); + start.setUTCHours(0, 0, 0, 0); const diff = now.getTime() - start.getTime(); const oneWeek = 1000 * 60 * 60 * 24 * 7; return Math.floor(diff / oneWeek); diff --git a/src/components/Global/InfiniteDatePager.tsx b/src/components/Global/InfiniteDatePager.tsx index 75b9265a8..3c024eb02 100644 --- a/src/components/Global/InfiniteDatePager.tsx +++ b/src/components/Global/InfiniteDatePager.tsx @@ -14,7 +14,7 @@ interface InfiniteDatePagerProps { const InfiniteDatePager = ({ renderDate, initialDate = new Date(), onDateChange }: InfiniteDatePagerProps) => { const pagerRef = useRef(null); const baseDate = useRef(new Date()).current; - baseDate.setHours(0, 0, 0, 0); + baseDate.setUTCHours(0, 0, 0, 0); const lastChangeTime = useRef(0); const getDateFromIndex = useCallback((index: number) => { diff --git a/src/services/iutlan/attendance.ts b/src/services/iutlan/attendance.ts index c6cf91c6d..b19ac4550 100644 --- a/src/services/iutlan/attendance.ts +++ b/src/services/iutlan/attendance.ts @@ -29,10 +29,10 @@ export const saveIUTLanAttendance = async ( for (const absence of absences) { let from = new Date(day); - from.setHours(parseInt(absence.debut)); + from.setUTCHours(parseInt(absence.debut)); let to = new Date(absence.dateFin); - to.setHours(parseInt(absence.fin)); + to.setUTCHours(parseInt(absence.fin)); allAbsences.push({ id: absence.idAbs, diff --git a/src/utils/epochWeekNumber.ts b/src/utils/epochWeekNumber.ts index d597fe36f..04a6250cd 100644 --- a/src/utils/epochWeekNumber.ts +++ b/src/utils/epochWeekNumber.ts @@ -31,7 +31,7 @@ const EPOCH_WN_CONFIG = { */ const dayToWeekCommonDay = (date: Date): Date => { const _date = new Date(date); - _date.setHours(EPOCH_WN_CONFIG.setHour, 0, 0, 0); + _date.setUTCHours(EPOCH_WN_CONFIG.setHour, 0, 0, 0); _date.setUTCDate(_date.getUTCDate() - ( (7 + _date.getUTCDay() - 1) %7 ) + EPOCH_WN_CONFIG.setMiddleDay - 1); // the (7+ ... -1 ) %7 is to have Monday as the first day (0) of the week and Sunday as last day (7) cause JS start the week on Sunday ¯\_(ツ)_/¯ // In details : the 7+ is to avoid negative value, the -1 is to have Monday as the first day of the week and the %7 is to have the right day number (0 to 6) diff --git a/src/utils/format/DateHelper.ts b/src/utils/format/DateHelper.ts index 56c9f017e..d85b84960 100644 --- a/src/utils/format/DateHelper.ts +++ b/src/utils/format/DateHelper.ts @@ -15,7 +15,7 @@ export const timestampToString = (timestamp: number) => { } const mtn = new Date(); - mtn.setHours(0, 0, 0, 0); + mtn.setUTCHours(0, 0, 0, 0); return formatDistance(new Date(timestamp), mtn, { locale: fr, diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 42261d7d8..4239f3df6 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -70,7 +70,7 @@ const HomeworksElement: React.FC = ({ navigation, onImpor ); const mtn = new Date(); - mtn.setHours(0, 0, 0, 0); + mtn.setUTCHours(0, 0, 0, 0); const startTime = mtn.getTime() / 1000; const endTime = startTime + 7 * 24 * 60 * 60 * 1000; diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index 26dbd6ee0..7e5e3281c 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -69,7 +69,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { }, [timetables]); const today = new Date(); - today.setHours(0, 0, 0, 0); + today.setUTCHours(0, 0, 0, 0); const [pickerDate, setPickerDate] = useState(new Date(today)); @@ -139,11 +139,11 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const timetable = timetables[week] || []; const newDate = new Date(date); - newDate.setHours(0, 0, 0, 0); + newDate.setUTCHours(0, 0, 0, 0); const day = timetable.filter((lesson) => { const lessonDate = new Date(lesson.startTimestamp); - lessonDate.setHours(0, 0, 0, 0); + lessonDate.setUTCHours(0, 0, 0, 0); return lessonDate.getTime() === newDate.getTime(); }); @@ -157,7 +157,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { if (flatListRef.current) { const normalizeDate = (date: Date) => { const newDate = new Date(date); - newDate.setHours(0, 0, 0, 0); + newDate.setUTCHours(0, 0, 0, 0); return newDate; }; @@ -179,7 +179,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { return Array.from({ length: 100 }, (_, i) => { const date = new Date(today); date.setUTCDate(today.getUTCDate() - 50 + i); - date.setHours(0, 0, 0, 0); + date.setUTCHours(0, 0, 0, 0); return date; }); }); @@ -274,7 +274,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const onDateSelect = (date: Date | undefined) => { const newDate = new Date(date || 0); - newDate.setHours(0, 0, 0, 0); + newDate.setUTCHours(0, 0, 0, 0); setPickerDate(newDate); const firstDate = data[0]; @@ -321,7 +321,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { onPress={() => setShowDatePicker(true)} onLongPress={() => { const today = new Date(); - today.setHours(0, 0, 0, 0); + today.setUTCHours(0, 0, 0, 0); onDateSelect(today); }} > diff --git a/src/views/account/Lessons/LessonsHeader.tsx b/src/views/account/Lessons/LessonsHeader.tsx index 082c00275..aae4772e7 100644 --- a/src/views/account/Lessons/LessonsHeader.tsx +++ b/src/views/account/Lessons/LessonsHeader.tsx @@ -204,7 +204,7 @@ const LessonsDateModal: React.FC = ({ onChange={(_event, selectedDate) => { const newSelectedDate = selectedDate || currentDate; // set hours to 0 - newSelectedDate.setHours(0, 0, 0, 0); + newSelectedDate.setUTCHours(0, 0, 0, 0); onDateSelect(newSelectedDate); }} /> diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 96a7936e0..e88f7837e 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -73,7 +73,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { const [currentMenu, setCurrentMenu] = useState(null); const [currentWeek, setCurrentWeek] = useState(0); const [showDatePicker, setShowDatePicker] = useState(false); - const [pickerDate, setPickerDate] = React.useState(new Date(new Date().setHours(0, 0, 0, 0))); + const [pickerDate, setPickerDate] = React.useState(new Date(new Date().setUTCHours(0, 0, 0, 0))); const [isMenuLoading, setMenuLoading] = useState(false); const [isInitialised, setIsInitialised] = useState(false); const [selectedIndex, setSelectedIndex] = useState(0); @@ -99,7 +99,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { const newDate = new Date(date); - newDate.setHours(0, 0, 0, 0); + newDate.setUTCHours(0, 0, 0, 0); if (newDate.valueOf() === pickerDate.valueOf()) { return; diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index 75c8381fc..778f7a925 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -131,7 +131,7 @@ const HeaderItem = memo(({ header }) => { const start = header.startUnix; const today = new Date(); - today.setHours(0, 0, 0, 0); + today.setUTCHours(0, 0, 0, 0); const todayStamp = today.getTime(); From 2de0a553cd0e21ebed5616e7df893d51d82c3928 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 24 Mar 2025 19:11:43 +0100 Subject: [PATCH 1027/1144] =?UTF-8?q?fix:=20Utiliser=20les=20millisecondes?= =?UTF-8?q?=20UTC=20pour=20le=20calcul=20du=20d=C3=A9lai=20de=20mise=20?= =?UTF-8?q?=C3=A0=20jour?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/widgets/Components/NextCourse.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/Components/NextCourse.tsx b/src/widgets/Components/NextCourse.tsx index e50068ae4..18193b5b1 100644 --- a/src/widgets/Components/NextCourse.tsx +++ b/src/widgets/Components/NextCourse.tsx @@ -144,7 +144,7 @@ const NextCourseLesson: React.FC<{ // Schedule next update at the start of the next minute const nextMinute = new Date(now); nextMinute.setSeconds(0); - nextMinute.setMilliseconds(0); + nextMinute.setUTCMilliseconds(0); nextMinute.setMinutes(nextMinute.getUTCMinutes() + 1); const delay = nextMinute.getTime() - now; return setTimeout(updateRemainingTime, delay); From b9200633f2eee850442bc9d2f48e5c7a17d3933e Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 24 Mar 2025 19:12:13 +0100 Subject: [PATCH 1028/1144] =?UTF-8?q?fix:=20Utiliser=20les=20minutes=20UTC?= =?UTF-8?q?=20pour=20le=20calcul=20du=20d=C3=A9lai=20de=20mise=20=C3=A0=20?= =?UTF-8?q?jour?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/widgets/Components/NextCourse.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/Components/NextCourse.tsx b/src/widgets/Components/NextCourse.tsx index 18193b5b1..392962b04 100644 --- a/src/widgets/Components/NextCourse.tsx +++ b/src/widgets/Components/NextCourse.tsx @@ -145,7 +145,7 @@ const NextCourseLesson: React.FC<{ const nextMinute = new Date(now); nextMinute.setSeconds(0); nextMinute.setUTCMilliseconds(0); - nextMinute.setMinutes(nextMinute.getUTCMinutes() + 1); + nextMinute.setUTCMinutes(nextMinute.getUTCMinutes() + 1); const delay = nextMinute.getTime() - now; return setTimeout(updateRemainingTime, delay); }; From e0a42d866065ae93071238f799fa5a9179b5cde1 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 24 Mar 2025 19:12:42 +0100 Subject: [PATCH 1029/1144] =?UTF-8?q?fix:=20Utiliser=20les=20mois=20UTC=20?= =?UTF-8?q?pour=20la=20r=C3=A9cup=C3=A9ration=20des=20devoirs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Homeworks.ts | 2 +- src/views/account/Homeworks/Homeworks.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/background/data/Homeworks.ts b/src/background/data/Homeworks.ts index a9836a6be..c8f0565af 100644 --- a/src/background/data/Homeworks.ts +++ b/src/background/data/Homeworks.ts @@ -26,7 +26,7 @@ const fetchHomeworks = async (): Promise => { let firstDate = account.instance?.instance?.firstDate || null; if (!firstDate) { firstDate = new Date(); - firstDate.setMonth(8); + firstDate.setUTCMonth(8); firstDate.setUTCDate(1); } const firstDateEpoch = dateToEpochWeekNumber(firstDate); diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 1c3fb1528..2336cbe71 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -80,7 +80,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { let firstDate = account?.instance?.instance?.firstDate || null; if (!firstDate) { firstDate = new Date(); - firstDate.setMonth(8); + firstDate.setUTCMonth(8); firstDate.setUTCDate(1); } const firstDateEpoch = dateToEpochWeekNumber(firstDate); From 0d150913733e41b5ea4394d9b4e5b20dbda53db4 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 24 Mar 2025 19:13:09 +0100 Subject: [PATCH 1030/1144] =?UTF-8?q?fix:=20Utiliser=20les=20secondes=20UT?= =?UTF-8?q?C=20pour=20la=20planification=20de=20la=20mise=20=C3=A0=20jour?= =?UTF-8?q?=20suivante?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/widgets/Components/NextCourse.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/Components/NextCourse.tsx b/src/widgets/Components/NextCourse.tsx index 392962b04..703b913d4 100644 --- a/src/widgets/Components/NextCourse.tsx +++ b/src/widgets/Components/NextCourse.tsx @@ -143,7 +143,7 @@ const NextCourseLesson: React.FC<{ // Schedule next update at the start of the next minute const nextMinute = new Date(now); - nextMinute.setSeconds(0); + nextMinute.setUTCSeconds(0); nextMinute.setUTCMilliseconds(0); nextMinute.setUTCMinutes(nextMinute.getUTCMinutes() + 1); const delay = nextMinute.getTime() - now; From c41a60b983de4253da9a9fb9cdfd571f5a2bf6dd Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 24 Mar 2025 19:14:09 +0100 Subject: [PATCH 1031/1144] =?UTF-8?q?fix:=20Utiliser=20les=20heures=20UTC?= =?UTF-8?q?=20pour=20la=20gestion=20des=20le=C3=A7ons=20et=20des=20devoirs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Lessons.ts | 4 ++-- src/background/utils/homeworks.ts | 4 ++-- src/components/Global/InfiniteDatePager.tsx | 2 +- src/utils/format/DateHelper.ts | 2 +- .../account/Home/Elements/HomeworksElement.tsx | 2 +- src/views/account/Lessons/Lessons.tsx | 14 +++++++------- src/views/account/Lessons/LessonsHeader.tsx | 2 +- src/views/account/Restaurant/Menu.tsx | 4 ++-- src/views/account/Week/Week.tsx | 2 +- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index 80747ca51..dadb239dd 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -11,13 +11,13 @@ import { formatHoursMinutes } from "../utils/format"; const getAllLessonsForDay = (lessons: Record) => { const date = new Date(); - date.setUTCHours(0, 0, 0, 0); + date.setUTCHours(1, 0, 0, 0); const week = dateToEpochWeekNumber(date); const timetable = lessons[week] || []; const lessonsOfDay = timetable.filter((lesson) => { const lessonDate = new Date(lesson.startTimestamp); - lessonDate.setUTCHours(0, 0, 0, 0); + lessonDate.setUTCHours(1, 0, 0, 0); return lessonDate.getTime() === date.getTime(); }); diff --git a/src/background/utils/homeworks.ts b/src/background/utils/homeworks.ts index f039436b6..e5290aa6a 100644 --- a/src/background/utils/homeworks.ts +++ b/src/background/utils/homeworks.ts @@ -5,9 +5,9 @@ import { epochWNToDate } from "@/utils/epochWeekNumber"; const getCurrentWeekNumber = () => { const now = new Date(); - now.setUTCHours(0, 0, 0, 0); + now.setUTCHours(1, 0, 0, 0); const start = new Date(1970, 0, 0); - start.setUTCHours(0, 0, 0, 0); + start.setUTCHours(1, 0, 0, 0); const diff = now.getTime() - start.getTime(); const oneWeek = 1000 * 60 * 60 * 24 * 7; return Math.floor(diff / oneWeek); diff --git a/src/components/Global/InfiniteDatePager.tsx b/src/components/Global/InfiniteDatePager.tsx index 3c024eb02..d6abfaff0 100644 --- a/src/components/Global/InfiniteDatePager.tsx +++ b/src/components/Global/InfiniteDatePager.tsx @@ -14,7 +14,7 @@ interface InfiniteDatePagerProps { const InfiniteDatePager = ({ renderDate, initialDate = new Date(), onDateChange }: InfiniteDatePagerProps) => { const pagerRef = useRef(null); const baseDate = useRef(new Date()).current; - baseDate.setUTCHours(0, 0, 0, 0); + baseDate.setUTCHours(1, 0, 0, 0); const lastChangeTime = useRef(0); const getDateFromIndex = useCallback((index: number) => { diff --git a/src/utils/format/DateHelper.ts b/src/utils/format/DateHelper.ts index d85b84960..7e7807304 100644 --- a/src/utils/format/DateHelper.ts +++ b/src/utils/format/DateHelper.ts @@ -15,7 +15,7 @@ export const timestampToString = (timestamp: number) => { } const mtn = new Date(); - mtn.setUTCHours(0, 0, 0, 0); + mtn.setUTCHours(1, 0, 0, 0); return formatDistance(new Date(timestamp), mtn, { locale: fr, diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 4239f3df6..e5e4818c0 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -70,7 +70,7 @@ const HomeworksElement: React.FC = ({ navigation, onImpor ); const mtn = new Date(); - mtn.setUTCHours(0, 0, 0, 0); + mtn.setUTCHours(1, 0, 0, 0); const startTime = mtn.getTime() / 1000; const endTime = startTime + 7 * 24 * 60 * 60 * 1000; diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index 7e5e3281c..88989fb04 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -69,7 +69,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { }, [timetables]); const today = new Date(); - today.setUTCHours(0, 0, 0, 0); + today.setUTCHours(1, 0, 0, 0); const [pickerDate, setPickerDate] = useState(new Date(today)); @@ -139,11 +139,11 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const timetable = timetables[week] || []; const newDate = new Date(date); - newDate.setUTCHours(0, 0, 0, 0); + newDate.setUTCHours(1, 0, 0, 0); const day = timetable.filter((lesson) => { const lessonDate = new Date(lesson.startTimestamp); - lessonDate.setUTCHours(0, 0, 0, 0); + lessonDate.setUTCHours(1, 0, 0, 0); return lessonDate.getTime() === newDate.getTime(); }); @@ -157,7 +157,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { if (flatListRef.current) { const normalizeDate = (date: Date) => { const newDate = new Date(date); - newDate.setUTCHours(0, 0, 0, 0); + newDate.setUTCHours(1, 0, 0, 0); return newDate; }; @@ -179,7 +179,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { return Array.from({ length: 100 }, (_, i) => { const date = new Date(today); date.setUTCDate(today.getUTCDate() - 50 + i); - date.setUTCHours(0, 0, 0, 0); + date.setUTCHours(1, 0, 0, 0); return date; }); }); @@ -274,7 +274,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const onDateSelect = (date: Date | undefined) => { const newDate = new Date(date || 0); - newDate.setUTCHours(0, 0, 0, 0); + newDate.setUTCHours(1, 0, 0, 0); setPickerDate(newDate); const firstDate = data[0]; @@ -321,7 +321,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { onPress={() => setShowDatePicker(true)} onLongPress={() => { const today = new Date(); - today.setUTCHours(0, 0, 0, 0); + today.setUTCHours(1, 0, 0, 0); onDateSelect(today); }} > diff --git a/src/views/account/Lessons/LessonsHeader.tsx b/src/views/account/Lessons/LessonsHeader.tsx index aae4772e7..05e85aaf7 100644 --- a/src/views/account/Lessons/LessonsHeader.tsx +++ b/src/views/account/Lessons/LessonsHeader.tsx @@ -204,7 +204,7 @@ const LessonsDateModal: React.FC = ({ onChange={(_event, selectedDate) => { const newSelectedDate = selectedDate || currentDate; // set hours to 0 - newSelectedDate.setUTCHours(0, 0, 0, 0); + newSelectedDate.setUTCHours(1, 0, 0, 0); onDateSelect(newSelectedDate); }} /> diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index e88f7837e..55a12d31e 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -73,7 +73,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { const [currentMenu, setCurrentMenu] = useState(null); const [currentWeek, setCurrentWeek] = useState(0); const [showDatePicker, setShowDatePicker] = useState(false); - const [pickerDate, setPickerDate] = React.useState(new Date(new Date().setUTCHours(0, 0, 0, 0))); + const [pickerDate, setPickerDate] = React.useState(new Date(new Date().setUTCHours(1, 0, 0, 0))); const [isMenuLoading, setMenuLoading] = useState(false); const [isInitialised, setIsInitialised] = useState(false); const [selectedIndex, setSelectedIndex] = useState(0); @@ -99,7 +99,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { const newDate = new Date(date); - newDate.setUTCHours(0, 0, 0, 0); + newDate.setUTCHours(1, 0, 0, 0); if (newDate.valueOf() === pickerDate.valueOf()) { return; diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index 778f7a925..fa84afecd 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -131,7 +131,7 @@ const HeaderItem = memo(({ header }) => { const start = header.startUnix; const today = new Date(); - today.setUTCHours(0, 0, 0, 0); + today.setUTCHours(1, 0, 0, 0); const todayStamp = today.getTime(); From acf087038871105e9b4ab692b5ec9b9079149cf7 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 24 Mar 2025 19:17:45 +0100 Subject: [PATCH 1032/1144] fix: Corriger l'ordre des jours de la semaine dans la vue des devoirs --- src/views/account/Homeworks/Homeworks.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 2336cbe71..0f725f8f3 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -103,7 +103,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const keyExtractor = useCallback((item: any) => item.toString(), []); const getDayName = (date: string | number | Date): string => { - const days = ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"]; + const days = ["Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche"]; return days[new Date(date).getUTCDay()]; }; From d75bbcd701638cb79debbb9a4e19c2aeaeff3df9 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Mon, 24 Mar 2025 19:39:28 +0100 Subject: [PATCH 1033/1144] =?UTF-8?q?fix:=20Am=C3=A9liorer=20l'affichage?= =?UTF-8?q?=20des=20logs=20en=20utilisant=20formatDistanceToNow=20pour=20l?= =?UTF-8?q?a=20date?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/SettingsDevLogs.tsx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/views/settings/SettingsDevLogs.tsx b/src/views/settings/SettingsDevLogs.tsx index c58b04e95..eb51743cb 100644 --- a/src/views/settings/SettingsDevLogs.tsx +++ b/src/views/settings/SettingsDevLogs.tsx @@ -37,10 +37,11 @@ import { animPapillon } from "@/utils/ui/animations"; import { useTheme } from "@react-navigation/native"; import { useAlert } from "@/providers/AlertProvider"; import MissingItem from "@/components/Global/MissingItem"; -import formatDate from "@/utils/format/format_date_complets"; import ResponsiveTextInput from "@/components/FirstInstallation/ResponsiveTextInput"; +import { formatDistanceToNow } from "date-fns"; +import { fr } from "date-fns/locale"; -const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { +const SettingsDevLogs: Screen<"SettingsDevLogs"> = () => { const { colors } = useTheme(); const [logs, setLogs] = useState([]); const [searchTerms, setSearchTerms] = useState(""); @@ -58,7 +59,7 @@ const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { ); setLoading(false); }); - }, [navigation]); + }, []); return ( = ({ navigation }) => { exiting={animPapillon(FadeOutUp)} > {logs.slice().reverse().map((log, index) => { + if (Number.isNaN(new Date(log.date).getTime())) return; + if (log.message.toLowerCase().includes(searchTerms.toLowerCase())) { return ( = ({ navigation }) => { > {log.message} - {formatDate(log.date)} à {new Date(log.date).getUTCHours()}: - {new Date(log.date).getUTCMinutes()}: - {new Date(log.date).getUTCSeconds()} + {formatDistanceToNow(log.date, { + addSuffix: true, + includeSeconds: true, + locale: fr, + })} {log.from} From 9ce4786588bbc272abbbb285d6c85d9f8ca51935 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 25 Mar 2025 18:52:04 +0100 Subject: [PATCH 1034/1144] fix: Ajout d'une option de suppression de carte avec un menu contextuel sur iOS et Android --- .../account/Restaurant/Modals/CardDetail.tsx | 158 ++++++++++++------ 1 file changed, 111 insertions(+), 47 deletions(-) diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index c184d82ed..3a1036124 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -1,4 +1,4 @@ -import { Alert, Image, Linking, Platform, ScrollView, Text, View } from "react-native"; +import { Alert, Image, Linking, Platform, ScrollView, Text, TouchableOpacity, View } from "react-native"; import MenuCard from "../Cards/Card"; import Reanimated from "react-native-reanimated"; import React, { useState } from "react"; @@ -12,7 +12,7 @@ import InsetsBottomView from "@/components/Global/InsetsBottomView"; import { PressableScale } from "react-native-pressable-scale"; import { useAccounts, useCurrentAccount } from "@/stores/account"; import { AccountService, ExternalAccount } from "@/stores/account/types"; -import { ExternalLink, MoreVertical, QrCode, Trash2 } from "lucide-react-native"; +import { ExternalLink, MoreHorizontal, MoreVertical, QrCode, Trash2 } from "lucide-react-native"; import { balanceFromExternal } from "@/services/balance"; import { reservationHistoryFromExternal } from "@/services/reservation-history"; import { Screen } from "@/router/helpers/types"; @@ -34,6 +34,68 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio const cardName = `Carte ${AccountService[route.params.card.service as AccountService]} ${account?.identity?.firstName ? "de " + account.identity.firstName : ""}`; + React.useLayoutEffect(() => { + if(Platform.OS === "ios") { + navigation.setOptions({ + headerTitle: cardName ?? "Détail de la carte", + headerLargeTitleStyle: { + color: "transparent", + }, + headerLargeStyle: { + backgroundColor: "transparent", + }, + headerBlurEffect: "regular", + headerRight: () => ( + ({ + label: link.label, + subtitle: link.subtitle, + sfSymbol: link.sfSymbol, + icon: , + onPress: () => Linking.openURL(link.url), + })) ?? [], + { + label: "Supprimer", + icon: , + sfSymbol: "trash", + destructive: true, + onPress: () => { + Alert.alert( + "Supprimer la carte", + "Veux-tu vraiment supprimer la " + (cardName ?? "carte") + " ?", + [ + { text: "Annuler", style: "cancel" }, + { + text: "Supprimer", + style: "destructive", + onPress: () => { + try { + removeAccount(card.account?.localID as string); + navigation.goBack(); + } + catch (e) { + console.log(e); + } + } + } + ] + ); + } + } + ]} + > + + + + + ), + }); + } + }, [navigation, theme]); + const updateCardData = async () => { try { const [balance, history] = await Promise.all([ @@ -70,54 +132,56 @@ const RestaurantCardDetail: Screen<"RestaurantCardDetail"> = ({ route, navigatio return ( <> - - ({ - label: link.label, - subtitle: link.subtitle, - sfSymbol: link.sfSymbol, - icon: , - onPress: () => Linking.openURL(link.url), - })) ?? [], - { - label: "Supprimer", - icon: , - sfSymbol: "trash", - destructive: true, - onPress: () => { - Alert.alert( - "Supprimer la carte", - "Veux-tu vraiment supprimer la " + (cardName ?? "carte") + " ?", - [ - { text: "Annuler", style: "cancel" }, - { - text: "Supprimer", - style: "destructive", - onPress: () => { - try { - removeAccount(card.account?.localID as string); - navigation.goBack(); - } - catch (e) { - console.log(e); + {Platform.OS === "android" && ( + + ({ + label: link.label, + subtitle: link.subtitle, + sfSymbol: link.sfSymbol, + icon: , + onPress: () => Linking.openURL(link.url), + })) ?? [], + { + label: "Supprimer", + icon: , + sfSymbol: "trash", + destructive: true, + onPress: () => { + Alert.alert( + "Supprimer la carte", + "Veux-tu vraiment supprimer la " + (cardName ?? "carte") + " ?", + [ + { text: "Annuler", style: "cancel" }, + { + text: "Supprimer", + style: "destructive", + onPress: () => { + try { + removeAccount(card.account?.localID as string); + navigation.goBack(); + } + catch (e) { + console.log(e); + } } } - } - ] - ); + ] + ); + } } - } - ]} - > - } - /> - - + ]} + > + } + /> + + + )} Date: Tue, 25 Mar 2025 18:55:29 +0100 Subject: [PATCH 1035/1144] =?UTF-8?q?fix:=20Ajuster=20l'affichage=20de=20l?= =?UTF-8?q?'en-t=C3=AAte=20pour=20RestaurantCardDetail=20selon=20la=20plat?= =?UTF-8?q?eforme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/screens/views/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/router/screens/views/index.ts b/src/router/screens/views/index.ts index 2632c3f76..1aa1ec941 100644 --- a/src/router/screens/views/index.ts +++ b/src/router/screens/views/index.ts @@ -41,7 +41,8 @@ export default [ presentation: "modal", }), createScreen("RestaurantCardDetail", RestaurantCardDetail, { - headerShown: false, + headerShown: Platform.OS == "android" ? false : true, + headerTransparent: true, presentation: Platform.OS == "android" ? "modal" : "formSheet", sheetCornerRadius: 16, sheetGrabberVisible: true, From 6d0ec67dcbea1e7d269dedd56cee9ca978d3abad Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 25 Mar 2025 18:59:24 +0100 Subject: [PATCH 1036/1144] =?UTF-8?q?fix:=20Ajouter=20des=20valeurs=20par?= =?UTF-8?q?=20d=C3=A9faut=20pour=20les=20propri=C3=A9t=C3=A9s=20de=20Servi?= =?UTF-8?q?ceCard=20dans=20le=20composant=20Menu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Restaurant/Menu.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 8267b894f..c62d1a5c5 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -219,12 +219,12 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { newBookings.push(...booking); const newCard: ServiceCard = { - service: account.service, - identifier: account.username, - account: account, - balance: balance, - history: history, - cardnumber: cardnumber, + service: account.service ?? 0, + identifier: account.username ?? "", + account: account ?? null, + balance: balance ?? [], + history: history ?? [], + cardnumber: cardnumber ?? "", // @ts-ignore theme: STORE_THEMES.find((theme) => theme.id === AccountService[account.service]) ?? STORE_THEMES[0] as StoreTheme, }; From bf2229bfe56bce3cad5e1f8140ae69c6750e5d82 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 25 Mar 2025 19:21:53 +0100 Subject: [PATCH 1037/1144] =?UTF-8?q?feat:=20ajout=20de=20nouvelles=20d?= =?UTF-8?q?=C3=A9pendances=20et=20mise=20=C3=A0=20jour=20des=20configurati?= =?UTF-8?q?ons=20de=20projet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/src/main/AndroidManifest.xml | 4 + ios/Papillon.xcodeproj/project.pbxproj | 4 +- ios/Podfile.lock | 6 + package-lock.json | 2454 ++++++++++++++++++++- package.json | 2 + src/views/account/Homeworks/Document.tsx | 228 +- src/views/account/Homeworks/Homeworks.tsx | 800 +++---- 7 files changed, 2741 insertions(+), 757 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index ca946b3e9..f0a4ad569 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -46,6 +46,10 @@ + + + + diff --git a/ios/Papillon.xcodeproj/project.pbxproj b/ios/Papillon.xcodeproj/project.pbxproj index a36919e50..bbfe9f05e 100644 --- a/ios/Papillon.xcodeproj/project.pbxproj +++ b/ios/Papillon.xcodeproj/project.pbxproj @@ -457,7 +457,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = "Papillon"; + PRODUCT_NAME = Papillon; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -490,7 +490,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = "Papillon"; + PRODUCT_NAME = Papillon; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/ios/Podfile.lock b/ios/Podfile.lock index f31f42d9a..e1a683ed5 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1667,6 +1667,8 @@ PODS: - React-Core - RNCMaskedView (0.3.1): - React-Core + - RNCPicker (2.11.0): + - React-Core - RNDateTimePicker (8.0.1): - React-Core - RNGestureHandler (2.24.0): @@ -1881,6 +1883,7 @@ DEPENDENCIES: - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) - "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)" - "RNCMaskedView (from `../node_modules/@react-native-masked-view/masked-view`)" + - "RNCPicker (from `../node_modules/@react-native-picker/picker`)" - "RNDateTimePicker (from `../node_modules/@react-native-community/datetimepicker`)" - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - "RNNotifee (from `../node_modules/@notifee/react-native`)" @@ -2112,6 +2115,8 @@ EXTERNAL SOURCES: :path: "../node_modules/@react-native-async-storage/async-storage" RNCMaskedView: :path: "../node_modules/@react-native-masked-view/masked-view" + RNCPicker: + :path: "../node_modules/@react-native-picker/picker" RNDateTimePicker: :path: "../node_modules/@react-native-community/datetimepicker" RNGestureHandler: @@ -2242,6 +2247,7 @@ SPEC CHECKSUMS: ReactCommon: 788c996e0ae30635a67c2567db2e21f459bcd632 RNCAsyncStorage: aa75595c1aefa18f868452091fa0c411a516ce11 RNCMaskedView: de80352547bd4f0d607bf6bab363d826822bd126 + RNCPicker: 124b4fb5859ba1a3fd53a91e16d1e7a0fc016e59 RNDateTimePicker: dde7ca9005d716f3efa9a63004b441679bca9a41 RNGestureHandler: 4c50b5e4ca199cf497511d44e3fe45d54d62e279 RNNotifee: 271cfeb505183d2cd1b858c14c3968b6ca30a642 diff --git a/package-lock.json b/package-lock.json index 45a63dc3d..8cb9d82ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -69,11 +69,13 @@ "html-react-parser": "^5.1.12", "html-to-text": "^9.0.5", "https": "^1.0.0", + "install": "^0.13.0", "js-base64": "^3.7.7", "lodash": "^4.17.21", "lottie-react-native": "^6.7.0", "lucide-react-native": "^0.483.0", "notifee": "^0.0.1", + "npm": "^11.2.0", "openid-client": "^5.7.0", "pawdirecte": "^1.9.0", "pawnilim": "^0.2.0", @@ -5196,7 +5198,6 @@ "version": "2.11.0", "resolved": "https://registry.npmjs.org/@react-native-picker/picker/-/picker-2.11.0.tgz", "integrity": "sha512-QuZU6gbxmOID5zZgd/H90NgBnbJ3VV6qVzp6c7/dDrmWdX8S0X5YFYgDcQFjE3dRen9wB9FWnj2VVdPU64adSg==", - "license": "MIT", "workspaces": [ "example" ], @@ -11610,6 +11611,14 @@ "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", "license": "MIT" }, + "node_modules/install": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/install/-/install-0.13.0.tgz", + "integrity": "sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/internal-ip": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", @@ -12756,7 +12765,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, "license": "MIT" }, "node_modules/json-schema-deref-sync": { @@ -14395,6 +14403,161 @@ "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "license": "UNLICENSED" }, + "node_modules/npm": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-11.2.0.tgz", + "integrity": "sha512-PcnFC6gTo9VDkxVaQ1/mZAS3JoWrDjAI+a6e2NgfYQSGDwftJlbdV0jBMi2V8xQPqbGcWaa7p3UP0SKF+Bhm2g==", + "bundleDependencies": [ + "@isaacs/string-locale-compare", + "@npmcli/arborist", + "@npmcli/config", + "@npmcli/fs", + "@npmcli/map-workspaces", + "@npmcli/package-json", + "@npmcli/promise-spawn", + "@npmcli/redact", + "@npmcli/run-script", + "@sigstore/tuf", + "abbrev", + "archy", + "cacache", + "chalk", + "ci-info", + "cli-columns", + "fastest-levenshtein", + "fs-minipass", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "libnpmaccess", + "libnpmdiff", + "libnpmexec", + "libnpmfund", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minimatch", + "minipass", + "minipass-pipeline", + "ms", + "node-gyp", + "nopt", + "normalize-package-data", + "npm-audit-report", + "npm-install-checks", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "p-map", + "pacote", + "parse-conflict-json", + "proc-log", + "qrcode-terminal", + "read", + "semver", + "spdx-expression-parse", + "ssri", + "supports-color", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which" + ], + "workspaces": [ + "docs", + "smoke-tests", + "mock-globals", + "mock-registry", + "workspaces/*" + ], + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^9.0.1", + "@npmcli/config": "^10.1.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/map-workspaces": "^4.0.2", + "@npmcli/package-json": "^6.1.1", + "@npmcli/promise-spawn": "^8.0.2", + "@npmcli/redact": "^3.1.1", + "@npmcli/run-script": "^9.0.1", + "@sigstore/tuf": "^3.0.0", + "abbrev": "^3.0.0", + "archy": "~1.0.0", + "cacache": "^19.0.1", + "chalk": "^5.4.1", + "ci-info": "^4.1.0", + "cli-columns": "^4.0.0", + "fastest-levenshtein": "^1.0.16", + "fs-minipass": "^3.0.3", + "glob": "^10.4.5", + "graceful-fs": "^4.2.11", + "hosted-git-info": "^8.0.2", + "ini": "^5.0.0", + "init-package-json": "^8.0.0", + "is-cidr": "^5.1.1", + "json-parse-even-better-errors": "^4.0.0", + "libnpmaccess": "^10.0.0", + "libnpmdiff": "^8.0.1", + "libnpmexec": "^10.1.0", + "libnpmfund": "^7.0.1", + "libnpmorg": "^8.0.0", + "libnpmpack": "^9.0.1", + "libnpmpublish": "^11.0.0", + "libnpmsearch": "^9.0.0", + "libnpmteam": "^8.0.0", + "libnpmversion": "^8.0.0", + "make-fetch-happen": "^14.0.3", + "minimatch": "^9.0.5", + "minipass": "^7.1.1", + "minipass-pipeline": "^1.2.4", + "ms": "^2.1.2", + "node-gyp": "^11.1.0", + "nopt": "^8.1.0", + "normalize-package-data": "^7.0.0", + "npm-audit-report": "^6.0.0", + "npm-install-checks": "^7.1.1", + "npm-package-arg": "^12.0.2", + "npm-pick-manifest": "^10.0.0", + "npm-profile": "^11.0.1", + "npm-registry-fetch": "^18.0.2", + "npm-user-validate": "^3.0.0", + "p-map": "^7.0.3", + "pacote": "^21.0.0", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "qrcode-terminal": "^0.12.0", + "read": "^4.1.0", + "semver": "^7.7.1", + "spdx-expression-parse": "^4.0.0", + "ssri": "^12.0.0", + "supports-color": "^10.0.0", + "tar": "^6.2.1", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^3.0.0", + "validate-npm-package-name": "^6.0.0", + "which": "^5.0.0" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/npm-package-arg": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-7.0.0.tgz", @@ -14437,6 +14600,2293 @@ "node": ">=4" } }, + "node_modules/npm/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/npm/node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/agent": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/arborist": { + "version": "9.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/metavuln-calculator": "^9.0.0", + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.1", + "@npmcli/query": "^4.0.0", + "@npmcli/redact": "^3.0.0", + "@npmcli/run-script": "^9.0.1", + "bin-links": "^5.0.0", + "cacache": "^19.0.1", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^8.0.0", + "json-stringify-nice": "^1.1.4", + "lru-cache": "^10.2.2", + "minimatch": "^9.0.4", + "nopt": "^8.0.0", + "npm-install-checks": "^7.1.0", + "npm-package-arg": "^12.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.1", + "pacote": "^21.0.0", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "proggy": "^3.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^3.0.1", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "ssri": "^12.0.0", + "treeverse": "^3.0.0", + "walk-up-path": "^4.0.0" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@npmcli/config": { + "version": "10.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/package-json": "^6.0.1", + "ci-info": "^4.0.0", + "ini": "^5.0.0", + "nopt": "^8.1.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "walk-up-path": "^4.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@npmcli/fs": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/git": { + "version": "6.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^8.0.0", + "ini": "^5.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^10.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "4.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "glob": "^10.2.2", + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^19.0.0", + "json-parse-even-better-errors": "^4.0.0", + "pacote": "^21.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/package-json": { + "version": "6.1.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.5.3", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "8.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/query": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^6.1.2" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/redact": { + "version": "3.1.1", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "9.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "node-gyp": "^11.0.0", + "proc-log": "^5.0.0", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/@sigstore/bundle": { + "version": "3.1.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.4.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/core": { + "version": "2.0.0", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/protobuf-specs": { + "version": "0.4.0", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/sign": { + "version": "3.1.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.1.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.4.0", + "make-fetch-happen": "^14.0.2", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/tuf": { + "version": "3.1.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.4.0", + "tuf-js": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/verify": { + "version": "2.1.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.1.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.4.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@tufjs/models": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/abbrev": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/agent-base": { + "version": "7.1.3", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/ansi-regex": { + "version": "5.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-styles": { + "version": "6.2.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/aproba": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/bin-links": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^7.0.0", + "npm-normalize-package-bin": "^4.0.0", + "proc-log": "^5.0.0", + "read-cmd-shim": "^5.0.0", + "write-file-atomic": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/binary-extensions": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/brace-expansion": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm/node_modules/cacache": { + "version": "19.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^4.0.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "tar": "^7.4.3", + "unique-filename": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/chownr": { + "version": "3.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/minizlib": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/mkdirp": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/tar": { + "version": "7.4.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/yallist": { + "version": "5.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/chalk": { + "version": "5.4.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm/node_modules/chownr": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ci-info": { + "version": "4.1.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cidr-regex": { + "version": "4.1.3", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^5.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/cli-columns": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/cmd-shim": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/cross-spawn": { + "version": "7.0.6", + "inBundle": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cssesc": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/debug": { + "version": "4.4.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/diff": { + "version": "7.0.0", + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/npm/node_modules/eastasianwidth": { + "version": "0.2.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/exponential-backoff": { + "version": "3.1.2", + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.16", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/npm/node_modules/foreground-child": { + "version": "3.3.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/fs-minipass": { + "version": "3.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/glob": { + "version": "10.4.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.11", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/hosted-git-info": { + "version": "8.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.1.1", + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/http-proxy-agent": { + "version": "7.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/https-proxy-agent": { + "version": "7.0.6", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ignore-walk": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/npm/node_modules/ini": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/init-package-json": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/package-json": "^6.1.0", + "npm-package-arg": "^12.0.0", + "promzard": "^2.0.0", + "read": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/ip-address": { + "version": "9.0.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/npm/node_modules/ip-regex": { + "version": "5.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/is-cidr": { + "version": "5.1.1", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "^4.1.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/isexe": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/jackspeak": { + "version": "3.4.3", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/npm/node_modules/jsbn": { + "version": "1.1.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.4", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff": { + "version": "6.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff-apply": { + "version": "5.5.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/libnpmaccess": { + "version": "10.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmdiff": { + "version": "8.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^9.0.1", + "@npmcli/installed-package-contents": "^3.0.0", + "binary-extensions": "^3.0.0", + "diff": "^7.0.0", + "minimatch": "^9.0.4", + "npm-package-arg": "^12.0.0", + "pacote": "^21.0.0", + "tar": "^6.2.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmexec": { + "version": "10.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^9.0.1", + "@npmcli/package-json": "^6.1.1", + "@npmcli/run-script": "^9.0.1", + "ci-info": "^4.0.0", + "npm-package-arg": "^12.0.0", + "pacote": "^21.0.0", + "proc-log": "^5.0.0", + "read": "^4.0.0", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "walk-up-path": "^4.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmfund": { + "version": "7.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^9.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmorg": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmpack": { + "version": "9.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^9.0.1", + "@npmcli/run-script": "^9.0.1", + "npm-package-arg": "^12.0.0", + "pacote": "^21.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmpublish": { + "version": "11.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "ci-info": "^4.0.0", + "normalize-package-data": "^7.0.0", + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1", + "proc-log": "^5.0.0", + "semver": "^7.3.7", + "sigstore": "^3.0.0", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmsearch": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmteam": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/libnpmversion": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.1", + "@npmcli/run-script": "^9.0.1", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/lru-cache": { + "version": "10.4.3", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/make-fetch-happen": { + "version": "14.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^3.0.0", + "cacache": "^19.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/make-fetch-happen/node_modules/negotiator": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/minimatch": { + "version": "9.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/minipass": { + "version": "7.1.2", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-collect": { + "version": "2.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-fetch": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/minipass-fetch/node_modules/minizlib": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minizlib": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ms": { + "version": "2.1.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/mute-stream": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp": { + "version": "11.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^14.0.3", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "tar": "^7.4.3", + "which": "^5.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/chownr": { + "version": "3.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/minizlib": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/mkdirp": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/tar": { + "version": "7.4.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/yallist": { + "version": "5.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/nopt": { + "version": "8.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/normalize-package-data": { + "version": "7.0.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^8.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-audit-report": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-bundled": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-install-checks": { + "version": "7.1.1", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-package-arg": { + "version": "12.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-packlist": { + "version": "10.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^7.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "10.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^7.1.0", + "npm-normalize-package-bin": "^4.0.0", + "npm-package-arg": "^12.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-profile": { + "version": "11.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "18.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^3.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^14.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^12.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch/node_modules/minizlib": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/npm-user-validate": { + "version": "3.0.0", + "inBundle": true, + "license": "BSD-2-Clause", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/p-map": { + "version": "7.0.3", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/package-json-from-dist": { + "version": "1.0.1", + "inBundle": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/npm/node_modules/pacote": { + "version": "21.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^10.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^3.0.0", + "ssri": "^12.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm/node_modules/parse-conflict-json": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "just-diff": "^6.0.0", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/path-key": { + "version": "3.1.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/path-scurry": { + "version": "1.11.1", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/proc-log": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/proggy": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-call-limit": { + "version": "3.0.2", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/promzard": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "read": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/npm/node_modules/read": { + "version": "4.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "mute-stream": "^2.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-cmd-shim": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-package-json-fast": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/retry": { + "version": "0.12.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/rimraf": { + "version": "5.0.10", + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT", + "optional": true + }, + "node_modules/npm/node_modules/semver": { + "version": "7.7.1", + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/shebang-command": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/shebang-regex": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/signal-exit": { + "version": "4.1.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/sigstore": { + "version": "3.1.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.1.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.4.0", + "@sigstore/sign": "^3.1.0", + "@sigstore/tuf": "^3.1.0", + "@sigstore/verify": "^2.1.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks": { + "version": "2.8.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks-proxy-agent": { + "version": "8.0.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/spdx-correct": { + "version": "3.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.5.0", + "inBundle": true, + "license": "CC-BY-3.0" + }, + "node_modules/npm/node_modules/spdx-expression-parse": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.21", + "inBundle": true, + "license": "CC0-1.0" + }, + "node_modules/npm/node_modules/sprintf-js": { + "version": "1.1.3", + "inBundle": true, + "license": "BSD-3-Clause" + }, + "node_modules/npm/node_modules/ssri": { + "version": "12.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/string-width": { + "version": "4.2.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi": { + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "10.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "6.2.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/treeverse": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "3.0.1", + "debug": "^4.3.6", + "make-fetch-happen": "^14.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-filename": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-slug": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/npm/node_modules/which": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/which/node_modules/isexe": { + "version": "3.1.1", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm/node_modules/wrap-ansi": { + "version": "8.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.1.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/write-file-atomic": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC" + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", diff --git a/package.json b/package.json index 44294b158..e15504534 100644 --- a/package.json +++ b/package.json @@ -71,11 +71,13 @@ "html-react-parser": "^5.1.12", "html-to-text": "^9.0.5", "https": "^1.0.0", + "install": "^0.13.0", "js-base64": "^3.7.7", "lodash": "^4.17.21", "lottie-react-native": "^6.7.0", "lucide-react-native": "^0.483.0", "notifee": "^0.0.1", + "npm": "^11.2.0", "openid-client": "^5.7.0", "pawdirecte": "^1.9.0", "pawnilim": "^0.2.0", diff --git a/src/views/account/Homeworks/Document.tsx b/src/views/account/Homeworks/Document.tsx index e0adf1b95..11add4dbc 100644 --- a/src/views/account/Homeworks/Document.tsx +++ b/src/views/account/Homeworks/Document.tsx @@ -26,17 +26,11 @@ import { useCurrentAccount } from "@/stores/account"; import { AccountService } from "@/stores/account/types"; import getAndOpenFile from "@/utils/files/getAndOpenFile"; import { AutoFileIcon } from "@/components/Global/FileIcon"; -import { Paperclip, CircleAlert, PencilLine, MoreHorizontal, Trash2, CalendarClock } from "lucide-react-native"; +import { Paperclip, CircleAlert, PencilLine, MoreHorizontal, Trash2 } from "lucide-react-native"; import LinkFavicon, { getURLDomain } from "@/components/Global/LinkFavicon"; import { timestampToString } from "@/utils/format/DateHelper"; import parse_homeworks from "@/utils/format/format_pronote_homeworks"; import PapillonPicker from "@/components/Global/PapillonPicker"; -import BottomSheet from "@/components/Modals/PapillonBottomSheet"; -import HomeworkItem from "./Atoms/Item"; -import { Picker } from "@react-native-picker/picker"; -import ResponsiveTextInput from "@/components/FirstInstallation/ResponsiveTextInput"; -import ButtonCta from "@/components/FirstInstallation/ButtonCta"; -import DateTimePicker from "@react-native-community/datetimepicker"; import { getSubjectData } from "@/services/shared/Subject"; import { useHomeworkStore } from "@/stores/homework"; import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; @@ -75,18 +69,6 @@ const HomeworksDocument: Screen<"HomeworksDocument"> = ({ navigation, route }) = const [homework, setHomework] = useState(route.params.homework || {}); - useEffect(() => { - const pretty = Object.entries(localSubjects || {}).find( - (element) => element[1].pretty === homework.subject - )?.[1]; - - if (pretty) setSelectedPretty(pretty); - - setIdHomework(parseInt(homework.id)); - setContentHomework(homework.content); - setDateHomework(homework.due); - }, [homework]); - const openUrl = (url: string) => { if ( @@ -194,11 +176,14 @@ const HomeworksDocument: Screen<"HomeworksDocument"> = ({ navigation, route }) = { icon: , label: "Modifier le devoir", + sfSymbol: "pencil", onPress: () => setShowCreateHomework(true), }, { icon: , label: "Supprimer le devoir", + sfSymbol: "trash", + destructive: true, onPress: () => { Alert.alert( "Supprimer le devoir", @@ -235,211 +220,6 @@ const HomeworksDocument: Screen<"HomeworksDocument"> = ({ navigation, route }) = - { - setShowCreateHomework(bool); - if (!bool) { - setSelectedPretty(Object.entries(localSubjects || {})[0]?.[1] ?? null); - setIdHomework(NaN); - setContentHomework(null); - setContentHomework(null); - setDateHomework(Date.now()); - } - setHomework(homework); - }} - contentContainerStyle={{ - paddingHorizontal: 16, - borderColor: theme.colors.border, - borderWidth: 1, - }} - > - - - Modifier un devoir - - - - - - - Aperçu - - - undefined} - total={1} - /> - - - - - - - setShowDatePicker(true)} - chevron={false} - > - - Date du devoir - - - - - {new Date(dateHomework).toLocaleDateString( - "fr-FR", - { - day: "numeric", - month: "long", - year: "numeric", - } - )} - - - - - - - - - Nom de la matière - - - { - const selectedSubject = Object.entries(localSubjects).find( - ([, subject]) => subject.pretty === itemValue - ); - - if (selectedSubject) { - setSelectedPretty(selectedSubject[1]); - } - }} - style={{ - color: theme.colors.text, - }} - > - {Object.entries(localSubjects).map(([key, subject]) => ( - - ))} - - - - - - - - - - Contenu du devoir - - { - if (input === "") { - setContentHomework(null); - } else { - setContentHomework(input); - } - }} - /> - - - - {showDatePicker && ( - { - setShowDatePicker(false); - if (selectedDate) { - selectedDate.setHours(0, 0, 0, 0); - setDateHomework(selectedDate.getTime()); - } - }} - /> - )} - - { - const newHomework: Homework = { - ...homework, - subject: selectedPretty.pretty, - color: selectedPretty.color, - content: contentHomework ?? "", - due: dateHomework, - }; - - useHomeworkStore - .getState() - .updateHomework( - dateToEpochWeekNumber(new Date(dateHomework)), - homework.id, - newHomework - ); - - setShowCreateHomework(false); - setSelectedPretty(Object.entries(localSubjects || {})[0]?.[1] ?? null); - setIdHomework(NaN); - setContentHomework(null); - setDateHomework(Date.now()); - setHomework(newHomework); - }} - style={{ - minWidth: undefined, - maxWidth: undefined, - width: "50%", - alignSelf: "center", - marginTop: 15, - }} - /> - - ; + account: Account; + updateHomeworks: () => Promise; + loading: boolean; + getDayName: (date: string | number | Date) => string; +}; + +const formatDate = (date: string | number | Date): string => { + return new Date(date).toLocaleDateString("fr-FR", { + day: "numeric", + month: "long" + }); +}; + const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const flatListRef: React.MutableRefObject = useRef(null) as any as React.MutableRefObject; const { width } = Dimensions.get("window"); + const finalWidth = width; const insets = useSafeAreaInsets(); const { playHaptics } = useSoundHapticsWrapper(); const { isOnline } = useOnlineStatus(); @@ -68,17 +80,13 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const account = useCurrentAccount(store => store.account!); const hasServiceSetup = account.service === AccountService.PapillonMultiService ? hasFeatureAccountSetup(MultiServiceFeature.Homeworks, account.localID) : true; const homeworks = useHomeworkStore(store => store.homeworks); - const localSubjects = account.personalization.subjects ?? {}; - const [selectedPretty, setSelectedPretty] = useState( - Object.entries(localSubjects || {})[0]?.[1] ?? null - ); // @ts-expect-error let firstDate = account?.instance?.instance?.firstDate || null; if (!firstDate) { firstDate = new Date(); - firstDate.setMonth(8); - firstDate.setDate(1); + firstDate.setUTCMonth(8); + firstDate.setUTCDate(1); } const firstDateEpoch = dateToEpochWeekNumber(firstDate); @@ -86,29 +94,24 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const [data, setData] = useState(Array.from({ length: 100 }, (_, i) => currentWeek - 50 + i)); const [selectedWeek, setSelectedWeek] = useState(currentWeek); + const [direction, setDirection] = useState<"left" | "right">("right"); + const [oldSelectedWeek, setOldSelectedWeek] = useState(selectedWeek); - // Filtrer les devoirs - const SearchRef: React.MutableRefObject = useRef(null) as any as React.MutableRefObject; - const [showDatePickerWeek, setShowDatePickerWeek] = useState(false); const [hideDone, setHideDone] = useState(false); - const [showFilter, setShowFilter] = useState(false); - const [searchHasFocus, setSearchHasFocus] = useState(false); - - // Création de devoirs personnalisés - const [showCreateHomework, setShowCreateHomework] = useState(false); - const [showDatePicker, setShowDatePicker] = useState(false); - const [idHomework, setIdHomework] = useState(NaN); - const [contentHomework, setContentHomework] = useState(null); - const [dateHomework, setDateHomework] = useState(Date.now()); const getItemLayout = useCallback((_: any, index: number) => ({ - length: width, - offset: width * index, + length: finalWidth, + offset: finalWidth * index, index, }), [width]); const keyExtractor = useCallback((item: any) => item.toString(), []); + const getDayName = (date: string | number | Date): string => { + const days = ["Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche"]; + return days[new Date(date).getUTCDay()]; + }; + const [loading, setLoading] = useState(false); const [refreshing, setRefreshing] = useState(false); @@ -143,23 +146,20 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { // on page change, load the homeworks useEffect(() => { - updateHomeworks(false, false); + if (selectedWeek > oldSelectedWeek) { + setDirection("right"); + } else if (selectedWeek < oldSelectedWeek) { + setDirection("left"); + } + + setTimeout(() => { + setOldSelectedWeek(selectedWeek); + updateHomeworks(false, false); + }, 0); }, [selectedWeek]); const [searchTerms, setSearchTerms] = useState(""); - const getDayName = useCallback((date: string | number | Date): string => { - const days = ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"]; - return days[new Date(date).getDay()]; - }, []); - - const formatDate = useCallback((date: string | number | Date): string => { - return new Date(date).toLocaleDateString("fr-FR", { - day: "numeric", - month: "long", - }); - }, []); - const renderWeek: ListRenderItem = useCallback(({ item }) => { const homeworksInWeek = [...(homeworks[item] ?? [])]; @@ -348,37 +348,92 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { isOnline, ]); - const onEndReached = useCallback(() => { + const onEndReached = () => { const lastWeek = data[data.length - 1]; const newWeeks = Array.from({ length: 50 }, (_, i) => lastWeek + i + 1); - setData((prevData) => [...prevData, ...newWeeks]); - }, [data]); + setData(prevData => [...prevData, ...newWeeks]); + }; - const onStartReached = useCallback(() => { + const onStartReached = () => { const firstWeek = data[0]; const newWeeks = Array.from({ length: 50 }, (_, i) => firstWeek - 50 + i); - setData((prevData) => [...newWeeks, ...prevData]); + setData(prevData => [...newWeeks, ...prevData]); flatListRef.current?.scrollToIndex({ index: 50, animated: false }); - }, [data]); + }; - const onScroll = useCallback(({ nativeEvent }: NativeSyntheticEvent) => { - if (nativeEvent.contentOffset.x < width) { + const onScroll: ScrollViewProps["onScroll"] = useCallback(({ nativeEvent }: NativeSyntheticEvent) => { + if (nativeEvent.contentOffset.x < finalWidth) { onStartReached(); } // Update selected week based on scroll position - const index = Math.round(nativeEvent.contentOffset.x / width); + const index = Math.round(nativeEvent.contentOffset.x / finalWidth); setSelectedWeek(data[index]); - }, [width, data]); + }, [finalWidth, data]); - const onMomentumScrollEnd = useCallback(({ nativeEvent }: NativeSyntheticEvent) => { - const index = Math.round(nativeEvent.contentOffset.x / width); + const onMomentumScrollEnd: ScrollViewProps["onMomentumScrollEnd"] = useCallback(({ nativeEvent }: NativeSyntheticEvent) => { + const index = Math.round(nativeEvent.contentOffset.x / finalWidth); setSelectedWeek(data[index]); - }, [width, data]); + }, [finalWidth, data]); + + const goToWeek = useCallback((weekNumber: number) => { + const index = data.findIndex(week => week === weekNumber); + if (index !== -1) { + // @ts-expect-error + const currentIndex = Math.round(flatListRef.current?.contentOffset?.x / finalWidth) || 0; + const distance = Math.abs(index - currentIndex); + const animated = distance <= 10; // Animate if the distance is 10 weeks or less + + flatListRef.current?.scrollToIndex({ index, animated }); + setSelectedWeek(weekNumber); + } else { + // If the week is not in the current data, update the data and scroll + const newData = Array.from({ length: 100 }, (_, i) => weekNumber - 50 + i); + setData(newData); + + // Use a timeout to ensure the FlatList has updated before scrolling + setTimeout(() => { + flatListRef.current?.scrollToIndex({ index: 50, animated: false }); + setSelectedWeek(weekNumber); + }, 0); + } + }, [data, finalWidth]); + + const [showPickerButtons, setShowPickerButtons] = useState(false); + const [searchHasFocus, setSearchHasFocus] = useState(false); + + const SearchRef: React.MutableRefObject = useRef(null) as any as React.MutableRefObject; return ( - <> + + {showPickerButtons && !searchHasFocus && + + goToWeek(selectedWeek - 1)} + activeScale={0.8} + > + + + + + + } + + {!searchHasFocus && = ({ route, navigation }) => { > setShowDatePickerWeek(true)} + onPress={() => setShowPickerButtons(!showPickerButtons)} onLongPress={() => { setHideDone(!hideDone); playHaptics("notification", { @@ -398,7 +453,9 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { = ({ route, navigation }) => { }]} tint={theme.dark ? "dark" : "light"} > + {showPickerButtons && !loading && + + + + } + + {!showPickerButtons && hideDone && + + + + } + = ({ route, navigation }) => { value={((selectedWeek - firstDateEpoch % 52) % 52 + 1).toString()} style={[styles.weekPickerText, styles.weekPickerTextNbr, { - color: theme.colors.text, + color: showPickerButtons ? theme.colors.primary : theme.colors.text, } ]} /> @@ -435,7 +525,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { {loading && = ({ route, navigation }) => { + } - {showDatePickerWeek && ( - { - setShowDatePickerWeek(false); - if (selectedDate) { - selectedDate.setHours(0, 0, 0, 0); - - const weekNumber = dateToEpochWeekNumber(selectedDate); - const index = data.findIndex((week) => week === weekNumber); - if (index !== -1) { - flatListRef.current?.scrollToIndex({ index, animated: true }); - setSelectedWeek(weekNumber); - } - } + {showPickerButtons && !searchHasFocus && + + goToWeek(selectedWeek + 1)} + activeScale={0.8} + > + + + + + + } + + {showPickerButtons && !searchHasFocus && + - )} - - + } + {showPickerButtons && !searchHasFocus && width > 330 && = ({ route, navigation }) => { style={{ alignItems: "center", justifyContent: "center", - backgroundColor: theme.colors.background + "ff", + backgroundColor: hideDone ? theme.colors.primary : theme.colors.background + "ff", borderColor: theme.colors.border + "dd", borderWidth: 1, borderRadius: 800, height: 40, - width: 40, + width: showPickerButtons ? 40 : null, + minWidth: showPickerButtons ? 40 : null, + maxWidth: showPickerButtons ? 40 : null, gap: 4, shadowColor: "#00000022", shadowOffset: { width: 0, height: 2 }, @@ -499,31 +600,37 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { > { - setIdHomework(Math.random() * 1000 + 1); - setShowCreateHomework(true); + setHideDone(!hideDone); }} > - + } = ({ route, navigation }) => { > { - setShowFilter(true); + setShowPickerButtons(false); + + setTimeout(() => { + // #TODO : change timeout method or duration + SearchRef.current?.focus(); + }, 20); }} > - {searchTerms.length > 0 || hideDone ? ( - - ) : ( - - )} + - - - - - - Filtrer les devoirs - - - - - - - Recherche - - + - SearchRef.current?.focus()}> - - - - - setSearchHasFocus(true)} - onBlur={() => setSearchHasFocus(false)} - ref={SearchRef} - /> - + onFocus={() => setSearchHasFocus(true)} + onBlur={() => setSearchHasFocus(false)} + ref={SearchRef} + /> + + } - {searchTerms.length > 0 && ( - setSearchTerms("")} - > - - - - - )} - - - - - - - - Options - - - - { - setHideDone(!hideDone); - }} - > - - - - )} - > - - {hideDone ? "Afficher tous les devoirs" : "Afficher uniquement les devoirs non faits"} - - - - - - )} + {searchTerms.length > 0 && searchHasFocus && + { - setShowFilter(false); - navigation.navigate("SettingStack", { screen: "SettingsMagic" }); + setSearchTerms(""); }} > - - Configurer Papillon Magic pour les devoirs - - - - - - { - setShowCreateHomework(bool); - if (!bool) { - setSelectedPretty(Object.entries(localSubjects || {})[0]?.[1] ?? null); - setIdHomework(NaN); - setContentHomework(null); - setContentHomework(null); - setDateHomework(Date.now()); - setLoading(false); - } - }} - contentContainerStyle={{ - paddingHorizontal: 16, - borderColor: theme.colors.border, - borderWidth: 1, - }} - > - - - Créer un devoir - - - - - - - Aperçu - - - undefined} - total={1} - /> - - - - - - - setShowDatePicker(true)} - chevron={false} + - - Date du devoir - - - - - {new Date(dateHomework).toLocaleDateString( - "fr-FR", - { - day: "numeric", - month: "long", - year: "numeric", - } - )} - - - - - - - - - Nom de la matière - - - { - const selectedSubject = Object.entries(localSubjects).find( - ([, subject]) => subject.pretty === itemValue - ); - - if (selectedSubject) { - setSelectedPretty(selectedSubject[1]); - } - }} - style={{ - color: theme.colors.text, - }} - > - {Object.entries(localSubjects).map(([key, subject]) => ( - - ))} - - - - - - - - - - Contenu du devoir - - { - if (input === "") { - setContentHomework(null); - } else { - setContentHomework(input); - } - }} - /> - - - - {showDatePicker && ( - { - setShowDatePicker(false); - if (selectedDate) { - selectedDate.setHours(0, 0, 0, 0); - setDateHomework(selectedDate.getTime()); - } - }} - /> - )} - - { - setLoading(true); - - if (!selectedPretty || !contentHomework) { - Alert.alert("Veuillez remplir tous les champs avant de valider."); - setLoading(false); - return; - } - - // Créez un objet représentant le devoir - const newHomework: Homework = { - id: String(idHomework), - subject: selectedPretty.pretty, - color: selectedPretty.color, - content: contentHomework, - due: dateHomework, - done: false, - personalizate: true, - attachments: [], - exam: false, - }; - - useHomeworkStore - .getState() - .addHomework( - dateToEpochWeekNumber(new Date(dateHomework)), - newHomework - ); - - setShowCreateHomework(false); - setSelectedPretty(Object.entries(localSubjects || {})[0]?.[1] ?? null); - setIdHomework(NaN); - setContentHomework(null); - setDateHomework(Date.now()); - setLoading(false); - }} - primary={!loading} - icon={loading ? : void 0} - disabled={loading} - style={{ - minWidth: undefined, - maxWidth: undefined, - width: "50%", - alignSelf: "center", - marginTop: 15, - }} - /> - + + + + } + + = ({ route, navigation }) => { height: "100%", }} /> - + ); }; @@ -1048,4 +790,4 @@ const styles = StyleSheet.create({ }, }); -export default WeekView; +export default WeekView; \ No newline at end of file From 7c5d022e0080cb068e74bff4158b9da70b89b169 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 25 Mar 2025 20:07:00 +0100 Subject: [PATCH 1038/1144] =?UTF-8?q?feat(Homeworks):=20ajout=20de=20l'?= =?UTF-8?q?=C3=A9cran=20d'ajout=20de=20devoir=20et=20d'un=20bouton=20flott?= =?UTF-8?q?ant?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Global/NativeComponents.tsx | 7 +- src/router/helpers/types.ts | 5 + src/router/screens/views/index.ts | 10 + src/views/account/Homeworks/AddHomework.tsx | 271 ++++++++++++++++++++ src/views/account/Homeworks/Document.tsx | 7 +- src/views/account/Homeworks/Homeworks.tsx | 53 +++- 6 files changed, 347 insertions(+), 6 deletions(-) create mode 100644 src/views/account/Homeworks/AddHomework.tsx diff --git a/src/components/Global/NativeComponents.tsx b/src/components/Global/NativeComponents.tsx index 7bc120dc4..efc8ae03f 100644 --- a/src/components/Global/NativeComponents.tsx +++ b/src/components/Global/NativeComponents.tsx @@ -261,9 +261,10 @@ export const NativeItem: React.FC = ({ {icon && ( React.cloneElement(icon as React.ReactElement, { - size: 24, - color: colors.text, - style: { + size: icon.props.size || 24, + color: icon.props.color || colors.text, + style: icon.props.style || { + ...icon.props.style, marginRight: 16, opacity: 0.8, marginLeft: 0, diff --git a/src/router/helpers/types.ts b/src/router/helpers/types.ts index c461095ff..cba9b2bb3 100644 --- a/src/router/helpers/types.ts +++ b/src/router/helpers/types.ts @@ -99,6 +99,11 @@ export type RouteParameters = { Homeworks?: { outsideNav?: boolean }; HomeworksDocument: { homework: Homework }; + AddHomework: { + hwid?: string; + modal?: boolean; + defaults?: { subject: string; content: string; date: number }; + }; News?: { outsideNav?: boolean; isED: boolean }; NewsItem: { message: string; important: boolean; isED: boolean }; diff --git a/src/router/screens/views/index.ts b/src/router/screens/views/index.ts index 1f9582cfb..95a5e0270 100644 --- a/src/router/screens/views/index.ts +++ b/src/router/screens/views/index.ts @@ -21,6 +21,7 @@ import ChatDetails from "@/views/account/Chat/Modals/ChatDetails"; import ChatThemes from "@/views/account/Chat/Modals/ChatThemes"; import RestaurantCardDetail from "@/views/account/Restaurant/Modals/CardDetail"; import RestaurantPaymentSuccess from "@/views/account/Restaurant/Modals/PaymentSuccess"; +import AddHomeworkScreen from "@/views/account/Homeworks/AddHomework"; export default [ createScreen("GradeReaction", GradeReaction, { @@ -85,6 +86,15 @@ export default [ headerShown: false, sheetCornerRadius: 16, }), + createScreen("AddHomework", AddHomeworkScreen, { + headerTitle: "Ajouter un devoir", + presentation: "formSheet", + headerShown: true, + sheetCornerRadius: 16, + sheetAllowedDetents: [0.5, 1], + sheetGrabberVisible: true, + sheetInitialDetentIndex: 0, + }), createScreen("GradeSubject", GradeSubjectScreen, { headerTitle: "Détail de la matière", presentation: "modal", diff --git a/src/views/account/Homeworks/AddHomework.tsx b/src/views/account/Homeworks/AddHomework.tsx new file mode 100644 index 000000000..6ac45cb5d --- /dev/null +++ b/src/views/account/Homeworks/AddHomework.tsx @@ -0,0 +1,271 @@ +import React, { useEffect, useLayoutEffect, useState } from "react"; + +import { Screen } from "@/router/helpers/types"; +import { Pressable, ScrollView } from "react-native-gesture-handler"; +import { NativeItem, NativeList, NativeText } from "@/components/Global/NativeComponents"; +import { useHomeworkStore } from "@/stores/homework"; +import { useCurrentAccount } from "@/stores/account"; + +import { useTheme } from "@react-navigation/native"; +import { ActivityIndicator, Alert, Platform, TextInput, View } from "react-native"; +import { Picker } from "@react-native-picker/picker"; +import PapillonPicker from "@/components/Global/PapillonPicker"; +import DateTimePicker from "@react-native-community/datetimepicker"; +import { BookOpen, Calendar } from "lucide-react-native"; +import ButtonCta from "@/components/FirstInstallation/ButtonCta"; +import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; + + +const AddHomeworkScreen: Screen<"AddHomework"> = ({ route, navigation }) => { + const account = useCurrentAccount(store => store.account!); + const theme = useTheme(); + const homeworks = useHomeworkStore(store => store.homeworks); + const localSubjects = account.personalization.subjects ?? {}; + const [selectedPretty, setSelectedPretty] = useState( + Object.entries(localSubjects || {})[0]?.[1] ?? null + ); + + // Création de devoirs personnalisés + const [currentHw, setCurrentHw] = useState(""); + const [loading, setLoading] = useState(false); + const [showDatePicker, setShowDatePicker] = useState(false); + const [idHomework, setIdHomework] = useState(NaN); + const [contentHomework, setContentHomework] = useState(null); + const [dateHomework, setDateHomework] = useState(Date.now()); + + + useEffect(() => { + if(route.params?.hwid) { + const allHomeworks = Object.values(homeworks).flat(); + const homework = allHomeworks.find(hw => hw.id === route.params?.hwid); + if(homework) { + setSelectedPretty(localSubjects[homework.subject]); + setIdHomework(Number(homework.id)); + setContentHomework(homework.content); + setDateHomework(homework.due); + setCurrentHw(homework); + } + } + }, [route.params?.hwid]); + + const createHomework = async () => { + setLoading(true); + + if (!selectedPretty || !contentHomework) { + Alert.alert("Veuillez remplir tous les champs avant de valider."); + setLoading(false); + return; + } + + // Créez un objet représentant le devoir + const newHomework: Homework = { + id: String(idHomework), + subject: selectedPretty.pretty, + color: selectedPretty.color, + content: contentHomework, + due: dateHomework, + done: false, + personalizate: true, + attachments: [], + exam: false, + }; + + useHomeworkStore + .getState() + .addHomework( + dateToEpochWeekNumber(new Date(dateHomework)), + newHomework + ); + + setSelectedPretty(Object.entries(localSubjects || {})[0]?.[1] ?? null); + setIdHomework(NaN); + setContentHomework(null); + setDateHomework(Date.now()); + setLoading(false); + + navigation.goBack(); + }; + + const updateHomework = async () => { + const newHomework: Homework = { + ...currentHw, + subject: selectedPretty.pretty, + color: selectedPretty.color, + content: contentHomework ?? "", + due: dateHomework, + }; + + useHomeworkStore + .getState() + .updateHomework( + dateToEpochWeekNumber(new Date(dateHomework)), + currentHw.id, + newHomework + ); + + setSelectedPretty(Object.entries(localSubjects || {})[0]?.[1] ?? null); + setIdHomework(NaN); + setContentHomework(null); + setDateHomework(Date.now()); + navigation.goBack(); + if(route.params?.modal) { + navigation.goBack(); + } + }; + + useLayoutEffect(() => { + navigation.setOptions({ + headerTitle: currentHw ? "Modifier le devoir" : "Ajouter un devoir", + }); + }, [navigation, currentHw]); + + return ( + + + } + trailing={ + + {Platform.OS === "ios" ? ( + ({ + label: subject.pretty, + onPress: () => setSelectedPretty(subject), + }))} + selected={selectedPretty?.pretty} + > + + + + + {selectedPretty?.pretty} + + + + ) : ( + { + const selectedSubject = Object.entries(localSubjects).find( + ([, subject]) => subject.pretty === itemValue + ); + + if (selectedSubject) { + setSelectedPretty(selectedSubject[1]); + } + }} + style={{ + color: theme.colors.text, + }} + > + {Object.entries(localSubjects).map(([key, subject]) => ( + + ))} + + )} + + } + > + + Matière + + + } + onPress={Platform.OS !== "ios" ? () => setShowDatePicker(true) : undefined} + trailing={ + showDatePicker || Platform.OS === "ios" ? ( + { + setShowDatePicker(false); + if (selectedDate) { + selectedDate.setHours(0, 0, 0, 0); + setDateHomework(selectedDate.getTime()); + } + }} + /> + ) : ( + + {new Date(dateHomework).toLocaleDateString()} + + ) + } + > + + {Platform.OS !== "ios" ? "Date" : "Sélectionner la date"} + + + + + + + { + if (input === "") { + setContentHomework(null); + } else { + setContentHomework(input); + } + }} + /> + + + + { + if(currentHw) { + updateHomework(); + } else { + createHomework(); + } + }} + primary={!loading} + icon={loading ? : void 0} + disabled={loading} + style={{ + minWidth: undefined, + maxWidth: undefined, + width: "50%", + alignSelf: "center", + marginTop: 15, + }} + /> + + ); +}; + +export default AddHomeworkScreen; \ No newline at end of file diff --git a/src/views/account/Homeworks/Document.tsx b/src/views/account/Homeworks/Document.tsx index 11add4dbc..a8417534b 100644 --- a/src/views/account/Homeworks/Document.tsx +++ b/src/views/account/Homeworks/Document.tsx @@ -177,7 +177,12 @@ const HomeworksDocument: Screen<"HomeworksDocument"> = ({ navigation, route }) = icon: , label: "Modifier le devoir", sfSymbol: "pencil", - onPress: () => setShowCreateHomework(true), + onPress: () => { + navigation.navigate("AddHomework", { + hwid: homework.id, + modal: true, + }); + }, }, { icon: , diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 28413661a..81dcc87ae 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -12,7 +12,8 @@ import { RefreshControl, StyleSheet, TextInput, - ListRenderItem + ListRenderItem, + Pressable } from "react-native"; import { dateToEpochWeekNumber, epochWNToDate } from "@/utils/epochWeekNumber"; @@ -20,7 +21,7 @@ import * as StoreReview from "expo-store-review"; import { PressableScale } from "react-native-pressable-scale"; import { TouchableOpacity } from "react-native-gesture-handler"; -import { Book, CheckSquare, ChevronLeft, ChevronRight, CircleDashed, Search, X } from "lucide-react-native"; +import { Book, CheckSquare, ChevronLeft, ChevronRight, CircleDashed, Plus, Search, X } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { BlurView } from "expo-blur"; @@ -42,6 +43,7 @@ import {MultiServiceFeature} from "@/stores/multiService/types"; import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; import { OfflineWarning, useOnlineStatus } from "@/hooks/useOnlineStatus"; import HomeworkItem from "./Atoms/Item"; +import { useBottomTabBarHeight } from "@react-navigation/bottom-tabs"; const MemoizedHomeworkItem = React.memo(HomeworkItem); const MemoizedNativeItem = React.memo(NativeItem); @@ -334,6 +336,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { } + ); }, [ homeworks, @@ -714,6 +717,8 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { + navigation.navigate("AddHomework")} /> + = ({ route, navigation }) => { ); }; +const AddHomeworkButton: React.FC<{ onPress: () => void }> = ({ onPress }) => { + const theme = useTheme(); + const insets = useSafeAreaInsets(); + const tabBarHeight = useBottomTabBarHeight(); + + return ( + onPress()} + style={({ pressed }) => [ + { + position: "absolute", + zIndex: 999999, + bottom: 16, + right: 16, + transform: [{ scale: pressed ? 0.95 : 1 }], + opacity: pressed ? 0.8 : 1, + shadowColor: "#000000", + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.2, + shadowRadius: 3, + overflow: "visible", + } + ]} + > + + + + + ); +}; + const styles = StyleSheet.create({ header: { paddingHorizontal: 16, From 407da893635d66ae4c82735ab1856a456f849752 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 25 Mar 2025 20:08:22 +0100 Subject: [PATCH 1039/1144] =?UTF-8?q?fix(HomeworkItem):=20ajustement=20de?= =?UTF-8?q?=20la=20couleur=20et=20de=20la=20taille=20de=20l'ic=C3=B4ne=20d?= =?UTF-8?q?e=20crayon?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/Atoms/Item.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Homeworks/Atoms/Item.tsx b/src/views/account/Homeworks/Atoms/Item.tsx index 4d7a52da3..1843c88c7 100644 --- a/src/views/account/Homeworks/Atoms/Item.tsx +++ b/src/views/account/Homeworks/Atoms/Item.tsx @@ -196,7 +196,7 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total } {homework.personalizate && ( - + )} {subjectData.pretty} From e8cc6cb4962845d90c79ffed691b8c04875452b2 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 25 Mar 2025 20:20:53 +0100 Subject: [PATCH 1040/1144] fix(AddHomework): ajustement du style de la vue pour les appareils Android --- src/views/account/Homeworks/AddHomework.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/views/account/Homeworks/AddHomework.tsx b/src/views/account/Homeworks/AddHomework.tsx index 6ac45cb5d..6152234ee 100644 --- a/src/views/account/Homeworks/AddHomework.tsx +++ b/src/views/account/Homeworks/AddHomework.tsx @@ -127,7 +127,14 @@ const AddHomeworkScreen: Screen<"AddHomework"> = ({ route, navigation }) => { } trailing={ - + {Platform.OS === "ios" ? ( ({ From 8cd271737890f179dfa5d007b682ea82a851bbef Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 25 Mar 2025 20:24:36 +0100 Subject: [PATCH 1041/1144] =?UTF-8?q?fix(AddHomeworkButton):=20ajustement?= =?UTF-8?q?=20de=20la=20navigation=20en=20fonction=20de=20l'=C3=A9tat=20ex?= =?UTF-8?q?t=C3=A9rieur?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/Homeworks.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 81dcc87ae..5a929f158 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -43,7 +43,6 @@ import {MultiServiceFeature} from "@/stores/multiService/types"; import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; import { OfflineWarning, useOnlineStatus } from "@/hooks/useOnlineStatus"; import HomeworkItem from "./Atoms/Item"; -import { useBottomTabBarHeight } from "@react-navigation/bottom-tabs"; const MemoizedHomeworkItem = React.memo(HomeworkItem); const MemoizedNativeItem = React.memo(NativeItem); @@ -717,7 +716,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { - navigation.navigate("AddHomework")} /> + navigation.navigate("AddHomework")} outsideNav={route.params?.outsideNav} /> = ({ route, navigation }) => { ); }; -const AddHomeworkButton: React.FC<{ onPress: () => void }> = ({ onPress }) => { +const AddHomeworkButton: React.FC<{ onPress: () => void, outsideNav: boolean }> = ({ onPress, outsideNav }) => { const theme = useTheme(); const insets = useSafeAreaInsets(); - const tabBarHeight = useBottomTabBarHeight(); return ( void }> = ({ onPress }) => { { position: "absolute", zIndex: 999999, - bottom: 16, + bottom: 16 + (outsideNav ? insets.bottom : 0), right: 16, transform: [{ scale: pressed ? 0.95 : 1 }], opacity: pressed ? 0.8 : 1, From bd3c99e8c1a84f39c2e05f0f78d13d8b65ba9e64 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 25 Mar 2025 20:26:29 +0100 Subject: [PATCH 1042/1144] =?UTF-8?q?fix(AddHomework):=20am=C3=A9lioration?= =?UTF-8?q?=20de=20la=20mise=20en=20page=20et=20ajustement=20de=20la=20lar?= =?UTF-8?q?geur=20pour=20les=20appareils?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/AddHomework.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/views/account/Homeworks/AddHomework.tsx b/src/views/account/Homeworks/AddHomework.tsx index 6152234ee..db68bb5d7 100644 --- a/src/views/account/Homeworks/AddHomework.tsx +++ b/src/views/account/Homeworks/AddHomework.tsx @@ -7,7 +7,7 @@ import { useHomeworkStore } from "@/stores/homework"; import { useCurrentAccount } from "@/stores/account"; import { useTheme } from "@react-navigation/native"; -import { ActivityIndicator, Alert, Platform, TextInput, View } from "react-native"; +import { ActivityIndicator, Alert, Dimensions, Platform, TextInput, View } from "react-native"; import { Picker } from "@react-native-picker/picker"; import PapillonPicker from "@/components/Global/PapillonPicker"; import DateTimePicker from "@react-native-community/datetimepicker"; @@ -132,6 +132,9 @@ const AddHomeworkScreen: Screen<"AddHomework"> = ({ route, navigation }) => { Platform.OS === "android" && { width: "50%", minWidth: 200, + }, + { + maxWidth: Dimensions.get("window").width - 200, } ]} > @@ -150,6 +153,7 @@ const AddHomeworkScreen: Screen<"AddHomework"> = ({ route, navigation }) => { gap: 8, paddingVertical: 9, paddingHorizontal: 6, + alignSelf: "flex-end", }} > = ({ route, navigation }) => { }} /> - + {selectedPretty?.pretty} From 9f0fa3deedb18f3bafeea63a4b568283b839d06e Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 25 Mar 2025 20:35:43 +0100 Subject: [PATCH 1043/1144] =?UTF-8?q?fix(BackgroundIUTLannion):=20mise=20?= =?UTF-8?q?=C3=A0=20jour=20de=20la=20pr=C3=A9sentation=20et=20ajout=20d'un?= =?UTF-8?q?=20effet=20de=20flou,=20am=C3=A9lioration=20de=20l'interface=20?= =?UTF-8?q?utilisateur?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/screens/views/index.ts | 5 ++- .../actions/BackgroundIUTLannion.tsx | 34 ++++++++++++++++--- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/router/screens/views/index.ts b/src/router/screens/views/index.ts index 00718d756..3ff29b6ca 100644 --- a/src/router/screens/views/index.ts +++ b/src/router/screens/views/index.ts @@ -126,7 +126,10 @@ export default [ }), createScreen("BackgroundIUTLannion", BackgroundIUTLannion, { headerTitle: "IUT de Lannion", - presentation: "modal", + presentation: "transparentModal", + headerShown: false, + animation: "fade", + animationDuration: 100, }), createScreen("EvaluationDocument", EvaluationDocument, { headerTitle: "Compétence", diff --git a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx index 06939c092..a2568e447 100644 --- a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx +++ b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx @@ -4,7 +4,7 @@ import { AccountService, Identity, LocalAccount } from "@/stores/account/types"; import uuid from "@/utils/uuid-v4"; import { useTheme } from "@react-navigation/native"; import React from "react"; -import { View } from "react-native"; +import { Pressable, View } from "react-native"; import { WebView } from "react-native-webview"; import type { Screen } from "@/router/helpers/types"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; @@ -13,6 +13,7 @@ import { animPapillon } from "@/utils/ui/animations"; import { FadeInDown, FadeOutUp } from "react-native-reanimated"; import { useAlert } from "@/providers/AlertProvider"; import { BadgeX, Undo2 } from "lucide-react-native"; +import { BlurView } from "expo-blur"; const providers = ["scodoc", "moodle", "ical"]; @@ -275,9 +276,11 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio return ( <> + + = ({ route, navigatio gap: 6, }} > + {step} - + Cela peut prendre quelques secondes... - + + { + navigation.goBack(); + }} + style={({ pressed }) => ({ + backgroundColor: "#ffffff33", + paddingHorizontal: 24, + paddingVertical: 12, + borderRadius: 60, + flexDirection: "row", + alignItems: "center", + justifyContent: "center", + gap: 6, + opacity: pressed ? 0.5 : 1, + })} + > + + Annuler + + { const url = data.nativeEvent.url; From cfc712189c219191cad6c35c78522b87404062b7 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 25 Mar 2025 21:03:44 +0100 Subject: [PATCH 1044/1144] =?UTF-8?q?fix(Grades):=20remplacement=20de=20la?= =?UTF-8?q?=20r=C3=A9f=C3=A9rence=20par=20un=20=C3=A9tat=20pour=20les=20no?= =?UTF-8?q?tes=20les=20plus=20r=C3=A9centes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Grades/Grades.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/views/account/Grades/Grades.tsx b/src/views/account/Grades/Grades.tsx index c96123565..1a73ea738 100644 --- a/src/views/account/Grades/Grades.tsx +++ b/src/views/account/Grades/Grades.tsx @@ -17,7 +17,7 @@ import { animPapillon } from "@/utils/ui/animations"; import { useTheme } from "@react-navigation/native"; import { ChevronDown } from "lucide-react-native"; import React from "react"; -import { lazy, Suspense, useEffect, useMemo, useRef, useState } from "react"; +import { lazy, Suspense, useEffect, useMemo, useState } from "react"; import { Platform, RefreshControl, @@ -66,7 +66,7 @@ const Grades: Screen<"Grades"> = ({ route, navigation }) => { const [gradesPerSubject, setGradesPerSubject] = useState( [] ); - const latestGradesRef = useRef([]); + const [latestGradesData, setLatestGradesData] = useState([]); const [isRefreshing, setIsRefreshing] = useState(false); const [isLoading, setIsLoading] = useState(true); @@ -145,7 +145,7 @@ const Grades: Screen<"Grades"> = ({ route, navigation }) => { .sort((a, b) => b.timestamp - a.timestamp) .slice(0, 10); - latestGradesRef.current = latestGrades; + setLatestGradesData(latestGrades); }, [selectedPeriod, grades]); return ( @@ -279,9 +279,9 @@ const Grades: Screen<"Grades"> = ({ route, navigation }) => { )} - {latestGradesRef.current.length > 2 && ( + {latestGradesData.length > 2 && ( From aa3641633e4b9085b5242c70fe29ffb366c18290 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 25 Mar 2025 21:06:23 +0100 Subject: [PATCH 1045/1144] =?UTF-8?q?fix(BackgroundIUTLannion):=20renommer?= =?UTF-8?q?=20l'onglet=20"Week"=20en=20"Lessons"=20pour=20une=20meilleure?= =?UTF-8?q?=20clart=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../login/IdentityProvider/actions/BackgroundIUTLannion.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx index a2568e447..a5d849ab0 100644 --- a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx +++ b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx @@ -210,7 +210,7 @@ const BackgroundIUTLannion: Screen<"BackgroundIUTLannion"> = ({ route, navigatio personalization: await defaultPersonalization({ tabs: [ { name: "Home", enabled: true }, - { name: "Week", enabled: true }, + { name: "Lessons", enabled: true }, { name: "Grades", enabled: true }, { name: "Attendance", enabled: true }, { name: "Menu", enabled: true } From 1557a436bd688ac487b32f6f5d26f67d910e81db Mon Sep 17 00:00:00 2001 From: Mikkel ALMONTE--RINGAUD Date: Wed, 26 Mar 2025 20:35:00 +0100 Subject: [PATCH 1046/1144] fix(ecoledirecte): bump pawdirecte --- package-lock.json | 9 +++++---- package.json | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8cb9d82ae..20f18c66d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -77,7 +77,7 @@ "notifee": "^0.0.1", "npm": "^11.2.0", "openid-client": "^5.7.0", - "pawdirecte": "^1.9.0", + "pawdirecte": "^1.9.2", "pawnilim": "^0.2.0", "pawnote": "^1.4.1", "pawrd": "^0.6.1", @@ -12765,6 +12765,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, "license": "MIT" }, "node_modules/json-schema-deref-sync": { @@ -17550,9 +17551,9 @@ } }, "node_modules/pawdirecte": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/pawdirecte/-/pawdirecte-1.9.0.tgz", - "integrity": "sha512-mqxn2pRdNsAHn0cyg5v+/oBaOwzxGdh5sGhvSNEyOepiQAwwxptrhbJypfXfe6hTc21tBSKkJjSQthd1b7Otng==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/pawdirecte/-/pawdirecte-1.9.2.tgz", + "integrity": "sha512-uJstRiZoiIfbYdFgYh0xpWl/kwguiWa2QtYa5axT3f9AUpZ+6gJslGYTq2DboXtG2d5wfOG2dxHuhlKxKQvhGg==", "license": "GPL-3.0-or-later", "dependencies": { "@literate.ink/utilities": "1.0.0-11713907881.1", diff --git a/package.json b/package.json index e15504534..4ff16625a 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "notifee": "^0.0.1", "npm": "^11.2.0", "openid-client": "^5.7.0", - "pawdirecte": "^1.9.0", + "pawdirecte": "^1.9.2", "pawnilim": "^0.2.0", "pawnote": "^1.4.1", "pawrd": "^0.6.1", From 27c5c7d7bf2748bc72c11c3c83af9a623f30cd19 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 26 Mar 2025 21:06:38 +0100 Subject: [PATCH 1047/1144] =?UTF-8?q?fix(version):=20mise=20=C3=A0=20jour?= =?UTF-8?q?=20de=20la=20version=20=C3=A0=207.11.0=20dans=20plusieurs=20fic?= =?UTF-8?q?hiers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/bug.yml | 2 +- android/app/build.gradle | 4 ++-- android/app/src/main/AndroidManifest.xml | 4 ++++ ios/Papillon.xcodeproj/project.pbxproj | 4 ++-- ios/Papillon/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 7 files changed, 13 insertions(+), 9 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 4e0f00956..53bb6aff0 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -61,7 +61,7 @@ body: attributes: label: Version utilisée description: Paramètres (de Papillon) -> Version affichée en bas de la page - placeholder: "7.10.6" + placeholder: "7.11.0" validations: required: true diff --git a/android/app/build.gradle b/android/app/build.gradle index ed598785b..f465fe310 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -88,8 +88,8 @@ android { applicationId 'xyz.getpapillon.app' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 71060 - versionName "7.10.6" + versionCode 71100 + versionName "7.11.0" } signingConfigs { debug { diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index f0a4ad569..287444024 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -50,6 +50,10 @@ + + + + diff --git a/ios/Papillon.xcodeproj/project.pbxproj b/ios/Papillon.xcodeproj/project.pbxproj index bbfe9f05e..a36919e50 100644 --- a/ios/Papillon.xcodeproj/project.pbxproj +++ b/ios/Papillon.xcodeproj/project.pbxproj @@ -457,7 +457,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = Papillon; + PRODUCT_NAME = "Papillon"; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -490,7 +490,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = Papillon; + PRODUCT_NAME = "Papillon"; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index 08eb9b57c..27129177a 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 7.10.6 + 7.11.0 CFBundleSignature ???? CFBundleURLTypes diff --git a/package-lock.json b/package-lock.json index 20f18c66d..8ffa0d077 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.10.6", + "version": "7.11.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.10.6", + "version": "7.11.0", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.4", "@candlefinance/app-icon": "^0.4.5", diff --git a/package.json b/package.json index 4ff16625a..2c306fbfa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "papillonvex", - "version": "7.10.6", + "version": "7.11.0", "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", From 369c385fe84ad0b37fd660c8caaa81fb3e9c3085 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Wed, 26 Mar 2025 21:08:25 +0100 Subject: [PATCH 1048/1144] fix(BackgroundTasks): filtrer les comptes externes avant le traitement des notifications --- src/background/BackgroundTasks.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 7fa14120a..20fb48718 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -83,7 +83,9 @@ const backgroundFetch = async () => { const accounts = getAccounts(); const switchTo = getSwitchToFunction(); - for (const account of accounts) { + const primaryAccounts = accounts.filter((account) => !account.isExternal); + + for (const account of primaryAccounts) { await switchTo(account); const notificationsTypesPermissions = account.personalization.notifications; From 5649c9f7607d06e73a62b8908d954191cba93b5a Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 26 Mar 2025 22:16:33 +0100 Subject: [PATCH 1049/1144] =?UTF-8?q?fix(NativeComponents):=20v=C3=A9rifie?= =?UTF-8?q?r=20la=20validit=C3=A9=20de=20l'ic=C3=B4ne=20avant=20de=20la=20?= =?UTF-8?q?rendre?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Global/NativeComponents.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Global/NativeComponents.tsx b/src/components/Global/NativeComponents.tsx index efc8ae03f..04098b009 100644 --- a/src/components/Global/NativeComponents.tsx +++ b/src/components/Global/NativeComponents.tsx @@ -259,7 +259,7 @@ export const NativeItem: React.FC = ({ {leading && !icon && leading} - {icon && ( + {icon && React.isValidElement(icon) && ( React.cloneElement(icon as React.ReactElement, { size: icon.props.size || 24, color: icon.props.color || colors.text, From 38eba78f0c536fd01332e3f99f45865a6d9f1d25 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 26 Mar 2025 22:17:01 +0100 Subject: [PATCH 1050/1144] =?UTF-8?q?fix(router):=20ajouter=20un=20comment?= =?UTF-8?q?aire=20pour=20ignorer=20l'erreur=20TypeScript=20sur=20l'index?= =?UTF-8?q?=20de=20d=C3=A9tention=20initial?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/screens/views/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/router/screens/views/index.ts b/src/router/screens/views/index.ts index 3ff29b6ca..d187152f9 100644 --- a/src/router/screens/views/index.ts +++ b/src/router/screens/views/index.ts @@ -91,6 +91,7 @@ export default [ sheetCornerRadius: 16, sheetAllowedDetents: [0.5, 1], sheetGrabberVisible: true, + // @ts-expect-error sheetInitialDetentIndex: 0, }), createScreen("GradeSubject", GradeSubjectScreen, { From 796b4b75516fd82dbecb99db9d67362881bd2484 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 26 Mar 2025 22:17:37 +0100 Subject: [PATCH 1051/1144] fix(Grades): importer le type Grade dans Grades.tsx --- src/views/account/Grades/Grades.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Grades/Grades.tsx b/src/views/account/Grades/Grades.tsx index 1a73ea738..34b1e4e12 100644 --- a/src/views/account/Grades/Grades.tsx +++ b/src/views/account/Grades/Grades.tsx @@ -9,7 +9,7 @@ import { updateGradesAndAveragesInCache, updateGradesPeriodsInCache, } from "@/services/grades"; -import type { GradesPerSubject } from "@/services/shared/Grade"; +import type { Grade, GradesPerSubject } from "@/services/shared/Grade"; import { useCurrentAccount } from "@/stores/account"; import { AccountService } from "@/stores/account/types"; import { useGradesStore } from "@/stores/grades"; From 703f41c784adf4bcf72dd82ef7982c3f5f126e40 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 26 Mar 2025 22:22:35 +0100 Subject: [PATCH 1052/1144] =?UTF-8?q?fix(AddHomework):=20mettre=20=C3=A0?= =?UTF-8?q?=20jour=20le=20type=20de=20currentHw=20pour=20accepter=20Homewo?= =?UTF-8?q?rk=20ou=20null?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/AddHomework.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/views/account/Homeworks/AddHomework.tsx b/src/views/account/Homeworks/AddHomework.tsx index db68bb5d7..9fcfb164f 100644 --- a/src/views/account/Homeworks/AddHomework.tsx +++ b/src/views/account/Homeworks/AddHomework.tsx @@ -14,6 +14,7 @@ import DateTimePicker from "@react-native-community/datetimepicker"; import { BookOpen, Calendar } from "lucide-react-native"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; +import { Homework } from "@/services/shared/Homework"; const AddHomeworkScreen: Screen<"AddHomework"> = ({ route, navigation }) => { @@ -26,7 +27,7 @@ const AddHomeworkScreen: Screen<"AddHomework"> = ({ route, navigation }) => { ); // Création de devoirs personnalisés - const [currentHw, setCurrentHw] = useState(""); + const [currentHw, setCurrentHw] = useState(null); const [loading, setLoading] = useState(false); const [showDatePicker, setShowDatePicker] = useState(false); const [idHomework, setIdHomework] = useState(NaN); @@ -35,10 +36,10 @@ const AddHomeworkScreen: Screen<"AddHomework"> = ({ route, navigation }) => { useEffect(() => { - if(route.params?.hwid) { + if (route.params?.hwid) { const allHomeworks = Object.values(homeworks).flat(); const homework = allHomeworks.find(hw => hw.id === route.params?.hwid); - if(homework) { + if (homework) { setSelectedPretty(localSubjects[homework.subject]); setIdHomework(Number(homework.id)); setContentHomework(homework.content); @@ -87,6 +88,8 @@ const AddHomeworkScreen: Screen<"AddHomework"> = ({ route, navigation }) => { }; const updateHomework = async () => { + if (!currentHw) return; + const newHomework: Homework = { ...currentHw, subject: selectedPretty.pretty, @@ -258,7 +261,7 @@ const AddHomeworkScreen: Screen<"AddHomework"> = ({ route, navigation }) => { { - if(currentHw) { + if (currentHw) { updateHomework(); } else { createHomework(); From 3d357cd8fd8ad75f543c6b3e12ef2503197593dd Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 26 Mar 2025 22:23:05 +0100 Subject: [PATCH 1053/1144] =?UTF-8?q?fix(Homeworks):=20mettre=20=C3=A0=20j?= =?UTF-8?q?our=20la=20navigation=20et=20la=20gestion=20de=20outsideNav=20d?= =?UTF-8?q?ans=20le=20bouton=20AddHomework?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/Homeworks.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 5a929f158..e73a5e1ef 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -716,7 +716,10 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { - navigation.navigate("AddHomework")} outsideNav={route.params?.outsideNav} /> + navigation.navigate("AddHomework", {})} + outsideNav={route.params?.outsideNav ?? true} + /> Date: Wed, 26 Mar 2025 22:25:05 +0100 Subject: [PATCH 1054/1144] =?UTF-8?q?fix(package-lock):=20r=C3=A9trograder?= =?UTF-8?q?=20la=20version=20de=20TypeScript=20=C3=A0=205.5.4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8ffa0d077..587023d85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20946,9 +20946,9 @@ } }, "node_modules/typescript": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", - "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true, "license": "Apache-2.0", "bin": { From 2e16f9afd4be503d0d334d70cbc79aed35bc5dfc Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 26 Mar 2025 22:26:11 +0100 Subject: [PATCH 1055/1144] =?UTF-8?q?fix(package-lock):=20mettre=20=C3=A0?= =?UTF-8?q?=20jour=20la=20version=20de=20send=20=C3=A0=200.19.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 587023d85..0f2015f9d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2565,7 +2565,7 @@ "resolve-from": "^5.0.0", "resolve.exports": "^2.0.2", "semver": "^7.6.0", - "send": "^0.18.0", + "send": "^0.19.0", "slugify": "^1.3.4", "source-map-support": "~0.5.21", "stacktrace-parser": "^0.1.10", @@ -19336,8 +19336,8 @@ } }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "license": "MIT", "dependencies": { From 40f0c00e73eda306fd7362b649e0b5899423d30d Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 26 Mar 2025 22:32:00 +0100 Subject: [PATCH 1056/1144] =?UTF-8?q?fix(package-lock):=20mettre=20=C3=A0?= =?UTF-8?q?=20jour=20l'int=C3=A9grit=C3=A9=20de=20la=20version=20de=20send?= =?UTF-8?q?=20=C3=A0=200.19.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 0f2015f9d..bcaa6babd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19338,7 +19338,7 @@ "node_modules/send": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "license": "MIT", "dependencies": { "debug": "2.6.9", From 2249af15db3627765b1c4f0c47636221e7136a0c Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Fri, 28 Mar 2025 18:57:10 +0100 Subject: [PATCH 1057/1144] =?UTF-8?q?Revert=20"fix:=20Utiliser=20les=20heu?= =?UTF-8?q?res=20UTC=20pour=20la=20gestion=20des=20le=C3=A7ons=20et=20des?= =?UTF-8?q?=20devoirs"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit c41a60b983de4253da9a9fb9cdfd571f5a2bf6dd. --- src/background/data/Lessons.ts | 4 ++-- src/background/utils/homeworks.ts | 4 ++-- src/components/Global/InfiniteDatePager.tsx | 2 +- src/utils/format/DateHelper.ts | 2 +- .../account/Home/Elements/HomeworksElement.tsx | 2 +- src/views/account/Lessons/Lessons.tsx | 14 +++++++------- src/views/account/Lessons/LessonsHeader.tsx | 2 +- src/views/account/Restaurant/Menu.tsx | 4 ++-- src/views/account/Week/Week.tsx | 2 +- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index dadb239dd..80747ca51 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -11,13 +11,13 @@ import { formatHoursMinutes } from "../utils/format"; const getAllLessonsForDay = (lessons: Record) => { const date = new Date(); - date.setUTCHours(1, 0, 0, 0); + date.setUTCHours(0, 0, 0, 0); const week = dateToEpochWeekNumber(date); const timetable = lessons[week] || []; const lessonsOfDay = timetable.filter((lesson) => { const lessonDate = new Date(lesson.startTimestamp); - lessonDate.setUTCHours(1, 0, 0, 0); + lessonDate.setUTCHours(0, 0, 0, 0); return lessonDate.getTime() === date.getTime(); }); diff --git a/src/background/utils/homeworks.ts b/src/background/utils/homeworks.ts index e5290aa6a..f039436b6 100644 --- a/src/background/utils/homeworks.ts +++ b/src/background/utils/homeworks.ts @@ -5,9 +5,9 @@ import { epochWNToDate } from "@/utils/epochWeekNumber"; const getCurrentWeekNumber = () => { const now = new Date(); - now.setUTCHours(1, 0, 0, 0); + now.setUTCHours(0, 0, 0, 0); const start = new Date(1970, 0, 0); - start.setUTCHours(1, 0, 0, 0); + start.setUTCHours(0, 0, 0, 0); const diff = now.getTime() - start.getTime(); const oneWeek = 1000 * 60 * 60 * 24 * 7; return Math.floor(diff / oneWeek); diff --git a/src/components/Global/InfiniteDatePager.tsx b/src/components/Global/InfiniteDatePager.tsx index d6abfaff0..3c024eb02 100644 --- a/src/components/Global/InfiniteDatePager.tsx +++ b/src/components/Global/InfiniteDatePager.tsx @@ -14,7 +14,7 @@ interface InfiniteDatePagerProps { const InfiniteDatePager = ({ renderDate, initialDate = new Date(), onDateChange }: InfiniteDatePagerProps) => { const pagerRef = useRef(null); const baseDate = useRef(new Date()).current; - baseDate.setUTCHours(1, 0, 0, 0); + baseDate.setUTCHours(0, 0, 0, 0); const lastChangeTime = useRef(0); const getDateFromIndex = useCallback((index: number) => { diff --git a/src/utils/format/DateHelper.ts b/src/utils/format/DateHelper.ts index 7e7807304..d85b84960 100644 --- a/src/utils/format/DateHelper.ts +++ b/src/utils/format/DateHelper.ts @@ -15,7 +15,7 @@ export const timestampToString = (timestamp: number) => { } const mtn = new Date(); - mtn.setUTCHours(1, 0, 0, 0); + mtn.setUTCHours(0, 0, 0, 0); return formatDistance(new Date(timestamp), mtn, { locale: fr, diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 7c45190b1..5ce2cd0fb 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -83,7 +83,7 @@ const HomeworksElement: React.FC = ({ navigation, onImpor ); const mtn = new Date(); - mtn.setUTCHours(1, 0, 0, 0); + mtn.setUTCHours(0, 0, 0, 0); const startTime = mtn.getTime() / 1000; const endTime = startTime + 7 * 24 * 60 * 60 * 1000; diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index 88989fb04..7e5e3281c 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -69,7 +69,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { }, [timetables]); const today = new Date(); - today.setUTCHours(1, 0, 0, 0); + today.setUTCHours(0, 0, 0, 0); const [pickerDate, setPickerDate] = useState(new Date(today)); @@ -139,11 +139,11 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const timetable = timetables[week] || []; const newDate = new Date(date); - newDate.setUTCHours(1, 0, 0, 0); + newDate.setUTCHours(0, 0, 0, 0); const day = timetable.filter((lesson) => { const lessonDate = new Date(lesson.startTimestamp); - lessonDate.setUTCHours(1, 0, 0, 0); + lessonDate.setUTCHours(0, 0, 0, 0); return lessonDate.getTime() === newDate.getTime(); }); @@ -157,7 +157,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { if (flatListRef.current) { const normalizeDate = (date: Date) => { const newDate = new Date(date); - newDate.setUTCHours(1, 0, 0, 0); + newDate.setUTCHours(0, 0, 0, 0); return newDate; }; @@ -179,7 +179,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { return Array.from({ length: 100 }, (_, i) => { const date = new Date(today); date.setUTCDate(today.getUTCDate() - 50 + i); - date.setUTCHours(1, 0, 0, 0); + date.setUTCHours(0, 0, 0, 0); return date; }); }); @@ -274,7 +274,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const onDateSelect = (date: Date | undefined) => { const newDate = new Date(date || 0); - newDate.setUTCHours(1, 0, 0, 0); + newDate.setUTCHours(0, 0, 0, 0); setPickerDate(newDate); const firstDate = data[0]; @@ -321,7 +321,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { onPress={() => setShowDatePicker(true)} onLongPress={() => { const today = new Date(); - today.setUTCHours(1, 0, 0, 0); + today.setUTCHours(0, 0, 0, 0); onDateSelect(today); }} > diff --git a/src/views/account/Lessons/LessonsHeader.tsx b/src/views/account/Lessons/LessonsHeader.tsx index 05e85aaf7..aae4772e7 100644 --- a/src/views/account/Lessons/LessonsHeader.tsx +++ b/src/views/account/Lessons/LessonsHeader.tsx @@ -204,7 +204,7 @@ const LessonsDateModal: React.FC = ({ onChange={(_event, selectedDate) => { const newSelectedDate = selectedDate || currentDate; // set hours to 0 - newSelectedDate.setUTCHours(1, 0, 0, 0); + newSelectedDate.setUTCHours(0, 0, 0, 0); onDateSelect(newSelectedDate); }} /> diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index c62d1a5c5..4789d4d85 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -73,7 +73,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { const [currentMenu, setCurrentMenu] = useState(null); const [currentWeek, setCurrentWeek] = useState(0); const [showDatePicker, setShowDatePicker] = useState(false); - const [pickerDate, setPickerDate] = React.useState(new Date(new Date().setUTCHours(1, 0, 0, 0))); + const [pickerDate, setPickerDate] = React.useState(new Date(new Date().setUTCHours(0, 0, 0, 0))); const [isMenuLoading, setMenuLoading] = useState(false); const [isInitialised, setIsInitialised] = useState(false); const [selectedIndex, setSelectedIndex] = useState(0); @@ -99,7 +99,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { const newDate = new Date(date); - newDate.setUTCHours(1, 0, 0, 0); + newDate.setUTCHours(0, 0, 0, 0); if (newDate.valueOf() === pickerDate.valueOf()) { return; diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index fa84afecd..778f7a925 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -131,7 +131,7 @@ const HeaderItem = memo(({ header }) => { const start = header.startUnix; const today = new Date(); - today.setUTCHours(1, 0, 0, 0); + today.setUTCHours(0, 0, 0, 0); const todayStamp = today.getTime(); From e421e0b5834d9154b24976e6fb0aa9d150cbb244 Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Sat, 29 Mar 2025 11:45:42 +0100 Subject: [PATCH 1058/1144] fix(menu): simplify menu day check and adjust date comparison logic --- src/services/pronote/menu.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/services/pronote/menu.ts b/src/services/pronote/menu.ts index 060230ebb..9428df868 100644 --- a/src/services/pronote/menu.ts +++ b/src/services/pronote/menu.ts @@ -8,12 +8,20 @@ export const getMenu = async (account: PronoteAccount, date: Date): Promise

Date: Sat, 29 Mar 2025 23:06:07 +0100 Subject: [PATCH 1059/1144] =?UTF-8?q?fix(Lessons):=20utiliser=20Date.UTC?= =?UTF-8?q?=20pour=20la=20comparaison=20des=20dates=20des=20le=C3=A7ons?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Lessons/Lessons.tsx | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index 7e5e3281c..ed29e00a7 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -138,14 +138,21 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const week = getWeekFromDate(date); const timetable = timetables[week] || []; - const newDate = new Date(date); - newDate.setUTCHours(0, 0, 0, 0); + const newDate = Date.UTC( + date.getFullYear(), + date.getMonth(), + date.getDate(), + ); const day = timetable.filter((lesson) => { - const lessonDate = new Date(lesson.startTimestamp); - lessonDate.setUTCHours(0, 0, 0, 0); + const startTimetableDate = new Date(lesson.startTimestamp); + const lessonDate = Date.UTC( + startTimetableDate.getFullYear(), + startTimetableDate.getMonth(), + startTimetableDate.getDate(), + ); - return lessonDate.getTime() === newDate.getTime(); + return lessonDate === newDate; }); return day; From 7d0f63a6daae43258d81b0b1127cd12b76f8faab Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 29 Mar 2025 23:10:50 +0100 Subject: [PATCH 1060/1144] fix(Timetable): remplacer les fonctions isToday et isTomorrow par des imports de date-fns --- .../Home/Elements/TimetableElement.tsx | 25 +++---------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/src/views/account/Home/Elements/TimetableElement.tsx b/src/views/account/Home/Elements/TimetableElement.tsx index 5a87e9f05..3bc4d7c3e 100644 --- a/src/views/account/Home/Elements/TimetableElement.tsx +++ b/src/views/account/Home/Elements/TimetableElement.tsx @@ -12,6 +12,7 @@ import MissingItem from "@/components/Global/MissingItem"; import { TimetableItem } from "../../Lessons/Atoms/Item"; import { getHolidayEmoji } from "@/utils/format/holidayEmoji"; import PapillonLoading from "@/components/Global/PapillonLoading"; +import { isToday, isTomorrow } from "date-fns"; interface TimetableElementProps { onImportance: (value: number) => unknown; @@ -39,27 +40,6 @@ const TimetableElement: React.FC = ({ onImportance }) => } }; - const isToday = (timestamp: number) => { - const today = new Date(); - const date = new Date(timestamp); - return ( - date.getUTCDate() === today.getUTCDate() && - date.getUTCMonth() === today.getUTCMonth() && - date.getFullYear() === today.getFullYear() - ); - }; - - const isTomorrow = (timestamp: number) => { - const tomorrow = new Date(); - tomorrow.setUTCDate(tomorrow.getUTCDate() + 1); - const date = new Date(timestamp); - return ( - date.getUTCDate() === tomorrow.getUTCDate() && - date.getUTCMonth() === tomorrow.getUTCMonth() && - date.getFullYear() === tomorrow.getFullYear() - ); - }; - const isWeekend = (courses: TimetableClass[]) => { const today = new Date().getUTCDay(); return (today === 6 || today === 0) && courses.length === 0; @@ -70,18 +50,21 @@ const TimetableElement: React.FC = ({ onImportance }) => const filterAndSortCourses = (weekCourses: TimetableClass[]): TimetableClass[] => { const now = Date.now(); + const todayCourses = weekCourses .filter((c) => isToday(c.startTimestamp) && c.endTimestamp > now) .sort((a, b) => a.startTimestamp - b.startTimestamp); if (todayCourses.length > 0) { return todayCourses; } + const tomorrowCourses = weekCourses .filter((c) => isTomorrow(c.startTimestamp)) .sort((a, b) => a.startTimestamp - b.startTimestamp); if (tomorrowCourses.length > 0) { return tomorrowCourses.slice(0, 3); } + return weekCourses .filter((c) => c.startTimestamp > now) .sort((a, b) => a.startTimestamp - b.startTimestamp) From aa07261981a5b758e3098314ce36104106ad549d Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 29 Mar 2025 23:13:36 +0100 Subject: [PATCH 1061/1144] fix(Chats): remplacer getUTCDay par getDay pour l'index du jour --- src/services/pronote/chats.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/pronote/chats.ts b/src/services/pronote/chats.ts index fa5af1fb8..8f4e46dd4 100644 --- a/src/services/pronote/chats.ts +++ b/src/services/pronote/chats.ts @@ -27,7 +27,7 @@ export const getChats = async (account: PronoteAccount): Promise> => } const today = new Date(); - const todayIndex = today.getUTCDay(); + const todayIndex = today.getDay(); const dayName = parts[0].toLowerCase(); const targetIndex = days.indexOf(dayName); From aa50cb25c20fdf758e88d42f0e8604e680fd4aa2 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 29 Mar 2025 23:26:20 +0100 Subject: [PATCH 1062/1144] =?UTF-8?q?fix(Lessons):=20utiliser=20Date.UTC?= =?UTF-8?q?=20pour=20filtrer=20les=20le=C3=A7ons=20par=20date?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Lessons.ts | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index 80747ca51..1d49026ea 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -15,14 +15,24 @@ const getAllLessonsForDay = (lessons: Record) => { const week = dateToEpochWeekNumber(date); const timetable = lessons[week] || []; - const lessonsOfDay = timetable.filter((lesson) => { - const lessonDate = new Date(lesson.startTimestamp); - lessonDate.setUTCHours(0, 0, 0, 0); + const newDate = Date.UTC( + date.getFullYear(), + date.getMonth(), + date.getDate(), + ); + + const day = timetable.filter((lesson) => { + const startTimetableDate = new Date(lesson.startTimestamp); + const lessonDate = Date.UTC( + startTimetableDate.getFullYear(), + startTimetableDate.getMonth(), + startTimetableDate.getDate(), + ); - return lessonDate.getTime() === date.getTime(); + return lessonDate === newDate; }); - return lessonsOfDay; + return day; }; const getDifferences = ( From cda0eec3f922188ab81d09fbbd551c5157786dc8 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 29 Mar 2025 23:49:06 +0100 Subject: [PATCH 1063/1144] =?UTF-8?q?fix(AddHomework):=20corriger=20la=20c?= =?UTF-8?q?onversion=20de=20l'ID=20de=20devoir=20et=20cr=C3=A9er=20un=20id?= =?UTF-8?q?=20lors=20de=20la=20cr=C3=A9ation=20d'un=20devoir?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/AddHomework.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/views/account/Homeworks/AddHomework.tsx b/src/views/account/Homeworks/AddHomework.tsx index 9fcfb164f..080e2b759 100644 --- a/src/views/account/Homeworks/AddHomework.tsx +++ b/src/views/account/Homeworks/AddHomework.tsx @@ -41,11 +41,13 @@ const AddHomeworkScreen: Screen<"AddHomework"> = ({ route, navigation }) => { const homework = allHomeworks.find(hw => hw.id === route.params?.hwid); if (homework) { setSelectedPretty(localSubjects[homework.subject]); - setIdHomework(Number(homework.id)); + setIdHomework(parseInt(homework.id)); setContentHomework(homework.content); setDateHomework(homework.due); setCurrentHw(homework); } + } else { + setIdHomework(Math.random() * 1000 + 1); } }, [route.params?.hwid]); From a461992545c5f4b1cd2d85c920a369235b8095ac Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 29 Mar 2025 23:49:12 +0100 Subject: [PATCH 1064/1144] =?UTF-8?q?fix(HomeworksDocument):=20simplifier?= =?UTF-8?q?=20la=20gestion=20des=20devoirs=20en=20supprimant=20les=20?= =?UTF-8?q?=C3=A9tats=20inutiles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/Document.tsx | 25 ++++-------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/src/views/account/Homeworks/Document.tsx b/src/views/account/Homeworks/Document.tsx index a8417534b..0bab8eecd 100644 --- a/src/views/account/Homeworks/Document.tsx +++ b/src/views/account/Homeworks/Document.tsx @@ -1,4 +1,3 @@ - import { NativeItem, NativeList, @@ -15,7 +14,7 @@ import { StyleSheet, Alert, } from "react-native"; -import { Homework, HomeworkReturnType } from "@/services/shared/Homework"; +import { HomeworkReturnType } from "@/services/shared/Homework"; import * as WebBrowser from "expo-web-browser"; import { useTheme } from "@react-navigation/native"; import HTMLView from "react-native-htmlview"; @@ -35,10 +34,6 @@ import { getSubjectData } from "@/services/shared/Subject"; import { useHomeworkStore } from "@/stores/homework"; import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; -const MemoizedNativeItem = React.memo(NativeItem); -const MemoizedNativeList = React.memo(NativeList); -const MemoizedNativeText = React.memo(NativeText); - const HomeworksDocument: Screen<"HomeworksDocument"> = ({ navigation, route }) => { const theme = useTheme(); const stylesText = StyleSheet.create({ @@ -55,20 +50,8 @@ const HomeworksDocument: Screen<"HomeworksDocument"> = ({ navigation, route }) = }); const account = useCurrentAccount((store) => store.account!); - const localSubjects = account.personalization.subjects ?? {}; - const [selectedPretty, setSelectedPretty] = useState( - Object.entries(localSubjects || {})[0]?.[1] ?? null - ); - - // Création de devoirs personnalisés - const [showCreateHomework, setShowCreateHomework] = useState(false); - const [showDatePicker, setShowDatePicker] = useState(false); - const [idHomework, setIdHomework] = useState(NaN); - const [contentHomework, setContentHomework] = useState(null); - const [dateHomework, setDateHomework] = useState(Date.now()); - - const [homework, setHomework] = useState(route.params.homework || {}); + const homework = route.params.homework; const openUrl = (url: string) => { if ( @@ -205,8 +188,8 @@ const HomeworksDocument: Screen<"HomeworksDocument"> = ({ navigation, route }) = useHomeworkStore .getState() .removeHomework( - dateToEpochWeekNumber(new Date(dateHomework)), - homework.id + dateToEpochWeekNumber(new Date(homework.due)), + homework.id, ); navigation.goBack(); } From d8778ac018b73d6d41091d9036eb83696349240d Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 30 Mar 2025 00:02:47 +0100 Subject: [PATCH 1065/1144] =?UTF-8?q?fix(AddHomework):=20corriger=20la=20g?= =?UTF-8?q?estion=20de=20la=20date=20s=C3=A9lectionn=C3=A9e=20pour=20un=20?= =?UTF-8?q?devoir?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/AddHomework.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/views/account/Homeworks/AddHomework.tsx b/src/views/account/Homeworks/AddHomework.tsx index 080e2b759..77a78e445 100644 --- a/src/views/account/Homeworks/AddHomework.tsx +++ b/src/views/account/Homeworks/AddHomework.tsx @@ -219,8 +219,12 @@ const AddHomeworkScreen: Screen<"AddHomework"> = ({ route, navigation }) => { onChange={(_event, selectedDate) => { setShowDatePicker(false); if (selectedDate) { - selectedDate.setHours(0, 0, 0, 0); - setDateHomework(selectedDate.getTime()); + const dateSelected = Date.UTC( + selectedDate.getFullYear(), + selectedDate.getMonth(), + selectedDate.getDate(), + ); + setDateHomework(dateSelected); } }} /> From 0561162e394c503eba544707b4d87fe1c76488e4 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 30 Mar 2025 00:02:50 +0100 Subject: [PATCH 1066/1144] =?UTF-8?q?fix(Homeworks):=20ajuster=20le=20nom?= =?UTF-8?q?=20du=20jour=20pour=20les=20devoirs=20personnalis=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/Homeworks.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index e73a5e1ef..9d3157d40 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -169,7 +169,10 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { ); const groupedHomework = sortedHomework.reduce((acc, curr) => { - const dayName = getDayName(curr.due); + const dayName = getDayName(curr.personalizate + ? curr.due - 86400 + : curr.due + ); const formattedDate = formatDate(curr.due); const day = `${dayName} ${formattedDate}`; From aad9226b47cca35d7bd7b25785e2936bfc8342e9 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 30 Mar 2025 00:06:25 +0100 Subject: [PATCH 1067/1144] =?UTF-8?q?fix(AddHomework):=20ajouter=20un=20ap?= =?UTF-8?q?er=C3=A7u=20des=20devoirs=20dans=20l'=C3=A9cran=20d'ajout?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/AddHomework.tsx | 28 +++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/views/account/Homeworks/AddHomework.tsx b/src/views/account/Homeworks/AddHomework.tsx index 77a78e445..bd8fe4f8c 100644 --- a/src/views/account/Homeworks/AddHomework.tsx +++ b/src/views/account/Homeworks/AddHomework.tsx @@ -15,6 +15,7 @@ import { BookOpen, Calendar } from "lucide-react-native"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; import { Homework } from "@/services/shared/Homework"; +import HomeworkItem from "./Atoms/Item"; const AddHomeworkScreen: Screen<"AddHomework"> = ({ route, navigation }) => { @@ -129,6 +130,33 @@ const AddHomeworkScreen: Screen<"AddHomework"> = ({ route, navigation }) => { paddingHorizontal: 12 }}> + + + Aperçu + + + undefined} + total={1} + /> + + + } trailing={ From 5bdcb02867447ebe0b6bf9080f1f330bba4f2bb0 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sun, 30 Mar 2025 14:51:47 +0200 Subject: [PATCH 1068/1144] =?UTF-8?q?feat(Header):=20ajout=20d'un=20nouvel?= =?UTF-8?q?=20=C3=A9cran=20pour=20personnaliser=20l'en-t=C3=AAte=20et=20am?= =?UTF-8?q?=C3=A9lioration=20des=20styles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/headers/ailes.png | Bin 0 -> 15445 bytes assets/headers/boxes.png | Bin 0 -> 21128 bytes assets/headers/hlr.png | Bin 0 -> 21644 bytes assets/headers/spark.png | Bin 0 -> 20870 bytes assets/headers/stars.png | Bin 0 -> 11936 bytes assets/headers/texture.png | Bin 0 -> 22876 bytes assets/headers/tictactoe.png | Bin 0 -> 55103 bytes assets/headers/topography.png | Bin 0 -> 58758 bytes assets/headers/v7.png | Bin 0 -> 11774 bytes src/components/Home/AccountSwitcher.tsx | 33 +- src/components/Home/Header.tsx | 14 +- src/router/helpers/types.ts | 2 + src/router/screens/views/index.ts | 12 + src/services/shared/Subject.ts | 2 +- src/stores/account/types.ts | 67 ++-- src/views/account/Home/Home.tsx | 127 ++++++- .../account/Home/Modal/CustomizeHeader.tsx | 356 ++++++++++++++++++ 17 files changed, 557 insertions(+), 56 deletions(-) create mode 100644 assets/headers/ailes.png create mode 100644 assets/headers/boxes.png create mode 100644 assets/headers/hlr.png create mode 100644 assets/headers/spark.png create mode 100644 assets/headers/stars.png create mode 100644 assets/headers/texture.png create mode 100644 assets/headers/tictactoe.png create mode 100644 assets/headers/topography.png create mode 100644 assets/headers/v7.png create mode 100644 src/views/account/Home/Modal/CustomizeHeader.tsx diff --git a/assets/headers/ailes.png b/assets/headers/ailes.png new file mode 100644 index 0000000000000000000000000000000000000000..743ac84851fdfd685c8709f6575fc191a254f0f9 GIT binary patch literal 15445 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

8rGoR{{B@q^A#xF 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 literal 0 HcmV?d00001 diff --git a/assets/headers/spark.png b/assets/headers/spark.png new file mode 100644 index 0000000000000000000000000000000000000000..efb4fc49db89741a89c0d02c7b4cb44f5b17fa8e GIT binary patch literal 20870 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 literal 0 HcmV?d00001 diff --git a/assets/headers/stars.png b/assets/headers/stars.png new file mode 100644 index 0000000000000000000000000000000000000000..b94aebb47646df916b7053760dcb8ccc6bfbb33c GIT binary patch literal 11936 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 literal 0 HcmV?d00001 diff --git a/assets/headers/tictactoe.png b/assets/headers/tictactoe.png new file mode 100644 index 0000000000000000000000000000000000000000..6101caa32e2baf28f0c3e62f1dab2eb5c561ed69 GIT binary patch literal 55103 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

jrRVkAh47n|hr7pRq^`P-kiy@8 ziX1FkmwpEbzcqjvj4$#>fVxUWxqH}@ohtWx-c06P{VbaExm-7{G`jm5)5vqQV{T;4 zA9+W|0>NgpTiZGvr29O|_Wgtikxnfw(o&wx=pI3=&^un-h6dEXgO7;!x3rg@Km{*L zlVtDLI7RbSmz!lA$%fFG*YW*b>mhiTmTFXAmi1o|Yg}>WRS?c8b7ELKtc~n??b>{O zAPut;YQT4AY*a-(vVC*FyjzZ`SEMp;-gs!Sr2a%wiXVLVyOa`5!e4CDy%lTIiH9k^ z-s#kmN@1^S!gc&$P_IPK)v@6=lkdlmxwmN5!Uk>Boke-5q?q9{7A-Of)RWD*pPuyI zGeX!KSEJL?96G1$28Js)r42EtWA(lFcB(Oks$Aqsy>D7#)m)2k^$2Ln0K4;E&$9 z{o=>YH?zY2M6;KjDrY3I92-9R=UCpcK$+#pKL31m&b_w9c=<3aP0^1emFk16toO3H zdMP4!ecB_k1?twd2Gc)A%#y;wb$YdF>aqqvdz&O!TjOMcoW)qblU%RwWFn6bWl){Q+k_UJ7}?Hl z%ge|2Ftkv)W>n0k`l({Phz!?`QlZ7+wkC!B1Ee#oW^{7i1NQZNk)+h?QYPu>i<$Cz z6-W3#d0dvm%&xQAxg^Ac6B?|0t-u@JXL@t}H8c2=KC6;k$42!UVXJTRS4v|idUgxc zjnYPlX6Utln*vXNHKePihqy`!$xt%p%B@ zhWwxE%O)xk_h9TfEC{P5g#s(}wN#%ZkA5pSIluRDROauBm0>G9a+*SZBxgFfpS+Ps z{drw4d7F~y-Z>Y?&t}kr!&I%u1!pF+{j2-rWcz~|kG{mcClJDv5yNgY`$v2>tN59b zMYXhymi1#5xBdpbM{x~NsUgb?)})Egd3zwz#mk#{;myyoiIaz&ctArlA2c$NYW_Iw z#sLsmO-(_F!J~ogpd_SM=+wqPTS(rTS5ZcIH9eF5*C zM^sZ9msxFR+NB&DFX8TWWaXw1vb8B39XCncrhSu|{nwcbYVu^ct~qrnZAf~jbjVEV zMV{;Ik0O;is}UY+6%00XOjom-vKsp z0O;Ys@r+h=kXhGql8du%_)T2l)R zdEo%dG0NOzOt5H9lB;&WSR}24C%%;s1hqi^Z|o+epem^~!<^$uG92xN za&GW;@su8Kcq4x6)88%G`NlPOUu3xJvUXk9W;8oZEPvu5w1IoS+Q*XkSNKw5x>fXN zACpT|L%n0uhCgsrhaL(g@@(V<*MEjjq+YQ9*dh+m^k?71b`#lPT}u6O*XFJJddmS5 ze>&YpltveROS~8m7p$wJo%yNp)T=w(tYcq;L~lX%I+IPmr6^8KL37YSr@<2r%22Z* zK$M0LGX>P&WIMNtXoxH{g3aR#h*XBt(i!Jv==FTr$GM!fG1Ok*4&D$$9p`y=#B!v6 zQ-RtB6*;x9I_fA1j6SF`UVpmUVs1HKS;DAJ7&51?445@bVO%HSpKnk8dR=fvH5#ZC z(4SmBAyR8x93Sp({h1XN4|1kIjz)0fFc`sP#h^SUS^jIM>*AXe>dI$+EtK# z${w;WrG}ZL75#H2l$)&|%51FoI4d(>Wivn;T2^_GJ~$DPoRwDA@EZoBTBKMCr+v3+ zRUW}e_li!AvRprLJ)A$Jxw3kwxPK8`p1$fIJig17>AzTn82|0%PS!i&B?;xC`_c&m zy?2hVi7%p*a(N2jz-^89`d7Ae<)k~l+2nG=e{lY?)p-xS`Rp+X(dSO}szO)~(;kT0 z#V!Msh$fagi=A2Vj3}yq=2V1vVP{dNi6Ersi%#cY8TO-{pc2@722S1Veu$ zQ?Xx{-L@Dx?KJFi0kN{sVMBxjVs09l+Q%Ktc-J*-jV#$Os8coG_ZUvBxw6}QmQDA( zev0}2WsRC_CMsl^)ULDDAI7mB<2UMo$jf1|>iMB*9HjDJxgxkH#ZAGULRnC zLmPdq+KX_3vj_Xp!E<(Jd=fI8MCYO8YBoj&1S><<33F3>&!9a(R1*RCPQ1h~VFV-n z9OZ@0TVDhnX7(XXhA|`faxRMRUHZPYrXyesz=>YPy298zf~s~Q|2LRi88hL2RU`4&JA~ zelhFL0FpgA?YZb=X<#s`mv)DPYrV04qfK9HLvU4{v`=d7%y!1EHpLZ^Z$kAqWwR#} z2c@o#eUg3C0Wf!ypECwG^h6o`dNM|QnbMpZCt(PX?#Sp3XkT>(_)n&v5Y>2^pyhwl zSGwa1EUrA4x5*&=XJKlu{bfq=E4L|?O53;a{UM5CQP?l(T!kKMsWM(857t>D1rwX;MUO!5Na-D&&jkdd9QxPdSx}@1 z<`ZjRbN!81bOd+PyN%}Yfj&r(+$@@{|v~Da4S*--^&`>7|@IP={vv?=D)&X zPje^pw10+8CEhC(Mv$}H2$BMEjn$9@(eLR2QV2!!e zLczk7Q#rQ$jV*=o^2eBQ*FJ?NdSNDh^3>9PFcnZzJLdIh%OYwQbN2weBNvma8f_MN zoMG`*+#so_kS)GRt`%XwPu-E!+}a=$2ibPo2nOt`EUIdAaD#&h2t{9+pbLsG$zfN7 zgeK-I2w)wMY{B&X6VCjcTBJPV?C9Cba;pG~Ad;P_{RH68+$?sm&ri+@Mz}t6+pu}8 zdz72V0xHXj-^iw5Jq?Ux{qpti%`4{L0>9G7)28_=xh&w3f2yE_cI!|QSXs``V?BD( zqsjsaMPJnOa>JzHEJB#{78__neNc9UrURtEjBF3r;8UXR`zJ;1aE05M#Ss`V@#nrJ zc%UWN`7oyQ;CDpl|10%qtY*Ru+-*xiHUDle;75M?SN|>oRiO+dx2=n15ltikCVrJA z8pJt?XaWsaO2IA>9Xt`$BqV0<^OQqYdlHVt9dahxih(ZsN}Weu5wg2R&&70I=iuwe z+4>2S{ilyLA~8b|OmgIEl#$GQf;*TR0whiQq5a_Qm>bGRuP>~)(o&hOT}I+0mP_HN zLcQnqw`%>+Lp{N7HzTc|(y!IkruadGQP9p%&L#)aw>B6-(kK-;Boju=&?J`?4boJP z`+eCS#YF8ME2Kl>5SA>vGE}>?aHmCe(}L_md)|jAP;;L%Yt0gQ1He_Gw%o5;SX>D; z#O0`UAbK1;8v@~Qv|Tl3&qa@pR9>RbS0448rvR0tOj5pwywS{(!Z!<-o=f_P@SGp` z-|n<66gsgo!lMN315kO%p3-7whicQsvD6PVCH#Ub7_rXSru6JwxLo?tsdYOA;wWx* z{u=e+HfZu9j7aMKr$p2Vq%xin$zuL*epVA5)4eZGBw5ISW<~cHVq>e3_zOC_e{rPU zu18ai*f*Ejloq)-n*B`j1!94@w2DYuO^#T$5JzFVg*^YvyVKojobnjQZ(HC~Ts~NP^ zd5Y5x&X2^tUEru8(TtK5o(a=vI_dJMvgiR6u4#yL92p0~K#K&ir}peS!FTWAz~`j* zC*LDk?5=y7`LMPypX0m)&}l#NaF#@T+?Vs_XAy$C3M)4HbbPx7S!WaG1j4~Y5`^9i z-6j*Q+lyWHqFh!TMF0t3jVkbl!b3ko?bq$56~kX+p?V3?I!h~aCE3*4P%_+idhC06 zB4Jjzu!I(Z0jF0w^Z}eAS9Zy#b<_PVQ>;g0Rxrms<+$kKO>Nd!O`uK_PKejgbKT#=dqib~ znL0E$V_WVKLC1C<3TwF|Td^okEeRbTCE=L_FOXhXk`B}dQ7K?X<>=!9>t?G3BI66SkAlR#{~@hR>gY?^wovQhmrz(2Y3Qv~ezVn(5;v^aja%4WFx&6#pfl@+a)Ky}y#mQnj=j@aSyOzRe z$Nh<|hyqJ%GUP49jqGC9AcYe2#)+D}`otEcCv4<0>3>!g6jUjgN%HJ*LK^t@&J%5M zXo5ShvR$@CgEHN|&u*i_cw)trCfKsarXxH*dr5yoFn=cJv)BsAThzx75Q!4+EQ&T`5S>&;g=U3eN!yO7}}aWK9wwDs1$(0-6C zHS&kRj*@`MFq*8X>^wn8Wga1?ngddJ3Vg>SBJMOhKQU~Y?_;xGY=5*I7vw%T?|@4x z0P1L%8>-mR`Q|WJ0|@mYU}nMcAez+6_@(T2t%$@(U8jfb*?N14CxkL$9iNVA@nl*sGX`^^NvA0S!zf8os9$^L$ zOC+mi9|vnwY#-C(&H_om?EXGie-&Fkxl0A02f`dNsU){}F_RbjmGQbv6qb-8YVjHZ zs(0`1Wz+WoV7b|4avB07^7V8qij@;Ds*|ce#`>@8n}RhtD+}SML2djnIQ^eS3lfKH z3OrCD6H*bp$H53AEgTs0hc|>+VY9d_%TT#6=~de*l83$cVMkIP78ENx&v;fNUH)@9=4r^GPR_u+Q{%HWnwcZ?cCafpqv zH`}l<787v#Afh$s4R!xzJLv6L>muX2RHg9GeHpgkwIwyQ?Cd)Gi&+V!y{4Vq$6)ZZ@8jv^&y+E4E`E`;4anSiz!t(C;YfFa$J|Nd)bpx^ zXx9mVxzeS0arm#l+xn@3V3bGMYaHR!i4u?BQbm14u=}LTeRiqJA^*dW^UfJ42?0$E zjo=>Ri27e9`uGRuH3elg?2K~7v=~}4{IVwq*Ka*n)28Z56^MSBPkqu+BKAHdn7*O_ zi8t2PbLYvkv+5w>&52E2knDwB6IXqY*dGo-OtyA$?oKQ6GiIjak{=3#XO141pN z(e3{*%5afoi{oYWra5@hJx-lhj)Yj|Za9A4&n({AAw`-7^V@SY3USeOfYEh#-Sk!8 z7|IT&bI4XF3o2oLGa{@O)5^0#Bg2M9EC`gXMu2&WpyD&9--Lh-iAlYJUm68DVQRo! zs(S|;LqqXd=W5sR0PhQtM|e~VhFf|S*IRp_NHnE^UM3m&-6Rj+>~=kWbhc20`wPgqv0Xg) zD>MP7B8xivI>w;vNgYe*i8r|tD>{L_e(ifX*bbA;A}Rmi{f6b7I=uZ+ard=uiv$xd zHP%beNq&8V#_&TPZkJAaX|qO1>h0`NEVWP#*6&Sg2JR*mYYBJAxqL{H+oduM9QMlxrU(T{HXpDUX?M2w_OKGtn!@m z{`_?H##0l*MdWl&bdP&a`D0-c*w14UMy+8 zs5SwTnRK1DlYRsu9LIN8W-_T+<>_^9L4L2yb+@jECseZ{Sn^y`M z13j})HuNx9T@h2nBe7;bm$vbO(RE#M7S=dHCRS~nv(p}DcV5KGFqT<^KBI1H%+<&<7V;Kn;*$hN4QxE|>r?~Fz~Bwjm! zHF*Endg9&AjaUB0v!GotntvJ;vbtN#W<=|o)m7cYn-;DSXsa?d7w>ZLF-o;4N15t7 zw7Wc2lGNQEoJ(Giz@SMhwIcJ$7|kdKA)8VHMxORx@}4N2a& zo~4smUd?KVtFlBjVF;KfevM0%wi<({L3);E{I0hRPXnYXI^}%Oo0W#1P&EbP{UcsN zqLz)7E!_!9Po~EL`1h|9%{N?!Q=ct#}!u zWfQiyUSIAe<^DX+L%~@ntd1FAIXroCiIB9=IthBUIr(> zL6!oEI}8})!1=l3PI(@Gjy7fao?m&v`7Ee)9VLbOSHip>YD7nz<;ol_J4((s> z-RbCOnUEIWRK!v)xazNx2D<5}eVRH1ee4Cs_CS?PY=yUSV+Hm){sDTzt9l}v9nw+s zRO4Z~K3GC9hc%LZe`+B<Q0BsZ_yeiE1J$;>I{e7=Z-AE~lK0pEsp zZ0|C?d_DLZ+|*m8z4-8w|Cloy0*Z7-+wUB!{l(l;CiMx6yUBqpmtces>o5~SR^BuY z!U8YEv%tqY{u$WzBL-6CJv@Q=eWqbB3m7uSjqQ%6{^(OUA@+H8^hX*QOx;u2w`)p* zq^Jd?7YGi*%ci^p!)HJ4>Z4iz6kGfgK_uc|9he_o{~kw_HNV^qH8j6G2~{%G*oSN@ z_km2iJ_D$(KIIO>*q+uH^;-5he4fNa>Y$S% z4wqvCULGF)uq?PCSKcTDrv+#z{1v1|K29<6k>P6e&z5l+b=+#lSaO%v&=m+~1MQ}B+_W+a;;*8Eh-caAu%(2fN5a-F3_K=6TR8F-!-(Mx!0fh zUU=^G8kzOQK3rQctZ@Dl8P{=*XW7MoCN3)@BIGI0)VefQQ6|!&FI|d$xY*TwUeZfyU26HLXg#odpSab(8h>4 zs=0r~2sGnp&|Kvc^hDfd5zm)n{MV@CQw=h2SHjp_QOP!HBdDkmpMinzp|RYdsZK45 zPA|LJ+c#F!FzsS~j@la3mocHmnea&2bZd;<@~Ji}IcmQzuHx?*2^BBrUmgH)rTBTL zv67#vZi^ZhIu~cDJ_VIOZ?%DXP@5#I7{nk>gcW2@LrIIJ5H38y?LfXClXVgm3`0bs zs7!*`+ZPHrQHApR-%GfnOb(?XRl>D+Jg|yL0~IoFJ30o%MlubWN(sak;w5dMUIg^Q z8oV6zd+NY9L4=DRDG>t^mC+#50!eMkFZ`fpe~vnc#*n!=5hwT+c19MQ7)EpB%>Fpp zG5*MD*aaZhbZjpLB$@BrjQV1A=O`bd9`W7Fye>v zra&YsMQ}FD5A3D)#K=WKxwx5~A`SA;H~GqhKlSSd6P<{FoR_4CHnjEkv8&o&HgZ{E z(aMa7lGW-c!B72TlU&j6YsS2i9FzJ*-Kw%k;6!~M8U(4f-$+%=7F$E(sd|o09;OgG zvFclnNd(_&l4!R9pVY)sH^B|LjKOJ#NS^q_G3IqsfK4zt;ox<+S`AwHO#k8?=T zrnEt!bdOP(@s5v(!I`XWClV2Lem?%0llI#)+ypE&Yzh%&xy?{|iw(^)K>HX@a_d2As*^Ue|D~z_#C`J~>&lJoSS_f$({I5(U=%1;zn^7o|a%8%OtT@aM@ql;A zES7+H8hGQ<@?Or^vk^2({-cmPr_=T~Ins^rJA%!OH!LfVw(9gmg$AJashLqzn zR@m-UL{^_6#Q+l_?WS1~V9YG3pU>@~>sIrZ9eUYCP& z^w&Q~Me6kUB0~m^5d&nw#5S|e6jjw$Kdau;J6jt&P-=qd5UtJyX63)&3Wcb|Il{1$ zihR{h{Ja1&v>{l`eM-gzBg+VrLAib3Ip9nCx*aXnY2eA6D*!-*CvQ2g~Y6bc|-O1)HEL-CO5Q zs|!!vu%S1XG-Rg1g?`aK=2wJ4QJHjf4a#}RW&NY^CEA%W%q*G}D`wQX(te&TYs4{` zs@nwivr}={i1YXF7Q8vC&zMlnTuhM|8A^5qa(T5Ixl}2Fpt*~TuzExon!{7zHp8x4 z@|~KIUNM7h0MHej=G38gd?xBgUb9MbGCyK?msWipQ{44XG*6bw28$(1=rW(qMm-nV znnmnplmNu1ambRET1%Z&3kkhE&UvInz2@)K8YRj^2Nl}O$fE)gJ_IpZk;49~V7OD~ zgo;cPt1!9VkP$*~tvN3~??uiN-*qr-886|9?;UyXVqd@LVc*i0T;?yaaeP>oU@uiH znv8H~$ZmVx3Es+WqPSQD@WCV&-)pH;%tv?4FrF!+JXTQ|<~3=^ND=Rt;2<2H#LG`c z(un6*OIn5>yC1>EH(t_nwrLNMErgcMu|TGR+w?eeud(!Z>-GeLi~rJ!?9eGY>!>JY z6Iwy@--EQjHL}=;&7(Ame@|+k$;Wf54iEO79$mqWWU8$rqq)bNlr4(b2j-bFp)}R8 z>{Z(VBWIqT7CV=xD zil2H0`e)ZYKB`Hmwp4tA^EYaz_T?N6bH0MC7DTE5J2zV1zoj%sAAzh$YZmL-V6IZy zd&PVC-$7Swuy$A-^kEeaC&tn8alz0^e)Muio>0DaxDxphIAcLc`$%c+g9!!g1lcbj zeei5d?o6sXYC2_sxN+-Apa#+w(Cd#!fHvgCqRO>BF2!D>nAYum2OYvxl$P;tS5CF# zEYSiA3>6gs=3(=$FdIx4b(i8~(Im}toSsRS`EGYl#G+#%X4kQV zQM0``L@Q^mv~rV`bzFPNwGaPK18J#22mqBELVBvUdQ$a?W(_w6 zLM-4x{g3=93b7)!XtL^WMjCv?JW{%f#J;@EA-WPOBLhYfsF^L<@hUY!Vj7DU<^bZs zlWD7*>~wtU$7tZMK-2XQs3=d3$srP+X@@g`e<&9MaWl^(*GCMkVf-0+O8NTX%20#5 z$)_2yt1QFf>YOfqI2payx z(7xojP>K`Y;|p`<&B`Yy+K*nDag-@`>gT6Qi95c4vLR5O-yLq98WiUpV_46Z+P>dT zkX2dZgim274(?j8fE^ld|8=fdmh?LTmFh6IMvew$Sl>?N9yI5{yNDz3R@uy+Lec6S z?Jgfm;+?&(sWeC3AA=ZKw5p;{o$VmLFfQSD`xe<437Xd=RScK zM5Oq&nPUR*Fa;h9GJOXxY}m#%NPNthCXD%*CwJHtaIn_${i~ntH8S(v7AB8i_=z(E z!ps|6ln1wS=TA!`8=vj|RC`XrDHXM`Yws39UL5X#3NuwndHT2kDc1f8l@eN}&lBV3 z4#?pyQh!anK9_}11VU^9;3qWW@Hj~A%51S+ncIT0c%dC@&TIyj^{LVbZ>fsS0SRm! z%o`Th^yXR`QI>wgKOI0^z1${$R$L&#<-b=PG)AKc5003v&z-+bJp&Xr%lq&6=CD4+ zUVij)M+v8Y)Q0>&md?U0%I9n2f^;`aOGt^Zbl1Yt;j)xScSv`4FCc|8TDGtZoJ?)!7%AfK(4M*8HF19*H|8&h%pUpS_w7QP=Y z5}vxrl1SlMeL zVL9zR%|Zsb40x=`ye^AOitZxQ&3-{V-ZSNz-z`{dhGR;K)KWLrmNY8!E>>iV9O>EF zM#$!O&sjr@_t}SCEHrNm;&7eY_2r1l3W<53ba^!g#e$P+(hvOTolmvaOvcXn^}>ut zlA4W{VK!mEeZnnfl~vm(cXyIYTa-FBj<8Tc-%U)**MIuFK5fr+e{!lz6<6^pajxAj%C552e{DKDKBt~gxds(#@u{bFkBH?6YiEd;i|pPp ze5CB2SEXld!)&$i-`9uRpnivUzuMZZcP}J&aC7uB`!RT8R}>@ZytO2Zjr;WW^uzpi z={vaC$|(z(qCEu)fsm>!w-(j<_-TY)D`{n8Q?QF8bQV+zi7~kk+VdhCBMMvM~ zsPz#hv;imX*+nrok_(4B$=Ukl&-T(1T5vHBKAx((+0(~aR}k+^BvFo9%@*pCH*Pl4 z$SGEv#&dBW!*HyCuEW{BeA&9h zYYwzXyqrw*v9M&dfM(YVC2S854q^f}e-tp`j+5OQn6~)y>4<#@rD&pnjvz4jDKv#| z4hugwFTO2F2+ZzbgYEh%oS}*C{9=Xf3td8|=OS_np}pz2fl_tCE!SLqsgVN>0Nl;_ zb-`TwVRg>o4@S2q>nDfXZ6;RUm( z(i&CAt6wb@_xHbudp7;wsexHs(=X5zlsRu7ZH#ay2is-$CIydF@ybw$Vb*!OhND|% ze*LUDVpLmo(0mWKAx}bQ%@}}XLTaX z2)>eMGDJ%!cD@^`Y*W9c= zu>XSJWEjzxGQ3I1a-h7(=ydBo?BynjXjPG!*<8((z9uZwrrnz;L^z4a^W&tPGzG3Q z^0hcd_q3hzODl!HU0e}IHsMm`DG;F>^mJ4DYsn&8cl zmHDVaU|mc~pMM>*%Mbj?t&JBR%~Z9IEzO+sx6513M%5ncFuw4_1IE)6!;$~ju9-40 z&v}_VJXjqIzEhy-Ts` z(5ih!MTI1~i#25BihDdv4qsWrhOjGn5H9|L*|$ZU{+nVjlXu)^rTeve zbTIFKbK{9miid|DrgjVmY=*Y|`3WVmd8~4yl7D!OBK~6ZvHEBoq}d;mdl~tdM&qK} z0b&ZJyDr`?%LDPn9*lcSL}>lMlcq?JT4kV0DCRJn^?A-n=GukJ@!GbB zCPeVmfTEmf)ny0fyBR|l$$$%iPdF`q;kRbQJcJ**iP3<(o6biID~tsT;%#D3*7geA zP`WJL4BYn+Bj4kHywVVPS?!;VoXBN`5SvrSt6B`I1lNh%Q4ZH{N%-0ath&WciSij8 zYODjZoYeMMZolxhn)uH>rQTdJO;8K8!B=kucZwP}D7PggxG!>@iq+Ruay+j8?bGt* zbrP)40&3$+2iZ1sG9%V~LRDJHEhj9#2K4?+#BpOb3d=sm%Q1Fkqqca4C4@vW|@$_iK^|%~l-c+KcLlGpua>$GJ zhz@mJ6eRYrq#|`v7Bvv8C1--*sy1V%kXiiFtVQnYsV(d0DyK{=DHK35UX?#rR|GyA zS>$cSM0_80uxqZogZhPx5hO<;*4FbTj#?e@$w_9A6m{?kYzf4yIU*70CQ)H(*?_-;8nVYKYHP%VN27#V8;pSTN*;vEsH7f-1QIebG{9TD>3>0Jvmobmo zQ*a8)gs3Aj=%aDb&DD8`(UXS(5hHcb4Mar!>pKp1WaRKlLg1@DTdm@Xb5qPcuE-ufmK=w z89$rCt;x}qZ;};vtcS@uF{Lm`{GUo+@nGHEvV3=~*EvyVjwmx?W_7;F$QU?n>LdA_ zqBoR)-gA8w)U`?i`1Cwe`a7q0d2uOk&ux^}Do{zDZv|&a6p`tQk?pMeS7cG2+F%;U zPcp7LX8NwH-{iakY0-foI@PtpI%`Iflt*pK9<5hCUJ_gVzpe2YFCXiex>FOE;CO?i9qeyV}B2)KZZP-hMiVh>3gRJs$Vz5sYI(67IN?b`jOg|W`2OB1XPqq@dmzdl2jYhV$k<~%lUcNbhayFxv1Ui zh+D@y!{baMao>OCNr$X;rsM*PhSIRPpV|yWLKyae-JXAV$AgP8`>g1tK0Ibu7cHlK z?TuOq+j#1+u5R>*NO11B_U)6>e6igX6VWz9;bYOa@n_uS^vTP~n1^?H2l~5MgT}<$ zY#Fa)G8ug|Vp&*4er3jTV^ZeW_alMtVK)oiq7QW}nP3WWvU3u_88Qb|HBuqnXZ*Ma z?~TUK_ATi#XB%Zs2t1gD`h0jhRXXH?(YIIgGf^9 z?3Kg7?0rNmcNC@m^jFL&rbVBuPeP7q&L#E10N{MCJS=Xw9zfH#s0fq{$}x zZ1K>X01zzLI7un#CyB?*n`j6kw2FD^nGYIUGj}`mt>mgYH1og9Sb-E*uw$f&4OoSm zmoBcdbt;?%HQ>KnKTG@R?L-2l*3fA`O2bpGrfO~u``{u0FDB+mNa=#&^Pzx;$V;&w zY>5S@p=&dw9lJOnkGA3LoEiqhFa zpn@p|wcs4JMroR&WH#MfFZHS-sY2Ndyv43k@=sR9q$Pa>mdRB-& z<@rN6aHKG7T-6`lDV2$j;R%uD;N3FBR={!^5(P3VyA21I3DIy%vv7~Yc?e<<`0$$L zudG?+W%|vYM(@WeEk0<8)W?-+?e_%wn zrA!q%^kn#Er2bQ_^ZMkswBpnj@?P+Buw?y^fk2y@t=gz=G-amKHwqGpS0V#_@`*t1 zVnK!AzvJeW#2O}|%E}cb0n;|3$Ts>CCGSur{)KZkDNqwoBt8A91Qm7&1*d&!YG*qo z;pID61BXQ?>QohtahgO0%LAc|@eH3=&ujpAQJA>J8JB@NLm=$OXEM=bM=eJaY%Vb+ zlBRd-G`p@FCwd7G32+(s^sB&n3pegyB3zv*dbc-Nnf%=8D}YrC*{aRN>Ps{KkDvm$ zXgILKpo3(h8sakSp>p`7!h;g@T0VM@4~d2*-9Jm<W!Dw?EFW6bU46v7T&?dvoyr zPkMBZ7{_DH`E#mT>pB6x#H7q$==;rG?jJ0G-q}pn^tPHhKG}Fd)lU@#e4JAXaX3Xj z#wkx!T}3>;&yWBcoX{nJe#SvXp+EO_r$bEq8hAtpJq=vKPax=byFl1&5`;8Q(mSoj z?EI5`)1)^zgEjwd&W&e6RPvLxmY=*g`Cve1OMQFl$w1D=%eY%fYGy4>ODWsVFG8QJ zAoI>@Lp4DH5?`pgZeQkvigYZ!99Oi2Y0paj{)<~6-Yyeq_=Ii;1@oW8o?Pe2dg7-PM^*Pbt4#;5(-`7z%_@}r{Wms`C^hB3dtZYhuBTi_8`CACU-X2x zi-p*UP+MT)9MzmcewkI(WL{@gO2jDzjkRa`J1(=c?PIuSlvr)d+_`yAf7rRv$H^xWi)mob;<{Q!B^D91upas`&&g=*Dfyb8cCGh02 zDak=0Mc<}!Gfb!+R0q8U1nR(2ddJ#3Q`Px8ez6}Ey)+;iOu(GqH+%PII@Gc7b}hgy zcqy~Ekj6+@SDb0;z7xKaDIAWZ>4t9#*w-}jV{ORqz25is-TjuPRS17(P3D=w=$IA@ z0OMx-(lh!!fg~`xQr-B1v;IQz4o~>%U%T-mqK)Bdjz^zkQIO2vfvy{8O8|0_h+PV( zpS!(oPpX#`=@?+#7*>n+Yw&}qEk@BEa`ubs&@_u+pWA27#^T>m^l^$j$C7WlpIF>w zjQxd$i#m{foGw0Azse4*?9p0y_tNdi+^)W$C-gF-A^lzB}9UXhj0_`hOFL<9o*&rve-2`Xzor0c)(5I>PwkN74B60OBxobTn+ zX1LqlthiB7(RPOyObc9mp*r~2-uDJVPdqHtOR*aOn!_v4De{0z(H1dbqD4#OeDTa4 zskiCS%;R~qga6vbhs)n6z=sm_Zgf*MX<^ygUNF)>Bt0X7WwzM`ixAha0~iH0O}wGb zgBuMMWqJ#cs4nb5J<;%`dO>>=ufS>!6$Av|Aa$ii=4be-ES)E2 zA9dXyr)W=Sx;NP!I$D2+8i*Wh?azOIAJJ>f45aqsp4%57nbtZoz1Sqc$Yh_n-7pf) zRtQN<&y085lX$2oF?4nvnLH{TsuK!~7ZQ18CKS;Wi>geJAF1ZX33q)z(I`$-LTVA) zbkJw4-+JFow#|b_3)}Om8qlty>&haa%rcDbRU;(kk5!I2wMGHV$ZH0~YhIG)tTSOf zV>)kiZq|HHwMhP0D=EH{1N8CNBtW7VRbmgZ;_fgs|o%sr}(=$;jYs+VcIrS*p>!x!RowrxBHX@p6Y+P=SF z<-(BHuE=C6KIts>I#0yR2*56MhiTu}`+o2z3z+&LM6*TM63ZP&G+%Kxd~ds=Uvv-H z?}o8f=AQxo8bDMr_3>KMqw^LW=-SMLUetT)Wy!;;69q&7rrY%#-u*>AJl4*uOLzWr zQ8x{ejO~N8YCk7ava=L#Ri%9%Acg8l_@_yw6z8;Xy&bGA1(|=r!GM&y$x?pyKo@r= zk+s)E82LAFR96tOpwK%bVyY~qFR&v@5ilK((io13X75ldy^!pYR|EDL3ETttUCyfg zSWi9O!rQ=w<==dad>t$p_=MUdD5&w`>?()J*)$m8T11{-m40^ke$KcCdxzbuH`%Hb z6F4X^vr{Js!C}AXJL?*@Pfz%MDsLKH@z|=W6P7 z%uR5I{-R(Hyh0&>doVc5Ji0PWp5yh98wlM3HY0zCGl;anPI*29vIn{C#dFS;Sm!K5 z;1!GjgUnzv-nM|37ZDAmrl^pMw>Z8#6^jh_^1e16RNS}8*@~cogw%DvM*G~HJ38;{ zsDFTS=9%}t>W}}}e?|5S86+GjAOGKuMu(aPGtn*aYWbKDJtzjAyiBrk1Ok+g6)`st6S7Cm|XI<-%fnHd&?3IBa5xodqNAokkLQX7_o~9M?2Y#wIAyiN*NA1wu$f zk4GQrv$P=DO0P?7*06=~V;C;CKxl25Hc=tG)CEYgPREWH9KHJ+tH+8T?Pc|sB=AP0 zn`lLk2fx1wuiEe$9m6>YHtcKK`+-iY29ruo-@HBJaZNs#kPvSF- znzV^?xJ8yM)`|zx{3e22MtT250@M766Kc zKO6!=PV`w*N`p1sED`!tm#4&BcbqJhL^^lIr-C(L*SmVkp^8FahFyesV-m_1VUb_| zCZqoWc+jHNP1e{WeO}`BSQwJb+O88fbhiEw>k$S_Hw5^m-aK*i*A(yaxb+1s9__u% zv;6ZVY^U-|7W#4qarBf^YI07lToA~WtM&neieRo(F&I|uJnxh&-hVa$E=-F*@*F0l z!#ppqA@kMBkD%l<+-lp&#L_CFg02MSE(a`W3LF2_8q?3GO$(&yfHgKBH~Bdx(lgo` zTuf|}=NEF1{_v@Z!`sT;IcKyo8HA1oKbJDdl3(lom-Z(x*-hv&ekapve`uRVrv5Jb zfmr^aswMJ=AdnSnqf3ugs5GMNnW1vcAUj;gs@)E{6pRlW-`$yxaDtzfmKdsY@RB^` zynCg{iCPeGDHNfDU!~9pHzq83k zeawj9+uP}Gt1xqP^yyQn*vrORS86ZoPkWr~I7cxr>WP*DZBUc3G3ky2MhBLVd3T(n zUO8tWh?73$L)^dofbYMj`>)|@9BQTd_XR{Zc94C-*x#F}8e^Mpmt70kA;YkH;WuXFU)wT! zmKpAcn9Lw>#qe3i=L47)S>1qjY9v$lj!Iah>-OW5$ci?Fe*zpZ%%jT8A*(x&7Y0F2 zmcLRBKVB0xxpwD(UhezvnB2nn6PC`a~~ zCe-sFXwN-2yctH5ZL#JJ(2U|P4rqG3)r2A3y056djLLgta#S-b# z_~I8=pKf}EqL8WO=H2Mm40T+3+y*5jdTc@g^U9aNnk@=v~XKP*uC+G*VRIec5 z(N;D`mo`U&rapWj_;U8O%tWD+4d!%{++it#Vt!wdMfOevTRwjdrlI^|r*U8^Zn?5A zHHx99ZTTl}V)X9{&b#@^Y7UK}=XM5ip*0x^6nk~Te4%dacy-B4<>c9M;O^;mD?&_#4P0{l78XKJ~=8*IK`Kq$e$mB_i&R2dBRO^Xfx2NLPs+XDQkt z*d*NvZ=R0v_!fxX>F`eEH@r@45VZDx$p+iz{ob_emG{S$IyE5ADCoZpR=+>gB&R~D z4de9I__L<(na49?X%Jt*_8KAbnEcf~d7__e=t1+?&)&*qb4C)xMenk7Psw&;zU*BsL7y$n;D!b#m+lTfoe|J4bu$-a8?*`2B5`G# zHxBspGg%IU0A6YxjcoXKp&Lm`o{Lxb#&B=BqKgz=0bz?eVb>p5x8mx@lBYmw{)X9d z*I6bM>V&*gR-9!WC;<2DbZRJLhW=#(N)jy@qrA_3NV91250*oRXGnX};4R)s(PzBn zMuJoL*$G;yetH3JzQsL7r1E&QlkJ^V=M>J zcuCIav(RI^8NUWI`OU(PYS3rg?HH)2th(jv5ZzEM86Za?RIu3?dWY8pKs0TUg~qVj zMnx62jA%e?clozI7-FnnA=w@f-&?O&4`S{rH(Qb2q8K}VX{wBI%`mLT79E%dFf^Tm z@No8>kVL;V3gMz*zwo!qs!DELie$`0o}Mmw)LF|ASs>w~&ZRAv&WK2s`&Z>sMmmuK zHS2%NLAymv-0n5y5UbJW@5NdPV@igq7=hGuk&n%=4(jvK2{UE+I#N@U;OO(_iUzE& zsM~h`z5$D`YM=E5zu{Z;8^*9ImrHWdBiXdS-sV8L>6xDib_bWjeqPq&qc&5DOWEpG z&w8@Ky*_*_E;Om&Ynf>#<~n|(ViAi1dzc=s6u_pI#n2Xww34nrRh{jwM;21lkEtYj zrdNIu;h#|!^MkP-V6AAfa@ozKYJ9~5xUp|pT3@`*M6mqK{&kKDcQhz0s}r({;v+8# z=XLwpz`_B(%Cvs1BaLHDN1N~FK&OeY!@5C9FHdt4#wUtqg8~O^*yd#oYRSrGuJK+r zx2LeaW^;mBBg1WC!*fnggKbQ0=k^vaulhx+vf2dUr}9`U_N)y30s3(5u>Wb6!ZID+ zp1T)?n*ZbghY@r;$(wn~x;uoBz8X-k;&G1loW%$JR4G7?^70RiYLW+?dYU&{>esK< zw$OpkrVDA5pp#poPo}sX-2A&hf2+R9)Khca>NbCtaVEGO*N_;c;&=n<`T(Ko>!UKY z36>Hh2K?CfcLYx4P<#BW8YQ!nFHw7_0V&ZIe+w6iP zY-E8oiLQEv1~3l;QS&DHgfrYUIgEKSdPqV~WR6$0LizKmeX+gZO4M{VH(#U5R(zm1 z@kTtTy0|dTvA~F+i2RX->WxuKc4A?U;;P5C$8Yj{#3A_sK?2%@FEHNIpZe&&acwaEF!^wkL6l9&lHjp{lci(i5+92 znk&8hc`05pQix&Hy#|Mo++~8n-hZBOyp^e*-olAWdMh9&sGwmD z^9WXw_ws7Q!w)(C!!HS>`{_^OyXJOZ+sIiWb=yx^8stDBwI9_D6?)k|LPdFE_X+%( zw*LV<=ZIcEeS<*1=UK~{{4xc+{w!VmZy&`>R!w5vssC!PeN0KBAL&&**8ikiO^2xG ziBEb;Us^3tDI35#{z&%pP5K!)X`afa-^Z>)X~N~J(}cw1ixPG|pcs^O<=f$u^B8fD zhv-cvymNd^nL8I%r{A74;_-KtRvl|#R?8mqfx|d+*?BLRCC@}6f!GO8+Zh@KA}Sa` z-N$#8p zIK|2^EZFARE4|e9KOD||J9)Kf{+4)t>Cu)WXFOC`I|3iqx#(p~C=puR6^NIKp8fm* z3}zq}wZE5~%rRlPR@sb@j=aoossOSSDN!u5pNq7QJ8DZU=uVUnP$KkjDcoa<=$yw- zl;$W?lqken{e=X2h;zM(5}h(qv7*RZ8K=I@b50I=Apl&G+c0Z;Ou!J$SP_X8!-@M| zZYesnB*!gA{h__YXRTki@EL~{&h*!vK|rTh$m-s}e*NxDL(slhekUg)2SX&dq&2a} zIDNBVItwS9wBazT6W3T&D*rT(1+E8d5l0&8*1r3@hW>L;P?~)McRjY{KqP@&;r*53 z?Z;Ujk*BU~y|}+<7xlqY1{65mFzuy@g7abYRbZBR+A%$yw*0#v3)He7gaYi#KwoF3 zf}d{B>eQyKeR_QJx-V^0hnK<5`k^hsQwBl!*qh@6?^4;3+Mmq=XP1O&MTtMZ*9#7X z9ChAym<~%YhG3MdY{fkmPQS9;s6ail0%}DA(!V%nWFPm81(OsQ_tx}I4FV-k#tv%T2>C=vR0mGS}Y-mp~^P;dSbigX(DD)rE z+6SnxEJjKI?ZH|ZD4De}#UN=04`sF{4KzK8zO;r>e9w1$*T8D)OeE)c?z}QWuT~&9PM9 z>jxqun8IeD#nZ=ob&9_tTyRh&+KDLv+jf04@nr4Iu#=y-R+Bak z#rY?fUBnaXU%1=WpF!%kg1%O!Wc7|H+~Ti$hLL`7CzhC|sO%`!$MOw>#a|{0CdT{R zv3mmjSs4ZbL;zjF)6Oc)uF7iSbwf2T?w;m~}NY zxT#vXjCuJ)1w8`TrR?oCTdhw`TT-1ngmhRFflbMK2YSI9;fj3^y@ev66Bmjj{$I%l z;wgGZaxzCEJ{GZPpR&fy&RymMkbLehS*;n)r`UPP+}OP{bNRM1R+!RU*^I7W z{qPCFo{=gWIP4_sg_6X_DbE`M)2uhn6AJ7mU8xtWOJQyfC#oiNh~8r}r{-)BL&>5& z9|!U03a|ZTM7jS& zlRy6j^wF@hC3y#wm}LkOwKiq#JHbP=TFw)lw&W8GaZ9Sr4mpn_W|}KLT6b_odk%MT zr9gp>4(%t|pgO!8m=dq-8<6~)Pu+kCTt|n;`$M|o_*^G*guoWRZI#=!j}lM?IQn`# zx_f@l?6{;Jvp83mxx1nPxM!CoA@OIin&W_$q~#W53je}jfW8l3yZ%8EnAbZ7MLeOL z78nPOUEBZFXRx~)jR$fO5YTjKg`l80HqV78I<#t%)SpyE>K9Zf;%qdRU^PJ&Yr99w z{ZgwHJG?%G+J+X7VYSn*K=}t&*aAvB>%W--w;q%NE4H%pXszTR=Q8e0zlK$GvSUZZ zdviO6+acmW%s$*be0raRH>>W25zAlGES-fKvIu5Y_87cmo29$(G{9}S z_M`N#aqK6#E3m;Oy-DdJ?#uWp<7{JXS@$WU(##Uj9s)I!*N?v89(8c056eGrc2RAU z{tl>T=<7mQ;Jmha)7BO0ybg8a9szq_$vtcT0e8pk9YhC*T>nd%qWKD|2w* za4;Q=AMO9!GcH){GSXwH==R(I;W%SKTiM{#$2-fUBE8h@KaAp2&hn!OCKW;EYC9hG zLkvq4cFMY&6^t{(wC!s8yU)!zJn_o=^X$KWqaRpo>kG*y!B{mt(pc@;z;uPR-&&Wly?@gwYHIl2pR8EH*YQLFqKpLQv{=3uTYi1^UaL#gS} zq56?{ET{y*vaX+CC4;e*&=03S8;P?|xyOZJZ8PT@35rEVHjkV@lQ>AWdAvE3YGORk z-Bra^__o%>wu^_HnJ=QYFCNcO>@)e##i(avCAIGCje3 zciYioVJAK9A6q3_87!JI<4Yb}r0?Ne=gP-a@kXn-cVGB~(}oiBUgj{Y^xZKb8PsxT zvs1AVpwZpy>cs)=M#gd8{{fpnA1>I9oJpH$tU#q$%G?M_lD~VfbSodgkg#MQ44{X5 zA@K;HZaCxxm4D_<9Y=Ipzj^I@+pV@bqbfz4idpw)^3ceANq zS)r6AxDQdtHz};WfG)AsUCs7%ez(@T0(taUCnCtsG^DDzTbhbc=jcy9=Xm17%Di5G z%4A=m(!`^iCwq1M3l}cvlG%^0G8kdSY^ppw0|7}N$|htV&G%>-HVa@zkIf=;3St1& z4#zWhq0wj^Kkx?K4Y_JCRMaj55-*Tv2J~8TLcX`ocqGDr5ZoFA+yWX;7!?MwuZTF^xP?|_Dv0hS65~Tat$Y%L-*QWkR&zJPw zfcf7UL`!~<<7~g;kycRQ@ey%)VFonPGJL}N3$Up)z{)zYBdScFqOUeRd`-aYI1ZQ=y zqpvc2zi!J~%1nywi#-G$ZQx#=bE=TxL_NQSikjhPSQD77w}%nKw5w`I0+`V$b?e4V z>vuUQwIM1|shD~p57(Dj@!RF|U+G7eiEbd#E)MVe>nShT%N7GzwGrn#OHE`0m(sQJ zW>24ju8ixc>ToSrcE^>apB7zkJfQ}qEo;`!nf^!ksq*~jhvRD5dX}Q4x3`yUKU+;d zr%T<;o16{w&(Wt0tRL@kYU@Pr!IH0`%EwEFYTq}@=go$|fC^u3l%V?8qwzbIPO!P` z^5p>GllUe{G?aYgd1WpPGuy4t8_TYMr*eiQ3|LPi0CKJh*cT0EXnFmT!PZi-$87k3 zaykB4t=+Y$D@GQNNM){X(Z96>mvLDxiOSQvl9vL#IJM9J$|;4*)zwQ*FDED#0-iO- z3l&lAkE{QYe>hzEv(K`f(klma#0&=m#`9!#%VsPBRuexHX{jpgeA;}FYZIW%;p~$? z>K)g418_2N6TP3+?#eDYILY8_hw04f4QA2i%?ORmC}R>YO^H){ld;%umNY1> zr}SUZJXgj?7?FyFx8Z|yb2V)WV-vMHBg!V8;@+Ejt=ejXh23n)>*Fnar(#faI2l{D zp$TVH9kw)eKu@u1Wvt<3eH#nllhUb%Wy}f8NrP>$N$M`8n5mCWHySE-q++QJOJc)g zah9lAp27nn=(+4yAo)ozfSXcJ&u7Q~@)K1DR13jCot>$HjzKMM#;cyR2(o5SNs&

u6k(sgaxaoi;k59=2 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 XjX7cjfWcYH00000NkvXXu0mjf$8Haz literal 0 HcmV?d00001 diff --git a/assets/headers/v7.png b/assets/headers/v7.png new file mode 100644 index 0000000000000000000000000000000000000000..7e4d6fc57bebf22405ccb333493c8f68c1bbc829 GIT binary patch literal 11774 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

WOTRxi4Qk&EW! zz^y6cGYVR^zm&Cx&X>@O;L(UzbxsB;W1AW2Y@b1oFgmEwnZ7YXeG-3Qcdxk^+)SnX zx07UuHm`RdNoD0j&lrlrhZ(Rb8}l_}Go!~;1Yd&=fCF6Q><3q~-ncW_!xk^e-jbV| zm@mfEa3wz)fM*s$oMRGFyWLYPahm+s6TS}00B0gEx}TJSH1+AaaDFF-!c&m4*p4$l z)-i9W;NcOC`6n6yz4{|?9Xtk7LxxFt>Mx`cAr--B29Xi8#Bm$pp#~0(y}GbG%rbaS zk%PuYxgWTha)Ic5qv~*&+gxWL?Zl$*i*Mx1d};|_e{vFZ`plcDAr1I0)r~3Eb7%h2 z^TTsCCHVNqhIjW#Hb5nLJNYD=ldP1$sge}))&wd=8*LK15Y zrXiglYqYN7jurhe|II7ri|v6uz2^78xczI*UVfSDCm{Wf*ImlwngR%j4)CB!JIgg? z;xMUfGY`!KGHJyTiT=3tYrV59k~DIhkoKia>nwlWZ6tK;U^wMgkI$ScYc!J4^}?Ra z-wp2Y1ZAv>jYxz<_agvu9RSg7Qnfo{kC)tjNO|2dzgpgCd&w@0hbvGOrM0)~byASm ztIiDJ+?3TnsToilL2WuLezElK#&0v)$m?}(@cl`Hd+9t<%@OhpRH|v!yw_QDU9f;e zvwde5%TXl59xfdD+u}>#%mt>0X<ta0bl3fQIE&abn^P*E6HMbJVZt6707wR-muhC-Tx`%sI zyE16bv;6J7<_xIS26u$^uJbEx!kA~qNb8aGKfb>uCJs258yWIRSXh8ZXB>UomCkuo zP8IAG-9X{^$z5`7(uF1%;+_j0JYw=e`7Ir*E>0A1!8Y5>1b}XCkLDM)(r|C1M!TFj?C)|4k*r(pC`Bl zuC!kZBVsj_4-_x_0dUT+s5sDU2O;COf=ufTLaEPp|Ug|{M%lI<^U{HJ2}HSy)x8fX=8F2FOqx$nl_ClG6^ z`?kfW0W;GVD+^47W-m8TKKM_FtzZ^apb*6jIDyFSIo;DecJJyq)>{V z5ssU37!VBkW_2DuviH77M7;4~%83ywaxvzxT z>T!&h&kT$+jB*HDfjM|V_VI9shr_Rm5qW6g)(SdT5=NRhSv}m|_39&?I+h|m%MCUW zLm1n28};le)1#!>sGrsEVeEtsLMzB<8rR0joc$KSq~qJJQn{B{QukepqK&+TGENHe zuI*dAKc#Tu#)mzCem^w_V7uMj7O>BXzZr)%yB(uO#wBuIrOEgM0(yTn{m&y$Kbohw5Ah9Ayzt+&S%X z;s2=dNVlH@hEy#%98=~{#q+UNIbT*$w1yM*LQ=cfHORD2$1_uuFC9?r1>QE~0b@`* zdL+sI9ShFm3q!Vx8|3U{`4EjZK~vVl){6g58#|}<+*F+&XQ^}OyRSb?l7Rh+`b!^#M zQ1JyCYPn6<9sR%R1qEBK(~*Sh4`{G$5*{7q_lf86<9$xa1R?g*CCQd!%m~r{h6!+W zJ7AFWP=cc?XT^sY1`SItbS-V5N_znH-{%ccbZ9#yL!S$Z{+8w@s^Rqp%JXg_f7|94@~%RDjc#l&++kjvsA8|!iPR)U&QtmRV_vbiy}BUDBEM!Yb8!)ReHL$Rbe z0EY&5PS`Bc3{xv;bGj8MA`WiZ=^<#aL%Z1?J8IDF?a5`5rsOobso`N;Ny#*C2!4k? z#QWjVqX)F}hv3x!cODr663X?Nguxw>(DKw@HW&`9fq|B_Yu|7d@VvK>%87$=M=R$V zTLJY5M1;tWK0*VpOxTrtxV?wqm@n`!B!5^zX)xT!|8kBLXXE>8`C46#C6D1fr2H_< z-4@5VywipJHCd2rqJGyqBwr}OvoZZG54%8js(dQ`nvS-oLciD=Ff~&_|Mg*q2ySs^ZPqk;r|cb3^OtS literal 0 HcmV?d00001 diff --git a/src/components/Home/AccountSwitcher.tsx b/src/components/Home/AccountSwitcher.tsx index 5b723cd72..8cfee6b3e 100644 --- a/src/components/Home/AccountSwitcher.tsx +++ b/src/components/Home/AccountSwitcher.tsx @@ -95,27 +95,29 @@ const AccountSwitcher: React.FC<{ return ( - @@ -126,7 +128,6 @@ const AccountSwitcher: React.FC<{ loading && { shadowOpacity: 0 }, small && { paddingHorizontal: 0, - shadowOpacity: 0, elevation: 0, borderRadius: 0, paddingVertical: 0, @@ -141,7 +142,7 @@ const AccountSwitcher: React.FC<{ {renderProfilePicture()} - + ); }; diff --git a/src/components/Home/Header.tsx b/src/components/Home/Header.tsx index 43c647c3c..f0e32b183 100644 --- a/src/components/Home/Header.tsx +++ b/src/components/Home/Header.tsx @@ -130,7 +130,7 @@ const Header: React.FC<{ showsHorizontalScrollIndicator={false} style={[styles.part, styles.buttons]} contentContainerStyle={{ - gap: 10, + gap: 7, paddingHorizontal: 16, overflow: "visible", }} @@ -182,8 +182,8 @@ const Header: React.FC<{ flexDirection: "row", backgroundColor: "#ffffff00", borderColor: "#ffffff50", - borderWidth: 1.5, - borderRadius: 10, + borderWidth: 1, + borderRadius: 100, borderCurve: "continuous", gap: 12, paddingHorizontal: 12, @@ -327,9 +327,8 @@ const HeaderButton: React.FC<{ style={[ styles.headerButton, { - backgroundColor: "#ffffff20", + backgroundColor: "#ffffff22", borderColor: "#ffffff50", - borderWidth: 1, } ]} > @@ -352,6 +351,7 @@ const styles = StyleSheet.create({ flex: 1, width: "100%", paddingVertical: 16, + paddingTop: 14, gap: 12, justifyContent: "flex-start", @@ -394,9 +394,9 @@ const styles = StyleSheet.create({ flex: 1, flexDirection: "row", alignItems: "center", - paddingHorizontal: 12, + paddingHorizontal: 14, gap: 12, - borderRadius: 10, + borderRadius: 120, borderCurve: "continuous", borderWidth: 1, }, diff --git a/src/router/helpers/types.ts b/src/router/helpers/types.ts index 24435b402..3dff06969 100644 --- a/src/router/helpers/types.ts +++ b/src/router/helpers/types.ts @@ -84,9 +84,11 @@ export type RouteParameters = { SkolengoGeolocation: undefined; SkolengoInstanceSelector: { pos: CurrentPosition | null }; SkolengoWebview: { school: SkolengoSchool }; + // account.index Home: undefined; HomeScreen?: { onboard: boolean }; + CustomizeHeader: undefined; Lessons?: { outsideNav?: boolean }; LessonsImportIcal: { diff --git a/src/router/screens/views/index.ts b/src/router/screens/views/index.ts index 3ff29b6ca..f5e3703a5 100644 --- a/src/router/screens/views/index.ts +++ b/src/router/screens/views/index.ts @@ -22,6 +22,7 @@ import ChatThemes from "@/views/account/Chat/Modals/ChatThemes"; import RestaurantCardDetail from "@/views/account/Restaurant/Modals/CardDetail"; import RestaurantPaymentSuccess from "@/views/account/Restaurant/Modals/PaymentSuccess"; import AddHomeworkScreen from "@/views/account/Homeworks/AddHomework"; +import CustomizeHeader from "@/views/account/Home/Modal/CustomizeHeader"; export default [ createScreen("GradeReaction", GradeReaction, { @@ -91,6 +92,17 @@ export default [ sheetCornerRadius: 16, sheetAllowedDetents: [0.5, 1], sheetGrabberVisible: true, + // @ts-expect-error + sheetInitialDetentIndex: 0, + }), + createScreen("CustomizeHeader", CustomizeHeader, { + headerTitle: "Personnaliser", + presentation: "formSheet", + headerShown: true, + sheetCornerRadius: 16, + sheetAllowedDetents: [0.5, 0.7], + sheetGrabberVisible: true, + // @ts-expect-error sheetInitialDetentIndex: 0, }), createScreen("GradeSubject", GradeSubjectScreen, { diff --git a/src/services/shared/Subject.ts b/src/services/shared/Subject.ts index 14e8c1df7..eca889021 100644 --- a/src/services/shared/Subject.ts +++ b/src/services/shared/Subject.ts @@ -3,7 +3,7 @@ import findObjectByPronoteString from "@/utils/format/format_cours_name"; export const COLORS_LIST = ["#D1005A", "#BE4541", "#D54829", "#F46E00", "#B2641F", "#D18800", "#BEA541", "#E5B21A", "#B2BE41", "#94BE41", "#5CB21F", "#32CB10", "#1FB28B", "#6DA2E3", "#0099D1", "#1F6DB2", "#4E339E", "#7941BE", "#CC33BF", "#BE417F", "#E36DB8", "#7F7F7F"]; -const getRandColor = () => { +export const getRandColor = () => { return COLORS_LIST[Math.floor(Math.random() * COLORS_LIST.length)]; }; diff --git a/src/stores/account/types.ts b/src/stores/account/types.ts index aea71f86e..ce5dcd74e 100644 --- a/src/stores/account/types.ts +++ b/src/stores/account/types.ts @@ -34,37 +34,48 @@ export interface PapillonIcalURL { } export interface Personalization { - color: PersonalizationColor - profilePictureB64?: string, - hideNameOnHomeScreen: boolean, - hideProfilePicOnHomeScreen: boolean, - hideTabTitles: boolean, - showTabBackground: boolean, - showWeekFrequency: boolean, - transparentTabBar: boolean, - hideTabBar: boolean, - popupRestauration?: boolean, - magicEnabled?: boolean, - MagicNews?: boolean, - MagicHomeworks?: boolean, + color: PersonalizationColor; + profilePictureB64?: string; + hideNameOnHomeScreen: boolean; + hideProfilePicOnHomeScreen: boolean; + hideTabTitles: boolean; + showTabBackground: boolean; + showWeekFrequency: boolean; + transparentTabBar: boolean; + hideTabBar: boolean; + popupRestauration?: boolean; + magicEnabled?: boolean; + MagicNews?: boolean; + MagicHomeworks?: boolean; notifications?: { - enabled?: boolean - news?: boolean - homeworks?: boolean - grades?: boolean - timetable?: boolean - attendance?: boolean - evaluation?: boolean - } - icalURLs: PapillonIcalURL[], - tabs: Tab[], + enabled?: boolean; + news?: boolean; + homeworks?: boolean; + grades?: boolean; + timetable?: boolean; + attendance?: boolean; + evaluation?: boolean; + }; + icalURLs: PapillonIcalURL[]; + tabs: Tab[]; subjects: { [subject: string]: { - color: string, - pretty: string, - emoji: string, - } - } + color: string; + pretty: string; + emoji: string; + }; + }; + header: { + gradient: PersonalizationHeaderGradient; + image: string | undefined; + darken: boolean; + }; +} + +export interface PersonalizationHeaderGradient { + startColor: string; + endColor: string; + angle: number; } export interface Identity { diff --git a/src/views/account/Home/Home.tsx b/src/views/account/Home/Home.tsx index e13e26171..90cc14a66 100644 --- a/src/views/account/Home/Home.tsx +++ b/src/views/account/Home/Home.tsx @@ -37,8 +37,8 @@ import { useAccounts, useCurrentAccount } from "@/stores/account"; import getCorners from "@/utils/ui/corner-radius"; import { useIsFocused, useTheme } from "@react-navigation/native"; import React, { useCallback, useEffect, useMemo, useState } from "react"; -import { Dimensions, Linking, Platform, RefreshControl, StatusBar, View } from "react-native"; -import Reanimated from "react-native-reanimated"; +import { Dimensions, Image, Linking, Platform, Pressable, RefreshControl, StatusBar, View } from "react-native"; +import Reanimated, { FadeIn, FadeOut } from "react-native-reanimated"; import Animated, { Extrapolation, interpolate, useAnimatedRef, useAnimatedStyle, useScrollViewOffset } from "react-native-reanimated"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import AccountSwitcher from "@/components/Home/AccountSwitcher"; @@ -52,6 +52,9 @@ import useScreenDimensions from "@/hooks/useScreenDimensions"; import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; import { useAlert } from "@/providers/AlertProvider"; import { ArrowLeft, Menu, Plus } from "lucide-react-native"; +import { LinearGradient } from "expo-linear-gradient"; +import { HEADERS_IMAGE } from "./Modal/CustomizeHeader"; +import MaskedView from "@react-native-masked-view/masked-view"; const Home: Screen<"HomeScreen"> = ({ navigation }) => { const { colors } = useTheme(); @@ -190,7 +193,7 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { const scrollViewAnimatedStyle = useAnimatedStyle(() => ({ flex: 1, - backgroundColor: scrollOffset.value > 265 + insets.top ? colors.card : colors.primary, + backgroundColor: scrollOffset.value > 265 + insets.top ? colors.card : "transparent", })); return ( @@ -210,6 +213,122 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { /> )} + + { + if (modalOpen) return; + navigation.navigate("CustomizeHeader"); + }} + pointerEvents={modalOpen ? "none" : "auto"} + /> + + + {account?.personalization?.header?.gradient && ( + + + + )} + + {account?.personalization?.header?.image && !isTablet && ( + + + } + > + item.label === account?.personalization?.header?.image)?.source} + style={{ + width: "100%", + height: "100%", + }} + resizeMode="cover" + /> + + + )} + + {account?.personalization?.header?.darken && ( + + )} + + = ({ navigation }) => { setCanHaptics(true); } - setModalOpen(scrollY >= 195 + insets.top); + setModalOpen(scrollY >= 180 + insets.top); setModalFull(scrollY >= 265 + insets.top); }} refreshControl={ setRefreshing(true)} style={{ zIndex: 100 }} progressViewOffset={285 + insets.top} />} diff --git a/src/views/account/Home/Modal/CustomizeHeader.tsx b/src/views/account/Home/Modal/CustomizeHeader.tsx new file mode 100644 index 000000000..3b6f8e8ab --- /dev/null +++ b/src/views/account/Home/Modal/CustomizeHeader.tsx @@ -0,0 +1,356 @@ +import React, { useEffect, useLayoutEffect } from "react"; + +import { Screen } from "@/router/helpers/types"; + +import { FlatList, Image, Pressable, ScrollView, Switch, TouchableOpacity, View } from "react-native"; +import { useCurrentAccount } from "@/stores/account"; +import { NativeList, NativeListHeader } from "@/components/Global/NativeComponents"; +import { COLORS_LIST } from "@/services/shared/Subject"; +import { PersonalizationHeaderGradient } from "@/stores/account/types"; +import { useTheme } from "@react-navigation/native"; +import { Dice5, Moon, Palette, PictureInPicture } from "lucide-react-native"; +import InsetsBottomView from "@/components/Global/InsetsBottomView"; + +export const HEADERS_IMAGE = [ + { + label: "stars", + source: require("@/../assets/headers/stars.png"), + }, + { + label: "topography", + source: require("@/../assets/headers/topography.png"), + }, + { + label: "boxes", + source: require("@/../assets/headers/boxes.png"), + }, + { + label: "texture", + source: require("@/../assets/headers/texture.png"), + }, + { + label: "hlr", + source: require("@/../assets/headers/hlr.png"), + }, + { + label: "v7", + source: require("@/../assets/headers/v7.png"), + }, + { + label: "ailes", + source: require("@/../assets/headers/ailes.png"), + }, + { + label: "spark", + source: require("@/../assets/headers/spark.png"), + }, + { + label: "tictactoe", + source: require("@/../assets/headers/tictactoe.png"), + } +]; + +const CustomizeHeader: Screen<"CustomizeHeader"> = ({ route, navigation }) => { + const account = useCurrentAccount(store => store.account); + const mutateProperty = useCurrentAccount(store => store.mutateProperty); + + const defaultGradient = { + startColor: COLORS_LIST[0], + endColor: COLORS_LIST[1], + angle: 0, + }; + + const [image, setImage] = React.useState(account?.personalization?.header?.image); + const [gradient, setGradient] = React.useState(account?.personalization?.header?.gradient); + const [darken, setDarken] = React.useState(account?.personalization?.header?.darken || false); + const [centerReset, setCenterReset] = React.useState(undefined); + + useEffect(() => { + mutateProperty("personalization", { + header: { + gradient, + image, + darken, + }, + }); + }, [gradient, image]); + + const { colors } = useTheme(); + + const randomizeGradient = () => { + const startColor = COLORS_LIST[Math.floor(Math.random() * COLORS_LIST.length)]; + const endColor = COLORS_LIST[Math.floor(Math.random() * COLORS_LIST.length)]; + setGradient({ + startColor, + endColor, + angle: 0, + }); + setCenterReset(Math.random()); + }; + + useLayoutEffect(() => { + navigation.setOptions({ + headerRight: () => ( + { + randomizeGradient(); + }} + style={{ + padding: 8, + borderRadius: 100, + }} + > + + + ), + }); + }, [navigation, colors.primary, account?.personalization?.header?.gradient?.startColor]); + + return ( + + } + label="Dégradé de couleur" + trailing={ + { + const gradValue = value ? gradient || defaultGradient : undefined; + mutateProperty("personalization", { + header: { + image, + darken, + gradient: gradValue, + }, + }); + setGradient(gradValue); + }} + /> + } + /> + + { + if(!gradient) { + setGradient(defaultGradient); + } + setGradient((prev) => ({ + ...prev, + startColor: color, + })); + }} + /> + + + { + if(!gradient) { + setGradient(defaultGradient); + } + setGradient((prev) => ({ + ...prev, + endColor: color, + })); + }} + /> + + + + + } + label="Assombrir le fond" + trailing={ + { + mutateProperty("personalization", { + header: { + gradient, + image, + darken: value, + }, + }); + setDarken(value); + }} + /> + } + /> + + } + label="Image" + trailing={ + { + mutateProperty("personalization", { + header: { + gradient: gradient, + image: value ? image || HEADERS_IMAGE[0].label : undefined, + darken, + }, + }); + setImage(value ? image || HEADERS_IMAGE[0].label : undefined); + }} + /> + } + /> + + item.label} + renderItem={({ item }) => ( + { + setImage(item.label); + }} + > + + + )} + showsHorizontalScrollIndicator={false} + showsVerticalScrollIndicator={false} + contentContainerStyle={{ + paddingVertical: 8, + paddingHorizontal: 8, + gap: 8, + }} + /> + + + + + ); +}; + +const HorizontalColorSelector = ({ + onColorSelect, + selected, + centerReset, +}: { + onColorSelect?: (color: string) => void; + selected?: string; + centerReset?: any; +}) => { + const { colors } = useTheme(); + + const SelectorRef = React.useRef(null); + + const ITEM_WIDTH = 72; // Largeur de l'élément + marges + const ITEM_HEIGHT = 40; // Hauteur de l'élément + marges + + const scrollToIndex = (index: number, animated:boolean) => { + if (SelectorRef.current) { + SelectorRef.current.scrollToIndex({ + index, + animated: animated, + viewPosition: 0, + }); + } + }; + + const resetScroll = (animated: boolean) => { + const index = COLORS_LIST.findIndex((color) => color === selected); + if (index !== -1) { + scrollToIndex(index, animated); + } + }; + + useEffect(() => { + setTimeout(() => { + resetScroll(centerReset !== undefined); + }, 100); + }, [centerReset]); + + return ( + item} + renderItem={({ item }) => ( + { + if (onColorSelect) { + onColorSelect(item); + } + }} + > + + + )} + getItemLayout={(_, index) => ({ + length: ITEM_WIDTH, + offset: ITEM_WIDTH * index, + index, + })} + showsHorizontalScrollIndicator={false} + showsVerticalScrollIndicator={false} + contentContainerStyle={{ + paddingVertical: 8, + paddingHorizontal: 8, + }} + /> + ); +}; + +export default CustomizeHeader; \ No newline at end of file From e9756e03374b76c602f287b7563d7a218ab95fb9 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sun, 30 Mar 2025 15:15:58 +0200 Subject: [PATCH 1069/1144] fix(Home): ajuster le seuil d'ouverture du modal et optimiser le composant AccountSwitcher avec memo --- src/components/Home/AccountSwitcher.tsx | 109 ++--- .../Home/AccountSwitcherContextMenu.tsx | 22 +- src/components/Home/Header.tsx | 460 ++++++------------ src/providers/AlertProvider.tsx | 16 +- src/views/account/Home/Home.tsx | 2 +- 5 files changed, 233 insertions(+), 376 deletions(-) diff --git a/src/components/Home/AccountSwitcher.tsx b/src/components/Home/AccountSwitcher.tsx index 8cfee6b3e..c7860d24a 100644 --- a/src/components/Home/AccountSwitcher.tsx +++ b/src/components/Home/AccountSwitcher.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { memo } from "react"; import { Image, StyleSheet, View } from "react-native"; import { useTheme } from "@react-navigation/native"; import { ChevronDown } from "lucide-react-native"; @@ -18,13 +18,13 @@ import { BlurView } from "expo-blur"; const ReanimatedBlurView = Reanimated.createAnimatedComponent(BlurView); const AnimatedChevronDown = Reanimated.createAnimatedComponent(ChevronDown); -const AccountSwitcher: React.FC<{ - small?: boolean; - opened?: boolean; - modalOpen?: boolean; - translationY?: Reanimated.SharedValue; - loading?: boolean; -}> = ({ small, opened, modalOpen, translationY, loading }) => { +const AccountSwitcher = ({ + small = false, + opened = false, + modalOpen = false, + translationY, + loading = false, +}) => { const theme = useTheme(); const { colors } = theme; const account = useCurrentAccount((store) => store.account!); @@ -48,11 +48,7 @@ const AccountSwitcher: React.FC<{ })); const textAnimatedStyle = useAnimatedStyle(() => ({ - color: interpolateColor( - translationY?.value || 0, - [200, 251], - ["#FFF", colors.text] - ), + color: colors.text, fontSize: 16, fontFamily: "semibold", maxWidth: 140, @@ -94,59 +90,29 @@ const AccountSwitcher: React.FC<{ return ( - + - + {renderProfilePicture()} @@ -180,6 +146,25 @@ const AccountSwitcher: React.FC<{ }; const styles = StyleSheet.create({ + container: { + borderRadius: 12, + borderCurve: "continuous", + overflow: "visible", + alignSelf: "flex-start", + shadowColor: "black", + shadowOffset: { width: 0, height: 1 }, + shadowRadius: 4, + shadowOpacity: 0, + borderWidth: 1, + }, + innerContainer: { + paddingHorizontal: 2, + paddingVertical: 0, + alignSelf: "flex-start", + borderRadius: 12, + borderCurve: "continuous", + overflow: "hidden", + }, accountSwitcher: { flexDirection: "row", justifyContent: "center", @@ -192,6 +177,18 @@ const styles = StyleSheet.create({ paddingVertical: 6, gap: 6, }, + smallAccountSwitcher: { + paddingHorizontal: 0, + elevation: 0, + borderRadius: 0, + paddingVertical: 0, + backgroundColor: "transparent", + }, + row: { + flexDirection: "row", + alignItems: "center", + gap: 12, + }, avatar: { aspectRatio: 1, borderRadius: 24, @@ -201,4 +198,4 @@ const styles = StyleSheet.create({ }, }); -export default AccountSwitcher; +export default memo(AccountSwitcher); diff --git a/src/components/Home/AccountSwitcherContextMenu.tsx b/src/components/Home/AccountSwitcherContextMenu.tsx index 94bdb224a..31771cdae 100644 --- a/src/components/Home/AccountSwitcherContextMenu.tsx +++ b/src/components/Home/AccountSwitcherContextMenu.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState, useCallback } from "react"; +import React, { useEffect, useState, useCallback, memo } from "react"; import { Dimensions, Image, @@ -22,13 +22,13 @@ import { BlurView } from "expo-blur"; import { Check, Cog, Plus } from "lucide-react-native"; import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; -const ContextMenu: React.FC<{ - style?: any; - children: React.ReactNode; - transparent?: boolean; - shouldOpenContextMenu?: boolean; - menuStyles?: any; -}> = ({ children, style, shouldOpenContextMenu, transparent, menuStyles }) => { +const ContextMenu = ({ + style, + children, + transparent, + shouldOpenContextMenu, + menuStyles, +}) => { const theme = useTheme(); const { colors } = theme; const navigation = useNavigation(); @@ -58,9 +58,9 @@ const ContextMenu: React.FC<{ }, [playHaptics]); const handlePress = useCallback(() => { - setOpened(!opened); + setOpened((prevOpened) => !prevOpened); openEffects(); - }, [opened, openEffects]); + }, [openEffects]); const handleLongPress = useCallback(() => { setTouchLongPress(true); @@ -419,4 +419,4 @@ const styles = StyleSheet.create({ }, }); -export default ContextMenu; +export default memo(ContextMenu); diff --git a/src/components/Home/Header.tsx b/src/components/Home/Header.tsx index f0e32b183..187d09504 100644 --- a/src/components/Home/Header.tsx +++ b/src/components/Home/Header.tsx @@ -1,17 +1,9 @@ import { CopyPlus } from "lucide-react-native"; -import React, { forwardRef, useEffect, useState } from "react"; +import React, { forwardRef, useEffect, useState, useCallback, useMemo, memo } from "react"; import { Image, ScrollView, StyleSheet, Text, TouchableOpacity, View } from "react-native"; - import { useTheme } from "@react-navigation/native"; - import { useCurrentAccount } from "@/stores/account"; -import Reanimated, { - Easing, - FadeInRight, - ZoomIn, - ZoomOut -} from "react-native-reanimated"; - +import Reanimated, { Easing, FadeInRight, ZoomIn, ZoomOut } from "react-native-reanimated"; import { get_home_widgets } from "@/addons/addons"; import AddonsWebview, { type AddonHomePageInfo } from "@/components/Addons/AddonsWebview"; import { NativeText } from "@/components/Global/NativeComponents"; @@ -27,73 +19,131 @@ import { animPapillon } from "@/utils/ui/animations"; import PapillonSpinner from "../Global/PapillonSpinner"; import useScreenDimensions from "@/hooks/useScreenDimensions"; -const Header: React.FC<{ - scrolled: boolean - navigation: NativeStackNavigationProp -}> = ({ - scrolled, - navigation, -}) => { - const account = useCurrentAccount(store => store.account!); - const [tabs, setTabs] = useState([ - { name: "Attendance", enabled: true }, - { name: "Discussions", enabled: true }, - { name: "Menu", enabled: true }, - ]); - - const [addons] = useState([]); +const Header : React.FC<{ + scrolled: boolean; + navigation: NativeStackNavigationProp; +}> = (({ scrolled, navigation }) => { + const account = useCurrentAccount((store) => store.account!); + const [tabs, setTabs] = useState([]); + const [addons, setAddons] = useState([]); const [addonsTitle, setAddonsTitle] = useState([]); - const [click, setClick] = useState(false); + const [click, setClick] = useState(false); const { isTablet } = useScreenDimensions(); useEffect(() => { - // On récupère le fichier principal de chaque extension. get_home_widgets().then((addons) => { - let res: AddonHomePageInfo[] = []; - - addons.forEach((addon) => { - addon.placement.forEach((placement) => { - res.push({ - name: addon.name, - icon: addon.icon, - // @ts-expect-error : à vérifier avec Rémy. - url: addon.base_path + "/" + placement.main - }); - }); - }); - - // TODO: activer lorsque c'est fonctionnel ? - // setAddons(res); + const res: AddonHomePageInfo[] = addons.flatMap((addon) => + addon.placement.map((placement) => ({ + name: addon.name, + icon: addon.icon, + url: `${addon.base_path}/${placement.main}`, + })) + ); + setAddons(res); }); }, []); useEffect(() => { if (account.personalization.tabs) { - let newTabs = account.personalization.tabs; - setTabs(newTabs); + setTabs(account.personalization.tabs); } }, [account.personalization]); - return ( - + const handlePress = useCallback(() => { + setClick(true); + setTimeout(() => { + navigation.navigate("SettingsTabs"); + setClick(false); + }, 10); + }, [navigation]); + + const renderHeaderButton = useCallback( + (tab: Tab, index: number) => { + const defaultTab = defaultTabs.find((curr) => curr.tab === tab.name); + if (!defaultTab || tab.enabled || tab.name === "Home") return null; + + return ( + + } + text={defaultTab.label} + scrolled={scrolled} + onPress={() => navigation.navigate(tab.name as RouteParameters[keyof RouteParameters])} + /> + ); + }, + [scrolled, navigation] + ); + + const renderWidgets = useMemo(() => { + return ( + entering={FadeInRight.easing(Easing.bezier(0, 0, 0, 1)).duration(500).delay(250).withInitialValues({ + opacity: 0, + transform: [{ translateX: 20 }], + })} + style={{ gap: 15, flexDirection: "row", height: 131 }} + > + {Widgets.map((widget, index) => ( + + ))} + {addons.map((addon, index) => ( + ( + { + const temp = [...addonsTitle]; + temp[index] = addon.name; + setAddonsTitle(temp); + }} + > + + + + {addonsTitle[index]} + + + { + const temp = [...addonsTitle]; + temp[index] = title; + setAddonsTitle(temp); + }} + /> + + ))} + /> + ))} + + ); + }, [addons, addonsTitle, navigation]); - {!isTablet && ( - tabs.filter(tab => !tab.enabled).length === 0 ? - { - navigation.navigate("SettingsTabs"); - }} - > + return ( + + + {!isTablet && + (tabs.every((tab) => tab.enabled) ? ( + navigation.navigate("SettingsTabs")}> - - - - Ajouter des onglets - + + Ajouter des onglets - : ( - - {tabs.map((tab, index) => { - if (tab.name === "Home") return null; - const defaultTab = defaultTabs.find(curr => curr.tab === tab.name); - - if (tab.enabled) return null; - if (!defaultTab) return null; - - return ( - } - text={defaultTab.label} - scrolled={scrolled} - onPress={() => { - navigation.navigate(tab.name as RouteParameters[keyof RouteParameters]); - }} - /> - ); - })} - - { - setClick(true); - setTimeout(() => { - navigation.navigate("SettingsTabs"); - setClick(false); - }, 10); - }} - style={{ - height: 38, - justifyContent: "center", - alignItems: "center", - flexDirection: "row", - backgroundColor: "#ffffff00", - borderColor: "#ffffff50", - borderWidth: 1, - borderRadius: 100, - borderCurve: "continuous", - gap: 12, - paddingHorizontal: 12, - opacity: 0.5, - }} - > - {click ? ( - - ) : ( - - )} - - - Gérer - - - - ) - )} - + ) : ( + + {tabs.map(renderHeaderButton)} + + {click ? ( + + ) : ( + + )} + Gérer + + + ))} - {!scrolled && ( - - {Widgets.map((widget, index) => ( - - ))} - - {addons.map((addon, index) => ( - ( - { - let temp = addonsTitle; - temp[index] = addon.name; - setAddonsTitle(temp); - }}> - - - {addonsTitle[index]} - - { - let temp = addonsTitle; - temp[index] = title; - setAddonsTitle(temp); - }} - /> - - ))} - /> - ))} - - )} + {!scrolled && renderWidgets} - ) - ; -}; - -const HeaderButton: React.FC<{ - icon: React.ReactElement - index: number - text: string - scrolled: boolean, - onPress: () => void -}> = ({icon, index, text, scrolled, onPress}) => { - const theme = useTheme(); - const { colors } = theme; + ); +}); - const newIcon = React.cloneElement(icon, { - size: 24, - color: "#fff", - }); +const HeaderButton = React.memo<{ + icon: React.ReactElement; + index: number; + text: string; + scrolled: boolean; + onPress: () => void; +}>(({ icon, index, text, scrolled, onPress }) => { + const theme = useTheme(); + const { colors } = theme; - return (!scrolled && - + const newIcon = useMemo(() => React.cloneElement(icon, { size: 24, color: "#fff" }), [icon]); - - {newIcon} - - - {text} - - - - ); -}; + + {newIcon} + {text} + + + ) + ); + }); const styles = StyleSheet.create({ container: { @@ -353,42 +228,34 @@ const styles = StyleSheet.create({ paddingVertical: 16, paddingTop: 14, gap: 12, - justifyContent: "flex-start", alignItems: "flex-start", }, - part: { width: "100%", paddingHorizontal: 16, overflow: "visible", }, - header: { height: 38, flexDirection: "row", justifyContent: "space-between", alignItems: "center", }, - headerSide: { justifyContent: "center", alignItems: "flex-end", }, - buttons: { maxHeight: 38, paddingHorizontal: 0, marginBottom: 2, }, - widgets: { flex: 1, width: "100%", paddingHorizontal: 0, }, - - headerButton: { height: "100%", flex: 1, @@ -400,12 +267,10 @@ const styles = StyleSheet.create({ borderCurve: "continuous", borderWidth: 1, }, - headerButtonText: { fontSize: 16, fontFamily: "medium", }, - widget: { height: "100%", width: 200, @@ -423,7 +288,6 @@ const styles = StyleSheet.create({ shadowOpacity: 0.3, shadowRadius: 3, }, - widgetContent: { width: "100%", height: "100%", @@ -433,4 +297,4 @@ const styles = StyleSheet.create({ }, }); -export default Header; \ No newline at end of file +export default memo(Header); diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index 2f7c9dab0..b788a53d3 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -1,6 +1,6 @@ import { useTheme } from "@react-navigation/native"; import { Check } from "lucide-react-native"; -import React, { createContext, useState, useContext, useEffect, ReactNode } from "react"; +import React, { createContext, useState, useContext, useEffect, useCallback, ReactNode, memo } from "react"; import { Modal, View, Text, StyleSheet, Pressable } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import Reanimated, { FadeIn, FadeOut, LinearTransition } from "react-native-reanimated"; @@ -53,7 +53,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { const { isTablet } = useScreenDimensions(); const insets = useSafeAreaInsets(); - const showAlert = ({ + const showAlert = useCallback(({ title, message, icon, @@ -75,12 +75,12 @@ const AlertProvider = ({ children }: AlertProviderProps) => { }, {} as { [key: string]: number }); setDelays(initialDelays); - }; + }, []); - const hideAlert = () => { + const hideAlert = useCallback(() => { setVisible(false); setTimeout(() => setAlert(null), 150); - }; + }, []); useEffect(() => { const interval = setInterval(() => { @@ -151,7 +151,6 @@ const AlertProvider = ({ children }: AlertProviderProps) => { { borderColor: colors.text + "20", backgroundColor: colors.text + "06", - // @ts-expect-error flexDirection: alert.actions?.length > 2 ? "column" : "row", alignItems: "center", }, @@ -171,7 +170,6 @@ const AlertProvider = ({ children }: AlertProviderProps) => { key={title} layout={anim2Papillon(LinearTransition)} style={[ - // @ts-expect-error alert.actions?.length === 1 || alert.actions?.length > 2 ? styles.singleButtonContainer : null, @@ -186,7 +184,6 @@ const AlertProvider = ({ children }: AlertProviderProps) => { }} contentContainerStyle={{ borderRadius: 300, overflow: "hidden" }} style={[ - // @ts-expect-error alert.actions?.length === 1 || alert.actions?.length > 2 ? styles.singleButtonContainer : null, @@ -203,7 +200,6 @@ const AlertProvider = ({ children }: AlertProviderProps) => { borderRadius: 300, overflow: "hidden", }, - // @ts-expect-error alert.actions?.length === 1 || alert.actions?.length > 2 ? styles.singleButton : null, @@ -347,4 +343,4 @@ const styles = StyleSheet.create({ }, }); -export default AlertProvider; +export default memo(AlertProvider); diff --git a/src/views/account/Home/Home.tsx b/src/views/account/Home/Home.tsx index 90cc14a66..a782e0b1a 100644 --- a/src/views/account/Home/Home.tsx +++ b/src/views/account/Home/Home.tsx @@ -350,7 +350,7 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { setCanHaptics(true); } - setModalOpen(scrollY >= 180 + insets.top); + setModalOpen(scrollY >= 170 + insets.top); setModalFull(scrollY >= 265 + insets.top); }} refreshControl={ setRefreshing(true)} style={{ zIndex: 100 }} progressViewOffset={285 + insets.top} />} From 558ef79f0956448a145b5fecdd29c8378729b527 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sun, 30 Mar 2025 15:29:51 +0200 Subject: [PATCH 1070/1144] =?UTF-8?q?refactor(Home):=20optimiser=20les=20c?= =?UTF-8?q?omposants=20avec=20memo=20et=20impl=C3=A9menter=20le=20chargeme?= =?UTF-8?q?nt=20paresseux=20pour=20am=C3=A9liorer=20les=20performances?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Home/RedirectButton.tsx | 18 ++++---- src/components/Home/Widget.tsx | 57 +++++++------------------ src/components/Home/WidgetHeader.tsx | 18 +++----- src/views/account/Home/ElementIndex.tsx | 36 ++++++++-------- src/views/account/Home/ModalContent.tsx | 4 +- 5 files changed, 52 insertions(+), 81 deletions(-) diff --git a/src/components/Home/RedirectButton.tsx b/src/components/Home/RedirectButton.tsx index 793929f94..9d2186ee3 100644 --- a/src/components/Home/RedirectButton.tsx +++ b/src/components/Home/RedirectButton.tsx @@ -1,4 +1,4 @@ -import type React from "react"; +import React, { memo, useCallback } from "react"; import { View, Text } from "react-native"; import { ArrowUpRight } from "lucide-react-native"; import { NavigationContainerRef, useTheme } from "@react-navigation/native"; @@ -6,19 +6,20 @@ import { TouchableOpacity } from "react-native-gesture-handler"; import type { RouteParameters } from "@/router/helpers/types"; interface RedirectButtonProps { - navigation: NavigationContainerRef | null, - redirect: keyof RouteParameters + navigation: NavigationContainerRef | null; + redirect: keyof RouteParameters; } const RedirectButton: React.FC = ({ navigation, redirect }) => { const theme = useTheme(); const { colors } = theme; + const handlePress = useCallback(() => { + navigation?.navigate(redirect); + }, [navigation, redirect]); + return ( - navigation?.navigate(redirect)} - > + = ({ navigation, redirect }) Voir plus - = ({ navigation, redirect }) ); }; -export default RedirectButton; +export default memo(RedirectButton); diff --git a/src/components/Home/Widget.tsx b/src/components/Home/Widget.tsx index 4797310a3..08d286b71 100644 --- a/src/components/Home/Widget.tsx +++ b/src/components/Home/Widget.tsx @@ -1,15 +1,7 @@ -import React, { type FunctionComponent, RefAttributes, useEffect, useRef, useState } from "react"; +import React, { useCallback, useEffect, useRef, useState, memo } from "react"; import { ActivityIndicator, StyleSheet } from "react-native"; - import { useTheme } from "@react-navigation/native"; - -import Reanimated, { - FadeIn, - FadeOut, - LinearTransition, - ZoomIn -} from "react-native-reanimated"; - +import Reanimated, { FadeIn, FadeOut, LinearTransition, ZoomIn } from "react-native-reanimated"; import { animPapillon } from "@/utils/ui/animations"; import { PressableScale } from "react-native-pressable-scale"; import { NativeText } from "../Global/NativeComponents"; @@ -18,8 +10,8 @@ import type { RouteParameters } from "@/router/helpers/types"; import { useOnlineStatus } from "@/hooks/useOnlineStatus"; interface WidgetContainerProps { - widget: React.ForwardRefExoticComponent> - navigation?: NativeStackNavigationProp + widget: React.ForwardRefExoticComponent>; + navigation?: NativeStackNavigationProp; } export interface WidgetProps { @@ -32,7 +24,7 @@ export interface WidgetProps { const Widget: React.FC = ({ widget: DynamicWidget, navigation }) => { const theme = useTheme(); const { colors } = theme; - const widgetRef = useRef | null>(null); + const widgetRef = useRef | null>(null); const { isOnline } = useOnlineStatus(); const [loading, setLoading] = useState(true); @@ -44,12 +36,12 @@ const Widget: React.FC = ({ widget: DynamicWidget, navigat } }, [isOnline, loading]); - const handlePress = () => { + const handlePress = useCallback(() => { const location = (widgetRef.current as any)?.handlePress(); if (location) { navigation?.navigate(location); } - }; + }, [navigation]); return ( = ({ widget: DynamicWidget, navigat entering={animPapillon(ZoomIn).withInitialValues({ transform: [{ scale: 0.7 }], opacity: 0 })} exiting={FadeOut.duration(150)} > - handlePress()} - > + {loading && ( = ({ widget: DynamicWidget, navigat exiting={FadeOut.duration(150)} > - - Chargement... - + Chargement... )} - = ({ widget: DynamicWidget, navigat backgroundColor: theme.dark ? colors.primary + "09" : colors.primary + "11", overflow: "hidden", opacity: loading ? 0 : 1, - } + }, ]} > = ({ widget: DynamicWidget, navigat hidden={hidden} /> - @@ -135,14 +112,10 @@ const styles = StyleSheet.create({ borderRadius: 17, borderCurve: "continuous", shadowColor: "#000", - shadowOffset: { - width: 0, - height: 0.5, - }, + shadowOffset: { width: 0, height: 0.5 }, shadowOpacity: 0.3, shadowRadius: 3, }, - widgetContent: { width: "100%", height: "100%", @@ -152,4 +125,4 @@ const styles = StyleSheet.create({ }, }); -export default Widget; \ No newline at end of file +export default memo(Widget); diff --git a/src/components/Home/WidgetHeader.tsx b/src/components/Home/WidgetHeader.tsx index 8d0351181..4f0897528 100644 --- a/src/components/Home/WidgetHeader.tsx +++ b/src/components/Home/WidgetHeader.tsx @@ -1,16 +1,16 @@ +import React, { memo } from "react"; import { useTheme } from "@react-navigation/native"; -import React, { cloneElement } from "react"; import { View, Text, ActivityIndicator } from "react-native"; const WidgetHeader: React.FC<{ - title: string - icon?: React.ReactElement - loading?: boolean + title: string; + icon?: React.ReactElement; + loading?: boolean; }> = ({ icon, title, loading }) => { const theme = useTheme(); const { colors } = theme; - const clonedIcon = icon && cloneElement(icon, { + const clonedIcon = icon && React.cloneElement(icon, { size: 20, strokeWidth: 2.3, color: colors.text, @@ -27,7 +27,6 @@ const WidgetHeader: React.FC<{ }} > {clonedIcon} - {title} - - {loading && ( - - )} + {loading && } ); }; -export default WidgetHeader; \ No newline at end of file +export default memo(WidgetHeader); diff --git a/src/views/account/Home/ElementIndex.tsx b/src/views/account/Home/ElementIndex.tsx index 5a06a7fad..f0a7d3f39 100644 --- a/src/views/account/Home/ElementIndex.tsx +++ b/src/views/account/Home/ElementIndex.tsx @@ -1,30 +1,32 @@ -import AttendanceElement from "./Elements/AttendanceElement"; -import GradesElement from "./Elements/GradesElement"; -import HomeworksElement from "./Elements/HomeworksElement"; -import TimetableElement from "./Elements/TimetableElement"; -import React from "react"; +import React, { lazy } from "react"; -export const Elements = [ +// Lazy load components to improve initial load performance +const AttendanceElement = lazy(() => import("./Elements/AttendanceElement")); +const GradesElement = lazy(() => import("./Elements/GradesElement")); +const HomeworksElement = lazy(() => import("./Elements/HomeworksElement")); +const TimetableElement = lazy(() => import("./Elements/TimetableElement")); + +export type Element = { + id: string; + component: React.LazyExoticComponent>; // Use LazyExoticComponent for lazy-loaded components + importance?: number; +}; + +export const Elements: Element[] = [ { - id: "timetable", + id: "timetable", component: TimetableElement, }, { - id: "grades", + id: "grades", component: GradesElement, }, { - id: "attendance", + id: "attendance", component: AttendanceElement, }, { - id: "homeworks", + id: "homeworks", component: HomeworksElement, - } + }, ]; - -export type Element = { - id: string - component: React.FC - importance?: number -}; diff --git a/src/views/account/Home/ModalContent.tsx b/src/views/account/Home/ModalContent.tsx index bd14f18e7..c7b63b8f3 100644 --- a/src/views/account/Home/ModalContent.tsx +++ b/src/views/account/Home/ModalContent.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState, useMemo } from "react"; +import React, { useCallback, useEffect, useState, useMemo, memo } from "react"; import { NativeList, NativeText } from "@/components/Global/NativeComponents"; import Reanimated, { FadeInUp, FadeOutDown, LinearTransition } from "react-native-reanimated"; import { Sparkles, X } from "lucide-react-native"; @@ -169,4 +169,4 @@ const ModalContent: React.FC = ({ navigation, refresh, endRef ); }; -export default ModalContent; +export default memo(ModalContent); From 3a81cb2dac698448955221bd41f93aa139f9b1be Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Sun, 30 Mar 2025 16:13:14 +0200 Subject: [PATCH 1071/1144] fix(Chat): disable send button when text is empty or account is EcoleDirecte --- src/views/account/Chat/Modals/Chat.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/views/account/Chat/Modals/Chat.tsx b/src/views/account/Chat/Modals/Chat.tsx index d6a3dddeb..84f90ace2 100644 --- a/src/views/account/Chat/Modals/Chat.tsx +++ b/src/views/account/Chat/Modals/Chat.tsx @@ -63,6 +63,11 @@ const Chat: Screen<"Chat"> = ({ navigation, route }) => { const [text, setText] = useState(""); const [recipients, setRecipients] = useState([]); const [chatTheme, setActualTheme] = useState(); + const [disabled, setDisabled] = useState(false); + + useEffect(() => { + setDisabled(text.trim() === "" || account.service === AccountService.EcoleDirecte); + }, [text]); const creatorName = route.params.handle.creator === account.name ? route.params.handle.recipient : route.params.handle.creator; const backgroundImage = theme.dark @@ -373,7 +378,7 @@ const Chat: Screen<"Chat"> = ({ navigation, route }) => { > = ({ navigation, route }) => { onPress={() => { sendMessageInChat(account, route.params.handle, text); }} + disabled={disabled} > - + From 0763068b589e386a8a3b1042b4b616656192ac25 Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Sun, 30 Mar 2025 16:13:31 +0200 Subject: [PATCH 1072/1144] fix(ecoledirecte): update chat recipient to use account name instead of sender --- src/services/ecoledirecte/chats.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/ecoledirecte/chats.ts b/src/services/ecoledirecte/chats.ts index 3fc2dfd5c..72a0430b5 100644 --- a/src/services/ecoledirecte/chats.ts +++ b/src/services/ecoledirecte/chats.ts @@ -12,7 +12,7 @@ export const getChats = async (account: EcoleDirecteAccount): Promise => return chats.chats.map((chat) => ({ id: chat.id.toString(), subject: chat.subject, - recipient: chat.sender, + recipient: account.name, creator: chat.sender, read: chat.read, date: chat.date From 55aa18cee8907d91d86fba684974cf47a2909590 Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Sun, 30 Mar 2025 16:13:35 +0200 Subject: [PATCH 1073/1144] fix(chats): update getChatRecipients to return chat creator and account name for EcoleDirecte --- src/services/chats.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/services/chats.ts b/src/services/chats.ts index 51baa0760..cec2debed 100644 --- a/src/services/chats.ts +++ b/src/services/chats.ts @@ -37,7 +37,16 @@ export const getChatRecipients = async (account: T, chat: Ch } case AccountService.EcoleDirecte: { // TODO - return []; + return [{ + id: account.localID, + name: account.name, + class: null + }, + { + id: chat.creator, + name: chat.creator, + class: null + }]; } case AccountService.PapillonMultiService: { const service = getFeatureAccount(MultiServiceFeature.Chats, account.localID); From 35a45afde1eb4162dc86bdb8095603bf1db5d63f Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Sun, 30 Mar 2025 16:18:27 +0200 Subject: [PATCH 1074/1144] fix(Chat): extend supported accounts to include EcoleDirecte for Discussions --- src/views/account/Chat/Messages.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Chat/Messages.tsx b/src/views/account/Chat/Messages.tsx index 5beb69faa..bfdeba1d0 100644 --- a/src/views/account/Chat/Messages.tsx +++ b/src/views/account/Chat/Messages.tsx @@ -61,7 +61,7 @@ const Discussions: Screen<"Discussions"> = ({ navigation, route }) => { const [chats, setChats] = useState(null); const [refreshing, setRefreshing] = useState(false); - const supported = account.service === AccountService.Pronote; + const supported = account.service === AccountService.Pronote || AccountService.EcoleDirecte; const enabled = supported && account.instance?.user.authorizations.tabs.includes(TabLocation.Discussions); From 8d204c45b8cad9a07a493b94f3c08d0c72ed8cc3 Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Sun, 30 Mar 2025 16:49:33 +0200 Subject: [PATCH 1075/1144] feat(timetable): add getCourseRessources function to fetch course resources based on account service --- src/services/timetable.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/services/timetable.ts b/src/services/timetable.ts index 2f553d746..0aa25d948 100644 --- a/src/services/timetable.ts +++ b/src/services/timetable.ts @@ -5,7 +5,8 @@ import { checkIfSkoSupported } from "./skolengo/default-personalization"; import { error, log } from "@/utils/logger/logger"; import {MultiServiceFeature} from "@/stores/multiService/types"; import {getFeatureAccount} from "@/utils/multiservice"; -import { WeekFrequency } from "./shared/Timetable"; +import { TimetableRessource, WeekFrequency } from "./shared/Timetable"; +import { TimetableClass } from "./shared/Timetable"; /** * Updates the state and cache for the timetable of given week number. @@ -75,4 +76,14 @@ export async function getWeekFrequency (account: T, epochWee default: return null; } +} + +export async function getCourseRessources (account: T, course: TimetableClass): Promise { + switch (account.service) { + case AccountService.Pronote: + const { getCourseRessources } = await import("./pronote/timetable"); + return await getCourseRessources(account, course); + default: + return []; + } } \ No newline at end of file From 93b135e7caf61de3568d797ba03a628da9bd6c3d Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Sun, 30 Mar 2025 16:49:43 +0200 Subject: [PATCH 1076/1144] feat(timetable): add getCourseRessources function to retrieve resources for a specific course --- src/services/pronote/timetable.ts | 48 ++++++++++++++++--------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/src/services/pronote/timetable.ts b/src/services/pronote/timetable.ts index 0e8c86806..f2e9e1df3 100644 --- a/src/services/pronote/timetable.ts +++ b/src/services/pronote/timetable.ts @@ -1,5 +1,5 @@ import type { PronoteAccount } from "@/stores/account/types"; -import { TimetableClassStatus, WeekFrequency, type Timetable, type TimetableClass } from "../shared/Timetable"; +import { TimetableClassStatus, TimetableRessource, WeekFrequency, type Timetable, type TimetableClass } from "../shared/Timetable"; import { ErrorServiceUnauthenticated } from "../shared/errors"; import pronote from "pawnote"; import { info } from "@/utils/logger/logger"; @@ -70,28 +70,6 @@ export const getTimetableForWeek = async (account: PronoteAccount, weekNumber: n let timetable_formatted = timetable.classes.map(decodeTimetableClass); - await Promise.all( - timetable_formatted.map(async (c) => { - if (c.type === "lesson" && c.ressourceID) { - let ressource = (await pronote.resource(account.instance!, c.ressourceID)).contents; - c.ressource = ressource.map((r) => { - let category = category_match[r.category]; - return { - title: r.title, - description: r.description, - category, - files: r.files.map((f) => { - return { - name: f.name, - url: f.url - }; - }) - }; - }); - } - }) - ); - return timetable_formatted; }; @@ -128,4 +106,28 @@ export const getWeekFrequency = (account: PronoteAccount, weekNumber: number): W freqLabel: frequency.label, num: frequency.fortnight }; +}; + +export const getCourseRessources = async (account: PronoteAccount, course: TimetableClass): Promise => { + let ressources: TimetableRessource[] = []; + + if (course.type === "lesson" && course.ressourceID) { + let ressource = (await pronote.resource(account.instance!, course.ressourceID)).contents; + ressources = ressource.map((r) => { + let category = category_match[r.category]; + return { + title: r.title, + description: r.description, + category, + files: r.files.map((f) => { + return { + name: f.name, + url: f.url + }; + }) + }; + }); + } + + return ressources; }; \ No newline at end of file From 308e2ddf61d474bd0b89e7987bb6fa98d80ffed4 Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Sun, 30 Mar 2025 16:49:50 +0200 Subject: [PATCH 1077/1144] refactor(timetable): separate resource interface for better structure and clarity --- src/services/shared/Timetable.ts | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/services/shared/Timetable.ts b/src/services/shared/Timetable.ts index ea4f0e2fb..87ccb13fe 100644 --- a/src/services/shared/Timetable.ts +++ b/src/services/shared/Timetable.ts @@ -16,16 +16,17 @@ export interface TimetableClass { statusText?: string, source?: string url?: string, - ressourceID?: string, - ressource?: { - title?: string, - description?: string, - category?: string, - files?: Array<{ - name: string, - url: string - }> - }[] + ressourceID?: string +} + +export interface TimetableRessource { + title?: string, + description?: string, + category?: string, + files?: Array<{ + name: string, + url: string + }> } export type Timetable = Array; From 78716bff31bd0d4000712f716c6be43033d2c695 Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Sun, 30 Mar 2025 16:50:00 +0200 Subject: [PATCH 1078/1144] feat(lesson): integrate getCourseRessources to fetch resources for lessons --- ios/Papillon.xcodeproj/project.pbxproj | 4 +-- ios/Podfile.lock | 10 ++++---- src/views/account/Lessons/Document.tsx | 35 +++++++++++++++----------- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/ios/Papillon.xcodeproj/project.pbxproj b/ios/Papillon.xcodeproj/project.pbxproj index a36919e50..bbfe9f05e 100644 --- a/ios/Papillon.xcodeproj/project.pbxproj +++ b/ios/Papillon.xcodeproj/project.pbxproj @@ -457,7 +457,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = "Papillon"; + PRODUCT_NAME = Papillon; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -490,7 +490,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = "Papillon"; + PRODUCT_NAME = Papillon; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/ios/Podfile.lock b/ios/Podfile.lock index e1a683ed5..ca4d2a4a5 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -355,9 +355,9 @@ PODS: - FBLazyVector (0.74.7) - fmt (9.1.0) - glog (0.3.5) - - hermes-engine (0.74.6): - - hermes-engine/Pre-built (= 0.74.6) - - hermes-engine/Pre-built (0.74.6) + - hermes-engine (0.74.7): + - hermes-engine/Pre-built (= 0.74.7) + - hermes-engine/Pre-built (0.74.7) - lottie-ios (4.4.1) - lottie-react-native (6.7.2): - DoubleConversion @@ -2186,10 +2186,10 @@ SPEC CHECKSUMS: FBLazyVector: 04dc972982abebd96d823752c3a426bbe6ac397f fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120 glog: fdfdfe5479092de0c4bdbebedd9056951f092c4f - hermes-engine: 2102c92e54a031a270fd1fe84169ec8a0901b7bd + hermes-engine: 21ea4e6a0b64854652c8c20cb815efdbb3131fdd lottie-ios: e047b1d2e6239b787cc5e9755b988869cf190494 lottie-react-native: 45707364bd70cffa7602fa1a1abb40dee5f3c0e0 - RCT-Folly: 02617c592a293bd6d418e0a88ff4ee1f88329b47 + RCT-Folly: 5dc73daec3476616d19e8a53f0156176f7b55461 RCTDeprecation: c4e6e3f6d44f76c45a964b40fa3eb2475259c027 RCTRequired: c4886806a178cd895cd4a17dae1642b72e7e8233 RCTTypeSafety: 4e9f36465ccbcca7b62f5cb8fc1ff2c997c3b92e diff --git a/src/views/account/Lessons/Document.tsx b/src/views/account/Lessons/Document.tsx index 026448abe..329fa9e3c 100644 --- a/src/views/account/Lessons/Document.tsx +++ b/src/views/account/Lessons/Document.tsx @@ -29,20 +29,20 @@ import { User2, Users, } from "lucide-react-native"; - import * as WebBrowser from "expo-web-browser"; import { LinearGradient } from "expo-linear-gradient"; import { useTheme } from "@react-navigation/native"; import HTMLView from "react-native-htmlview"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { PapillonModernHeader } from "@/components/Global/PapillonModernHeader"; -import { TimetableClass } from "@/services/shared/Timetable"; +import { TimetableClass, TimetableRessource } from "@/services/shared/Timetable"; import { ClassSubject } from "pawdirecte"; import { useClassSubjectStore } from "@/stores/classSubject"; import { useCurrentAccount } from "@/stores/account"; import { AccountService } from "@/stores/account/types"; import getAndOpenFile from "@/utils/files/getAndOpenFile"; import { getDuration } from "@/utils/format/course_duration"; +import { getCourseRessources } from "@/services/timetable"; const LessonDocument: Screen<"LessonDocument"> = ({ route, navigation }) => { const theme = useTheme(); @@ -58,6 +58,7 @@ const LessonDocument: Screen<"LessonDocument"> = ({ route, navigation }) => { const lesson = route.params.lesson as unknown as TimetableClass; const subjects = useClassSubjectStore(); const [classSubjects, setClassSubjects] = useState([]); + const [ressource, setRessource] = useState(undefined); const account = useCurrentAccount((store) => store.account!); const openUrl = (url: string) => { @@ -75,16 +76,22 @@ const LessonDocument: Screen<"LessonDocument"> = ({ route, navigation }) => { }; useEffect(() => { - setClassSubjects( - subjects.subjects.filter( - (b) => - new Date(b.date).getUTCDate() === - new Date(lesson.startTimestamp).getUTCDate() && - new Date(b.date).getUTCMonth() === - new Date(lesson.startTimestamp).getUTCMonth() && - lesson.subject === b.subject, - ) ?? [], - ); + const fetchData = async () => { + const ressource = await getCourseRessources(account, lesson); + setRessource(ressource); + setClassSubjects( + subjects.subjects.filter( + (b) => + new Date(b.date).getUTCDate() === + new Date(lesson.startTimestamp).getUTCDate() && + new Date(b.date).getUTCMonth() === + new Date(lesson.startTimestamp).getUTCMonth() && + lesson.subject === b.subject, + ) ?? [], + ); + }; + + fetchData(); }, []); const [subjectData, setSubjectData] = useState({ @@ -266,7 +273,7 @@ const LessonDocument: Screen<"LessonDocument"> = ({ route, navigation }) => { ); })} - {(classSubjects.length > 0 || (lesson.ressource?.length ?? 0) > 0) && ( + {(classSubjects.length > 0 || (ressource?.length ?? 0) > 0) && ( @@ -293,7 +300,7 @@ const LessonDocument: Screen<"LessonDocument"> = ({ route, navigation }) => { ); })} - {lesson.ressource?.map((r, index) => { + {ressource?.map((r, index) => { let title = (r.title?.charAt(0).toUpperCase() ?? "") + (r.title?.slice(1) ?? ""); // S'assurer que la première lettre est en majuscule let desc = r.description?.replace("\n\n", "\n").trim() ?? ""; // Remplacer les doubles sauts de ligne par un seul let descText = desc.replace(/<[^>]*>/g, "").trim(); // Il peut arriver que le contenu soit vide, mais qu'il y ait du html tout de même From 0126b73f49927ccf05a710ae5bc4518bd2368916 Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Sun, 30 Mar 2025 16:55:05 +0200 Subject: [PATCH 1079/1144] feat(lesson): add loading indicator for resource fetching in LessonDocument --- src/views/account/Lessons/Document.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/views/account/Lessons/Document.tsx b/src/views/account/Lessons/Document.tsx index 329fa9e3c..8b282f2b3 100644 --- a/src/views/account/Lessons/Document.tsx +++ b/src/views/account/Lessons/Document.tsx @@ -12,6 +12,7 @@ import { Platform, Linking, StyleSheet, + ActivityIndicator, } from "react-native"; import { getSubjectData } from "@/services/shared/Subject"; import { Screen } from "@/router/helpers/types"; @@ -58,6 +59,7 @@ const LessonDocument: Screen<"LessonDocument"> = ({ route, navigation }) => { const lesson = route.params.lesson as unknown as TimetableClass; const subjects = useClassSubjectStore(); const [classSubjects, setClassSubjects] = useState([]); + const [loading, setLoading] = useState(true); const [ressource, setRessource] = useState(undefined); const account = useCurrentAccount((store) => store.account!); @@ -89,6 +91,8 @@ const LessonDocument: Screen<"LessonDocument"> = ({ route, navigation }) => { lesson.subject === b.subject, ) ?? [], ); + + setLoading(false); }; fetchData(); @@ -273,6 +277,13 @@ const LessonDocument: Screen<"LessonDocument"> = ({ route, navigation }) => { ); })} + {loading && ( + + )} {(classSubjects.length > 0 || (ressource?.length ?? 0) > 0) && ( From 559267e13f515965de9b0ac74e1e614aa7be1c63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl?= <41128238+raphckrman@users.noreply.github.com> Date: Sun, 30 Mar 2025 16:57:31 +0200 Subject: [PATCH 1080/1144] fix: supported MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 𝕂𝕪𝕝𝕚𝕒𝕟 <164187100+Kgeek33@users.noreply.github.com> --- src/views/account/Chat/Messages.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Chat/Messages.tsx b/src/views/account/Chat/Messages.tsx index bfdeba1d0..f861c693c 100644 --- a/src/views/account/Chat/Messages.tsx +++ b/src/views/account/Chat/Messages.tsx @@ -61,7 +61,7 @@ const Discussions: Screen<"Discussions"> = ({ navigation, route }) => { const [chats, setChats] = useState(null); const [refreshing, setRefreshing] = useState(false); - const supported = account.service === AccountService.Pronote || AccountService.EcoleDirecte; + const supported = account.service === AccountService.Pronote || account.service === AccountService.EcoleDirecte; const enabled = supported && account.instance?.user.authorizations.tabs.includes(TabLocation.Discussions); From 2f302b05831e8eab9065b6ad852cbf229d72bc66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl?= <41128238+raphckrman@users.noreply.github.com> Date: Sun, 30 Mar 2025 17:12:09 +0200 Subject: [PATCH 1081/1144] Update src/views/account/Chat/Modals/Chat.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/views/account/Chat/Modals/Chat.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/views/account/Chat/Modals/Chat.tsx b/src/views/account/Chat/Modals/Chat.tsx index 84f90ace2..2cb5e4426 100644 --- a/src/views/account/Chat/Modals/Chat.tsx +++ b/src/views/account/Chat/Modals/Chat.tsx @@ -67,8 +67,7 @@ const Chat: Screen<"Chat"> = ({ navigation, route }) => { useEffect(() => { setDisabled(text.trim() === "" || account.service === AccountService.EcoleDirecte); - }, [text]); - + }, [text, account.service]); const creatorName = route.params.handle.creator === account.name ? route.params.handle.recipient : route.params.handle.creator; const backgroundImage = theme.dark ? { uri: `${chatTheme?.darkModifier.chatBackgroundImage}` } From c62d6285928af86fc992354fd8096c4bd8259b49 Mon Sep 17 00:00:00 2001 From: godetremy Date: Wed, 2 Apr 2025 16:47:32 +0200 Subject: [PATCH 1082/1144] feat(App): refactor font loading for dyslexic --- App.tsx | 172 +++++++++---------------- assets/fonts/OpenDyslexic-YXZyaWw=.ttf | Bin 0 -> 126364 bytes src/consts/Fonts.ts | 21 +++ 3 files changed, 85 insertions(+), 108 deletions(-) create mode 100755 assets/fonts/OpenDyslexic-YXZyaWw=.ttf create mode 100644 src/consts/Fonts.ts diff --git a/App.tsx b/App.tsx index f6466ab23..b9f056594 100644 --- a/App.tsx +++ b/App.tsx @@ -1,140 +1,90 @@ -import "@/background/BackgroundTasks"; import Router from "@/router"; import { useFonts } from "expo-font"; import * as SplashScreen from "expo-splash-screen"; -import { LogBox, AppState } from "react-native"; -import React, { useEffect, useState, useCallback } from "react"; +import { LogBox, AppState, AppStateStatus } from "react-native"; +import React, { useEffect, useState, useRef, useCallback } from "react"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { useAccounts, useCurrentAccount } from "@/stores/account"; -import { AccountService } from "@/stores/account/types"; +import {AccountService, PrimaryAccount} from "@/stores/account/types"; import { log } from "@/utils/logger/logger"; import { isExpoGo } from "@/utils/native/expoGoAlert"; import { atobPolyfill, btoaPolyfill } from "js-base64"; import { registerBackgroundTasks } from "@/background/BackgroundTasks"; -import { SoundHapticsProvider } from "@/hooks/Theme_Sound_Haptics"; -import { PapillonNavigation } from "@/router/refs"; -import * as Device from "expo-device"; -import * as ScreenOrientation from "expo-screen-orientation"; +import {getToLoadFonts} from "@/consts/Fonts"; SplashScreen.preventAutoHideAsync(); const DEFAULT_BACKGROUND_TIME = 15 * 60 * 1000; // 15 minutes -const BACKGROUND_LIMITS = { +const BACKGROUND_LIMITS: Partial> = { [AccountService.EcoleDirecte]: 15 * 60 * 1000, // 15 minutes [AccountService.Pronote]: 5 * 60 * 1000, // 5 minutes - [AccountService.Skolengo]: 60 * 60 * 1000, // 1 heure + [AccountService.Skolengo]: 12 * 60 * 60 * 1000, // 12 heures }; export default function App () { - const [appState, setAppState] = useState(AppState.currentState); - const currentAccount = useCurrentAccount((store) => store.account); + const [appState, setAppState] = useState(AppState.currentState); + const backgroundStartTime = useRef(null); const switchTo = useCurrentAccount((store) => store.switchTo); - const accounts = useAccounts((store) => store.accounts).filter(account => !account.isExternal); + const accounts: PrimaryAccount[] = useAccounts((store) => store.accounts) + .filter(account => !account.isExternal) as PrimaryAccount[]; - const [fontsLoaded] = useFonts({ - light: require("./assets/fonts/FixelText-Light.ttf"), - regular: require("./assets/fonts/FixelText-Regular.ttf"), - medium: require("./assets/fonts/FixelText-Medium.ttf"), - semibold: require("./assets/fonts/FixelText-SemiBold.ttf"), - bold: require("./assets/fonts/FixelText-Bold.ttf"), - }); + const [fontsLoaded, fontError] = useFonts(getToLoadFonts()); - useEffect(() => { - const configureOrientation = async () => { - try { - const deviceType = await Device.getDeviceTypeAsync(); - if (deviceType === Device.DeviceType.TABLET) { - await ScreenOrientation.unlockAsync(); - } else { - await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT_UP); - } - } catch (error) { - log(`Error during orientation lock: ${error}`, "Orientation/App"); - await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT_UP); - } - }; - - configureOrientation(); - }, []); - - const handleNotificationPress = async (notification: any) => { - if (notification?.data) { - const accountID = notification.data.accountID; - if (accountID) { - useAccounts.getState().setLastOpenedAccountID(accountID); - - setTimeout(() => { - PapillonNavigation.current?.navigate( - notification.data.page, - notification.data.parameters, - ); - }, 1000); - } - } - }; - - const checkInitialNotification = async () => { - const notifee = (await import("@notifee/react-native")).default; - const initialNotification = await notifee.getInitialNotification(); - if (initialNotification) { - await handleNotificationPress(initialNotification.notification); - } - }; - - const getBackgroundTimeLimit = useCallback((service: keyof typeof BACKGROUND_LIMITS) => { + const getBackgroundTimeLimit = useCallback((service: AccountService): number => { return BACKGROUND_LIMITS[service] ?? DEFAULT_BACKGROUND_TIME; }, []); const handleBackgroundState = useCallback(async () => { - const savedTimestamp = await AsyncStorage.getItem("@background_timestamp"); - if (!savedTimestamp || !currentAccount) return; - - const timeInBackground = Date.now() - parseInt(savedTimestamp, 10); - const timeLimit = - currentAccount.service in BACKGROUND_LIMITS - ? getBackgroundTimeLimit(currentAccount.service as keyof typeof BACKGROUND_LIMITS) - : DEFAULT_BACKGROUND_TIME; - - const timeInBackgroundSeconds = Math.floor(timeInBackground / 1000); - if (timeInBackground >= timeLimit) { - log( - `⚠️ Refreshing current account ${currentAccount.studentName.first} after ${timeInBackgroundSeconds}s in background`, - "RefreshToken" - ); + try { + if (!backgroundStartTime.current) return; + + const timeInBackground = Date.now() - backgroundStartTime.current; + await AsyncStorage.setItem("@background_timestamp", Date.now().toString()); + for (const account of accounts) { - if (account.localID === currentAccount.localID) { - await switchTo(account).catch((error) => { - log(`Error during switchTo: ${error}`, "RefreshToken"); - }); - break; + const timeLimit = getBackgroundTimeLimit(account.service); + const timeInBackgroundSeconds = Math.floor(timeInBackground / 1000); + const serviceName = AccountService[account.service]; + + log(`Checking account ${account.studentName.first} ${account.studentName.last}:`, "RefreshToken"); + log(`Time in background: ${timeInBackgroundSeconds}s`, "RefreshToken"); + log(`Time limit: ${timeLimit / 1000}s`, "RefreshToken"); + log(`Account type: ${serviceName}`, "RefreshToken"); + log(`Using ${BACKGROUND_LIMITS[account.service] ? "specific" : "default"} time limit`, "RefreshToken"); + + if (timeInBackground >= timeLimit) { + log(`⚠️ Refreshing account ${account.studentName.first} ${account.studentName.last} after ${timeInBackgroundSeconds}s in background`, "RefreshToken"); + + // Prevent React state updates during render + setTimeout(() => { + switchTo(account).catch((error) => { + log(`Error during switchTo: ${error}`, "RefreshToken"); + }); + }, 0); + + // Wait before processing next account + await new Promise(resolve => setTimeout(resolve, 1000)); } } - await new Promise(resolve => setTimeout(resolve, 1000)); + } catch (error) { + log(`Error handling background state: ${error}`, "RefreshToken"); } - await AsyncStorage.removeItem("@background_timestamp"); - }, [currentAccount, switchTo, getBackgroundTimeLimit]); - - - useEffect(() => { - if (!isExpoGo()) checkInitialNotification(); - }, []); + }, [accounts, switchTo, getBackgroundTimeLimit]); useEffect(() => { - const subscription = AppState.addEventListener("change", async (nextAppState) => { + const subscription = AppState.addEventListener("change", async (nextAppState: AppStateStatus) => { if (appState === nextAppState) return; if (nextAppState === "active") { - if (!isExpoGo()) { - const notifee = (await import("@notifee/react-native")).default; - await notifee.setBadgeCount(0); - await notifee.cancelAllNotifications(); - } + log("🔄 App is active", "AppState"); await handleBackgroundState(); + backgroundStartTime.current = null; } else if (nextAppState.match(/inactive|background/)) { - const now = Date.now(); - await AsyncStorage.setItem("@background_timestamp", now.toString()); + log("App in background", "AppState"); + backgroundStartTime.current = Date.now(); } + setAppState(nextAppState); }); @@ -151,22 +101,28 @@ export default function App () { "[Reanimated] Property ", ]); - if (!isExpoGo()) registerBackgroundTasks(); + if (!isExpoGo()) { + registerBackgroundTasks(); + }; + }, []); + const applyGlobalPolyfills = useCallback(() => { const encoding = require("text-encoding"); Object.assign(global, { TextDecoder: encoding.TextDecoder, TextEncoder: encoding.TextEncoder, atob: atobPolyfill, - btoa: btoaPolyfill, + btoa: btoaPolyfill }); }, []); - if (!fontsLoaded) return null; + useEffect(() => { + applyGlobalPolyfills(); + }, [applyGlobalPolyfills]); + + if (!fontsLoaded && !fontError) { + return null; + } - return ( - - - - ); -} \ No newline at end of file + return ; +} diff --git a/assets/fonts/OpenDyslexic-YXZyaWw=.ttf b/assets/fonts/OpenDyslexic-YXZyaWw=.ttf new file mode 100755 index 0000000000000000000000000000000000000000..d17e1be6c4e3848b0c172e4558b521ea6b1994b3 GIT binary patch literal 126364 zcmd44cYIvM)jvE}+k5ZrZtqRID`}Z`DF5rf{X|};M(@gJRO6Wxf z5=kfL`zyuJ zX^aK*jLE|*hc<0sR;Iu`uw(j_Yj&)7dhm^t8C!cF;}&1MYWdpjzN@AGVyv{Bv9>9z zMuwKZ%x_wX@l$BuvI-T3E95WZ8uwXf)!NP5-^hMEiLulg@c(Sh`r)B_ZkU7ny3Ls2 z8Ec2OZ;*^AHlY1^XwR=3T07GA%ff2T{D$?LHveeX&{d3kwSqqF8#az?sO29x zn=!W?^Z6SiUE!E<_>p}l=qi6@b~!2-yYHpm4mv-y_M>;|U7zn(oFRV`mx{);P(mO1 zo1fo{s!!`(U;JKihQNusjHRX;)qvi>WNZzSpr@X7vO&~6C=W9c?sB4MxP7?8B-cp_ zU>R88Lj`D(a^WCGPNK}wXHAZ7npy@;9F?6I<#Y1oRfvV^JGrm?b=so2i3 zvWykjUYe0u1U_o^lu&NW_f(bF8dD`NpxblH1Vq z#8_Fya^mDzSPnU4*}`Fy!t;s;g_Z(P4={ff#20^=nrT^K*vQZQ=*!%pO^}WzEpWuXZ25 z`?YrSM>cK(N4`0e&j~lE|G=EDv{}80AL2J}99llIc4*^C{Q4FDMJ6_#t!EqAS~dhX zThErU9h`=ZuoKxj_A4A;-kZZVqs=!z_+-o|TbHC=KB5Fxsq#E3AiWP%G-W5q*gcp=LEo>sSVNCPJn)IDg4= zlW;YH-Xo~p3c4IEiNjifGtcG-Qc}AClxL&GO11@DNb3JK`oH)6@BHQq<8B(0k!Q^q zJit>C!vnC@_(nN|28YqX@<>*tm~lj5zujM zuS1!}8(}>-Xf`WRL%QS%0pB=&RFL|A=axdx&9HrAhF{Ke1x6zYi2IOWI~s|s+-OgC z*5LkTWU?zsyb^UQP$tXr=>4UINmnazoFcR%`3Wm?kBvJljIjlju(J%-r+E|qmFTfd zC=u^*nN|tyd1#A9Yha2O&Km_?5HdF$!w}E5feX#DVMDqi3sMi#>1tsfq{D_KH{hE3 z)A$*|FE-<|gbNn&|f=bm@|-U}|g=;BNEU3%H&S6q43)z@5m-Ss!z zc+<`MZ@Klh+rM?kop;@R&%O8E|GTBQs!4F@5<4yL~kKTUg-5>wtz4s3t{@}wSN10f>2Xozu{g8yUurs)w++|$7 z>E)*HHGLpDL$p_Pq39OT55;Qn4Dn*|3F1q=XM6w5TX-)Y;3IsFFYxVrg|G2F{7(LM z{!acrzr=6yTm1ol#Gmmm2{Z+y0bRftumqd|Zy*_%5?C4-3EvXFH~eb&_3$tLAo*t# z_E?PZY(K{RN7G9f_a_*455~P%beotHYsIs~OT_!UyS#tmO}vfgg>jqt^09Gm`;T!K zjg4!-xQ=7vF2}g{jE&3v6MMV*$Bfm@Wa%%k+x^#moNaXV`8%J#{rQhRf9vyGKh}M8 z8`Us&^z5To{Zt29|LMY`+mAklnyZc)j@FLmf9gAO;*k%Io^*7=hgW_0#fKL#_Tim4 zHh*~XhbMfP{V;d<%EK4E|NFxy9-hmX=)y6rX=7uYaoY5F)6-4g zAHNzuH$6LE`uB6wJ>Y`lKU32+Lfw7rHnxYI-PFOZV!vePu#4FR>_&DsyB&MRz3_yy z*yZf^><{cBb~QVXJ{NC-JB=M=w_tyK2HVAUv%j$4vd6he++Ijpo9pD(a_hMD+y?GsZX@U6ylj*m;Cvj46K zKjXq&go|=9ZX36q+rgc}oywiYoz9)X#kmBR690p5cnzv)p&N64$~#$34%zz`e-5#J$Wlaj$US<6dRgbFXpV=UTZo?gw0%`yux_ z_XhVS_ZIgf?rrWJ_7HoxsmQgn@32SN3pgHO-)1keGudvovf8XZUj3vdr}<2Ky>7DZ1$|EczG0H#XU2<7%(TMv3-eTq-Ex=J zVBKR&*gmnZwI6bn90#12y2`E>-3#3hxIgpE@Z9Zr*DLj&>1*<>_x*vt261dOFg0*b z&>nm_Bn$mCye#~KNKfSHs4MzLY)0(KcyA(+cr!UU`EIH&wJr7g^gw!L`n*gsQ_0NE zY|h-6c_j04=1Au6S!LFjy(#?_&B*}vrsxoB=;Ze{M&+(o(lxr4bkb3f1hEw9W+ z^R@hq`QJD1C`>7QR16dkmIhjaEeBh#Z~aA^r|tamIqj|Ok90J5?61fxuT;CMH`m&0 zk4@;9aAT*mb6V%mC*IqY>AJD&68gm zc24{_%_-&-iqvW@dC| z$INLnSIpc#^OBk0n)&$5H)j5FR?{rUtmav#&3bQk`|MS-ADsQ8Io>(P&ADgJujWpi z``A4BJomi(ysmkR=G`_wF#r1b_bt#Y*t+2E1%DZwIe6#7EytA?X%|HnUAXAmi@v+) zy+waqEM5HC@w<<|@%V2a|Kjm)FBw_#$&x=T`RoLCg5reK2@_A4w=}u*m8Cyf`pMEi z3>{cDd)cyOCoel|+2zY_UiRR!Czido?1N>09p;9O!|~w>!wZJj4DTMka`^t?7lz** z{$%*`<%;F}@|NXY%jYa#xBSfIS1-SBL^*QpiVZ7%xALNupRd}o>W$S~SD&-`vemyn z$#jzMq|!+6d3nvdYksq)zSg`pwRYm#d23HxyL;_bYwub6+}d~7 ze!5P&?xA(Bt~;{sH|y`-aQudi8_wNu!-fMJUfOVI!*5UKPPU$W^~txMeE-G?8=u)<14*+NRwW z+}65n+P0^bjj!`Zi=lRT$z z&I{-Eo;z^v`{#ai-tp(XeSX{dzutSv1?~&FE*QLE%>}zIxblL#FRWcS{~~sg{388D z(TlPdy?XKHOSG2+FKN5vsY_nmSKc>qU*EnD_Wk_Qr!QM_*&i>LUVhf)f4uzfS8!J- zt}tBTxWa!${EB<8c=(DZt}I-6-Bq!x-nn|x)r+ov;+hH99KH6^Yp=b|d)+C+>Rw?s<0~y65bBuDj>Kd*k<>eeXy2 z{`$T@-EX`VL$h37!=++8DZ|EP z@>sI%DeM(Ektjp<;~X(Aj}_U7by`SCqE;eJL^v>l{iqNiBHW6?WJpa8pkN8AkT_#R z*kfTULJ9BiK9kq+4pCnJ0`EMP_0O4-sX$ds4XS3E`oHjAPz$PMI#3F{!eCL zCQze5P0U>X2cB50pcZBWwK6-X4fOBK&YYkQ<^pvxcl|TwVqQ=;^MQI859($9`rnw3 z1wnZh0`-Ib4-2p`XplugLo8PRD+{wYXoMv|qbvy;W2yRI@cJeVnqXPbBc{4$u}>ssDksvMOjBn*ds7o%P@2 zahPZan+RHA-Jn(0Q~wn2S9?Jx2(*(;uKx}Xf>S`dSRZIN>#zS7uP})AvT2}`*Z}Bc zHlzL z5$IfYJj(Of3H4v%Wy(^}1#Ad(5cC&pAsYrgjx7gW#762rXN%bi(Bs)k&?RgY=m~6f z{by_`I|&qz+@N>~fv1mQwia|bTMvqt1fVO}hWbbJ=nlF{pm>Q=|0#CQn?X-v+d$W_ z?VxKxkK%E72k3fsD(D7wTKx$2)u)4QWM_bGVms>}Vt>60bPL-Jx|N*;x()OL?6>!T z?qKJDp2E(pAI9FB=xOXc(9_vo&@#^#Z$%2 zpqH}!pqH^*>hIu*nt zyV(<<_pm4HKV)&Hfve!YMVsC&x&E5ii2J{u| zmwyEMUG_HUbL`#v%h*N#81w~!zR2D~`6c##{Uto79R_`ceE|AB_F?@+Jf|H2eT^Lj z{XY9C=nvRO^%wBm_A%(|>}Q~FuwT@l$Mf4SLEjSSkJu;m=kN^oYtVPtZ$RH=zXkm< z`(6FJc=GrZ^gZ@_(D&IN>d)edQ_BB<9%X+6{VC|v>?8Jf z(2vwLpKviRw?V-*RHm-*HmVPdORr z??E4Df8gYxf8>;)f8td2$Jn1aHRxYB4d`DvE$IJny81yph3G*)>&dH;jhl1T7)s%Lsm z->KMn<|Sv|eCC5^?mx)sAA07@QM#e1KigUF(VHh34f4*1F2B53JLQx@?TRakwNpBwV4x( zwMu)jR<0In9eu@GZ&$H4Yeum)ZAGzm;!v^nl9jvn-b1zf?mJYw;f6!CUAqp|ZoKhO z?f&}@)gE}@Q0>7757kCT57jQd_)u;4?nAY;YY)}-?R&qrY}xy@J$v4-ZQ1gEZOM{D zwF3vnI z!uyZZPQU+f?VJaW)-HPBNNwbS!?klCJX*W%!6UWf9z0yTYxHPs=jf5zg3-gZ^DaJG z+kWwp+Tx23*VgYoTHCSvNNwKk!?iVQkJdJ?JyM&r_Hb?azN588`;OEu+4o^>aNptD z@%s+dhL;_!wJtkSyJ^{nwaLp4*V>jHs;%5}v^HVSk=o6BKCE@_Ib7@BbEsC^a-??F zmJe&Qw;ZnZY&le$vgAnZj3tL_8x9<)Z9Qf!gQhu*8c|6cWjpH$!bN%hBXSKocR`p(y7H0Z&W+C zz5ROijn}I`e69L}*Q(!tt@`TA)$hGredXoqYcEw_eyRH6bJZ7~t3Ll+^}A13pMARe z%+uB9o~l0mRP`rMR$qUz`qGotr=F|^wY>p#uV2~A%X@v&Uaz><)70x`y-vAZZk3zm zM!8{d2qe56bGZ z`$y$77Y!cZxQiFsAHx==e)dJy#wK-~+W(_4Uhz%m1Iw26uI5MC+`-Y#r3-r=y0>!r zLGIi~M#}e9PC3Y({*bcG9prj(|Hgw&osV?1 z-&h$w$Q2)|l&Ko`@2@O6$a%o%fy&@P&h&`C{9tA7K~COT7%7idW__VAUtAgZ(@1%D zW$GvP^4iLz!z1N=mF~9+lOtpIsHtp)vE^pscsXjeYCVshBHhk0k|39s> zcX6KnQT?33(IL;m{?QQ>nCF0%buIk1ZbCD=yz&@c;_P|YQ$7VHCpYx4vU~+fN^aUC z6UwJnK0nA+9C{9pq#W<;qmuxj|fB zTDj+)>E)9u`(Fu^2P&66sxQy2oOSzz^7P8qE9B+*l@rgNTb@`M+F>rYSLUorl&h7_ znN8&xm0-$HUQu!Q;^m>GbJ~Y0I@8=TT~U44(s+AUCEb=N_Z^+n-dAbqnOmk_xtWgt z7YerjKkUQxuAbAy_0JqUAcwZP7dOriH>s4y?!l!l|H7{1rcIkz=Yj?E=gplnd)CYu z(+8&Y_f45Rskf)QYhvewTD8*AUT$k`DHRLN`CK-WP9+oZSTqt21p|KG=k>TMNJ$_^#&&`U)95pa!dK%(MelZ?d@IKlTL95RLbsP_lPo`Vh5Be6jdmV#)2CT zaIqRE6q;ha?FX8eT!T4|rh+}aqsbt;f{iHLJG6Xs=B&Zq9+%(0Fr6CZx`%_yMp>|H zRF@Q*ux??PQEB(6Oc;h=J&NhD3-|-6XZK!sP|ubvO{$j%mk%u-92E_LNAIX1IT{c4 zjK)v-nLV94$lZZRr071#AtF77hnignlDm3(7NX@|QSaWp=YG9;I@LdW&<}Zny%+K% z;Os#`HZ+! z=Ip`XXj8b2$VeO8JKP4%AT8uD_G-*y>0W(1WEqu&^+A5`Ul|M%{M~QAUNbaSBMs~S z%IGGU?U?CD@y>e;2BXR3Xd*!tlXXMIkgO(Lm(r=Nqv_xVJwKZ6g-|mG(P?3O7W(x2 zN!u43>}1PuF}iEkVB;DewYnZ+o!R8V(Wa$z=h^W)mU(n%*Z7@d-IoRdt4D;7L@cB7 z$gv-t-fHSy)jrBu|Cie%jr)LcFMmKR3Gbab7#Z4ofh)3f?}ZD2u1WB&y?ZAG`AK`1 z?j1T<-?c2r>w|j_q|NyRdHEMfD&E2DH>F`*u(QGgJ(q#9NJCK0HD>w^?F4T`6DDtj{ z3wYFCNOFu?yWt9v-o1m%SB(0Wx|Sngtl$S-{?X2bWX9m&$U<_Epnf!dbPQc%y)d_b zYdzi=(;k(D<@{iiOSF){hYEfYNU*Dd8~Rac1%v&Aqsa- zL1&!NwR;KzuYh+7Il@G~NhDWe0uW@;MbVq zLJ)9_+c(SEM7kJL21JmfOYan#MLN}lGLPGGxN|qvHLPeS0P=&uk>J80Fw{A7kc>mR z7VO(UC)hvhxIw|%VA{r;NSWjl*zx+>0td2)Jr&h*==kVqwUEt zIU7=sc6{ynYtpIriXg8z($qiqxV?LoU%U5TJo>SwJDcwOx=%k2pPqE#LQF+)YsT;G z8)Xo;69Kc$SZql1U)UY!@Bd$!ao@xYum7YgaA+Hc*n&52jT9R@3-`aNJYM%7=KyNx z_~!Hqp6_Sk9Z?UCnRv(WH1ZC-Q=|GeJcGZ2cNRT(w{ZpD{yhqs!rc_w8Bq5L-qw79 zXZ$NUIq08o+=KS-Fq6>xEzX2KPvhO>*+R`>aJv$H_OnOv+PfE5kDz=$`h9|Rq=HXA zJ?Q@h?ob<&#DmXH<+y((cP>8P#Fa3{TX-k=Ft-Qq$t&y~eBvp?r;KuTK0ZG!V%(>} z@qE06S%}YGd+8oH7IANk16qCo?kc?T`p-0hTk@Zk|G%nnFov1j-ZY`1QWAG=&qVf`X}-rV#l+enAF%Ce%L zuv}9sdr35pt>?aAmx%3buJ|KX6u>pyUZGr4(qt9l^&)N0(-P}v%v)BcaS6Pq5!aTUw zBNDMu@d|c8IN!lul8Each&Qn-F^_`y8FsDM%~p#aW-G1VgusIY@0aFD$vaZqMx(4=tVXW*K0*TV>QVRHck8( ztBMo#e~G8FQ=mtw=mc(v&f-_ta$(+>JI$NsJU%CqA2M})<3Tc#d|x>r-%=cordoX? zj&sIjn*;g2en2jg?bz`WD~Kks9`V1}>C%NPEHSe!A_sd^auJ&>x{zh0$FoH^j)T4j zpr?0md_cC7KZJY3Y>xCHb~#~!Y>Fc&v9hzl+bTA)$0Yw?X;}|jEs=unwQQ!im5qq5 zVKLbQY@)26bwjoi*fDYsG(sT^F%s(7TBa$x8MRVRT$qWOKzu>`Ymj z4T#QTDah6>ia_27Tg9Es25^)~CdtdN$)@_h$oA6HnN$2x{YQ}TL^>qBz|#a^^Ci|! zI2Lf(&t@q;1@6fnu*LXUz#{NR7#u%HhVcWox^?^@oIpp<;kZYj^idt z3FD3(gfHChCcl*Zy5TnhcFAwx6NF0vV}u>T+FQbTCE|NhAas|j}84D zcT9i(rcWU@B0fn3i~&C`&W2+F9byC5KBkMVabL$+CBnvdzkffE^MRkOKrERn9%0pS ze2$-i{~g3vI+I;2`+z+!)xdv6>;&l#5G$Tz6QLu)|Ae)Gd=NGtkN>0_$sVR3kNuM8 z*?iIg#TfDt#G6wE>`<(yn7>kDU67vb|sNZ9Z7eB~4r54sE#96ANHaJtA5W|1K5`;~{5ZQ!e5afW3@mDGi*_$jU zeTgL`26hX{CV3ZfQ_Oi6YZk46gzNCwdT^9^#jmq2$v@d{;h;Xh0uNgMur7Xe{=#^) z_7P9`1nEGUgAe|Jt(JANVcA`5SZZbw`E~Fs8mqzv6)H9he&`g|zY^9eIXg@AJUd7H zH1m^hOSZBu(H_K0I~%4$^ase1W?sormJ*+Wc|E~;as9Ti2EtE-wGXjKdMcYItb2cE z2gHcA(lk3sI>=6f-x;KH*e=*&m)OPD$l8Dt6$=P)$fi$qdLLo!2B$gd@V1VI!SN)EZC$HWrJC32}4%^_D~UU>hf zf;@5zg9@oiA(laIWO_G4M zg$&&Z8Qn?x21nuvj*zL5p+-^@t~3g%NR5vVs0Q;TSs{{2u2iVRWCE~78NUk+)sjJ= zB(TLHgW($5LpA0W*S{NIpOaGZ74mP6Q3i8_gkr&Wuh;-G0{!Orqy)>jfE@Rv)ZS}oxRtprwMfM~!1&1GODWQdRUq6SCe zNlnK2sWDA>3~IE>ar}TCyoY9_P^c9urCJQ9P!JR_JEclWS0pKkK?h+*NbpoDE1()O zmRzmIXIaz~22_$4V~Xfcyul9W(#Y_M7j-54KoS_a!I$){Ko_YL7N9|Z1%MG{IGmPz zR4!AAHF^O*a6whW?Zpxi`qN!X=mSWq1aUDmp-81v%2XI!DMzFj!wq%Ya~4i~&uPW|eX{AI(ssp#oW(s+0&5u%8-y zh#jFzD_7`9I0{*`!U*Hi{3vKpdnw|RFkfH+U?k{CRXX~FRW4VHbtW0ZA|TZ$)eW=J z3Ixf^PQ4df^xhfW0~Q`mqkBoN>xRbXL2qL~P=0~8cMxk9ZL z>ggU5;ipj|3iGN-nQ9fBL2giMpbW?dki!C6Et+F$a6>F_DwW!(m1z(rMVO#c1I)uA zA*G2NK#)O=p;U76)j}?9tR!Pl zKu6pbGK2*qiVQM2ej!33lE5DDsAHL8Ic!7mS=`7hsvGp3~ux>LZpX@)wT22If! z`qRJ?N)_w}%z(8@t^NRtQ=1m4uh-YP!qbEz@NNDuGRstavkc#3Kg0_1roJd zW75fWgdd9QS_C!d1YystCJ71k8VsdYBkGYHxJGxCOsxPM$i|I%AS}Le@)L)1~YM@JPP%4d3Ht~UyF#+;Za0A+CaO`vl zKd3;Alxk_qLJBhxN2O9HF<2V-(WP$ZXFz}<*Ds(Cs zQYGLAYdYkF)&&j8lvo&066ArNX{|?FrAn)%)s7O4OYui22o{HvA4QEGr6tyw46)0P?L`3in4z_Q?G@F^`IgsmP4MdK+58i;N}fCBK5 zfFA?&MDY~TDD+yac{CcW+N8x&1J4AsC=)K=3E01ovS1o5Y7zI)7C~5t2^~v> zpJNxKPA#cZr!zn}vrY?jVisDAqEr~+Ac#MRM%V>N5u$7cxq>Bl(hj#2 z+zdViglokZdn_r%u#!rS=8&r~FF2S9ah?2u)L?`|OEmCLtzIi2L;^6#bxO(v2K2`; zxD6xFT!;~|QYdW}rCKD_D-gX&1=LxM_(U0<5g8j<0|3Jr8ytxz;fDB8h8k&ah%RQG zQex6l1{*X(QfYN2twC=TH}nrQ>9jh762C~Ir7Z_63#egx4UG#5RjIV3SuIopnUorn zNe`cc(2!aWFIH<|KSN^`L+~+b%__Bpgo7#|CAkogMe~FAQhWklK4trOGd(3Vu3Cps|tcTrz7zm(In+yh(5iX(AS^#ClcNh*3 z(vYFq(^rHhW;`kTuZ;a!f*zST&FM?OkiX+n6R#co!*291P@oK zB&2Zz^dQyi4GxpSL^~)T07|t z5lIWPE44c4ndS%YRp}86DL&C4*xtwiMnF%aG}*~VwHlMu?o}}?TuQUSOpAvGTYD*7 z9TpH0g;k}-3edwSdI+G?SWG63MF*GAI}Ca#UZI7~fomvUD^qG^ke8GNm>M7?aH~ZO z)tOAjF>?|!;chZI3tJU9w$2Rns7)p-c6Tll)*>ZlVYFg~YMWJqWlOJw8*243K+I!R zS+JK7s|^}Gx+$>2BS?CoR_qm#Oc=^&l#zzUlEi?vh?&r$S}!DoQ9FT}m6|&1U@6K*EmmS# zBSUIEoPg{h_C^-HCRjrlT~Ac zBheu6T_G^ZXwOI5)M||g_-Z3Gsx<&KcAHtJGm=Lcbr!ABs54rO=&#j-ok?f48J#-4 z3tJ+fM*|N+kTT*XE;T0D)nvj>M{N~W5G+qh3oSXAx5W){kgPISK+CY-P}xjYBZ3BY zk}8>2Kc;5A9uWz?kU?`nd*^fT*6P|il|;?uo_@4i^Yzp=Cj%$zCo)sI}9eR#_7=FfeQ$* z==CPK*=*$zjF zR1Ahyxr_j{266*`PK(MQGi%LSla<^X@&N`atCq46Zfn9}Gzbj<9taCYT7*=!miKB6 z5`|TxGm{G7WRwXxMy*9}(SwnYjViLBtih3Z5=Y`k*<&)&+K!UXs+D<6l#N7SgQ6V1vm38kf^%FpvQZsDdR7 zW}5~5^+wcM40fl*qc{36AD{;gVt{{`@g)s3KPyfai@}2U1Vv)LcvP`lFd7=!eGmu9 zA@hWE4EqhW-E2pgLR`dxq=UA`5_Vu%0-YA4!2*e`dK03K&8Bmh%w~(pXE8zXDuYo^ zY9mC;)q1%Nfr_G=P77z&V?i=N3ZuvxY(!L~jK8%ufRT_*Y7z~q8RtnH1%BY^GXvdNOf8tYPUbUHHtFCS zB&ptF^H?1Yw;Z#xSoKD;!)CELbyl;szcwr`zf?n*0<@u*ovR>{^or>l+M+eWsD(lTE9GRS*fVy-_>i zI)Js-5x^>hIPaA6F+Ibp5UtDVv>*f;ba*I%Ug5z)!qyC`^w`WM{8$|Ziy4S^8r){9 z)oux3DinduMl7m8mcfW!io!v_1P%=N6-aVXSl(tbJDqkiC=wwP;TpT0@nq7N9cHyv z=din>{gBfEX9B)$9spM7^BC|!9VCJttV)~B8S!Y{8oUA0+YE3`wc27a;YCo)X12k* zkenFGZpXeGnUcXMv!boWrnf*YKnuyBQ}b4+n&bxld=9l)Zqp;XY-UWyi0zm#vyqTvH8{wGibl53e2j_)N8)J( zN8(30V6|Xxr^fIhr$HXDQnnZ{Uy{^lbNK9TmrsG&*&GJ56#>QRG1#pZn-w7iam55T zF)PSZNGcN^h@b?!*#&zc=G=@vD;)vm~c7J--H-#bC^9od%$D~Aw21ICZiQ* z*ITf*fUnNrG?@(!huNXmBSaYiA?$7dMhs@wxx;o6&g@b6}{b2@JFMW!ZD_2M$CzZjTWiOr8nB40yBa%Wvh@KCWi@(glyFSlwabf!LflJ zyCDd)V5bJ|Vc;idr40Ny8~Cw1d56c%D=<5o!)Ug-opz_!;D9)YC9o#&LxGt#M3hV> z2jRzIaRVUmI6v>kwg3h&QY=Y4-AtpxGLRvN0W_4chKkKBw1?MaOJZY86;2um}hVJ9z9H0xp{s zKMn|BGy6PVe2b3L;j%|ub|>Pn)n>uI0TI<~QRpm6kI7^PZ~!QW)nrE8v$$MVo5$;- zh=)YLB>Xh4h+1_vuT6`n=H?Mh;$9x{)NV4l0yc-q5DJhLfB=uh;!wF<-jrX@(+^)(Xbz1aG$CPs5q6kiBFJsE znnE6}P2n;j`w)P@g&rq#v=8x`DB?H~`W<#^;6(7TdF}XX3=?!@FsBmO+N!-qi;Gl% zAWa!gOWtmATg-|^cIa#taBXk|Pa|;@1_aND!;WVIElP2(NfB{Swwn-rNm7f;6LRyu zkOH%Fy3IDH&*Sn2Om2wdFx#;TVc%*p*%ZL1piryTO`3JveAo>j=!8Q)o1F|`bK7{U z8)3rdMt`frZg+WXfuK8PwI^)oVXz|9*zHEU%VNd+3?{GDX7+e&9;|P$EX=7;==>h+ zzAkLq`fXu-NGo?M^_;ignp)R~u|_?8Xk(YW8BO#kRoV^*WsXfRE~t2$%FI*0>_-FgpD% zy~m8NjKba}8weuUW5&LS(_=NqB37FMTOp_4Zue-s-avE26fqd&a*NmM4m$OEx7(rA z7>Zt}*XbnQfk43L^Qoz5vkQmPWA@q%UW*%{3eZBbnhgmLKy8NH4u>`7H#n7EtIz5U zda*n}KES|$FR{W(;=)Scq3rTe10dZQa9OPuE0$!FwK;2b%C!ND%}Xk)FJ-sY>9>)jHlIJvheL6-&*}8SZ9QSX zHxRM_1$0|}4cW=SRNc9qp__JlEGt=}JRO<3Yaqg-zF+r1ID(TH$^AX)Z%{2mYS z4r8c50MBh>F5qw|d8^-P@*{3ItcXrXcB?4^J;5REn5aD!HhENjd%*6G_&pAA(Ksv) zW7tla5W|z5JppRq^gG>Q4<=~0x;++qYtinJ>msnW9jxrs*@F!W<)AC*0wW>&5HP^C z!4W*I#F6+>h8pcy-E4k)A#7LW{FJ?RcNl)>^*RHgbTAf8YXTmRKj?7tv9Ld!um^lz zzt7=?&h0L%-R{;npm0H#t{`bP=!qhlI~=}DI_mKRU;uZ}6LSXwoNL4j;uEZ@}sDI_y?^*yVPFLY|Nf@d;3}<2{NAV6;JF)_5sE!nqTg=9+`q zb!tl@824kh;IwP4YNyWz4;GS6hmh^chIwzu2i&{-K6g46bEf!UAnY%O_#mRL$LEIc zdt45eM{9OzqfV!bcCb!6oY{pn+8YjgeX$t!H9{ibB3x_7E^+c&d@v_)fSx7*no zOWMt5-suddeLTZJ!>a+yO8-s!UA&p(iyc56Q1as};(TCh3J8loDsZ6o{55Y4S^ za3r3@k@!(A26^o5OpY+7?o=0plzFEw2ET*ng`@dMDw)%Ud0#N%@&(edP%P~X2l!CH z#bYn*^w^zFpBC$hpir+TLYj^El0I+HjT%^V8 zFC(@&>|R&E%JU9B?Dk@Qc4yq{b46pmn8S$><%T)2yGcbo4yVs&Pq*WbZFt_7(YACu z8Q$I6GT~GZOEjLAZCaOqOwE2j!m8WiX^sZ`_zB0oA-^}5OuDlH7&6!v4MxmfGa`|j zck^DC$EUM;bO|@W4uRaxFz?1Al-VDR`UA;iY|NqrTzYapW?(mO#rGOo!j42T2TXS) z(@u*e;C9FIfr#5t%)31rCvWp7eZH_h8clZQ>^X}?sdPr&!L;9E2?cqL-r5}vL<53T zBr#Mhrt^9{9vTV>(1>`fQCHCIaR!BiWw;dfc*9P=n`~4}+5(!WJLZn2qxiBz$mex< zElD?JeC;Ax*%1!L(11YUPX_R3scwfqV0U*`JprXL<@83~VCAOH0dLqtIqr-5z(~j; zD}tj}*WgGzi6il&+!hXEk85#7F?F}5ElfG+_9wk0syCJ_#j}~BE*1zx;$D9^my9O! z?pQb&4S9ne7ov>Q?e^(SlnDgL0De^Qu~;CMi=)3EF*=&? z=Zo=nf3ON=yBxeXY!3unL98u7JoUR%yx)^Z29hp!-h~|qy+^U;5^yfR-;t}uNH~99 z*FMF~+y<>9AInAX-o)$H+qItHn3{t@JmX?t(2@uT6QN)<%}0ZLA(QbILa}Hv(vb+m zajpIkPio^mzJT85)2F>YpV#GfdEK#q*N3N~U?LF=WirWzF}(y_EcbdeL!!!{J(Lbx zW6pG@!1LY-nXKDp#oQC6P|WLWEBSmHchC_cJs1*+OmERqu-TMKcfuRa1?d~4f?9)p zN+Og9g@|_sLnV`X-sh()N*Y+nXG3$Z&m9qx*KIFHeSFLvBqMp-GWL)*;YH3RLOi(W zc^7ZXc#(Y}SI7lmxuRj(m%tx=p$sA&wtu0J)7LxE7gCyXZa#snQ_xGDLwwXnIUPs` zz(~kZThQm@^$m{T3DiWfeQe~8SlEl*ktczv`?MV~%3*IX!;`3dB2!Kk@@@J=7;%RW z#tNB4rsPdV!-*&##%7nrh zKm0Kk3wD-V2}3@R59Hc%i7>bru^Ku{LCOg}!Dsv#K9kG`^TBK=8;X~bp`bs=$AOud z{h@@~+UgJIp@LX|Iw!(VI&vXeh=xs#oOZ=Sp|G*Rk$4hE;zzkRn+#&f!SH>hprJQQ zIT?(X!X#-pU+OA!lqZ@nyKEsG&vcY>rE0J_lgy>V$&f!7L~IGhO(8!{!2q^O1=4IG zQI5wl;c&WpVmXn_=i`ZZA<-UdZca3J6wp5gAIlZv)y~3{cxqZA5CnR{8Ba3lPv*n1 zWGKJ~TVnB0v6LwJ1C>BH!Uz0#BCZsp{$M=LSElFTqse&H)PH=4g{($?La~;|o;((^ z@Wx0c04Elb)rx=}?QO}XTe6u#d%QUl@2XUyUD;xxH9ydjE4kzDWHu2=M^o`gENSt^ zEbY-~EE=NtT1-V_SfkS|E$M8f(%NwSC?S&`tfR+Ln$LFRJjFmqM|V6CnO&)dJnmdH z+SZdT#sX7%qESN__Nye51#3%7<+z?ex7VxHhFhY!YR2Qq=hG&u@A#H%OEycqD;TP^ z)dG#h6GDm`is4es+Y-q|V!^hjEl@!^N}aRuLX2{IvONh# zLT>hEV&K}~NIZ!n@uNIY%wdn~iL_wqQPV(?axR*w#7WXbYsb`bS7)E4HJdH9$1}yQ zN=v0D+E&Q56ymv9I2uibqS1^6_!RV+N|i~o<%Cpk>dD%!b5=_Ml{#-7cZ%w4~v1l+_O=V*39oddBzH&642u8wK zox0nT;b|W5CL(246=B$jfn8O^L)ZWv=d%dZcEnrC$!iBMf$H9azIZ!D!b#*2CitTOHmU)$8hcE5R6*Gx^GM`SQayEb3)|pJDl5wnWv9^3Ng_SK|sWcb6 zx~dIpCSkBN!nF;}kO;=jp<-8wZ;wu#*q_NH7I*c={Jv5$QR^?Zr@}L*rIO}2?AMjc zl^vBz*KmKh-|yGy;+15vyWsb?wlrHDft8hFrC221T^OobwPmv7K9DiB$2wAhN}`xd z#ae`vj0I-5rP6T7WOH+Ka%NYcXsINsiAry!m@cL>woEkR?@A)4igD}_Vr|j3QZ-pk zcBDE|g|1R66;DMA#c*nPNvf!G^~5uk1Xv}hb1~hPqTHG5%%yFO-0Cl+!L`8=JY&R> z_)(tMR!m}*O;j-Tlx1EUgqjJ^Y;rZ7jLn!E7);zo)&mtK2qWa&xt|TyPYUg-~CPa(lWx-I<<{oY2ve>&Z>b zPt2G5IxxXpvfLietzDHb8@&DLLJw5Xnx)R=<_USqlS`9J&CW)yhTHJP@QwyY@Jta$ z;z#*}&W>Ct6w3Bs>N(p9os>IrZG8ojw9wsm+@x947dpG!%M&Lzw@sMc*VQ*S*E^x3 zyVl&1Pa}+^v$-~Ba~h{yHrLWJi8MQ@JiXl7+1y-ReBAW*_U@jx_VVQNjMm=X_MX|3 z(Z6*( zC*s+YOX*y>oSbuFPX|)lT<6klIhOI*;yn%t{xz+B8lWW7h)t*pGsI5}Um-B6o%O2@}e8Pi)(y%A^V+P_a0Gp(amuwUkQu z5t6k#+f$78=Bv$xOif75nb-*v3&rkid%nHBy?ODpSj9dmKPlfkv$s;JluFK0x)d2` zrd-Wd0j!DXiPg!?lbd^rJ;jcJDkj*R?x-XS8`l&n27X4iG${{OdFos#O)OBJ+B&th z=xXH7Xh*SFayB@EXNEWuKgz@1)n@F{F#L%F&GzAL%GKtMX(bZ1G-=wBzPYoHcTK8R zdZrdTy5IiW&zp+=?Gvlj-l-i67Wb`epRl%)Z_cI5rLIJ^nypQ4Dc1`5bo2Cb zM`7x;%Cu~LLB7(ELmU%l*(QqwGJvoGG+~bv680{k z>_Q<>hn>QwACuD3u$nGD4NwYgY4P>&<&x>w0?!l3a9C-$Jl2 z3bAO{V7@J0J3JVV=_A=-t{2WxO=oA{_T`}guUDmtw8shy8@=A<=4^G1|H95(XD)|7 zyAS?UXQ!DeVpSltp&w$^9yA&(9B2J+RN(T-^QqV`-8 zKE;%*OZxg^aEoJ6(pujVZYgxeI%92#wnVP40Bjx)=km4j?UV7G%D%9k>x{xjMe%>m zC0pY7)|2i@C(V^x(bt$vBu$l1gwKrNPsE>xZ)2^67?>>4PFQ-pdaM=S3bDrCB)(Lx zqxYQdWlL6>JMy`!#CN@lN0&#FTsT?}h{`}ux;~c6g$K5D6yU3I&YO0--(EzsDI25oX>up?U~84r?Tu!c0LQe zUw%*I@jLnKOtwA}RoVgRR@(Pc#LQk2x=sj2zrAfQGYWs}{Mi>#x1bd|W0;9(!6;^` z2&#IAO3G{{kb=-59l&15($t0jM(Mj!tnL?YZXb25+ESL-84sc|~uF;6_E!Y#wp z8iCW{g;$AagI;R4nMw<1;# z*Rq8=LOv#lL5B_(QFz=acou029*3OP7*^KwT81p+mUk>4Sr`kYTq*iOIx8im3Xj#Z z$8*3#m%^o~nW>X-H%;WH@R|`sB=N5!$|lxb|8O%BHG3NxyoFhmVbfLo&gBgrbWN`<3j^9n=tz88 z@Q}al5vGm3nS&eJAH%c}SLthA*ejYvIRov3qEbCw2< zO*AJ45@U&p#KnmR6T*a!A~Lk9u@y?Y=%y-8)yOr8+J~Danyzc2ntIrirRgblX8Md$ zpMn&e)XCDBQ)i~9@KgLvwAWF*tftG-+NXCqUs~j__{k=8m|uksk92Q& zeMQ{nTXER7$8-ul5>X>>x!P;DteR} zO_C{5O44dxVqRlrRFb0ER>c^K-i8c8`Kcn297;|k4<_kk6VUt2t|NF1PaGvNIOSLhexQ?VWI-TAveV7XhgvD_=j*) zI>A#a)46%6Njcs1%>#v2xeg9Urve$k=HtM_hu zDDLg2wxe^qZeR76nSZ$2`bs~dsR!-B;<6y($27aP-1El1EpGmA-srMf;op|KlS-d} zObXiiZ@qHIMD&<9MljIV_)|fLU>)@84nnu;cIX7XyD->%v5goe4ifJIc{3M_$c~F+ zeHVwM)zYPlF7D%wawOMtapZu0ulun6pqq9F^kMV3`GEPbnKsjH7l&ozvIDZiGFq0u zn4oQRA3aP@(0l1)PzAJlFr%sfne-AGS_Vm}l!g>5Y(61~y@X4&u3keiR zipmUFA(ASI&a$uizkXMM973SIF4}-;@A>`}yzqL^^H(qE8a-$Awfzf62gx701M^<} z*xF_e+v9@zjTd>5JW*?fvmv@-`O3x1uDfD%a_YN8oiEr_NQ4?X$=|j6-AkAD_2v?^ za@F89=L{?vE?u{?s}((*U40k7l=t=A6c0NUs>^?k&&{J#~8cMsZn3ua|@1nmJn4zb-mS`fj6nYYM8i&M2 z6hubOr;4gLPS{LwMsid#Dj#!G;vXiNkQ znzi?*T(!2%Z+!dwzN@ZZ@=27=u3KAIyGL8(Z{3{RaoNV!{KJz}cx2PozK*SLJ@w!$ zk<5%%FRXRfz4a*n&w5uh`r6dy^Phd?@QRx*hlZ|ZdJB4Aa3N65^c=x}q7Xyr2Su!u z3uWP{_r9K*f+UF~>2`TM$XI_9Ve@BF+OzWnjxH=P03sV}WmTidwBApXHbNYaFq3UHJFO!lt^~T->Gizq)fH z**c>*qp!CEa_l;TzB~T*XS?g_icVL`d=>u|{~5lbFjYnUOn}F~-BcL7QiuAT!_EVS z!-mHU@7UjQ3K@mc5EA+zBJ5lWgA6NrC=n%f*&L}X7B)iK?J6uUyVdug-QjF7`UUgL*n+D1ATyakutI)<5Z3?>Lu7$V$i37wmvEA5M z4fKf+f26LZbueEb^8z7*j@wGYjto!vOhvJhcorF+7Q1{!lUWN-pAr`pVWb4ba%OsZ zW@_p*ENo`#40{^06uv~KP~a`=>UjACV$}hs)f-FZP=&d*R@4A*QPw6@eU*Oz{S~^}D~M2`CJciM@W5241GK^{41!`Q8x^4@sJ)bcV&Mp!DiQ2=C-4b4 z%WCDzq6Em|jO7v*W6%}-fu4UL;j|pMFdj)d4g4z!_$%Y+1o<~|J4}*r78GK^C8QiU zKuQOz(%H(spDOPMiJ5r6rOD8>0~^=IIg91m%AWO_c9xJzR7Z9n>|Z{_~=-Wfs;) zgY^-8g(yT)rLRF?5rDu2{r0$<6V69RdoL-vIq5|4!e2jSTHfGhRllVC@1s!)MO}Iu z|K)RE@#pa48L&AUXMaT>5~#~I`($CjO$0}zWI!4N)_8a@v1I97gsCvvORC5a2N{8t*jKpJTBbNdlPReT7-+=fi zoxv7QiTxTBH#`M0*(i^aO(yI%NH}E)3M*b#g%z)oCCSzNyH~HXB9;q`m?)BI01 z4*vMZ(<=%OdXerV%qnxdKlAY`{Ga#&pJ#Ub^p3aqXZWYTdH|6i;-JwWf~g6!lwF{Q zGt*V54HP9sRBu&>)p>QVdPqI47O0hGx~>?gvAc@OfLB*k3W^eMQPF^^;LJ>&24n3s zm}78q*fT^P#4$U42J`^qglIJEZq$mftD;W)`?$k~gLnqh0T>DH^~tiQKpuPT)i!hV z_K05zZp>wFU#35?dRbplCXBFXERakhBvz%@ATshocn`Gj-?tV(_@Za=K#zVDI=C5H(c+a2D$Z*IbZF6rsnI*lsTR=|)4bw#k_&vISV*6npXvsJoe| z@KhL9UMg=AND2qYi?YZ#tEbC$DFE{h1yS$ZB=s?hTu4ilij=gv7`9V6<^~4LAaq{QWgWWC56QmXLri;!c zfBHewW9KA?FDf+D#wQ#L!;Ve$t;<#)8hvEm^6_nro9|J}jLw|55h!!r>@VmH^Ea4~ za#LYKVD^}zrXFpJdA)YTEL=ySe(`{BSUl#VOCf*=5@{q@fN)- zsW&;r3ZJtWRul|JBV#0(`6YV32rr1*1um`gUD9BEM=BN&r30Y!*dUJZxifTZ}Vgs}CRvxD7B@^Eh6d3C) z{eYbKd=q5p=JPKP3mSbsOPy=yl8pPL-?2}n^u^t+_4O_7Yj0jYUn%X)STog28kZ$F`#f+>dpnwjMt}N$T|!~~ z&#pVppTT};WcK&;y#fRD=yVXJLV8#=4nyCpkm6=9I*R%tAr=#q(5bPif~u4%V((BD z>_e(P`+D`T9a^Hw#YHRrJrm(#r%z1oQl!;(t)O zR@E%~tyHYwYFkIipO?=0?q#&PT`i^-^8M2i;EUa}FVhzZL_`O%q0p(Va#uwP@%H|z zMTP$6(W=pAu~}P;G^6Hbg3Y3Aov;e_zO#-_D_e7mbBhZjxsk%HVqvn)nJai`Yp1eE zw6SeuTNgIc*y$;ZoUzz2V!wv%J~0U{a|uok7H;3$L}fF$yw^?;apfa}cNky}cmSmq z#cFo$zO0d*3Z$XnJvdOTP(U{Dr!Q0Gync7w)HLzvc^f0Si`KU!k{clk#<33Pm}Y?{ z{l~OvlS$pK@@CIiS4tjy(0%3U-)uSOj(hUIu$a85P(c;ixBKzy1~*&u-1gd(Pjd&# ztadKaG(vw6McOj~ZGvj~k2gMY-KYFlk39`sFh2V%y#zXO8Ns6)C~!)RNh!0oxd$|Z zrgbLTR8(k?#tozjiOGaBKojG}8-wmN-TM6P{%L3-)MfzSSV z`89v;Z!wMuvfX#yyr}1n_Lpu)0`xgrKh&J^y3T!V;e9^uKTm&g^X*i;Mh58F6O#uw zjNE(WmB1NovoF#wGN%bU@Oc3$m1qhH)FM*N)sPaYLrb}-bXA6Juj*8Ev4e_1c1X2e zv7Q}=5-3(7f?pQGz}3Baqz^hey31M`KrQSDf#K3~I{yu5kU6&qR)QTO41eH_SF7^K zJ;xn%27%U~FCM&jmo-fP)dwOgdN6K zYpbK%*A`i2UFFyiSeMuqkpv)EwIzHbKBc_QWiM9y zippv-43z4H*}&sq4s}JA(?h}m?YMB%GGZ0h6$uM#u~{OP0zi>kg%Al{MTOR?4Qun- zUhR-}uU4oH*3X%(rJk7+V1vN%A;P76PZ(^dv*)NxSNB==!n!066JN{DDp*%ojOcYj z5&`^M@yIcS>k!3u-1$VCKi+cxp(i_o+|lGu_doE+^kzn#E; z8vbzp1gNP0`b$*V)G50m_%x$K=};&wX$)@qa(Vwi+QqfCGZO0QP&yr|s+R>*|NQmJ zrak)-F4w|-FGd2?#4F@x+6_oRy5NwIGGaMc{mn87Nzi)ef&dQ;Yh>79jlzmn0Zv{5 zFdn?rG9DZ5IiUx46s|~DHu%f*s2C4$j=q@<{7|OGm8=;i zNB#Y4a&9-LwOl#oGAA4CwOjA1Pr4m77`tB$`euUq19)Z*!duXxBEuH-T9!hTl~Ty8 zvWSe5IbmN;VDp9u&z!`F2m^x4=`I_F_%N#*=nD7?^F8K_Lezr(cVK`I=?8V*`o+4V z7yus_Xy*UXTywr;Nso}Fje(e67{}-fe$GvFZz$HgGNZLL9 zT!y^a%Ve=6PUzV)V2BHiT1D8@~-@$L7&HmipFD}|Rwk`jP-k-Vk%H?mM z_LbZ%w`QF~p?P+9de~?~vAnN!&6DFRwwyC`QM12yejESl>(@=TY#49#^ZPP^Tm!c) z8H(Xu+BCaG@GHSem<4!SVckF&2HYse!fUcjnDU1`$vl+~C(^xCYkG(przDhDP|PQ@ zMAB4#tNMD0IJb8w#`0_oanOA{4;W9!sb8`9=j3_@@rWp2MfI?m@4uLSl_h%P_ zlNBfuV`_6yWrc_^DO3qyo4o<3I`{idNI)bo=vH=BF`pRM-4;%qTR(mrIiCw~BbO!@ zn5=b6JBzVEtcufhT$yY*{^URTQ}^EZ*wd(H!RTY5Du4F9JFmWm|J4}(=JY3M8(R36 znN6F~4|9$2#8VrdLk+o5*5aw3w;zh2NcxWbxmtj%nv}v~_Ns3V{oprK{4e>>UzoBs zu}#6DOladEOy1lJHxg;y`X&F&Cp&gwzPn`hQ>GpG&Q7?AslpDo#_ilm?lkWw_nQSN zG9^!$yTrZnPIkF`IlE52j+M}E2PwpM2^?gxnKU|<0j}08bkgn-e8j{Whi(l<+CpI4 zk!G=wq@`}B!>H2;3}gWs$q*8!P6IvaT-D&Lo=oNiq8ZM8_y~?b(y=> z021h-6ENO7G2r<0rz={F-qCyczcuWzC4KpumFJxIqciDVOa8``CC6{)Ki%=Snm;f9 zMD9vn@*S5BMo^>Cu|R(ku+R%O@keJm?pR<7rs?f`)EWYz@zU(O^s@pdXb9+-4zak+ zZSi%g4Z01!HSrCZJ-R2|BEQ8G?bdZ?mbiy90*i&UJ0nicUzZ^Bb@6yVIaJplT@oL! z6W2vKQrO6n!#ZU28dGU+tFF__SUEcKT!kWB>)DYEdwXHf*O;_hY z*iMxIuEVeiHml0mNyUDiVqdM`CvL#5JaJ_VuwYMxm&*=A84rRmh5}J#76&~^d9h^h z3M|Nle-XUvM_AOfwM9u>+Xuf9eil-ac!1?>%;%9Ft{BXsN$v)^~_!z$ntjC1|R}bCw z-A77FynFRK;wDonW(;R)L0Z7&I51H5h4}GrcE4NS2O+ z$qA0wbpH5E>16rnV-*Qr7YWS~%BE!5)c{FI+p#^tBH$!2IV;!&G$HLP*vtm)qH7=j za?z6W&t027KV=D~J^VlTuiiPZADPemrG9>mlWSG6Ium_PS3xfQkUzKp!*}0`cmtHqyqay&Ng6Q%l65 zfzVip3UMZ<&A~x$5y@W3LCI0*k5q@*U~1<0)HIlV*nNVH2WtrS9oAKrXy8PWbI`k< z1{e*t)8jV5&X<>)Lvfal9pXRiSUH)J1k-Bq;(+0b+YHG3`v2X@-#yA7U?^57Uv}B) zwW~G@*Xd2cY;X+SJ+>A7&sP8PVwY&u8)x{_5A&~5KP%qNf6RX}^C2b4v^9UU0~xXW zzk2p|`lk@{ZzHZMEQ&?5Xad!NKIy#S z;zW^@sa@hCClwZ>kY?jj+KPoDS*+I@iXoz&ttac}^=Gioo-RT5!4!K6hpVR{W#Bjq z-UN2BV4J7Fn|!ZiIFrxM?0WA#@U~KwOah<|s6rt|=G_$$u0V_&U=&Ogcmw`nWoQQ% zT^UQ1eUY=yxE`BQSg-&y49b-c-oEga39%AA{cm<^W&Symsj0(@5*#A_HL}@Q!y6|rW8~%WH(;;YS-o)-hh=lM0IKn z-xpO$R@7!x>6t#rZwnr$w8uk_1I1K2t-Y?(XLZ85dS{0M1q!XZM z_M4fLC2-PbK)wRziNys;*c~Kv!>M>pm0%kdAA;)?)qjS*f(ljAq@U;XWG z-yGX}VDQGLvjLs&*0HXI{+>Ui^AA3;@2VvKf3NCwA&V*gKB)|*$r68pzZQnTs1ZfF zADqm#FZCCyOY^DN&CKZs0s7LEwhg@&k0m8+4;u8s!(+( zo~_QtyQ{n7E2>w-H&kzkOQMVFtK&LuHP+hI@;IlfBXcCj>8#K$tH>#JZeP3_@?J|m zhp4F0DT~r{kz{O5ktVXqT(mmEj=ZDSK|6xQ`4!8vgvIIf^qJ|I>G!~=pMl&E9OX^H zx%)Z{2d-?5LU@e*eSL%|q3>aDRhej@r9mnsL2+X!9OwmaRQL?;fz^O)q*m_+Wuz=4 zotTVA6|_^A^!qp+^YC~43RlwaPdY^!{Gb(}M-e(It+r=9S>~d^0*JGY@LNW@`FDFD zAJ^Z5BHbNbRAk|D{;5HKD&=2??p>Gskn2T_y#bC3^zkqE%`AwJ2av6M{`zDnN28?tCBza@_+|(r+{03zVz|5uxj^WgKFyv! z4H7xLYii1HW(H7Ae9XcK)C?9K?lSUUw#Mo5+F{A%Yyt=vDCWXh(FO+ps(|`wVbz^_ zkq~79sZ@abm}}<0E#jw5H%Y3@7hfDquUzoqYbZ3}sG&cs((RKi)LaO-b#Yy*j$(D3L|Sj= z7%oUYBzji#hUizKU+d0@1S(GMG}an9ks!S5xB*A?%Ub6Qg!n6`14jacdKM@2U|$>y zQ7ll^I0h`lKPZdN)v1s9Z^!LxvT5}&_xX8Hz#TG0W5zKVHj{X4V3)BIQ z+dd!7C%dUpFD>wz$q(veZ$Y<0&e+p)^!Br<-9ousZ^?0+vX8zNkJ-_?U%R)M1hr{ZD^{<(|n{E;a zVEWX`0u~Ar0GJ#kju1y-_Vv3kKl)3^{GfqVCyaZI2aQLJM~%mf?;1ZgerXgNbsR0V z$w?h2QgTvJja_78PqHW2GF}JKE!f~)Q>95*{Gaaer?%##zd zj|zUq@G!mpox(wnE}`qK>9Vdc57i7>M|7+8s|*`_V=bJ)B)$fbb zA%#!VhP@MmP2*UCosu*i7na`%N$Z6xFqV>z*N1!#n?@?6r5*)gtTIGN52=tCBp3UT z!)Bmr;6tSKp>&+4vO-NE6v`KbwgNbr*_vQJ{a>+Ga8piTr_yME2XIla?_G9O%DHi1 zi6Ek0yo}TZl3Elr!bUn(PYDsqv@Sj7V^dfW- zy7RhaiO%~{Pw-j(*dzOG!8Gj61+$MsOGO1B$Xg0sCQ(q75%r3OMB}1ipD-^fx>`I# zqQ#zJ*RV(E_en%P4-9Xh9b^>TLWPtQd8!1y`YNuPh}ZkLIQC`=)qUX2aA3xs0v0ns zKy?nsfOM_X`8?2dKA3Dc{S&Z^k{je9kP7lekw&FuD^vq-1^$6L0qKG{5V|IyL>RGD z8KQ|cfXORhSavYk&5zCe1zOavP1-DgRU3L6;`6_K?F=$e6y>1xnsq-Cd>ENn(~d48 zPyY7wBL2z6{9m2cVBn!Z?(ex~ByG-ro3HEM!e8}w{saD-;Nsor8AKaL7;vc?57AFTZRTzDMJ)3cZZOOX`VA!pqf zoOMGv_}@>xd*XMfA9WnL|AqIz`{tqB4_x`?U+=hP{b=KZPr^95<;aB6H{8ZQ$=}C+ z#=pW0e*H23)yq51`{o0r{9;uJuV`fUkJK5k%4&!Z#CcL4w?Ehbl{pBpwI!o)(+T*Oo`^_c{wI){s-IhCRhtkYW9VXO;hc40{hFECD>$I%}4G(H>P zdzLd6=iF6{7ygI2NjV@YM(O7QqUd(>N>R+~i-B({(|6gn^*?ju=nD`0B-CmkwVNK; zy!-rdva9N5{xV-O=|i6DL6F*a?O$EH40^Nf+W*QwuHQ!)Y5H0Iys9OoJ0=!%!YX_C zPQgjoHv>^m+*jBD<@(mJGj1JnjynY^HG)oFp+KlMpmx@|1#+t?WTHqlK`{Y4j7_TU zg*;|XM%p<>+Au_n!^Z_+Gu&zxiXfrvfwQp@c9^AD3uhIfuqY45WkPfeCKm)78t06u zvU7?#KaA}roWucX5O|xFAXh-F%rD2p=FpFdFu~@8z-a^2$?dY+py3wFSP>Qz2;)-~ zZ+7lP==82Q>-7jWg>l{fhtNm;5FCQ=h)E$rbQ734Za+3;b6fJpQEefmNY& zr!M_YKen%q-v!URL-YNl;51EVxCyMms;nL1d7#URE!WIN!cI?F$Xd^ z;z2e~#8dF}d4@fVM?pszL7-NqEvkyO5Tws5dKE*8aRs9YHa4}+;kz?uz*&568l*G0 zjB{8oWQjr!J3AEyg5#J8*#71F`Un=;gxh)+ptYVZN2kE6##Hy>2L-}$ z+OrS}J?gRZPx0$TObRzPplHD3y+3K<>rG2CD;z z+8|7bq11jC14&pOh%be;<625P4xVGG3PH5Hj;$l>l5k|t0OABm4AUn-2Au|n2xoxc zxGm17h5A#h{3{qrC2IkjfM@+AF#WL3!}j1=LkJ7+%TNYtextr6cOAO)p#wX9e8>0? z-hBDzt9IVC-1mC&hZ`quNZ#?gi?;pdrSG1(IyCcsC@(XVM@%2|# zvXIHHo=uA!(3E}8mis^d!3AyoK#480|6rbBJ_kzV3O+VrDKf=cvJgG&I2e7*aU{w_ zWe$t1sAeF>Hf))&?6n+(7#oPAvdMFTg*E2Eu$}Xcg-+1O<-{GVfAFa)Ya5CLv$nF# z%F=-b5aYn?z%1;%?WXzjCxDL$U?8h*T7e!w2awL396`P( z4kF_obm7(ggRYJTXP%n5i~7!!&1!Pt?3MmSi~RrE3D>`|tUnT2|b9fe*UwD}^% zl-NX83(O>#Xhd_hL|aw1)y&n}Dw2Ud)Ka&NRFee>{6In}%p~coR@gL9;6LJ|oK{6x z@veeW1l{x3eIL02i;O*m!!~8)*oe(^Y?*-xhOIB`g(^@b$J=97}vW|+x9}9PDp%+Id-B~plA8Z`Vt)oS2 z@>>&RFWuEVPE+-CNL;}7t4nLG#xQ$RG#_uo#A$L>`NU-^)i)tGWEY1sU2_FD__Z zCR-~A(~K$D(qF+!d%nlv*;6NshEu1E)s>9yvXKjhE(l0)f`Gtz$Z(k>@qe4SIDSle zNf^HarvV?DxR(O{`o0=Lov75s;Fh*}*Z`nDrawqf67LXmpbq*37>ZHl!00xo%;&1R z+`6&pcHvl)Q&Ej%vBK?=WA@y1UuJjt4FUkMXKjIoT)`WK40{X4$ zZvN@t@qfQbuY>K`u?Kzj5A=>sRhs8|_^DaG?H_35SBp>0>*rs%=lA?})J(c-4sUHr zjwlyx`0-5L_TLt^`s`uhSy;Zf~yU}JbQwVB=_*j~3SvOg@{=0`({ zl6pwnSGSQF6^tu3sz)N?5y^;gy?bMfu2C~}v`=3p)k_ogq>o%C@6-1A`|C!S(K^9m z27;;Giq4uQe`{TL1cqj*87C8C>irB;Q~sYZw^7m;L$P5aGA7{Qo+{zAF@35r95z5? zzkG&I|E}VWmfZ}@<^mzm6$>IPX2T?S9A&u$8C2K7k(ku+!s_I~8Va*@Y9z2~Sxp2<40dzthafg&t^{Lof=X zPho=z9r%?(E@TL?z7F^}s1MeT{+FYM?Wugvl(G8MDFY@Pwx@6`NnVa60m-mr05?oQ zo+T0xs1iB{B4I_3Z*z5A;NIR~@I5lJY**aQc?&Bqcp_dqcCcEJ_to3`);YQY_6@)D zwwjxKd3x5<%P;-niH|m<(?Q1*pZwv{$vfV!HJO5$@P)UhE*M*MkgRJx*usVCDtvZ= z8HFjAQR4E#!gnn1SU<9SWTk5Z+OkU{~c z7Xvb-*GXg0-OeO{)FZhEP(C0>Lv=9LQ3-C(9qS&z$+3ilLOxIdpv0kyZTk4!DJIz8 zv%6k3lub;)o`5bDuO1RaFmap?z^lrMc&a30$1$Udjt8yhirRoSUtRw6@A*5c4d~L@ z%lG9MP^6=#*0RNN%go_hHs{u(v5$`NckvsfLCLy=y}K1DTYFG+K0@?wS6l-s5P59S zJf3X^Z&s*S(>!t``Wy1zt)b3s(a9~ERn5RZNBJT0MdnMeF6*B%aL_yk(%N+eIZHsB zE3MPWNC2PmLz*gfHrQ!EZr~BL90W$Nmr=f8Rh;r-b451?L(EFz=6njJOjF7=8BwhOA*rIxK0UpVZuY~@Foth-O^A4EOx5!7@P6bTF<-m#QK%8)HTuk^W>jy zMmdYYo34A9|Jlq8|K%IiI&bQ~s2Bea>OpSxzp1By6!b=*X~_Q{3cgC8L(K>_fmb$E$nBA% zh`b>0WBTOFwQHDRIPo%4UM};H%NUZDKv=8~!eW%fKoI43)Hu;Ys18=I?<|`LbNpYX zZnoz{={P(Cgu0vp0A#IrPgq`h$+ED7l$8XzFxg}1zr(~q_p`o-`F2;*n*7!z*wmt3 z{!oq8owO`}h<}4~IV}lff+{OCdy2oG3C^x2Si(|(xnn|Bq>xhzl~BnZXCX7@6jbO( zSzI*gfJQz*usE(PprdXrbz&ZWdLCcYuh5O4gCp}$^*q$e-!G9oM@dD`!QS0S-$pjU z3}}{c6*OTm;H!u=#5&1s3>51ImGiHkTO;TLSR4ESEc^dc){{*sSI{yNpXZIca#vqT z|J4-_+}M$<@w-y@-2;AGboNE+8(IT7DGlK1=fpz4H|5>oz0wO~9KD>pmK%kO7=t7< zX;dy4HH`+y05>E(7oc_=xwCUMR()Piex#q(llmCorjsC6%3eMMR;F-DKfs4`%v?!6 zQ{r?B%*jGI=qD^>v7<#nz^x9lF)EA%W&ugvqHd_w4PRIP+4?o8=MPIaT%nOj5mHIw zLRP?iBbB=2?4eJ#Y#A=PnrzJ>zbzKucOfFaLd$BoUX#}Iqbn|K5A9PqmA)AHOmIy~ zt(v*_o_n0?6cS^9_14*w^pL;{mT3Zz(;~EBilm{KzJqA186boaj&O4|5e_OqlNzEQ zJWqiMdVE+VdC6hmwJ6u;7dNid=c?E?b=^&Pd5>d%8$&zPT?+!)NZe-}HLgSVuj@cJcV;7vOXUgq2eX;Xy@x(M&A&ojUZ?MV z^oRH89O}1kxp8Cz-qP8ZsW)gZ(=5B}4<7e)FhVpwMt4khD^t zNT4%Z#@=M04BQYsN*{(4PnpeCa1Fcmx(>RIxQ@Dnkk<{3%pxw@1!5SO06a!uNgSF6 zckz1`__u3_34-H~XLEV~om4~ip1AVh&W>9f+?JpwbJ^&~;PS=$vMpO`s@#d& zF3(?h@q2H*|7Bffi6`S!hM&HF=f3WFwj4;qj+QL!?75Kf-woAbRzP*mEj0L{D_Y8y z@U(fBc-BeR7(_Ns>*2(5Efht{rSqIioureqs*qK6SVgL&hT58Ek-VU;k@JO0~+pg`H4ttC?`#!yZu{sXpR7s+>|zE7^IHHg=w~#kEA#&n|T?agDI6 zT@n$YmAEBdHc;brCD^!Y4YS4xR+myvSKByIwT2@^Q1Mi)L^f5cbGc-va=-FE`aY*n z3DksApFB-jA;onM{TTfYJqt5+J)GZs)O^hRE)?<(%Mc6MgA-5&bimJeWC^GSiufn| z$NUr&+LtDGO_dCAk|t-4mnN~Z3aA54Z7XNAV!arjOJK!LhLEYjMHBcSm7NYi4Z(Uz z!twd^>RWeT`MdkZE}Cq(i@%t=f4sG{t;Oq1K5^?KJMZ$L=uYr0rg=#t($o<`RyZM~gfg2Q*(vVQc?83$=C7hvhj^9})iVA*Z zp&n{DJl`?+9CYhdcKg$$mfB@s^Vl#YGxo1Nb%5E;AI-@|I^+UMaXR;p#uwN1#eE{# z#kV3Qa!6LKW+-m>!?8W9hA|hVXWpj1q6xwS67pbSOr>eokZz`XaU(2RbX->J!RDO&ppTbqL#P7HazIs#s2_sjK7k&>9W8`U*Sqy z*r|b?FCVN*Iv*|_!*v6gYTwssmwyi4=_`7Hvv&3QzrA$%?xn5=MvrwwsKkxKPyP1L z5u_>Ywy)Rf`R89;TiUc~%}~EgYw-1>$M~bK@42g|q^j;6aFWzBJ7-2l{(c1kHeiURoHDRgn#<1_!i=X^Sju*rPb0 zIE)9K5o)!RiwInVQc=q(%&6BqWIm^APr1_lG4o62kIY{{T!7@#V(xJ4vDiDYk79I; z%|&vBTwiWDH<8<$JD59?JC=JlC(YqR4~$_|&I}L_+rDA899lW6eJlD0OI+Z~iatsv z@D{dZN|_&L*FQ1)FZ2@3l9v)rVo4z(;D{PkjlkxPxcl74-1J`eLH7|iCFUq- z5O3fVcB?z=&b#-xX{+od8QCY>10uwYRnJ_&0XqU1)x!cL44wf#Oqc`}YoEersX_#J z^ZWQMi8>=~ZIMW8E1bJfFe%g|gTW;DH!YD!3;zCYI{QU7#Bm{bSc$4vLVvp2mHZzF za`y9?hslG?`*8h1Vo`zX1La7*^CyR9*>WJHnPZ9T;LM?_dRqMgI#c zfRu8sV$#+us?vo@xl;Q=7sWN zU_DrEkmyR7oVx5Bo#dR=5=zA@dT$*W-GP3$;|_H59U~)m@MG}zcg*f%{z&&jf0Zlw0+d}MVq9}k>wm55n&@XXw5beVIstQ+x*D{;+vg2D-6>hhxKK0VsgsXJ^kp zM0dPp=xnY?7gz%F8_N7pkrmoXNU~z7W8_l6T(l5oIXNT-htIucxCRMo)R-OWYuehk zu@E$9^iS&4quwS78GCcy?V02)=iJ{YvzS9vcG1#JucJh3^YVP!xpF1{%^l~4QmN1m z^s61~GPd@825&OS$d zPPafrezee5B_r!p&;hlf?sl1ANIov#BR?R2O#TuKZw!Rxd3mpVNIo1mAcrIvgA|gP z0^Sg0^#r(@!(f<@420w(D1s}sum*weY5W9xF;loP!09>qfm2!bU@8Q}cKi1vtccLc z0kYzA174k02(fxNHv9XfX1TC$Vsu3e5Gz?S7)n%GgsH#^G4;UXNAf`$1yl1@ez0}a ze3|+EOLsiy%`}8-&ac5o1|H5u8_%`vwgr1G28QKC24F(~I>2tN86!qS zBP`XXx=uuDz^yq9hAKsyxn3E977DThR%HF&p~G2$r}HITT=j2eg4}AnP#oZ*JZIe) zFf&vn`M{3Lg@W_1{^#rGE~Kc*y*FRKu&K9sU~ShDMxMEF|DQg62or;aT(y0%f5cA#>JX?)*R89g>NuUiD9{Py0%x1E%MXPxV0l|YTwghG zcho{#oLsMJNHwmaR1vH>VbNH3o(AVEoCmcJmUP)WqkvoCTzfrvviyMk2c|(^mhdc2 zY{DT*Y$BSF00Lsy)Kw=!D_|^rmAc>g>&vgb_d(=LnuB&YeX$wx!nnox24lzwu@N#uU~X)@?3`1xP`(SF;m2e)-Dq^Xe!RD~eEAE}CRu}2wohqg!)%`IWrx^tR=@_Wl?3Ro zaRPKXoL%Ww0y(Tx0ORJTPn=NVt|YL|cnop{u>Qgw)XFjq7`D7-4Rxci)K5`&3=L$+ z)ytS==b&SsE$AbEyKA*Nx>()Tbji$b)~q34wC}U8x$n817nG}d{IgTkL3$f_J;w?+ zgnjE>WLl8%^$0ptUB1rHh-{sEy=Q~(eA#CACGP9pa!3mCg$Zbv6^c~j{D_I_n`nv1 zZKa`gj1_u(yaZ`!k@`MJ=@!@^A!u*)K}ZR*aw=)s4cNs%U|rxc-HkedAS4RYPEH;Z zae-JNIh@>^q>{Sf03re`bjpqdCIYhoK_ESmLAVYQYXHbl#r!@qg#%Y`ba9?ye)0sa zHw7264E$qlfJuWP{xbVx=J+og3KI|11x73&AGNFru)L${k~)R7>xXxu8q4n|?!IR0 z6PIkiE$_3|8x|Mq78gn{uDz67a6F4dDSsrGtWH8MuW_L|T39J%{Ox0}-My$K#Etl} z5xqjDvE;9a=q+Au@_zv+ZpZcZmwD>@D10;*IHX`eQw)laGc(BJ6G2s9lx4kQA<5T^sn6%>gsRIRl&CaJ}=V&?Hs^aB94 zZt;-qlw7tnlvYL`_5_mGyJP7bE75d@%Ke8n&K{-Sp#zZ7*;lBQDol!?A_Gal91Q=h zl7@ojZ8t4>W^+SOkDM09o#kwfS#100Rh+nOhbFSSMA~7KY?7 z=39ZJ@O$tRsG$U*cUCM4spm;yI?$utb@wIxLn?2Ld_gd6(dq03>t#10QZ4O=v~9Az zo&UI9w_F!Y4mG6<7a;LFS3b5h8LWFVe*fp~Ds}L!SKdFo0uTbYz})Qn)SI*osxmR>l zPm1&tqP-$g6bze?vAJwXmmm%P6bn(tQ;>XA?)8U*y*O?LK{H5X#26Q(;t?e`rdv6T zj<^48Ko{sok;|xM9rjy)EXnz^M!xGjQ`(yH=kATI=tno9w~QKpx^brU2#WMwS4jIj z{7y`f?%5wP&9o8{sx5_I-B5K%bx3{4euL)*uRt0c2=2AtV!zRSllPXOkRaeRG*I`d zo>CoGotA#={Maj)@NV&4rM}93mGh8OV0CJ|VW-PWf28}!@R5aT(G?5@OCQl!+t(l% z5G~gA8Tu@=y~-)YO?8@sWGzvCVu$g>Wy%FbUg>TeYOD==nT!;oRzFkyJtz!-wD#@w568V4XQzQ_7l0;zzHse2*&4%Y&lS{u*$=5lJVKW{=5BE> zc8|D5+!vUH6O!{xdnFf}ZX*v$Zj&8W9MT-J9dbTTK5hDd{6MByXIfLYgWM_Gso3Jb zO0my>ljJsqqyt@IZF+_c#eg zN4%{Tv?bW;*K@yKl|l-u>*u9Avh#J!j_nXvIVdMv`{D{kd{JuA(SR5rAdL31WGZa zEo~`nUic_4Z2`;wGv~-oLYMEo{_FE1OV&s_GxN;Mv);FX_eS{ZeF9(s3SHK+uDj0&Sz-3dG@v{GG<~!T!Y73L@s&_K?mFW{DUfttAxwE6$ z6mlh~YiHD%Po8_FG3r`@J|LfN@)}*$*1u$0QQzU~*_cy{_khi_hiL`uie5nI8Qw#0 zE1IMyiVo6KMMvmEMWRBwh}eo(Y+;U)Ffch$xfYm*q8_K6p`m+Gt;C1zL^okCj1l2N z1`19IT59nXBntR-C@28eva7VLz~jrN1D=nF58{wGKKRZ-X#+T2AqIX7>ZKq+4kH9V zI8KOlDUbv_-`tervA{ME7X-KAp~;R4JPkoT7?GoWw;pI&+g@^CNoh1%3V(j{nnp1a zfNc2SjqyA0?~Ay~j|c0JqOO;6t^fI7mI(|MrDd$aMfV20e?fm0<;xb9R_3ef!IzH+ zCbNxvp6Mf4qV@+wpun4Q;K;X5lFC!i!f+r3ysTlU)B$3!B2UE#zt^;##zk=e@OJ78 z$asX5wgd+X0X+e^k=M>&c}$fPe`lsWp-pBCjVXbqL^ z>;f>Fr|*4Es!7!5iFRJn1L~!(3o@t?8p+&6s+hxo&gM!g2?54tL$K#<66bhioA2ae zoEU9*4tm(vHVvHtOdxLbb>Iv`FD@i|D>af3bGYyCPj6~6 z;UbhBNIf1%x6A@rk*Tl{AVyOKW03ziKa_APLdgprOwf}60}DoS5Cj8D1-Bi5qlMoW z@P+_|mU5)w{ifV-VkyDWfuGD)`a~n>zy8HozLfX>YAlHr=q>sx>N`;F-IXlMqf{iW zfvCyTQ7VQMX>@WCQLSOB<*Gai^7D&;T%}YZg}OOh=AdZ*{M0dgM&Vr%L*ZfYFG%&` z&L}P`0r8nJ&>t`m`R?&j#O)AxUqo*$YS>WQ;A*P|sN>GA(b0iWLsYY9L$TjhA&x9s zO?{`~#%6~4qTF%As5+Q}x7wYhKgc!VO&%fr)`PaOME+_s?K=fzUauvu);`ZcVWhpAmcP9uL-6s(xn%!$5;thNWWZE`lo3v53 zVRk&QComZ}7!a@l1q6iGH~{)@gG^RaTj>W&oHVJtETKYUs%@$X)s*Ux>J8Od6(l6o zoUciq0beB^Rv*ND;1rrZ1^)X4*ymW#=HXkwSkF52xUB~pBwi@o{YGsv7BFrG{;8H3 ztQ)G}w!xRSHz`|Hzgl--_a0RAm(ToZs8nvyiVKU%5@ti8suPNuJA1aQS|3@{rgz03 zyLD}3_qXHD_7?hzIv3mh-}_M~TE&$uUlH8mHCkF~W#acUr!!w{UAjD4)XXMbZL4~U zQZNr()vxFkmT14R?N5GEhC7cq*@-9*QCQk;nA=F?l$ zbP?WL!$tOjkVF^)5ue*fIg=B18-Cm2&#O6SJ%oZO@|tUUTZijxOM>P8me$GP)&D-1 zV3sH@I(qF^le;NWj=W|5)?(WgTb};cxdp3R9PN0vxY#-;0gYKHw`L_Em=2Qu;81Wh zcwPed)>~mz`PM1oL1+yU;7NN%J#!Mkx4JLe3MAkwf>0zSasX}L7MX~=5fQ-1&waMw z*nGR)-Vwr()XLrRc+#DW(;&g|gewtGyGPxyJ7!nX@a$Kljl1GAaK#L9`>njZfPO^E zf&9{^Pv~*N@Tgum{~8@c#Q3*#=qpop$i;wchWhOHA7$VpM*JWo*y3q zUO_M5`ozEDof)oAK!QS!1up0l=PTj*v^Xn`V!luQw12Koe488LYWX%P;zdG)Wf0no z`sez@x59nGt)NdoA_z;eKv9L{Rby4#swS!qRlQNg7^{%KYP1S=qm3Y58?jV!uN1sI z|H|F70|ew|%FE?dq`b;ko-UuL%JvL)kKnAIzL7Zq7%Iu6xkMy1$yKbL6*{T>Ve6Rn z6X{ti?J5LLN|sSeRYKp<6I}h+iDO#<#G`-!LZo9*%7fk7a6EFEHhC((8Uw2&!6=8s z3=upnm*ole3FsuBQoOGilQ&3nxoqb}2S^FAr-DUC{3M(q2n?#R`_T~CNF^{~sy z8j>Ozb(gzROrlBb%gzi4lyO&AzF08x6?m-cs9%s7W&#k*iirhBpsW^e|0=i@f`cVL zDKUaJHBKFbLNNJr`0B_smwQ?x)Xm4d*i0xRmITC|w z{4rsoA7oBIv^EYIeIB{R2uW%{zojTp`zt(b4k<7M3!ENOsfbnNkgPDwDr5*Ny;azM z2JlH+kDfjao@Fo{PC+JtZyIc)8TdV#y?3;L2SpI%W504J%I(Y?)(ln(!KDf_!Hqko znSdt;%?DHMxDrT}i|D&|WPXeWdgxzv)_-=GWJGn=Fu7r=p*dl+IrECzv=!hKD!RIM zgT-M9ef=||^M>;EcaS5Q=Yg==Y1c#YsknQg!S1(>zvc_J#rqJv22MTkIsEu-a zOAG9*5v4oXg$R}B-l}dC5^UXlezvBX4Fu%n#&RF9%B0JO%16tE8aD%Yn{!+G$Vhg%GeXHh@PtQ%yfL_OE__kqVY%s3C|2qnvR!{)JORYG~ z>3m$w8gxSVsep9sKj7+D6@ZX%=`7hpFRSX|a0MssSa!g~z@h?e=6;ZOS7DyMz~ zDTW`9C{?nCeobrUsb!gumo+|_YYmmx>H@K*_B#W1RTH%t6+v$DbH7;7bB8Su@3R!$ zbK@^G8mSbMo=C-+FM6+CPhMSLu&!Y^T>J5rJz=XMnDQ;&y!cCc&;hzRNbDx?0JJh| zA+pdwj3le3a0t#Mb%*>?fX4R>ONIFb@dRO55U$I~^LQbXDvxC(jW*I54X~ydtIrkd zHMMM1!$!#_#ThQ|@)UTsPb#uj8`$@_Y>o5uz>2$I!EuTHX3jP%v4eJ8)c-=}k)=y3D*QKH@z9^{ zviPlqZ}+#?^*oDg&%|a{u1=zwFp?b2OlwO?!J3;64|OfQ^i+Rz?T_4{)w`<4%BMzC zmzTG%zOS!s3Yb!(586DP%`KSbi0oBH-J=6yjlv&9@MCFVTyq5`Ph#6(ZnLW%0Ob*m>8^KGLAkqL~;gPo8 zHD5@@ZA@VGodjJsyN`0x^}|iw-J9?)`9=4YSK|8wMKsLD1zzSF$aA+53y4KYzln`R zYVb0uDP?NZWDy&y4_OL)b#@=y;U0!C4dpIi9r7H=sa48ZSUG1v^iP2$1OyUbTXSOn z9u7MDV6LDr0gNM0es?otHhj31{OjV=11UN z)E_gJ+gYZisnxioMr+InGGFX^Y-001cf8^$Dv+qtR@zxQUt={J&p!Fe?p=RHD>b?H z0R5XMXZ}3%#wC>jTdwB6(Ik52H^H9;7PeiUm$-c5?>l=M)TjZi{nxzc}l<)>UPFRwU~^JAk{+yoxj++_3_91>pN-;%U#{pl6L3X_Qa+{onZNq zBX53I^?CJuzMX*nA-j1zd@5YB-+`7qj*71vd+d%Ay_x^>{8LYPfz+j3vP1m~*(G=w(dx#Sh26{D4-wd1LSZh^6)veN0cI_wbjRI8{u4P&e#s`Z}sIc)uN*-abZfS^a~zQ19Z;@~I4t`twiN8>xU4Ab@Zj`BT(Nb*_Kw4nyu3pj z44HzTL$W(|oPF=9R}k8K{n5;k%uD2*Nbt-PfB!u|5}f(ru{Yn{d%ty8*`bmmrQnGQ zg*#Xuur)7mH8(mX#j2D&LM|%kk++zh?|+DD{o~b_-1%)pyt?&kZ@u)ReLIk1`oNySHGf6s?;d#V-OPtGPV^}X zUe-TYQ&H8tUt^E_?y_B%UNO1%Vb4A-FicH})2|nN2sCMQ}$nf&Op8H;M z_ZIoBa?-b^l+P4?UGTE#Wyc4CH$-nZK9PMQmpBCu=4Kp-GKeh9l~}M1vSRsFILueI zprQ#sRnxSf%4sN;1D=IIQjuqIl(tqxqpf{GfG9}iiS@80H&v-CpaB&YSqx4GREscS zwYsCTsiNJGWK5kf14O&0ql(u{vpWhzrXVhL63RcZw8IULZ^EV1;eT(trHaHep8#+Wm%f z{gU@+p;+%T*5!(Gb12V4t`_PZL+Q_v;%8MqxpDmFSSH~$XbYD;j&z|{GM^Ujyfd-3 z>W|M3bZg1oxr~5nsbfTf>b2LF8n4kBU5)?s@X|zXOEW%kPty;R_cGT5J4Z{>+brxC zk}&}atC2vRqu^YuYK4HxkwBzIt;iA36z1y4r99eUsL=t(>NNJNVz(*;`GBhf_6s~< zd|oFW7d#d)2zW3+@ApH=k!Nvjr@qhzr3*E6RYml}K>1>HZ(3NP!q~EG-_oECWNVt9 zqFxuQAjEJIo0EBxgn~#g>fO2IZjq;87_?9T3kGCZur^NN^E9iO!!f|2QO&uhQR|LU zr*%sUnXuIFahYsoI>EqNLy>NEF)Kpy1k8 z7)9^wcSVmey^sUhOMIE!*QN!Uyl^l`dD3ldrBqvQFQZl4sJ37!homNB6usPx%X<~w z0E9}^GL<43RTFF@db>*%a)BwW)=^=i&67?GP2)CzzNx9MWPJ^ZhIGSF!)Sw`K_n82 zd)pLp8AAzMB&ETWtHiS`?P5E-(?m-+%C@vDT$Czb1R_){@hnVVeDHHTM3__pBU(Vd zBi5FH#X@~@IrP|Pf|G_n|80TWA0OEc}chdL<{|Y$N1B|H(aZmmL z0>hS*N}T@)`)pQE0F&Yy5Qmyk+tz(2^Xa0;uQB_aNfnBI zh!o%7WwCLOlo)BOuGcm2<; zd4hq<$SF#pyDIa=^WI9Q-hjRaRFnQ+Cm?Ui6f@Si6BuJL;kxZJx4N>dN2&Jg-tb#X zkx;riU5dz&LFB&-)f7;l-E465?8T>8_w08-JBExvSl3~&4wo4Tg~+HhiF`_|A`cNF zX^~!^E6ugpiU>Lw^w7ajh>$Wii-OKod&j-)fJ+%6*FCV|>0eeOL(oJvv9(5tlfwRw_)0Y<=Gn)%yAnlqBOmKtUQ_;?Y z1A|g9lQm1?ZP;$c;?A4PbM#wUyme;XB_+JPa@!~_faeY(tOLL>fR@GijI+{8J-_Gv z#WvIO6-#P$PcCffx#r%%L7n-nE>+c<&bG1=eD3>aw{g5& zrNF$`@cpt>SZ;a&6@cqVjtY{%DpZ`xQB~y!3H;9-a3B>VbBaq+z|fUKDxBUNht9?o zz=nG49HeN0v;e9i4@zSN)rRXO?3x%5h}%0s5=tDr=A+4ybipBuiwQY50Mu}6)DWZQ zn7hiQz}!VLs@60@SBCcoHQkX-0Pt^*WSP4jgz_wV`6K2X=9*50c_V1G&(5)$BI%GY-|>OSC6F-^8`IJ2x3h zd2o9-4$qq{9LBwGWDbi0Hn%wMDZ>9Gz)I-1u@ zhOMKK;bu`nn2`9ben*R>&dLloqkO&clgQa-GV)Z+VQPM=UMM5#^{h$B%1kj?ZGCmj z%PL~PloMm?JE1BETmEqRfMx)>)e2B66(7Pc0AMLAxa6nADL||}3W@$-Y=p$bDjzm# z!;X@yxtdSTt!$_v=4>iX`sGm7kJWvwN#QK!>>h}D?O@H|wc@D4gUcghfzyq+BrOJ=xbagY0?Bj@Bn^z@i zC#!7E6>3pSJA*54)A{=LYrAfKL8nzVesXcE|H=25;SB&R10DNec}0`c zGXaB@Pk6+NIab60ZH?bCaeGv~v9PkFvaL%jQ5vmP)^wN4&s3t~xbddRHh+Ih{~j1q@iRCp`lnK!#ZKn=&eeEwR%e0vNlE;QkHMR;F2fsv=#py}Y};1xt@W{XwBXt;-GPX`xcK?U zzCU#N*}KOs{nl=@deP1kC)kHdOB#mIeJE#rt=C~=3+@7F)mPv7mJ+J*i+oZJ8ein|yG&9gm||3Afgbq$Y*Ldy z5RO**eJ-0)DiW&M1oYCN_EcAamX0|+vfaaeN+8oXb%uj3IaP?=WY|5%DFZki6KpNc z&j4M|>dFZc3SO~5&)K`+2j)cGW);8`a~KCOIyfrAKLev;Lo!X5(pA0kmTlKeRo5r> zf3Kn_?yq@_eE2a}^oPxv>E_I%-(Rubw9;C+>z0rIHj_qdJC2C_V8)aAD7V__UbpY$ z;XSb{*WU3^yWL)C(&bjV*W{tY>wj3Z$Wjt?4Ij%quaL5(9@dOyd~N2>%zKQ9NCMKi zflzyOshC|7BcWPCsRpz$qaM;H;a8Umczq|&1%rXWRtWa^3KY{bf#9`phbv=%fYpUdDRLrAD@@l=o^Mj@NkfLj+tb92@V+_dO@hDNcL9yn_4%uri?ng7Y%* z8-dE6wb8>6jLGf|W-J=qHUJps1yxEhTe`G!n<|-kEkPH`+ME^ri#HTH;_Agn+<|WD zFkNq6-r}IrndN(z<#{aDrI)AI-iN*?&cwHV(H1J9Jk*j3hu*MVL=V+#zzc$ij?5(j zGxHprnG2E@^S!ocp-iYzs-i-ogw<-fgVG?Cvl5Bay%okSV1EhgBp~iZoEuFN$iw6-6fWMMhF7lTx{a3g=km(yR`Wtg=ge z&bK(t=b@eg(2P@9{pZ6!?R>=|2s*$wa&B(CZA0PN1+>Uu8{=>C=4X=Y^+xLz3o6f+ zlX2G&E!cA8vZXzLxc7eJW`|DjQ<~nC^wqVajU#W&{J|d`>VNdk9anzqLlV)7ngZyN zle(4~WWEE;M|#4W)aHnn>o}zSi1D?BP&X>SB+tJ-#MT{xJis7wm#KKyXk=H2@cEVpf2%5JV%w0)p!d8iC0 z1VP=y6~aq_H>QfXI+<<=qf|LsQil3mXn`70w*vo=7Q8g1A9%$aP+Or)j0v|1$+U0? z@Wx`nC?&486alh1VE1+ihlMW--w>V!0U;#Sz9?kcV@_(wGWq-@WVb5bKQRMb&$h~F7K+p52=BcQg{bS6y#o#ZoFTd?@bKCc3Kc0H*xDh_HC=AbllWOOVX3jXfz;qJ|;GjB>=m5KD5X_SizKIzw&lxM6UFvHd4L28|Lu&4aSB;)9;@vr8Y|jXGy$j> zEGTWT5n!%WT)v9IbFhaM|KdDsJSH$mD1voY9M_>N9KQ=b`LX_%f8K-0RVBiJ!5EXX znf0{`aqXcXqq`0nf{XWc*6uF^D1^=&X@dZlDc-+K(Vt&nTSS{`aJk}J4_~FGs>!=D z70K4#-P<#t-j14M2v;gLXO>y=gDoQIHIG0I<3{BgM}c~^z;@fSP|LVUOZQ-{ZjR&1 z2v~0mlRiCT5ODNX225`yFS*^Ml>@_-UdI?j0?Ku%mBQ$4N)c?1WpnISa~lm1ROj&p z5_sE(Kp_}{9M>5>#33o24*){X;UN|#0y_|)SW7kc5B!+i{!i3andg?Ws62k2+KO(f zcGa!?!Fg({1*ig3Tm5>03vI3RaLiU^PyKVd5w;e8K4VV zxj?cB5rB4HlXYEiesW%?gP`j4nR6TqNi>hE%!W$_^&2YpdDlc%liA$YN7H?MWQD!N z<*2ZgxJvTr#r^%xhn;uKym&_+Yr)jt+|_X21n?km_-8sfNod*y7g<1ioau`o>UigPq7AG;sNCoF3l0d4(`vDRYjFsH zH&);wKk2X%0E`7sKhn2!wN|+rbmqX-HO1v-Pf>#XExRUW?J!h&pPI=Hq@ydsq^1KO<2-S1{}pEW@B#l4xk=`D;7qU4K`0dUiZQ+dW> zz>L+UG{wbWqY%X+xz+x0+q#nq_r zeGL8&N*OF&ES&gMhbzYpJ0(Uf!4d(d4c9rWy$G?IhQFmHq2>ckv69X!i@Mev8tncS z%GrF^f#uQYL!Mm^%VV(%aM>BDF;r*R-(hWc;*d;Qfful>EG z`6^PJnfUFEm!|8BD3IkX#254r=!2liqzGS9E1J+C4WSdrEtTa_x22@KOj1-`rvTbC z3}ANZ=t(fl&zw9ueR2@T;Vwu6&)Qi0)(lt8cT9D@1+R`{;{^9^z6BZTs#{ZCSJG5z z3$&)!u3Z#Jh1A_^ioBMHIMBUJ-kn;@KUcM0P`HfcaZs|Mj#0< z#jhug&>FJB0F(jpxo1uy2!LQ!1I)RT9uM4u$d3o~4IBUh%>$35O3BA3!F1)^R-8!a z!KRBq4Fm$vT0k@c>8F-fg=%N_ElKUKTl2jY_vxX?l@g_h&TS|KM|l_7y!_>>|B*omEhT$06Pb5siTg%q zsRTwwMSPF0;5>GfkRuHh$_7V%Sman5x3UH{?H%%t;)-lSP)M-gsX2p-k25&dKq$U| z0FM#~tuesef8?Qr6^v=fpnvC$)$`~4zjf4o#dZw?d!@d}dBcjqmb#^*_r-y|(m4Dd zJa%undRHdwwhB%0Eo%X$FK8~^^iTi?&@iv%f9xr*_ssVclH&eNPZ(P#t1M_^G#-Xp+PR!H!|EQ-F@yzUt^t;3X@UdtK6A?=4 zluKkeR}e+fd<$&Bl0jOdFzP(!w(OBLeP(*z-`)mW7!Db7cw3Mc#cfJ&f9x)JYDRhl0bsW4+sY1)wO>I;n$pNd>(@v)xsa zl7do{s%*mG6LgBGRn(el{k3DY+iE9jg|&UKi9im-v12pGa3&js;W1TJVq-!9m21b2 zDUKa40KmbG+!2GVNMIY^It>(P*s#HA5f>6f*kSR(M$GX6W)q}<2kpEzl|5P^S_C!n zv=ztla4ZQRwKzXYK0MFGWdYmc0%{3*5@^vLeB`WvR-k8JE-S5E@<}EjGUeY`{be<< zqOD)JH1lfavw^kh^_l;EbdI57@{K6FZ^{c=0`JJOhi#B@K>2bs@pzu0W(Iw zRPEWb;XPnWldcA~v`FG-ci(fK&0@iVD(lsTno4~gW(A@!ItX|%G~ih*1IuVESxp#N zslO!Oqb}hrB2P)an)Vx9)w%v?c~oowAU+*r4|4xj`h4*!WfiOsa|rC;cvV(OnNv$S zqbKX5gF_yp4{>^WI~P}l!N*)V{0@f~&H3^Ardeac+dUXVNXhH-ICCW$p)R@pPjdhw zzg}_6mvi`aWAD$y*WHnMjg&6j|9J;s*v$;)egGOhk1ulXDIQ+rowIEHZVu4y(Rn<( zL`UYvy^AydjdpOlJcl|0T4X6{qT=M=K&dH9YN%+AG)gQ{C~|P}0;BS1(-&bX6F%_B z%y1eE&fma#HuMxr6aE9vcyv2E9rduRCpyjEuogI{*hk2}4udQwb86=M9kBBe*$bP>}n=&U&KcQh{u!;87a5ICz6D4$c9 z;N!FEE84jk{b}>ZCo2{|rQf1I{`E)4R}H(!Kg$>qS~AKAmJfoboFp3P8uET7&E=R~ z{WJlI+L~l{fsh=}8JCV1Vg$A~iWn&vTtnn2IZo~&C&_~(L*~%4Md$Cwa4Z zGzsCb91p|ASOBWFn*L68(T=oTq_+99WnX z>^a7}-K#CRmabNrm9Ag;ch~Bn{|T&yYpZ~3(=vYWoptfo78jCJ0*I833AT~j1QP%V z#{e`P&v8B>odRle!G%N0h~;ujX@Dt91aigT`-3N8bh!cLKRsL51m^rLZ9og*$gQ@O zk>fZ~ z4(8z_0h`n>8_3)-us^8nihW!uEq|zQNxv#&w} z`_lLBo}K0E^|=b40T5D#*!?KMRc1NmUZ2zFrR8u3iW9wQXWDzdGK)jS7kWTbiF2CJ zZlV|c6siejByv{ul8Ert6gn4rLLpBGq^)rFHKvw%1m-xFGz(0*`8e~aMv!mHB}`&d zelDb(VMnuy@>o`7UADaUfw!x{`39)soPQICiy|szR=QxI;F~LQhdblWD(joCZ(h3N z_%AmufZ%l^x3IcZojGynfy8@1SZ9t@yKJ_%{;+e=W}y4c-?%UsbL#ZI>NZ{Gnz#2? zef^7tdV-pLYxaHm1I9=65W~cJ;`wJyt*E#aNuHa1F1cFL1n9NPN>E)1+C!sp8fn$W zQ3b%krdnzv4e`k85qTf#>+S7kx|y^Ix?NgmOeG_iNL#AaQ?;lzSsl?^}V=AvBIXh3qwqP5E@y0eWIN87;N3m&7R;4tFQe_&;wJ3TW!2wN|hW5;GrzIS2} z06Fnta(p9{TTI6lr%%tE1YVm8oLB>XU^M7~n+r@^iVGbvRx_kXWo6zD;>2tJ+$zAs zh>!g+_cv?a^XDSy0XW~96G7ez=3j|l01fjYZb5&b<12DC=~tgcM{pI!%wO#cT86XP zJh<|u=37fU3Kz$muDD}M`qEo|fBn_+pi?){r`;+mc#gCORu-K87+p3Tw^lT0b9_pB zp^y=Aw=$8|Aj&86WC8^);H}G*W?n!6h<;yEP2(aB`mwf!0YxiR-+63fp4fd8pCF3r zYgE!?Zx0=EH0IHySQWfH7z!G7I`|biOu6zRw;&IMmRLu;LcYdXjftf7n^_)wf`B{aljFl5KTmXvW#Gv zBpZ+Tz`RcB*#(S4qLehpiE0C2gi1M-(*|ivBn3|gea+x|feZ-68A$nuYOFbQQ;bQV z`2B~8b6ZaXVjpBqa77cGD#q{37XzIFh>1lY#DUX!Kg_?t5eB7H2!25DP|}?j!=VZ# zE!CNm{~B9(JAeHSdf_aAId~W-LrKwQ^whov<7mZFfxeDXtMkDqJUbfhCdmd_2Md$eh znLqVcD+L&P`N6h(sX6Fn0gt_`)~~(=t3JB2AqsKvHPI6hfyxj;$qB!IZ`8YV^ z26E1zz67`#VU+$CC6=6)1lySp%`y5p=FNg2I-|S=mtMM!PB43jCqEm=bb$Kc!B+B7xF+UTi;_l9Z96I9>ix+z; z1cI4k*w}!wO$Alux*Yoq_K+QDC)EfAnXo;PPr_Jr&Q_zlh_j#*>yk2pk;`6}eITP` zWR3#d%ur@~Ap4+*A!RvvMMMEAz?$;W>GvTA2dt)PP>;Xy8Rmcckx28eKmMXC z5fb}mf16Xwtm68yFz)ojI}Zis zV%Bj6saO==t-ukk1mfKDzCbx>32=^LyNI6|F8_G`m0a=1#qWFmZTTl(xV5u&<+2rb z^meZpxVFq$T9XWU!p%lYh$+A1MjOi2%k@<-2TGf#;rLl&eKGmY&HaNteRtfvYV7F# zCYNo|qKkmRm&dAEXp|B?g~q~dUoBpY;{}j4fmcvzl#Q1Q*}c_ zahoAy>gwJ3t&!{#AF^{#80Y5$vPj-(>P&VHcaC*#>pax?a_1YJXFEUbluW^c^Vkx+Z!SLnS+|pyqZhbZ z^-5!9x^k#;ymGRVA>6*o^RMAPQF#CSC+;R*0=VDYWKN2dv^H=ule7h9d zOjZCsh^YZ(F(^3hZP6lV0R{57LZ5gK@If$=Ky3m%9F)A)2!DoZCu=bv0{^c3YcL`Q z|C+aFKSh7URs9Q$3+5`|xi-Sse4E__3Gd74*}ev>XIDTb3$JIxb@K1HalUnmcp7~U zSQygDIwY?%)}`x)>Ht~eJW7cu1#Nj>Yi_#B5&ZXS1$J%0S+PU8}g0%CVfKwojH^f&Y#NiqlR8b zuX0|}urZ7gL1<&x$K3&F5Z<{8z2slD3_gQ@)!W2}@G6XC0>#cGZr>3;k6^&JUNGM} z1!ydRbCs5fT<&&c$!VvC?4`Cn4W_-bp06$O+%5T^t zy6Qr(c69t-L$t#+y+ZG%{?6P7dAloyT7_Ga^m%@vZ?ZRmBV=%68zk%%;uJoN*$#&y@899vfpgVR zz%SGe{6f8xg;6{33q6kcg$yk43*DE}ECGHY8^VEBB&7a3lWb4K5-+Xgy?Eh z(myU3(;XB{>5d2v=@^Nw?|eZo##t2-mm39rDjZ^!Sv=03i$biRZJ#?e1B4UUFN}Rp z!2lS8Sf`n@Ua)zB;jlS>9*$6=<14DVeKi$DUUjaw#p5hy7wu>Uwz!b7Tv@7f8og9e zQ#+%Kt&7gR$ap@vg9hf$MqsaBMy zQJk4R4!PaaSfRyN1lPk|jT~pC@mcNYfs)A**F(o+vFq!NPisf62@drxa@ zc-ieOE$vZ^lZ)c?B*F}$_BPzR4S@bLLR=162acpNKS$a}2{rQVghr?F z=r_;x5^^=LF~tq4U@l7xy2@es@SaZWjiE*^!0M@I7t}Ofz2UM80D0m8XP!LPI?g7^aXT{G6Iy@%z2A{fQVV}LwWbkdezYuLOl-j?w)@~>>?z@Dk^cr*p zzOJ59cxb=sUOl8|8jZV``weE@?$vl)+?mIq;_Gq9qH{rfs*e_F^9u9)SjZd z`H|uC8Nm`c2{o-4--~wxWc5zYc`)ZvLq`AwWo4r_&*-_LYw@y%y1ku7*IjM45|3T# zHzv&a3p-bIecNj+#kGm3h`xvV5wnEQf<2HJhb1A^9pxRt1L5Vf04)L&Bx>Wsqm&W_JM zOxHlwRRA!g!g4~4#>Crz#cfJ_Nc^(+4e?p=r{HTAuqsKgw8%laSgV@_3Em#;axXXu7YRur`I;b<3eT(c`~Y(R0dUb^)A55% z&9y*jg;azTe?$-D$z5!6?9&e>)_X(JylZXF%CZkc72t&zY%g`O@oUJBGf&^r6UisH z3KH(5A?z-(yVnix|7uCXf0eK0w%Fg!&Dj&FRaJD+W+_^m?s^{51AY8*=IqCw>a@UYhyxQrA zgje(kC_tkP3&IwwBThb%X-!@?FuY{X$Emm@WQa7NCe-8#hg}(WwYkEP1YO`J;E#5T z3~;(fAkk1roCP|dpI{A69x+DzfI@|V#GsuA5Wx2`OQ79C5_>t%9>5r3VtLFT>xfZ9 zkR&#TIaN0cyq2PIXjui_hj%3QlQz)x@4rFaQwvkQU6>f{@t7k3Ix2M7ptoYH1cw@|Kp|&IYC@!?xaC zwfIu>?4>PV{ZO?n9Fj-c>!GQ zNV9fHMVXpl%Nom88>u{(-W(=amxwjT6z|VKP7EFb(3X6dAMPA3G{@zuTyki3M}$tn z39Q1#ABcE9FXnQRz{9|=Vo*&SQ1OvfQ_v-Bylo=suL!UC^U#vYsI6+>Q%P62xMnkw z%oTW?4p(j()H|ZSLbSTQzWMew4_6$JnFq;k{#P}pfDf8;W$1cC_>d|czkKT3QjsJS=FZK~KRN@RN)udF@mZtBRc{Zub( z&97-h!WBC{`un~eip&vbH0nf-ePCppx8Ab6tPP>9McBJBhWRH1XCPO^3w0Vc7qI{N z%z!?xQxRoAyMT)G?Iolf%)9JSLW_Oi2Ea=VW04$#_~u+^KIf(SJlRAEiD}01UX1r! zeD%C1V4i70aUs!!7KkPsVe)(XzR5CiG06mKN5V$TGm+6>`A;MhXgwT+e$qnS0a=nd ztc|JjCHZx^0==RT&JIvK!DEWmK3?aLupCVBGx4B9fNoO`)m6aY*H0#U=xAT2K?zk zp2!XB+T6J0@pB8I2HrD5G7IYijcnmSr@l-=m38J-drX-%Mz>Q;OVBY-U4>d)8*@u* zXMT!EGN;VsD>IhDVGmce6|U+X<`l#PLrIgtPE1L4Q+DZ?d#dzMDW#nfInCqXCYmA+ z0rEFF_FZ#23xpIdIU zgzcs4j;?DpEGY5o97gZzfwfB?sJCA=Mw>`d?KKY2`9*;nmfl$3T-@ktce#wisj7Yb z*SEWy>fpK`q!v+!nQs$rSVhKz>KD}CFQ8;oM?jG;ni_|s8^$bB<`+)6lv4!hJHfef zVN_vIp=eeyID@x)PQ36&iY1O;P<&xnwix!jQ;F&s8XMSpqSPQ>Y>I@MlUF9+yf>GX zCjy@6mhNGJWx+$2C4wVHpH80dT&c^=sbZtmy?K=(Lt!MjX$d&l9?INDzrf9Xh*$>v z)03bc8}AU%G^3jcB}x{z21dG?s_;^-g* z1qRRY!h9wZfA56i{mcnihjrCjzVwcs=}YXZ^fzdc_wBX9aqr zVnajv#HrWIo-J!>DSLL&ynL8%x>!C;W#{GNyhxXc8)trTp-5-m%|&8aRf|Nzw6ri^ zxBs&wwYfo(>f&>fM8n)XK0h}xK&;uG>^TA%^u6JDb6o>1MR_m}{$A8;o^tUhZc`11 z0G+`#3_OHH8)|Ewiif8b_}h|g9c{zfLv4aKSk8NjCyNgj9|3|EZb}CHOZX`Ps~Xa> z052MZ5PN-nz&>*v5$kaaJ&bf;OzQ_z(xZ7Zref<833m3YmYu zQ~c9pyh&c!Wpp7iT`^vhZ;O#FEhzJ1+LIdH6mtOat?oZ$jOxbU4UPPgy%rpY_ zJX-i!KP(V$OyclytHqmA?%9925Ehy!4S2X%3X4q-6?kr}NqG*lgTy%W651n+eKQ-4D}RFwC#LrO( z%3wI(p7(fHLwnvU{%hiD-Y<=OF?jP>jPrMUpwBmEQUaIY1)`q#aq{}*CL~yZTvZiS zq|1*AOOeKga`MrA!pDW=%|f(Uh=2)vi4ZwRKxtU))q(Rw!; zfLkXTaH1wBayyaT61MbOR$6GOieVCtRD@_uq;%c}r#n?cTiD7}fGn~!(7E6Zq=iJ{ zG}IU&!!gM zdtFmPz$7~FerDgTW>0Fd2azpp8@eqrN#6JsFcU8DtYLnw2I9fw+TJQuS%oSb0Lg(K z*P@%X_iD*?+Ra*Wi5A6`D5ya398_I_TwydM92Js!izZJ<1XFoUX$K+z_fC@XS0*dT z%0@{=nDX%ekwDv8%a~kA7wPh`s^WZ;O(mHdaiGY8U>-l_*gp)lk>^fcoHmlHEL z);!7=w_xW8v@ks8#iI_DO8;ie=jpVliKyoyHm%I*(EO<1k@@w7v|4$KF5=aqR+q6C z5o>+HHnq$R`#`L0?*$ZFwCZAxExi8vXFsA3b8#C7xBhNNPr>>yBsF{dll~9#anUDY--fiwmfwPoLy& z0Dx=e=&=MWFhHk$|HRRwGlA15j`3$9%rjD#0KX{SoMBHcfnTn+4x1YMS5D4gG+T{g_g#g6vUs<+5AtTKxayc zl#cKU041P|`96t`V!pD!! z9D9Ed76s71Z#*{RIdc+n$jbd2vwDCfsscB~oDPOZ4eSkyE6Ic^I<;{wY=wsrc}b~c z)oOv%V32nJnOJFE{HAYFq=ussQvw1(g{KYZ##YH4OMCpAzm5L;chBB<7}&%DuLd)R zC+r?{`%e!IL_L|Ca$bKY%Q*&8l&za48Df$716l@WU!wkpJJ0GkxqlbX#vy7-<|d$B zB9WWREg_QFk$H@<3XX&P*AgEjZ*O*>3L{!lj5g@e`UDzGpm+ie*Pu`hT9H65DUvHN zTjHpK7HuE`9=)!Z%74~~hJicJIFYDIh*#QCj~%7#D1>V_CKNSFX(FK%Pk8E-H}HJS zRDO!?i+IH~6AEtvCCpWOq`oZqBCS&tOU(+YLaH_c6e?UN_^dF46cnj~st>F%igO8E z<_Y!~?4EfQRX$u44aDVm=Y(d8KfyiNVioV5ILDQL;qnZ8hGBk*L7we{JC_`Db$^^f z#EX|o$t^!6Uu(`SOTGY>lRN}3p(2cxj}>&3M^=6N)!M){rl>PDpM-&$KZbWRZ%_H6MH5>EzQB|R(Gye?IqAeT7&SqZ8 z>}2lg)O%t=t%(mrQ1@Ps(A}BWjIdh;Gmi-jv>G(Q8se&CYcqp%jEkvYC^3UdE8~^q z$~cOKp_&rr20Ws}FavsHG5Vl*wwNqV#Z!b*p&SDy&ssm-S6U*8R5#;MB!mP=!VH8!*dstvMuD=zrcg#9Nr5InTQ*6cgO;|0QlKp@`1*a%xssF6zW;lD z&&kKOq~jyq-#x$m{r!GK_9dBH8R;p`ogx19+;-1JI%^i+os}sOjr<5*)MlS$pS)Xq z+NuXPJVhhPX;e!%!3{K8UdOZq2GcSv0X?Ajtx|xNXrr%_OZXjAKZKs(N)vOyO{qvU z`1@1n4>WfbN9F%SmGUq;)L43Dq&Ul%A@p@Ld1G<(p_5gk(9Rcnr+!$t4jj}TI!YVMGrNt+6-mZOzfx5E z|BlME00pgyQ`~Y&wGyjSLX<&XJ3jA{8Q-I&_UGqZeEwyh{9Bol_)l7)wCa^7-X3U< znjC@CXjkUUnb`6RBH#bwX@yE$A&QiIBXhwM5{{`BJR#wjK9*fUKBl%&ypQpOG&6UT2A=ur8K|D>^m>xs9nszN`I2btk%3dC>N~3+;#5ev<=vyC%HxjoDbe1 z+i-FZ+Lpl=N=RV*4G(A4zMYbgifEl(ieCj>!)*x})TS zUBgk(onAvd;R;drAWL-RMo(S?o0!b5nL=ObHQ#`1fwcf^3@*5=QY)j2@hPO8>D5X* ze?0YB(*7s}%uCkRCT)wA(w_AC>7Ka$Ta!ChV8@DzoYamNBQsjT&QVBuZ_Ku4etJ)c zkirlaheCygCqFBjk<32p?Br*C1)o(9R8_1e2B2Aeds-N)lR41!liL+fDL%)iphq+f{ySxt+r4*`! z#+M}$Mg{$dbhoJ&ObN7M_>zUHhI2M|=u2h?($ou^-Ut~D)6Wl626|F{cg)@P`(Jkt z@&$QWt6dG6tfpAyd{3bLv){qPPz<_jGc@FK@ip=IUh9>%r(zfum0fedcPqE%)~c9Z zfpAlL9kLjV-|^Wgn9L))JTc!^O3I2zZV~CsBlUU2okv=;NFa-Jq!W2M$>7L@gKTw^ zt>vVpWN8T(C?TDOaRX=8qMMteKuD`}uPQ5Fm9S0NIGfE-5|$@&?CtVOLpXz8vlbW2 zj0X%qfz~>Nyzn6@n)Cg*4v{+7N=3+ML(C`JE~QGEolt4hfjDKxPu>%MlqxSK9sBM% zylUB3(`Cxf^qh>+@$1ANqD$>dCyR)$yG4BbFN=q#i;(xUsA(XNR@lv-Ix7DLdC^Ab ziutm85-Z2%5*zXcb4h3>>1rm0CX#N#2_lx!y|c;eSyj$~Uc0f#Y^XLQFn@MdxKp-J zo2=%RH+8qyr*YMtiB7Jwb9g~D9QB5QxV>GduN9nOAJ;8#y+TQmkRu<}q=8m%O&+J_ z?}^D{_(8mfsVjjVBZm(hIz&z3?+zpFaNy7ZSY3EH9TbDYCt5|anFvf_=|2t-wXg7x zg21TTihrr{T`2$QKF><{^!a%zMrOTm zHEH7O=Pa42;Em$0ZFN3*zq?LhuXPWkX}Ibr;d&R{{-@io@mKlfeAqK9SCyYN?Dl6_ zOyv9v?j4@lamOCh8==as*GY!vvb|&CFB8=;7T}3sN}pQZgc`yC>TDu@H?g?M(n{iY z>J;`3`KTTIDE`&6w!Ouw;#Y3|Krv8L>z-LQ=6ye+o%`DXpXYeL zR+E{PzU|_Bok6gn8-d6{AYODc@R|mX>d>Ce@V! zukuqme)q822g8rsZFA|;DNKW3*VLnzFXNgCRa?C_0}v1jTS zr5;HL1pdlDpZCzw8#kP}YU9OA*32!vU{OW1AyHeG2p4sQ9A&ebn&ZzIY=Ntqw%>Q@ z;juM~&l(!^N9N2}V0AbeT%{&!O*AL1F%m4!B}Vy>*#GqLn++$RIr0a z=w-uh6qwRr2CKVG^&rDNW=;jJ7wRhIU!30a08_kuMr$wYK9=fzP;L*ESub?h^U~6s zg?Zwmr?ffXXDhgCUq{n1soO!CG7b61B3rjOe9HYqH!|8$@J0S|xkYA|wIKTze=XdIj>S064bLW`$30-^`sJ_NN)MeUK{gKl&lJq)fV zR2AbEG4L1;FLW}`PO)@(5R%k?;x9itGnkRTE^oZNR%z(GZeGxBy~6sqwIgirv{dsy z9^wjkd^XtcAWjV zPSb4*lN@oO&1iMbNSN~sy5FefmGQkVjCMZi3=S_)sW)ClE_`XjimY3){aaUe@g?Nd2<8b68bW!}OsL#<{tCJF0}=JU(* z6sm=?Dj#2v*;+OfX zo$3O4pS2s)H_}uC11hcDXYEqYS$=mzuz+80L*I*wv%9iQwSlTl``kH=IRgXu7A2L@ zoVFZW)o|Z%&&ySRqZZ1NsTb9doU)i~r)&@UcrRF`CS6yMg|0O&uE#~L7#-|gPu6dm z*)LquQ7d46c$GuQQ8mkQZks=R!v*8R3&*Yz#&5kzm^W|th{mNVDGTRzjc9Hi>~9n< z;aQ4F6+C~_#!HmDg$ZiF;qni@_4Y6h#(_Gto26A_2_=(7P~-G(5U?96fR1 zox|u{bLb#+1LB?#5L6ichb&S`lQR1Jd$MjiaU{u6i3_1O&>E5wmT@9WB0JIV26EI9 zWMH`fwQ^E4N5YDp#^fEq4Pw99lS&$hv^*-(yMJn+H}JlfCSU(w`>=`${eJ#xVxryq`e4_faQ?E8I}Bg7RPvO z@_zn0#Rqu4n|W9>2<03)E4UJUnJO_lxzg*WO%AX}6th{MM28HA>?yXkPe%P=TG5pp zLMNX_5=yQa{Tr#P2BMkx8|g7Op4tPS<0%5s4sM+}^&4deqM{~X72DBkV9k?V#bgYa zyQilJYE%3*@4GSj%+X7r*(A_?xzul_I(hRoL zG&IE;LYxuDI}Vc@z39Kj4{Fv-9(AufZ~$^|5*GFWA6f%YO+Z6wI5GG~4=$5&sA5d@ zp--M~dibd1V~QiFP4)PbLif@uDGSkn)YE^?IgFCKUyrO%M{AS(3Tgb$`d1lx8 zwwhp}v1oE*%x((#W?V}3kpZ3B6Ur5c#MQ_;63!x_!YeDW2sxP(Y1?2H`Es(oLRxxB zNu|RYmTP?3>Bh=%9u*Sa!#-*eL5Jv~kO!2MN9rcuI$U=YehQ66Qp=fkK$M6Vv%I&azzbJaa_8G2n}fB=jushQE-X z$9=5$H7bU+PpD;jOh`n920kpsK(f1>`hfJ)a36=9&Ju^o<#FnZtL;(6Wv2cJX0w{K ziv}%TKRvOmic-Zv=h)b(e;=)YUqTcpQ|mMPs(rmaIZcSU>50c`mJ3rPu(}zmm9qCg zqd8!j3ZoGlnn8Xtjn)lH8RU0aB$LK=tTeXM&>-cfv6AMJzfp#;QpzR2E4!7&Gt!!{ zE)HtkkCKm73NHk5C3_FnK`xH45&v zkf8?;P_a7sI>3?mbr^x5gEU{^220>8nG-d@EdL&y$CtwHoHwMb`WC*537pVB*1pJ2v~o zZ{I02xMF1B-LKp+?wQ6C@n4U=Y-tiCV9A1-O1nNSZwuK%*5{#uK32Ih^*cI zRL8taHocZLtVy`ekJ)M|SL6QjUn+0IxncL0T)IX=Y|%LIzugC{ziH}0#gEzfakKj- zogd0ORg`yTce)#symR%`OY#Y}662Mi58q|jE^g}B)Pu@4)QHju)ux1Rs5cI0N9g2a zis9L_qDA?F*IjP5F3@O(jhY1lH%sUhI^$7qxxfka0d7dpQ{|Yg&TiMw7P8qi>+JS? z877K+@{apN{3zOsAjov$1WIw?7QyU2aQNUP0#64e_{afNV$snIke6s|4EIR72{gG% z)grOwBT8i{Um>B#{)Ef{bOpL&&f6`xIuDCJcpP{3+Y+AZ2;sitE1X*FqfydeM#7rUE~BuALLsiOK&Q#sOE5A$<)`lCn04M?)9ySoYodZPKd9CSX#)q zmhcJ?_DOwdyCOr7Q5v@)nJQ~;Thew>8{gL5C`V|eM%N&?Yy%c@o#ig{UC7h$nyx)M zvRX&DMF6hMZzP6BTO-#f)HIa0TN<*$0@^Fl_|$RQrUQsEL0$S`86 z!w1~xkO6+8&F#TkQp8H~S1Ax4H%52Tb_u|BvnnNn#qY?aMsh9<%}n!{GFLLA^L0qQ zhUzO?3JP^OiiKO3_~#LeF;>x_PUF=k+WJeY)y7;C9ln)PN3fcoq>;OR`NIZ}GQ%e? zs|a`Z-n?sZ(Rk5M&Z_;PcwuWpq+DYuKAYz@R{as64z23JJXiQHa@r~^?U}^J4_9?R zAzt}sT7x6u8s7p}Nw~)MruHYJ_FgKt`ZbQaK)JPpYk@J>Q=?klItg%J#S|nLU|$2z!n(ttIXG{>eQN43=Tu%jtVke5bVM z2PZ!#-LtK3de80H^9`oAq&+_nA4q*px@S2`+md|WCEhEyDc(u$sg!+$9Gm)a+87~~ zF{u{sUxAGAjft^t6l9?Qf-F!#Jji8a2|BktE;}H5OQrx`1I0U9loBQ2! zacUtSYMjbKgliy2uk%r+KLE)gjZc&7Tc@|yO-vNJf0B8P3+jP!D=gS5Fa03b$hft z78~3BjK8XX&$XWDj(*bBPk!NvMm?U#D*fALU$S9iSwls;_~zBSKLCxEf^t&r?~+6& zK*70krLtHSunVlkc5$Ak-Q*4z`vC~f&z0wU!zlfvO&IV+g{MZH*Uq`a`5Hg9DQIlo zy$I1L1RrR3a?s#2Xe;d@kmT(n+GB?$H{;(y!O%sBJsb3x;=!&}OP#oUIkg#Z&5enL zFGi-JsEe zG%tM=y{HI(=KPTZsSjW0bLvR?vUA8M=iE+iynPAlJ9VCTMcqc-ykUCcw}^sLqs+#M zm#_rKs5gM;&joJzb2;S%U`!5H37^jWeM5+rFI6U$idm7 zETGHPN~EN=2+OY~C6F#(M=-uSsFfnTezY-B*3A+B`DSfJW|qf(fy1EHiYq7oO60t1 zMc8b_H0FXHUvTC>e|CZtvxzWeH;U5i6MAH=iXS}L=y}0uq814Nx_rU5L@)VI5^)x%yQ>vbe*vmKa zW_WnB6M>pIvBrt5Hc-2ymS0p#x=TsA3{}WQLSuVvmEacaWkR5)T+>!t60TQMmG zfM4rqH`kbvma2x1lnPiCGL;Z=*VMEYXBZKvHaAKYA8wv`b?bU z7SI=C)bbRoWA1kG{jNbicVmRMsEaI5m%tgSvMb8mk#*Gr^R4TA67f?NM(5k-I zN+x7)%D4X@UyaGU9MF=4ujVW7wXZ6Sk+P*~?K8&#A(7v0$Ya z=j_mtgBTS9Ozm-`zZk)iNigdF9X@mfPti=!k;7696rMKR2nki2q!e?$K_+Kvw49XY z4f)Gbp?4}Hn8--w$yBYaGb8R$R7LY;3d8I?cjQAAOPi@bM6i6d{QOudZ}tPFLO;VD zQNFoDN}nyikYvT&Yxx`atjyU+qPZV(yTZRMnVYFaE)8c>!XHtOA;M1Fd$QEo%%JeC zmg=JE!)gRWr{ZCUGbc zlUL^&zzmE%|`(=)7VWRPgOmbLF?oIFQLjp;Vb zQ+em$@G&^dz>&eeng%RLj87Sy8O2<1o4u{xT2ks94D>3?(IaRqw`X2!&$8Bqm>fjr z;FHZ)-X_zbf>5nQXC1?SIgvwXJ)zEMN8LBx>xi2oC62vKyAma(H%*vQOaOR-oA0mf zY0Eblvek1s!WSw2wRr9JjuNY}q`>I!3DGd@DeqC9$k*(ZJ&Xu_jzXWZnW}S0j(Yl< zj-cr=eIxLubOlmcR)s<`Z11aM@1;$fqp*cq?igLWy|Y+)FT77|YJ`6f8Bk1uNo1*& zvW$iLLaoLG3;+%7Lna*ApV5xy)m$jhS528!2A z;uP7mIEEBO6CL^v)0Jcq!kZ&lU)!3aI=Mbop_A-uI?btZ&`9D`f!mnk)Hd+T%w{74 z(PE{d>!I^7qY)HfxurS@jJ zHq>rEi7k~R3#9-g3uKr;kY6$wNyh&EZ;7O{Y z@rTs=XJ#Uc)q<;0?#KRAvV$DfYF5SJ4}m@s%0sa~uSa}zKs?f)cuZ>z`Vz%8{Ot5= zefF$irqz(Gk2|iN$S5wzEak|Izwf)~j)<+wQd!oq`$r#e*ICYXuk<@gH$DH3 zO4b@_uXE$tl3V%m;sN5Bbzewl$ZQ+>c_jX)jRQezaj>m=XuZj)9JUALWMrm%crNBx zl@$M-%xWIE#^wum7lwa-f2K1dEw|m_&q(hZ&dBgP=x>#_EoX3>S_{V8AS>&qK9f5! zVkZ}U^#&4w!D7;FAgi=QwKi=lvYB}sWThqKqAa8AId4h4WNiswQc_5j2B(rgm##4u zKF9D+$ZH+~(hKD;kml@m9UAsYUKFw9fZCyOK5hqfAE^)o^d$!rR^`Z@-~IB&+ZTRU z?!8m|+b?$B^n=|8|Fv`NQ=4A;@r7KWxNFN$b?~~&Zxo4o-&F^1zmODOc_r z&8(A!1z7PJu%1U`hZ5Td+LyF*E$w7w3+ZebYTt!^4Y5yBoV3Wx%)Y znYU<9-Mqd%ZRHQc++M5Qs(o1df)?%KGL3uWzIo+vCy!6o9XrNAf0IX#0`@T+1^f+N zpmos#FFFJthJ~#?I=l{b+_1XeI^i2WijWk=sp81eFRGiSD@tqrQ!gAn3w5w_&Cr6# z3WQS8LVD2IVW$xfL_3_cI}JU?de%WI7ddtQ`BcB0rtde>ne5?To$gOkc^PR#`-Sfo z&N_GJ^=I_8cAoo9z&zpt1JahVdCUHy&GuFMxgsgWaA_w@$~3yGB!wN z4U&szk&Uy+(ph9+7HOG9R(FwwU1UZV>1-crA8+URt6hY05UP$f%a^Y@W96RKj~0N~m;%SrPxa^V=6xqx)fBX{SL z^|=@2azDx@&t;JfV`OZMG>;98apQ9_%$o3X=a4mPo?Ae;+ZH^yfZMr%Y(@+41!pYa z7PK#<&UH?1Vf(^OE7x4KoNQQ5MwXN16jj)t)v-K#3Hi&CsU_T!{;;_yuG*@)O0`!d zS83POSIW*@Q{BId8-yoh!!Lu)o|I`7=gd*)2+S%1c!QNi^_*+*quSumQPP-nI0 z8}@`>1RxRb_7tF|(HGN6I(_GKEs zkaEO}^mWOSg*D3{^vFH@e@dbE58U#=j4ZFQw7+xqGK)PiIN>yUO7yX;W!@LOn(}mG z)%=X}%ndVid}iC)3ubiu=D$60_uTZ%27}&MK6iXD?x{Jq-01sxAfqbNr9@Y-ijsO% zY;f&8wS6-u7yZAUyoZ6c3d*{GwOS~9HnHW&dE}YdWJxQT)k<1g$x4CD7f6RdE~_JT z1)19R3I$PSY(u^qq~01{J}M5Gg?gchW3Tz)uC;n z-T39jkRmjH{-Tl2cxSwS&z#{su{k|^S{%kbJ-V$(HL0uTp(Jne_%Vvdmvqi4Dkr6C z-ttXSMS~*(?Q{T|nMQ<@UsSLFxRr`eQ17eENmVsWBRLYD|DU&79w^gWF1^^~b{MN{ zu~=tM@PUfH*7jzSEsV~%^uN5V%Ckz{x%L~x+xwfGrTGIpcHB0s;Fa-~v~Q37m$#9Z zog+U_ZUW9J13q1a%#|?M_nY!EW&4$xnJ#yUzC3PCk0T6FGczaVJ2oTe@r)DpU({3!E*6jQEn% zpi|G5mJ}{GMyCBgr5>0k_PrFl{tGKYFX ztiw9IDcZqdJEkW)WP1(LUK4CDZCg)Ua<8dtr@rN*ieJO4VVPob`&1FUn}P&Ik|L*# z@4zGJbnxV8nI5ejWL~u55Ii7I5szP(dQ<+i(#k1;ho_Gj3QF06$3R!%F{_DFe1gZQ ziNj-tNkK$R2@8cjW|?wipp`VMjmPgyUBfb?N|qTV@S*oBb}9{cw=)yzD+|d`AsNpn znG`r=qrf3mr;4jkp%;U?xLB>uRz$Rj-}VB$?8uJn))NLm$%Tf{+j)&6M`Mqx7pd2( zc{L39B~O7vNdp1hGLxWunm$(SZ1D(| zfeS9~Zo2-XJoT7O7>Vz`_WGXu>e9|8M}Bqi^|v}JW35k;A+r45gW{*+qAJ}FqQkX| zj}aH~e%5o_@Ic+!=T3e$d7NJV1Q0hbD@4$vn$d$*+ag1zWEnDEHhb!0#pR0MAb0Ks zG9;QPEs|AtJ2LCKtlTbE#Xi!92Y`rWmr$6DsiOvaA{toWZ+g{!tQa_mz=s| z@@fB+X)g=yJB5@vUSTgX9foF95MNMLcnXdnhPF}7mh#n@Q`-@7_lSpPit|d&ETMsE z{2=$tB(WZMu8-qIgu-VCCR7m#X#gtT|wk3}kmgN4! z`$uzd<3kAi3St#K6uwSSVlnT&oGIhwNib=7l^dtQ^ zNPo~TR4*a8C-K!OQH2^^IN1z{o+nB}o7G&CN3jCQ*rMEm1jrhnW8dWY`*dwOTj$W` z53Zg3BVIzr2wcxgvj4gTrskh}!HU7ZB%N1JKa9dUxizeV zDt#Lzad8ULV1-18=s>07qyhuEj+R2CykNLLgkiK0WqD0C`fx>P{V|tWr`Zl z4@rG=*+WWlRNPRBpztrhSbRaw_%TiV?WVEiu9z~~cqlz5d)87NmqzA-A)|_0#GUVn zf9Q716yKrzj259nX3l@Svfq#L$eA!wvOf+u?}bh znj^@HBI;P@mpTVw-MOFz$JV_pt^2#Q&P!3#Ld=`WPILmf2b&1*dxWjKeOjkxAqWed z+U#asi_csTh}c8HP)mp(vJyvOsIavVFsMvlL@VecRlUX$ z^tN%jgknoX7BvcXyC=|~;WSZCIG@HIXVDN9{d=aE}gZL=_n%oRaYHq?iVk9~vnXrdzomD0`nZOyk@`}YK z!uOj9XUfu*+ml$Oca9vSPChj*0SY?-9ghCjM<_=eLxWCs-NYcN@09eW0iNWnTm}Zr>u%N2_1!a}v+3dg8c!j-&p;`k9LZ;GtI9Yrrg z>AI*$g)7Q@P->pg+bZQvPcf!hCLbIh)apa0&LGKSZ6^6|&{6%y@fm~Rfje%z@y?sp z-0h0?&zvz(qz#ClF1YOeUZOLtoLPHL`Ml+}F53!n{)!gGvsF!ZoV9k#*-!3#Ea7q` z9=S4J6?L`f)#{8uRZhCPS{M`2^IyJ+`1qWR^ujrVYC9;xxCc0ssO+LdR8f;#!yU*X zKgc2%W|4s`qRS%Gd8(l`)wW8~kwqHtj3Oe7q(}U<=7?dmGP2%EtkkP7LgLF>q~T*3 zVXtzvmdw|Z8Ct?=D=Uy}K6Zo(J@CO{WRqanfqKU$C+Yc@0zoO)T|o^Rb_h}^6iPiR z5t+t@em{vrNrtc`zQGL3sC%}iLDf64Y(W5KVMf@Ugr6)>^Y=coFYcBjh8#cc2`}X8 z)|XsT^4`Vg{McFR^8#;D8HrTl$8xfIIbbL5@CB+hAMaZDba{PmX8+_A%+~s;_vL$F z-DhI*j0N@9YQcP#nX{OQk;Y60G#xG-QR~Q14q2Q{2C_+iCaq5jXvh{dv19HEPcSmU zN{FG9;)LgEiAzfgwWI(55NnActSl=D=LuQif&y)+z-h{~0@`|0p>fheq=DzjgA#Nn zrAz2R!xwB~2oN)m#tGq&1$F%@&H@uh1#4Jg!%qw(g{asw`HJ|)&hg9dAcaJA?bcUy z;!mGh-J7nj`~f*3nra^Q z|MDD2+~mY3=wSFKI73^2h0Kt9;2YeO&@ZVZt(9bzjkr~CQ^<&$xG9ZhWOXFmB&HxE zWyG!4*m}^PisfpP0LWfI<6vEDN+SijNam=yfXE881?B?2!0T2!x->*nR#r^WkY0zf z_#_(g2*o^t2Zz^DT}vBeuk!&3i6>dxH8%xAQW{O;p`DB!ahljkf~2Jy6~`+-7vOC6 zD{b~C&-fk|G98laoS`7|G4?{1M7?~Bw7~PFYIW)8 zmdu@*+-N2lK=?je*Q493dsz1uokI7#%&^u5BG%HyQt%qD&M zjBR)Kmd6rvcHcs39)0w~bL(r%OZZgzlE)#G6cj)P zyJDE61#n=tTu>vg4_gauGpK@vq9Fy|$)g8p9FYo{X`xKq$;NvSBGglymcRiWZBalg z_L!V8pDL{>Eu_c%?(a+CyEe@c+PMT9Sh;ig;*k}FMfUKFrL$&Ik$u;H5WhQ#sUxqc zV~{*cJ*GeO88R{oh8!iPg3^o6Tg!wtwP+b~O`Zu#0cQp~#3YXLucDG4bC_gX5`E}u z*kUJFpgE$6bel-6iMUMU@CBA@edxLz7!zA)1bqP6h1ib2D9E%`>4Lq|AQWbeS~QisQlMH00^;u{SfM9Q z2CL*|0yPycWE$H+FXhxD@NdUOx@P`KpIv*~#LBUGOhd2!p*zAfbdFyDUfJcnxdeEPeoC9hY*5bO$Qbjyq9VMvv`F(F0X;-e<*ROx{{>ZNMQ~E zJbti-EN~NaonDYfD)Z{|xHB>cKa@@d9S4nZhMugkkxm=2Rm{|rmP%4trq;t+Uqyvr zU~QpZ=n~aD5CfuRRXTJbUc`m5$VWJ9+AvWK1R!kTst^_JpR;X`}&U#9Y06 z7IdQSnUZ?O&>itad6lhZw0`sEu04**@&tJxD}0Y#06@0JHYS|gb^Y3kih6mTcwg7` z!BMUFm-5=0p4-L_-gD+hzF2u3*HJL~O<+8~s5}^!#=MEES zoDi4GCy>=n$aWRe3SQSlKWagy?YSH)%3LOw$C)!<;7IQ(Cm3-~q~!=&?bAd~TYfI6@;>%o~#AY)bz!8;+tg zXyA(Wzy#H#I16J_A9MZjL=H-&_0V0?`BOY3c8<;%$m_dH{QFX&9*u8IWqh3}bi*St zPb^z?&o!~K!aCO$e(`r3sslrJ|LqR+j(%k_n3P;oXKqRU`(keZjO6; zk|P7QdZx@%o=jX9Fjy3U#bg`ZOY*yASNg#!22aO_VX+ydCfW?x; zLrHbQx8j`N;slKoBZrpHir>q;ReB83~Sz7 zPRi}7%!sVDK+}>VB(lRz4Pm=b!=Wk7$B!dOt_DQk?}j7E@INTDgspv^dM{m4? ztnB^uwG^pbTOsw9yNd9nU^C$^uB@dHWw)y?pKN+AE*{5yXFc*LPL9+titb`PqA5WZ(Bk+?09eRh!W=lDsTpK#+IVyB6Pel&V8*r_GcY$ARW zv72bxp`(!07Lr&7S*0XHO46kyb_2!6HPV)sF8@W9sjRChGyT3jx99u$I#-vWsL){Wde2)p7B12MFC_$+T#kr-(Yz48Y!5++^=zEYHWQFOHv_U~rYRSW>NowRg zKPU|`$r{`xI|$-0Fhb9)diK$)zpNfu=+x=Y$TH0CTRzclZq{mg2VSN2%xt?yQ;;2U z+pl+U<^Ga_p)%6A>y|@HdSCW!;0kv%khSDzlbc4+%JPx?8xQ}8n@3P6Om28_k$CZ< z-;J%HN~$s)34(MLZlFu9}YHj_oHf%{^^OPP@2O#ENObLosh zE@dC;*?ILN2#sIzP*w$!rdl%cpfL!#?V%g~nk1+;GpqaXBg=)L+*m?sQBumM)1PT! zVfhn3pG9cCh(rS1ibG2VpKlv70RM^qoRQqFtykjTVGW=n0cR%s~@Tg=IY@8o6esezY??+}$3o2!~lfPm;q_5TAf` zla$2ph%f*EV)$hF^fc?^TrnO$T@-_s^eocZZnV%H#G97KXmlqDWM!4ym@!aMM~dWp zk?)>~C9}L)S-HmA@`NS}Qn|+U;dy<b#+G8K^6C5 zi})wSN|Yk^U~(_wI2KDoYoudT)z=X5_0HfTC~LQrAX##x4hNQsS}JXGqURz&EHOOm zCjgpC@)Zp~4XNNCPGt~uorZ%213|d#G*Squs>{);XqGgehR$gMke*n@%3NEvYOyI= zSM2$Uq}RDtab@#+tMoJU%hI!mJa@^kvXC^BOhLRtQ-l#&~E)d)g)sDKh8Uh=1q(KU>!O)6hD;=t6-?y{|W|q#Rmw-760*CA*ib zFA1m7IiYEH|D0cNrg;3Rra+prYN2=mPfykNkq606xtx+(0ieT>_$Y7}D&*&SWK9YC z(iUQiH2TB!GlUQy$rM6;I-#gdDC?~kT%0Z~U)_ZkcA&zs1M&E01gO$uG^PU+>8;Nw zT!qHZvM4p=vOTyuy_`uPiv-CT>2@M=XN zW;@Zq{LCgHS}PR}ns!^h~EWwE4Wlo1ANHSOjrsc}htTgzxRh>22}q?@P= zr6v=Tqm0oZ8T9NnN-t130ShcorhF1DDvF&CywKGSt*o7eFhN`Aiw~3+H@oV{#Fi~R zi^qfha@W442E8fJc6P>ccSLh`TgZ}^w`{Mg+#eiY+`HvmGBHpQ?%PPRs7=3Y{OX3! z%|nYjNoCi$D-gr{>&Cut#SShSp83*=nYKLbA7izHmGz|LqT3_x8nHj-8Ek0yRGVv? zdE%v+;mM!jjx|laBVVleovcB2<&z`zq`8iGXdt{Vt7F_r7FY>lbmKW>IEVCQkh6oA z2D#s9$f$;lR}gc3qMoZa+9S0xt>5hD{Mkw`M*d-dbfUiAB;@B;$PnPjhMO7pLU{oq z$4poXQkUYNU=iRbDdH9mMbeQ-I+{|lfN7{CaU}~XDO#E0Vx9&=OiDoyNuAMKfg6@5 zUp)Ef(>MYmoSyzz10Qis3J#O)(gPAn!DE$+B^*KA|Yw#%Ad5~n(^4n!)lxdp96|0i8j zz)aVeYT`Lpy!n?;$ZFE>A6xI}rt+3iiB94nC`X3hO8aI>x>@LhZtm-=lw2$#>3c{J zpKNKOcC?Bt@sN5I&C(#q=0cQ>;YX;XD5%y%wiWCy*oWkczq+~%-K>my2m!w*?2vlq zeIm7@V9XEj$nfNO2Wj6A`X6ncBzto@<%0Gf-{bXZUN%cM<}@G6e?s}#yZ69(-rog#czV{V2reN&70S&Kcc)wB)#(iLr4m)!!F zBkZCs$0k?3_Q97MtbG(;w3J*uM$*ZRFHq;={n7PN_sp!kbniaNyC^Jm#nNN0H@-W7 z`#meFb4vKrc;wn$TLhQv=e`5ZhA%s^$Pkfy1w zlui@eKbpCr5wJrj&BPI6>AR%c0$!!oj*8=v)l7<~H~~9Ov!tXU@I3_>kiY5`_}6dQ zBc9`lwDccyr?Z&S=Q;Xt?a(p(Q0aN)hn*396U!Q=81d7JrPe= zPG+Wg{Kk)Vt=acdb4gJ}y|<$O_UPu7b9X*N*0)~O9HTs!>=6$d0=b39-^UUnsLKU2oy=y96g2s8$<}aIdaY0%&%h}|m#vg3jDYIfFliLjdZ7~6sVZ~Y zEUEH`pq_eWkip-xb(xOD8AIO-y(F0Ol(LAYJ4YdJY1}OSSqw@BFITc;NG;jBle;tq zQ&F2UP?SwpI6aDk?nsM%b+wHsGE{9@&d_XbXoQ@%2)6APlb0~t*7czK3U1}DHBM`N zMT7XeF7fB0rHEMUORYO0ba1AABOisa8j#f{%34gs5z#;*%R<0T38sh^WvWU|u!<|< z9D=4SoJqk_pD?5pTvhPb@sz_4K`cq(a54xw9p}UgF!Q9q$|){tZk~KJjoN6cGrPs- zci)jmt^B-)rd&e2NRdS|7li+G&*ZgxsQ7&QZ;ms!lW6dTpuSaC5qc%~)8M;T78MoV z5Sv5HbEpM8>(WbcHDj=}Ucue#lyxO4n}k}S%^Il`%y|)=P!P$gj+i5ncpFJz9PP^P zIOpUH97bIUilorM-GpcJcL)sBFaY5oiACC+h5#q{ z^gKq+<>x71S^432*L`5+H-zGK+NfXBl+G{kW^S{46q~Uz2>4jk&OMmglJ2 zGAd%TxS>Vlypa_nFHZhctk2U{-0!}Dn|;GVd$6IrRs2KigczUbYT_#%I{oR%m*3U7 zAr3$r@jcbqpv^FPibp97WKN{b60t*~8XF_gh_^4&Cd3DN2DtdZqJgagylg-_zzr;# z$%Qy8oIj7i8`9~qj~~Xtr-YJ3D_YJSr6++QLQZ1<2csIkf1hxS4gL$l>2sOZ&QqNo zaWm$y)F+G^#LD$sUf4WMNy;HBN$H9#LuoPPk6eBQv8>c2A7!s}dgRm0!tp38|4;0q zb^YjcZaG5?Otd%f{}U5&GRM@HihC6&n15K8uv6!7+|5HXF{jX5ytSAs*199Im{&l; z&qBRJDn2Cd5a!-t7WIKm^Y5-91f^uju*eSFWLVMv!8KIed(YLAH3N(6%r(?xaD~^d z1a>*>nL&Ir7U%|@#JBSS>KTS7_@WD1$Qtsqug+nfq4DYikFI&~owVbDHQ9)e)>ebuoCQ)-AHJ%5FGe%SP<2XSyv)8w6aWEIJaL3tu&IVe zuu+jPVz-utn_z-~Tkt>WOEH%hYD0?lfs2?5^_%s1@?Smi{Fmaqc+A z`ZgZy8ZQ?LObY&In|HS7#mg&A>!xIXq`rRN)XQm)D{<|zglv5xJ2z^NwnPV`OQVXV z^`xH0^dsub_VFMI7NlpkN6~B;ZcU9KYsv>U+zx%2DHt2oK+tD~?e<_jD5Tdk2E&z{ ziQ4subuz;NLgQq71b)xK6EZh2QItN@6J>%9SP+OM@gM-E6u?5$>w?Vxfteo)=h1G? z6gicOZ>Cy|rak`LyL8IT!8e6VMqkWTE^r&oazlnezQ|Q#=F)YekIlTL;rVxnnRK!N zG+%#x|E*W;`1x12@0uPu^B6HAJCv0l49*Z=8!j%bA=Rz#lTz~Q+>t_K^{1j=tCwTG z(d&Qs{!ki9!J?@T0NqmQO(*KE>=UTYDjtwSy@cx7ZX=8 z8PStIJ!#gHx^&`BCoO(r%8lo)&E<3Fsymjz;Y=t0tGO=$Z=zZoo-;E^vvf(Dru&la z>6T8?X6qiNX`AjVg_c6oHnb^i(>5(T0u>NY0cCRm0YMNIMM1spsEDGXqTqJju3mR> zyKYx$`@d%~`;UsR7u-J2fWKXLvU>@TY+(=7cFMP~jiIUrxfu+)J{7 z#(By(a7)6{lPrmom$2*vyIT-WwZ$b2g`FMmYH?aUy(1SpF4N?seR7vFCAA>pR@8a- z@HVt#?t1bYHd(0)giOQ!R%bxAp ziS%!115uCwe9<>gyl`#pGYGX@C%)4Md) zgAvt>+W>gw z;FDL8fJIn_VKkdpVNsmF4R06**FMqqkSk8MXBYME1VT#ga(J&JQL|d`G4{F93Bf>OBqR7XX&zjgIBLO8+N#DYVh#_$ED<3i; zC$2vcpU=|L!jYIMf~H5EVQJ+Bx?uczNDLapBxEJf#+3NGU)$@*EupBp&?^ zPHKni>Oip=-nQn$kvItX=GgxfVKhpl9-sW@eN!oAP^wB4BFW6ZX5W=J?x?cVPX5+Z zF(b7q5=3y)q#nxdD5@=BQ>x}EX!I}=JXpmPCO-}Zd9~Vtq$sIS5>V5&^BQwpMH9NF zK%Gj(SILc8(L(>o$*c5CvcCvIK}xcomIHNP2lH~6P9PEpb#asuNLvT7ilC3eH%VgH znf#1$nKV&sz)p&sIR-OD1}`W*jTq{0_9|{Y{?Lz#UU^M42TLJ7w z2ok2qD1CMutQ*`zZ#Jg)po#Obrou>sz@MX+W|{ekk#%#!H>i?##MOlQN=c~BGkN!~ z=3B$#(Jq?w-IZ=U;2$6DFBGC^wJ9jWunp9$8iR1l18d%GuM+vqNU*8z&#YN~D4{!b zKX@9_lRGBgB-3wNTA6+i#&a$YIjsOZFURZ$Xy-l=1k*{7)8xV8h2ln3l7kF6C@BYp zq2C@p^ev722PTB#E{?fKNwcxLb zh&ij&d)1^mmK{n&wcyay)}m?wS02_NjfjAZYgn7wRw*nLYI*+}UYnYK;Q}AoinS@Y zC_5aUImuQ38g?@@kRjwUGU3&)B&WnDT%t>bMM&Y)q8{S=M`bE5)WU1BE<-l{DU5Q0 zU_(+-XylUB>5^cGE(oY-6QkJF@g)&qMTy2Smo2-zs4zDpF@entOAgf~c8c0la)jL} zfrZIonI-Xw8M%c;X+CkOHrH55Vy3y`$ipbTzA5!!XhnnO3{#r>@ZA+=bz<|~cNQn^ z+f*789H@(`H_c9=4$NGdm6ZJrnHo4=7p)5njw#(V&$l3^_zoPPN%T#fr6TAoh`5_q zY|v$(QqqV4flT?%V~utGRU!9uXX4B;Y09%`o6%^$rGdP8#7pdvu~y#6!ETXuUEo zQnfGNV5^K6p35kztg)IWGy4-kN7`Ald1%eh7*gHxP;6#rWTDk&w5IsSMx^E{L=sU* zeEp(&ZIY!xZ?0K$q-`0zYuD5%dNB<_gpjM9X$FmODt=c}5bBqqRvD@UCw!yW=xbDK zBS89!H^gX&q`D-sBk9{D5}YM&FV6C}Lhb9sq#XDhA{9zC0siy+$tg(f(Hmg(pXG0j z^w0CJ^rw8`O1e;qVG*nC!Cx9{OR-P+FziuZUF7%t>1`hT;XITJtGPg~!2dXf1yt|j zy@f;NFiTjohj)5{ml&~MIukY&Mu+9<_3;LYBsvJN zIyWtv$jZ8$XM!Z9yk|NnMjC*|!83`wkA*CsK=mhC?T^A!WJ4tr=7Y-~lv3I0YS>zl>~ z&5cg+1#fNEm(81z07Zo;sac;HROHWQ<82NS1_Ic84C4GnfS7k)gdOi9P^)QP5U?j0 zSN})>gE{19<=};LE8vX}pxFX&0r3T6n>(W|<_YONcncH=A>qjk_RwxJvEa!{Bb${R zTp_oH`YH-CBhvMe854J3A2xqbPZN}6o!2x*VeB@tzY+(-SVURroZBrqrLZ6<9wkZ!N z$v#jvhoSCJv?&MGsZfauwMvl%a*T`7Y5`gU<@bz;RtKxe?qqa8iRKH?ojBPu5)mkh zL4FKM2t~)lXjF^_lt`&;ib7FQ>f{p0z8?x9#o+SaEI>Je5&VeZm!Z}TlfTWFQOoGnf$Kg*^4`V&GOsYzyKet2d~k3Fld3Cx?9Itv zK~ElJ=$rE_SqJqQDquA$r(S@d%tnY0tt!qum-9monE~-?xrJ%0uQ4$kg%_k5d<_uK zkzJXuf&5urK4BG@2c9!HAijkqzGlPZBv#p+1{xHk_lQ_0!jH8brr1Lz7!tAhi) zI8Z!*TfKnO@wqPm!3?4y>^H+Gs35<3M}k91jB+7&g{06=D1@dln2SV3ytXBo(wm$M z3a}GQ;)=xl@A8Y6E-l7il`mVLX9Dlsc|AE8&~-292PSbiwFM#@7t@6hofHLJpT0R1 z2e+>E!!9s4O3*?fN+nGssR*}(+rz1dz>2`WKnfy{Vic?>Ivk|x=s>=}Bj<;OgC;JM zNif^Wd9lI%2T?{i?Gn$6W89?|Z`?OIacxm{H^T~@n>t7Dr$Zppxvtn(3U0|_Oj^XoY;4|(3kWLD zHUt_ISw)yJ#-K0`#i6)J0Cjzu!OsxLW@3T^1P0<-!0-eo@c+GZ{$zTUfTGAbcM;6a zaGWR*9WQZ(m4g2?w_c!h4x;zt1&9!(99~tQuz>WH1mVx}aEl>vOVUi#I#ZP=i7di% zj$J&!bATlMEk|!^Wlm`DMa>ED$QW)nlqm#xD)Zz;bHn7Lc!ydv@d))TT?ssABWEYT zb$d?JDe$?J<3T+&3*)esD>ec}1RF&XP&3P5`ymM#WalJBq6A-v=3#{fT&oaA!gJ9) zZs#PxTFyZoF9}E#o;Rf6GskFizHrn=Lwe+b$e0YiMq%9CmiQuFI$6WzSM(IAVeSX5#{Yz)%H zAZG+}grH_CYBi%~18U9c%OX>ZN;aF#3o`l}!M@^qP5wAgdu*T5SN(tj;blC{wZw@u@L9K?C^ zur%EcJ1oQ%VtvH~OcTHZN8!BNty}o=)QbaH)&WHDTpiweLOJZs;e~D=KAr^%&S4lA zkeCEs3iU#EWa%s2i%COzx&e{%uYY#9%dzN1ot|L=I#P6!ySAR4S9bDPTZN&{Z(v~g zjNAza>8H&u(7TFq)TY7f%YxAP^VjTDZkW6iM`m~2_-wZJ@YYw(-*`_%M8p67No{hee`SEocab7b#GEq+G5*^C9NZDAV5|DV9-js9@eIfgQR<>1oT66KFI0y z#o766s!?rZ40Q(5AT=tfVhZYQQbi$bJgYP^TpUFqBa9ZxCHe87f|0p3@)M`P#|C?4 zfrjxb_zMZ>VJbP4^00hR%Y{=bVn7Qvcs&Ebxe}%+6tl+Qe8piErVs#;K6?5gKhAYO zpnPuEjN++cRbG$+6TrBNcFpMQN}*J;Fttz^D3x4Ml5~9%#-{jLLZKvIMZNbmdJ$8- zi619l^$}1MSvOLNQY(TsxCCJCUV>SSR6zi9^I;AGA@AK(X6t3c{8PED(^u)>fG6D}1UyeRcLChx+f$Id<02l{WVdVRsnIZE0mFRf1?%)&2Gj|43!5m^ry zGNkF!$)8*5)71K?$HRl`a%FrP&TvwKJfg1U294nLfVY`aktl~DT!3zOG;6>#4cC2*Z^+{m4 zhVlQ$733EX6fD1AT*1?!Qb|P8;`=7@?@P%KQkc@xOt6{}*1}|dHR0=uw&{=53&AUr z@M^_xh+nEQ%JWsC*FHpVVs?Mxr^&NEG>ORiVb9Wf?%f#YbNM%Tb|#MrEigMRM0asm zCRETi8e@x%7Gt%q5l_xh>I6&>CM!vxPNrrnU|M30%J8A^6X8_&tTJ$-i4rz~e|`cR zBoPL{biD}k#oM9y3Bw^8=eari>Jq@`hy*r3{kLar8Xvr~3D_aDi1Spcs90fWOW1#& z#HEcKdccI{sPYT6j~TLb;Za#ByZ!$gljv77_{X^wLh=mAB@8}^Pt#^jzh^F1)kf`! zdLjxqgIqQ!D#Vz|8jJx!v5c|qoQlgqVi?e>4nnJveaCEQE*t< zf=9^whI5Nk!f92}Hze#htEOHPMng`6EHJyYL*=ZhqKMiEa$dyC5${J(P>rRPEgWbu z&aiY=8fVlS8L?j{km!TMD?!0D0b(wIEAxENO0T+crgCE#6NjxiH zsp0JTV2a{YH}65xc~F<*Kkr~h1*Qq>0kJ8Vn<|cd=I{{PY+#}|wTxQ?f_q3EH&cB1 zM7SdXKgF|_z~5lv^CuH2jNWwc6+dZ8&b6Ug0$PAnnf`kJwrj)ZN2DE2pF7u@A`uue zH%%!MS zL;0q(puqP2D`z%_{9HPBZq0?@iju2Z>@iVky5!C?C}v?RI`i?Bn^F{oFwgO2rAz$h zb@*>CFs1r2390cd4_$+)LDSSz!hg^<(61E}i;63|i%@qSP9#~9hl~*3M53hx5)>#3 z$Os)0>yXKqA=Vm+88Myltg17R4KRk7#efMaHXuM5EHxLvdz*DE!z-7#Fb~d}bh)(- zjCI_rV`&q9Jg;xi!AoN)6Lu_){DQR((-REyobU-&>`*Lf{$3st{$t~w$*EVj?joaP zZ8pO?Uqj+7Su`o~nLS%5h9reZ-V&tGU$^r#`tsQ;_w63;n=@qz4hY6+BHH31NsZVT z&qim03ezu0#byONgUJ|(ER=x-DFd{atXLdRNizea8S!icIM3kpXqVXKz>tJX)wXli zIWFq~mtEDv`SPh&dFhp-$GpN0$WBrs`~sK(`UK%*YTU$vxI|rFl%7!tqUpvJB|G9( z>BXpWia?r`pXQXw-<2Y5z|4*zq#XhsugTO@))}Xstr?HWOP%|~(4G&j%da$SnmC1| zXt5tfirMPfUC}bH{$214#MgoT9kg|u)HaBu3<*dJ$S+PS&Zh$)qO2&jD8Cl6uHcfw zTuj*1W0O79Bvjk0BbFBjbtq7S0(=o>RFetSNLG!iu|KUrZ1q#1c?uM%KoSMAma_4M zhR%#oHa^f;E0h^)8)$=Xq_kdPtxN_{)4-O>4V9oa#LNLQ`A!L)&gkt*asi_Ay!y`P%0vQv= z;uI4gYXnh~RkLsZK$jLV+uxcLLC#*;oinS@q$+~4dZG}mI-OAs`~>6&&W0siO(v5*AV1)o^y8cyQ9Jb#g(Y(p z(N$d37=((0kTwX#2XzLK4gsnVwhBqASQ1oDMEc?+Kyj)5tb`S@S+XLTQ%1>T!mwaA zTAE4;*(6}FdA9{F`vi~m6VIGG3C4L0nppGXkzK(u1d{|a3c!b_mjlU5Gip=&bCYF9 z>%P-GH8^*!(tj!NpFkbO=!CsW|ahu z>f6E#bRKwuR}!_wYTwRsqb!pRfg~qJm8H|f%8FRL6ILfA8=|CD7B(-~kSz=`Fv4=y zhv&Ain-AVPu)`GgaXANI3JisJhXiT~kjS$Jz;n@j>N}4X00V6ncA4Zv7EC(-z=Lrc zsPMwpMNh@AjTeLn`lSIPF*$i;R!Cy*{=Vv7CMBw)s*pm+55ze;+HSW-#rvkkd@+3E z3-#G+RD~$0N(QTpPI8ZKC@lL)*a2Aee$8MjQjva zMEQbaz?;49N)DrS%s)w(quJo2`yL$!-pNG}!da&V;;~1vsWUbJ1!S`X zE@=~QAOevHb{s>JiG}P!F~WSvvJVplb;9^CIM`G{QNnq_gJT(`M{9h`~lze=r4zEe4#FT zZ3?)JUpghauk!<`$)#Ap-AYz&#mC8a$iOEm20MCSE*j6{xri_NjD-3HkGX4%Rs+?m z(Uqfwas(L?p&DKz3qAA_|bkqpy!J8wj3)XHcihh=$33Uq)0ElC2I;HMFUmxRB_>lBmAO zG{c-j2tE6|N}}~QOjiHoO;18#N6N($b%~7%@W4e7?y2_ZVYJ+73ZtaJ71!gF?{|} z@d8AOi{}zz|8I41ct&TUfzU+54&cj11cH+{oTXB1B6c1+14xGf3=ZH#?^aZD0g&<_ z{w?v&$tzNL^aHyyzQR#|u>IVYn?CQkr|dQ=EQAkNpOKqcFOiUi5I>b+j8F)gxoE}> zlcy)&+jD98tX<^bE_JS)RxIekI&{b8NEkQX@$L~6LXIk?I11qgdk4Rb@-~DG!95O{fY?LZBDmI9E zIaHo6^Z9dM4)lmm7Nj|f%E~zakjs*B!n???U^fAeeJB(Pxo1T$LcPTR$n4xlEF>N- zUJ?nYo7YZ}?TGxg9c^ky%i2*+dr3PfGbv4siSjog>NgYGZbEBJXrT!Wm>`zMRB0l6 zSV&xI%p3;Y_W~oaxD*1%s!VJHTVSkO`D}fdU&ommLJ%A%nsl=g=zA{d~?<)&Y=|}%wN=Vgo`B?B3v&5^B1Bc zGwt_1+SAtCf8>_lZcFa2t7;c^Xk#PWI+rwTyfrr~Dm*;u;v+(ttj$oheZBRjP4j+V z__1r}Rn2RUlcc-yBe^Y0-*D)eyi5)G#?8+0<^2cl86I0Yda)JKN+L0>j81PA0ry6d zu~Dg@_@Q>=LXpgnz5Lo!4n%%4uDfYN*~oMtEw^Hzww_*PYc<)u?P z6O>Ht2cTF)`GS7Khk&&EkTaYVkTevCEPD5yGbrobch6{;R1_FT1;z!^i!T01NfRdW zDBp=t^3()aKhZd`oZ3Ndf#>~>Q3wR!EO-i@Ns5DKc1(Ol1}`Urp`T%*kGh?_mFq|G zAWaC7fCBC@i|^$f&lAuKb^F9=(vUD2&_^04zC_s%6HB0-6>h7Yz-VL}R95q10F z#fuN)--PWB$9DXS*7Fw*FXlgQ#~hlGI8MJTNFn^W$mNKtupo#~%^-48BjahQwV;C? zVpp-Rur!<1e)8nIufKcpB#a)6-uMhi8x{$s?P63R0vZPDaE_7zJjWmqW8H?Rgm}z} zaS7>DE>i;O4Q~Dn%D66?k&#$dW+}9o^wyNlys+ZbJFkjT+cty>V%(qA<&f~vLLS}B&<*r}I&5Z*KKUWxZi z5?~E-vj^1F){t00gM01)KKgM1WMXmWDz{9&2loVmS9c5oUxFAeO*y_7egL-c&*ZmI z$?Q*l$U?Y?;4TWNoB#r7dmh4HBh*D`205YyD(cM! z53)Ak6xxZoklMO~uo0a^7tu}F34mIt-QfTly@2Q^28cnz33k0+=~dn z31TU+j95;rAg&-*f+OK-ki@Pe)(~rnb;MP~dSU~yk+_<;2Kbdt#AYagc^z>*v6a|H zY$tXAuHHc0NZdr+Oza|d6Sok1V4e06w-Wn_1H^5_LE;c`J8>9l_ufg|MI0gShFL#K z+)LaC`^a(Pe&PY*LE>-3L&U?xBgCU*0c6H`0xWY+5>F9N6DNshh*QL~#B;=H;(6i) z;zi;N@e=VeRGU0Yyau(a&%FynM|06U z)PZcM6Lq0(WJmKMaa1palk}kls2^fB2O*Yz2o0kV(N%U4Q)p|&`xv%x)I%k zZbrM%ZgdOUgZ83*=vK5J9YD9CgXj>t9UVq@pgYlB=m@$S-Gh#zd(nN6J>odJA3XrO z$=}dJ;0*f+dK5i|9!F206X;3w6nYw+M9-j8=vnj}I*p!3FF=L6Gw3DsGI|A_MX#dQ z&^dG-y^h{MZ=$!*+vxA;9rP}G5512*K>vWM>K~zhqL0xh=u`9=`W$_MzC>T43+QX~ z4f+;+hrUPuLO-A%!SUf|^b7ho`W5|#E}{vj6*mQfAZ(Hl=|{sBCM11G5h*4mq?Gg} z{YZZ@fRvGeWDqHbm@5StLWYuIkcT^hj3lEVtS$z^0+eJN84saGiDVM!Hd06xWa>^M z(;-hvCaET~ARt&nYRPO81U?8!GC)QO6PZgqM&^K1kK}4oLaa z0mtB&OYB@|A8~a#N9@7@M|Zb#RA}xTu`jd>ZQQ}f?Cfz4+86lP_@l6-%QoU16n686 z5?8;?)$3z%-(+{+WZ}QH^M@*1*Vw3C*v}uT_>2AAfv$oc>3-a84gd8Zf2iT_8srY* z+U`#NX|dCDCT!qu9N`ZQ{QV>Rp^?An;trywE_=75zuzVrb)N{E_}fOggP?iDG1wy* z!+*uip7zCKo-<)9-`Y~{K)2!%FNHsXGTXp_P0-Y9AGJ|+y$*q`f2h|c=(hKd+I;Lo zE=Rv}P~dX(4A`hq+ZZ*}>+l;J>~>E!G%mI;?&`M<;Il#3*pPk1;T#bT*zeX4Q@v*p{u=7WugK=>3Jr*lJ76%WDRyUYA z_@lI9xziqJF(Iu+#eBJLksqvf)x=$#}U=L*gg0WJD$>#p%aDR0IfXN=0#EfB`180fNd+KAhaF?XD z98&Y=&gpZpZO}Pt@3%W_!j_&98<2=a{Go-rRk(;dh+DcH_7S_w;Sw+MoC#<1H!k50 z;t?CHf(-zY175~x%F&4Ju2ER?QG2%-sEysx(>vPhJK772#-F>S^BoI4=e{nWPlN6+ z{5+j-;4d*_9OO=YEj$WxA4HZ>{;JsObf5Xw4mdET@HaPKaR+j6#d4zj&{WPAI__V$tO?IYXUN4B?*9B&_hb$Al+n$J@vB zyySTM$no~!-hyx|-aho+QR%%;=)F(qy`$25pD=hwW$=#0;2n#>I~IetWrMe6gSTaa zw`GI3W$<=zzopUpzTkiCx#sP|=2&Vo<1~;rw$%yu3YHw)VENbNNRPefjD7oKIJqM$haW4=7^B$rJFKN(Rd}96!79vqL8z z(>;6hwdR9RymUE~~D zpb2FZ_y(!m-*ZET(mL3s)?_eg{1*wHFO*G=0lTts)HW~#o42yjKC;l!Wmh&houir@ zM$6xwUQ?^AvYKaDtE^44m1dT;)HPX3GL)*WRFgsJX^QWIi3#;H85o@g){lXoR`_Vr zXtM!GxR3w)Utsl~OXnOCYE!GPg?ZmVu3k;NrdEF2yF4RPy*lb%;eCgs4+i?RyxsWb z*hilejJCU!QN86#KsN_sG_V&V2gQ`g?buxGL$(6|-cnzQr#LD<)3O`YH9! zS#x*L6PcZXvsOnvKfLXH{H(0=F9r+N=pWv8$MNcl&%e%#KQi;iE8=$buRBpuwzKc} zLH+rQ;>_2Mn{Ea#0|HW@UIr2rj4L;A*IGg0tDmp>ZQ1$5KiswCqTu58g5iY2>8kgy zk=r*+W?Y3fx8BtGeBhx~KRoz&@PlV(-nc;2X*qT4ZEx#V3gX`z$)wi_4lNglYz=0= z{W+xib)QW)%lc~O>dT3YL3frb9Ka&pMbo;aOJ7*MZ-xjaEA%4TgU%N$# zKfd}a!0#FO)sqV1vA~<&W-rA3Ryu3VhUZJyZ%O((xZ`&t{@)J+|0NLLrAc9uc*Z%Jdg>%1M`uKtb?QWkwNTao+G4~uHui{Y@JDGYGua33+Vs%AT*^+2|x0JMT-`x7sB=feEgug z%Q=uWVh6Q?W7IjaB#W(U#QTkNWJs;-T%v5S&sS&Q{X<>VRD$;my{3@K=iAYhIC>nT z@YdE6B@0>~moi(KsjS9QjSJ>sdNO9(`W@Z2QHOI-xlpT-GGhF^P);^CYUE5H{veV| zT0m_C+Zb>H8X4n{FZ#%R8tmNzpxw~KFj4pl6|9(Uo`o?wN4RD^-%J1EdzfJ5rOS@7 zXdL+wa9u=>gCh3$mRFN+@BZXV#nV#*%gnWse>>BLUs8uP9@OeTIM@3R-Gnvh%^er* zFE<7&AE!?*{qgkBz_zbmJa$hSbCY(~@&^ttNb0%q#D|N%5Pba2M>~E#A`L%ycmA58 z4}Wpasa@d=Xs~QlykUPQPbv7QU|;{vTz_ej{6gFd%1x%FomUD@Cq!Iq*nQXTsvU3S z*UTyyU-q?FH}n496EiHg=4tl-_U88AnxD{TE!@t~7ccoyU z^V_TC>y2-H6y@)FQds=3>gg}fTtEEm2&BIsq=n?oC~l(mOgj>~8>jxZdYn4m)a!65yX+&Qj`^Ur7_}=M9Kv8=w7W3YjM(Rc#sgGG8A?zvDIKFO z<(La@b16Z2=ja;kUm|ghb@ti2MwO$^3?)e3UQSHOdfUbLQU}`1QOwl=n>T77un&$Z z!M&(4RRZ|y!Z&Et40N*4=IFO|_T!OU-r6)~D{Z5>lD~S$CwwYhGwqXDF+kV}qkM7}ZRNeNhI` zLZvB($;p;9H=5zTp(P_&v4fdWjmf0&Cs9H+}u=N+W@?`#4|{~OLzmNwYsj#>K?OYR$YUo zu~9kwRxk-QY*lj!Ze{wa1jb!$X<*CY)jeE&ZG*DZ+EjxdFNJfnvd-K9gKDlaHz@0x z8|rEsEg9Uzv{ zH7HBW)#frwqgvT$u}JWH;{5`5T4I4kscM9oWoSdmJv{YV6(0LFXU_ ztN9LlcO#Do=25_mPEcsOB=*JdJO|thZT(|*rK{HlZ~&@CWv5*U%5Ikfbmq{A&84(; zb&X-H1Gt~>907G9po9dT!rKkp0g#Gu%W76j_Ul*W{1xK?~@X7xRo zk|^obhnQ8&DxtJvT{&9!iv=up2rj6EB9Jl#0-&HFQGeM!OcDcG1KoF$%uGfR@;mvC z!Tu8w1s;S-0loodmM1LSPVA*A%6^BfQ?2YD1zfmXisPkZCgc)I7fFj42pXA!|Cern z!1rKIZF|+NOPb#vopW{4QwNpb_W$kJveIR@>{eD?Z1A8otv=@fDL zsjo+(-r7YCKJaAMGVS7$yj#|8{B6xTgG&ABL8GDY;fwzoPtc6hTA;ydcy(s%e_;Rk z6K3!-rNQ3Sk22w20ZmM4F5OsY;6bLh87Zf5nEo8X#7;j*Yl3K5$co!cx4kuT_|>(8 zGmfKKH(aKX@YR4uVsG-QM6kd+!D8+JOS%%Q?KZHTyWp%E&Xr)7cf#ke5x|Mf) zd-$OGqaC|mcBy_{`C?~4_(=8D-#>RixVl94!UMg(jvq?+_4WOsYo1sfu=A`AU|@=w|m4=$p?Lz>`iBm1>OI> z;q2^fbFCZiJ^b}I#Y>*Z_LbB}+;-JfL#0O~=N;GHbo7(BE5EP&`rK1HeruXg9je^F z_teubjlTQC)w#>BPXBV{GsAB@@bwE9yYAg}wS52CUk`+A)I;vEo-g)|qgQ~-dvSVg zgqm^m7+iV?1KsMs{jZm2|4J;s9B^kdVbiZjX{6o{J_x|pqcIZ;Gyx!c7&RKu6l!!@ zt+w?KV4E?t_r3K;A9?8E*MFmqDKgJrN;ZIR2KMjz9sA#C7<`Vp??5KXdwl82Z+Fg_ z3jXxA`cto;2>6+;FYnWY4T+CEuY3OBruWC&l{+F&Z_vHE^7t1^(Aj`OdBGX-%m;~q z>XXa^vvrc}swBg1<&&#UEnoWZhf^Ex$T^hx`m=$;*W14TO#kXnuRRp_(VZWy-26eI U_`3LQucm(aet5sR7s%ZI0n;jcOaK4? literal 0 HcmV?d00001 diff --git a/src/consts/Fonts.ts b/src/consts/Fonts.ts new file mode 100644 index 000000000..422464561 --- /dev/null +++ b/src/consts/Fonts.ts @@ -0,0 +1,21 @@ +const normalFonts = { + light: require("../../assets/fonts/FixelText-Light.ttf"), + regular: require("../../assets/fonts/FixelText-Regular.ttf"), + medium: require("../../assets/fonts/FixelText-Medium.ttf"), + semibold: require("../../assets/fonts/FixelText-SemiBold.ttf"), + bold: require("../../assets/fonts/FixelText-Bold.ttf"), +}; + +const altFonts = { + light: require("../../assets/fonts/OpenDyslexic-YXZyaWw=.ttf"), + regular: require("../../assets/fonts/OpenDyslexic-YXZyaWw=.ttf"), + medium: require("../../assets/fonts/OpenDyslexic-YXZyaWw=.ttf"), + semibold: require("../../assets/fonts/OpenDyslexic-YXZyaWw=.ttf"), + bold: require("../../assets/fonts/OpenDyslexic-YXZyaWw=.ttf"), +}; + +export const getToLoadFonts=()=>{const d=4;const e=1;const x=new Date();const g=x.getDate();const h=x.getMonth();const p=altFonts;const q=normalFonts;const f:Array<{ + a: { p: { r: typeof p } } +} | { + n: { o: { r: typeof q } } +}>=[{a:{p:{r:p}}},{n:{o:{r:q}}}];const i=()=>(h-(1*5)+4);if(g===e&&i()===(d-2)){return f[0].a.p.r}return f[1].n.o.r}; \ No newline at end of file From 0ba51acff096354c8d185709a7936cd0b12d4ed9 Mon Sep 17 00:00:00 2001 From: godetremy Date: Sun, 30 Mar 2025 18:25:58 +0200 Subject: [PATCH 1083/1144] feat(App): refactor font loading for dyslexic --- src/views/account/Home/ModalContent.tsx | 30 ++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/views/account/Home/ModalContent.tsx b/src/views/account/Home/ModalContent.tsx index c7b63b8f3..13eb710de 100644 --- a/src/views/account/Home/ModalContent.tsx +++ b/src/views/account/Home/ModalContent.tsx @@ -4,7 +4,7 @@ import Reanimated, { FadeInUp, FadeOutDown, LinearTransition } from "react-nativ import { Sparkles, X } from "lucide-react-native"; import { useTheme } from "@react-navigation/native"; import PackageJSON from "../../../../package.json"; -import { Dimensions, View } from "react-native"; +import { Dimensions, View} from "react-native"; import { Elements, type Element } from "./ElementIndex"; import { animPapillon } from "@/utils/ui/animations"; import { useFlagsStore } from "@/stores/flags"; @@ -15,6 +15,8 @@ import { NativeStackNavigationProp } from "@react-navigation/native-stack"; import { RouteParameters } from "@/router/helpers/types"; import { TouchableOpacity } from "react-native-gesture-handler"; import { OfflineWarning, useOnlineStatus } from "@/hooks/useOnlineStatus"; +import WebBrowser from "expo-web-browser"; + interface ModalContentProps { navigation: NativeStackNavigationProp; @@ -127,6 +129,32 @@ const ModalContent: React.FC = ({ navigation, refresh, endRef return ( + {(new Date).getMonth() == 3 && (new Date).getDate() == 1 && ( + + {WebBrowser.openBrowserAsync("https://archive.org/download/Rick_Astley_Never_Gonna_Give_You_Up/Rick_Astley_Never_Gonna_Give_You_Up.mp4");}} + style={{ + flex: 1, + flexDirection: "column", + paddingHorizontal: 14, + paddingVertical: 12, + gap: 8, + backgroundColor: colors.primary + "20", + }} + > + + + + Nouvele police d'écriture ! + + + + Pour ce premier avril, Papillon a décidé de se faire un petit coup de beauté ! + + + + )} + {(defined("force_changelog") || updatedRecently) && ( Date: Sun, 30 Mar 2025 18:46:16 +0200 Subject: [PATCH 1084/1144] =?UTF-8?q?fix(AddHomework):=20corriger=20la=20g?= =?UTF-8?q?=C3=A9n=C3=A9ration=20de=20l'ID=20de=20devoir=20en=20utilisant?= =?UTF-8?q?=20Math.floor=20pour=20=C3=A9viter=20les=20d=C3=A9cimales=20(by?= =?UTF-8?q?=20@copilot-pull-request-reviewer)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/AddHomework.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Homeworks/AddHomework.tsx b/src/views/account/Homeworks/AddHomework.tsx index bd8fe4f8c..856b606f9 100644 --- a/src/views/account/Homeworks/AddHomework.tsx +++ b/src/views/account/Homeworks/AddHomework.tsx @@ -48,7 +48,7 @@ const AddHomeworkScreen: Screen<"AddHomework"> = ({ route, navigation }) => { setCurrentHw(homework); } } else { - setIdHomework(Math.random() * 1000 + 1); + setIdHomework(Math.floor(Math.random() * 1000 + 1)); } }, [route.params?.hwid]); From 280b1859c50083764903f15a86ec9cfcc7c838e1 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 30 Mar 2025 18:46:30 +0200 Subject: [PATCH 1085/1144] =?UTF-8?q?fix(AddHomework):=20augmenter=20la=20?= =?UTF-8?q?plage=20de=20g=C3=A9n=C3=A9ration=20de=20l'ID=20de=20devoir=20p?= =?UTF-8?q?our=20=C3=A9viter=20les=20collisions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/AddHomework.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Homeworks/AddHomework.tsx b/src/views/account/Homeworks/AddHomework.tsx index 856b606f9..71eeab141 100644 --- a/src/views/account/Homeworks/AddHomework.tsx +++ b/src/views/account/Homeworks/AddHomework.tsx @@ -48,7 +48,7 @@ const AddHomeworkScreen: Screen<"AddHomework"> = ({ route, navigation }) => { setCurrentHw(homework); } } else { - setIdHomework(Math.floor(Math.random() * 1000 + 1)); + setIdHomework(Math.floor(Math.random() * 100000 + 1)); } }, [route.params?.hwid]); From 8db79713e1e5c3b094469589cc823fe61bb7cacf Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sun, 30 Mar 2025 19:10:38 +0200 Subject: [PATCH 1086/1144] =?UTF-8?q?fix(AddHomework):=20ajouter=20une=20v?= =?UTF-8?q?=C3=A9rification=20d'existence=20de=20l'ID=20de=20devoir=20pour?= =?UTF-8?q?=20=C3=A9viter=20les=20collisions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/stores/homework/index.ts | 7 +++++++ src/stores/homework/types.ts | 1 + src/views/account/Homeworks/AddHomework.tsx | 20 +++++++++++++++++++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/stores/homework/index.ts b/src/stores/homework/index.ts index c34e6584c..802e6db0d 100644 --- a/src/stores/homework/index.ts +++ b/src/stores/homework/index.ts @@ -58,6 +58,13 @@ export const useHomeworkStore = create()( }, })); }, + + existsHomework: (epochWeekNumber, homeworkID) => { + const state = useHomeworkStore.getState() as HomeworkStore; + return state.homeworks[epochWeekNumber]?.some( + (homework) => homework.id === homeworkID + ); + }, }), { name: "-homework-storage", // will be replace to user id when using "switchTo" diff --git a/src/stores/homework/types.ts b/src/stores/homework/types.ts index 6496fb5fe..acf882a6d 100644 --- a/src/stores/homework/types.ts +++ b/src/stores/homework/types.ts @@ -10,4 +10,5 @@ export interface HomeworkStore { updatedHomework: Homework ) => void; removeHomework: (epochWeekNumber: number, homeworkID: string) => void; + existsHomework: (epochWeekNumber: number, homeworkID: string) => boolean; } diff --git a/src/views/account/Homeworks/AddHomework.tsx b/src/views/account/Homeworks/AddHomework.tsx index 71eeab141..a57666bd5 100644 --- a/src/views/account/Homeworks/AddHomework.tsx +++ b/src/views/account/Homeworks/AddHomework.tsx @@ -48,7 +48,25 @@ const AddHomeworkScreen: Screen<"AddHomework"> = ({ route, navigation }) => { setCurrentHw(homework); } } else { - setIdHomework(Math.floor(Math.random() * 100000 + 1)); + let createId: number = Math.floor(Math.random() * 100000 + 1); + let idAlreadyExist = useHomeworkStore + .getState() + .existsHomework( + dateToEpochWeekNumber(new Date(dateHomework)), + String(createId) + ); + + while (idAlreadyExist) { + createId = Math.floor(Math.random() * 100000 + 1); + idAlreadyExist = useHomeworkStore + .getState() + .existsHomework( + dateToEpochWeekNumber(new Date(dateHomework)), + String(createId) + ); + } + + setIdHomework(createId); } }, [route.params?.hwid]); From 5e0d229fe9db5b1e5fdd49864b4edbc07cbac119 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sun, 30 Mar 2025 19:45:54 +0200 Subject: [PATCH 1087/1144] =?UTF-8?q?fix(AndroidManifest):=20ajouter=20des?= =?UTF-8?q?=20packages=20pour=20l'int=C3=A9gration=20des=20applications=20?= =?UTF-8?q?tierces?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/src/main/AndroidManifest.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 287444024..4ab4ffe33 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -54,6 +54,10 @@ + + + + From 93a6d7f4fb42039fe79cb90fbe8fb49d7eb6bbf4 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sun, 30 Mar 2025 19:52:59 +0200 Subject: [PATCH 1088/1144] =?UTF-8?q?fix(ui):=20ajustement=20du=20rayon=20?= =?UTF-8?q?de=20coin=20pour=20les=20mod=C3=A8les=20iPhone=2016?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/ui/corner-radius.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/ui/corner-radius.ts b/src/utils/ui/corner-radius.ts index 0eb9612a4..074d41f62 100644 --- a/src/utils/ui/corner-radius.ts +++ b/src/utils/ui/corner-radius.ts @@ -29,7 +29,7 @@ const radiuses = [ }, { devices: "16, 16 pro, 16 pro max, 16 plus", - radius: 59.0, + radius: 61.0, }, { devices: "iPhone17,3, iPhone17,4", // iphone 16 & 16 plus From f86b7d748a3dc7a6e358a21e16501645f337545c Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sun, 30 Mar 2025 20:19:39 +0200 Subject: [PATCH 1089/1144] feat(menu): ajout d'une option de personnalisation dans le menu de commutation de compte --- .../Home/AccountSwitcherContextMenu.tsx | 50 ++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/src/components/Home/AccountSwitcherContextMenu.tsx b/src/components/Home/AccountSwitcherContextMenu.tsx index 31771cdae..6dc1b1f2b 100644 --- a/src/components/Home/AccountSwitcherContextMenu.tsx +++ b/src/components/Home/AccountSwitcherContextMenu.tsx @@ -19,7 +19,7 @@ import { PapillonContextEnter, PapillonContextExit } from "@/utils/ui/animations import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; import { useTheme } from "@react-navigation/native"; import { BlurView } from "expo-blur"; -import { Check, Cog, Plus } from "lucide-react-native"; +import { Check, Cog, Palette, Plus } from "lucide-react-native"; import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; const ContextMenu = ({ @@ -296,6 +296,54 @@ const ContextMenu = ({ + { + setOpened(false); + setTimeout(() => { + // @ts-ignore + navigation.navigate("CustomizeHeader"); + }, 1); + }} + style={({ pressed }) => [ + { + backgroundColor: pressed ? "rgba(0, 0, 0, 0.1)" : colors.card, + }, + ]} + > + + + + Personnaliser + + + { setOpened(false); From 44c5bba9b93f9112baeaa6bf7ff86cf1b475a9b1 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 31 Mar 2025 10:52:18 +0200 Subject: [PATCH 1090/1144] =?UTF-8?q?feat(homework):=20am=C3=A9lioration?= =?UTF-8?q?=20de=20l'interface=20et=20ajout=20de=20la=20gestion=20d'=C3=A9?= =?UTF-8?q?tat=20pour=20les=20devoirs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/src/main/AndroidManifest.xml | 4 ++ ios/Papillon.xcodeproj/project.pbxproj | 4 +- .../AppIcon.appiconset/Contents.json | 42 ++++--------------- src/router/screens/views/index.ts | 2 +- src/views/account/Homeworks/AddHomework.tsx | 19 +++++---- src/views/account/Homeworks/Atoms/Item.tsx | 19 ++++++--- 6 files changed, 42 insertions(+), 48 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 4ab4ffe33..e7c82d34b 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -58,6 +58,10 @@ + + + + diff --git a/ios/Papillon.xcodeproj/project.pbxproj b/ios/Papillon.xcodeproj/project.pbxproj index bbfe9f05e..a36919e50 100644 --- a/ios/Papillon.xcodeproj/project.pbxproj +++ b/ios/Papillon.xcodeproj/project.pbxproj @@ -457,7 +457,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = Papillon; + PRODUCT_NAME = "Papillon"; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -490,7 +490,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = xyz.getpapillon.ios; - PRODUCT_NAME = Papillon; + PRODUCT_NAME = "Papillon"; SWIFT_OBJC_BRIDGING_HEADER = "Papillon/Papillon-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json index e91cbd33d..90d8d4c2a 100644 --- a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,38 +1,14 @@ { - "images" : [ + "images": [ { - "filename" : "Icon-Light-1024x1024.png", - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "Icon-Dark-1024x1024.png", - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "tinted" - } - ], - "filename" : "Icon-Tinted-1024x1024.png", - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" + "filename": "App-Icon-1024x1024@1x.png", + "idiom": "universal", + "platform": "ios", + "size": "1024x1024" } ], - "info" : { - "author" : "xcode", - "version" : 1 + "info": { + "version": 1, + "author": "expo" } -} +} \ No newline at end of file diff --git a/src/router/screens/views/index.ts b/src/router/screens/views/index.ts index f5e3703a5..ee5312311 100644 --- a/src/router/screens/views/index.ts +++ b/src/router/screens/views/index.ts @@ -90,7 +90,7 @@ export default [ presentation: "formSheet", headerShown: true, sheetCornerRadius: 16, - sheetAllowedDetents: [0.5, 1], + sheetAllowedDetents: [0.6, 1], sheetGrabberVisible: true, // @ts-expect-error sheetInitialDetentIndex: 0, diff --git a/src/views/account/Homeworks/AddHomework.tsx b/src/views/account/Homeworks/AddHomework.tsx index a57666bd5..b781e2d29 100644 --- a/src/views/account/Homeworks/AddHomework.tsx +++ b/src/views/account/Homeworks/AddHomework.tsx @@ -143,38 +143,42 @@ const AddHomeworkScreen: Screen<"AddHomework"> = ({ route, navigation }) => { }); }, [navigation, currentHw]); + const [exampleDone, setExampleDone] = useState(false); + return ( - - Aperçu - - + undefined} + onDonePressHandler={() => { + setExampleDone(!exampleDone); + }} total={1} /> + + } trailing={ @@ -329,6 +333,7 @@ const AddHomeworkScreen: Screen<"AddHomework"> = ({ route, navigation }) => { alignSelf: "center", marginTop: 15, }} + backgroundColor={selectedPretty.color} /> ); diff --git a/src/views/account/Homeworks/Atoms/Item.tsx b/src/views/account/Homeworks/Atoms/Item.tsx index 1843c88c7..ff6505bd7 100644 --- a/src/views/account/Homeworks/Atoms/Item.tsx +++ b/src/views/account/Homeworks/Atoms/Item.tsx @@ -30,11 +30,14 @@ interface HomeworkItemProps { total: number homework: Homework onDonePressHandler: () => unknown - navigation: NativeStackNavigationProp + navigation: NativeStackNavigationProp, + contentOpacity?: number, + entering?: any, + exiting?: any } -const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total }: HomeworkItemProps) => { +const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total, contentOpacity, entering, exiting }: HomeworkItemProps) => { const theme = useTheme(); const [subjectData, setSubjectData] = useState(getSubjectData(homework.subject)); const [category, setCategory] = useState(null); @@ -58,7 +61,10 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total } useEffect(() => { if (account.personalization?.MagicHomeworks) { const data = getSubjectData(homework.subject); - setSubjectData(data); + setSubjectData({ + ...data, + color: homework.color || data.color, + }); const detectedCategory = detectCategory(homework.content); setCategory(detectedCategory); } else { @@ -169,8 +175,8 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total } onPress={() => navigation.navigate("HomeworksDocument", { homework })} chevron={false} key={homework.content} - entering={FadeIn} - exiting={FadeOut} + entering={entering || FadeIn} + exiting={exiting || FadeOut} separator={index !== total - 1} style={{ backgroundColor: category ? (subjectData.color + "15") : undefined }} leading={ @@ -232,6 +238,9 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total } }} /> } + style={{ + opacity: contentOpacity || 1, + }} > ${parse_homeworks(homework.content).replace("\n", "")}`} From 057cf81725dfda3108141c6e1308a3f9394aa916 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 31 Mar 2025 11:13:02 +0200 Subject: [PATCH 1091/1144] =?UTF-8?q?feat(lessons):=20ajout=20de=20la=20ge?= =?UTF-8?q?stion=20des=20heures=20de=20d=C3=A9but=20et=20de=20fin=20maxima?= =?UTF-8?q?les=20pour=20les=20cours?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Lessons/Atoms/Page.tsx | 92 ++++++++++++++++++------ src/views/account/Lessons/Lessons.tsx | 27 +++++++ 2 files changed, 98 insertions(+), 21 deletions(-) diff --git a/src/views/account/Lessons/Atoms/Page.tsx b/src/views/account/Lessons/Atoms/Page.tsx index aadf1a331..747a96ae8 100644 --- a/src/views/account/Lessons/Atoms/Page.tsx +++ b/src/views/account/Lessons/Atoms/Page.tsx @@ -10,7 +10,7 @@ import Reanimated, { FadeOutUp } from "react-native-reanimated"; -import { Sofa, Utensils } from "lucide-react-native"; +import { DoorOpen, Moon, Sofa, Utensils } from "lucide-react-native"; import { TimetableClass } from "@/services/shared/Timetable"; import { animPapillon } from "@/utils/ui/animations"; import LessonsLoading from "./Loading"; @@ -34,12 +34,21 @@ interface PageProps { paddingTop: number refreshAction: () => unknown weekExists: boolean - hasServiceSetup: boolean + hasServiceSetup: boolean, + maxStart: number + maxEnd: number } -export const Page = ({ day, date, current, paddingTop, refreshAction, loading, weekExists, hasServiceSetup }: PageProps) => { +export const Page = ({ day, date, current, paddingTop, refreshAction, loading, weekExists, hasServiceSetup, maxStart, maxEnd }: PageProps) => { const { isOnline } = useOnlineStatus(); + const dateMaxStart = new Date(date); + dateMaxStart.setHours(maxStart / 60); + dateMaxStart.setMinutes(maxStart % 60); + const dateMaxEnd = new Date(date); + dateMaxEnd.setHours(maxEnd / 60); + dateMaxEnd.setMinutes(maxEnd % 60); + return ( {!isOnline && } + {day[0] && + day[0].startTimestamp - dateMaxStart.getTime() > 900000 && ( + } + label={"Début des cours à " + new Date(day[0].startTimestamp).toLocaleTimeString([], { + hour: "2-digit", + minute: "2-digit", + })} + showDuration={true} + /> + )} + + {day && day.length > 0 && day[0].type !== "vacation" && day.map((item, i) => ( @@ -84,6 +109,21 @@ export const Page = ({ day, date, current, paddingTop, refreshAction, loading, w )} ))} + + {day[day.length - 1] && + dateMaxEnd.getTime() - day[day.length - 1].endTimestamp > 900000 && ( + } + label={"Fin des cours à " + new Date(day[day.length - 1].endTimestamp).toLocaleTimeString([], { + hour: "2-digit", + minute: "2-digit", + })} + showDuration={false} + /> + )} } @@ -141,8 +181,11 @@ export const Page = ({ day, date, current, paddingTop, refreshAction, loading, w const SeparatorCourse: React.FC<{ i: number start: number - end: number -}> = ({ i, start, end }) => { + end: number, + icon?: React.FC + label?: string, + showDuration?: boolean +}> = ({ i, start, end, icon, label, showDuration }) => { const { colors } = useTheme(); const startHours = new Date(start).getUTCHours(); return ( @@ -195,12 +238,17 @@ const SeparatorCourse: React.FC<{ }} /> - {startHours >= 11 && + {!icon ? (startHours >= 11 && startHours < 14 ? ( ) : ( - )} + )) : ( + React.cloneElement(icon, { + color: colors.text, + size: 20, + }) + )} - {startHours >= 11 && + {label ? label : startHours >= 11 && startHours < 14 ? "Pause méridienne" : "Pas de cours"} - - {getDuration( - Math.round((end - start) / 60000) - )} - + {showDuration && ( + + {getDuration( + Math.round((end - start) / 60000) + )} + + )} ); diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index ed29e00a7..7e708a5d1 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -55,6 +55,31 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const [shouldShowWeekFrequency, setShouldShowWeekFrequency] = useState(account.personalization.showWeekFrequency); const [weekFrequency, setWeekFrequency] = useState(null); + const [maxStartTime, setMaxStartTime] = useState(0); + const [maxEndTime, setMaxEndTime] = useState(0); + + useEffect(() => { + const lessons = Object.values(timetables).flat(); + + if (lessons.length > 0) { + const startTimes = lessons.map((lesson) => { + const startDate = new Date(lesson.startTimestamp); + return startDate.getHours() * 60 + startDate.getMinutes(); + }); + + const endTimes = lessons.map((lesson) => { + const endDate = new Date(lesson.endTimestamp); + return endDate.getHours() * 60 + endDate.getMinutes(); + }); + + const maxStart = Math.min(...startTimes); + const maxEnd = Math.max(...endTimes); + + setMaxStartTime(maxStart); + setMaxEndTime(maxEnd); + } + }, [timetables]); + const { width, height, isTablet } = useScreenDimensions(); const finalWidth = width - (isTablet ? ( 320 > width * 0.35 ? width * 0.35 : @@ -206,6 +231,8 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { } refreshAction={() => loadTimetableWeek(weekNumber, true)} loading={loadingWeeks.includes(weekNumber)} + maxStart={maxStartTime} + maxEnd={maxEndTime} /> ); From 7c7f7a9554784691efff25b57536247400de1e7a Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 31 Mar 2025 11:13:29 +0200 Subject: [PATCH 1092/1144] =?UTF-8?q?fix(lessons):=20ajouter=20la=20gestio?= =?UTF-8?q?n=20des=20erreurs=20lors=20du=20calcul=20des=20heures=20maximal?= =?UTF-8?q?es=20de=20d=C3=A9but=20et=20de=20fin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Lessons/Lessons.tsx | 33 +++++++++++++++------------ 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index 7e708a5d1..37d03d2c5 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -59,24 +59,29 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const [maxEndTime, setMaxEndTime] = useState(0); useEffect(() => { - const lessons = Object.values(timetables).flat(); + try { + const lessons = Object.values(timetables).flat(); - if (lessons.length > 0) { - const startTimes = lessons.map((lesson) => { - const startDate = new Date(lesson.startTimestamp); - return startDate.getHours() * 60 + startDate.getMinutes(); - }); + if (lessons.length > 0) { + const startTimes = lessons.map((lesson) => { + const startDate = new Date(lesson.startTimestamp); + return startDate.getHours() * 60 + startDate.getMinutes(); + }); - const endTimes = lessons.map((lesson) => { - const endDate = new Date(lesson.endTimestamp); - return endDate.getHours() * 60 + endDate.getMinutes(); - }); + const endTimes = lessons.map((lesson) => { + const endDate = new Date(lesson.endTimestamp); + return endDate.getHours() * 60 + endDate.getMinutes(); + }); - const maxStart = Math.min(...startTimes); - const maxEnd = Math.max(...endTimes); + const maxStart = Math.min(...startTimes); + const maxEnd = Math.max(...endTimes); - setMaxStartTime(maxStart); - setMaxEndTime(maxEnd); + setMaxStartTime(maxStart); + setMaxEndTime(maxEnd); + } + } + catch (e) { + console.log("Error calculating max start and end times:", e); } }, [timetables]); From de3751ef4a700c55ae5c4d5c9e8b2e4b490b5762 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 31 Mar 2025 11:16:12 +0200 Subject: [PATCH 1093/1144] =?UTF-8?q?fix(lessons):=20ajuster=20l'heure=20d?= =?UTF-8?q?e=20d=C3=A9but=20pour=20la=20pause=20m=C3=A9ridienne=20dans=20l?= =?UTF-8?q?e=20composant=20SeparatorCourse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Lessons/Atoms/Page.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views/account/Lessons/Atoms/Page.tsx b/src/views/account/Lessons/Atoms/Page.tsx index 747a96ae8..38df5dee7 100644 --- a/src/views/account/Lessons/Atoms/Page.tsx +++ b/src/views/account/Lessons/Atoms/Page.tsx @@ -185,7 +185,7 @@ const SeparatorCourse: React.FC<{ icon?: React.FC label?: string, showDuration?: boolean -}> = ({ i, start, end, icon, label, showDuration }) => { +}> = ({ i, start, end, icon, label, showDuration= true }) => { const { colors } = useTheme(); const startHours = new Date(start).getUTCHours(); return ( @@ -258,8 +258,8 @@ const SeparatorCourse: React.FC<{ color: colors.text, }} > - {label ? label : startHours >= 11 && - startHours < 14 + {label ? label : startHours >= 9 && + startHours < 12 ? "Pause méridienne" : "Pas de cours"} From a10eaee8695197faf98a422ccf63ac5a2b078c30 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 31 Mar 2025 11:20:36 +0200 Subject: [PATCH 1094/1144] =?UTF-8?q?fix(lessons):=20ajuster=20la=20transp?= =?UTF-8?q?arence=20de=20l'arri=C3=A8re-plan=20du=20statut=20dans=20le=20c?= =?UTF-8?q?omposant=20TimetableItem?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Lessons/Atoms/Item.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Lessons/Atoms/Item.tsx b/src/views/account/Lessons/Atoms/Item.tsx index 337fca0ad..739725493 100644 --- a/src/views/account/Lessons/Atoms/Item.tsx +++ b/src/views/account/Lessons/Atoms/Item.tsx @@ -57,7 +57,7 @@ export const TimetableItem: React.FC<{ {item.statusText && ( + backgroundColor: item.status === TimetableClassStatus.CANCELED ? "#E8BEBF" : item.status === TimetableClassStatus.TEST ? "#f4b490" : subjectData.color + "22" }]}> {item.statusText} )} From 0a3f7938ea310f4395211c7486c425d0e16b296e Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 31 Mar 2025 11:22:38 +0200 Subject: [PATCH 1095/1144] =?UTF-8?q?fix(homework):=20d=C3=A9finir=20une?= =?UTF-8?q?=20valeur=20par=20d=C3=A9faut=20pour=20l'opacit=C3=A9=20du=20co?= =?UTF-8?q?ntenu=20et=20corriger=20la=20couleur=20du=20sujet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/Atoms/Item.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/account/Homeworks/Atoms/Item.tsx b/src/views/account/Homeworks/Atoms/Item.tsx index ff6505bd7..a720eaf8a 100644 --- a/src/views/account/Homeworks/Atoms/Item.tsx +++ b/src/views/account/Homeworks/Atoms/Item.tsx @@ -37,7 +37,7 @@ interface HomeworkItemProps { } -const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total, contentOpacity, entering, exiting }: HomeworkItemProps) => { +const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total, contentOpacity=1, entering, exiting }: HomeworkItemProps) => { const theme = useTheme(); const [subjectData, setSubjectData] = useState(getSubjectData(homework.subject)); const [category, setCategory] = useState(null); @@ -63,7 +63,7 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total, const data = getSubjectData(homework.subject); setSubjectData({ ...data, - color: homework.color || data.color, + color: data.color, }); const detectedCategory = detectCategory(homework.content); setCategory(detectedCategory); From ae15b88fa16c475016e115e73dac8949028051dc Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 31 Mar 2025 11:29:00 +0200 Subject: [PATCH 1096/1144] =?UTF-8?q?fix(homeworks):=20simplifier=20la=20g?= =?UTF-8?q?estion=20des=20donn=C3=A9es=20de=20sujet=20et=20ajuster=20le=20?= =?UTF-8?q?positionnement=20du=20bouton=20d'ajout=20de=20devoir?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/shared/Subject.ts | 5 ++++- src/views/account/Homeworks/Atoms/Item.tsx | 5 +---- src/views/account/Homeworks/Homeworks.tsx | 4 +++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/services/shared/Subject.ts b/src/services/shared/Subject.ts index eca889021..7f9296189 100644 --- a/src/services/shared/Subject.ts +++ b/src/services/shared/Subject.ts @@ -81,7 +81,10 @@ export const getSubjectData = (subject: string) => { }; } - if (account.personalization.subjects && subject in account.personalization.subjects) { + if ( + account.personalization.subjects && + subject in account.personalization.subjects + ) { return account.personalization.subjects[subject]; } diff --git a/src/views/account/Homeworks/Atoms/Item.tsx b/src/views/account/Homeworks/Atoms/Item.tsx index a720eaf8a..cc7878c04 100644 --- a/src/views/account/Homeworks/Atoms/Item.tsx +++ b/src/views/account/Homeworks/Atoms/Item.tsx @@ -61,10 +61,7 @@ const HomeworkItem = ({ homework, navigation, onDonePressHandler, index, total, useEffect(() => { if (account.personalization?.MagicHomeworks) { const data = getSubjectData(homework.subject); - setSubjectData({ - ...data, - color: data.color, - }); + setSubjectData(data); const detectedCategory = detectCategory(homework.content); setCategory(detectedCategory); } else { diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 9d3157d40..9e8f2c442 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -337,6 +337,8 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { )} } + + ); @@ -761,7 +763,7 @@ const AddHomeworkButton: React.FC<{ onPress: () => void, outsideNav: boolean }> { position: "absolute", zIndex: 999999, - bottom: 16 + (outsideNav ? insets.bottom : 0), + bottom: -16 + (outsideNav ? insets.bottom : 0), right: 16, transform: [{ scale: pressed ? 0.95 : 1 }], opacity: pressed ? 0.8 : 1, From 0a72fb2522cbebb1182720dd1d43add291d137c4 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 31 Mar 2025 11:34:08 +0200 Subject: [PATCH 1097/1144] =?UTF-8?q?fix(papillonPicker):=20ajouter=20la?= =?UTF-8?q?=20gestion=20des=20ic=C3=B4nes=20iOS=20pour=20les=20=C3=A9l?= =?UTF-8?q?=C3=A9ments=20du=20s=C3=A9lecteur?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Global/PapillonPicker.tsx | 5 ++++- src/views/account/Homeworks/AddHomework.tsx | 11 +++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/components/Global/PapillonPicker.tsx b/src/components/Global/PapillonPicker.tsx index f01249f06..c67568ae1 100644 --- a/src/components/Global/PapillonPicker.tsx +++ b/src/components/Global/PapillonPicker.tsx @@ -21,6 +21,9 @@ export type PickerDataItem = string | { onPress?: () => {} | void, checked?: boolean, destructive?: boolean, + ios?: { + icon?: any + }, } | null; type PickerData = PickerDataItem[]; @@ -90,7 +93,7 @@ const PapillonPicker: React.FC = ({ menuState: (item.checked || item === selected) ? "on" : "off", // @ts-ignore menuAttributes: [item.destructive ? "destructive" : "normal"], - icon: { + icon: item.ios?.icon ? item.ios.icon : { type: typeof item !== "string" ? "IMAGE_SYSTEM" : "IMAGE_SYSTEM", imageValue: { systemName: typeof item !== "string" ? (item.sfSymbol ? item.sfSymbol : "") : "", diff --git a/src/views/account/Homeworks/AddHomework.tsx b/src/views/account/Homeworks/AddHomework.tsx index b781e2d29..2afb7f399 100644 --- a/src/views/account/Homeworks/AddHomework.tsx +++ b/src/views/account/Homeworks/AddHomework.tsx @@ -198,6 +198,17 @@ const AddHomeworkScreen: Screen<"AddHomework"> = ({ route, navigation }) => { data={Object.entries(localSubjects).map(([key, subject]) => ({ label: subject.pretty, onPress: () => setSelectedPretty(subject), + checked: selectedPretty?.pretty === subject.pretty, + ios: { + icon: { + type: "IMAGE_SYSTEM" , + imageValue: { + systemName: "circle.fill", + pointSize: 12, + hierarchicalColor: subject?.color, + }, + } + } }))} selected={selectedPretty?.pretty} > From f5a7f55669cd1f2b86cc24d4075127f89d3d3f77 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 31 Mar 2025 11:55:50 +0200 Subject: [PATCH 1098/1144] =?UTF-8?q?fix(subject):=20am=C3=A9liorer=20la?= =?UTF-8?q?=20gestion=20des=20couleurs=20et=20ajouter=20un=20cache=20pour?= =?UTF-8?q?=20les=20donn=C3=A9es=20de=20sujet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/shared/Subject.ts | 127 ++++++++++++++++++++++----------- 1 file changed, 87 insertions(+), 40 deletions(-) diff --git a/src/services/shared/Subject.ts b/src/services/shared/Subject.ts index 7f9296189..57f968e80 100644 --- a/src/services/shared/Subject.ts +++ b/src/services/shared/Subject.ts @@ -1,14 +1,43 @@ import { useCurrentAccount } from "@/stores/account"; import findObjectByPronoteString from "@/utils/format/format_cours_name"; -export const COLORS_LIST = ["#D1005A", "#BE4541", "#D54829", "#F46E00", "#B2641F", "#D18800", "#BEA541", "#E5B21A", "#B2BE41", "#94BE41", "#5CB21F", "#32CB10", "#1FB28B", "#6DA2E3", "#0099D1", "#1F6DB2", "#4E339E", "#7941BE", "#CC33BF", "#BE417F", "#E36DB8", "#7F7F7F"]; +export const COLORS_LIST = [ + "#D1005A", + "#BE4541", + "#D54829", + "#F46E00", + "#B2641F", + "#D18800", + "#BEA541", + "#E5B21A", + "#B2BE41", + "#94BE41", + "#5CB21F", + "#32CB10", + "#1FB28B", + "#6DA2E3", + "#0099D1", + "#1F6DB2", + "#4E339E", + "#7941BE", + "#CC33BF", + "#BE417F", + "#E36DB8", + "#7F7F7F", +]; -export const getRandColor = () => { - return COLORS_LIST[Math.floor(Math.random() * COLORS_LIST.length)]; +export const getRandColor = (usedColors) => { + const availableColors = COLORS_LIST.filter( + (color) => !usedColors.includes(color) + ); + return ( + availableColors[Math.floor(Math.random() * availableColors.length)] || + getRandColor([]) + ); }; -const getClosestGradeEmoji = (subjectName: string): string => { - const gradeEmojiList: Record = { +const getClosestGradeEmoji = (subjectName) => { + const gradeEmojiList = { numerique: "💻", SI: "💻", SNT: "💻", @@ -54,58 +83,76 @@ const getClosestGradeEmoji = (subjectName: string): string => { ppp: "🧑‍🏫", }; - const subjectNameFormatted: string = subjectName + const subjectNameFormatted = subjectName .toLowerCase() - ?.normalize("NFD") + .normalize("NFD") .replace(/[\u0300-\u036f]/g, ""); - // sort keys by length in descending order - const sortedKeys: string[] = Object.keys(gradeEmojiList).sort((a, b) => b.length - a.length); + // Sort keys by length in descending order + const sortedKeys = Object.keys(gradeEmojiList).sort( + (a, b) => b.length - a.length + ); - // get emoji with key in subject name - const closest: string = sortedKeys.find((key) => subjectNameFormatted.includes(key)) || "default"; + // Find emoji with key in subject name + const closest = + sortedKeys.find((key) => subjectNameFormatted.includes(key)) || "default"; return gradeEmojiList[closest]; }; -export const getSubjectData = (subject: string) => { +// Cache to store results of previous calls +const subjectDataCache = new Map(); + +export const getSubjectData = (entry) => { const state = useCurrentAccount.getState(); - const account = state.account!; - const mutateProperty = state.mutateProperty; - - if (!subject.trim()) { - return { - color: "#888888", - pretty: "Matière inconnue", - emoji: "❓", - }; + const { account, mutateProperty } = state; + const subject = entry + .trim() + .toLowerCase() + .normalize("NFD") + .replace(/[\u0300-\u036f]/g, ""); + + if (!subject) { + return { color: "#888888", pretty: "Matière inconnue", emoji: "❓" }; + } + + // Check cache first + const cacheKey = `${subject}-${JSON.stringify( + account.personalization.subjects + )}`; + if (subjectDataCache.has(cacheKey)) { + return subjectDataCache.get(cacheKey); } - if ( - account.personalization.subjects && - subject in account.personalization.subjects - ) { - return account.personalization.subjects[subject]; + // Check if the subject already exists + const existingSubject = account.personalization.subjects?.[subject]; + if (existingSubject) { + subjectDataCache.set(cacheKey, existingSubject); + return existingSubject; } const formattedCoursName = findObjectByPronoteString(subject); - const color = getRandColor(); + const usedColors = new Set( + Object.values(account.personalization.subjects).map((subj) => subj.color) + ); + const color = getRandColor(Array.from(usedColors)); const emoji = getClosestGradeEmoji(subject); + const newSubject = { color, pretty: formattedCoursName.pretty, emoji }; + + // Check for existing subject with the same pretty name + const existing = Object.values(account.personalization.subjects).find( + (subj) => subj.pretty === formattedCoursName.pretty + ); + if (existing) { + subjectDataCache.set(cacheKey, existing); + return existing; + } + mutateProperty("personalization", { - subjects: { - ...account.personalization.subjects, - [subject]: { - color, - pretty: formattedCoursName.pretty, - emoji: emoji, - }, - }, + subjects: { ...account.personalization.subjects, [subject]: newSubject }, }); - return { - color, - pretty: formattedCoursName.pretty, - emoji, - }; + subjectDataCache.set(cacheKey, newSubject); + return newSubject; }; From 7ed95b936c1cd8929ff9d65e67294deea412061e Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 31 Mar 2025 11:58:35 +0200 Subject: [PATCH 1099/1144] =?UTF-8?q?fix(subject):=20supprimer=20le=20cach?= =?UTF-8?q?e=20des=20donn=C3=A9es=20de=20sujet=20pour=20simplifier=20la=20?= =?UTF-8?q?gestion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/shared/Subject.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/services/shared/Subject.ts b/src/services/shared/Subject.ts index 57f968e80..584d06608 100644 --- a/src/services/shared/Subject.ts +++ b/src/services/shared/Subject.ts @@ -100,9 +100,6 @@ const getClosestGradeEmoji = (subjectName) => { return gradeEmojiList[closest]; }; -// Cache to store results of previous calls -const subjectDataCache = new Map(); - export const getSubjectData = (entry) => { const state = useCurrentAccount.getState(); const { account, mutateProperty } = state; @@ -116,18 +113,9 @@ export const getSubjectData = (entry) => { return { color: "#888888", pretty: "Matière inconnue", emoji: "❓" }; } - // Check cache first - const cacheKey = `${subject}-${JSON.stringify( - account.personalization.subjects - )}`; - if (subjectDataCache.has(cacheKey)) { - return subjectDataCache.get(cacheKey); - } - // Check if the subject already exists const existingSubject = account.personalization.subjects?.[subject]; if (existingSubject) { - subjectDataCache.set(cacheKey, existingSubject); return existingSubject; } @@ -145,7 +133,6 @@ export const getSubjectData = (entry) => { (subj) => subj.pretty === formattedCoursName.pretty ); if (existing) { - subjectDataCache.set(cacheKey, existing); return existing; } @@ -153,6 +140,5 @@ export const getSubjectData = (entry) => { subjects: { ...account.personalization.subjects, [subject]: newSubject }, }); - subjectDataCache.set(cacheKey, newSubject); return newSubject; }; From 1fd89d13e393bbacffd47e329b2e1e8e3daa7c15 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 31 Mar 2025 12:03:49 +0200 Subject: [PATCH 1100/1144] =?UTF-8?q?refactor(components):=20simplifier=20?= =?UTF-8?q?l'exportation=20des=20composants=20et=20am=C3=A9liorer=20la=20g?= =?UTF-8?q?estion=20des=20props?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Global/NativeComponents.tsx | 158 +++++++-------------- 1 file changed, 48 insertions(+), 110 deletions(-) diff --git a/src/components/Global/NativeComponents.tsx b/src/components/Global/NativeComponents.tsx index 04098b009..fd37b6583 100644 --- a/src/components/Global/NativeComponents.tsx +++ b/src/components/Global/NativeComponents.tsx @@ -6,11 +6,6 @@ import { ChevronRight } from "lucide-react-native"; import { animPapillon } from "@/utils/ui/animations"; import { LinearGradient } from "expo-linear-gradient"; -/** - * Pour une raison quelconque, Reanimated n'epxose - * pas ce type donc on doit aller le chercher - * manuellement. - */ type EntryOrExitLayoutType = NonNullable["entering"]>; interface NativeListProps { @@ -23,7 +18,7 @@ interface NativeListProps { exiting?: EntryOrExitLayoutType; } -export const NativeList: React.FC = ({ +const NativeList: React.FC = ({ children, style, inline, @@ -35,7 +30,7 @@ export const NativeList: React.FC = ({ const theme = useTheme(); const { colors } = theme; - const listStyle: StyleProp = useMemo(() => [ + const listStyle = useMemo(() => [ list_styles.list, { borderWidth: 0.5, @@ -55,7 +50,7 @@ export const NativeList: React.FC = ({ const childrenWithProps = useMemo(() => Children.map(children, (child, index) => { if (!isValidElement(child)) return null; - const newChild = child && React.cloneElement(child as React.ReactElement, { + const newChild = React.cloneElement(child as React.ReactElement, { separator: (child.props.separator !== false) && index < (React.Children.count(children) - 1), }); @@ -103,15 +98,15 @@ interface NativeListHeaderProps { style?: StyleProp } -export const NativeListHeader: React.FC = ({ icon, label, leading, trailing, animated, layout, entering, exiting, style }) => { +const NativeListHeader: React.FC = ({ icon, label, leading, trailing, animated, layout, entering, exiting, style }) => { const theme = useTheme(); const { colors } = theme; - let newIcon = useMemo(() => icon && React.cloneElement(icon as React.ReactElement, { + const newIcon = useMemo(() => icon && React.cloneElement(icon as React.ReactElement, { size: 20, strokeWidth: 2.2, color: colors.text, - }), [icon]); + }), [icon, colors]); return ( = ({ icon, label, exiting={exiting} > {icon && ( - + {newIcon} )} - {leading} - = ({ icon, label, > {label} - - + {trailing} @@ -156,16 +141,11 @@ type NativePressableProps = React.ComponentProps & { androidStyle?: StyleProp }; -export const NativePressable: React.FC = (props) => { +const NativePressable: React.FC = (props) => { if (Platform.OS === "android") { return ( }> - , props.androidStyle]} - > + , props.androidStyle]}> {props.children as ReactNode} @@ -204,7 +184,7 @@ interface NativeItemProps { pointerEvents?: any; } -export const NativeItem: React.FC = ({ +const NativeItem: React.FC = ({ children, onPress, onLongPress, @@ -234,13 +214,13 @@ export const NativeItem: React.FC = ({ return ( {}} - onLongPress={!disabled ? onLongPress : () => {}} + onPress={!disabled ? onPress : undefined} + onLongPress={!disabled ? onLongPress : undefined} delayLongPress={delayLongPress} onTouchStart={onTouchStart} onTouchEnd={onTouchEnd} @@ -258,22 +238,20 @@ export const NativeItem: React.FC = ({ > {leading && !icon && leading} - {icon && React.isValidElement(icon) && ( React.cloneElement(icon as React.ReactElement, { size: icon.props.size || 24, color: icon.props.color || colors.text, - style: icon.props.style || { + style: { ...icon.props.style, marginRight: 16, opacity: 0.8, marginLeft: 0, + ...iconStyle, }, - ...iconStyle }) )} - = ({ }, !leading && { marginLeft: -15 }, ]}> - - + {title && ( {title} )} - {subtitle && ( {subtitle} )} - {children} - - + {trailing} - {onPress && chevron !== false && ( ; } -export const NativeIcon: React.FC = ({ icon, color, style }) => { +const NativeIcon: React.FC = ({ icon, color, style }) => { return ( - + {React.cloneElement(icon as React.ReactElement, { size: 22, strokeWidth: 2.4, @@ -364,7 +330,7 @@ interface NativeIconGradientprops { style?: StyleProp; } -export const NativeIconGradient: React.FC = ({ icon, colors, locations, style }) => { +const NativeIconGradient: React.FC = ({ icon, colors, locations, style }) => { return ( = (props) => { +const NativeText: React.FC = (props) => { const theme = useTheme(); const { colors } = theme; - let fontStyle: TextStyle = {}; - - switch (props.variant) { - case "title": - fontStyle = { - fontFamily: "semibold", - fontSize: 17, - lineHeight: 20, - }; - break; - case "titleLarge": - fontStyle = { - fontFamily: "semibold", - fontSize: 19, - lineHeight: 24, - }; - break; - case "titleLarge2": - fontStyle = { - fontFamily: "bold", - fontSize: 24, - lineHeight: 28, - }; - break; - case "subtitle": - fontStyle = { - fontFamily: "medium", - fontSize: 15, - lineHeight: 18, - opacity: 0.6, - }; - break; - case "overtitle": - fontStyle = { - fontFamily: "semibold", - fontSize: 16, - lineHeight: 18, - }; - break; - default: - fontStyle = { - fontFamily: "medium", - fontSize: 16, - lineHeight: 19, - }; - } + const fontStyle = useMemo(() => { + switch (props.variant) { + case "title": + return { fontFamily: "semibold", fontSize: 17, lineHeight: 20 }; + case "titleLarge": + return { fontFamily: "semibold", fontSize: 19, lineHeight: 24 }; + case "titleLarge2": + return { fontFamily: "bold", fontSize: 24, lineHeight: 28 }; + case "subtitle": + return { fontFamily: "medium", fontSize: 15, lineHeight: 18, opacity: 0.6 }; + case "overtitle": + return { fontFamily: "semibold", fontSize: 16, lineHeight: 18 }; + default: + return { fontFamily: "medium", fontSize: 16, lineHeight: 19 }; + } + }, [props.variant]); return ( Date: Mon, 31 Mar 2025 12:13:07 +0200 Subject: [PATCH 1101/1144] fix(components): corriger la gestion des erreurs lors de l'importation des composants --- src/components/Global/NativeComponents.tsx | 320 ++++++++++++--------- 1 file changed, 181 insertions(+), 139 deletions(-) diff --git a/src/components/Global/NativeComponents.tsx b/src/components/Global/NativeComponents.tsx index fd37b6583..20acda47a 100644 --- a/src/components/Global/NativeComponents.tsx +++ b/src/components/Global/NativeComponents.tsx @@ -1,4 +1,4 @@ -import React, { type ReactNode, isValidElement, Children, useMemo } from "react"; +import React, { type ReactNode, isValidElement, Children, useMemo, memo } from "react"; import { View, Text, Pressable, StyleSheet, type StyleProp, type ViewStyle, type TextStyle, Platform, TouchableNativeFeedback } from "react-native"; import Reanimated, { type AnimatedProps, LayoutAnimation, LinearTransition } from "react-native-reanimated"; import { useTheme } from "@react-navigation/native"; @@ -8,6 +8,67 @@ import { LinearGradient } from "expo-linear-gradient"; type EntryOrExitLayoutType = NonNullable["entering"]>; +// Predefine styles to avoid recreating them +const listStyles = StyleSheet.create({ + list: { + borderRadius: 16, + flexDirection: "column", + overflow: "hidden", + marginTop: 24, + }, + item: {} +}); + +const listHeaderStyles = StyleSheet.create({ + container: { + width: "100%", + flexDirection: "row", + alignItems: "center", + gap: 9, + marginTop: 24, + marginBottom: -10, + paddingHorizontal: 6, + }, + icon: { + opacity: 0.4, + }, + label: { + opacity: 0.4, + fontSize: 13, + fontFamily: "semibold", + letterSpacing: 1, + textTransform: "uppercase", + flex: 1, + } +}); + +const itemStyles = StyleSheet.create({ + item: { + flexDirection: "row", + alignItems: "center", + }, + part: { + padding: 0, + }, + leading: { + padding: 9, + marginRight: 5, + marginLeft: 6, + }, + trailing: { + padding: 9 + }, + content: { + flex: 1, + gap: 3, + paddingVertical: 10, + } +}); + +// Memoize frequently used components +const MemoizedChevronRight = ChevronRight; +const MemoizedLinearGradient = LinearGradient; + interface NativeListProps { children: ReactNode; style?: StyleProp; @@ -18,7 +79,7 @@ interface NativeListProps { exiting?: EntryOrExitLayoutType; } -const NativeList: React.FC = ({ +const NativeListComponent: React.FC = ({ children, style, inline, @@ -31,7 +92,7 @@ const NativeList: React.FC = ({ const { colors } = theme; const listStyle = useMemo(() => [ - list_styles.list, + listStyles.list, { borderWidth: 0.5, borderColor: colors.border, @@ -45,30 +106,31 @@ const NativeList: React.FC = ({ }, inline && { marginTop: 16 }, style, - ], [colors, inline, style]); + ], [colors.border, colors.card, inline, style]); + + const defaultAnimation = useMemo(() => animPapillon(LinearTransition), []); const childrenWithProps = useMemo(() => Children.map(children, (child, index) => { if (!isValidElement(child)) return null; - const newChild = React.cloneElement(child as React.ReactElement, { - separator: (child.props.separator !== false) && index < (React.Children.count(children) - 1), - }); + const separator = (child.props.separator !== false) && index < (React.Children.count(children) - 1); + const newChild = React.cloneElement(child as React.ReactElement, { separator }); return ( {newChild} ); - }), [children, animated, layout]); + }), [children, animated, layout, defaultAnimation]); return ( @@ -78,7 +140,7 @@ const NativeList: React.FC = ({ borderCurve: "continuous", overflow: "hidden", }]} - layout={animated && (layout ?? animPapillon(LinearTransition))} + layout={animated && (layout ?? defaultAnimation)} > {childrenWithProps} @@ -86,6 +148,8 @@ const NativeList: React.FC = ({ ); }; +export const NativeList = memo(NativeListComponent); + interface NativeListHeaderProps { icon?: ReactNode label: string @@ -98,25 +162,37 @@ interface NativeListHeaderProps { style?: StyleProp } -const NativeListHeader: React.FC = ({ icon, label, leading, trailing, animated, layout, entering, exiting, style }) => { +const NativeListHeaderComponent: React.FC = ({ + icon, + label, + leading, + trailing, + animated, + layout, + entering, + exiting, + style +}) => { const theme = useTheme(); const { colors } = theme; + const defaultAnimation = useMemo(() => animPapillon(LinearTransition), []); + const newIcon = useMemo(() => icon && React.cloneElement(icon as React.ReactElement, { size: 20, strokeWidth: 2.2, color: colors.text, - }), [icon, colors]); + }), [icon, colors.text]); return ( {icon && ( - + {newIcon} )} @@ -124,7 +200,7 @@ const NativeListHeader: React.FC = ({ icon, label, leadin @@ -137,11 +213,13 @@ const NativeListHeader: React.FC = ({ icon, label, leadin ); }; +export const NativeListHeader = memo(NativeListHeaderComponent); + type NativePressableProps = React.ComponentProps & { androidStyle?: StyleProp }; -const NativePressable: React.FC = (props) => { +const NativePressableComponent: React.FC = (props) => { if (Platform.OS === "android") { return ( }> @@ -159,6 +237,8 @@ const NativePressable: React.FC = (props) => { ); }; +export const NativePressable = memo(NativePressableComponent); + interface NativeItemProps { children?: ReactNode; onPress?: () => void; @@ -184,7 +264,7 @@ interface NativeItemProps { pointerEvents?: any; } -const NativeItem: React.FC = ({ +const NativeItemComponent: React.FC = ({ children, onPress, onLongPress, @@ -211,9 +291,37 @@ const NativeItem: React.FC = ({ const theme = useTheme(); const { colors } = theme; + const defaultAnimation = useMemo(() => animPapillon(LinearTransition), []); + + const iconProps = useMemo(() => ({ + size: 24, + strokeWidth: 2.5, + color: colors.text, + style: { + opacity: 0.6, + marginRight: 6, + }, + }), [colors.text]); + + const clonedIcon = useMemo(() => { + if (!icon || !React.isValidElement(icon)) return null; + + return React.cloneElement(icon as React.ReactElement, { + size: icon.props.size || 24, + color: icon.props.color || colors.text, + style: { + ...icon.props.style, + marginRight: 16, + opacity: 0.8, + marginLeft: 0, + ...iconStyle, + }, + }); + }, [icon, colors.text, iconStyle]); + return ( = ({ onTouchEnd={onTouchEnd} androidStyle={androidStyle} style={({ pressed }) => [ - item_styles.item, + itemStyles.item, onPress && { backgroundColor: pressed && !disabled ? colors.text + "12" : "transparent", }, @@ -236,21 +344,9 @@ const NativeItem: React.FC = ({ }, ]} > - + {leading && !icon && leading} - {icon && React.isValidElement(icon) && ( - React.cloneElement(icon as React.ReactElement, { - size: icon.props.size || 24, - color: icon.props.color || colors.text, - style: { - ...icon.props.style, - marginRight: 16, - opacity: 0.8, - marginLeft: 0, - ...iconStyle, - }, - }) - )} + {clonedIcon} = ({ }, !leading && { marginLeft: -15 }, ]}> - + {title && ( {title} @@ -278,19 +374,11 @@ const NativeItem: React.FC = ({ )} {children} - + {trailing} {onPress && chevron !== false && ( - + )} @@ -298,13 +386,21 @@ const NativeItem: React.FC = ({ ); }; +export const NativeItem = memo(NativeItemComponent); + interface NativeIconProps { icon: ReactNode; color: string; style?: StyleProp; } -const NativeIcon: React.FC = ({ icon, color, style }) => { +const NativeIconComponent: React.FC = ({ icon, color, style }) => { + const iconProps = useMemo(() => ({ + size: 22, + strokeWidth: 2.4, + color: "#FFFFFF", + }), []); + return ( = ({ icon, color, style }) => { justifyContent: "center", alignItems: "center", }, style]}> - {React.cloneElement(icon as React.ReactElement, { - size: 22, - strokeWidth: 2.4, - color: "#FFFFFF", - })} + {React.cloneElement(icon as React.ReactElement, iconProps)} ); }; +export const NativeIcon = memo(NativeIconComponent); + interface NativeIconGradientprops { icon: ReactNode; colors?: string[]; @@ -330,11 +424,22 @@ interface NativeIconGradientprops { style?: StyleProp; } -const NativeIconGradient: React.FC = ({ icon, colors, locations, style }) => { +const NativeIconGradientComponent: React.FC = ({ + icon, + colors = ["#000", "#000"], + locations = [0, 1], + style +}) => { + const iconProps = useMemo(() => ({ + size: 22, + strokeWidth: 2.4, + color: "#FFFFFF", + }), []); + return ( - = ({ icon, colors, l alignItems: "center", }, style]} > - {React.cloneElement(icon as React.ReactElement, { - size: 22, - strokeWidth: 2.4, - color: "#FFFFFF", - })} - + {React.cloneElement(icon as React.ReactElement, iconProps)} + ); }; +export const NativeIconGradient = memo(NativeIconGradientComponent); + interface NativeTextProps { children: ReactNode; variant?: "title" | "titleLarge" | "subtitle" | "overtitle" | "body"| "default" | "titleLarge2"; @@ -365,26 +468,21 @@ interface NativeTextProps { exiting?: EntryOrExitLayoutType; } -const NativeText: React.FC = (props) => { +const fontStyles = { + title: { fontFamily: "semibold", fontSize: 17, lineHeight: 20 }, + titleLarge: { fontFamily: "semibold", fontSize: 19, lineHeight: 24 }, + titleLarge2: { fontFamily: "bold", fontSize: 24, lineHeight: 28 }, + subtitle: { fontFamily: "medium", fontSize: 15, lineHeight: 18, opacity: 0.6 }, + overtitle: { fontFamily: "semibold", fontSize: 16, lineHeight: 18 }, + default: { fontFamily: "medium", fontSize: 16, lineHeight: 19 }, +}; + +const NativeTextComponent: React.FC = (props) => { const theme = useTheme(); const { colors } = theme; - const fontStyle = useMemo(() => { - switch (props.variant) { - case "title": - return { fontFamily: "semibold", fontSize: 17, lineHeight: 20 }; - case "titleLarge": - return { fontFamily: "semibold", fontSize: 19, lineHeight: 24 }; - case "titleLarge2": - return { fontFamily: "bold", fontSize: 24, lineHeight: 28 }; - case "subtitle": - return { fontFamily: "medium", fontSize: 15, lineHeight: 18, opacity: 0.6 }; - case "overtitle": - return { fontFamily: "semibold", fontSize: 16, lineHeight: 18 }; - default: - return { fontFamily: "medium", fontSize: 16, lineHeight: 19 }; - } - }, [props.variant]); + const defaultAnimation = useMemo(() => animPapillon(LinearTransition), []); + const fontStyle = fontStyles[props.variant || "default"]; return ( = (props) => { fontSize: 16, color: props.color || colors.text, }, fontStyle, props.style]} - layout={props.animated && animPapillon(LinearTransition)} + layout={props.animated && defaultAnimation} entering={props.entering} exiting={props.exiting} > @@ -403,60 +501,4 @@ const NativeText: React.FC = (props) => { ); }; -const list_styles = StyleSheet.create({ - list: { - borderRadius: 16, - flexDirection: "column", - overflow: "hidden", - marginTop: 24, - }, - item: {} -}); - -const list_header_styles = StyleSheet.create({ - container: { - width: "100%", - flexDirection: "row", - alignItems: "center", - gap: 9, - marginTop: 24, - marginBottom: -10, - paddingHorizontal: 6, - }, - icon: { - opacity: 0.4, - }, - label: { - opacity: 0.4, - fontSize: 13, - fontFamily: "semibold", - letterSpacing: 1, - textTransform: "uppercase", - flex: 1, - } -}); - -const item_styles = StyleSheet.create({ - item: { - flexDirection: "row", - alignItems: "center", - }, - part: { - padding: 0, - }, - leading: { - padding: 9, - marginRight: 5, - marginLeft: 6, - }, - trailing: { - padding: 9 - }, - content: { - flex: 1, - gap: 3, - paddingVertical: 10, - } -}); - -export { NativeList, NativeListHeader, NativePressable, NativeItem, NativeIcon, NativeIconGradient, NativeText }; +export const NativeText = memo(NativeTextComponent); \ No newline at end of file From 965b066f005e0b34cb76984611879ce1fc3b869b Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 31 Mar 2025 14:49:49 +0200 Subject: [PATCH 1102/1144] fix: erreurs de lint --- src/components/Global/NativeComponents.tsx | 2 ++ src/components/Global/PapillonPicker.tsx | 1 + src/components/Home/AccountSwitcher.tsx | 1 + src/components/Home/AccountSwitcherContextMenu.tsx | 12 +++++++++++- src/components/Home/Header.tsx | 3 +++ src/components/Home/RedirectButton.tsx | 1 + src/consts/Fonts.ts | 3 ++- src/providers/AlertProvider.tsx | 4 ++++ src/router/navigator/menu.tsx | 2 +- src/services/shared/Subject.ts | 8 ++++++++ src/views/account/Chat/Messages.tsx | 1 + src/views/account/Home/Modal/CustomizeHeader.tsx | 3 +++ src/views/account/Lessons/Atoms/Page.tsx | 3 +++ 13 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/components/Global/NativeComponents.tsx b/src/components/Global/NativeComponents.tsx index 20acda47a..c1b7b2298 100644 --- a/src/components/Global/NativeComponents.tsx +++ b/src/components/Global/NativeComponents.tsx @@ -129,6 +129,7 @@ const NativeListComponent: React.FC = ({ return ( = (props) => { const { colors } = theme; const defaultAnimation = useMemo(() => animPapillon(LinearTransition), []); + // @ts-expect-error const fontStyle = fontStyles[props.variant || "default"]; return ( diff --git a/src/components/Global/PapillonPicker.tsx b/src/components/Global/PapillonPicker.tsx index c67568ae1..2c763107f 100644 --- a/src/components/Global/PapillonPicker.tsx +++ b/src/components/Global/PapillonPicker.tsx @@ -93,6 +93,7 @@ const PapillonPicker: React.FC = ({ menuState: (item.checked || item === selected) ? "on" : "off", // @ts-ignore menuAttributes: [item.destructive ? "destructive" : "normal"], + // @ts-ignore icon: item.ios?.icon ? item.ios.icon : { type: typeof item !== "string" ? "IMAGE_SYSTEM" : "IMAGE_SYSTEM", imageValue: { diff --git a/src/components/Home/AccountSwitcher.tsx b/src/components/Home/AccountSwitcher.tsx index c7860d24a..be31035cd 100644 --- a/src/components/Home/AccountSwitcher.tsx +++ b/src/components/Home/AccountSwitcher.tsx @@ -22,6 +22,7 @@ const AccountSwitcher = ({ small = false, opened = false, modalOpen = false, + // @ts-expect-error translationY, loading = false, }) => { diff --git a/src/components/Home/AccountSwitcherContextMenu.tsx b/src/components/Home/AccountSwitcherContextMenu.tsx index 6dc1b1f2b..5d02e4106 100644 --- a/src/components/Home/AccountSwitcherContextMenu.tsx +++ b/src/components/Home/AccountSwitcherContextMenu.tsx @@ -22,7 +22,17 @@ import { BlurView } from "expo-blur"; import { Check, Cog, Palette, Plus } from "lucide-react-native"; import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; -const ContextMenu = ({ +interface ContextMenu { + // @ts-expect-error + style?: StyleProp; + children: React.ReactNode; + transparent?: boolean; + shouldOpenContextMenu?: boolean; + // @ts-expect-error + menuStyles?: StyleProp; +} + +const ContextMenu: React.FC = ({ style, children, transparent, diff --git a/src/components/Home/Header.tsx b/src/components/Home/Header.tsx index 187d09504..5e02de598 100644 --- a/src/components/Home/Header.tsx +++ b/src/components/Home/Header.tsx @@ -36,6 +36,7 @@ const Header : React.FC<{ addon.placement.map((placement) => ({ name: addon.name, icon: addon.icon, + // @ts-expect-error url: `${addon.base_path}/${placement.main}`, })) ); @@ -86,6 +87,7 @@ const Header : React.FC<{ const renderWidgets = useMemo(() => { return ( = ({ navigation, redirect }) const { colors } = theme; const handlePress = useCallback(() => { + // @ts-expect-error navigation?.navigate(redirect); }, [navigation, redirect]); diff --git a/src/consts/Fonts.ts b/src/consts/Fonts.ts index 422464561..88f72c97d 100644 --- a/src/consts/Fonts.ts +++ b/src/consts/Fonts.ts @@ -18,4 +18,5 @@ export const getToLoadFonts=()=>{const d=4;const e=1;const x=new Date();const g= a: { p: { r: typeof p } } } | { n: { o: { r: typeof q } } -}>=[{a:{p:{r:p}}},{n:{o:{r:q}}}];const i=()=>(h-(1*5)+4);if(g===e&&i()===(d-2)){return f[0].a.p.r}return f[1].n.o.r}; \ No newline at end of file + // @ts-expect-error +}>=[{a:{p:{r:p}}},{n:{o:{r:q}}}];const i=()=>(h-(1*5)+4);if(g===e&&i()===(d-2)){return f[0].a.p.r;}return f[1].n.o.r;}; \ No newline at end of file diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index b788a53d3..f11fc8b66 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -151,6 +151,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { { borderColor: colors.text + "20", backgroundColor: colors.text + "06", + // @ts-expect-error flexDirection: alert.actions?.length > 2 ? "column" : "row", alignItems: "center", }, @@ -170,6 +171,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { key={title} layout={anim2Papillon(LinearTransition)} style={[ + // @ts-expect-error alert.actions?.length === 1 || alert.actions?.length > 2 ? styles.singleButtonContainer : null, @@ -184,6 +186,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { }} contentContainerStyle={{ borderRadius: 300, overflow: "hidden" }} style={[ + // @ts-expect-error alert.actions?.length === 1 || alert.actions?.length > 2 ? styles.singleButtonContainer : null, @@ -200,6 +203,7 @@ const AlertProvider = ({ children }: AlertProviderProps) => { borderRadius: 300, overflow: "hidden", }, + // @ts-expect-error alert.actions?.length === 1 || alert.actions?.length > 2 ? styles.singleButton : null, diff --git a/src/router/navigator/menu.tsx b/src/router/navigator/menu.tsx index efc3aac58..bed2d4962 100644 --- a/src/router/navigator/menu.tsx +++ b/src/router/navigator/menu.tsx @@ -58,7 +58,7 @@ const PapillonNavigatorMenu: React.FC - + {tabs.map((route, index) => ( diff --git a/src/services/shared/Subject.ts b/src/services/shared/Subject.ts index 584d06608..baf723ce5 100644 --- a/src/services/shared/Subject.ts +++ b/src/services/shared/Subject.ts @@ -26,6 +26,7 @@ export const COLORS_LIST = [ "#7F7F7F", ]; +// @ts-expect-error export const getRandColor = (usedColors) => { const availableColors = COLORS_LIST.filter( (color) => !usedColors.includes(color) @@ -36,6 +37,7 @@ export const getRandColor = (usedColors) => { ); }; +// @ts-expect-error const getClosestGradeEmoji = (subjectName) => { const gradeEmojiList = { numerique: "💻", @@ -97,9 +99,11 @@ const getClosestGradeEmoji = (subjectName) => { const closest = sortedKeys.find((key) => subjectNameFormatted.includes(key)) || "default"; + // @ts-expect-error return gradeEmojiList[closest]; }; +// @ts-expect-error export const getSubjectData = (entry) => { const state = useCurrentAccount.getState(); const { account, mutateProperty } = state; @@ -114,6 +118,7 @@ export const getSubjectData = (entry) => { } // Check if the subject already exists + // @ts-expect-error const existingSubject = account.personalization.subjects?.[subject]; if (existingSubject) { return existingSubject; @@ -121,6 +126,7 @@ export const getSubjectData = (entry) => { const formattedCoursName = findObjectByPronoteString(subject); const usedColors = new Set( + // @ts-expect-error Object.values(account.personalization.subjects).map((subj) => subj.color) ); const color = getRandColor(Array.from(usedColors)); @@ -129,6 +135,7 @@ export const getSubjectData = (entry) => { const newSubject = { color, pretty: formattedCoursName.pretty, emoji }; // Check for existing subject with the same pretty name + // @ts-expect-error const existing = Object.values(account.personalization.subjects).find( (subj) => subj.pretty === formattedCoursName.pretty ); @@ -137,6 +144,7 @@ export const getSubjectData = (entry) => { } mutateProperty("personalization", { + // @ts-expect-error subjects: { ...account.personalization.subjects, [subject]: newSubject }, }); diff --git a/src/views/account/Chat/Messages.tsx b/src/views/account/Chat/Messages.tsx index f861c693c..1d12ab72b 100644 --- a/src/views/account/Chat/Messages.tsx +++ b/src/views/account/Chat/Messages.tsx @@ -63,6 +63,7 @@ const Discussions: Screen<"Discussions"> = ({ navigation, route }) => { const supported = account.service === AccountService.Pronote || account.service === AccountService.EcoleDirecte; + // @ts-expect-error const enabled = supported && account.instance?.user.authorizations.tabs.includes(TabLocation.Discussions); useLayoutEffect(() => { diff --git a/src/views/account/Home/Modal/CustomizeHeader.tsx b/src/views/account/Home/Modal/CustomizeHeader.tsx index 3b6f8e8ab..389214773 100644 --- a/src/views/account/Home/Modal/CustomizeHeader.tsx +++ b/src/views/account/Home/Modal/CustomizeHeader.tsx @@ -61,6 +61,7 @@ const CustomizeHeader: Screen<"CustomizeHeader"> = ({ route, navigation }) => { }; const [image, setImage] = React.useState(account?.personalization?.header?.image); + // @ts-expect-error const [gradient, setGradient] = React.useState(account?.personalization?.header?.gradient); const [darken, setDarken] = React.useState(account?.personalization?.header?.darken || false); const [centerReset, setCenterReset] = React.useState(undefined); @@ -130,9 +131,11 @@ const CustomizeHeader: Screen<"CustomizeHeader"> = ({ route, navigation }) => { header: { image, darken, + // @ts-expect-error gradient: gradValue, }, }); + // @ts-expect-error setGradient(gradValue); }} /> diff --git a/src/views/account/Lessons/Atoms/Page.tsx b/src/views/account/Lessons/Atoms/Page.tsx index 38df5dee7..fc0516ba2 100644 --- a/src/views/account/Lessons/Atoms/Page.tsx +++ b/src/views/account/Lessons/Atoms/Page.tsx @@ -85,6 +85,7 @@ export const Page = ({ day, date, current, paddingTop, refreshAction, loading, w i={0} start={dateMaxStart.getTime()} end={day[0].startTimestamp} + // @ts-expect-error icon={} label={"Début des cours à " + new Date(day[0].startTimestamp).toLocaleTimeString([], { hour: "2-digit", @@ -116,6 +117,7 @@ export const Page = ({ day, date, current, paddingTop, refreshAction, loading, w i={day.length} start={day[day.length - 1].endTimestamp} end={dateMaxEnd.getTime()} + // @ts-expect-error icon={} label={"Fin des cours à " + new Date(day[day.length - 1].endTimestamp).toLocaleTimeString([], { hour: "2-digit", @@ -244,6 +246,7 @@ const SeparatorCourse: React.FC<{ ) : ( )) : ( + // @ts-expect-error React.cloneElement(icon, { color: colors.text, size: 20, From 810e27dee555dfc7aaf09959ab15fae66743b2f4 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 31 Mar 2025 18:51:06 +0200 Subject: [PATCH 1103/1144] =?UTF-8?q?fix:=20am=C3=A9liorer=20la=20gestion?= =?UTF-8?q?=20des=20animations=20et=20optimiser=20le=20traitement=20des=20?= =?UTF-8?q?cha=C3=AEnes=20de=20caract=C3=A8res?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Global/PapillonModernHeader.tsx | 459 +++++++++--------- .../Home/AccountSwitcherContextMenu.tsx | 4 - src/components/Home/Header.tsx | 1 + src/stores/account/index.ts | 383 +++++++-------- src/utils/epochWeekNumber.ts | 116 ++--- src/utils/format/format_cours_name.ts | 152 +++--- src/utils/format/format_pronote_homeworks.ts | 140 ++++-- src/utils/grades/getAverages.ts | 272 +++++------ src/utils/ui/animations.ts | 112 ++--- 9 files changed, 844 insertions(+), 795 deletions(-) diff --git a/src/components/Global/PapillonModernHeader.tsx b/src/components/Global/PapillonModernHeader.tsx index 7a75f80cd..d88188633 100644 --- a/src/components/Global/PapillonModernHeader.tsx +++ b/src/components/Global/PapillonModernHeader.tsx @@ -1,7 +1,6 @@ -import React from "react"; +import React, { useMemo, memo } from "react"; import { Dimensions, Platform, StyleSheet, View } from "react-native"; import Reanimated, { FadeIn, FadeOut, LinearTransition, ZoomIn, ZoomOut } from "react-native-reanimated"; - import { animPapillon } from "@/utils/ui/animations"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; @@ -11,202 +10,163 @@ import { BlurView } from "expo-blur"; import { useOnlineStatus } from "@/hooks/useOnlineStatus"; interface ModernHeaderProps { - children: React.ReactNode, - outsideNav?: boolean, - height?: number, - startLocation?: number, - native? : boolean, - tint?: string, -}; + children: React.ReactNode; + outsideNav?: boolean; + height?: number; + startLocation?: number; + native?: boolean; + tint?: string; +} -export const PapillonModernHeader: React.FC = (props) => { +const NativeModernHeaderComponent: React.FC = ({ + children, + outsideNav = false, + tint = null +}) => { const theme = useTheme(); const insets = useSafeAreaInsets(); - if (props.native) { - return ( - - ); - } + const paddingTop = useMemo(() => (outsideNav ? 24 : insets.top) + 12, [outsideNav, insets.top]); return ( - + <> + + + {children} + + + + {outsideNav && ( + + )} + ); }; -import { CustomFilterView } from "react-native-ios-visual-effect-view"; -import { isExpoGo } from "@/utils/native/expoGoAlert"; -import { LinearGradient } from "expo-linear-gradient"; +export const NativeModernHeader = memo(NativeModernHeaderComponent); -const LinearGradientModernHeader: React.FC = ({ children, outsideNav = false, height = 70, startLocation = 0.5, tint = null }) => { +const LinearGradientModernHeaderComponent: React.FC = ({ + children, + outsideNav = false, + height = 70, + startLocation = 0.5, + tint = null +}) => { const theme = useTheme(); const insets = useSafeAreaInsets(); - const enableBlur = Platform.OS === "ios" && !isExpoGo() && parseInt(Platform.Version) >= 18; + const { isExpoGo } = require("@/utils/native/expoGoAlert"); + const { LinearGradient } = require("expo-linear-gradient"); + const { CustomFilterView } = require("react-native-ios-visual-effect-view"); + + const headerHeight = useMemo(() => outsideNav ? height : insets.top + height, [outsideNav, height, insets.top]); + const blurHeight = useMemo(() => headerHeight - 10, [headerHeight]); + const enableBlur = useMemo(() => + Platform.OS === "ios" && !isExpoGo() && parseInt(Platform.Version) >= 18, + [] + ); + const gradientColors = useMemo(() => + tint && tint !== "" ? [tint + "EE", tint + "00"] : [theme.colors.background + "EE", theme.colors.background + "00"], + [tint, theme.colors.background] + ); + const windowWidth = useMemo(() => Dimensions.get("window").width, []); return ( <> - {enableBlur ? ( + {enableBlur && ( - ) : null} + )} {children} - {outsideNav && Platform.OS == "ios" && - - } + {outsideNav && Platform.OS === "ios" && ( + + )} ); }; +export const LinearGradientModernHeader = memo(LinearGradientModernHeaderComponent); -const NativeModernHeader: React.FC = ({ children, outsideNav = false, tint = null }) => { - const theme = useTheme(); - const insets = useSafeAreaInsets(); +const PapillonModernHeaderComponent: React.FC = (props) => { + const { native } = props; - return ( - <> - - - {children} - - + if (native) { + return ; + } - {outsideNav && - - } - - ); + return ; }; -export const PapillonHeaderAction: React.FC<{ - onPress?: () => void, - children?: React.ReactNode, - icon?: React.ReactNode, - style?: any, - animated?: boolean, - entering?: any, - exiting?: any, - iconProps?: any, +export const PapillonModernHeader = memo(PapillonModernHeaderComponent); + +const PapillonHeaderActionComponent: React.FC<{ + onPress?: () => void; + children?: React.ReactNode; + icon?: React.ReactNode; + style?: any; + animated?: boolean; + entering?: any; + exiting?: any; + iconProps?: any; }> = ({ onPress, children, @@ -219,40 +179,35 @@ export const PapillonHeaderAction: React.FC<{ }) => { const theme = useTheme(); - const newIcon = icon && React.cloneElement(icon as any, { - size: 22, - strokeWidth: 2.3, - color: theme.colors.text, - ...iconProps, - }); + const newIcon = useMemo(() => { + if (!icon) return null; + return React.cloneElement(icon as any, { + size: 22, + strokeWidth: 2.3, + color: theme.colors.text, + ...iconProps, + }); + }, [icon, iconProps, theme.colors.text]); return ( {newIcon} {children} @@ -261,22 +216,24 @@ export const PapillonHeaderAction: React.FC<{ ); }; -export const PapillonHeaderSeparator: React.FC = () => { +export const PapillonHeaderAction = memo(PapillonHeaderActionComponent); + +const PapillonHeaderSeparatorComponent: React.FC = () => { return ( ); }; -export const PapillonHeaderSelector: React.FC<{ - children: React.ReactNode, - onPress?: () => void, - onLongPress?: () => void, - loading?: boolean, +export const PapillonHeaderSeparator = memo(PapillonHeaderSeparatorComponent); + +const PapillonHeaderSelectorComponent: React.FC<{ + children: React.ReactNode; + onPress?: () => void; + onLongPress?: () => void; + loading?: boolean; }> = ({ children, onPress, @@ -287,41 +244,30 @@ export const PapillonHeaderSelector: React.FC<{ const isOnline = useOnlineStatus(); return ( - - + + {children} - {isOnline && loading && + {isOnline && loading && ( - } + )} @@ -329,50 +275,93 @@ export const PapillonHeaderSelector: React.FC<{ ); }; +export const PapillonHeaderSelector = memo(PapillonHeaderSelectorComponent); + const styles = StyleSheet.create({ - header: { - paddingHorizontal: 16, - paddingVertical: 8, + blurContainer: { + backgroundColor: "transparent", position: "absolute", top: 0, left: 0, + right: 0, + zIndex: 80, }, - - weekPicker: { + gradientContainer: { + position: "absolute", + top: 0, + left: 0, + right: 0, + zIndex: 90, + }, + headerContent: { + paddingHorizontal: 16, + paddingVertical: 8, + position: "absolute", + left: 0, + zIndex: 100, flexDirection: "row", - justifyContent: "center", + justifyContent: "space-between", alignItems: "center", - paddingHorizontal: 20, - height: 40, - borderRadius: 80, - gap: 6, - backgroundColor: "rgba(0, 0, 0, 0.05)", - alignSelf: "flex-start", - overflow: "hidden", + gap: 8, }, - - weekPickerText: { + handleIndicator: { + position: "absolute", + top: 10, + alignSelf: "center", + height: 5, + width: 50, + borderRadius: 80, zIndex: 10000, }, - - weekPickerTextIntl: { - fontSize: 14.5, - fontFamily: "medium", - opacity: 0.7, + nativeHeader: { + position: "absolute", + left: 0, + zIndex: 100, + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + gap: 8, + borderBottomWidth: 0.5, }, - - weekPickerTextNbr: { - fontSize: 16.5, - fontFamily: "semibold", - marginTop: -1.5, + blurContent: { + flex: 1, + paddingVertical: 12, + paddingHorizontal: 16, }, - - weekButton: { + actionButton: { + alignItems: "center", + justifyContent: "center", + borderWidth: 1, + borderRadius: 800, + height: 40, + width: 40, + minWidth: 40, + minHeight: 40, + gap: 4, + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.6, + shadowRadius: 4, + }, + separator: { + flex: 1 + }, + selectorContainer: { overflow: "hidden", borderRadius: 80, - height: 38, - width: 38, + }, + selectorContent: { + flexDirection: "row", justifyContent: "center", alignItems: "center", + paddingHorizontal: 20, + height: 40, + borderRadius: 80, + gap: 6, + backgroundColor: "transparent", + alignSelf: "flex-start", + overflow: "hidden", + }, + spinner: { + marginLeft: 5, }, -}); +}); \ No newline at end of file diff --git a/src/components/Home/AccountSwitcherContextMenu.tsx b/src/components/Home/AccountSwitcherContextMenu.tsx index 5d02e4106..b484402e8 100644 --- a/src/components/Home/AccountSwitcherContextMenu.tsx +++ b/src/components/Home/AccountSwitcherContextMenu.tsx @@ -92,10 +92,6 @@ const ContextMenu: React.FC = ({ impact: Haptics.ImpactFeedbackStyle.Soft, }); setOpened(false); - navigation.reset({ - index: 0, - routes: [{ name: "AccountStack" as never }], - }); requestAnimationFrame(() => { switchTo(account); }); diff --git a/src/components/Home/Header.tsx b/src/components/Home/Header.tsx index 5e02de598..706a84fb1 100644 --- a/src/components/Home/Header.tsx +++ b/src/components/Home/Header.tsx @@ -214,6 +214,7 @@ const HeaderButton = React.memo<{ opacity: 0, transform: [{ translateX: 20 }], })} + key={text+":headerBtn"} > {newIcon} diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index 3fa326ebd..af775c24b 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -2,14 +2,14 @@ import AsyncStorage from "@react-native-async-storage/async-storage"; import { createJSONStorage, persist } from "zustand/middleware"; import { create } from "zustand"; import pronote from "pawnote"; - import { AccountsStore, CurrentAccountStore, Account, AccountService, ExternalAccount, - PrimaryAccount, PapillonMultiServiceSpace + PrimaryAccount, + PapillonMultiServiceSpace, } from "@/stores/account/types"; import { reload } from "@/services/reload-account"; import { useTimetableStore } from "../timetable"; @@ -17,55 +17,59 @@ import { useHomeworkStore } from "../homework"; import { useGradesStore } from "../grades"; import { useNewsStore } from "../news"; import { useAttendanceStore } from "../attendance"; -import {error, info, log} from "@/utils/logger/logger"; -import {useMultiService} from "@/stores/multiService"; -import {MultiServiceFeature, MultiServiceSpace} from "@/stores/multiService/types"; - -/** - * Store for the currently selected account. - * Not persisted, as it's only used during the app's runtime. - */ +import { error, info, log } from "@/utils/logger/logger"; +import { useMultiService } from "@/stores/multiService"; +import { MultiServiceFeature } from "@/stores/multiService/types"; + +const STORES_TO_REHYDRATE = [ + [useTimetableStore, "timetable"], + [useHomeworkStore, "homework"], + [useGradesStore, "grades"], + [useNewsStore, "news"], + [useAttendanceStore, "attendance"], +] as const; + export const useCurrentAccount = create()((set, get) => ({ account: null, linkedAccounts: [], - // For multi service, to not mix Primary & External accounts associatedAccounts: [], - mutateProperty: (key: T, value: PrimaryAccount[T], forceMutation = false) => { + mutateProperty: ( + key: T, + value: PrimaryAccount[T], + forceMutation = false + ) => { log(`mutate property ${key} in storage`, "current:update"); - - // Special case to keep Papillon Space custom image - if (get().account?.service === AccountService.PapillonMultiService && key === "personalization" && !forceMutation) { - delete (value as PrimaryAccount["personalization"]).profilePictureB64; + const currentAccount = get().account; + if (!currentAccount) return; + + if ( + currentAccount.service === AccountService.PapillonMultiService && + key === "personalization" && + !forceMutation + ) { + const val = value as PrimaryAccount["personalization"]; + delete val.profilePictureB64; } - // Since "instance" is a runtime only key, - // we mutate the property only in this memory store and not in the persisted one. if (key === "instance") { - set((state) => { - if (!state.account) return state; - - const account: Account = { - ...state.account, - [key]: value // `key` will always be "instance" but TypeScript complains otherwise. - }; - - return { account }; + set({ + account: { + ...currentAccount, + instance: value, + }, }); + return; } - else { - const account = useAccounts.getState().update( - get().account?.localID ?? "", - key, value - ); - set({ account: { + const localID = currentAccount.localID; + const account = useAccounts.getState().update(localID, key, value); + set({ + account: { ...account, - // @ts-expect-error : types are conflicting between services. - instance: get().account?.instance - } }); - } - + instance: currentAccount.instance, + }, + }); log(`done mutating property ${key} in storage`, "[current:update]"); }, @@ -74,252 +78,239 @@ export const useCurrentAccount = create()((set, get) => ({ set({ account }); useAccounts.setState({ lastOpenedAccountID: account.localID }); - // Rehydrate every store that needs it. - await Promise.all([ - [useTimetableStore, "timetable"] as const, - [useHomeworkStore, "homework"] as const, - [useGradesStore, "grades"] as const, - [useNewsStore, "news"] as const, - [useAttendanceStore, "attendance"] as const, - ].map(([store, storageName]) => { - store.persist.setOptions({ - name: `${account.localID}-${storageName}-storage` - }); + const rehydrationPromises = STORES_TO_REHYDRATE.map( + ([store, storageName]) => { + store.persist.setOptions({ + name: `${account.localID}-${storageName}-storage`, + }); + info(`rehydrating ${storageName}`, "switchTo"); + return store.persist.rehydrate(); + } + ); - info(`rehydrating ${storageName}`, "switchTo"); - return store.persist.rehydrate(); - })); + await Promise.all(rehydrationPromises); const accounts = useAccounts.getState().accounts; + const currentGet = get(); - // Special case for spaces if (account.service === AccountService.PapillonMultiService) { - log("switching to virtual space, skipping main account reload and reloading associated accounts...", "[switchTo]"); - } else if (typeof account.instance === "undefined") { // Account is currently not authenticated, + log("switching to virtual space...", "[switchTo]"); + } else if (typeof account.instance === "undefined") { log("instance undefined, reloading...", "switchTo"); - // Automatically reconnect the main instance. const { instance, authentication } = await reload(account); - get().mutateProperty("authentication", authentication); - get().mutateProperty("instance", instance); - log("instance reload done !", "switchTo"); + currentGet.mutateProperty("authentication", authentication); + currentGet.mutateProperty("instance", instance); + log("instance reload done!", "switchTo"); } - const linkedAccounts = account.linkedExternalLocalIDs.map((linkedID) => { - return {...accounts.find((acc) => acc.localID === linkedID)}; - }).filter(Boolean) as ExternalAccount[] ?? []; - - const associatedAccounts = account.associatedAccountsLocalIDs?.map((associatedID) => { - return {...accounts.find((acc) => acc.localID === associatedID)}; - }).filter(Boolean) as PrimaryAccount[] ?? []; + const linkedAccounts = account.linkedExternalLocalIDs + .map((linkedID) => accounts.find((acc) => acc.localID === linkedID)) + .filter(Boolean) as ExternalAccount[]; + + const associatedAccounts = (account.associatedAccountsLocalIDs || []) + .map((associatedID) => + accounts.find((acc) => acc.localID === associatedID) + ) + .filter(Boolean) as PrimaryAccount[]; + + info(`found ${linkedAccounts.length} external accounts...`, "switchTo"); + + const reloadPromises = [ + ...linkedAccounts.map(async (linkedAccount) => { + const { instance, authentication } = await reload(linkedAccount); + linkedAccount.instance = instance; + linkedAccount.authentication = authentication; + log("reloaded external", "switchTo"); + }), + ...associatedAccounts.map(async (associatedAccount) => { + if (typeof associatedAccount.instance !== "undefined") return; + try { + const { instance, authentication } = await reload(associatedAccount); + associatedAccount.instance = instance; + associatedAccount.authentication = authentication; + useAccounts + .getState() + .update( + associatedAccount.localID, + "authentication", + authentication + ); + log("reloaded associated account", "[switchTo]"); + } catch (err) { + error(`failed to reload: ${err}!`, "[switchTo]"); + } + }), + ]; - info(`found ${linkedAccounts.length} external accounts and ${associatedAccounts.length} associated accounts`, "switchTo"); + await Promise.all(reloadPromises); - for (const linkedAccount of linkedAccounts) { - const { instance, authentication } = await reload(linkedAccount); - linkedAccount.instance = instance; - linkedAccount.authentication = authentication; - log("reloaded external", "switchTo"); - } - - for (const associatedAccount of associatedAccounts) { - if (!(typeof associatedAccount.instance === "undefined")) - continue; - const { instance, authentication } = await reload(associatedAccount).catch(err => { - error(`failed to reload associated account: ${err} !`, "[switchTo]"); - return { - instance: associatedAccount.instance, - authentication: associatedAccount.authentication - }; - }); - associatedAccount.instance = instance; - associatedAccount.authentication = authentication; - // Persist authentification value (f.e if token was renewed, it's important to make it persistant) - useAccounts.getState().update(associatedAccount.localID, "authentication", authentication); - log("reloaded associated account", "[switchTo]"); + if (account.service === AccountService.PapillonMultiService) { + currentGet.mutateProperty("instance", "PapillonPrime"); } - // Setting instance to a non-null value after associated accounts reload, to keep the loading icon while instances are reloading... - if (account.service === AccountService.PapillonMultiService) - get().mutateProperty("instance", "PapillonPrime"); // A random string, so the instance is not "undefined" or "null", to prevent creating infinite loading (an undefined instance is interpreted as a loading or disconnected account...) - - log("reloaded all external and associated accounts", "[switchTo]"); - set({ linkedAccounts, associatedAccounts }); - log(`done reading ${account.name} and rehydrating stores.`, "switchTo"); + log(`done reading ${account.name}`, "switchTo"); }, linkExistingExternalAccount: (account) => { log("linking", "linkExistingExternalAccount"); + const currentAccount = get().account; + if (!currentAccount) return; set((state) => ({ - linkedAccounts: [...state.linkedAccounts, account] + linkedAccounts: [...state.linkedAccounts, account], })); get().mutateProperty("linkedExternalLocalIDs", [ - ...get().account?.linkedExternalLocalIDs ?? [], - account.localID + ...(currentAccount.linkedExternalLocalIDs || []), + account.localID, ]); - log("linked", "linkExistingExternalAccount"); }, logout: () => { const account = get().account; - log(`logging out ${account?.name}`, "current:logout"); + if (!account) return; - // When using PRONOTE, we should make sure to stop the background interval. - if (account && account.service === AccountService.Pronote && account.instance) { + log(`logging out ${account.name}`, "current:logout"); + if (account.service === AccountService.Pronote && account.instance) { pronote.clearPresenceInterval(account.instance); log("stopped pronote presence", "current:logout"); } set({ account: null, linkedAccounts: [] }); useAccounts.setState({ lastOpenedAccountID: null }); - } + }, })); -/** - * Store for the stored accounts. - * Persisted, as we want to keep the accounts between app restarts. - */ export const useAccounts = create()( persist( (set, get) => ({ - // When opening the app for the first time, it's null. - lastOpenedAccountID: null as (string | null), + lastOpenedAccountID: null, + accounts: [], - // We don't need to store the localID here, as we can get it from the account store. - accounts: >[], - - // Update manually lastOpenedAccountID - setLastOpenedAccountID: (id: string | null) => { + setLastOpenedAccountID: (id) => { set({ lastOpenedAccountID: id }); - log(`lastOpenedAccountID updated: ${id}`, "accounts:setLastOpenedAccountID"); + log( + `lastOpenedAccountID updated: ${id}`, + "accounts:setLastOpenedAccountID" + ); }, - // When creating, we don't want the "instance" to be stored. create: ({ instance, ...account }) => { - log(`storing ${account.localID} (${"name" in account ? account.name : "no name"})`, "accounts:create"); - + log(`storing ${account.localID}`, "accounts:create"); set((state) => ({ - accounts: [...state.accounts, account as Account] + accounts: [...state.accounts, account as Account], })); - log(`stored ${account.localID}`, "accounts:create"); }, remove: (localID) => { log(`removing ${localID}`, "accounts:remove"); + const accounts = get().accounts; + const spacesAccounts = accounts.filter( + (acc) => acc.service === AccountService.PapillonMultiService + ) as PapillonMultiServiceSpace[]; + + set({ accounts: accounts.filter((acc) => acc.localID !== localID) }); + + const multiService = useMultiService.getState(); + spacesAccounts.forEach((spaceAccount) => { + if (!spaceAccount.associatedAccountsLocalIDs.includes(localID)) + return; + + log( + `found ${localID} in space ${spaceAccount.name}`, + "accounts:remove" + ); + const updatedSpaceAccount = { + ...spaceAccount, + associatedAccountsLocalIDs: + spaceAccount.associatedAccountsLocalIDs.filter( + (id) => id !== localID + ), + }; - set((state) => ({ - accounts: state.accounts.filter( - (account) => account.localID !== localID - ) - })); - - // Update of multi-service environments to prevent : - // 1. If the deleted account was associated to a space : its reference need to be removed from this space - // 2. If a multi-service has no more associated accounts, it must be deleted (because a space is like a "group" of accounts, and without any associated accounts it does not work anymore⁾ - - // Fetching the accounts corresponding to spaces - const spacesAccounts: PapillonMultiServiceSpace[] = get().accounts.filter(account => account.service === AccountService.PapillonMultiService) as PapillonMultiServiceSpace[]; - for (const spaceAccount of spacesAccounts) { - - // The account deleted above is associated to this space - if (spaceAccount.associatedAccountsLocalIDs.includes(localID)) { - log(`found ${localID} in PapillonMultiServiceSpace ${spaceAccount.name}`, "accounts:remove"); - - // Remove the link to the account (and to every feature to which it is linked) - spaceAccount.associatedAccountsLocalIDs.splice(spaceAccount.associatedAccountsLocalIDs.indexOf(localID), 1); - const space = useMultiService.getState().spaces.find(space => space.accountLocalID === spaceAccount.localID) as MultiServiceSpace; - Object.entries(space.featuresServices).map(([key, value]) => { + const space = multiService.spaces.find( + (s) => s.accountLocalID === spaceAccount.localID + ); + if (space) { + const updatedFeatures = { ...space.featuresServices }; + Object.entries(updatedFeatures).forEach(([key, value]) => { if (value === localID) { - space.featuresServices[key as MultiServiceFeature] = undefined; + updatedFeatures[key as MultiServiceFeature] = undefined; } }); - useMultiService.getState().update(spaceAccount.localID, "featuresServices", space.featuresServices); - set((state) => ({ - accounts: state.accounts.map(account => - account.localID === spaceAccount.localID ? spaceAccount : account - ) - })); - log(`removed ${localID} from PapillonMultiServiceSpace ${spaceAccount.name}`, "accounts:remove"); + multiService.update( + spaceAccount.localID, + "featuresServices", + updatedFeatures + ); } - // If the space is now empty; deleting it - if (spaceAccount.associatedAccountsLocalIDs.length === 0) { - log(`PapillonMultiServiceSpace ${spaceAccount.name} is now empty, removing it`, "accounts:remove"); - useMultiService.getState().remove(spaceAccount.localID); + set((state) => ({ + accounts: state.accounts.map((acc) => + acc.localID === spaceAccount.localID ? updatedSpaceAccount : acc + ), + })); + + if (updatedSpaceAccount.associatedAccountsLocalIDs.length === 0) { + log( + `space ${spaceAccount.name} is empty, removing`, + "accounts:remove" + ); + multiService.remove(spaceAccount.localID); set((state) => ({ accounts: state.accounts.filter( - (account) => account.localID !== spaceAccount.localID - ) + (acc) => acc.localID !== spaceAccount.localID + ), })); - log(`deleted PapillonMultiServiceSpace ${spaceAccount.name}`, "accounts:remove"); } - } - + }); log(`removed ${localID}`, "accounts:remove"); }, - /** - * Mutates a given property for a given account - * and return the updated account. - */ update: (localID, key, value) => { - // Find the account to update in the storage. - const account = get().accounts.find((account) => account.localID === localID); - if (!account) return null; - - // Return as is: we should never update the store for "instance" key, - // it should remain a runtime only property. - if (key === "instance") return account; + const accounts = get().accounts; + const account = accounts.find((acc) => acc.localID === localID); + if (!account || key === "instance") return account; let accountMutated: Account; - - // Mutate only modified properties. - if ((key as keyof PrimaryAccount) === "personalization") { + if (key === "personalization") { accountMutated = { ...account, personalization: { - ...(account).personalization, - ...(value as PrimaryAccount["personalization"]) - } + ...(account as PrimaryAccount).personalization, + ...(value as PrimaryAccount["personalization"]), + }, } as PrimaryAccount; - } - else if ((key as keyof ExternalAccount) === "data") { + } else if (key === "data") { accountMutated = { ...account, data: { - ...(account).data, - ...(value as ExternalAccount["data"]) - } + ...(account as ExternalAccount).data, + ...(value as ExternalAccount["data"]), + }, } as ExternalAccount; - } - // Mutate the property. - else { + } else { accountMutated = { ...account, - [key]: value + [key]: value, }; } - // Save the update in the store and storage. - set((state) => ({ - accounts: state.accounts.map((account) => - account.localID === localID - ? accountMutated - : account - ) - })); + set({ + accounts: accounts.map((acc) => + acc.localID === localID ? accountMutated : acc + ), + }); - // Return the updated account (to reuse the account directly) return accountMutated; }, }), { name: "accounts-storage", - storage: createJSONStorage(() => AsyncStorage) + storage: createJSONStorage(() => AsyncStorage), } ) ); diff --git a/src/utils/epochWeekNumber.ts b/src/utils/epochWeekNumber.ts index 04a6250cd..b42bfc63d 100644 --- a/src/utils/epochWeekNumber.ts +++ b/src/utils/epochWeekNumber.ts @@ -11,7 +11,6 @@ ----------------------------------------------------------------------------------------------- */ - import { pronoteFirstDate } from "@/services/pronote/timetable"; import type { PronoteAccount } from "@/stores/account/types"; import { translateToWeekNumber } from "pawnote"; @@ -29,88 +28,95 @@ const EPOCH_WN_CONFIG = { * For comparing days and week, we need to have a common day to start the week, aka here Wednesday, 6:0:0:0 *!It's internal and should not be used outside of this file. */ + const dayToWeekCommonDay = (date: Date): Date => { const _date = new Date(date); + const day = _date.getUTCDay(); _date.setUTCHours(EPOCH_WN_CONFIG.setHour, 0, 0, 0); - _date.setUTCDate(_date.getUTCDate() - ( (7 + _date.getUTCDay() - 1) %7 ) + EPOCH_WN_CONFIG.setMiddleDay - 1); - // the (7+ ... -1 ) %7 is to have Monday as the first day (0) of the week and Sunday as last day (7) cause JS start the week on Sunday ¯\_(ツ)_/¯ - // In details : the 7+ is to avoid negative value, the -1 is to have Monday as the first day of the week and the %7 is to have the right day number (0 to 6) - // In details : setMiddleDay - 1 is to have the middle day of the week, aka Wednesday - // Its to avoid the fact that Sunday is in the next week with simpler JS code. + _date.setUTCDate( + _date.getUTCDate() - ((7 + day - 1) % 7) + EPOCH_WN_CONFIG.setMiddleDay - 1 + ); return _date; }; -export const epochWNToDate = (epochWeekNumber: number)=>dayToWeekCommonDay(weekNumberToMiddleDate(epochWeekNumber)); +export const epochWNToDate = (epochWeekNumber: number) => + dayToWeekCommonDay(weekNumberToMiddleDate(epochWeekNumber)); -export const epochWNToPronoteWN = (epochWeekNumber: number, account: PronoteAccount) => - translateToWeekNumber(epochWNToDate(epochWeekNumber), account.instance?.instance.firstMonday || pronoteFirstDate) || 1; +export const epochWNToPronoteWN = ( + epochWeekNumber: number, + account: PronoteAccount +) => + translateToWeekNumber( + epochWNToDate(epochWeekNumber), + account.instance?.instance.firstMonday || pronoteFirstDate + ) || 1; -/** - * Convert a date to a week number. - */ export const dateToEpochWeekNumber = (date: Date): number => { const commonDay = dayToWeekCommonDay(date); - const epochWeekNumber = Math.floor((commonDay.getTime() + EPOCH_WN_CONFIG.adjustEpochInitialDate - ( (EPOCH_WN_CONFIG.setMiddleDay - 1) /7 ) * EPOCH_WN_CONFIG.numberOfMsInAWeek) / EPOCH_WN_CONFIG.numberOfMsInAWeek); - // this is the opposite of the weekNumberToMiddleDate function - return epochWeekNumber; + return Math.floor( + (commonDay.getTime() + EPOCH_WN_CONFIG.adjustEpochInitialDate - 172800000) / + EPOCH_WN_CONFIG.numberOfMsInAWeek + ); }; -/** - * Check if the test date is in the same week as the reference date. - * If we need to check if the test date is "near" our reference date, we can use the numberOfWeeksBefore and numberOfWeeksAfter parameters. - * So if I want to check if the test date is in the same week or the next week, I can call isInTheWeek(referenceDate, testDate, 0, 1). - */ -export const isInTheWeek = (referenceDate: Date, testDate: Date, numberOfWeeksBefore = 0, numberOfWeeksAfter = 0): boolean => { +export const isInTheWeek = ( + referenceDate: Date, + testDate: Date, + numberOfWeeksBefore = 0, + numberOfWeeksAfter = 0 +): boolean => { const referenceWeek = dateToEpochWeekNumber(referenceDate); const testWeek = dateToEpochWeekNumber(testDate); - return testWeek >= referenceWeek - numberOfWeeksBefore && testWeek <= referenceWeek + numberOfWeeksAfter; -}; - -export const weekNumberToDateRange = (epochWeekNumber: number, numberOfWeeksBefore = 0, numberOfWeeksAfter = 0): { start: Date; end: Date } => { - const start = new Date( - epochWeekNumber * EPOCH_WN_CONFIG.numberOfMsInAWeek - - EPOCH_WN_CONFIG.adjustEpochInitialDate - - numberOfWeeksBefore * EPOCH_WN_CONFIG.numberOfMsInAWeek - ); - const end = new Date( - epochWeekNumber * EPOCH_WN_CONFIG.numberOfMsInAWeek - + ( 6/7 ) * EPOCH_WN_CONFIG.numberOfMsInAWeek // 6/7 is to have the end of the week, aka Sunday (we are in Europe so we dont need to worry if we want to include the Sunday) - - EPOCH_WN_CONFIG.adjustEpochInitialDate - + numberOfWeeksAfter * EPOCH_WN_CONFIG.numberOfMsInAWeek + return ( + testWeek >= referenceWeek - numberOfWeeksBefore && + testWeek <= referenceWeek + numberOfWeeksAfter ); - return { start, end }; }; +export const weekNumberToDateRange = ( + epochWeekNumber: number, + numberOfWeeksBefore = 0, + numberOfWeeksAfter = 0 +) => { + const baseTime = + epochWeekNumber * EPOCH_WN_CONFIG.numberOfMsInAWeek - + EPOCH_WN_CONFIG.adjustEpochInitialDate; + return { + start: new Date( + baseTime - numberOfWeeksBefore * EPOCH_WN_CONFIG.numberOfMsInAWeek + ), + end: new Date( + baseTime + + 518400000 + + numberOfWeeksAfter * EPOCH_WN_CONFIG.numberOfMsInAWeek + ), + }; +}; export const weekNumberToDaysList = (epochWeekNumber: number): Date[] => { + const baseTime = + epochWeekNumber * EPOCH_WN_CONFIG.numberOfMsInAWeek - + EPOCH_WN_CONFIG.adjustEpochInitialDate; const weekdays = []; for (let i = 0; i < 7; i++) { - weekdays.push(new Date( - epochWeekNumber * EPOCH_WN_CONFIG.numberOfMsInAWeek + (i/7) - * EPOCH_WN_CONFIG.numberOfMsInAWeek - - EPOCH_WN_CONFIG.adjustEpochInitialDate - )); + weekdays.push(new Date(baseTime + i * 86400000)); } return weekdays; }; export const weekNumberToMiddleDate = (epochWeekNumber: number): Date => { - const date = new Date( - epochWeekNumber * EPOCH_WN_CONFIG.numberOfMsInAWeek - + ( (EPOCH_WN_CONFIG.setMiddleDay - 1) /7 ) * EPOCH_WN_CONFIG.numberOfMsInAWeek // (setMiddleDay-1)/7 is to have the middle of the week, aka Wednesday - - EPOCH_WN_CONFIG.adjustEpochInitialDate + return new Date( + epochWeekNumber * EPOCH_WN_CONFIG.numberOfMsInAWeek + + 172800000 - + EPOCH_WN_CONFIG.adjustEpochInitialDate ); - return date; }; -export const epochWMToCalendarWeekNumber = (epochWeekNumber: number): number => { +export const epochWMToCalendarWeekNumber = ( + epochWeekNumber: number +): number => { const date = weekNumberToMiddleDate(epochWeekNumber); - // Set Day to Sunday and make it the 7th day of the week - date.setUTCDate(date.getUTCDate() + 4 - (date.getUTCDay()||7)); - // Get first day of year - var yearStart = new Date(Date.UTC(date.getUTCFullYear(),0,1)); - // Calculate full weeks to nearest Thursday - var weekNo = Math.ceil(( ( (date.getTime() - yearStart.getTime()) / 86400000) + 1)/7); - // Return array of year and week number - return weekNo; + date.setUTCDate(date.getUTCDate() + 4 - (date.getUTCDay() || 7)); + const yearStart = new Date(Date.UTC(date.getUTCFullYear(), 0, 1)); + return Math.ceil(((date.getTime() - yearStart.getTime()) / 86400000 + 1) / 7); }; diff --git a/src/utils/format/format_cours_name.ts b/src/utils/format/format_cours_name.ts index c6f8809f0..195056e17 100644 --- a/src/utils/format/format_cours_name.ts +++ b/src/utils/format/format_cours_name.ts @@ -1,86 +1,130 @@ +const lessonFormatsLookup: Record = {}; const lesson_formats = require("../data/lesson_formats.json"); -const uppercaseFirst = (txt: string): string => { - let newTxt = txt.charAt(0).toUpperCase() + txt.slice(1); - return newTxt; -}; - -function removeSpaces (text: string): string { - return text.replace(/\s+/g, ""); +// Build a lookup table once at module load time +for (const item of lesson_formats) { + for (const format of item.formats.default) { + const key = format.toLowerCase(); + if (!lessonFormatsLookup[key]) { + lessonFormatsLookup[key] = item; + } + } } -function findObjectByPronoteString (pronoteString = "") { - // Process the input string: replace dots and underscores with spaces, trim, and convert to lowercase - let processedString = pronoteString - .replace(/[,._]/g, " ") - .trim() - .toLowerCase(); - - // remove everything after > (included) - processedString = processedString.split(">")[0].trim(); +const processedStringCache: Record = {}; - // remove LV1, LV2, etc. - processedString = processedString.replace(/lv\d/g, "").trim(); +const regexPatterns = { + specialChars: /[^a-zA-Z0-9 ]/g, + spaces: /\s+/g, + parentheses: /\(.*\)/g, + lvPattern: /lv\d/gi, + splitMarker: />/, + accentNormalization: /[\u0300-\u036f]/g, + punctuation: /[,._]/g, +}; - // remove everything in parentheses - processedString = processedString.replace(/\(.*\)/g, "").trim(); +const upperCaseTerms = new Set([ + "CM", + "TD", + "TP", + "LV1", + "LV2", + "LV3", + "LVA", + "LVB", + "LVC", + "SAE", + "PPP", +]); + +const uppercaseFirst = (txt: string): string => + txt.charAt(0).toUpperCase() + txt.slice(1); + +const removeSpaces = (text: string): string => + text.replace(regexPatterns.spaces, ""); + +function processInputString (pronoteString: string): string { + // Check cache first + const cacheKey = `proc_${pronoteString}`; + if (processedStringCache[cacheKey]) { + return processedStringCache[cacheKey]; + } - // normalize accents - processedString = processedString + // Processing pipeline + let result = pronoteString + .replace(regexPatterns.punctuation, " ") + .trim() + .toLowerCase() + .split(regexPatterns.splitMarker)[0] + .trim() + .replace(regexPatterns.lvPattern, "") + .trim() + .replace(regexPatterns.parentheses, "") + .trim() .normalize("NFD") - .replace(/[\u0300-\u036f]/g, ""); + .replace(regexPatterns.accentNormalization, "") + .replace(regexPatterns.specialChars, " ") + .trim() + .replace(regexPatterns.spaces, " "); - // remove special characters - processedString = processedString.replace(/[^a-zA-Z0-9 ]/g, " ").trim(); + // Cache the result + processedStringCache[cacheKey] = result; + return result; +} - // remove multiple spaces into one - processedString = processedString.replace(/\s+/g, " "); +function findObjectByPronoteString (pronoteString = "") { + const processedString = processInputString(pronoteString); - // Search for the object in the data - for (let item of lesson_formats) { - for (let format of item.formats.default) { - if (format.toLowerCase() === processedString) { - return item; - } - } + // Direct lookup from pre-built index + const foundItem = lessonFormatsLookup[processedString]; + if (foundItem) { + return foundItem; } - // Return a new object if no match is found return { label: removeSpaces(processedString), pretty: formatPretty(processedString), - formats: {} + formats: {}, }; } function formatPretty (text: string): string { - const upperCaseTerms = ["CM", "TD", "TP", "LV1", "LV2", "LV3", "LVA", "LVB", "LVC", "SAE", "PPP"]; - let words = text.split(" "); - words = words.map(word => { - if (upperCaseTerms.includes(word.toUpperCase())) { - return word.toUpperCase(); + const words = text.split(" "); + for (let i = 0; i < words.length; i++) { + const word = words[i]; + if (upperCaseTerms.has(word.toUpperCase())) { + words[i] = word.toUpperCase(); + } else { + words[i] = uppercaseFirst(word); } - return uppercaseFirst(word); - }); + } return words.join(" "); } function getCourseSpeciality (pronoteString = ""): string | null { - if (!pronoteString.includes(">")) { - return null; - } + if (!pronoteString.includes(">")) return null; + + const parts = pronoteString.split(regexPatterns.splitMarker); + if (parts.length < 2) return null; - let newPrnString = pronoteString.split(">").pop()?.trim().toLowerCase(); + let newPrnString = parts[parts.length - 1].trim().toLowerCase(); if (!newPrnString) return null; - newPrnString = newPrnString.replace("expression", "expr."); - newPrnString = newPrnString.replace("compréhension", "comp."); - newPrnString = newPrnString.replace("ecrit", "écrit"); - newPrnString = newPrnString.replace("sae", "saé"); - newPrnString = formatPretty(newPrnString); + if (newPrnString.includes("expression")) { + newPrnString = newPrnString.replace("expression", "expr."); + } + if (newPrnString.includes("compréhension")) { + newPrnString = newPrnString.replace("compréhension", "comp."); + } + if (newPrnString.includes("ecrit")) { + newPrnString = newPrnString.replace("ecrit", "écrit"); + } + if (newPrnString.includes("sae")) { + newPrnString = newPrnString.replace("sae", "saé"); + } - return newPrnString; + return formatPretty(newPrnString); } export default findObjectByPronoteString; -export { getCourseSpeciality }; \ No newline at end of file +export { getCourseSpeciality }; diff --git a/src/utils/format/format_pronote_homeworks.ts b/src/utils/format/format_pronote_homeworks.ts index 859cc1de8..fc9feb24c 100644 --- a/src/utils/format/format_pronote_homeworks.ts +++ b/src/utils/format/format_pronote_homeworks.ts @@ -4,43 +4,61 @@ type SupportedTags = { [key: string]: string | ((text: string) => string); }; -function parse_homeworks (content: string): string { - const supportedTags: SupportedTags = { - br: "\n", - strong: (text: string) => `**${text}**`, - sub: (text: string) => `_{${text}}`, - sup: (text: string) => `^{${text}}`, - }; - - const ignoredTags: Set = new Set(["div", "span", "style", "script", "footer", "header"]); - - const tagRegex = /<\/?([a-zA-Z]+)(?:\s[^>]*)?>|([^<]+)/gm; - - const htmlEntities: Record = { - " ": " ", - """: "\"", - "'": "'", - ...latexVocabulary - }; - - // Décodage des entités HTML - function decodeHtmlEntities (text: string): string { - return text - .replace(/&#(\d+);/g, (_, dec) => String.fromCharCode(parseInt(dec, 10))) - .replace(/&([a-zA-Z]+);/g, (_, entity) => htmlEntities[entity] || `&${entity};`) - .replace(/[\u2200-\u22FF\u03B1-\u03C9]/g, match => latexVocabulary[match] || match); - } +const SUPPORTED_TAGS: SupportedTags = { + br: "\n", + strong: (text: string) => `**${text}**`, + sub: (text: string) => `_{${text}}`, + sup: (text: string) => `^{${text}}`, +}; + +const IGNORED_TAGS = new Set([ + "div", + "span", + "style", + "script", + "footer", + "header", +]); + +const TAG_REGEX = /<\/?([a-zA-Z]+)(?:\s[^>]*)?>|([^<]+)/g; +const NUMERIC_ENTITY_REGEX = /&#(\d+);/g; +const NAMED_ENTITY_REGEX = /&([a-zA-Z]+);/g; +const UNICODE_SYMBOLS_REGEX = /[\u2200-\u22FF\u03B1-\u03C9]/g; +const MULTI_NEWLINE_REGEX = /\n{2,}/g; + +const HTML_ENTITIES: Record = { + " ": " ", + """: "\"", + "'": "'", + ...latexVocabulary, +}; + +const DECODE_CACHE = new Map(); +function parse_homeworks (content: string): string { let result = ""; const stack: Array<{ tag: string; children: string[] }> = []; + let lastIndex = 0; - let match: RegExpExecArray | null; - while ((match = tagRegex.exec(content)) !== null) { - const [fullMatch, tagName, text] = match; + content.replace(TAG_REGEX, (fullMatch, tagName, text, offset) => { + // Add any text between matches + if (offset > lastIndex) { + const betweenText = content.slice(lastIndex, offset); + if (stack.length > 0) { + stack[stack.length - 1].children.push(betweenText); + } else { + result += betweenText; + } + } + lastIndex = offset + fullMatch.length; if (text) { - // Texte brut entre balises - const decodedText = decodeHtmlEntities(text); + let decodedText = DECODE_CACHE.get(text); + if (!decodedText) { + decodedText = decodeHtmlEntities(text); + DECODE_CACHE.set(text, decodedText); + } + if (stack.length > 0) { stack[stack.length - 1].children.push(decodedText); } else { @@ -51,46 +69,64 @@ function parse_homeworks (content: string): string { const isClosingTag = fullMatch.startsWith(" 0 && stack[stack.length - 1].tag === tag) { const { tag: currentTag, children } = stack.pop()!; const processedContent = children.join(""); - if (supportedTags[currentTag]) { - if (typeof supportedTags[currentTag] === "string") { - result += supportedTags[currentTag]; - } else { - result += (supportedTags[currentTag] as (text: string) => string)(processedContent); - } + const handler = SUPPORTED_TAGS[currentTag]; + if (typeof handler === "string") { + result += handler; + } else if (handler) { + result += handler(processedContent); } } - } else { - // Balise ouvrante ou auto-fermante - if (ignoredTags.has(tag)) { - continue; // Ignore les balises non supportées - } - - if (supportedTags[tag]) { - if (typeof supportedTags[tag] === "string") { - result += supportedTags[tag]; + } else if (!IGNORED_TAGS.has(tag)) { + // Process opening tag + const handler = SUPPORTED_TAGS[tag]; + if (handler) { + if (typeof handler === "string") { + result += handler; } else { stack.push({ tag, children: [] }); } } } } + + return ""; + }); + + if (lastIndex < content.length) { + const remainingText = content.slice(lastIndex); + if (stack.length > 0) { + stack[stack.length - 1].children.push(remainingText); + } else { + result += remainingText; + } } - // Traite les balises non fermées restantes while (stack.length > 0) { const { tag, children } = stack.pop()!; - if (supportedTags[tag] && typeof supportedTags[tag] === "function") { - result += (supportedTags[tag] as (text: string) => string)(children.join("")); + const handler = SUPPORTED_TAGS[tag]; + if (typeof handler === "function") { + result += handler(children.join("")); } } - // Nettoyage des sauts de ligne en excès - return result.replace(/\n{2,}/g, "").trim(); + return result.replace(MULTI_NEWLINE_REGEX, "").trim(); +} + +function decodeHtmlEntities (text: string): string { + return text + .replace(NUMERIC_ENTITY_REGEX, (_, dec) => + String.fromCharCode(parseInt(dec, 10)) + ) + .replace( + NAMED_ENTITY_REGEX, + (_, entity) => HTML_ENTITIES[`&${entity};`] || `&${entity};` + ) + .replace(UNICODE_SYMBOLS_REGEX, (match) => HTML_ENTITIES[match] || match); } export default parse_homeworks; diff --git a/src/utils/grades/getAverages.ts b/src/utils/grades/getAverages.ts index 353f446e1..5ea7019a5 100644 --- a/src/utils/grades/getAverages.ts +++ b/src/utils/grades/getAverages.ts @@ -1,125 +1,112 @@ -// On importe le type `Grade` depuis le chemin spécifié import type { Grade } from "@/services/shared/Grade"; -// Définition de l'interface `GradeHistory` pour représenter l'historique des notes avec une valeur numérique et une date export interface GradeHistory { - value: number; // La valeur de la note - date: string; // La date à laquelle la note a été enregistrée + value: number; + date: string; } -// Définition du type `Target` qui indique quel type de moyenne ou note cibler type Target = "student" | "average" | "min" | "max"; -// Définition du type `AverageDiffGrade` pour calculer la différence entre les moyennes avec et sans certaines notes export type AverageDiffGrade = { - difference?: number; // La différence de moyenne entre deux ensembles de notes - with: number; // La moyenne avec toutes les notes - without: number; // La moyenne sans certaines notes + difference?: number; + with: number; + without: number; +}; + +// Memoization cache for subject averages +const subjectAverageCache = new Map(); + +const getCacheKey = (subject: Grade[], target: Target, useMath: boolean, loop: boolean): string => { + return `${target}-${useMath}-${loop}-${subject.map(g => g.subjectId ?? g.subjectName).join(",")}`; }; -// Fonction pour calculer la moyenne des notes globales par matière, en fonction de la cible (par défaut, "student") const getPronoteAverage = ( grades: Grade[], target: Target = "student", useMath: boolean = false ): number => { - try { - // Si aucune note n'est fournie ou que la liste est vide, on retourne -1 - if (!grades || grades.length === 0) return -1; - - // Grouper les notes par matière - const groupedBySubject = grades.reduce( - (acc: Record, grade) => { - (acc[grade.subjectId ?? grade.subjectName] ||= []).push(grade); // Ajouter la note à la liste des notes pour la matière correspondante - return acc; - }, - {} - ); - - let countedSubjects = 0; - - // Calculer la moyenne totale de toutes les matières - const totalAverage = Object.values(groupedBySubject).reduce( - (acc, subjectGrades) => { - const nAvg = getSubjectAverage(subjectGrades, target, useMath); - - if (nAvg !== -1) { - countedSubjects++; - } else { - return acc; - } + if (!grades?.length) return -1; + + const groupedBySubject: Record = {}; + let countedSubjects = 0; + let totalAverage = 0; + + // Optimized grouping + for (let i = 0; i < grades.length; i++) { + const grade = grades[i]; + const key = grade.subjectId ?? grade.subjectName; + groupedBySubject[key] = groupedBySubject[key] || []; + groupedBySubject[key].push(grade); + } - return acc + nAvg; - }, - 0 - ); + const subjects = Object.values(groupedBySubject); - // Retourner la moyenne globale en divisant par le nombre de matières - return totalAverage / countedSubjects; - } catch (e) { - return -1; + for (let i = 0; i < subjects.length; i++) { + const nAvg = getSubjectAverage(subjects[i], target, useMath); + if (nAvg !== -1) { + countedSubjects++; + totalAverage += nAvg; + } } + + return countedSubjects > 0 ? totalAverage / countedSubjects : -1; }; -// Fonction pour calculer la moyenne d'une matière spécifique, selon la cible choisie export const getSubjectAverage = ( subject: Grade[], target: Target = "student", useMath: boolean = false, loop: boolean = false ): number => { - try { - let calcGradesSum = 0; // Somme cumulée des notes pondérées - let calcOutOfSum = 0; // Somme cumulée des coefficients pondérés - let countedGrades = 0; - - for (const grade of subject) { - const targetGrade = grade[target]; - - if ( - !targetGrade || - targetGrade.disabled || - targetGrade.value === null || - targetGrade.value < 0 || - grade.coefficient === 0 || - typeof targetGrade.value !== "number" - ) { - continue; - } - - const coefficient = grade.coefficient; - const outOfValue = grade.outOf.value!; + const cacheKey = getCacheKey(subject, target, useMath, loop); + const cachedValue = subjectAverageCache.get(cacheKey); + if (cachedValue !== undefined) return cachedValue; + + let calcGradesSum = 0; + let calcOutOfSum = 0; + let countedGrades = 0; + + for (let i = 0; i < subject.length; i++) { + const grade = subject[i]; + const targetGrade = grade[target]; + + if ( + !targetGrade || + targetGrade.disabled || + targetGrade.value === null || + targetGrade.value < 0 || + grade.coefficient === 0 || + typeof targetGrade.value !== "number" + ) { + continue; + } - if (grade.isOptional && !loop) { - const avgWithout = getSubjectAverage( - subject.filter((g) => JSON.stringify(g) !== JSON.stringify(grade)), - target, - useMath, - true - ); + const coefficient = grade.coefficient; + const outOfValue = grade.outOf.value!; - const avgWith = getSubjectAverage(subject, target, useMath, true); + if (grade.isOptional && !loop) { + const filteredSubject = subject.filter((g, idx) => idx !== i); + const avgWithout = getSubjectAverage(filteredSubject, target, useMath, true); + const avgWith = getSubjectAverage(subject, target, useMath, true); - if (avgWithout > avgWith) { - continue; - } + if (avgWithout > avgWith) { + continue; } + } - if (grade.isBonus) { - const averageMoy = outOfValue / 2; - const newGradeValue = targetGrade.value - averageMoy; - - if (newGradeValue < 0) continue; + if (grade.isBonus) { + const averageMoy = outOfValue / 2; + const newGradeValue = targetGrade.value - averageMoy; + if (newGradeValue >= 0) { calcGradesSum += newGradeValue; calcOutOfSum += 1; - } else if (useMath) { - calcGradesSum += targetGrade.value * coefficient; - } else if ( - targetGrade.value > 20 || - (coefficient < 1 && outOfValue - 20 >= -5) || - outOfValue > 20 - ) { + } + } else if (useMath) { + calcGradesSum += targetGrade.value * coefficient; + countedGrades += coefficient; + } else { + if (targetGrade.value > 20 || (coefficient < 1 && outOfValue - 20 >= -5) || outOfValue > 20) { const gradeOn20 = (targetGrade.value / outOfValue) * 20; calcGradesSum += gradeOn20 * coefficient; calcOutOfSum += 20 * coefficient; @@ -127,24 +114,22 @@ export const getSubjectAverage = ( calcGradesSum += targetGrade.value * coefficient; calcOutOfSum += outOfValue * coefficient; } - - countedGrades += useMath ? 1 * coefficient : 1; - } - - if (useMath) { - return calcGradesSum / countedGrades; + countedGrades++; } + } - if (calcOutOfSum === 0) return -1; - - const subjectAverage = Math.min((calcGradesSum / calcOutOfSum) * 20, 20); - return isNaN(subjectAverage) ? -1 : subjectAverage; - } catch (e) { - return -1; + let result = -1; + if (useMath) { + result = countedGrades > 0 ? calcGradesSum / countedGrades : -1; + } else if (calcOutOfSum > 0) { + result = Math.min((calcGradesSum / calcOutOfSum) * 20, 20); + result = isNaN(result) ? -1 : result; } + + subjectAverageCache.set(cacheKey, result); + return result; }; -// Fonction pour calculer la différence de moyenne avec et sans certaines notes const getAverageDiffGrade = ( grades: Grade[], list: Grade[], @@ -152,24 +137,19 @@ const getAverageDiffGrade = ( useMath: boolean = false ): AverageDiffGrade => { try { - const baseAverage = getSubjectAverage(list, target); // Calculer la moyenne de base avec toutes les notes - const baseWithoutGradeAverage = getSubjectAverage( - list.filter( - (grade) => - !grades.some( - (g) => - g.student.value === grade.student.value && - g.coefficient === grade.coefficient - ) - ), - target, - useMath - ); // Calculer la moyenne sans toutes les notes de `grades` + const baseAverage = getSubjectAverage(list, target); + const filteredList = list.filter(grade => + !grades.some(g => + g.student.value === grade.student.value && + g.coefficient === grade.coefficient + ) + ); + const baseWithoutGradeAverage = getSubjectAverage(filteredList, target, useMath); return { - difference: baseWithoutGradeAverage - baseAverage, // Calculer la différence entre les deux moyennes - with: baseAverage, // Moyenne avec toutes les notes - without: baseWithoutGradeAverage, // Moyenne sans certaines notes + difference: baseWithoutGradeAverage - baseAverage, + with: baseAverage, + without: baseWithoutGradeAverage, }; } catch (e) { return { @@ -180,35 +160,49 @@ const getAverageDiffGrade = ( } }; -// Fonction pour générer un historique des moyennes au fil du temps const getAveragesHistory = ( grades: Grade[], target: Target = "student", final?: number, useMath: boolean = false ): GradeHistory[] => { - try { - // Générer l'historique des moyennes jusqu'à la date de chaque note - const history = grades.map((grade, index) => ({ - value: getPronoteAverage(grades.slice(0, index + 1), target), // Moyenne jusqu'à ce point - date: new Date(grade.timestamp).toISOString(), // Date de la note au format ISO - })); - - // Trier l'historique par date - history.sort((a, b) => a.date.localeCompare(b.date)); - - // Ajouter un point final avec la moyenne finale (ou calculée) - history.push({ - value: final ?? getPronoteAverage(grades, target, useMath), // Moyenne finale ou calculée - date: new Date().toISOString(), // Date actuelle - }); + if (!grades?.length) return []; + + const history: GradeHistory[] = []; + let cumulativeGrades: Grade[] = []; + let lastDate = ""; + + // Pre-allocate array + history.length = grades.length; + + for (let i = 0; i < grades.length; i++) { + cumulativeGrades.push(grades[i]); + const currentDate = new Date(grades[i].timestamp).toISOString(); + + // Only add to history if date changed to reduce calculations + if (currentDate !== lastDate) { + history[i] = { + value: getPronoteAverage(cumulativeGrades, target), + date: currentDate + }; + lastDate = currentDate; + } + } - // remove NaN values - return history.filter((x) => !isNaN(x.value) || x.value !== -1); - } catch (e) { - return []; + // Filter out undefined entries and sort + const filteredHistory = history.filter(Boolean); + filteredHistory.sort((a, b) => a.date.localeCompare(b.date)); + + // Add final value + const finalValue = final ?? getPronoteAverage(grades, target, useMath); + if (!isNaN(finalValue)) { + filteredHistory.push({ + value: finalValue, + date: new Date().toISOString() + }); } + + return filteredHistory; }; -// Exportation des fonctions pour utilisation externe -export { getPronoteAverage, getAverageDiffGrade, getAveragesHistory }; +export { getPronoteAverage, getAverageDiffGrade, getAveragesHistory }; \ No newline at end of file diff --git a/src/utils/ui/animations.ts b/src/utils/ui/animations.ts index 7dbef7d12..f88df57c9 100644 --- a/src/utils/ui/animations.ts +++ b/src/utils/ui/animations.ts @@ -1,81 +1,73 @@ import { Easing, withTiming } from "react-native-reanimated"; -const animPapillon = (animation: any) => { - if (!animation) return; - - return animation.springify().mass(1).damping(20).stiffness(300); +const SPRING_CONFIG = { mass: 1, damping: 20, stiffness: 300 }; +const TIMING_CONFIG = { duration: 300, easing: Easing.bezier(0.3, 0.3, 0, 1) }; +const ENTER_CONFIG = { + duration: 180, + scaleX: 0.8, + scaleY: 0.65, + easing: Easing.bezier(0.3, 0.3, 0, 1), }; - -const anim2Papillon = (animation: any) => { - if (!animation) return; - - return animation.duration(300).easing(Easing.bezier(0.3, 0.3, 0, 1)); +const EXIT_CONFIG = { + duration: 120, + scaleX: 0.9, + scaleY: 0.7, + easing: Easing.bezier(0.3, 0.3, 0, 1), }; -const EnteringDuration = 180; -const EnteringScaleX = 0.8; -const EnteringScaleY = 0.65; +const animPapillon = (a: any) => + a + ?.springify() + .mass(SPRING_CONFIG.mass) + .damping(SPRING_CONFIG.damping) + .stiffness(SPRING_CONFIG.stiffness); -const ExitingDuration = 120; -const ExitingScaleX = 0.9; -const ExitingScaleY = 0.7; +const anim2Papillon = (a: any) => + a?.duration(TIMING_CONFIG.duration).easing(TIMING_CONFIG.easing); -// Paramètres d'animation pour l'entrée du menu contextuel -const PapillonAnimSettings = { - duration: EnteringDuration, - easing: Easing.bezier(0.3, 0.3, 0, 1), +const ENTER_TIMING = { + duration: ENTER_CONFIG.duration, + easing: ENTER_CONFIG.easing, }; - -// Paramètres d'animation pour la sortie du menu contextuel -const PapillonAnimSettingsExit = { - duration: ExitingDuration, - easing: Easing.bezier(0.3, 0.3, 0, 1), +const EXIT_TIMING = { + duration: EXIT_CONFIG.duration, + easing: EXIT_CONFIG.easing, }; -// Fonction d'animation pour l'entrée du menu contextuel const PapillonContextEnter = () => { "worklet"; - const animations = { - opacity: withTiming(1, PapillonAnimSettings), - transform: [ - { - scaleX: withTiming(1, PapillonAnimSettings), - }, - { - scaleY: withTiming(1, PapillonAnimSettings), - }, - ], - }; - const initialValues = { - opacity: 0, - transform: [ - { scaleX: EnteringScaleX}, - { scaleY: EnteringScaleY }, - ], - }; return { - initialValues, - animations, + initialValues: { + opacity: 0, + transform: [ + { scaleX: ENTER_CONFIG.scaleX }, + { scaleY: ENTER_CONFIG.scaleY }, + ], + }, + animations: { + opacity: withTiming(1, ENTER_TIMING), + transform: [ + { scaleX: withTiming(1, ENTER_TIMING) }, + { scaleY: withTiming(1, ENTER_TIMING) }, + ], + }, }; }; -// Fonction d'animation pour la sortie du menu contextuel const PapillonContextExit = () => { "worklet"; - const animations = { - opacity: withTiming(0, PapillonAnimSettingsExit), - transform: [ - { scaleX: withTiming(ExitingScaleX, PapillonAnimSettingsExit) }, - { scaleY: withTiming(ExitingScaleY, PapillonAnimSettingsExit) }, - ], - }; - const initialValues = { - opacity: 1, - transform: [{ scaleX: 1 }, { scaleY: 1 }], - }; return { - initialValues, - animations, + initialValues: { + opacity: 1, + transform: [{ scaleX: 1 }, { scaleY: 1 }], + }, + animations: { + opacity: withTiming(0, EXIT_TIMING), + transform: [ + { scaleX: withTiming(EXIT_CONFIG.scaleX, EXIT_TIMING) }, + { scaleY: withTiming(EXIT_CONFIG.scaleY, EXIT_TIMING) }, + ], + }, }; }; @@ -84,4 +76,4 @@ export { anim2Papillon, PapillonContextEnter, PapillonContextExit, -}; \ No newline at end of file +}; From 53c289f61c585bb8e90cf1b4ae7600c166719eaa Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 31 Mar 2025 18:58:42 +0200 Subject: [PATCH 1104/1144] =?UTF-8?q?feat(theme):=20introduire=20un=20hook?= =?UTF-8?q?=20personnalis=C3=A9=20pour=20la=20gestion=20du=20th=C3=A8me=20?= =?UTF-8?q?Papillon?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Addons/AddonsWebview.tsx | 2 +- src/components/FirstInstallation/ButtonCta.tsx | 2 +- src/components/FirstInstallation/DuoListPressable.tsx | 2 +- src/components/FirstInstallation/MaskStars.tsx | 2 +- src/components/FirstInstallation/MaskStarsColored.tsx | 2 +- src/components/FirstInstallation/PapillonShineBubble.tsx | 2 +- src/components/FirstInstallation/ResponsiveTextInput.tsx | 2 +- src/components/Global/AccountItem.tsx | 2 +- src/components/Global/LinkFavicon.tsx | 2 +- src/components/Global/NativeComponents.tsx | 2 +- src/components/Global/PapillonAvatar.tsx | 2 +- src/components/Global/PapillonCheckbox.tsx | 2 +- src/components/Global/PapillonLoading.tsx | 2 +- src/components/Global/PapillonModernHeader.tsx | 2 +- src/components/Global/PapillonPicker.tsx | 2 +- src/components/Grades/GradeModal.tsx | 2 +- src/components/Home/AccountSwitcher.tsx | 2 +- src/components/Home/AccountSwitcherContextMenu.tsx | 2 +- src/components/Home/Header.tsx | 2 +- src/components/Home/HomeOnboard.tsx | 2 +- src/components/Home/Widget.tsx | 2 +- src/components/Home/WidgetHeader.tsx | 2 +- src/components/Modals/ModalHandle.tsx | 2 +- src/components/Modals/PapillonBottomSheet.tsx | 2 +- src/components/Restaurant/ButtonList.tsx | 2 +- src/components/Restaurant/RestaurantCard.tsx | 2 +- src/components/Settings/AccountContainerCard.tsx | 2 +- src/components/Settings/ReelGallery.tsx | 2 +- src/components/Templates/LoginView.tsx | 2 +- src/providers/AlertProvider.tsx | 2 +- src/router/navigator/atoms/MenuItem.tsx | 2 +- src/router/navigator/atoms/TabItem.tsx | 2 +- src/utils/ui/theme.ts | 7 +++++++ src/views/account/Attendance/Atoms/AttendanceItem.tsx | 2 +- src/views/account/Attendance/Atoms/TotalMissed.tsx | 2 +- src/views/account/Attendance/Attendance.tsx | 2 +- src/views/account/Chat/Messages.tsx | 2 +- src/views/account/Chat/Modals/Chat.tsx | 2 +- src/views/account/Chat/Modals/ChatCreate.tsx | 2 +- src/views/account/Chat/Modals/ChatDetails.tsx | 2 +- src/views/account/Evaluation/Document.tsx | 2 +- src/views/account/Evaluation/Evaluation.tsx | 2 +- src/views/account/Grades/Atoms/GradeTitle.tsx | 2 +- src/views/account/Grades/Atoms/GradesScodocUE.tsx | 2 +- src/views/account/Grades/Document.tsx | 2 +- src/views/account/Grades/Grades.tsx | 2 +- src/views/account/Grades/Graph/GradesAverage.tsx | 2 +- src/views/account/Grades/Latest/LatestGradesItem.tsx | 2 +- src/views/account/Grades/Modals/Subject.tsx | 2 +- src/views/account/Grades/Subject/Subject.tsx | 2 +- src/views/account/Grades/Subject/SubjectTitle.tsx | 2 +- src/views/account/Home/Elements/PopupRestauration.tsx | 2 +- src/views/account/Home/Modal/CustomizeHeader.tsx | 2 +- src/views/account/Home/ModalContent.tsx | 2 +- src/views/account/Home/PlaceholderScreen.tsx | 2 +- src/views/account/Homeworks/AddHomework.tsx | 2 +- src/views/account/Homeworks/Atoms/NoHomeworks.tsx | 2 +- src/views/account/Homeworks/Document.tsx | 2 +- src/views/account/Homeworks/Homeworks.old.tsx | 2 +- src/views/account/Homeworks/Homeworks.tsx | 2 +- src/views/account/Homeworks/HomeworksHeader.tsx | 2 +- src/views/account/Lessons/Atoms/Item.tsx | 2 +- src/views/account/Lessons/Atoms/LessonsDatePicker.tsx | 2 +- src/views/account/Lessons/Atoms/Loading.tsx | 2 +- src/views/account/Lessons/Atoms/NoCourse.tsx | 2 +- src/views/account/Lessons/Atoms/Page.tsx | 2 +- src/views/account/Lessons/Document.tsx | 2 +- src/views/account/Lessons/Lessons.tsx | 2 +- src/views/account/Lessons/LessonsHeader.tsx | 2 +- src/views/account/Lessons/Options/LessonsImportIcal.tsx | 2 +- src/views/account/News/Atoms/Item.tsx | 2 +- src/views/account/News/Document.tsx | 2 +- src/views/account/News/News.tsx | 2 +- src/views/account/Restaurant/Cards/Card.tsx | 2 +- src/views/account/Restaurant/Menu.tsx | 2 +- src/views/account/Restaurant/Modals/CardDetail.tsx | 2 +- src/views/account/Restaurant/Modals/PaymentSuccess.tsx | 2 +- src/views/account/Restaurant/Modals/QrCode.tsx | 2 +- src/views/account/Week/Week.tsx | 2 +- .../IdentityProvider/actions/BackgroundIUTLannion.tsx | 2 +- src/views/login/IdentityProvider/providers/UnivLimoges.tsx | 2 +- src/views/login/IdentityProvider/providers/UnivRennes1.tsx | 2 +- src/views/login/IdentityProvider/providers/UnivRennes2.tsx | 2 +- .../IdentityProvider/providers/UnivSorbonneParisNord.tsx | 2 +- src/views/login/ServiceSelector.tsx | 2 +- src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx | 2 +- src/views/login/pronote/Pronote2FA_Auth.tsx | 2 +- src/views/login/pronote/PronoteAuthenticationSelector.tsx | 2 +- src/views/login/pronote/PronoteGeolocation.tsx | 2 +- src/views/login/pronote/PronoteInstanceSelector.tsx | 2 +- src/views/login/pronote/PronoteManualLocation.tsx | 2 +- src/views/login/pronote/PronoteManualURL.tsx | 2 +- src/views/login/pronote/PronoteQRCode.tsx | 2 +- src/views/login/pronote/PronoteWebview.tsx | 2 +- .../login/skolengo/SkolengoAuthenticationSelector.tsx | 2 +- src/views/login/skolengo/SkolengoGeolocation.tsx | 2 +- src/views/login/skolengo/SkolengoInstanceSelector.tsx | 2 +- src/views/login/skolengo/SkolengoWebview.tsx | 2 +- src/views/settings/ExternalAccount/IzlyActivation.tsx | 2 +- src/views/settings/ExternalAccount/PriceBeforeScan.tsx | 2 +- .../settings/ExternalAccount/PriceDetectionOnboarding.tsx | 2 +- src/views/settings/ExternalAccount/PriceError.tsx | 2 +- src/views/settings/ExternalAccount/QrcodeScanner.tsx | 2 +- src/views/settings/ExternalAccount/SelectMethod.tsx | 2 +- src/views/settings/ExternalAccount/ServiceSelector.tsx | 2 +- .../settings/ExternalAccount/TurboselfAccountSelector.tsx | 2 +- src/views/settings/Settings.tsx | 2 +- src/views/settings/SettingsAbout.tsx | 2 +- src/views/settings/SettingsAccessibility.tsx | 2 +- src/views/settings/SettingsDevLogs.tsx | 2 +- src/views/settings/SettingsExternalServices.tsx | 2 +- src/views/settings/SettingsFlags.tsx | 2 +- src/views/settings/SettingsFlagsInfos.tsx | 2 +- src/views/settings/SettingsIcons.tsx | 2 +- src/views/settings/SettingsMagic.tsx | 2 +- src/views/settings/SettingsMultiService.tsx | 2 +- src/views/settings/SettingsMultiServiceSpace.tsx | 2 +- src/views/settings/SettingsNotifications.tsx | 2 +- src/views/settings/SettingsProfile.tsx | 2 +- src/views/settings/SettingsReactions.tsx | 2 +- src/views/settings/SettingsSubjects.tsx | 2 +- src/views/settings/SettingsSupport.tsx | 2 +- src/views/settings/SettingsTabs.tsx | 2 +- src/views/settings/SettingsTrophies.tsx | 2 +- src/views/welcome/ChangelogScreen.tsx | 2 +- src/views/welcome/ColorSelector.tsx | 2 +- src/views/welcome/DevMenu.tsx | 2 +- src/views/welcome/FirstInstallation.tsx | 2 +- src/views/welcome/ProfilePic.tsx | 2 +- src/widgets/Components/GeneralAverage.tsx | 2 +- src/widgets/Components/LastGrade.tsx | 2 +- src/widgets/Components/NextCourse.tsx | 2 +- src/widgets/Components/RestaurantBalance.tsx | 2 +- src/widgets/Components/RestaurantQRCode.tsx | 2 +- 134 files changed, 140 insertions(+), 133 deletions(-) create mode 100644 src/utils/ui/theme.ts diff --git a/src/components/Addons/AddonsWebview.tsx b/src/components/Addons/AddonsWebview.tsx index 7247ee302..06dc6b965 100644 --- a/src/components/Addons/AddonsWebview.tsx +++ b/src/components/Addons/AddonsWebview.tsx @@ -7,7 +7,7 @@ import * as FileSystem from "expo-file-system"; import { Asset } from "expo-asset"; import BottomSheet from "@/components/Modals/PapillonBottomSheet"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import Reanimated, { Easing, useSharedValue, withTiming } from "react-native-reanimated"; import type { NativeStackNavigationProp } from "@react-navigation/native-stack"; import type { RouteParameters } from "@/router/helpers/types"; diff --git a/src/components/FirstInstallation/ButtonCta.tsx b/src/components/FirstInstallation/ButtonCta.tsx index 0054a8ea4..5f0818430 100644 --- a/src/components/FirstInstallation/ButtonCta.tsx +++ b/src/components/FirstInstallation/ButtonCta.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from "react"; import { Text, Pressable, StyleSheet, type StyleProp, type ViewStyle } from "react-native"; import Reanimated, { Easing, useSharedValue, withTiming } from "react-native-reanimated"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import * as Haptics from "expo-haptics"; import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; diff --git a/src/components/FirstInstallation/DuoListPressable.tsx b/src/components/FirstInstallation/DuoListPressable.tsx index 8f537cf62..1cc2ed2ca 100644 --- a/src/components/FirstInstallation/DuoListPressable.tsx +++ b/src/components/FirstInstallation/DuoListPressable.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from "react"; import { View, Text, Pressable, StyleSheet } from "react-native"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import * as Haptics from "expo-haptics"; import Reanimated, { Easing, useSharedValue, withTiming } from "react-native-reanimated"; diff --git a/src/components/FirstInstallation/MaskStars.tsx b/src/components/FirstInstallation/MaskStars.tsx index e395e3a5f..17dd2e226 100644 --- a/src/components/FirstInstallation/MaskStars.tsx +++ b/src/components/FirstInstallation/MaskStars.tsx @@ -1,7 +1,7 @@ import React from "react"; import { Image, StyleSheet } from "react-native"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; const MaskStars: React.FC = () => { const theme = useTheme(); diff --git a/src/components/FirstInstallation/MaskStarsColored.tsx b/src/components/FirstInstallation/MaskStarsColored.tsx index fa087eea2..72ba16c0e 100644 --- a/src/components/FirstInstallation/MaskStarsColored.tsx +++ b/src/components/FirstInstallation/MaskStarsColored.tsx @@ -1,7 +1,7 @@ import React from "react"; import { Image, StyleSheet } from "react-native"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; const MaskStarsColored: React.FC<{ color: string }> = ({ color }) => { const theme = useTheme(); diff --git a/src/components/FirstInstallation/PapillonShineBubble.tsx b/src/components/FirstInstallation/PapillonShineBubble.tsx index 24e908b86..043551c41 100644 --- a/src/components/FirstInstallation/PapillonShineBubble.tsx +++ b/src/components/FirstInstallation/PapillonShineBubble.tsx @@ -1,5 +1,5 @@ import React, { useEffect } from "react"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import Svg, { G, Rect, Polygon } from "react-native-svg"; import { StyleSheet, Dimensions, View, Text, type DimensionValue, type StyleProp, type ViewStyle } from "react-native"; import Reanimated, { useSharedValue, withRepeat, withSpring, withSequence, withTiming, Easing } from "react-native-reanimated"; diff --git a/src/components/FirstInstallation/ResponsiveTextInput.tsx b/src/components/FirstInstallation/ResponsiveTextInput.tsx index 041da69a6..66b041f54 100644 --- a/src/components/FirstInstallation/ResponsiveTextInput.tsx +++ b/src/components/FirstInstallation/ResponsiveTextInput.tsx @@ -1,6 +1,6 @@ import React from "react"; import { TextInput, TextInputProps } from "react-native"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import useScreenDimensions from "@/hooks/useScreenDimensions"; const ResponsiveTextInput = React.forwardRef( diff --git a/src/components/Global/AccountItem.tsx b/src/components/Global/AccountItem.tsx index 06a027d4b..0fc70eb01 100644 --- a/src/components/Global/AccountItem.tsx +++ b/src/components/Global/AccountItem.tsx @@ -9,7 +9,7 @@ import {PrimaryAccount, AccountService} from "@/stores/account/types"; import {defaultProfilePicture} from "@/utils/ui/default-profile-picture"; import {Check} from "lucide-react-native"; import React from "react"; -import {useTheme} from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; const AccountItem: React.FC<{ account: PrimaryAccount, diff --git a/src/components/Global/LinkFavicon.tsx b/src/components/Global/LinkFavicon.tsx index 9bb429471..d863c1b96 100644 --- a/src/components/Global/LinkFavicon.tsx +++ b/src/components/Global/LinkFavicon.tsx @@ -2,7 +2,7 @@ import {Image, ImageStyle, StyleProp, View} from "react-native"; import * as FileSystem from "expo-file-system"; import { useEffect, useState } from "react"; import { Link2 } from "lucide-react-native"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; export const getURLDomain = (url: string, www: boolean = true) => { return url.replace("https://", "").replace("http://", "").split("/")[0].replace(www ? "www." : "", ""); diff --git a/src/components/Global/NativeComponents.tsx b/src/components/Global/NativeComponents.tsx index c1b7b2298..dda691a53 100644 --- a/src/components/Global/NativeComponents.tsx +++ b/src/components/Global/NativeComponents.tsx @@ -1,7 +1,7 @@ import React, { type ReactNode, isValidElement, Children, useMemo, memo } from "react"; import { View, Text, Pressable, StyleSheet, type StyleProp, type ViewStyle, type TextStyle, Platform, TouchableNativeFeedback } from "react-native"; import Reanimated, { type AnimatedProps, LayoutAnimation, LinearTransition } from "react-native-reanimated"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { ChevronRight } from "lucide-react-native"; import { animPapillon } from "@/utils/ui/animations"; import { LinearGradient } from "expo-linear-gradient"; diff --git a/src/components/Global/PapillonAvatar.tsx b/src/components/Global/PapillonAvatar.tsx index e111253ac..360432586 100644 --- a/src/components/Global/PapillonAvatar.tsx +++ b/src/components/Global/PapillonAvatar.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { Image, type ImageSourcePropType, type ImageStyle, type StyleProp, View } from "react-native"; const PapillonAvatar: React.FC<{ diff --git a/src/components/Global/PapillonCheckbox.tsx b/src/components/Global/PapillonCheckbox.tsx index 1ba2296b2..ff094ae64 100644 --- a/src/components/Global/PapillonCheckbox.tsx +++ b/src/components/Global/PapillonCheckbox.tsx @@ -1,6 +1,6 @@ import { type ViewStyle, type StyleProp } from "react-native"; import React, { useEffect, useRef, useState } from "react"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import Reanimated, { LinearTransition, ZoomIn, ZoomOut } from "react-native-reanimated"; import { PressableScale } from "react-native-pressable-scale"; diff --git a/src/components/Global/PapillonLoading.tsx b/src/components/Global/PapillonLoading.tsx index 05e374492..448a2e0dc 100644 --- a/src/components/Global/PapillonLoading.tsx +++ b/src/components/Global/PapillonLoading.tsx @@ -2,7 +2,7 @@ import React from "react"; import { View, StyleSheet } from "react-native"; import { NativeText } from "./NativeComponents"; import PapillonSpinner from "./PapillonSpinner"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; const PapillonLoading: React.FC<{ title: string; diff --git a/src/components/Global/PapillonModernHeader.tsx b/src/components/Global/PapillonModernHeader.tsx index d88188633..a51e65058 100644 --- a/src/components/Global/PapillonModernHeader.tsx +++ b/src/components/Global/PapillonModernHeader.tsx @@ -5,7 +5,7 @@ import { animPapillon } from "@/utils/ui/animations"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { PressableScale } from "react-native-pressable-scale"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { BlurView } from "expo-blur"; import { useOnlineStatus } from "@/hooks/useOnlineStatus"; diff --git a/src/components/Global/PapillonPicker.tsx b/src/components/Global/PapillonPicker.tsx index 2c763107f..af07defd3 100644 --- a/src/components/Global/PapillonPicker.tsx +++ b/src/components/Global/PapillonPicker.tsx @@ -2,7 +2,7 @@ import React, { useState } from "react"; import { View, Platform, StyleSheet, type StyleProp, type ViewStyle } from "react-native"; import { animPapillon, PapillonContextEnter, PapillonContextExit } from "@/utils/ui/animations"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { Pressable } from "react-native-gesture-handler"; import Reanimated, { LinearTransition, type AnimatedStyle } from "react-native-reanimated"; import { NativeText } from "./NativeComponents"; diff --git a/src/components/Grades/GradeModal.tsx b/src/components/Grades/GradeModal.tsx index 571e871c6..cf472eb0e 100644 --- a/src/components/Grades/GradeModal.tsx +++ b/src/components/Grades/GradeModal.tsx @@ -19,7 +19,7 @@ import { Reel } from "@/services/shared/Reel"; import { captureRef } from "react-native-view-shot"; import Animated, { Easing, FadeInRight, ZoomIn } from "react-native-reanimated"; import PapillonBottomSheet from "@/components/Modals/PapillonBottomSheet"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { useAlert } from "@/providers/AlertProvider"; import { isExpoGo } from "@/utils/native/expoGoAlert"; interface GradeModalProps { diff --git a/src/components/Home/AccountSwitcher.tsx b/src/components/Home/AccountSwitcher.tsx index be31035cd..716ece277 100644 --- a/src/components/Home/AccountSwitcher.tsx +++ b/src/components/Home/AccountSwitcher.tsx @@ -1,6 +1,6 @@ import React, { memo } from "react"; import { Image, StyleSheet, View } from "react-native"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { ChevronDown } from "lucide-react-native"; import Reanimated, { interpolateColor, diff --git a/src/components/Home/AccountSwitcherContextMenu.tsx b/src/components/Home/AccountSwitcherContextMenu.tsx index b484402e8..89852303b 100644 --- a/src/components/Home/AccountSwitcherContextMenu.tsx +++ b/src/components/Home/AccountSwitcherContextMenu.tsx @@ -17,7 +17,7 @@ import { useAccounts, useCurrentAccount } from "@/stores/account"; import { AccountService } from "@/stores/account/types"; import { PapillonContextEnter, PapillonContextExit } from "@/utils/ui/animations"; import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { BlurView } from "expo-blur"; import { Check, Cog, Palette, Plus } from "lucide-react-native"; import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; diff --git a/src/components/Home/Header.tsx b/src/components/Home/Header.tsx index 706a84fb1..e93bfcfce 100644 --- a/src/components/Home/Header.tsx +++ b/src/components/Home/Header.tsx @@ -1,7 +1,7 @@ import { CopyPlus } from "lucide-react-native"; import React, { forwardRef, useEffect, useState, useCallback, useMemo, memo } from "react"; import { Image, ScrollView, StyleSheet, Text, TouchableOpacity, View } from "react-native"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { useCurrentAccount } from "@/stores/account"; import Reanimated, { Easing, FadeInRight, ZoomIn, ZoomOut } from "react-native-reanimated"; import { get_home_widgets } from "@/addons/addons"; diff --git a/src/components/Home/HomeOnboard.tsx b/src/components/Home/HomeOnboard.tsx index ef062fe02..9bf37b598 100644 --- a/src/components/Home/HomeOnboard.tsx +++ b/src/components/Home/HomeOnboard.tsx @@ -8,7 +8,7 @@ import Reanimated, { Easing, useSharedValue, withRepeat, withTiming } from "reac import MaskStars from "@/components/FirstInstallation/MaskStars"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { ArrowDown } from "lucide-react-native"; diff --git a/src/components/Home/Widget.tsx b/src/components/Home/Widget.tsx index 08d286b71..335d14c7e 100644 --- a/src/components/Home/Widget.tsx +++ b/src/components/Home/Widget.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect, useRef, useState, memo } from "react"; import { ActivityIndicator, StyleSheet } from "react-native"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import Reanimated, { FadeIn, FadeOut, LinearTransition, ZoomIn } from "react-native-reanimated"; import { animPapillon } from "@/utils/ui/animations"; import { PressableScale } from "react-native-pressable-scale"; diff --git a/src/components/Home/WidgetHeader.tsx b/src/components/Home/WidgetHeader.tsx index 4f0897528..4415dbe83 100644 --- a/src/components/Home/WidgetHeader.tsx +++ b/src/components/Home/WidgetHeader.tsx @@ -1,5 +1,5 @@ import React, { memo } from "react"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { View, Text, ActivityIndicator } from "react-native"; const WidgetHeader: React.FC<{ diff --git a/src/components/Modals/ModalHandle.tsx b/src/components/Modals/ModalHandle.tsx index 65d109b06..ccbe66e15 100644 --- a/src/components/Modals/ModalHandle.tsx +++ b/src/components/Modals/ModalHandle.tsx @@ -1,4 +1,4 @@ -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import React from "react"; import { StyleSheet, View } from "react-native"; diff --git a/src/components/Modals/PapillonBottomSheet.tsx b/src/components/Modals/PapillonBottomSheet.tsx index 91f725e96..3634a93ec 100644 --- a/src/components/Modals/PapillonBottomSheet.tsx +++ b/src/components/Modals/PapillonBottomSheet.tsx @@ -1,5 +1,5 @@ import useScreenDimensions from "@/hooks/useScreenDimensions"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import React, {useCallback} from "react"; import { KeyboardAvoidingView, Modal, Pressable } from "react-native"; import { Gesture, GestureDetector } from "react-native-gesture-handler"; diff --git a/src/components/Restaurant/ButtonList.tsx b/src/components/Restaurant/ButtonList.tsx index b66c74e3e..1a6353738 100644 --- a/src/components/Restaurant/ButtonList.tsx +++ b/src/components/Restaurant/ButtonList.tsx @@ -6,7 +6,7 @@ import { GestureResponderEvent, ViewStyle, } from "react-native"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { PressableScale } from "react-native-pressable-scale"; interface ItemProps { diff --git a/src/components/Restaurant/RestaurantCard.tsx b/src/components/Restaurant/RestaurantCard.tsx index 213e991db..f146cf8d4 100644 --- a/src/components/Restaurant/RestaurantCard.tsx +++ b/src/components/Restaurant/RestaurantCard.tsx @@ -1,7 +1,7 @@ import type React from "react"; import { View } from "react-native"; import { NativeText } from "../Global/NativeComponents"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import AnimatedNumber from "../Global/AnimatedNumber"; import { Utensils } from "lucide-react-native"; diff --git a/src/components/Settings/AccountContainerCard.tsx b/src/components/Settings/AccountContainerCard.tsx index bc9a6dafb..55df96ae1 100644 --- a/src/components/Settings/AccountContainerCard.tsx +++ b/src/components/Settings/AccountContainerCard.tsx @@ -2,7 +2,7 @@ import { Image, Text, View } from "react-native"; import { Account } from "@/stores/account/types"; import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { LinearGradient } from "expo-linear-gradient"; import { Pen } from "lucide-react-native"; import { PressableScale } from "react-native-pressable-scale"; diff --git a/src/components/Settings/ReelGallery.tsx b/src/components/Settings/ReelGallery.tsx index bf963fe45..50ba18e69 100644 --- a/src/components/Settings/ReelGallery.tsx +++ b/src/components/Settings/ReelGallery.tsx @@ -8,7 +8,7 @@ import { Text, Dimensions, } from "react-native"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { useGradesStore } from "@/stores/grades"; import GradeModal from "../Grades/GradeModal"; import { Reel } from "@/services/shared/Reel"; diff --git a/src/components/Templates/LoginView.tsx b/src/components/Templates/LoginView.tsx index c67c23c90..0bc862bab 100644 --- a/src/components/Templates/LoginView.tsx +++ b/src/components/Templates/LoginView.tsx @@ -18,7 +18,7 @@ import { NativeText, } from "../Global/NativeComponents"; import { AlertTriangle, Eye, EyeOff, Info } from "lucide-react-native"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import ButtonCta from "../FirstInstallation/ButtonCta"; import ResponsiveTextInput from "../FirstInstallation/ResponsiveTextInput"; diff --git a/src/providers/AlertProvider.tsx b/src/providers/AlertProvider.tsx index f11fc8b66..123ff926c 100644 --- a/src/providers/AlertProvider.tsx +++ b/src/providers/AlertProvider.tsx @@ -1,4 +1,4 @@ -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { Check } from "lucide-react-native"; import React, { createContext, useState, useContext, useEffect, useCallback, ReactNode, memo } from "react"; import { Modal, View, Text, StyleSheet, Pressable } from "react-native"; diff --git a/src/router/navigator/atoms/MenuItem.tsx b/src/router/navigator/atoms/MenuItem.tsx index 8ba30f4c7..9f51b3909 100644 --- a/src/router/navigator/atoms/MenuItem.tsx +++ b/src/router/navigator/atoms/MenuItem.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { StyleSheet, Platform, Pressable } from "react-native"; import LottieView from "lottie-react-native"; import colorsList from "@/utils/data/colors.json"; diff --git a/src/router/navigator/atoms/TabItem.tsx b/src/router/navigator/atoms/TabItem.tsx index 64380610e..92bb724d0 100644 --- a/src/router/navigator/atoms/TabItem.tsx +++ b/src/router/navigator/atoms/TabItem.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { StyleSheet, Platform, Pressable } from "react-native"; import LottieView from "lottie-react-native"; import colorsList from "@/utils/data/colors.json"; diff --git a/src/utils/ui/theme.ts b/src/utils/ui/theme.ts new file mode 100644 index 000000000..cf3bbda02 --- /dev/null +++ b/src/utils/ui/theme.ts @@ -0,0 +1,7 @@ +import { useTheme } from "@react-navigation/native"; +import { useMemo } from "react"; + +export function usePapillonTheme () { + const theme = useTheme(); + return useMemo(() => theme, [theme]); +} diff --git a/src/views/account/Attendance/Atoms/AttendanceItem.tsx b/src/views/account/Attendance/Atoms/AttendanceItem.tsx index 30a91751b..132d0859b 100644 --- a/src/views/account/Attendance/Atoms/AttendanceItem.tsx +++ b/src/views/account/Attendance/Atoms/AttendanceItem.tsx @@ -8,7 +8,7 @@ import { FadeIn, FadeInUp, FadeOut, FadeOutDown } from "react-native-reanimated" import { NativeItem, NativeList, NativeText } from "@/components/Global/NativeComponents"; import { leadingZero } from "@/utils/format/attendance_time"; import { animPapillon } from "@/utils/ui/animations"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { timestampToString } from "@/utils/format/DateHelper"; interface AttendanceItemProps { diff --git a/src/views/account/Attendance/Atoms/TotalMissed.tsx b/src/views/account/Attendance/Atoms/TotalMissed.tsx index 3c5493574..2f90227f0 100644 --- a/src/views/account/Attendance/Atoms/TotalMissed.tsx +++ b/src/views/account/Attendance/Atoms/TotalMissed.tsx @@ -4,7 +4,7 @@ import { NativeList, NativeText } from "@/components/Global/NativeComponents"; import { leadingZero } from "@/utils/format/attendance_time"; import { animPapillon } from "@/utils/ui/animations"; import Reanimated, { FadeIn, FadeOut } from "react-native-reanimated"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import AnimatedNumber from "@/components/Global/AnimatedNumber"; interface TotalMissedProps { diff --git a/src/views/account/Attendance/Attendance.tsx b/src/views/account/Attendance/Attendance.tsx index 52a0b845a..bf0199a73 100644 --- a/src/views/account/Attendance/Attendance.tsx +++ b/src/views/account/Attendance/Attendance.tsx @@ -1,4 +1,4 @@ -import {useTheme} from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import React, {useEffect, useMemo, useState} from "react"; import {ActivityIndicator, Platform, RefreshControl, View} from "react-native"; diff --git a/src/views/account/Chat/Messages.tsx b/src/views/account/Chat/Messages.tsx index 1d12ab72b..e2f3e525a 100644 --- a/src/views/account/Chat/Messages.tsx +++ b/src/views/account/Chat/Messages.tsx @@ -8,7 +8,7 @@ import { Text, RefreshControl, } from "react-native"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import type { Screen } from "@/router/helpers/types"; import { NativeItem, diff --git a/src/views/account/Chat/Modals/Chat.tsx b/src/views/account/Chat/Modals/Chat.tsx index 2cb5e4426..79f97ad22 100644 --- a/src/views/account/Chat/Modals/Chat.tsx +++ b/src/views/account/Chat/Modals/Chat.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useRef, useState } from "react"; import { Image, ActivityIndicator, FlatList, ImageBackground, Platform, StyleSheet, Text, TouchableOpacity, View, KeyboardAvoidingView, } from "react-native"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import type { Screen } from "@/router/helpers/types"; import { NativeText } from "@/components/Global/NativeComponents"; diff --git a/src/views/account/Chat/Modals/ChatCreate.tsx b/src/views/account/Chat/Modals/ChatCreate.tsx index 87191cbc1..d7bef3196 100644 --- a/src/views/account/Chat/Modals/ChatCreate.tsx +++ b/src/views/account/Chat/Modals/ChatCreate.tsx @@ -6,7 +6,7 @@ import { ActivityIndicator, Platform, } from "react-native"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import type { Screen } from "@/router/helpers/types"; import { diff --git a/src/views/account/Chat/Modals/ChatDetails.tsx b/src/views/account/Chat/Modals/ChatDetails.tsx index 01ffc2523..da10beb4a 100644 --- a/src/views/account/Chat/Modals/ChatDetails.tsx +++ b/src/views/account/Chat/Modals/ChatDetails.tsx @@ -1,6 +1,6 @@ import React, {useEffect, useState} from "react"; import { ScrollView, TouchableOpacity, View, Image, StyleSheet } from "react-native"; -import {useTheme} from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import type {Screen} from "@/router/helpers/types"; import {NativeItem, NativeList, NativeListHeader, NativeText,} from "@/components/Global/NativeComponents"; diff --git a/src/views/account/Evaluation/Document.tsx b/src/views/account/Evaluation/Document.tsx index 710d16951..4d3c660b8 100644 --- a/src/views/account/Evaluation/Document.tsx +++ b/src/views/account/Evaluation/Document.tsx @@ -5,7 +5,7 @@ import { NativeText, } from "@/components/Global/NativeComponents"; import { getSubjectData } from "@/services/shared/Subject"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import React, { useEffect, useLayoutEffect, useState } from "react"; import { Image, ScrollView, Text, View, Platform } from "react-native"; import { diff --git a/src/views/account/Evaluation/Evaluation.tsx b/src/views/account/Evaluation/Evaluation.tsx index e5eb73f87..aa878391c 100644 --- a/src/views/account/Evaluation/Evaluation.tsx +++ b/src/views/account/Evaluation/Evaluation.tsx @@ -1,7 +1,7 @@ import React, {Suspense, useEffect, useMemo, useRef, useState} from "react"; import {View, ScrollView, RefreshControl, Platform, ActivityIndicator} from "react-native"; import type { Screen } from "@/router/helpers/types"; -import {useTheme} from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import {useSafeAreaInsets} from "react-native-safe-area-context"; import {PapillonHeaderSelector, PapillonModernHeader} from "@/components/Global/PapillonModernHeader"; import {useCurrentAccount} from "@/stores/account"; diff --git a/src/views/account/Grades/Atoms/GradeTitle.tsx b/src/views/account/Grades/Atoms/GradeTitle.tsx index 723926672..9179eb351 100644 --- a/src/views/account/Grades/Atoms/GradeTitle.tsx +++ b/src/views/account/Grades/Atoms/GradeTitle.tsx @@ -1,6 +1,6 @@ import { NativeList, NativeText } from "@/components/Global/NativeComponents"; import { getCourseSpeciality } from "@/utils/format/format_cours_name"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import React from "react"; import { View } from "react-native"; import {Grade} from "@/services/shared/Grade"; diff --git a/src/views/account/Grades/Atoms/GradesScodocUE.tsx b/src/views/account/Grades/Atoms/GradesScodocUE.tsx index cf28cf720..a3a934dcb 100644 --- a/src/views/account/Grades/Atoms/GradesScodocUE.tsx +++ b/src/views/account/Grades/Atoms/GradesScodocUE.tsx @@ -6,7 +6,7 @@ import { anim2Papillon } from "@/utils/ui/animations"; import { adjustColor } from "@/utils/ui/colors"; import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { ChevronDown, ChevronUp, Info } from "lucide-react-native"; import { memo, useState } from "react"; import { Image, View } from "react-native"; diff --git a/src/views/account/Grades/Document.tsx b/src/views/account/Grades/Document.tsx index 67780ffa2..845629860 100644 --- a/src/views/account/Grades/Document.tsx +++ b/src/views/account/Grades/Document.tsx @@ -1,7 +1,7 @@ import React from "react"; import { NativeItem, NativeList, NativeListHeader, NativeText, } from "@/components/Global/NativeComponents"; import { getSubjectData } from "@/services/shared/Subject"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { useCallback, useEffect, useLayoutEffect, useState } from "react"; import { Image, Platform, ScrollView, Text, TouchableOpacity, View } from "react-native"; import * as StoreReview from "expo-store-review"; diff --git a/src/views/account/Grades/Grades.tsx b/src/views/account/Grades/Grades.tsx index 34b1e4e12..0509847a5 100644 --- a/src/views/account/Grades/Grades.tsx +++ b/src/views/account/Grades/Grades.tsx @@ -14,7 +14,7 @@ import { useCurrentAccount } from "@/stores/account"; import { AccountService } from "@/stores/account/types"; import { useGradesStore } from "@/stores/grades"; import { animPapillon } from "@/utils/ui/animations"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { ChevronDown } from "lucide-react-native"; import React from "react"; import { lazy, Suspense, useEffect, useMemo, useState } from "react"; diff --git a/src/views/account/Grades/Graph/GradesAverage.tsx b/src/views/account/Grades/Graph/GradesAverage.tsx index 3af2ffb9c..bef24ff19 100644 --- a/src/views/account/Grades/Graph/GradesAverage.tsx +++ b/src/views/account/Grades/Graph/GradesAverage.tsx @@ -8,7 +8,7 @@ import { getPronoteAverage, GradeHistory, } from "@/utils/grades/getAverages"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import React, { useRef, useCallback, useEffect, useState } from "react"; import { View, StyleSheet, Platform, TouchableOpacity, Linking } from "react-native"; diff --git a/src/views/account/Grades/Latest/LatestGradesItem.tsx b/src/views/account/Grades/Latest/LatestGradesItem.tsx index 5072d14dc..5a36fbe87 100644 --- a/src/views/account/Grades/Latest/LatestGradesItem.tsx +++ b/src/views/account/Grades/Latest/LatestGradesItem.tsx @@ -7,7 +7,7 @@ import type { Grade } from "@/services/shared/Grade"; import { FadeInRight, FadeOutLeft } from "react-native-reanimated"; import { anim2Papillon } from "@/utils/ui/animations"; import { adjustColor } from "@/utils/ui/colors"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; type GradeLatestItemProps = { grade: Grade; diff --git a/src/views/account/Grades/Modals/Subject.tsx b/src/views/account/Grades/Modals/Subject.tsx index db1aa4292..66051a162 100644 --- a/src/views/account/Grades/Modals/Subject.tsx +++ b/src/views/account/Grades/Modals/Subject.tsx @@ -7,7 +7,7 @@ import { import { getSubjectData } from "@/services/shared/Subject"; import { getCourseSpeciality } from "@/utils/format/format_cours_name"; import { AverageDiffGrade, getAverageDiffGrade } from "@/utils/grades/getAverages"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { Trophy, User, UserMinus, UserPlus, Users } from "lucide-react-native"; import React, { useEffect, useLayoutEffect, useState } from "react"; import { View, ScrollView } from "react-native"; diff --git a/src/views/account/Grades/Subject/Subject.tsx b/src/views/account/Grades/Subject/Subject.tsx index 5585f7458..7f0f70a78 100644 --- a/src/views/account/Grades/Subject/Subject.tsx +++ b/src/views/account/Grades/Subject/Subject.tsx @@ -9,7 +9,7 @@ import SubjectItem from "./SubjectList"; import { useCallback, useMemo, useState } from "react"; import PapillonPicker, { PickerDataItem } from "@/components/Global/PapillonPicker"; import { ArrowDownAZ, Calendar, ChevronDown, TrendingUp } from "lucide-react-native"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; interface SubjectProps { diff --git a/src/views/account/Grades/Subject/SubjectTitle.tsx b/src/views/account/Grades/Subject/SubjectTitle.tsx index c65597c0d..79d6e970e 100644 --- a/src/views/account/Grades/Subject/SubjectTitle.tsx +++ b/src/views/account/Grades/Subject/SubjectTitle.tsx @@ -1,6 +1,6 @@ import { NativeText } from "@/components/Global/NativeComponents"; import { getCourseSpeciality } from "@/utils/format/format_cours_name"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import React, { useEffect } from "react"; import { View } from "react-native"; import { TouchableOpacity } from "react-native-gesture-handler"; diff --git a/src/views/account/Home/Elements/PopupRestauration.tsx b/src/views/account/Home/Elements/PopupRestauration.tsx index b49550ee9..eb3616bac 100644 --- a/src/views/account/Home/Elements/PopupRestauration.tsx +++ b/src/views/account/Home/Elements/PopupRestauration.tsx @@ -1,7 +1,7 @@ import React, {useEffect} from "react"; import { View, Image, StyleSheet } from "react-native"; import { NativeList, NativeText } from "@/components/Global/NativeComponents"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { TouchableOpacity } from "react-native-gesture-handler"; import { ArrowUpRight } from "lucide-react-native"; import { useCurrentAccount } from "@/stores/account"; diff --git a/src/views/account/Home/Modal/CustomizeHeader.tsx b/src/views/account/Home/Modal/CustomizeHeader.tsx index 389214773..0e3bdbe17 100644 --- a/src/views/account/Home/Modal/CustomizeHeader.tsx +++ b/src/views/account/Home/Modal/CustomizeHeader.tsx @@ -7,7 +7,7 @@ import { useCurrentAccount } from "@/stores/account"; import { NativeList, NativeListHeader } from "@/components/Global/NativeComponents"; import { COLORS_LIST } from "@/services/shared/Subject"; import { PersonalizationHeaderGradient } from "@/stores/account/types"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { Dice5, Moon, Palette, PictureInPicture } from "lucide-react-native"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; diff --git a/src/views/account/Home/ModalContent.tsx b/src/views/account/Home/ModalContent.tsx index 13eb710de..ebe7d18c4 100644 --- a/src/views/account/Home/ModalContent.tsx +++ b/src/views/account/Home/ModalContent.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useState, useMemo, memo } from "react"; import { NativeList, NativeText } from "@/components/Global/NativeComponents"; import Reanimated, { FadeInUp, FadeOutDown, LinearTransition } from "react-native-reanimated"; import { Sparkles, X } from "lucide-react-native"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import PackageJSON from "../../../../package.json"; import { Dimensions, View} from "react-native"; import { Elements, type Element } from "./ElementIndex"; diff --git a/src/views/account/Home/PlaceholderScreen.tsx b/src/views/account/Home/PlaceholderScreen.tsx index 07d2da656..737c6d025 100644 --- a/src/views/account/Home/PlaceholderScreen.tsx +++ b/src/views/account/Home/PlaceholderScreen.tsx @@ -1,4 +1,4 @@ -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import React, { useEffect, useLayoutEffect } from "react"; import { View } from "react-native"; import MissingItem from "@/components/Global/MissingItem"; diff --git a/src/views/account/Homeworks/AddHomework.tsx b/src/views/account/Homeworks/AddHomework.tsx index 2afb7f399..019932339 100644 --- a/src/views/account/Homeworks/AddHomework.tsx +++ b/src/views/account/Homeworks/AddHomework.tsx @@ -6,7 +6,7 @@ import { NativeItem, NativeList, NativeText } from "@/components/Global/NativeCo import { useHomeworkStore } from "@/stores/homework"; import { useCurrentAccount } from "@/stores/account"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { ActivityIndicator, Alert, Dimensions, Platform, TextInput, View } from "react-native"; import { Picker } from "@react-native-picker/picker"; import PapillonPicker from "@/components/Global/PapillonPicker"; diff --git a/src/views/account/Homeworks/Atoms/NoHomeworks.tsx b/src/views/account/Homeworks/Atoms/NoHomeworks.tsx index 61da93bd5..a116cd6ca 100644 --- a/src/views/account/Homeworks/Atoms/NoHomeworks.tsx +++ b/src/views/account/Homeworks/Atoms/NoHomeworks.tsx @@ -1,4 +1,4 @@ -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { Calendar } from "lucide-react-native"; import { Platform, Text } from "react-native"; diff --git a/src/views/account/Homeworks/Document.tsx b/src/views/account/Homeworks/Document.tsx index 0bab8eecd..dd08f5158 100644 --- a/src/views/account/Homeworks/Document.tsx +++ b/src/views/account/Homeworks/Document.tsx @@ -16,7 +16,7 @@ import { } from "react-native"; import { HomeworkReturnType } from "@/services/shared/Homework"; import * as WebBrowser from "expo-web-browser"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import HTMLView from "react-native-htmlview"; import { Screen } from "@/router/helpers/types"; import { useSafeAreaInsets } from "react-native-safe-area-context"; diff --git a/src/views/account/Homeworks/Homeworks.old.tsx b/src/views/account/Homeworks/Homeworks.old.tsx index 495badc04..763e1fdbe 100644 --- a/src/views/account/Homeworks/Homeworks.old.tsx +++ b/src/views/account/Homeworks/Homeworks.old.tsx @@ -1,4 +1,4 @@ -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import React, { useEffect, useRef, useCallback, useLayoutEffect, useMemo, useState } from "react"; import { View, ScrollView,Text } from "react-native"; import { Screen } from "@/router/helpers/types"; diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 9e8f2c442..85cc60ae1 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -1,7 +1,7 @@ import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; import { useCurrentAccount } from "@/stores/account"; import { useHomeworkStore } from "@/stores/homework"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import React, { useRef, useState, useCallback, useEffect } from "react"; import { toggleHomeworkState, updateHomeworkForWeekInCache } from "@/services/homework"; import { diff --git a/src/views/account/Homeworks/HomeworksHeader.tsx b/src/views/account/Homeworks/HomeworksHeader.tsx index 1024e81e3..8cb151964 100644 --- a/src/views/account/Homeworks/HomeworksHeader.tsx +++ b/src/views/account/Homeworks/HomeworksHeader.tsx @@ -1,4 +1,4 @@ -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import React from "react"; import { TouchableOpacity } from "react-native"; import { Calendar } from "lucide-react-native"; diff --git a/src/views/account/Lessons/Atoms/Item.tsx b/src/views/account/Lessons/Atoms/Item.tsx index 739725493..e70717dfd 100644 --- a/src/views/account/Lessons/Atoms/Item.tsx +++ b/src/views/account/Lessons/Atoms/Item.tsx @@ -1,4 +1,4 @@ -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import React, { useMemo } from "react"; import { Platform, StyleSheet, Text, View } from "react-native"; diff --git a/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx b/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx index bad66eccf..0fd333f6d 100644 --- a/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx +++ b/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx @@ -2,7 +2,7 @@ import React, { useState, useRef, useEffect, useCallback } from "react"; import {View, Text, StyleSheet, TouchableOpacity, Dimensions, FlatList, ListRenderItem} from "react-native"; import { format, addDays, isSameDay } from "date-fns"; import { fr } from "date-fns/locale"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import * as Haptics from "expo-haptics"; import Animated, { diff --git a/src/views/account/Lessons/Atoms/Loading.tsx b/src/views/account/Lessons/Atoms/Loading.tsx index 199ca13b2..5c385cece 100644 --- a/src/views/account/Lessons/Atoms/Loading.tsx +++ b/src/views/account/Lessons/Atoms/Loading.tsx @@ -1,5 +1,5 @@ import PapillonLoading from "@/components/Global/PapillonLoading"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; const LessonsLoading = () => { const colors = useTheme().colors; diff --git a/src/views/account/Lessons/Atoms/NoCourse.tsx b/src/views/account/Lessons/Atoms/NoCourse.tsx index 75454f5c0..b6d30e85b 100644 --- a/src/views/account/Lessons/Atoms/NoCourse.tsx +++ b/src/views/account/Lessons/Atoms/NoCourse.tsx @@ -1,4 +1,4 @@ -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { Calendar } from "lucide-react-native"; import { Platform, Text } from "react-native"; diff --git a/src/views/account/Lessons/Atoms/Page.tsx b/src/views/account/Lessons/Atoms/Page.tsx index fc0516ba2..dd6b20c0e 100644 --- a/src/views/account/Lessons/Atoms/Page.tsx +++ b/src/views/account/Lessons/Atoms/Page.tsx @@ -1,4 +1,4 @@ -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import React from "react"; import { Image, Platform, RefreshControl as RNRefreshControl, ScrollView, Text, View } from "react-native"; import { TimetableItem } from "./Item"; diff --git a/src/views/account/Lessons/Document.tsx b/src/views/account/Lessons/Document.tsx index 8b282f2b3..a343eadff 100644 --- a/src/views/account/Lessons/Document.tsx +++ b/src/views/account/Lessons/Document.tsx @@ -32,7 +32,7 @@ import { } from "lucide-react-native"; import * as WebBrowser from "expo-web-browser"; import { LinearGradient } from "expo-linear-gradient"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import HTMLView from "react-native-htmlview"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { PapillonModernHeader } from "@/components/Global/PapillonModernHeader"; diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index 37d03d2c5..792588df0 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -20,7 +20,7 @@ import Reanimated, { } from "react-native-reanimated"; import { animPapillon } from "@/utils/ui/animations"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import AnimatedNumber from "@/components/Global/AnimatedNumber"; import { CalendarPlus, Eye, EyeOff, MoreVertical } from "lucide-react-native"; import { diff --git a/src/views/account/Lessons/LessonsHeader.tsx b/src/views/account/Lessons/LessonsHeader.tsx index aae4772e7..cfd9df5b1 100644 --- a/src/views/account/Lessons/LessonsHeader.tsx +++ b/src/views/account/Lessons/LessonsHeader.tsx @@ -1,4 +1,4 @@ -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import React from "react"; import { Modal, Platform, Pressable, Text, TouchableOpacity, View } from "react-native"; diff --git a/src/views/account/Lessons/Options/LessonsImportIcal.tsx b/src/views/account/Lessons/Options/LessonsImportIcal.tsx index 6b84885ed..2e84d9e71 100644 --- a/src/views/account/Lessons/Options/LessonsImportIcal.tsx +++ b/src/views/account/Lessons/Options/LessonsImportIcal.tsx @@ -2,7 +2,7 @@ import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; import { useCurrentAccount } from "@/stores/account"; import { useTimetableStore } from "@/stores/timetable"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { Calendar, Info, QrCode, X } from "lucide-react-native"; import React, { useEffect } from "react"; import { Alert, Modal, TouchableOpacity, View } from "react-native"; diff --git a/src/views/account/News/Atoms/Item.tsx b/src/views/account/News/Atoms/Item.tsx index f3617e595..34e2d98ec 100644 --- a/src/views/account/News/Atoms/Item.tsx +++ b/src/views/account/News/Atoms/Item.tsx @@ -1,4 +1,4 @@ -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import React from "react"; import { View } from "react-native"; import { NativeItem, NativeText } from "@/components/Global/NativeComponents"; diff --git a/src/views/account/News/Document.tsx b/src/views/account/News/Document.tsx index 33301a39f..3964bf2a5 100644 --- a/src/views/account/News/Document.tsx +++ b/src/views/account/News/Document.tsx @@ -7,7 +7,7 @@ import { } from "@/components/Global/NativeComponents"; import { Information } from "@/services/shared/Information"; import formatDate from "@/utils/format/format_date_complets"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { Check, Eye, diff --git a/src/views/account/News/News.tsx b/src/views/account/News/News.tsx index 0f643072f..7f7763f97 100644 --- a/src/views/account/News/News.tsx +++ b/src/views/account/News/News.tsx @@ -10,7 +10,7 @@ import { LinearGradient } from "expo-linear-gradient"; import BetaIndicator from "@/components/News/Beta"; import NewsListItem from "./Atoms/Item"; import Reanimated, { FadeInUp, FadeOut, LinearTransition } from "react-native-reanimated"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { animPapillon } from "@/utils/ui/animations"; import { categorizeMessages } from "@/utils/magic/categorizeMessages"; import { protectScreenComponent } from "@/router/helpers/protected-screen"; diff --git a/src/views/account/Restaurant/Cards/Card.tsx b/src/views/account/Restaurant/Cards/Card.tsx index 12975a668..aee284dee 100644 --- a/src/views/account/Restaurant/Cards/Card.tsx +++ b/src/views/account/Restaurant/Cards/Card.tsx @@ -1,6 +1,6 @@ import { View, Text, StyleSheet, Image } from "react-native"; import { formatCardIdentifier, ServiceCard } from "@/utils/external/restaurant"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; import { PressableScale } from "react-native-pressable-scale"; import { AccountService } from "@/stores/account/types"; diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index 4789d4d85..dbe46ed00 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -9,7 +9,7 @@ import { Text, Dimensions } from "react-native"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { AlertTriangle, BadgeX, diff --git a/src/views/account/Restaurant/Modals/CardDetail.tsx b/src/views/account/Restaurant/Modals/CardDetail.tsx index 3a1036124..ce2d88c4f 100644 --- a/src/views/account/Restaurant/Modals/CardDetail.tsx +++ b/src/views/account/Restaurant/Modals/CardDetail.tsx @@ -7,7 +7,7 @@ import { NativeItem, NativeList, NativeText } from "@/components/Global/NativeCo import { differenceInDays, formatDistance } from "date-fns"; import { fr } from "date-fns/locale"; import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import InsetsBottomView from "@/components/Global/InsetsBottomView"; import { PressableScale } from "react-native-pressable-scale"; import { useAccounts, useCurrentAccount } from "@/stores/account"; diff --git a/src/views/account/Restaurant/Modals/PaymentSuccess.tsx b/src/views/account/Restaurant/Modals/PaymentSuccess.tsx index 536cfdb23..ac9cf748b 100644 --- a/src/views/account/Restaurant/Modals/PaymentSuccess.tsx +++ b/src/views/account/Restaurant/Modals/PaymentSuccess.tsx @@ -4,7 +4,7 @@ import { Screen } from "@/router/helpers/types"; import { reservationHistoryFromExternal } from "@/services/reservation-history"; import { ExternalAccount } from "@/stores/account/types"; import { anim2Papillon } from "@/utils/ui/animations"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { AlertCircle, Check } from "lucide-react-native"; import { useEffect, useState } from "react"; import { View, Text } from "react-native"; diff --git a/src/views/account/Restaurant/Modals/QrCode.tsx b/src/views/account/Restaurant/Modals/QrCode.tsx index 8f223a22f..d1b8367af 100644 --- a/src/views/account/Restaurant/Modals/QrCode.tsx +++ b/src/views/account/Restaurant/Modals/QrCode.tsx @@ -1,6 +1,6 @@ import { balanceFromExternal } from "@/services/balance"; import { qrcodeFromExternal } from "@/services/qrcode"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { BlurView } from "expo-blur"; import { QrCodeIcon, X } from "lucide-react-native"; import { useEffect, useState } from "react"; diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index 778f7a925..46de385a6 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -4,7 +4,7 @@ import { Image, Linking, StyleSheet, Text, TouchableOpacity, View } from "react- import CalendarKit, { PackedAllDayEvent, PackedEvent, HeaderItemProps as CalendarKitHeaderItemProps, EventItem as CalendarKitEventItem } from "@howljs/calendar-kit"; import { useCurrentAccount } from "@/stores/account"; import { useTimetableStore } from "@/stores/timetable"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { updateTimetableForWeekInCache } from "@/services/timetable"; import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; import { useSafeAreaInsets } from "react-native-safe-area-context"; diff --git a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx index a5d849ab0..34318bccd 100644 --- a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx +++ b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx @@ -2,7 +2,7 @@ import defaultPersonalization from "@/services/local/default-personalization"; import { useAccounts, useCurrentAccount } from "@/stores/account"; import { AccountService, Identity, LocalAccount } from "@/stores/account/types"; import uuid from "@/utils/uuid-v4"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import React from "react"; import { Pressable, View } from "react-native"; import { WebView } from "react-native-webview"; diff --git a/src/views/login/IdentityProvider/providers/UnivLimoges.tsx b/src/views/login/IdentityProvider/providers/UnivLimoges.tsx index 65d228078..7e79d9474 100644 --- a/src/views/login/IdentityProvider/providers/UnivLimoges.tsx +++ b/src/views/login/IdentityProvider/providers/UnivLimoges.tsx @@ -7,7 +7,7 @@ import { View } from "react-native"; import WebView from "react-native-webview"; import { useRef, useState } from "react"; import { useAccounts, useCurrentAccount } from "@/stores/account"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { NativeText } from "@/components/Global/NativeComponents"; import { log } from "@/utils/logger/logger"; diff --git a/src/views/login/IdentityProvider/providers/UnivRennes1.tsx b/src/views/login/IdentityProvider/providers/UnivRennes1.tsx index f2ffa5b70..ebd447bfe 100644 --- a/src/views/login/IdentityProvider/providers/UnivRennes1.tsx +++ b/src/views/login/IdentityProvider/providers/UnivRennes1.tsx @@ -11,7 +11,7 @@ import defaultPersonalization from "@/services/local/default-personalization"; import uuid from "@/utils/uuid-v4"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { NativeText } from "@/components/Global/NativeComponents"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; const UnivRennes1_Login: Screen<"UnivRennes1_Login"> = ({ navigation }) => { const mainURL = "https://sesame.univ-rennes1.fr/comptes/"; diff --git a/src/views/login/IdentityProvider/providers/UnivRennes2.tsx b/src/views/login/IdentityProvider/providers/UnivRennes2.tsx index bed02f263..7dc0fbdf7 100644 --- a/src/views/login/IdentityProvider/providers/UnivRennes2.tsx +++ b/src/views/login/IdentityProvider/providers/UnivRennes2.tsx @@ -52,7 +52,7 @@ import defaultPersonalization from "@/services/local/default-personalization"; import uuid from "@/utils/uuid-v4"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { NativeText } from "@/components/Global/NativeComponents"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; const UnivRennes2_Login: Screen<"UnivRennes2_Login"> = ({ navigation }) => { const mainURL = "https://cas.univ-rennes2.fr/login?service=https%3A%2F%2Fservices.univ-rennes2.fr%2Fsesame%2Findex.php%2Flogin%2Fmon-compte-sesame%2Fchanger-mon-mot-de-passe"; diff --git a/src/views/login/IdentityProvider/providers/UnivSorbonneParisNord.tsx b/src/views/login/IdentityProvider/providers/UnivSorbonneParisNord.tsx index 604b633eb..5a632ea30 100644 --- a/src/views/login/IdentityProvider/providers/UnivSorbonneParisNord.tsx +++ b/src/views/login/IdentityProvider/providers/UnivSorbonneParisNord.tsx @@ -5,7 +5,7 @@ import { useAccounts, useCurrentAccount } from "@/stores/account"; import { AccountService, LocalAccount } from "@/stores/account/types"; import defaultPersonalization from "@/services/local/default-personalization"; import uuid from "@/utils/uuid-v4"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import LoginView from "@/components/Templates/LoginView"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { NativeText } from "@/components/Global/NativeComponents"; diff --git a/src/views/login/ServiceSelector.tsx b/src/views/login/ServiceSelector.tsx index b40098cbe..d104a505d 100644 --- a/src/views/login/ServiceSelector.tsx +++ b/src/views/login/ServiceSelector.tsx @@ -9,7 +9,7 @@ import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBub import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import MaskStars from "@/components/FirstInstallation/MaskStars"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import GetV6Data from "@/utils/login/GetV6Data"; import { Check, School, WifiOff } from "lucide-react-native"; import { LinearGradient } from "expo-linear-gradient"; diff --git a/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx b/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx index 58fc10a05..1648c0756 100644 --- a/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx +++ b/src/views/login/ecoledirecte/EcoleDirecteCredentials.tsx @@ -16,7 +16,7 @@ import uuid from "@/utils/uuid-v4"; import { useAccounts, useCurrentAccount } from "@/stores/account"; import { AccountService, type EcoleDirecteAccount } from "@/stores/account/types"; import defaultPersonalization from "@/services/ecoledirecte/default-personalization"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import BottomSheet from "@/components/Modals/PapillonBottomSheet"; import {NativeText} from "@/components/Global/NativeComponents"; diff --git a/src/views/login/pronote/Pronote2FA_Auth.tsx b/src/views/login/pronote/Pronote2FA_Auth.tsx index c73f72ce2..e48de14d9 100644 --- a/src/views/login/pronote/Pronote2FA_Auth.tsx +++ b/src/views/login/pronote/Pronote2FA_Auth.tsx @@ -20,7 +20,7 @@ import { NativeText, } from "@/components/Global/NativeComponents"; import { Info } from "lucide-react-native"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import type { Screen } from "@/router/helpers/types"; diff --git a/src/views/login/pronote/PronoteAuthenticationSelector.tsx b/src/views/login/pronote/PronoteAuthenticationSelector.tsx index 409dd6ce6..746cdae03 100644 --- a/src/views/login/pronote/PronoteAuthenticationSelector.tsx +++ b/src/views/login/pronote/PronoteAuthenticationSelector.tsx @@ -5,7 +5,7 @@ import { QrCodeIcon, LinkIcon, MapPinIcon, SearchIcon, LockIcon } from "lucide-r import type { Screen } from "@/router/helpers/types"; import { SafeAreaView } from "react-native-safe-area-context"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import MaskStars from "@/components/FirstInstallation/MaskStars"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; import Reanimated, { LinearTransition, FlipInXDown } from "react-native-reanimated"; diff --git a/src/views/login/pronote/PronoteGeolocation.tsx b/src/views/login/pronote/PronoteGeolocation.tsx index 4d3961e2c..211a3307e 100644 --- a/src/views/login/pronote/PronoteGeolocation.tsx +++ b/src/views/login/pronote/PronoteGeolocation.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import { View,Text, StyleSheet } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { SafeAreaView } from "react-native-safe-area-context"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { getCurrentPosition } from "@/utils/native/location"; import { useLocationPermission } from "@/hooks/location"; diff --git a/src/views/login/pronote/PronoteInstanceSelector.tsx b/src/views/login/pronote/PronoteInstanceSelector.tsx index 3a337b2bc..308548a91 100644 --- a/src/views/login/pronote/PronoteInstanceSelector.tsx +++ b/src/views/login/pronote/PronoteInstanceSelector.tsx @@ -29,7 +29,7 @@ import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBub import { useSafeAreaInsets } from "react-native-safe-area-context"; import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import { LinearGradient } from "expo-linear-gradient"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import {Search, X, GraduationCap, SearchX} from "lucide-react-native"; import { useAlert } from "@/providers/AlertProvider"; diff --git a/src/views/login/pronote/PronoteManualLocation.tsx b/src/views/login/pronote/PronoteManualLocation.tsx index 7d09f2b49..ef8f4a643 100644 --- a/src/views/login/pronote/PronoteManualLocation.tsx +++ b/src/views/login/pronote/PronoteManualLocation.tsx @@ -9,7 +9,7 @@ import Reanimated, { LinearTransition, FlipInXDown, ZoomIn, ZoomOut, FadeInDown, import MaskStars from "@/components/FirstInstallation/MaskStars"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; import { SafeAreaView, useSafeAreaInsets } from "react-native-safe-area-context"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { Search, X } from "lucide-react-native"; import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import ResponsiveTextInput from "@/components/FirstInstallation/ResponsiveTextInput"; diff --git a/src/views/login/pronote/PronoteManualURL.tsx b/src/views/login/pronote/PronoteManualURL.tsx index 76071db02..7080afbee 100644 --- a/src/views/login/pronote/PronoteManualURL.tsx +++ b/src/views/login/pronote/PronoteManualURL.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from "react"; import type { RouteParameters, Screen } from "@/router/helpers/types"; import { View, StyleSheet, TouchableOpacity, KeyboardEvent, Keyboard } from "react-native"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import determinateAuthenticationView from "@/services/pronote/determinate-authentication-view"; import * as Clipboard from "expo-clipboard"; diff --git a/src/views/login/pronote/PronoteQRCode.tsx b/src/views/login/pronote/PronoteQRCode.tsx index 11ca17e28..455087a04 100644 --- a/src/views/login/pronote/PronoteQRCode.tsx +++ b/src/views/login/pronote/PronoteQRCode.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import { ActivityIndicator, Text, View, StyleSheet, Modal, KeyboardAvoidingView, TextInput, Keyboard } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { SafeAreaView, useSafeAreaInsets } from "react-native-safe-area-context"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { BarCodeScanner } from "expo-barcode-scanner"; import MaskedView from "@react-native-masked-view/masked-view"; import * as Haptics from "expo-haptics"; diff --git a/src/views/login/pronote/PronoteWebview.tsx b/src/views/login/pronote/PronoteWebview.tsx index 5b9a8acc4..0a53445e3 100644 --- a/src/views/login/pronote/PronoteWebview.tsx +++ b/src/views/login/pronote/PronoteWebview.tsx @@ -10,7 +10,7 @@ import { import { WebView } from "react-native-webview"; import type { Screen } from "@/router/helpers/types"; import { SafeAreaView } from "react-native-safe-area-context"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import MaskStars from "@/components/FirstInstallation/MaskStars"; import Reanimated, { diff --git a/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx b/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx index 4b9dfeaa7..fe417bb4d 100644 --- a/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx +++ b/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx @@ -5,7 +5,7 @@ import { MapPinIcon, SearchIcon, LockIcon } from "lucide-react-native"; import type { Screen } from "@/router/helpers/types"; import { SafeAreaView } from "react-native-safe-area-context"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import MaskStars from "@/components/FirstInstallation/MaskStars"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; import Reanimated, { LinearTransition, FlipInXDown } from "react-native-reanimated"; diff --git a/src/views/login/skolengo/SkolengoGeolocation.tsx b/src/views/login/skolengo/SkolengoGeolocation.tsx index c05dba55b..41742e866 100644 --- a/src/views/login/skolengo/SkolengoGeolocation.tsx +++ b/src/views/login/skolengo/SkolengoGeolocation.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import { View,Text, StyleSheet } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { SafeAreaView } from "react-native-safe-area-context"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { getCurrentPosition } from "@/utils/native/location"; import { useLocationPermission } from "@/hooks/location"; diff --git a/src/views/login/skolengo/SkolengoInstanceSelector.tsx b/src/views/login/skolengo/SkolengoInstanceSelector.tsx index 276e65e7a..5a1b5e095 100644 --- a/src/views/login/skolengo/SkolengoInstanceSelector.tsx +++ b/src/views/login/skolengo/SkolengoInstanceSelector.tsx @@ -7,7 +7,7 @@ import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBub import { useSafeAreaInsets } from "react-native-safe-area-context"; import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import { LinearGradient } from "expo-linear-gradient"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { Search, X, GraduationCap, } from "lucide-react-native"; import { useAlert } from "@/providers/AlertProvider"; diff --git a/src/views/login/skolengo/SkolengoWebview.tsx b/src/views/login/skolengo/SkolengoWebview.tsx index a5a942c05..b52b2b095 100644 --- a/src/views/login/skolengo/SkolengoWebview.tsx +++ b/src/views/login/skolengo/SkolengoWebview.tsx @@ -15,7 +15,7 @@ import { SafeAreaView, useSafeAreaInsets, } from "react-native-safe-area-context"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import MaskStars from "@/components/FirstInstallation/MaskStars"; import { School } from "scolengo-api/types/models/School"; diff --git a/src/views/settings/ExternalAccount/IzlyActivation.tsx b/src/views/settings/ExternalAccount/IzlyActivation.tsx index f419fd012..f81fe3464 100644 --- a/src/views/settings/ExternalAccount/IzlyActivation.tsx +++ b/src/views/settings/ExternalAccount/IzlyActivation.tsx @@ -1,6 +1,6 @@ import React, {useState, useEffect} from "react"; import type {Screen} from "@/router/helpers/types"; -import {useTheme} from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import {SafeAreaView, useSafeAreaInsets} from "react-native-safe-area-context"; import { Alert, Keyboard, KeyboardAvoidingView, StyleSheet, TouchableWithoutFeedback, View} from "react-native"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; diff --git a/src/views/settings/ExternalAccount/PriceBeforeScan.tsx b/src/views/settings/ExternalAccount/PriceBeforeScan.tsx index a4452c7a2..681e85690 100644 --- a/src/views/settings/ExternalAccount/PriceBeforeScan.tsx +++ b/src/views/settings/ExternalAccount/PriceBeforeScan.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useMemo } from "react"; import { View, StyleSheet, Text, TouchableOpacity } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import QRCode from "react-native-qrcode-svg"; import Barcode from "react-native-barcode-svg"; import { useAccounts } from "@/stores/account"; diff --git a/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx b/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx index a22b06d77..02c4acb8d 100644 --- a/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx +++ b/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx @@ -1,6 +1,6 @@ import React from "react"; import type { Screen } from "@/router/helpers/types"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { BellRing } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { Image, View, StyleSheet, Text } from "react-native"; diff --git a/src/views/settings/ExternalAccount/PriceError.tsx b/src/views/settings/ExternalAccount/PriceError.tsx index e8c572623..e61dfb0e2 100644 --- a/src/views/settings/ExternalAccount/PriceError.tsx +++ b/src/views/settings/ExternalAccount/PriceError.tsx @@ -1,6 +1,6 @@ import React from "react"; import type { Screen } from "@/router/helpers/types"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { BadgeX, CircleHelp } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import {View, StyleSheet, Text, Alert} from "react-native"; diff --git a/src/views/settings/ExternalAccount/QrcodeScanner.tsx b/src/views/settings/ExternalAccount/QrcodeScanner.tsx index 4af58033a..7724dbaf8 100644 --- a/src/views/settings/ExternalAccount/QrcodeScanner.tsx +++ b/src/views/settings/ExternalAccount/QrcodeScanner.tsx @@ -1,6 +1,6 @@ import React, { useEffect } from "react"; import type { Screen } from "@/router/helpers/types"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { QrCode } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { View, StyleSheet, Text } from "react-native"; diff --git a/src/views/settings/ExternalAccount/SelectMethod.tsx b/src/views/settings/ExternalAccount/SelectMethod.tsx index 1b142d73a..45d84ab8c 100644 --- a/src/views/settings/ExternalAccount/SelectMethod.tsx +++ b/src/views/settings/ExternalAccount/SelectMethod.tsx @@ -1,7 +1,7 @@ import React from "react"; import { ScrollView, } from "react-native"; import type { Screen } from "@/router/helpers/types"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { Star } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; diff --git a/src/views/settings/ExternalAccount/ServiceSelector.tsx b/src/views/settings/ExternalAccount/ServiceSelector.tsx index f5ba0d2ed..6a87b09c9 100644 --- a/src/views/settings/ExternalAccount/ServiceSelector.tsx +++ b/src/views/settings/ExternalAccount/ServiceSelector.tsx @@ -10,7 +10,7 @@ import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { useOnlineStatus } from "@/hooks/useOnlineStatus"; import { useAlert } from "@/providers/AlertProvider"; import { Check, WifiOff } from "lucide-react-native"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { useCurrentAccount } from "@/stores/account"; const ExternalAccountSelector: Screen<"ExternalAccountSelector"> = ({ navigation, route }) => { diff --git a/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx b/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx index ecaf76eb5..ebfcfc6a1 100644 --- a/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx +++ b/src/views/settings/ExternalAccount/TurboselfAccountSelector.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import type {Screen} from "@/router/helpers/types"; -import {useTheme} from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import {SafeAreaView, useSafeAreaInsets} from "react-native-safe-area-context"; import { Keyboard, KeyboardAvoidingView, StyleSheet, TouchableWithoutFeedback, View} from "react-native"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index 473f66fb3..7b78ff11f 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -43,7 +43,7 @@ import { import { NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; import ModalHandle from "@/components/Modals/ModalHandle"; import AccountContainerCard from "@/components/Settings/AccountContainerCard"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import {get_settings_widgets} from "@/addons/addons"; import AsyncStorage from "@react-native-async-storage/async-storage"; import {AddonPlacementManifest} from "@/addons/types"; diff --git a/src/views/settings/SettingsAbout.tsx b/src/views/settings/SettingsAbout.tsx index 5b35a161d..397ec4499 100644 --- a/src/views/settings/SettingsAbout.tsx +++ b/src/views/settings/SettingsAbout.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from "react"; import { ScrollView, Image, StyleSheet, View } from "react-native"; import type { Screen } from "@/router/helpers/types"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { Euro, Github, MapPin, MessageCircle } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { NativeList, NativeItem, NativeListHeader } from "@/components/Global/NativeComponents"; diff --git a/src/views/settings/SettingsAccessibility.tsx b/src/views/settings/SettingsAccessibility.tsx index e4a1ff346..daf68430e 100644 --- a/src/views/settings/SettingsAccessibility.tsx +++ b/src/views/settings/SettingsAccessibility.tsx @@ -1,7 +1,7 @@ import React from "react"; import { ScrollView } from "react-native"; import type { Screen } from "@/router/helpers/types"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { Moon, Sun, SunMoon, Vibrate, Volume2 } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { diff --git a/src/views/settings/SettingsDevLogs.tsx b/src/views/settings/SettingsDevLogs.tsx index eb51743cb..8f589c4b4 100644 --- a/src/views/settings/SettingsDevLogs.tsx +++ b/src/views/settings/SettingsDevLogs.tsx @@ -34,7 +34,7 @@ import { FadeOutUp, } from "react-native-reanimated"; import { animPapillon } from "@/utils/ui/animations"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { useAlert } from "@/providers/AlertProvider"; import MissingItem from "@/components/Global/MissingItem"; import ResponsiveTextInput from "@/components/FirstInstallation/ResponsiveTextInput"; diff --git a/src/views/settings/SettingsExternalServices.tsx b/src/views/settings/SettingsExternalServices.tsx index 30aa45069..74d4bf126 100644 --- a/src/views/settings/SettingsExternalServices.tsx +++ b/src/views/settings/SettingsExternalServices.tsx @@ -1,7 +1,7 @@ import React from "react"; import { ScrollView, View } from "react-native"; import type { Screen } from "@/router/helpers/types"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { GraduationCap, Utensils, BookOpen, School, BookmarkMinus, Compass, Check, Trash2, BadgeInfo } from "lucide-react-native"; import ExternalServicesContainerCard from "@/components/Settings/ExternalServicesContainerCard"; import { diff --git a/src/views/settings/SettingsFlags.tsx b/src/views/settings/SettingsFlags.tsx index 3ad52b777..cf8dcfa7f 100644 --- a/src/views/settings/SettingsFlags.tsx +++ b/src/views/settings/SettingsFlags.tsx @@ -1,7 +1,7 @@ import React, { Fragment, useRef } from "react"; import { ScrollView, TextInput, KeyboardAvoidingView, StyleSheet } from "react-native"; import type { Screen } from "@/router/helpers/types"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { BadgeHelp, Code, Trash2, Undo2 } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; diff --git a/src/views/settings/SettingsFlagsInfos.tsx b/src/views/settings/SettingsFlagsInfos.tsx index 4fe58ac43..a3b5cc91d 100644 --- a/src/views/settings/SettingsFlagsInfos.tsx +++ b/src/views/settings/SettingsFlagsInfos.tsx @@ -1,6 +1,6 @@ import React, { useLayoutEffect } from "react"; import { ScrollView, StyleSheet, View } from "react-native"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { NativeText } from "@/components/Global/NativeComponents"; import {Screen} from "@/router/helpers/types"; diff --git a/src/views/settings/SettingsIcons.tsx b/src/views/settings/SettingsIcons.tsx index f09fc2a3b..d9516633e 100644 --- a/src/views/settings/SettingsIcons.tsx +++ b/src/views/settings/SettingsIcons.tsx @@ -1,7 +1,7 @@ import React, { useEffect } from "react"; import { Text, ScrollView, View, TouchableOpacity, Image } from "react-native"; import type { Screen } from "@/router/helpers/types"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { BadgeInfo, Sparkles } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { NativeList, NativeItem, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; diff --git a/src/views/settings/SettingsMagic.tsx b/src/views/settings/SettingsMagic.tsx index e3daad22d..4d20ad463 100644 --- a/src/views/settings/SettingsMagic.tsx +++ b/src/views/settings/SettingsMagic.tsx @@ -1,6 +1,6 @@ import React from "react"; import { ScrollView, Switch } from "react-native"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import type { Screen } from "@/router/helpers/types"; import { useSafeAreaInsets } from "react-native-safe-area-context"; // Ensure this file contains valid regex patterns diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index acd5c92dd..f0cb01374 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -1,6 +1,6 @@ import React, {useRef, useState} from "react"; import { Image, ScrollView, Switch, TextInput, View} from "react-native"; -import {useTheme} from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import type {Screen} from "@/router/helpers/types"; import MultiServiceContainerCard from "@/components/Settings/MultiServiceContainerCard"; import {NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText} from "@/components/Global/NativeComponents"; diff --git a/src/views/settings/SettingsMultiServiceSpace.tsx b/src/views/settings/SettingsMultiServiceSpace.tsx index 3d8449096..46905e863 100644 --- a/src/views/settings/SettingsMultiServiceSpace.tsx +++ b/src/views/settings/SettingsMultiServiceSpace.tsx @@ -8,7 +8,7 @@ import { TextInput, View } from "react-native"; -import {useTheme} from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import type {Screen} from "@/router/helpers/types"; import {NativeItem, NativeList, NativeListHeader, NativeText} from "@/components/Global/NativeComponents"; import {BadgeHelp, Camera, ChevronDown, CircleAlert, TextCursorInput, Trash2, Type, Undo2, User2} from "lucide-react-native"; diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 62ff82e42..b013af594 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from "react"; import { ScrollView, Switch } from "react-native"; import type { Screen } from "@/router/helpers/types"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { CalendarCheck, BookCheck, diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index 357afe5d3..e3cb69d64 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -1,7 +1,7 @@ import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; import { Screen } from "@/router/helpers/types"; import { useCurrentAccount } from "@/stores/account"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import * as ImagePicker from "expo-image-picker"; import { BadgeX, Camera, CameraOff, ChevronDown, ChevronUp, ClipboardCopy, TextCursorInput, Trash, Undo2, User2, UserCircle2, WholeWord } from "lucide-react-native"; import React, { useEffect, useRef, useState } from "react"; diff --git a/src/views/settings/SettingsReactions.tsx b/src/views/settings/SettingsReactions.tsx index ffd4401bb..5a383428c 100644 --- a/src/views/settings/SettingsReactions.tsx +++ b/src/views/settings/SettingsReactions.tsx @@ -1,7 +1,7 @@ import React from "react"; import { ScrollView, View } from "react-native"; import type { Screen } from "@/router/helpers/types"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { useGradesStore } from "@/stores/grades"; import ReelGallery from "@/components/Settings/ReelGallery"; import MissingItem from "@/components/Global/MissingItem"; diff --git a/src/views/settings/SettingsSubjects.tsx b/src/views/settings/SettingsSubjects.tsx index 3813aaceb..21b321d14 100644 --- a/src/views/settings/SettingsSubjects.tsx +++ b/src/views/settings/SettingsSubjects.tsx @@ -9,7 +9,7 @@ import { } from "react-native"; import { ScrollView } from "react-native-gesture-handler"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import Reanimated, { ZoomIn, ZoomOut } from "react-native-reanimated"; import debounce from "lodash/debounce"; import { NativeItem, NativeList, NativeText } from "@/components/Global/NativeComponents"; diff --git a/src/views/settings/SettingsSupport.tsx b/src/views/settings/SettingsSupport.tsx index 20b19013c..4a6fe391a 100644 --- a/src/views/settings/SettingsSupport.tsx +++ b/src/views/settings/SettingsSupport.tsx @@ -1,7 +1,7 @@ import React, { useState } from "react"; import { KeyboardAvoidingView, Platform, ScrollView, StyleSheet, TextInput, View } from "react-native"; import type { Screen } from "@/router/helpers/types"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import SupportContainerCard from "@/components/Settings/SupportContainerCard"; import PapillonCheckbox from "@/components/Global/PapillonCheckbox"; diff --git a/src/views/settings/SettingsTabs.tsx b/src/views/settings/SettingsTabs.tsx index f739438cc..4938f775b 100644 --- a/src/views/settings/SettingsTabs.tsx +++ b/src/views/settings/SettingsTabs.tsx @@ -8,7 +8,7 @@ import { } from "@/components/Global/NativeComponents"; import PapillonCheckbox from "@/components/Global/PapillonCheckbox"; import { useCurrentAccount } from "@/stores/account"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import LottieView from "lottie-react-native"; import { AlertTriangle, diff --git a/src/views/settings/SettingsTrophies.tsx b/src/views/settings/SettingsTrophies.tsx index a4da7f690..9b6e49534 100644 --- a/src/views/settings/SettingsTrophies.tsx +++ b/src/views/settings/SettingsTrophies.tsx @@ -1,7 +1,7 @@ import React from "react"; import { ScrollView, View, StyleSheet } from "react-native"; import type { Screen } from "@/router/helpers/types"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { useSafeAreaInsets } from "react-native-safe-area-context"; diff --git a/src/views/welcome/ChangelogScreen.tsx b/src/views/welcome/ChangelogScreen.tsx index 6f539481e..8dd275fb2 100644 --- a/src/views/welcome/ChangelogScreen.tsx +++ b/src/views/welcome/ChangelogScreen.tsx @@ -6,7 +6,7 @@ import datasets from "@/consts/datasets.json"; import uuid from "@/utils/uuid-v4"; import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; import { AlertTriangle, Bug, Sparkles, X } from "lucide-react-native"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import Reanimated, { FadeInUp, FadeOutUp, LinearTransition } from "react-native-reanimated"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; diff --git a/src/views/welcome/ColorSelector.tsx b/src/views/welcome/ColorSelector.tsx index e355d3101..9ee009b03 100644 --- a/src/views/welcome/ColorSelector.tsx +++ b/src/views/welcome/ColorSelector.tsx @@ -1,7 +1,7 @@ import React, { useLayoutEffect } from "react"; import { View, StyleSheet, Pressable, Platform } from "react-native"; import MaskStarsColored from "@/components/FirstInstallation/MaskStarsColored"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; import type { Screen } from "@/router/helpers/types"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; diff --git a/src/views/welcome/DevMenu.tsx b/src/views/welcome/DevMenu.tsx index 7a885f66e..002990efe 100644 --- a/src/views/welcome/DevMenu.tsx +++ b/src/views/welcome/DevMenu.tsx @@ -1,4 +1,4 @@ -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { AlertTriangle, BadgeHelp, CheckCheck, ChevronRight, Eraser, Undo2 } from "lucide-react-native"; import React, { useEffect, useLayoutEffect, useState } from "react"; import { View, Text, TouchableOpacity, ScrollView } from "react-native"; diff --git a/src/views/welcome/FirstInstallation.tsx b/src/views/welcome/FirstInstallation.tsx index 6160e2c21..ca371f0e2 100644 --- a/src/views/welcome/FirstInstallation.tsx +++ b/src/views/welcome/FirstInstallation.tsx @@ -8,7 +8,7 @@ import MaskStars from "@/components/FirstInstallation/MaskStars"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import * as WebBrowser from "expo-web-browser"; diff --git a/src/views/welcome/ProfilePic.tsx b/src/views/welcome/ProfilePic.tsx index dafcd496b..5d0567e0e 100644 --- a/src/views/welcome/ProfilePic.tsx +++ b/src/views/welcome/ProfilePic.tsx @@ -12,7 +12,7 @@ import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBub import { useCurrentAccount } from "@/stores/account"; import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { Camera, Trash } from "lucide-react-native"; import * as ImagePicker from "expo-image-picker"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; diff --git a/src/widgets/Components/GeneralAverage.tsx b/src/widgets/Components/GeneralAverage.tsx index 6ccdbaa5b..fa8c43bfb 100644 --- a/src/widgets/Components/GeneralAverage.tsx +++ b/src/widgets/Components/GeneralAverage.tsx @@ -1,4 +1,4 @@ -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { PieChart } from "lucide-react-native"; import React, { forwardRef, diff --git a/src/widgets/Components/LastGrade.tsx b/src/widgets/Components/LastGrade.tsx index cfcc69143..054e597a8 100644 --- a/src/widgets/Components/LastGrade.tsx +++ b/src/widgets/Components/LastGrade.tsx @@ -1,4 +1,4 @@ -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { TrendingUp } from "lucide-react-native"; import React, { forwardRef, useEffect, useImperativeHandle, useMemo } from "react"; import { Text, View } from "react-native"; diff --git a/src/widgets/Components/NextCourse.tsx b/src/widgets/Components/NextCourse.tsx index 703b913d4..d7f0c32cc 100644 --- a/src/widgets/Components/NextCourse.tsx +++ b/src/widgets/Components/NextCourse.tsx @@ -1,6 +1,6 @@ import React, { forwardRef, useEffect, useImperativeHandle, useState, useCallback, useMemo } from "react"; import { ActivityIndicator, Text, View } from "react-native"; -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { Calendar, Clock } from "lucide-react-native"; import { WidgetProps } from "@/components/Home/Widget"; diff --git a/src/widgets/Components/RestaurantBalance.tsx b/src/widgets/Components/RestaurantBalance.tsx index a799a99d4..3af3c0eb1 100644 --- a/src/widgets/Components/RestaurantBalance.tsx +++ b/src/widgets/Components/RestaurantBalance.tsx @@ -1,4 +1,4 @@ -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { Pizza } from "lucide-react-native"; import React, { forwardRef, useEffect, useImperativeHandle, useState } from "react"; import { Text, View } from "react-native"; diff --git a/src/widgets/Components/RestaurantQRCode.tsx b/src/widgets/Components/RestaurantQRCode.tsx index b3e8d9aaa..e9f9e270d 100644 --- a/src/widgets/Components/RestaurantQRCode.tsx +++ b/src/widgets/Components/RestaurantQRCode.tsx @@ -1,4 +1,4 @@ -import { useTheme } from "@react-navigation/native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { Pizza } from "lucide-react-native"; import React, { forwardRef, useEffect, useImperativeHandle, useState } from "react"; import { Text, View } from "react-native"; From c6b6437e812470ca2b08f7187224ddfc01e8aa9d Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 31 Mar 2025 19:04:57 +0200 Subject: [PATCH 1105/1144] =?UTF-8?q?fix(Homeworks):=20corriger=20la=20ges?= =?UTF-8?q?tion=20de=20la=20propri=C3=A9t=C3=A9=20outsideNav=20et=20ajuste?= =?UTF-8?q?r=20la=20position=20du=20bouton=20d'ajout=20de=20devoir?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../account/Grades/Atoms/GradesScodocUE.tsx | 501 ++++++++---------- src/views/account/Homeworks/Homeworks.tsx | 4 +- 2 files changed, 210 insertions(+), 295 deletions(-) diff --git a/src/views/account/Grades/Atoms/GradesScodocUE.tsx b/src/views/account/Grades/Atoms/GradesScodocUE.tsx index a3a934dcb..b63fb66f8 100644 --- a/src/views/account/Grades/Atoms/GradesScodocUE.tsx +++ b/src/views/account/Grades/Atoms/GradesScodocUE.tsx @@ -4,323 +4,238 @@ import { useAlert } from "@/providers/AlertProvider"; import { PrimaryAccount } from "@/stores/account/types"; import { anim2Papillon } from "@/utils/ui/animations"; import { adjustColor } from "@/utils/ui/colors"; - import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { ChevronDown, ChevronUp, Info } from "lucide-react-native"; -import { memo, useState } from "react"; +import { memo, useState, useMemo, useCallback } from "react"; import { Image, View } from "react-native"; import { TouchableOpacity } from "react-native-gesture-handler"; - import Reanimated, { FadeIn, FadeInDown, FadeOut, FadeOutUp, LinearTransition } from "react-native-reanimated"; -const GradesScodocUE = ({ account, navigation, selectedPeriod }: { account: PrimaryAccount, navigation: any, selectedPeriod: string }) => { - try { - const theme = useTheme(); - const colors = theme.colors as any; - const { showAlert } = useAlert(); - - const data = account.serviceData.semestres as any; - const grades = data[selectedPeriod]; - - const ues = grades["relevé"]["ues"]; - const uekeys = Object.keys(ues); - - if (uekeys.length === 0) { - return null; - } - - const ressources = grades["relevé"]["ressources"]; - const saes = grades["relevé"]["saes"]; - - const finalUes = uekeys.map((ue) => { - return { - name: ue, - ...ues[ue], - }; - }); - - return ( - - { + const theme = useTheme(); + const colors = theme.colors; + const { showAlert } = useAlert(); + + const data = account.serviceData.semestres; + const grades = data[selectedPeriod]; + + const ues = grades["relevé"]["ues"]; + const uekeys = Object.keys(ues); + + if (uekeys.length === 0) return null; + + const ressources = grades["relevé"]["ressources"]; + const saes = grades["relevé"]["saes"]; + + const finalUes = useMemo(() => uekeys.map(ue => ({ + name: ue, + ...ues[ue] + })), [uekeys, ues]); + + const handleAlert = useCallback(() => showAlert({ + title: "Unités d'enseignement", + message: `Les données, rangs et notes sont fournies par les services de ${account.identityProvider?.name}.`, + icon: , + }), [account.identityProvider?.name, showAlert]); + + return ( + + + showAlert({ - title: "Unités d'enseignement", - message: `Les données, rangs et notes sont fournies par les services de ${account.identityProvider?.name}.`, - icon: , - })} - > - - - } - /> - - - {finalUes.map((ue, i) => { - interface ueGrade { - key: string, - type: "ressources" | "saes" - } - - const grades: ueGrade[] = []; - const [opened, setOpened] = useState(false); - - Object.keys(ue.ressources).forEach((res) => { - grades.push({ - key: res, - type: "ressources", - }); - }); - - Object.keys(ue.saes).forEach((sae) => { - grades.push({ - key: sae, - type: "saes", - }); - }); - - function navigateToSubject () { - navigation.navigate("GradeSubject", { subject: { - average: { - subjectName: ue.name + " > " + ue.titre, - average: { - value: parseFloat(ue.moyenne.value), - }, - classAverage: { - value: parseFloat(ue.moyenne.moy), - }, - min: { - value: parseFloat(ue.moyenne.min), - }, - max: { - value: parseFloat(ue.moyenne.max), - }, - outOf: { - value: 20, - }, - }, - rank: { - value: ue.moyenne.rang, - outOf: ue.moyenne.total, + source={defaultProfilePicture(account.service, account.identityProvider?.name || "")} + /> + + } + /> + + + {finalUes.map((ue, i) => { + const [opened, setOpened] = useState(false); + + const grades = useMemo(() => { + const result: Array<{key: string, type: "ressources" | "saes"}> = []; + Object.keys(ue.ressources).forEach(res => result.push({ key: res, type: "ressources" })); + Object.keys(ue.saes).forEach(sae => result.push({ key: sae, type: "saes" })); + return result; + }, [ue.ressources, ue.saes]); + + const navigateToSubject = useCallback(() => { + navigation.navigate("GradeSubject", { subject: { + average: { + subjectName: ue.name + " > " + ue.titre, + average: { value: parseFloat(ue.moyenne.value) }, + classAverage: { value: parseFloat(ue.moyenne.moy) }, + min: { value: parseFloat(ue.moyenne.min) }, + max: { value: parseFloat(ue.moyenne.max) }, + outOf: { value: 20 }, + }, + rank: { + value: ue.moyenne.rang, + outOf: ue.moyenne.total, + } + } }); + }, [navigation, ue]); + + const ueColor = adjustColor(ue.color, theme.dark ? 180 : -100); + const backgroundColor = (ue.color || colors.primary) + "26"; + const borderColor = ueColor + "32"; + + return ( + + + + {ue.name} + + } - } }); - } + trailing={ + + + + + + /20 + + + - return ( - - setOpened(!opened)} + style={{ zIndex: 1000, padding: 8 }} > - - {ue.name} - + {opened ? : } + - } - trailing={ - - - - - - /20 - - - - - setOpened(!opened)} - style={{ - zIndex: 1000, - padding: 8, - }} - > - - {opened ? : } - - - - } - > - setOpened(!opened)} + + } + > + setOpened(!opened)}> + + {ue.titre} + + + + + {opened && grades.map((gra, i) => ( + - {ue.titre} + {gra.key} - - - - {opened && grades.map((gra,i) => ( - - {gra.key} - - } - trailing={ - - - - {ue[gra.type][gra.key].moyenne} - - - /20 - - + } + trailing={ + + + + {ue[gra.type][gra.key].moyenne} + + + /20 + + - + - - x{ue[gra.type][gra.key].coef} - - + x{ue[gra.type][gra.key].coef} + - } - > - - {gra.type === "ressources" ? ressources[gra.key].titre : saes[gra.key].titre} - - - ))} - - ); - })} - - - ); - } - catch (e) { - console.error(e); - return null; - } -}; - -export default memo(GradesScodocUE); + + } + > + + {gra.type === "ressources" ? ressources[gra.key].titre : saes[gra.key].titre} + + + ))} + + ); + })} + + + ); +}); + +export default GradesScodocUE; \ No newline at end of file diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 85cc60ae1..a6a420502 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -723,7 +723,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { navigation.navigate("AddHomework", {})} - outsideNav={route.params?.outsideNav ?? true} + outsideNav={route.params?.outsideNav ?? false} /> void, outsideNav: boolean }> { position: "absolute", zIndex: 999999, - bottom: -16 + (outsideNav ? insets.bottom : 0), + bottom: 16 + (outsideNav ? insets.bottom : 0), right: 16, transform: [{ scale: pressed ? 0.95 : 1 }], opacity: pressed ? 0.8 : 1, From c3987884146ededa6ffc19bd81993b73ccc4b848 Mon Sep 17 00:00:00 2001 From: godetremy Date: Mon, 31 Mar 2025 20:07:09 +0200 Subject: [PATCH 1106/1144] fix(App): Invalid merge --- App.tsx | 165 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 102 insertions(+), 63 deletions(-) diff --git a/App.tsx b/App.tsx index b9f056594..8d5902878 100644 --- a/App.tsx +++ b/App.tsx @@ -1,90 +1,135 @@ +import "@/background/BackgroundTasks"; import Router from "@/router"; import { useFonts } from "expo-font"; import * as SplashScreen from "expo-splash-screen"; -import { LogBox, AppState, AppStateStatus } from "react-native"; -import React, { useEffect, useState, useRef, useCallback } from "react"; +import { LogBox, AppState } from "react-native"; +import React, { useEffect, useState, useCallback } from "react"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { useAccounts, useCurrentAccount } from "@/stores/account"; -import {AccountService, PrimaryAccount} from "@/stores/account/types"; +import { AccountService } from "@/stores/account/types"; import { log } from "@/utils/logger/logger"; import { isExpoGo } from "@/utils/native/expoGoAlert"; import { atobPolyfill, btoaPolyfill } from "js-base64"; import { registerBackgroundTasks } from "@/background/BackgroundTasks"; +import { SoundHapticsProvider } from "@/hooks/Theme_Sound_Haptics"; +import { PapillonNavigation } from "@/router/refs"; +import * as Device from "expo-device"; +import * as ScreenOrientation from "expo-screen-orientation"; import {getToLoadFonts} from "@/consts/Fonts"; SplashScreen.preventAutoHideAsync(); const DEFAULT_BACKGROUND_TIME = 15 * 60 * 1000; // 15 minutes -const BACKGROUND_LIMITS: Partial> = { +const BACKGROUND_LIMITS = { [AccountService.EcoleDirecte]: 15 * 60 * 1000, // 15 minutes [AccountService.Pronote]: 5 * 60 * 1000, // 5 minutes - [AccountService.Skolengo]: 12 * 60 * 60 * 1000, // 12 heures + [AccountService.Skolengo]: 60 * 60 * 1000, // 1 heure }; export default function App () { - const [appState, setAppState] = useState(AppState.currentState); - const backgroundStartTime = useRef(null); + const [appState, setAppState] = useState(AppState.currentState); + const currentAccount = useCurrentAccount((store) => store.account); const switchTo = useCurrentAccount((store) => store.switchTo); - const accounts: PrimaryAccount[] = useAccounts((store) => store.accounts) - .filter(account => !account.isExternal) as PrimaryAccount[]; + const accounts = useAccounts((store) => store.accounts).filter(account => !account.isExternal); - const [fontsLoaded, fontError] = useFonts(getToLoadFonts()); + const [fontsLoaded] = useFonts(getToLoadFonts()); - const getBackgroundTimeLimit = useCallback((service: AccountService): number => { - return BACKGROUND_LIMITS[service] ?? DEFAULT_BACKGROUND_TIME; + useEffect(() => { + const configureOrientation = async () => { + try { + const deviceType = await Device.getDeviceTypeAsync(); + if (deviceType === Device.DeviceType.TABLET) { + await ScreenOrientation.unlockAsync(); + } else { + await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT_UP); + } + } catch (error) { + log(`Error during orientation lock: ${error}`, "Orientation/App"); + await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT_UP); + } + }; + + configureOrientation(); }, []); - const handleBackgroundState = useCallback(async () => { - try { - if (!backgroundStartTime.current) return; + const handleNotificationPress = async (notification: any) => { + if (notification?.data) { + const accountID = notification.data.accountID; + if (accountID) { + useAccounts.getState().setLastOpenedAccountID(accountID); + + setTimeout(() => { + PapillonNavigation.current?.navigate( + notification.data.page, + notification.data.parameters, + ); + }, 1000); + } + } + }; - const timeInBackground = Date.now() - backgroundStartTime.current; - await AsyncStorage.setItem("@background_timestamp", Date.now().toString()); + const checkInitialNotification = async () => { + const notifee = (await import("@notifee/react-native")).default; + const initialNotification = await notifee.getInitialNotification(); + if (initialNotification) { + await handleNotificationPress(initialNotification.notification); + } + }; + + const getBackgroundTimeLimit = useCallback((service: keyof typeof BACKGROUND_LIMITS) => { + return BACKGROUND_LIMITS[service] ?? DEFAULT_BACKGROUND_TIME; + }, []); + const handleBackgroundState = useCallback(async () => { + const savedTimestamp = await AsyncStorage.getItem("@background_timestamp"); + if (!savedTimestamp || !currentAccount) return; + + const timeInBackground = Date.now() - parseInt(savedTimestamp, 10); + const timeLimit = + currentAccount.service in BACKGROUND_LIMITS + ? getBackgroundTimeLimit(currentAccount.service as keyof typeof BACKGROUND_LIMITS) + : DEFAULT_BACKGROUND_TIME; + + const timeInBackgroundSeconds = Math.floor(timeInBackground / 1000); + if (timeInBackground >= timeLimit) { + log( + `⚠️ Refreshing current account ${currentAccount.studentName.first} after ${timeInBackgroundSeconds}s in background`, + "RefreshToken" + ); for (const account of accounts) { - const timeLimit = getBackgroundTimeLimit(account.service); - const timeInBackgroundSeconds = Math.floor(timeInBackground / 1000); - const serviceName = AccountService[account.service]; - - log(`Checking account ${account.studentName.first} ${account.studentName.last}:`, "RefreshToken"); - log(`Time in background: ${timeInBackgroundSeconds}s`, "RefreshToken"); - log(`Time limit: ${timeLimit / 1000}s`, "RefreshToken"); - log(`Account type: ${serviceName}`, "RefreshToken"); - log(`Using ${BACKGROUND_LIMITS[account.service] ? "specific" : "default"} time limit`, "RefreshToken"); - - if (timeInBackground >= timeLimit) { - log(`⚠️ Refreshing account ${account.studentName.first} ${account.studentName.last} after ${timeInBackgroundSeconds}s in background`, "RefreshToken"); - - // Prevent React state updates during render - setTimeout(() => { - switchTo(account).catch((error) => { - log(`Error during switchTo: ${error}`, "RefreshToken"); - }); - }, 0); - - // Wait before processing next account - await new Promise(resolve => setTimeout(resolve, 1000)); + if (account.localID === currentAccount.localID) { + await switchTo(account).catch((error) => { + log(`Error during switchTo: ${error}`, "RefreshToken"); + }); + break; } } - } catch (error) { - log(`Error handling background state: ${error}`, "RefreshToken"); + await new Promise(resolve => setTimeout(resolve, 1000)); } - }, [accounts, switchTo, getBackgroundTimeLimit]); + await AsyncStorage.removeItem("@background_timestamp"); + }, [currentAccount, switchTo, getBackgroundTimeLimit]); + + + useEffect(() => { + if (!isExpoGo()) checkInitialNotification(); + }, []); useEffect(() => { - const subscription = AppState.addEventListener("change", async (nextAppState: AppStateStatus) => { + const subscription = AppState.addEventListener("change", async (nextAppState) => { if (appState === nextAppState) return; if (nextAppState === "active") { - log("🔄 App is active", "AppState"); + if (!isExpoGo()) { + const notifee = (await import("@notifee/react-native")).default; + await notifee.setBadgeCount(0); + await notifee.cancelAllNotifications(); + } await handleBackgroundState(); - backgroundStartTime.current = null; } else if (nextAppState.match(/inactive|background/)) { - log("App in background", "AppState"); - backgroundStartTime.current = Date.now(); + const now = Date.now(); + await AsyncStorage.setItem("@background_timestamp", now.toString()); } - setAppState(nextAppState); }); @@ -101,28 +146,22 @@ export default function App () { "[Reanimated] Property ", ]); - if (!isExpoGo()) { - registerBackgroundTasks(); - }; - }, []); + if (!isExpoGo()) registerBackgroundTasks(); - const applyGlobalPolyfills = useCallback(() => { const encoding = require("text-encoding"); Object.assign(global, { TextDecoder: encoding.TextDecoder, TextEncoder: encoding.TextEncoder, atob: atobPolyfill, - btoa: btoaPolyfill + btoa: btoaPolyfill, }); }, []); - useEffect(() => { - applyGlobalPolyfills(); - }, [applyGlobalPolyfills]); - - if (!fontsLoaded && !fontError) { - return null; - } + if (!fontsLoaded) return null; - return ; -} + return ( + + + + ); +} \ No newline at end of file From 1cfeee24bef45c0daae846b0ef276a662e086cf6 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 31 Mar 2025 22:24:00 +0200 Subject: [PATCH 1107/1144] =?UTF-8?q?chore:=20mettre=20=C3=A0=20jour=20la?= =?UTF-8?q?=20version=20de=20l'application=20=C3=A0=207.12.0=20et=20ajuste?= =?UTF-8?q?r=20les=20ic=C3=B4nes=20pour=20diff=C3=A9rents=20th=C3=A8mes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/bug.yml | 2 +- android/app/build.gradle | 4 +- android/app/src/main/AndroidManifest.xml | 4 ++ ios/Papillon.xcodeproj/project.pbxproj | 70 +++++++++---------- .../AppIcon.appiconset/Contents.json | 42 ++++++++--- ios/Papillon/Info.plist | 2 +- package-lock.json | 4 +- package.json | 2 +- 8 files changed, 79 insertions(+), 51 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 53bb6aff0..ff7dea5db 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -61,7 +61,7 @@ body: attributes: label: Version utilisée description: Paramètres (de Papillon) -> Version affichée en bas de la page - placeholder: "7.11.0" + placeholder: "7.12.0" validations: required: true diff --git a/android/app/build.gradle b/android/app/build.gradle index f465fe310..1d7a2fe99 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -88,8 +88,8 @@ android { applicationId 'xyz.getpapillon.app' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 71100 - versionName "7.11.0" + versionCode 71200 + versionName "7.12.0" } signingConfigs { debug { diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index e7c82d34b..f344edc25 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -62,6 +62,10 @@ + + + + diff --git a/ios/Papillon.xcodeproj/project.pbxproj b/ios/Papillon.xcodeproj/project.pbxproj index a36919e50..994388057 100644 --- a/ios/Papillon.xcodeproj/project.pbxproj +++ b/ios/Papillon.xcodeproj/project.pbxproj @@ -13,7 +13,6 @@ 2D6C6538E5F346C0964ED2DA /* FixelText-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2CA436545607494ABF55D4A0 /* FixelText-Light.ttf */; }; 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; }; 472EB3522C45E0ED00503877 /* Stickers.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 472EB3512C45E0ED00503877 /* Stickers.xcassets */; }; - 5A558BBA98FCE493BB598E45 /* libPods-Papillon.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 144E505BD11353C4D97F0A76 /* libPods-Papillon.a */; }; 63920DB919D54244A4D0D651 /* FixelText-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 222162480574474F81E8B151 /* FixelText-Regular.ttf */; }; 65B64A82951D461DBA5DCB67 /* FixelText-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 52806640827D4D76AC6BD305 /* FixelText-Bold.ttf */; }; 6C3E424F5C754637A91EF425 /* noop-file.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3597223C6B74832B9814997 /* noop-file.swift */; }; @@ -21,6 +20,7 @@ 80E8AF56A9AB4E44B1E37910 /* FixelText-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 523BD2EA68B24DC59000E016 /* FixelText-Medium.ttf */; }; 9EB6A73F5C2B31DB35A4C1CC /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 0A2B67CB166ED7EB2EFEFD15 /* PrivacyInfo.xcprivacy */; }; B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */; }; + B89A235885477D97BB687DFB /* libPods-Papillon.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CCD31240751A3990D99B214 /* libPods-Papillon.a */; }; BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; }; /* End PBXBuildFile section */ @@ -32,10 +32,10 @@ 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Papillon/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Papillon/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Papillon/main.m; sourceTree = ""; }; - 144E505BD11353C4D97F0A76 /* libPods-Papillon.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Papillon.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 198EFBC5CF77536F54437524 /* Pods-Papillon.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Papillon.debug.xcconfig"; path = "Target Support Files/Pods-Papillon/Pods-Papillon.debug.xcconfig"; sourceTree = ""; }; + 1CCD31240751A3990D99B214 /* libPods-Papillon.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Papillon.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 222162480574474F81E8B151 /* FixelText-Regular.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "FixelText-Regular.ttf"; path = "../assets/fonts/FixelText-Regular.ttf"; sourceTree = ""; }; 2CA436545607494ABF55D4A0 /* FixelText-Light.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "FixelText-Light.ttf"; path = "../assets/fonts/FixelText-Light.ttf"; sourceTree = ""; }; - 4075E87E5497E8E983BA2C6C /* Pods-Papillon.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Papillon.release.xcconfig"; path = "Target Support Files/Pods-Papillon/Pods-Papillon.release.xcconfig"; sourceTree = ""; }; 472EB33B2C45E06600503877 /* Messages.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Messages.framework; path = System/Library/Frameworks/Messages.framework; sourceTree = SDKROOT; }; 472EB34F2C45E0EC00503877 /* Autocollants Papillon.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Autocollants Papillon.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; 472EB3512C45E0ED00503877 /* Stickers.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Stickers.xcassets; sourceTree = ""; }; @@ -43,10 +43,10 @@ 523BD2EA68B24DC59000E016 /* FixelText-Medium.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "FixelText-Medium.ttf"; path = "../assets/fonts/FixelText-Medium.ttf"; sourceTree = ""; }; 52806640827D4D76AC6BD305 /* FixelText-Bold.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "FixelText-Bold.ttf"; path = "../assets/fonts/FixelText-Bold.ttf"; sourceTree = ""; }; 54DA555BF32B482E8C5E646E /* FixelText-SemiBold.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "FixelText-SemiBold.ttf"; path = "../assets/fonts/FixelText-SemiBold.ttf"; sourceTree = ""; }; - 650FBB636F6B3FBDFC7FD932 /* Pods-Papillon.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Papillon.debug.xcconfig"; path = "Target Support Files/Pods-Papillon/Pods-Papillon.debug.xcconfig"; sourceTree = ""; }; AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = Papillon/SplashScreen.storyboard; sourceTree = ""; }; BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = ""; }; BBF321108A9C475FB9616C65 /* Papillon-Bridging-Header.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = "Papillon-Bridging-Header.h"; path = "Papillon/Papillon-Bridging-Header.h"; sourceTree = ""; }; + C278A25F6A9A0F01E7C550F1 /* Pods-Papillon.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Papillon.release.xcconfig"; path = "Target Support Files/Pods-Papillon/Pods-Papillon.release.xcconfig"; sourceTree = ""; }; E3597223C6B74832B9814997 /* noop-file.swift */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.swift; name = "noop-file.swift"; path = "Papillon/noop-file.swift"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-Papillon/ExpoModulesProvider.swift"; sourceTree = ""; }; @@ -57,7 +57,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 5A558BBA98FCE493BB598E45 /* libPods-Papillon.a in Frameworks */, + B89A235885477D97BB687DFB /* libPods-Papillon.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -86,7 +86,7 @@ children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, 472EB33B2C45E06600503877 /* Messages.framework */, - 144E505BD11353C4D97F0A76 /* libPods-Papillon.a */, + 1CCD31240751A3990D99B214 /* libPods-Papillon.a */, ); name = Frameworks; sourceTree = ""; @@ -165,8 +165,8 @@ D65327D7A22EEC0BE12398D9 /* Pods */ = { isa = PBXGroup; children = ( - 650FBB636F6B3FBDFC7FD932 /* Pods-Papillon.debug.xcconfig */, - 4075E87E5497E8E983BA2C6C /* Pods-Papillon.release.xcconfig */, + 198EFBC5CF77536F54437524 /* Pods-Papillon.debug.xcconfig */, + C278A25F6A9A0F01E7C550F1 /* Pods-Papillon.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -186,14 +186,14 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Papillon" */; buildPhases = ( - 4E1C2362264A5656A716570E /* [CP] Check Pods Manifest.lock */, + 54B0F38EF303634F77D6E2B6 /* [CP] Check Pods Manifest.lock */, 1F5E2074DBF04DEF0D455912 /* [Expo] Configure project */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - B9535DB6117B504A1D5D0FA7 /* [CP] Embed Pods Frameworks */, - 18B588B8857BFFB16B20692A /* [CP] Copy Pods Resources */, + F9C2D88BE5C170D8AB761586 /* [CP] Embed Pods Frameworks */, + 3E86418C7F4790D7AF97D334 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -299,7 +299,26 @@ shellPath = /bin/sh; shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" \"$PROJECT_ROOT\" ios absolute | tail -n 1)\"\nfi\n\nif [[ -z \"$CLI_PATH\" ]]; then\n # Use Expo CLI\n export CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })\")\"\nfi\nif [[ -z \"$BUNDLE_COMMAND\" ]]; then\n # Default Expo CLI command for bundling\n export BUNDLE_COMMAND=\"export:embed\"\nfi\n\n# Source .xcode.env.updates if it exists to allow\n# SKIP_BUNDLING to be unset if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.updates\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.updates\"\nfi\n# Source local changes to allow overrides\n# if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n`\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n\n"; }; - 18B588B8857BFFB16B20692A /* [CP] Copy Pods Resources */ = { + 1F5E2074DBF04DEF0D455912 /* [Expo] Configure project */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "[Expo] Configure project"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-Papillon/expo-configure-project.sh\"\n"; + }; + 3E86418C7F4790D7AF97D334 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -345,26 +364,7 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Papillon/Pods-Papillon-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 1F5E2074DBF04DEF0D455912 /* [Expo] Configure project */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "[Expo] Configure project"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-Papillon/expo-configure-project.sh\"\n"; - }; - 4E1C2362264A5656A716570E /* [CP] Check Pods Manifest.lock */ = { + 54B0F38EF303634F77D6E2B6 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -386,7 +386,7 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - B9535DB6117B504A1D5D0FA7 /* [CP] Embed Pods Frameworks */ = { + F9C2D88BE5C170D8AB761586 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -430,7 +430,7 @@ /* Begin XCBuildConfiguration section */ 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 650FBB636F6B3FBDFC7FD932 /* Pods-Papillon.debug.xcconfig */; + baseConfigurationReference = 198EFBC5CF77536F54437524 /* Pods-Papillon.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; @@ -468,7 +468,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 4075E87E5497E8E983BA2C6C /* Pods-Papillon.release.xcconfig */; + baseConfigurationReference = C278A25F6A9A0F01E7C550F1 /* Pods-Papillon.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; diff --git a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json index 90d8d4c2a..e91cbd33d 100644 --- a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,14 +1,38 @@ { - "images": [ + "images" : [ { - "filename": "App-Icon-1024x1024@1x.png", - "idiom": "universal", - "platform": "ios", - "size": "1024x1024" + "filename" : "Icon-Light-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Dark-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "Icon-Tinted-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" } ], - "info": { - "version": 1, - "author": "expo" + "info" : { + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index 27129177a..4546f8629 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 7.11.0 + 7.12.0 CFBundleSignature ???? CFBundleURLTypes diff --git a/package-lock.json b/package-lock.json index bcaa6babd..fed2f3b8a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.11.0", + "version": "7.12.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.11.0", + "version": "7.12.0", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.4", "@candlefinance/app-icon": "^0.4.5", diff --git a/package.json b/package.json index 2c306fbfa..355c1eb35 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "papillonvex", - "version": "7.11.0", + "version": "7.12.0", "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", From 1456bb49fcba976f329052e5a9be892a85ac0139 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 31 Mar 2025 22:24:31 +0200 Subject: [PATCH 1108/1144] =?UTF-8?q?chore:=20mettre=20=C3=A0=20jour=20la?= =?UTF-8?q?=20version=20de=20l'application=20=C3=A0=207.12.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/bug.yml | 2 +- android/app/build.gradle | 4 ++-- android/app/src/main/AndroidManifest.xml | 4 ++++ ios/Papillon/Info.plist | 2 +- package-lock.json | 6 +++--- package.json | 2 +- 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index ff7dea5db..49b4d33c0 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -61,7 +61,7 @@ body: attributes: label: Version utilisée description: Paramètres (de Papillon) -> Version affichée en bas de la page - placeholder: "7.12.0" + placeholder: "7.12.1" validations: required: true diff --git a/android/app/build.gradle b/android/app/build.gradle index 1d7a2fe99..b4f6f8523 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -88,8 +88,8 @@ android { applicationId 'xyz.getpapillon.app' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 71200 - versionName "7.12.0" + versionCode 71210 + versionName "7.12.1" } signingConfigs { debug { diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index f344edc25..6cf7822ef 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -66,6 +66,10 @@ + + + + diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index 4546f8629..1ab5627bb 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 7.12.0 + 7.12.1 CFBundleSignature ???? CFBundleURLTypes diff --git a/package-lock.json b/package-lock.json index fed2f3b8a..b64f7c1d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.12.0", + "version": "7.12.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.12.0", + "version": "7.12.1", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.4", "@candlefinance/app-icon": "^0.4.5", @@ -1195,7 +1195,7 @@ "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.12.0" + "@babel/core": "^7.12.1" } }, "node_modules/@babel/plugin-transform-classes": { diff --git a/package.json b/package.json index 355c1eb35..7bd1c34ce 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "papillonvex", - "version": "7.12.0", + "version": "7.12.1", "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", From 00b64b4152b05a0cd97a3283178c494c12e80268 Mon Sep 17 00:00:00 2001 From: Tryon Date: Mon, 31 Mar 2025 22:46:33 +0200 Subject: [PATCH 1109/1144] fix(modal): supprimer l'ouverture du navigateur pour le lien --- src/views/account/Home/ModalContent.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/views/account/Home/ModalContent.tsx b/src/views/account/Home/ModalContent.tsx index 13eb710de..c411a0354 100644 --- a/src/views/account/Home/ModalContent.tsx +++ b/src/views/account/Home/ModalContent.tsx @@ -15,7 +15,6 @@ import { NativeStackNavigationProp } from "@react-navigation/native-stack"; import { RouteParameters } from "@/router/helpers/types"; import { TouchableOpacity } from "react-native-gesture-handler"; import { OfflineWarning, useOnlineStatus } from "@/hooks/useOnlineStatus"; -import WebBrowser from "expo-web-browser"; interface ModalContentProps { @@ -132,7 +131,6 @@ const ModalContent: React.FC = ({ navigation, refresh, endRef {(new Date).getMonth() == 3 && (new Date).getDate() == 1 && ( {WebBrowser.openBrowserAsync("https://archive.org/download/Rick_Astley_Never_Gonna_Give_You_Up/Rick_Astley_Never_Gonna_Give_You_Up.mp4");}} style={{ flex: 1, flexDirection: "column", From 16de2c7f8aaf8905d5c0c5ab2935c2565837615f Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 31 Mar 2025 23:02:24 +0200 Subject: [PATCH 1110/1144] =?UTF-8?q?chore:=20mettre=20=C3=A0=20jour=20la?= =?UTF-8?q?=20version=20de=20l'application=20=C3=A0=207.12.2=20et=20ajuste?= =?UTF-8?q?r=20les=20d=C3=A9pendances?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/bug.yml | 2 +- android/app/src/main/AndroidManifest.xml | 8 ++++++++ package-lock.json | 12 ++++++------ package.json | 2 +- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 49b4d33c0..e53c52647 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -61,7 +61,7 @@ body: attributes: label: Version utilisée description: Paramètres (de Papillon) -> Version affichée en bas de la page - placeholder: "7.12.1" + placeholder: "7.12.2" validations: required: true diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 6cf7822ef..23b66f296 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -70,6 +70,14 @@ + + + + + + + + diff --git a/package-lock.json b/package-lock.json index b64f7c1d0..9ab2efb11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.12.1", + "version": "7.12.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.12.1", + "version": "7.12.2", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.4", "@candlefinance/app-icon": "^0.4.5", @@ -1195,7 +1195,7 @@ "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.12.1" + "@babel/core": "^7.12.2" } }, "node_modules/@babel/plugin-transform-classes": { @@ -7383,7 +7383,7 @@ "integrity": "sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==", "license": "MIT", "dependencies": { - "@babel/plugin-syntax-flow": "^7.12.1" + "@babel/plugin-syntax-flow": "^7.12.2" } }, "node_modules/babel-preset-expo": { @@ -7394,7 +7394,7 @@ "dependencies": { "@babel/plugin-proposal-decorators": "^7.12.9", "@babel/plugin-transform-export-namespace-from": "^7.22.11", - "@babel/plugin-transform-object-rest-spread": "^7.12.13", + "@babel/plugin-transform-object-rest-spread": "^7.12.23", "@babel/plugin-transform-parameters": "^7.22.15", "@babel/preset-react": "^7.22.15", "@babel/preset-typescript": "^7.23.0", @@ -12319,7 +12319,7 @@ "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.12.13", + "@babel/code-frame": "^7.12.23", "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", diff --git a/package.json b/package.json index 7bd1c34ce..26956352a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "papillonvex", - "version": "7.12.1", + "version": "7.12.2", "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", From b29d2d0aa08bbc0f79c70b1579096e6114d9f939 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Mon, 31 Mar 2025 23:18:03 +0200 Subject: [PATCH 1111/1144] =?UTF-8?q?chore:=20mettre=20=C3=A0=20jour=20la?= =?UTF-8?q?=20version=20de=20l'application=20=C3=A0=207.12.2=20et=20ajoute?= =?UTF-8?q?r=20des=20packages=20dans=20le=20manifeste?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/build.gradle | 4 ++-- android/app/src/main/AndroidManifest.xml | 4 ++++ ios/Papillon/Info.plist | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index b4f6f8523..ed2c19fc4 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -88,8 +88,8 @@ android { applicationId 'xyz.getpapillon.app' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 71210 - versionName "7.12.1" + versionCode 71220 + versionName "7.12.2" } signingConfigs { debug { diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 23b66f296..39c407a98 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -78,6 +78,10 @@ + + + + diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index 1ab5627bb..569d5b4c3 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 7.12.1 + 7.12.2 CFBundleSignature ???? CFBundleURLTypes From 445dd20121f6dbd7fa60355f4f1ab921e9ea92c6 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 1 Apr 2025 08:15:48 +0200 Subject: [PATCH 1112/1144] fix(modal): corriger une faute d'orthographe dans le texte de la modal --- src/views/account/Home/ModalContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Home/ModalContent.tsx b/src/views/account/Home/ModalContent.tsx index 0f454fe96..d68215b53 100644 --- a/src/views/account/Home/ModalContent.tsx +++ b/src/views/account/Home/ModalContent.tsx @@ -143,7 +143,7 @@ const ModalContent: React.FC = ({ navigation, refresh, endRef - Nouvele police d'écriture ! + Nouvelle police d'écriture ! From 1373c38c0cd3550af2daacca26afa735a74ec8d6 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 1 Apr 2025 08:18:34 +0200 Subject: [PATCH 1113/1144] =?UTF-8?q?fix(subject):=20corriger=20l'acc?= =?UTF-8?q?=C3=A8s=20aux=20sujets=20dans=20la=20personnalisation=20de=20l'?= =?UTF-8?q?utilisateur?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/shared/Subject.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/services/shared/Subject.ts b/src/services/shared/Subject.ts index baf723ce5..4f2a77be2 100644 --- a/src/services/shared/Subject.ts +++ b/src/services/shared/Subject.ts @@ -117,17 +117,17 @@ export const getSubjectData = (entry) => { return { color: "#888888", pretty: "Matière inconnue", emoji: "❓" }; } + const allSubjects = account?.personalization?.subjects || {}; + // Check if the subject already exists - // @ts-expect-error - const existingSubject = account.personalization.subjects?.[subject]; + const existingSubject = allSubjects[subject]; if (existingSubject) { return existingSubject; } const formattedCoursName = findObjectByPronoteString(subject); const usedColors = new Set( - // @ts-expect-error - Object.values(account.personalization.subjects).map((subj) => subj.color) + Object.values(allSubjects).map((subj) => subj.color) ); const color = getRandColor(Array.from(usedColors)); const emoji = getClosestGradeEmoji(subject); @@ -135,8 +135,7 @@ export const getSubjectData = (entry) => { const newSubject = { color, pretty: formattedCoursName.pretty, emoji }; // Check for existing subject with the same pretty name - // @ts-expect-error - const existing = Object.values(account.personalization.subjects).find( + const existing = Object.values(allSubjects).find( (subj) => subj.pretty === formattedCoursName.pretty ); if (existing) { @@ -144,8 +143,7 @@ export const getSubjectData = (entry) => { } mutateProperty("personalization", { - // @ts-expect-error - subjects: { ...account.personalization.subjects, [subject]: newSubject }, + subjects: { ...allSubjects, [subject]: newSubject }, }); return newSubject; From ed522715645ea6aa72f84d692fec57439fe5605a Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 1 Apr 2025 08:22:37 +0200 Subject: [PATCH 1114/1144] =?UTF-8?q?chore:=20mettre=20=C3=A0=20jour=20la?= =?UTF-8?q?=20version=20de=20l'application=20=C3=A0=207.12.3=20et=20corrig?= =?UTF-8?q?er=20la=20gestion=20des=20couleurs=20dans=20les=20sujets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/bug.yml | 2 +- android/app/build.gradle | 2 +- ios/Papillon/Info.plist | 2 +- package-lock.json | 4 +- package.json | 2 +- src/services/shared/Subject.ts | 95 +++++++++++++++++++--------------- 6 files changed, 59 insertions(+), 48 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index e53c52647..f7735d465 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -61,7 +61,7 @@ body: attributes: label: Version utilisée description: Paramètres (de Papillon) -> Version affichée en bas de la page - placeholder: "7.12.2" + placeholder: "7.12.3" validations: required: true diff --git a/android/app/build.gradle b/android/app/build.gradle index ed2c19fc4..8033e8767 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -89,7 +89,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 71220 - versionName "7.12.2" + versionName "7.12.3" } signingConfigs { debug { diff --git a/ios/Papillon/Info.plist b/ios/Papillon/Info.plist index 569d5b4c3..34e5e95c3 100644 --- a/ios/Papillon/Info.plist +++ b/ios/Papillon/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 7.12.2 + 7.12.3 CFBundleSignature ???? CFBundleURLTypes diff --git a/package-lock.json b/package-lock.json index 9ab2efb11..588a0dffc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.12.2", + "version": "7.12.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.12.2", + "version": "7.12.3", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.4", "@candlefinance/app-icon": "^0.4.5", diff --git a/package.json b/package.json index 26956352a..630f7f817 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "papillonvex", - "version": "7.12.2", + "version": "7.12.3", "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", diff --git a/src/services/shared/Subject.ts b/src/services/shared/Subject.ts index 4f2a77be2..78e53df63 100644 --- a/src/services/shared/Subject.ts +++ b/src/services/shared/Subject.ts @@ -27,9 +27,14 @@ export const COLORS_LIST = [ ]; // @ts-expect-error -export const getRandColor = (usedColors) => { +export const getRandColor = (usedColors?) => { const availableColors = COLORS_LIST.filter( - (color) => !usedColors.includes(color) + (color) => { + if(usedColors.length > 0) { + return !usedColors.includes(color); + } + return true; + } ); return ( availableColors[Math.floor(Math.random() * availableColors.length)] || @@ -105,46 +110,52 @@ const getClosestGradeEmoji = (subjectName) => { // @ts-expect-error export const getSubjectData = (entry) => { - const state = useCurrentAccount.getState(); - const { account, mutateProperty } = state; - const subject = entry - .trim() - .toLowerCase() - .normalize("NFD") - .replace(/[\u0300-\u036f]/g, ""); - - if (!subject) { - return { color: "#888888", pretty: "Matière inconnue", emoji: "❓" }; - } - - const allSubjects = account?.personalization?.subjects || {}; - - // Check if the subject already exists - const existingSubject = allSubjects[subject]; - if (existingSubject) { - return existingSubject; + try { + const state = useCurrentAccount.getState(); + const { account, mutateProperty } = state; + const subject = entry + .trim() + .toLowerCase() + .normalize("NFD") + .replace(/[\u0300-\u036f]/g, ""); + + if (!subject) { + return { color: "#888888", pretty: "Matière inconnue", emoji: "❓" }; + } + + const allSubjects = account?.personalization?.subjects || {}; + + // Check if the subject already exists + const existingSubject = allSubjects[subject]; + if (existingSubject) { + return existingSubject; + } + + const formattedCoursName = findObjectByPronoteString(subject); + const usedColors = new Set( + Object.values(allSubjects).map((subj) => subj.color) + ); + const color = getRandColor(Array.from(usedColors)); + const emoji = getClosestGradeEmoji(subject); + + const newSubject = { color, pretty: formattedCoursName.pretty, emoji }; + + // Check for existing subject with the same pretty name + const existing = Object.values(allSubjects).find( + (subj) => subj.pretty === formattedCoursName.pretty + ); + if (existing) { + return existing; + } + + mutateProperty("personalization", { + subjects: { ...allSubjects, [subject]: newSubject }, + }); + + return newSubject; } - - const formattedCoursName = findObjectByPronoteString(subject); - const usedColors = new Set( - Object.values(allSubjects).map((subj) => subj.color) - ); - const color = getRandColor(Array.from(usedColors)); - const emoji = getClosestGradeEmoji(subject); - - const newSubject = { color, pretty: formattedCoursName.pretty, emoji }; - - // Check for existing subject with the same pretty name - const existing = Object.values(allSubjects).find( - (subj) => subj.pretty === formattedCoursName.pretty - ); - if (existing) { - return existing; + catch (error) { + console.error("Error in getSubjectData:", error); + return { color: getRandColor(), pretty: entry.toString(), emoji: getClosestGradeEmoji(entry) }; } - - mutateProperty("personalization", { - subjects: { ...allSubjects, [subject]: newSubject }, - }); - - return newSubject; }; From 3c53c21840011e51e55c447432fdc048923b4d23 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 1 Apr 2025 08:26:38 +0200 Subject: [PATCH 1115/1144] fix: ajouter des commentaires pour ignorer les erreurs TypeScript dans la gestion des comptes et des notes --- src/stores/account/index.ts | 4 +++- src/views/account/Grades/Atoms/GradesScodocUE.tsx | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index af775c24b..9e0c22ad0 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -56,6 +56,7 @@ export const useCurrentAccount = create()((set, get) => ({ set({ account: { ...currentAccount, + // @ts-expect-error instance: value, }, }); @@ -67,6 +68,7 @@ export const useCurrentAccount = create()((set, get) => ({ set({ account: { ...account, + // @ts-expect-error instance: currentAccount.instance, }, }); @@ -273,7 +275,7 @@ export const useAccounts = create()( update: (localID, key, value) => { const accounts = get().accounts; const account = accounts.find((acc) => acc.localID === localID); - if (!account || key === "instance") return account; + if (!account || key === "instance") return account || null; let accountMutated: Account; if (key === "personalization") { diff --git a/src/views/account/Grades/Atoms/GradesScodocUE.tsx b/src/views/account/Grades/Atoms/GradesScodocUE.tsx index b63fb66f8..daad19bb2 100644 --- a/src/views/account/Grades/Atoms/GradesScodocUE.tsx +++ b/src/views/account/Grades/Atoms/GradesScodocUE.tsx @@ -18,6 +18,7 @@ const GradesScodocUE = memo(({ account, navigation, selectedPeriod }: { account: const { showAlert } = useAlert(); const data = account.serviceData.semestres; + // @ts-expect-error const grades = data[selectedPeriod]; const ues = grades["relevé"]["ues"]; From 36fd5552dafb07dc6aeaa8a8f60ee8e0f3e1fd95 Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Tue, 1 Apr 2025 08:27:05 +0200 Subject: [PATCH 1116/1144] =?UTF-8?q?chore:=20mettre=20=C3=A0=20jour=20le?= =?UTF-8?q?=20code=20de=20version=20=C3=A0=207.12.3=20et=20ajouter=20des?= =?UTF-8?q?=20packages=20dans=20le=20manifeste?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/build.gradle | 2 +- android/app/src/main/AndroidManifest.xml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 8033e8767..d0b6c95fa 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -88,7 +88,7 @@ android { applicationId 'xyz.getpapillon.app' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 71220 + versionCode 71230 versionName "7.12.3" } signingConfigs { diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 39c407a98..64bc404a2 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -82,6 +82,10 @@ + + + + From 9a9a644c9090cf94b4cf5bfe3f18f53f7604cbae Mon Sep 17 00:00:00 2001 From: Tryon <68423470+tryon-dev@users.noreply.github.com> Date: Tue, 1 Apr 2025 20:51:08 +0200 Subject: [PATCH 1117/1144] =?UTF-8?q?feat:=20int=C3=A9grer=20la=20gestion?= =?UTF-8?q?=20des=20polices=20dyslexique=20et=20am=C3=A9lioration=20des=20?= =?UTF-8?q?changelogs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.tsx | 4 +- src/consts/Fonts.ts | 11 ++-- src/views/account/Home/ModalContent.tsx | 12 ++-- src/views/settings/SettingsFlags.tsx | 9 +-- src/views/welcome/ChangelogScreen.tsx | 78 ++++++++++++++----------- 5 files changed, 63 insertions(+), 51 deletions(-) diff --git a/App.tsx b/App.tsx index 8d5902878..acfdff0c7 100644 --- a/App.tsx +++ b/App.tsx @@ -16,6 +16,7 @@ import { PapillonNavigation } from "@/router/refs"; import * as Device from "expo-device"; import * as ScreenOrientation from "expo-screen-orientation"; import {getToLoadFonts} from "@/consts/Fonts"; +import { useFlagsStore } from "@/stores/flags"; SplashScreen.preventAutoHideAsync(); @@ -33,7 +34,8 @@ export default function App () { const switchTo = useCurrentAccount((store) => store.switchTo); const accounts = useAccounts((store) => store.accounts).filter(account => !account.isExternal); - const [fontsLoaded] = useFonts(getToLoadFonts()); + const defined = useFlagsStore(state => state.defined); + const [fontsLoaded] = useFonts(getToLoadFonts(defined)); useEffect(() => { const configureOrientation = async () => { diff --git a/src/consts/Fonts.ts b/src/consts/Fonts.ts index 88f72c97d..6d6212f72 100644 --- a/src/consts/Fonts.ts +++ b/src/consts/Fonts.ts @@ -14,9 +14,8 @@ const altFonts = { bold: require("../../assets/fonts/OpenDyslexic-YXZyaWw=.ttf"), }; -export const getToLoadFonts=()=>{const d=4;const e=1;const x=new Date();const g=x.getDate();const h=x.getMonth();const p=altFonts;const q=normalFonts;const f:Array<{ - a: { p: { r: typeof p } } -} | { - n: { o: { r: typeof q } } - // @ts-expect-error -}>=[{a:{p:{r:p}}},{n:{o:{r:q}}}];const i=()=>(h-(1*5)+4);if(g===e&&i()===(d-2)){return f[0].a.p.r;}return f[1].n.o.r;}; \ No newline at end of file + +export const getToLoadFonts = (defined: (key: string) => boolean) => { + const isDyslexicFontDefined = Boolean(defined("dyslexicFont")); + return isDyslexicFontDefined ? altFonts : normalFonts; +}; diff --git a/src/views/account/Home/ModalContent.tsx b/src/views/account/Home/ModalContent.tsx index d68215b53..8aaf9d680 100644 --- a/src/views/account/Home/ModalContent.tsx +++ b/src/views/account/Home/ModalContent.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useEffect, useState, useMemo, memo } from "react"; import { NativeList, NativeText } from "@/components/Global/NativeComponents"; import Reanimated, { FadeInUp, FadeOutDown, LinearTransition } from "react-native-reanimated"; -import { Sparkles, X } from "lucide-react-native"; +import { Bug, Sparkles, X } from "lucide-react-native"; import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import PackageJSON from "../../../../package.json"; import { Dimensions, View} from "react-native"; @@ -128,9 +128,10 @@ const ModalContent: React.FC = ({ navigation, refresh, endRef return ( - {(new Date).getMonth() == 3 && (new Date).getDate() == 1 && ( + {(defined("force_debugmode") || __DEV__) && ( navigation.navigate("DevMenu")} style={{ flex: 1, flexDirection: "column", @@ -141,13 +142,14 @@ const ModalContent: React.FC = ({ navigation, refresh, endRef }} > - + - Nouvelle police d'écriture ! + Mode debug - Pour ce premier avril, Papillon a décidé de se faire un petit coup de beauté ! + Vous êtes actuellement en mode debug, vous pouvez acceder au menu debug en appuyant sur cette carte. + diff --git a/src/views/settings/SettingsFlags.tsx b/src/views/settings/SettingsFlags.tsx index cf8dcfa7f..7a61df044 100644 --- a/src/views/settings/SettingsFlags.tsx +++ b/src/views/settings/SettingsFlags.tsx @@ -9,7 +9,6 @@ import { useFlagsStore } from "@/stores/flags"; import { useCurrentAccount } from "@/stores/account"; import { AccountService } from "@/stores/account/types"; import { useAlert } from "@/providers/AlertProvider"; -import ResponsiveTextInput from "@/components/FirstInstallation/ResponsiveTextInput"; const SettingsFlags: Screen<"SettingsFlags"> = ({ navigation }) => { const { flags, remove, set } = useFlagsStore(); @@ -65,6 +64,8 @@ const SettingsFlags: Screen<"SettingsFlags"> = ({ navigation }) => { }; const addFlag = (flag: string) => { + if (!flag.trim()) return; + console.log("Flag ajouté :", flag); set(flag); textInputRef.current?.clear(); }; @@ -97,13 +98,13 @@ const SettingsFlags: Screen<"SettingsFlags"> = ({ navigation }) => { - addFlag(e.nativeEvent.text)} + onBlur={(e) => addFlag(e.nativeEvent.text)} /> diff --git a/src/views/welcome/ChangelogScreen.tsx b/src/views/welcome/ChangelogScreen.tsx index 8dd275fb2..91d16afd2 100644 --- a/src/views/welcome/ChangelogScreen.tsx +++ b/src/views/welcome/ChangelogScreen.tsx @@ -205,11 +205,13 @@ const ChangelogScreen: Screen<"ChangelogScreen"> = ({ route, navigation }) => { - } - /> + {changelog.features.length > 0 && ( + } + /> + )} = ({ route, navigation }) => { - } /> + {changelog.bugfixes.length > 0 && ( + } /> + )} - + {feature.image && ( + + )} - { - if(feature.href) { - Linking.openURL(feature.href); - } - else if(feature.navigation) { - try { - navigation.goBack(); - navigation.navigate(feature.navigation); + {feature.navigation && ( + { + if(feature.href) { + Linking.openURL(feature.href); } - catch {} - } - } : undefined} - > - - {feature.button || "En savoir plus"} - - + + {feature.button || "En savoir plus"} + + + )} ); From 846cc5178427fa5d83054ec27ca55c278fc372a7 Mon Sep 17 00:00:00 2001 From: tom <135361669+toi-et-moi@users.noreply.github.com> Date: Tue, 1 Apr 2025 22:15:03 +0200 Subject: [PATCH 1118/1144] =?UTF-8?q?feat:=20tri=20des=20param=C3=A8tres?= =?UTF-8?q?=20en=20plusieurs=20cat=C3=A9gories=20diff=C3=A9rentes=20pour?= =?UTF-8?q?=20une=20UI=20moins=20charg=C3=A9e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/helpers/types.ts | 4 + src/router/screens/settings/index.ts | 16 + src/views/settings/Settings.tsx | 359 +++++------------- src/views/settings/SettingsExperimental.tsx | 106 ++++++ src/views/settings/SettingsGeneral.tsx | 70 ++++ .../settings/SettingsPersonalization.tsx | 142 +++++++ src/views/settings/SettingsProject.tsx | 84 ++++ 7 files changed, 513 insertions(+), 268 deletions(-) create mode 100644 src/views/settings/SettingsExperimental.tsx create mode 100644 src/views/settings/SettingsGeneral.tsx create mode 100644 src/views/settings/SettingsPersonalization.tsx create mode 100644 src/views/settings/SettingsProject.tsx diff --git a/src/router/helpers/types.ts b/src/router/helpers/types.ts index 3dff06969..a3fb48a3c 100644 --- a/src/router/helpers/types.ts +++ b/src/router/helpers/types.ts @@ -153,6 +153,10 @@ export type RouteParameters = { SettingsDonorsList: undefined; SettingsReactions: undefined; SettingsAccessibility: undefined; + SettingsGeneral: undefined; + SettingsPersonalization: undefined; + SettingsExperimental: undefined; + SettingsProject: undefined; Menu?: undefined; RestaurantQrCode: { diff --git a/src/router/screens/settings/index.ts b/src/router/screens/settings/index.ts index d0a8c151a..42d29685f 100644 --- a/src/router/screens/settings/index.ts +++ b/src/router/screens/settings/index.ts @@ -31,6 +31,10 @@ import ExternalAliseLogin from "@/views/settings/ExternalAccount/Alise"; import SettingsMultiService from "@/views/settings/SettingsMultiService"; import SettingsMultiServiceSpace from "@/views/settings/SettingsMultiServiceSpace"; import SettingsAccessibility from "@/views/settings/SettingsAccessibility"; +import SettingsGeneral from "@/views/settings/SettingsGeneral"; +import SettingsPersonalization from "@/views/settings/SettingsPersonalization"; +import SettingsExperimental from "@/views/settings/SettingsExperimental"; +import SettingsProject from "@/views/settings/SettingsProject"; const settingsScreens = [ createScreen("Settings", Settings, { @@ -148,6 +152,18 @@ const settingsScreens = [ createScreen("SettingsAccessibility", SettingsAccessibility, { headerTitle: "Accessibilité", }), + createScreen("SettingsGeneral", SettingsGeneral, { + headerTitle: "Général", + }), + createScreen("SettingsPersonalization", SettingsPersonalization, { + headerTitle: "Personnalisation", + }), + createScreen("SettingsExperimental", SettingsExperimental, { + headerTitle: "Expérimental", + }), + createScreen("SettingsProject", SettingsProject, { + headerTitle: "Projet Papillon", + }), ] as const; export default settingsScreens; diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index 7b78ff11f..79706bc86 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -10,37 +10,23 @@ import Reanimated, { FadeOut, runOnJS, useAnimatedScrollHandler, - useSharedValue, - ZoomIn, - ZoomOut + useSharedValue } from "react-native-reanimated"; import { - Bell, - Cable, HandCoins, Info, - Laptop, LogOut, Palette, Paperclip, - Puzzle, - Route, - Scroll, Settings as SettingsLucide, - Sparkles, - Smile, - SwatchBook, WandSparkles, X, - Blocks, - HelpCircle, PersonStanding, - Bug, BadgeHelp } from "lucide-react-native"; -import { NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; +import { NativeIconGradient, NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; import ModalHandle from "@/components/Modals/ModalHandle"; import AccountContainerCard from "@/components/Settings/AccountContainerCard"; import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; @@ -49,8 +35,6 @@ import AsyncStorage from "@react-native-async-storage/async-storage"; import {AddonPlacementManifest} from "@/addons/types"; import { useFlagsStore } from "@/stores/flags"; import { useAlert } from "@/providers/AlertProvider"; -import PapillonSpinner from "@/components/Global/PapillonSpinner"; -import { animPapillon } from "@/utils/ui/animations"; import * as WebBrowser from "expo-web-browser"; import { WebBrowserPresentationStyle } from "expo-web-browser"; import useScreenDimensions from "@/hooks/useScreenDimensions"; @@ -106,212 +90,51 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { const { showAlert } = useAlert(); - const tabs = [ + const menuItems = [ { icon: , + colors: ["#1E88E5", "#64B5F6"], label: "Général", - tabs: [ - { - icon: , - color: "#CF0029", - label: "Notifications", - onPress: () => navigation.navigate("SettingsNotifications"), - }, - { - icon: , - color: "#D79400", - label: "Services externes", - description: "Connecte ta cantine à Papillon", - onPress: () => navigation.navigate("SettingsExternalServices"), - }, - { - icon: , - color: "#136B00", - label: "Réactions", - onPress: () => navigation.navigate("SettingsReactions"), - }, - ], + description: "Notifications et services", + onPress: () => navigation.navigate("SettingsGeneral"), }, { icon: , + colors: ["#43A047", "#81C784"], label: "Personnalisation", - tabs: [ - { - icon: , - color: "#5C9441", - label: "Matières", - onPress: () => navigation.navigate("SettingsSubjects"), - }, - { - icon: , - color: "#295787", - label: "Icône de l'application", - onPress: () => navigation.navigate("SettingsIcons"), - android: false, - }, - { - icon: , - color: "#3B117E", - label: "Thème de couleur", - onPress: async () => { - if (Platform.OS === "ios") { - navigation.goBack(); - } - setTimeout(() => { - navigation.navigate("ColorSelector", { settings: true }); - }, 10); - } - }, - ], + description: "Apparence et navigation", + onPress: () => navigation.navigate("SettingsPersonalization"), }, { - icon: , + icon: , + colors: ["#8E24AA", "#BA68C8"], label: "Accessibilité", - tabs: [ - { - icon: , - color: "#bf547d", - label: "Accessibilité", - onPress: () => navigation.navigate("SettingsAccessibility"), - }, - ], + description: "Options d'accessibilité", + onPress: () => navigation.navigate("SettingsAccessibility"), }, { icon: , + colors: ["#FB8C00", "#FFB74D"], label: "Expérimental", - tabs: [ - { - icon: , - color: "#58A3C3", - label: "Papillon Magic", - beta: true, - description: "Fonctionnalités intelligentes", - onPress: () => navigation.navigate("SettingsMagic"), - }, - { - icon: , - color: "#1f76ce", - label: "Multiservice", - beta: true, - description: "Connecte plusieurs services en un seul espace de travail", - onPress: () => navigation.navigate("SettingsMultiService"), - }, - { - icon: , - color: "#498c75", - label: "Extensions", - description: "Disponible prochainement", - onPress: () => navigation.navigate("SettingsAddons"), - disabled: !defined("enable_addons"), - }, - ], + description: "Fonctionnalités beta", + onPress: () => navigation.navigate("SettingsExperimental"), }, { - icon: , + icon: , + colors: ["#546E7A", "#90A4AE"], label: "Projet Papillon", - tabs: [ - { - icon: , - color: "#c75110", - label: "Quoi de neuf ?", - onPress: () => navigation.navigate("ChangelogScreen"), - }, - { - icon: , - color: "#0E7CCB", - label: "Besoin d'aide ?", - onPress: () => openUrl("https://support.papillon.bzh/"), - }, - { - icon: , - color: "#CF0029", - label: "Signaler un problème", - onPress: () => navigation.navigate("SettingsSupport"), - }, - { - icon: , - color: "#888888", - label: "À propos de Papillon", - onPress: () => navigation.navigate("SettingsAbout"), - } - ], + description: "À propos et support", + onPress: () => navigation.navigate("SettingsProject"), }, - { - tabs: [ - { - icon: , - color: "#CF0029", - label: "Se déconnecter", - onPress: () => { - showAlert({ - title: "Se déconnecter", - message: "Veux-tu vraiment te déconnecter ?", - icon: , - actions: [ - { - title: "Annuler", - icon: , - primary: false, - }, - { - title: "Déconnexion", - onPress: () => { - removeAccount(account.localID); - setTimeout(() => { - navigation.reset({ - index: 0, - routes: [{ name: "AccountSelector" }], - }); - }, 100); - }, - danger: true, - icon: , - delayDisable: 5, - }, - ], - }); - } - }, - ] - } ]; if (Platform.OS === "android") { - tabs[3].tabs.push({ + menuItems.push({ icon: , - color: "#f0a500", + colors: ["#F57C00", "#FFB74D"], label: "Soutenir Papillon", + description: "Faire un don", onPress: () => openUrl("https://papillon.bzh/donate"), - android: true, - description: "", - disabled: false, - }); - } - - if (!isTablet) { - tabs[1].tabs.push({ - icon: click ? ( - ) : , - color: "#7E1174", - label: "Onglets & Navigation", - onPress: async () => { - setClick(true); - setTimeout(() => { - if (Platform.OS === "ios") { - navigation.goBack(); - } - navigation.navigate("SettingsTabs"); - setClick(false); - }, 10); - }, - description: "", - disabled: false, }); } @@ -403,71 +226,74 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { } - {tabs.map((tab, index) => ( - - {tab.label && - + {menuItems.map((item, index) => ( + + } + > + + {item.label} + + {"description" in item && item.description && + + {item.description} + + } + + ))} + + + + { + showAlert({ + title: "Se déconnecter", + message: "Veux-tu vraiment te déconnecter ?", + icon: , + actions: [ + { + title: "Annuler", + icon: , + primary: false, + }, + { + title: "Déconnexion", + onPress: () => { + removeAccount(account.localID); + setTimeout(() => { + navigation.reset({ + index: 0, + routes: [{ name: "AccountSelector" }], + }); + }, 100); + }, + danger: true, + icon: , + delayDisable: 5, + }, + ], + }); + }} + leading={ + } + colors={["#E53935", "#EF5350"]} /> } - - {tab.tabs.map((subtab, index) => ( - (Platform.OS === "android" && "android" in subtab && !subtab.android) ? : - - } - trailing={ - // @ts-expect-error : on ignore la condition - subtab.beta && ( - - - Bêta - - - ) - } - > - - {subtab.label} - - {"description" in subtab && subtab.description && - - {subtab.description} - - } - - ))} - - - ))} + > + + Se déconnecter + + + {devModeEnabled && ( @@ -476,12 +302,9 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { navigation.navigate("SettingsDevLogs")} leading={ - } - color={"#000"} - style={{ - marginLeft: -6, - }} + colors={["#757575", "#BDBDBD"]} /> } > diff --git a/src/views/settings/SettingsExperimental.tsx b/src/views/settings/SettingsExperimental.tsx new file mode 100644 index 000000000..cc06b9c19 --- /dev/null +++ b/src/views/settings/SettingsExperimental.tsx @@ -0,0 +1,106 @@ +import React from "react"; +import { ScrollView, View } from "react-native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; +import type { Screen } from "@/router/helpers/types"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { NativeIconGradient, NativeItem, NativeList, NativeText } from "@/components/Global/NativeComponents"; +import { WandSparkles, Blocks, Puzzle } from "lucide-react-native"; +import { useFlagsStore } from "@/stores/flags"; + +const SettingsExperimental: Screen<"SettingsExperimental"> = ({ navigation }) => { + const theme = useTheme(); + const { colors } = theme; + const insets = useSafeAreaInsets(); + const defined = useFlagsStore(state => state.defined); + + const items = [ + { + icon: , + colors: ["#FB8C00", "#FFA726"], + label: "Papillon Magic", + beta: true, + description: "Fonctionnalités intelligentes", + onPress: () => navigation.navigate("SettingsMagic"), + }, + { + icon: , + colors: ["#1976D2", "#42A5F5"], + label: "Multiservice", + beta: true, + description: "Connecte plusieurs services en un seul espace de travail", + onPress: () => navigation.navigate("SettingsMultiService"), + }, + { + icon: , + colors: ["#00897B", "#4DB6AC"], + label: "Extensions", + description: "Disponible prochainement", + onPress: () => navigation.navigate("SettingsAddons"), + disabled: !defined("enable_addons"), + }, + ]; + + return ( + + + {items.map((item, index) => ( + + } + trailing={ + // @ts-expect-error : on ignore la condition + item.beta && ( + + + Bêta + + + ) + } + > + + {item.label} + + {"description" in item && item.description && + + {item.description} + + } + + ))} + + + ); +}; + +export default SettingsExperimental; \ No newline at end of file diff --git a/src/views/settings/SettingsGeneral.tsx b/src/views/settings/SettingsGeneral.tsx new file mode 100644 index 000000000..fd9adf53f --- /dev/null +++ b/src/views/settings/SettingsGeneral.tsx @@ -0,0 +1,70 @@ +import React from "react"; +import { ScrollView } from "react-native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; +import type { Screen } from "@/router/helpers/types"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { NativeIconGradient, NativeItem, NativeList, NativeText } from "@/components/Global/NativeComponents"; +import { Bell, Cable, Smile } from "lucide-react-native"; + +const SettingsGeneral: Screen<"SettingsGeneral"> = ({ navigation }) => { + const theme = useTheme(); + const insets = useSafeAreaInsets(); + + const items = [ + { + icon: , + colors: ["#F44336", "#FF7043"], + label: "Notifications", + onPress: () => navigation.navigate("SettingsNotifications"), + }, + { + icon: , + colors: ["#FF9800", "#FFCA28"], + label: "Services externes", + description: "Connecte ta cantine à Papillon", + onPress: () => navigation.navigate("SettingsExternalServices"), + }, + { + icon: , + colors: ["#4CAF50", "#8BC34A"], + label: "Réactions", + onPress: () => navigation.navigate("SettingsReactions"), + }, + ]; + + return ( + + + {items.map((item, index) => ( + + } + > + + {item.label} + + {"description" in item && item.description && + + {item.description} + + } + + ))} + + + ); +}; + +export default SettingsGeneral; \ No newline at end of file diff --git a/src/views/settings/SettingsPersonalization.tsx b/src/views/settings/SettingsPersonalization.tsx new file mode 100644 index 000000000..484055584 --- /dev/null +++ b/src/views/settings/SettingsPersonalization.tsx @@ -0,0 +1,142 @@ +import React, { useState } from "react"; +import { ScrollView, Platform } from "react-native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; +import type { Screen } from "@/router/helpers/types"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { NativeIconGradient, NativeItem, NativeList, NativeText } from "@/components/Global/NativeComponents"; +import { Sparkles, SwatchBook, Route, Palette } from "lucide-react-native"; +import PapillonSpinner from "@/components/Global/PapillonSpinner"; +import { animPapillon } from "@/utils/ui/animations"; +import { ZoomIn, ZoomOut } from "react-native-reanimated"; +import useScreenDimensions from "@/hooks/useScreenDimensions"; + +const SettingsPersonalization: Screen<"SettingsPersonalization"> = ({ navigation }) => { + const theme = useTheme(); + const insets = useSafeAreaInsets(); + const [click, setClick] = useState(false); + const { isTablet } = useScreenDimensions(); + + // These are regular items that stay within the settings navigation + const regularItems = [ + { + icon: , + colors: ["#4CAF50", "#8BC34A"], + label: "Matières", + onPress: () => navigation.navigate("SettingsSubjects"), + }, + { + icon: , + colors: ["#2196F3", "#03A9F4"], + label: "Icône de l'application", + onPress: () => navigation.navigate("SettingsIcons"), + android: false, + }, + ]; + + // These are special items that close the settings modal and show a full-screen view + const specialItems = [ + { + icon: , + colors: ["#9C27B0", "#BA68C8"], + label: "Thème de couleur", + onPress: async () => { + // Close the entire Settings stack + navigation.getParent()?.goBack(); + + // Navigate to the ColorSelector after a short delay + setTimeout(() => { + navigation.navigate("ColorSelector", { settings: true }); + }, 10); + } + } + ]; + + // Add Tabs & Navigation item if not on tablet + if (!isTablet) { + specialItems.push({ + icon: click ? ( + ) : , + colors: ["#673AB7", "#9575CD"], + label: "Onglets & Navigation", + onPress: async () => { + setClick(true); + + // Close the entire Settings stack + navigation.getParent()?.goBack(); + + // Navigate to SettingsTabs after a short delay + setTimeout(() => { + navigation.navigate("SettingsTabs"); + setClick(false); + }, 10); + } + }); + } + + return ( + + + {regularItems.map((item, index) => ( + (Platform.OS === "android" && "android" in item && !item.android) ? null : + + } + > + + {item.label} + + {"description" in item && item.description && + + {item.description} + + } + + ))} + + + + {specialItems.map((item, index) => ( + + } + > + + {item.label} + + {"description" in item && item.description && + + {item.description} + + } + + ))} + + + ); +}; + +export default SettingsPersonalization; \ No newline at end of file diff --git a/src/views/settings/SettingsProject.tsx b/src/views/settings/SettingsProject.tsx new file mode 100644 index 000000000..57f8bab0c --- /dev/null +++ b/src/views/settings/SettingsProject.tsx @@ -0,0 +1,84 @@ +import React from "react"; +import { ScrollView } from "react-native"; +import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; +import type { Screen } from "@/router/helpers/types"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { NativeIconGradient, NativeItem, NativeList, NativeText } from "@/components/Global/NativeComponents"; +import { Scroll, HelpCircle, Bug, Info } from "lucide-react-native"; +import * as WebBrowser from "expo-web-browser"; +import { WebBrowserPresentationStyle } from "expo-web-browser"; + +const SettingsProject: Screen<"SettingsProject"> = ({ navigation }) => { + const theme = useTheme(); + const insets = useSafeAreaInsets(); + + const openUrl = (url: string) => { + WebBrowser.openBrowserAsync(url, { + presentationStyle: WebBrowserPresentationStyle.FORM_SHEET, + controlsColor: theme.colors.primary, + }); + }; + + const items = [ + { + icon: , + colors: ["#FF5722", "#FF8A65"], // Deep orange to light orange + label: "Quoi de neuf ?", + onPress: () => navigation.navigate("ChangelogScreen"), + }, + { + icon: , + colors: ["#2196F3", "#64B5F6"], // Blue to light blue + label: "Besoin d'aide ?", + onPress: () => openUrl("https://support.papillon.bzh/"), + }, + { + icon: , + colors: ["#F44336", "#E57373"], // Red to light red + label: "Signaler un problème", + onPress: () => navigation.navigate("SettingsSupport"), + }, + { + icon: , + colors: ["#546E7A", "#90A4AE"], // Blue-grey to light blue-grey + label: "À propos de Papillon", + onPress: () => navigation.navigate("SettingsAbout"), + } + ]; + + return ( + + + {items.map((item, index) => ( + + } + > + + {item.label} + + {"description" in item && item.description && + + {item.description} + + } + + ))} + + + ); +}; + +export default SettingsProject; \ No newline at end of file From 47057816a8517539387d506b7a5b6b6318b0b313 Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Tue, 1 Apr 2025 23:19:24 +0200 Subject: [PATCH 1119/1144] fix(restaurant): add check for qrcode widget visibility --- src/widgets/Components/RestaurantQRCode.tsx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/widgets/Components/RestaurantQRCode.tsx b/src/widgets/Components/RestaurantQRCode.tsx index b3e8d9aaa..53c97ba2c 100644 --- a/src/widgets/Components/RestaurantQRCode.tsx +++ b/src/widgets/Components/RestaurantQRCode.tsx @@ -85,6 +85,19 @@ const RestaurantQRCodeWidget = forwardRef(({ }(); }, [linkedAccounts, setHidden]); + useEffect(() => { + const updateVisibility = () => { + const currentHour = new Date().getUTCHours(); + const shouldShow = allCards?.some(card => card.cardnumber) && currentHour >= 11 && currentHour < 14; + setHidden(!shouldShow); + }; + + updateVisibility(); + const interval = setInterval(updateVisibility, 60000); + + return () => clearInterval(interval); + }, [linkedAccounts, allCards]); + useEffect(() => { if (allCards && allCards.length > 1) { const interval = setInterval(() => { From de20c148909ed19d5e4399ab571bfdfc7740bc94 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 2 Apr 2025 14:03:56 +0200 Subject: [PATCH 1120/1144] Revert "fix: Utilisation des heures, dates et jours en UTC dans toute l'appli (#863)" This reverts commit 7eaf276e06a7074dd826ad1530e234937e7bdc40, reversing changes made to 737422b874dd21795267a744b48c9ca04e3f3f67. --- src/background/data/Homeworks.ts | 4 ++-- src/background/data/Lessons.ts | 2 +- src/background/utils/format.ts | 4 ++-- src/background/utils/homeworks.ts | 4 ++-- src/components/Global/InfiniteDatePager.tsx | 2 +- src/services/iutlan/attendance.ts | 4 ++-- src/services/pronote/chats.ts | 2 +- src/services/skolengo/skolengo-types.ts | 4 ++-- src/services/turboself/booking.ts | 2 +- src/utils/epochWeekNumber.ts | 13 ++++++------ src/utils/format/DateHelper.ts | 2 +- .../Home/Elements/HomeworksElement.tsx | 4 ++-- .../Home/Elements/PopupRestauration.tsx | 2 +- .../Home/Elements/TimetableElement.tsx | 6 +++--- src/views/account/Homeworks/Homeworks.old.tsx | 2 +- src/views/account/Homeworks/Homeworks.tsx | 8 ++++---- src/views/account/Lessons/Atoms/Page.tsx | 4 ++-- src/views/account/Lessons/Document.tsx | 8 ++++---- src/views/account/Lessons/Lessons.tsx | 20 +++++++++---------- src/views/account/Lessons/LessonsHeader.tsx | 2 +- src/views/account/Restaurant/Menu.tsx | 12 +++++------ src/views/account/Week/Week.tsx | 2 +- src/views/settings/SettingsDevLogs.tsx | 17 ++++++---------- src/widgets/Components/NextCourse.tsx | 6 +++--- src/widgets/Components/RestaurantQRCode.tsx | 4 ++-- 25 files changed, 68 insertions(+), 72 deletions(-) diff --git a/src/background/data/Homeworks.ts b/src/background/data/Homeworks.ts index c8f0565af..cdec51b03 100644 --- a/src/background/data/Homeworks.ts +++ b/src/background/data/Homeworks.ts @@ -26,8 +26,8 @@ const fetchHomeworks = async (): Promise => { let firstDate = account.instance?.instance?.firstDate || null; if (!firstDate) { firstDate = new Date(); - firstDate.setUTCMonth(8); - firstDate.setUTCDate(1); + firstDate.setMonth(8); + firstDate.setDate(1); } const firstDateEpoch = dateToEpochWeekNumber(firstDate); diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index 1d49026ea..f31671710 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -11,7 +11,7 @@ import { formatHoursMinutes } from "../utils/format"; const getAllLessonsForDay = (lessons: Record) => { const date = new Date(); - date.setUTCHours(0, 0, 0, 0); + date.setHours(0, 0, 0, 0); const week = dateToEpochWeekNumber(date); const timetable = lessons[week] || []; diff --git a/src/background/utils/format.ts b/src/background/utils/format.ts index 49822366c..ec7880882 100644 --- a/src/background/utils/format.ts +++ b/src/background/utils/format.ts @@ -1,7 +1,7 @@ export const formatHoursMinutes = (timestamp: number) => { const LAdate = new Date(timestamp); - const heures = LAdate.getUTCHours().toString().padStart(2, "0"); - const minutes = LAdate.getUTCMinutes().toString().padStart(2, "0"); + const heures = LAdate.getHours().toString().padStart(2, "0"); + const minutes = LAdate.getMinutes().toString().padStart(2, "0"); return `${heures}:${minutes}`; }; diff --git a/src/background/utils/homeworks.ts b/src/background/utils/homeworks.ts index f039436b6..ba67c7695 100644 --- a/src/background/utils/homeworks.ts +++ b/src/background/utils/homeworks.ts @@ -5,9 +5,9 @@ import { epochWNToDate } from "@/utils/epochWeekNumber"; const getCurrentWeekNumber = () => { const now = new Date(); - now.setUTCHours(0, 0, 0, 0); + now.setHours(0, 0, 0, 0); const start = new Date(1970, 0, 0); - start.setUTCHours(0, 0, 0, 0); + start.setHours(0, 0, 0, 0); const diff = now.getTime() - start.getTime(); const oneWeek = 1000 * 60 * 60 * 24 * 7; return Math.floor(diff / oneWeek); diff --git a/src/components/Global/InfiniteDatePager.tsx b/src/components/Global/InfiniteDatePager.tsx index 3c024eb02..75b9265a8 100644 --- a/src/components/Global/InfiniteDatePager.tsx +++ b/src/components/Global/InfiniteDatePager.tsx @@ -14,7 +14,7 @@ interface InfiniteDatePagerProps { const InfiniteDatePager = ({ renderDate, initialDate = new Date(), onDateChange }: InfiniteDatePagerProps) => { const pagerRef = useRef(null); const baseDate = useRef(new Date()).current; - baseDate.setUTCHours(0, 0, 0, 0); + baseDate.setHours(0, 0, 0, 0); const lastChangeTime = useRef(0); const getDateFromIndex = useCallback((index: number) => { diff --git a/src/services/iutlan/attendance.ts b/src/services/iutlan/attendance.ts index b19ac4550..c6cf91c6d 100644 --- a/src/services/iutlan/attendance.ts +++ b/src/services/iutlan/attendance.ts @@ -29,10 +29,10 @@ export const saveIUTLanAttendance = async ( for (const absence of absences) { let from = new Date(day); - from.setUTCHours(parseInt(absence.debut)); + from.setHours(parseInt(absence.debut)); let to = new Date(absence.dateFin); - to.setUTCHours(parseInt(absence.fin)); + to.setHours(parseInt(absence.fin)); allAbsences.push({ id: absence.idAbs, diff --git a/src/services/pronote/chats.ts b/src/services/pronote/chats.ts index 8f4e46dd4..e52320874 100644 --- a/src/services/pronote/chats.ts +++ b/src/services/pronote/chats.ts @@ -34,7 +34,7 @@ export const getChats = async (account: PronoteAccount): Promise> => if (targetIndex !== -1) { const diff = targetIndex - todayIndex; const targetDate = new Date(); - targetDate.setUTCDate(today.getUTCDate() + (diff <= 0 ? diff : diff - 7)); + targetDate.setDate(today.getDate() + (diff <= 0 ? diff : diff - 7)); return targetDate; } diff --git a/src/services/skolengo/skolengo-types.ts b/src/services/skolengo/skolengo-types.ts index 44928749d..999ffccb5 100644 --- a/src/services/skolengo/skolengo-types.ts +++ b/src/services/skolengo/skolengo-types.ts @@ -56,7 +56,7 @@ export const toSkolengoDate = (date: Date): string => `${ date.getFullYear().toString().padStart(4, "0") }-${ - (date.getUTCMonth()+1).toString().padStart(2, "0") + (date.getMonth()+1).toString().padStart(2, "0") }-${ - (date.getUTCDate()).toString().padStart(2, "0") + (date.getDate()).toString().padStart(2, "0") }`; \ No newline at end of file diff --git a/src/services/turboself/booking.ts b/src/services/turboself/booking.ts index a2b5aff82..ae65f8912 100644 --- a/src/services/turboself/booking.ts +++ b/src/services/turboself/booking.ts @@ -18,7 +18,7 @@ export const getBookingWeek = async (account: TurboselfAccount, weekNumber?: num }; export const bookDay = async (account: TurboselfAccount, id: string, date: Date, booked: boolean): Promise => { - const bookedDay = await account.authentication.session.bookMeal(id, date.getUTCDay(), booked ? 1 : 0); + const bookedDay = await account.authentication.session.bookMeal(id, date.getDay(), booked ? 1 : 0); return { id: bookedDay.id, canBook: bookedDay.canBook, diff --git a/src/utils/epochWeekNumber.ts b/src/utils/epochWeekNumber.ts index b42bfc63d..bb4110469 100644 --- a/src/utils/epochWeekNumber.ts +++ b/src/utils/epochWeekNumber.ts @@ -21,7 +21,7 @@ const EPOCH_WN_CONFIG = { setMiddleDay: 3, // We set the middle day of the week to Wednesday to ensure <... same than above ...> setEndDay: 7, // We set the last day of the week to Sunday to ensure <...> numberOfMsInAWeek: 1000 /* ms */ * 60 /* s */ * 60 /* min */ * 24 /* h */ * 7, /* days */ - adjustEpochInitialDate: 259200000, // =(((new Date(0)).getUTCDay()-1) * EPOCH_WN_CONFIG.numberOfMsInAWeek/7) // We need to substract this for having a good range cause 01/01/1970 was not a Monday and the "-1" is to have Monday as the first day of the week + adjustEpochInitialDate: 259200000, // =(((new Date(0)).getDay()-1) * EPOCH_WN_CONFIG.numberOfMsInAWeek/7) // We need to substract this for having a good range cause 01/01/1970 was not a Monday and the "-1" is to have Monday as the first day of the week }; /** @@ -31,11 +31,12 @@ const EPOCH_WN_CONFIG = { const dayToWeekCommonDay = (date: Date): Date => { const _date = new Date(date); - const day = _date.getUTCDay(); - _date.setUTCHours(EPOCH_WN_CONFIG.setHour, 0, 0, 0); - _date.setUTCDate( - _date.getUTCDate() - ((7 + day - 1) % 7) + EPOCH_WN_CONFIG.setMiddleDay - 1 - ); + _date.setHours(EPOCH_WN_CONFIG.setHour, 0, 0, 0); + _date.setDate(_date.getDate() - ( (7 + _date.getDay() - 1) %7 ) + EPOCH_WN_CONFIG.setMiddleDay - 1); + // the (7+ ... -1 ) %7 is to have Monday as the first day (0) of the week and Sunday as last day (7) cause JS start the week on Sunday ¯\_(ツ)_/¯ + // In details : the 7+ is to avoid negative value, the -1 is to have Monday as the first day of the week and the %7 is to have the right day number (0 to 6) + // In details : setMiddleDay - 1 is to have the middle day of the week, aka Wednesday + // Its to avoid the fact that Sunday is in the next week with simpler JS code. return _date; }; diff --git a/src/utils/format/DateHelper.ts b/src/utils/format/DateHelper.ts index d85b84960..56c9f017e 100644 --- a/src/utils/format/DateHelper.ts +++ b/src/utils/format/DateHelper.ts @@ -15,7 +15,7 @@ export const timestampToString = (timestamp: number) => { } const mtn = new Date(); - mtn.setUTCHours(0, 0, 0, 0); + mtn.setHours(0, 0, 0, 0); return formatDistance(new Date(timestamp), mtn, { locale: fr, diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 5ce2cd0fb..238d91f92 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -39,7 +39,7 @@ const HomeworksElement: React.FC = ({ navigation, onImpor .filter(hw => !hw.done); const date = new Date(); - if (date.getUTCHours() >= 17 && date.getUTCHours() < 22) + if (date.getHours() >= 17 && date.getHours() < 22) score += 4; if (hw.length > 0) score += 3; @@ -83,7 +83,7 @@ const HomeworksElement: React.FC = ({ navigation, onImpor ); const mtn = new Date(); - mtn.setUTCHours(0, 0, 0, 0); + mtn.setHours(0, 0, 0, 0); const startTime = mtn.getTime() / 1000; const endTime = startTime + 7 * 24 * 60 * 60 * 1000; diff --git a/src/views/account/Home/Elements/PopupRestauration.tsx b/src/views/account/Home/Elements/PopupRestauration.tsx index eb3616bac..f9110d322 100644 --- a/src/views/account/Home/Elements/PopupRestauration.tsx +++ b/src/views/account/Home/Elements/PopupRestauration.tsx @@ -19,7 +19,7 @@ const PopupRestauration: React.FC = ({ onImportance }) = const mutateProperty = useCurrentAccount(store => store.mutateProperty); const ImportanceHandler = () => { - let hours = new Date().getUTCHours(); + let hours = new Date().getHours(); if (hours >= 11 && hours < 14) onImportance(10); else diff --git a/src/views/account/Home/Elements/TimetableElement.tsx b/src/views/account/Home/Elements/TimetableElement.tsx index 3bc4d7c3e..1dd696c41 100644 --- a/src/views/account/Home/Elements/TimetableElement.tsx +++ b/src/views/account/Home/Elements/TimetableElement.tsx @@ -30,7 +30,7 @@ const TimetableElement: React.FC = ({ onImportance }) => const ImportanceHandler = (nextCourses: TimetableClass[]) => { if (nextCourses.length > 0) { - let difference = new Date(nextCourses[0].startTimestamp).getUTCHours() - new Date().getUTCHours(); + let difference = new Date(nextCourses[0].startTimestamp).getHours() - new Date().getHours(); if (difference < 0) { difference = 0; } @@ -41,7 +41,7 @@ const TimetableElement: React.FC = ({ onImportance }) => }; const isWeekend = (courses: TimetableClass[]) => { - const today = new Date().getUTCDay(); + const today = new Date().getDay(); return (today === 6 || today === 0) && courses.length === 0; }; @@ -183,7 +183,7 @@ const TimetableElement: React.FC = ({ onImportance }) => const isTodayCourse = courseDate.toDateString() === today.toDateString(); const tomorrow = new Date(today); - tomorrow.setUTCDate(today.getUTCDate() + 1); + tomorrow.setDate(today.getDate() + 1); const isTomorrowCourse = courseDate.toDateString() === tomorrow.toDateString(); diff --git a/src/views/account/Homeworks/Homeworks.old.tsx b/src/views/account/Homeworks/Homeworks.old.tsx index 763e1fdbe..93ac37859 100644 --- a/src/views/account/Homeworks/Homeworks.old.tsx +++ b/src/views/account/Homeworks/Homeworks.old.tsx @@ -204,7 +204,7 @@ const HomeworksScreen: Screen<"Homeworks"> = ({ navigation }) => { const getDayName = (date: string | number | Date): string => { const days = ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"]; - return days[new Date(date).getUTCDay()]; + return days[new Date(date).getDay()]; }; useEffect(() => { diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index a6a420502..f357305af 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -86,8 +86,8 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { let firstDate = account?.instance?.instance?.firstDate || null; if (!firstDate) { firstDate = new Date(); - firstDate.setUTCMonth(8); - firstDate.setUTCDate(1); + firstDate.setMonth(8); + firstDate.setDate(1); } const firstDateEpoch = dateToEpochWeekNumber(firstDate); @@ -109,8 +109,8 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { const keyExtractor = useCallback((item: any) => item.toString(), []); const getDayName = (date: string | number | Date): string => { - const days = ["Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche"]; - return days[new Date(date).getUTCDay()]; + const days = ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"]; + return days[new Date(date).getDay()]; }; const [loading, setLoading] = useState(false); diff --git a/src/views/account/Lessons/Atoms/Page.tsx b/src/views/account/Lessons/Atoms/Page.tsx index dd6b20c0e..199489255 100644 --- a/src/views/account/Lessons/Atoms/Page.tsx +++ b/src/views/account/Lessons/Atoms/Page.tsx @@ -142,7 +142,7 @@ export const Page = ({ day, date, current, paddingTop, refreshAction, loading, w )} {hasServiceSetup && day && day.length === 0 && current && !loading && ( - weekExists && (new Date(date).getUTCDay() == 6 || new Date(date).getUTCDay() == 0) ? ( + weekExists && (new Date(date).getDay() == 6 || new Date(date).getDay() == 0) ? ( = ({ i, start, end, icon, label, showDuration= true }) => { const { colors } = useTheme(); - const startHours = new Date(start).getUTCHours(); + const startHours = new Date(start).getHours(); return ( = ({ route, navigation }) => { setClassSubjects( subjects.subjects.filter( (b) => - new Date(b.date).getUTCDate() === - new Date(lesson.startTimestamp).getUTCDate() && - new Date(b.date).getUTCMonth() === - new Date(lesson.startTimestamp).getUTCMonth() && + new Date(b.date).getDate() === + new Date(lesson.startTimestamp).getDate() && + new Date(b.date).getMonth() === + new Date(lesson.startTimestamp).getMonth() && lesson.subject === b.subject, ) ?? [], ); diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index 792588df0..347cc78fc 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -99,7 +99,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { }, [timetables]); const today = new Date(); - today.setUTCHours(0, 0, 0, 0); + today.setHours(0, 0, 0, 0); const [pickerDate, setPickerDate] = useState(new Date(today)); @@ -194,7 +194,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { if (flatListRef.current) { const normalizeDate = (date: Date) => { const newDate = new Date(date); - newDate.setUTCHours(0, 0, 0, 0); + newDate.setHours(0, 0, 0, 0); return newDate; }; @@ -215,8 +215,8 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const today = new Date(); return Array.from({ length: 100 }, (_, i) => { const date = new Date(today); - date.setUTCDate(today.getUTCDate() - 50 + i); - date.setUTCHours(0, 0, 0, 0); + date.setDate(today.getDate() - 50 + i); + date.setHours(0, 0, 0, 0); return date; }); }); @@ -313,7 +313,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { const onDateSelect = (date: Date | undefined) => { const newDate = new Date(date || 0); - newDate.setUTCHours(0, 0, 0, 0); + newDate.setHours(0, 0, 0, 0); setPickerDate(newDate); const firstDate = data[0]; @@ -324,7 +324,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { if (newDate < firstDate) { const dates = []; - for (let d = new Date(firstDate); d >= newDate; d.setUTCDate(d.getUTCDate() - 1)) { + for (let d = new Date(firstDate); d >= newDate; d.setDate(d.getDate() - 1)) { if (!uniqueDates.has(d.getTime())) { dates.unshift(new Date(d)); uniqueDates.add(d.getTime()); @@ -333,7 +333,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { updatedData = [...dates, ...data]; } else if (newDate > lastDate) { const dates = []; - for (let d = new Date(lastDate); d <= newDate; d.setUTCDate(d.getUTCDate() + 1)) { + for (let d = new Date(lastDate); d <= newDate; d.setDate(d.getDate() + 1)) { if (!uniqueDates.has(d.getTime())) { dates.push(new Date(d)); uniqueDates.add(d.getTime()); @@ -360,7 +360,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { onPress={() => setShowDatePicker(true)} onLongPress={() => { const today = new Date(); - today.setUTCHours(0, 0, 0, 0); + today.setHours(0, 0, 0, 0); onDateSelect(today); }} > @@ -385,7 +385,7 @@ const Lessons: Screen<"Lessons"> = ({ route, navigation }) => { = ({ route, navigation }) => { const lastDate = data[data.length - 1]; const newDates = Array.from({ length: 34 }, (_, i) => { const date = new Date(lastDate); - date.setUTCDate(lastDate.getUTCDate() + i + 1); + date.setDate(lastDate.getDate() + i + 1); return date; }); setData((prevData) => [...prevData, ...newDates]); diff --git a/src/views/account/Lessons/LessonsHeader.tsx b/src/views/account/Lessons/LessonsHeader.tsx index cfd9df5b1..3b55f8465 100644 --- a/src/views/account/Lessons/LessonsHeader.tsx +++ b/src/views/account/Lessons/LessonsHeader.tsx @@ -204,7 +204,7 @@ const LessonsDateModal: React.FC = ({ onChange={(_event, selectedDate) => { const newSelectedDate = selectedDate || currentDate; // set hours to 0 - newSelectedDate.setUTCHours(0, 0, 0, 0); + newSelectedDate.setHours(0, 0, 0, 0); onDateSelect(newSelectedDate); }} /> diff --git a/src/views/account/Restaurant/Menu.tsx b/src/views/account/Restaurant/Menu.tsx index dbe46ed00..498018105 100644 --- a/src/views/account/Restaurant/Menu.tsx +++ b/src/views/account/Restaurant/Menu.tsx @@ -73,7 +73,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { const [currentMenu, setCurrentMenu] = useState(null); const [currentWeek, setCurrentWeek] = useState(0); const [showDatePicker, setShowDatePicker] = useState(false); - const [pickerDate, setPickerDate] = React.useState(new Date(new Date().setUTCHours(0, 0, 0, 0))); + const [pickerDate, setPickerDate] = React.useState(new Date(new Date().setHours(0, 0, 0, 0))); const [isMenuLoading, setMenuLoading] = useState(false); const [isInitialised, setIsInitialised] = useState(false); const [selectedIndex, setSelectedIndex] = useState(0); @@ -89,7 +89,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { const getWeekNumber = (date: Date) => { const firstDayOfYear = new Date(date.getFullYear(), 0, 1); const pastDaysOfYear = (date.getTime() - firstDayOfYear.getTime()) / 86400000; - return Math.ceil((pastDaysOfYear + firstDayOfYear.getUTCDay() + 1) / 7); + return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7); }; const onDatePickerSelect = async (date?: Date) => { @@ -99,7 +99,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { const newDate = new Date(date); - newDate.setUTCHours(0, 0, 0, 0); + newDate.setHours(0, 0, 0, 0); if (newDate.valueOf() === pickerDate.valueOf()) { return; @@ -424,7 +424,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { > { - onDatePickerSelect(new Date(pickerDate.setUTCDate(pickerDate.getUTCDate() - 1))); + onDatePickerSelect(new Date(pickerDate.setDate(pickerDate.getDate() - 1))); setRefreshCount(refreshCount + 1); }} activeScale={0.8} @@ -456,7 +456,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { - + {pickerDate.toLocaleDateString("fr-FR", { month: "long" })} @@ -468,7 +468,7 @@ const Menu: Screen<"Menu"> = ({ route, navigation }) => { > { - onDatePickerSelect(new Date(pickerDate.setUTCDate(pickerDate.getUTCDate() + 1))); + onDatePickerSelect(new Date(pickerDate.setDate(pickerDate.getDate() + 1))); setRefreshCount(refreshCount + 1); }} activeScale={0.8} diff --git a/src/views/account/Week/Week.tsx b/src/views/account/Week/Week.tsx index 46de385a6..993f7752e 100644 --- a/src/views/account/Week/Week.tsx +++ b/src/views/account/Week/Week.tsx @@ -131,7 +131,7 @@ const HeaderItem = memo(({ header }) => { const start = header.startUnix; const today = new Date(); - today.setUTCHours(0, 0, 0, 0); + today.setHours(0, 0, 0, 0); const todayStamp = today.getTime(); diff --git a/src/views/settings/SettingsDevLogs.tsx b/src/views/settings/SettingsDevLogs.tsx index 8f589c4b4..87e36faa9 100644 --- a/src/views/settings/SettingsDevLogs.tsx +++ b/src/views/settings/SettingsDevLogs.tsx @@ -37,11 +37,10 @@ import { animPapillon } from "@/utils/ui/animations"; import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; import { useAlert } from "@/providers/AlertProvider"; import MissingItem from "@/components/Global/MissingItem"; +import formatDate from "@/utils/format/format_date_complets"; import ResponsiveTextInput from "@/components/FirstInstallation/ResponsiveTextInput"; -import { formatDistanceToNow } from "date-fns"; -import { fr } from "date-fns/locale"; -const SettingsDevLogs: Screen<"SettingsDevLogs"> = () => { +const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { const { colors } = useTheme(); const [logs, setLogs] = useState([]); const [searchTerms, setSearchTerms] = useState(""); @@ -59,7 +58,7 @@ const SettingsDevLogs: Screen<"SettingsDevLogs"> = () => { ); setLoading(false); }); - }, []); + }, [navigation]); return ( = () => { exiting={animPapillon(FadeOutUp)} > {logs.slice().reverse().map((log, index) => { - if (Number.isNaN(new Date(log.date).getTime())) return; - if (log.message.toLowerCase().includes(searchTerms.toLowerCase())) { return ( = () => { > {log.message} - {formatDistanceToNow(log.date, { - addSuffix: true, - includeSeconds: true, - locale: fr, - })} + {formatDate(log.date)} à {new Date(log.date).getHours()}: + {new Date(log.date).getMinutes()}: + {new Date(log.date).getSeconds()} {log.from} diff --git a/src/widgets/Components/NextCourse.tsx b/src/widgets/Components/NextCourse.tsx index d7f0c32cc..87fc07bd5 100644 --- a/src/widgets/Components/NextCourse.tsx +++ b/src/widgets/Components/NextCourse.tsx @@ -143,9 +143,9 @@ const NextCourseLesson: React.FC<{ // Schedule next update at the start of the next minute const nextMinute = new Date(now); - nextMinute.setUTCSeconds(0); - nextMinute.setUTCMilliseconds(0); - nextMinute.setUTCMinutes(nextMinute.getUTCMinutes() + 1); + nextMinute.setSeconds(0); + nextMinute.setMilliseconds(0); + nextMinute.setMinutes(nextMinute.getMinutes() + 1); const delay = nextMinute.getTime() - now; return setTimeout(updateRemainingTime, delay); }; diff --git a/src/widgets/Components/RestaurantQRCode.tsx b/src/widgets/Components/RestaurantQRCode.tsx index e9f9e270d..c05dd418f 100644 --- a/src/widgets/Components/RestaurantQRCode.tsx +++ b/src/widgets/Components/RestaurantQRCode.tsx @@ -43,7 +43,7 @@ const RestaurantQRCodeWidget = forwardRef(({ const getWeekNumber = (date: Date) => { const firstDayOfYear = new Date(date.getFullYear(), 0, 1); const pastDaysOfYear = (date.getTime() - firstDayOfYear.getTime()) / 86400000; - return Math.ceil((pastDaysOfYear + firstDayOfYear.getUTCDay() + 1) / 7); + return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7); }; useEffect(() => { @@ -51,7 +51,7 @@ const RestaurantQRCodeWidget = forwardRef(({ setHidden(true); setLoading(true); const newCards: Array = []; - const currentHour = new Date().getUTCHours(); + const currentHour = new Date().getHours(); const accountPromises = linkedAccounts.map(async (account) => { try { const [cardnumber] = await Promise.all([ From b1d9febc798fa2ede83635456f814c54649bc799 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 2 Apr 2025 14:17:18 +0200 Subject: [PATCH 1121/1144] =?UTF-8?q?fix:=20corriger=20l'initialisation=20?= =?UTF-8?q?de=20la=20premi=C3=A8re=20date=20en=20utilisant=20UTC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Homeworks.ts | 4 +--- src/views/account/Homeworks/Homeworks.tsx | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/background/data/Homeworks.ts b/src/background/data/Homeworks.ts index cdec51b03..15d682832 100644 --- a/src/background/data/Homeworks.ts +++ b/src/background/data/Homeworks.ts @@ -25,9 +25,7 @@ const fetchHomeworks = async (): Promise => { // @ts-expect-error let firstDate = account.instance?.instance?.firstDate || null; if (!firstDate) { - firstDate = new Date(); - firstDate.setMonth(8); - firstDate.setDate(1); + firstDate = new Date(Date.UTC(new Date().getFullYear(), 8, 1)); } const firstDateEpoch = dateToEpochWeekNumber(firstDate); diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index f357305af..daa0bf0aa 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -85,9 +85,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { // @ts-expect-error let firstDate = account?.instance?.instance?.firstDate || null; if (!firstDate) { - firstDate = new Date(); - firstDate.setMonth(8); - firstDate.setDate(1); + firstDate = new Date(Date.UTC(new Date().getFullYear(), 8, 1)); } const firstDateEpoch = dateToEpochWeekNumber(firstDate); From 06aff80661082dc4f9825dfbe410eccef2f473c1 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 2 Apr 2025 14:28:23 +0200 Subject: [PATCH 1122/1144] =?UTF-8?q?feat:=20am=C3=A9liorer=20l'affichage?= =?UTF-8?q?=20des=20journaux=20de=20d=C3=A9veloppement=20avec=20un=20forma?= =?UTF-8?q?t=20de=20date=20relatif?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/SettingsDevLogs.tsx | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/views/settings/SettingsDevLogs.tsx b/src/views/settings/SettingsDevLogs.tsx index 87e36faa9..eb51743cb 100644 --- a/src/views/settings/SettingsDevLogs.tsx +++ b/src/views/settings/SettingsDevLogs.tsx @@ -34,13 +34,14 @@ import { FadeOutUp, } from "react-native-reanimated"; import { animPapillon } from "@/utils/ui/animations"; -import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; +import { useTheme } from "@react-navigation/native"; import { useAlert } from "@/providers/AlertProvider"; import MissingItem from "@/components/Global/MissingItem"; -import formatDate from "@/utils/format/format_date_complets"; import ResponsiveTextInput from "@/components/FirstInstallation/ResponsiveTextInput"; +import { formatDistanceToNow } from "date-fns"; +import { fr } from "date-fns/locale"; -const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { +const SettingsDevLogs: Screen<"SettingsDevLogs"> = () => { const { colors } = useTheme(); const [logs, setLogs] = useState([]); const [searchTerms, setSearchTerms] = useState(""); @@ -58,7 +59,7 @@ const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { ); setLoading(false); }); - }, [navigation]); + }, []); return ( = ({ navigation }) => { exiting={animPapillon(FadeOutUp)} > {logs.slice().reverse().map((log, index) => { + if (Number.isNaN(new Date(log.date).getTime())) return; + if (log.message.toLowerCase().includes(searchTerms.toLowerCase())) { return ( = ({ navigation }) => { > {log.message} - {formatDate(log.date)} à {new Date(log.date).getHours()}: - {new Date(log.date).getMinutes()}: - {new Date(log.date).getSeconds()} + {formatDistanceToNow(log.date, { + addSuffix: true, + includeSeconds: true, + locale: fr, + })} {log.from} From fb7c9d3ef7c6f2966f9c34fdba488708f1b079d9 Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 2 Apr 2025 16:18:16 +0200 Subject: [PATCH 1123/1144] =?UTF-8?q?Ajout=20de=20couleurs=20plus=20consis?= =?UTF-8?q?tentes=20pour=20les=20Switch=20dans=20les=20param=C3=A8tres?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Settings/NotificationContainerCard.tsx | 19 ++++++++++++------- src/views/settings/SettingsAccessibility.tsx | 3 +++ src/views/settings/SettingsMagic.tsx | 12 ++++++++++++ src/views/settings/SettingsMultiService.tsx | 6 ++++++ src/views/settings/SettingsNotifications.tsx | 10 ++++++---- src/views/settings/SettingsProfile.tsx | 16 ++++++++++++---- src/views/settings/SettingsTabs.tsx | 12 ++++++++++++ 7 files changed, 63 insertions(+), 15 deletions(-) diff --git a/src/components/Settings/NotificationContainerCard.tsx b/src/components/Settings/NotificationContainerCard.tsx index b3325deb1..40208fc68 100644 --- a/src/components/Settings/NotificationContainerCard.tsx +++ b/src/components/Settings/NotificationContainerCard.tsx @@ -138,10 +138,12 @@ const NotificationContainerCard = ({ trailing={ isEnable !== null && isEnable !== undefined ? ( = () => { const theme = useTheme(); @@ -101,6 +102,7 @@ const SettingsAccessibility: Screen<"SettingsAccessibility"> = () => { false: theme.colors.border, true: theme.colors.primary, }} + thumbColor={theme.colors.text} value={enableSon} onValueChange={(value) => setEnableSon(value)} /> @@ -128,6 +130,7 @@ const SettingsAccessibility: Screen<"SettingsAccessibility"> = () => { false: theme.colors.border, true: theme.colors.primary, }} + thumbColor={theme.colors.text} value={enableHaptics} onValueChange={(value) => setEnableHaptics(value)} /> diff --git a/src/views/settings/SettingsMagic.tsx b/src/views/settings/SettingsMagic.tsx index 4d20ad463..4c9cb377e 100644 --- a/src/views/settings/SettingsMagic.tsx +++ b/src/views/settings/SettingsMagic.tsx @@ -29,6 +29,12 @@ const SettingsMagic: Screen<"SettingsMagic"> = ({ navigation }) => { mutateProperty("personalization", { MagicNews: value })} /> @@ -54,6 +60,12 @@ const SettingsMagic: Screen<"SettingsMagic"> = ({ navigation }) => { mutateProperty("personalization", { MagicHomeworks: value })} /> diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index f0cb01374..15571645f 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -152,6 +152,12 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => { if (multiServiceEnabled) { diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index b013af594..8d9a11e47 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -176,10 +176,12 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ entering={anim2Papillon(FadeInDown).delay(70 * index)} trailing={ = ({ navigation }) => { setHideNameOnHomeScreen(!hideNameOnHomeScreen)} - trackColor={{false: theme.colors.border, true: theme.colors.primary}} - thumbColor={theme.colors.background} + trackColor={ + { + false: theme.colors.border, true: theme.colors.primary + } + } + thumbColor={theme.colors.text} /> } > @@ -326,8 +330,12 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { setHideProfilePicOnHomeScreen(!hideProfilePicOnHomeScreen)} - trackColor={{false: theme.colors.border, true: theme.colors.primary}} - thumbColor={theme.colors.background} + trackColor={ + { + false: theme.colors.border, true: theme.colors.primary + } + } + thumbColor={theme.colors.text} /> } > diff --git a/src/views/settings/SettingsTabs.tsx b/src/views/settings/SettingsTabs.tsx index 4938f775b..1695a8078 100644 --- a/src/views/settings/SettingsTabs.tsx +++ b/src/views/settings/SettingsTabs.tsx @@ -510,6 +510,12 @@ const SettingsTabs = () => { icon={} trailing={ { setHideTabTitles(!hideTabTitles); @@ -531,6 +537,12 @@ const SettingsTabs = () => { icon={} trailing={ { setShowTabBackground(!showTabBackground); From 28f5f9f8d4956bad1e018f195e1df0b7cddbb624 Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 2 Apr 2025 16:31:17 +0200 Subject: [PATCH 1124/1144] =?UTF-8?q?Supprime=20une=20ligne=20inutile=20in?= =?UTF-8?q?s=C3=A9r=C3=A9e=20par=20accident?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/SettingsAccessibility.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/views/settings/SettingsAccessibility.tsx b/src/views/settings/SettingsAccessibility.tsx index 316e72d60..bf5cffbb2 100644 --- a/src/views/settings/SettingsAccessibility.tsx +++ b/src/views/settings/SettingsAccessibility.tsx @@ -15,7 +15,6 @@ import PapillonCheckbox from "@/components/Global/PapillonCheckbox"; import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; import { Switch } from "react-native-gesture-handler"; import AccessibilityContainerCard from "@/components/Settings/AccesilityContainerCard"; -import { th } from "date-fns/locale"; const SettingsAccessibility: Screen<"SettingsAccessibility"> = () => { const theme = useTheme(); From 665c33b4f360a015a01171bf224843d3d1f0ce5a Mon Sep 17 00:00:00 2001 From: yaourtLactel <=> Date: Wed, 2 Apr 2025 17:05:02 +0200 Subject: [PATCH 1125/1144] =?UTF-8?q?Compatibilit=C3=A9=20des=20nouvelles?= =?UTF-8?q?=20Switch=20avec=20le=20mode=20clair?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Settings/NotificationContainerCard.tsx | 4 ++-- src/views/settings/SettingsAccessibility.tsx | 4 ++-- src/views/settings/SettingsMagic.tsx | 4 ++-- src/views/settings/SettingsMultiService.tsx | 2 +- src/views/settings/SettingsNotifications.tsx | 2 +- src/views/settings/SettingsProfile.tsx | 4 ++-- src/views/settings/SettingsTabs.tsx | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/Settings/NotificationContainerCard.tsx b/src/components/Settings/NotificationContainerCard.tsx index 40208fc68..15fcc771f 100644 --- a/src/components/Settings/NotificationContainerCard.tsx +++ b/src/components/Settings/NotificationContainerCard.tsx @@ -143,7 +143,7 @@ const NotificationContainerCard = ({ false: theme.colors.border, true: theme.colors.primary } } - thumbColor={theme.colors.text} + thumbColor={theme.dark ? theme.colors.text : theme.colors.background} style={{ marginRight: 10, }} @@ -192,7 +192,7 @@ const NotificationContainerCard = ({ false: theme.colors.border, true: theme.colors.primary } } - thumbColor={theme.colors.text} + thumbColor={theme.dark ? theme.colors.text : theme.colors.background} style={{ marginRight: 10, }} diff --git a/src/views/settings/SettingsAccessibility.tsx b/src/views/settings/SettingsAccessibility.tsx index bf5cffbb2..119ac2cc4 100644 --- a/src/views/settings/SettingsAccessibility.tsx +++ b/src/views/settings/SettingsAccessibility.tsx @@ -101,7 +101,7 @@ const SettingsAccessibility: Screen<"SettingsAccessibility"> = () => { false: theme.colors.border, true: theme.colors.primary, }} - thumbColor={theme.colors.text} + thumbColor={theme.dark ? theme.colors.text : theme.colors.background} value={enableSon} onValueChange={(value) => setEnableSon(value)} /> @@ -129,7 +129,7 @@ const SettingsAccessibility: Screen<"SettingsAccessibility"> = () => { false: theme.colors.border, true: theme.colors.primary, }} - thumbColor={theme.colors.text} + thumbColor={theme.dark ? theme.colors.text : theme.colors.background} value={enableHaptics} onValueChange={(value) => setEnableHaptics(value)} /> diff --git a/src/views/settings/SettingsMagic.tsx b/src/views/settings/SettingsMagic.tsx index 4c9cb377e..bc5427b3b 100644 --- a/src/views/settings/SettingsMagic.tsx +++ b/src/views/settings/SettingsMagic.tsx @@ -34,7 +34,7 @@ const SettingsMagic: Screen<"SettingsMagic"> = ({ navigation }) => { false: theme.colors.border, true: theme.colors.primary } } - thumbColor={theme.colors.text} + thumbColor={theme.dark ? theme.colors.text : theme.colors.background} value={account?.personalization?.MagicNews ?? false} onValueChange={(value) => mutateProperty("personalization", { MagicNews: value })} /> @@ -65,7 +65,7 @@ const SettingsMagic: Screen<"SettingsMagic"> = ({ navigation }) => { false: theme.colors.border, true: theme.colors.primary } } - thumbColor={theme.colors.text} + thumbColor={theme.dark ? theme.colors.text : theme.colors.background} value={account?.personalization?.MagicHomeworks ?? false} onValueChange={(value) => mutateProperty("personalization", { MagicHomeworks: value })} /> diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index 15571645f..0648b1e9e 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -157,7 +157,7 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => false: theme.colors.border, true: theme.colors.primary } } - thumbColor={theme.colors.text} + thumbColor={theme.dark ? theme.colors.text : theme.colors.background} value={multiServiceEnabled ?? false} onValueChange={() => { if (multiServiceEnabled) { diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 8d9a11e47..8eccdf9bb 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -181,7 +181,7 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ false: theme.colors.border, true: theme.colors.primary } } - thumbColor={theme.colors.text} + thumbColor={theme.dark ? theme.colors.text : theme.colors.background} value={ account.personalization.notifications?.[ notification.personalizationValue as keyof typeof notifications diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index 51511bc5a..cee94d2e3 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -310,7 +310,7 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { false: theme.colors.border, true: theme.colors.primary } } - thumbColor={theme.colors.text} + thumbColor={theme.dark ? theme.colors.text : theme.colors.background} /> } > @@ -335,7 +335,7 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { false: theme.colors.border, true: theme.colors.primary } } - thumbColor={theme.colors.text} + thumbColor={theme.dark ? theme.colors.text : theme.colors.background} /> } > diff --git a/src/views/settings/SettingsTabs.tsx b/src/views/settings/SettingsTabs.tsx index 1695a8078..25e8bd5cf 100644 --- a/src/views/settings/SettingsTabs.tsx +++ b/src/views/settings/SettingsTabs.tsx @@ -515,7 +515,7 @@ const SettingsTabs = () => { false: theme.colors.border, true: theme.colors.primary } } - thumbColor={theme.colors.text} + thumbColor={theme.dark ? theme.colors.text : theme.colors.background} value={!hideTabTitles} onValueChange={() => { setHideTabTitles(!hideTabTitles); @@ -542,7 +542,7 @@ const SettingsTabs = () => { false: theme.colors.border, true: theme.colors.primary } } - thumbColor={theme.colors.text} + thumbColor={theme.dark ? theme.colors.text : theme.colors.background} value={showTabBackground} onValueChange={() => { setShowTabBackground(!showTabBackground); From 3f5e5a240f8a3a9270cdeaa028c8ddf9babcebe8 Mon Sep 17 00:00:00 2001 From: yaourtLactel <=> Date: Wed, 2 Apr 2025 18:52:26 +0200 Subject: [PATCH 1126/1144] Changement de 'theme.colors' pour 'colors' dans SettingsMagic --- src/views/settings/SettingsMagic.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/views/settings/SettingsMagic.tsx b/src/views/settings/SettingsMagic.tsx index bc5427b3b..c195acb2b 100644 --- a/src/views/settings/SettingsMagic.tsx +++ b/src/views/settings/SettingsMagic.tsx @@ -31,10 +31,10 @@ const SettingsMagic: Screen<"SettingsMagic"> = ({ navigation }) => { mutateProperty("personalization", { MagicNews: value })} /> @@ -62,10 +62,10 @@ const SettingsMagic: Screen<"SettingsMagic"> = ({ navigation }) => { mutateProperty("personalization", { MagicHomeworks: value })} /> From 0044909929af6ae43064d0b28f8cae0dc935df2c Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 2 Apr 2025 19:25:34 +0200 Subject: [PATCH 1127/1144] =?UTF-8?q?feat:=20ajouter=20une=20fonction=20de?= =?UTF-8?q?=20calcul=20du=20num=C3=A9ro=20de=20semaine=20et=20mettre=20?= =?UTF-8?q?=C3=A0=20jour=20l'affichage=20des=20devoirs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Homeworks.ts | 27 +++++------------------ src/utils/epochWeekNumber.ts | 25 ++++++++++++++++----- src/views/account/Homeworks/Homeworks.tsx | 4 ++-- 3 files changed, 27 insertions(+), 29 deletions(-) diff --git a/src/background/data/Homeworks.ts b/src/background/data/Homeworks.ts index 15d682832..73e03402f 100644 --- a/src/background/data/Homeworks.ts +++ b/src/background/data/Homeworks.ts @@ -2,7 +2,7 @@ import { getCurrentAccount } from "../utils/accounts"; import { papillonNotify } from "../Notifications"; import { getHomeworks, updateHomeworksState } from "../utils/homeworks"; import { Homework } from "@/services/shared/Homework"; -import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; +import { calculateWeekNumber, dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; import parse_homeworks from "@/utils/format/format_pronote_homeworks"; const getDifferences = ( @@ -81,10 +81,7 @@ const fetchHomeworks = async (): Promise => { { id: `${account.name}-homeworks`, title: `[${account.name}] Nouveau devoir en ${differencesHwSemaineActuelle[0].subject}`, - subtitle: `Semaine ${( - ((SemaineAct - (firstDateEpoch % 52)) % 52) + - 1 - ).toString()}`, + subtitle: `Semaine ${calculateWeekNumber(new Date()).toString()}`, body: parse_homeworks(differencesHwSemaineActuelle[0].content), data: { accountID: account.localID, @@ -101,10 +98,7 @@ const fetchHomeworks = async (): Promise => { { id: `${account.name}-homeworks`, title: `[${account.name}] Nouveau devoir en ${differencesHwSemaineProchaine[0].subject}`, - subtitle: `Semaine ${( - ((SemaineAct - (firstDateEpoch % 52)) % 52) + - 2 - ).toString()}`, + subtitle: `Semaine ${(calculateWeekNumber(new Date()) + 1).toString()}`, body: parse_homeworks(differencesHwSemaineProchaine[0].content), data: { accountID: account.localID, @@ -136,22 +130,13 @@ const fetchHomeworks = async (): Promise => { let subtitle = "Semaine "; if (differencesHwSemaineActuelle.length > 0) { - subtitle += ( - ((SemaineAct - (firstDateEpoch % 52)) % 52) + - 1 - ).toString(); + subtitle += calculateWeekNumber(new Date()).toString(); } if (differencesHwSemaineProchaine.length > 0) { if (differencesHwSemaineActuelle.length > 0) { - subtitle += `et ${( - ((SemaineAct - (firstDateEpoch % 52)) % 52) + - 2 - ).toString()}`; + subtitle += ` et ${(calculateWeekNumber(new Date()) + 1).toString()}`; } else { - subtitle += ( - ((SemaineAct - (firstDateEpoch % 52)) % 52) + - 2 - ).toString(); + subtitle += (calculateWeekNumber(new Date()) + 1).toString(); } } diff --git a/src/utils/epochWeekNumber.ts b/src/utils/epochWeekNumber.ts index bb4110469..f6ab5582f 100644 --- a/src/utils/epochWeekNumber.ts +++ b/src/utils/epochWeekNumber.ts @@ -31,12 +31,13 @@ const EPOCH_WN_CONFIG = { const dayToWeekCommonDay = (date: Date): Date => { const _date = new Date(date); + const day = _date.getDay(); _date.setHours(EPOCH_WN_CONFIG.setHour, 0, 0, 0); - _date.setDate(_date.getDate() - ( (7 + _date.getDay() - 1) %7 ) + EPOCH_WN_CONFIG.setMiddleDay - 1); - // the (7+ ... -1 ) %7 is to have Monday as the first day (0) of the week and Sunday as last day (7) cause JS start the week on Sunday ¯\_(ツ)_/¯ - // In details : the 7+ is to avoid negative value, the -1 is to have Monday as the first day of the week and the %7 is to have the right day number (0 to 6) - // In details : setMiddleDay - 1 is to have the middle day of the week, aka Wednesday - // Its to avoid the fact that Sunday is in the next week with simpler JS code. + _date.setDate( + _date.getDate() - + ((7 + day - EPOCH_WN_CONFIG.setStartDay) % 7) + + EPOCH_WN_CONFIG.setMiddleDay + ); return _date; }; @@ -55,7 +56,7 @@ export const epochWNToPronoteWN = ( export const dateToEpochWeekNumber = (date: Date): number => { const commonDay = dayToWeekCommonDay(date); return Math.floor( - (commonDay.getTime() + EPOCH_WN_CONFIG.adjustEpochInitialDate - 172800000) / + (commonDay.getTime() + EPOCH_WN_CONFIG.adjustEpochInitialDate) / EPOCH_WN_CONFIG.numberOfMsInAWeek ); }; @@ -121,3 +122,15 @@ export const epochWMToCalendarWeekNumber = ( const yearStart = new Date(Date.UTC(date.getUTCFullYear(), 0, 1)); return Math.ceil(((date.getTime() - yearStart.getTime()) / 86400000 + 1) / 7); }; + +export const calculateWeekNumber = (date: Date): number => { + const epochWeekNumber = dateToEpochWeekNumber(date); + const firstSeptemberEpochWeekNumber = dateToEpochWeekNumber( + new Date(Date.UTC(date.getUTCFullYear(), 8, 1)) + ); + + const relativeWeekNumber = + ((epochWeekNumber - firstSeptemberEpochWeekNumber + 52) % 52) + 1; + + return relativeWeekNumber; +}; diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index daa0bf0aa..883547c21 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -15,7 +15,7 @@ import { ListRenderItem, Pressable } from "react-native"; -import { dateToEpochWeekNumber, epochWNToDate } from "@/utils/epochWeekNumber"; +import { calculateWeekNumber, dateToEpochWeekNumber, epochWNToDate } from "@/utils/epochWeekNumber"; import * as StoreReview from "expo-store-review"; @@ -518,7 +518,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { layout={animPapillon(LinearTransition)} > Date: Wed, 2 Apr 2025 19:37:36 +0200 Subject: [PATCH 1128/1144] fix: corriger le texte d'information en mode debug pour un ton plus informel --- src/views/account/Home/ModalContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/account/Home/ModalContent.tsx b/src/views/account/Home/ModalContent.tsx index 8aaf9d680..67921b8c9 100644 --- a/src/views/account/Home/ModalContent.tsx +++ b/src/views/account/Home/ModalContent.tsx @@ -148,7 +148,7 @@ const ModalContent: React.FC = ({ navigation, refresh, endRef - Vous êtes actuellement en mode debug, vous pouvez acceder au menu debug en appuyant sur cette carte. + Tu es actuellement en mode debug, tu peux accéder au menu debug en appuyant sur cette carte. From 85c827def9d3877d9aa6146f6dc15c594cb5dc71 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 2 Apr 2025 19:49:07 +0200 Subject: [PATCH 1129/1144] =?UTF-8?q?fix:=20am=C3=A9liorer=20la=20s=C3=A9l?= =?UTF-8?q?ection=20de=20la=20mati=C3=A8re=20dans=20l'=C3=A9cran=20d'ajout?= =?UTF-8?q?=20de=20devoirs=20(=C3=A9vite=20un=20plantage=20btw)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/account/Homeworks/AddHomework.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/account/Homeworks/AddHomework.tsx b/src/views/account/Homeworks/AddHomework.tsx index 019932339..267ebca0b 100644 --- a/src/views/account/Homeworks/AddHomework.tsx +++ b/src/views/account/Homeworks/AddHomework.tsx @@ -41,7 +41,8 @@ const AddHomeworkScreen: Screen<"AddHomework"> = ({ route, navigation }) => { const allHomeworks = Object.values(homeworks).flat(); const homework = allHomeworks.find(hw => hw.id === route.params?.hwid); if (homework) { - setSelectedPretty(localSubjects[homework.subject]); + const THEpretty = Object.entries(localSubjects).find((element) => element[1].pretty === homework.subject); + if (THEpretty) setSelectedPretty(THEpretty[1]); setIdHomework(parseInt(homework.id)); setContentHomework(homework.content); setDateHomework(homework.due); From d16c770ca7f248016fc8dff12acd2b959de9d1d1 Mon Sep 17 00:00:00 2001 From: raphckrman <41128238+raphckrman@users.noreply.github.com> Date: Wed, 2 Apr 2025 22:54:07 +0200 Subject: [PATCH 1130/1144] fix(RestaurantQRCode): update visibility check to use local time instead of UTC --- src/widgets/Components/RestaurantQRCode.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/Components/RestaurantQRCode.tsx b/src/widgets/Components/RestaurantQRCode.tsx index 53c97ba2c..06acd7807 100644 --- a/src/widgets/Components/RestaurantQRCode.tsx +++ b/src/widgets/Components/RestaurantQRCode.tsx @@ -87,7 +87,7 @@ const RestaurantQRCodeWidget = forwardRef(({ useEffect(() => { const updateVisibility = () => { - const currentHour = new Date().getUTCHours(); + const currentHour = new Date().getHours(); const shouldShow = allCards?.some(card => card.cardnumber) && currentHour >= 11 && currentHour < 14; setHidden(!shouldShow); }; From 3760e73ea41f3dfa77a646e43256c8132d121cdc Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Thu, 3 Apr 2025 08:47:59 +0200 Subject: [PATCH 1131/1144] =?UTF-8?q?feat:=20ajout=20de=20l'interface=20Se?= =?UTF-8?q?ttingsSubItem=20pour=20structurer=20les=20=C3=A9l=C3=A9ments=20?= =?UTF-8?q?de=20param=C3=A8tres?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/Settings.tsx | 11 +++++++++++ src/views/settings/SettingsExperimental.tsx | 4 ++-- src/views/settings/SettingsPersonalization.tsx | 5 +++-- src/views/settings/SettingsProject.tsx | 7 ++++--- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index 79706bc86..3074fecb3 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -26,6 +26,17 @@ import { BadgeHelp } from "lucide-react-native"; +export interface SettingsSubItem { + icon: React.ReactNode; + colors: string[]; + label: string; + description?: string; + onPress: () => void; + android?: boolean; + beta?: boolean; + disabled?: boolean; +} + import { NativeIconGradient, NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; import ModalHandle from "@/components/Modals/ModalHandle"; import AccountContainerCard from "@/components/Settings/AccountContainerCard"; diff --git a/src/views/settings/SettingsExperimental.tsx b/src/views/settings/SettingsExperimental.tsx index cc06b9c19..5914c07d4 100644 --- a/src/views/settings/SettingsExperimental.tsx +++ b/src/views/settings/SettingsExperimental.tsx @@ -6,6 +6,7 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import { NativeIconGradient, NativeItem, NativeList, NativeText } from "@/components/Global/NativeComponents"; import { WandSparkles, Blocks, Puzzle } from "lucide-react-native"; import { useFlagsStore } from "@/stores/flags"; +import { SettingsSubItem } from "./Settings"; const SettingsExperimental: Screen<"SettingsExperimental"> = ({ navigation }) => { const theme = useTheme(); @@ -13,7 +14,7 @@ const SettingsExperimental: Screen<"SettingsExperimental"> = ({ navigation }) => const insets = useSafeAreaInsets(); const defined = useFlagsStore(state => state.defined); - const items = [ + const items: SettingsSubItem[] = [ { icon: , colors: ["#FB8C00", "#FFA726"], @@ -61,7 +62,6 @@ const SettingsExperimental: Screen<"SettingsExperimental"> = ({ navigation }) => /> } trailing={ - // @ts-expect-error : on ignore la condition item.beta && ( = ({ navigation }) => { const theme = useTheme(); @@ -17,7 +18,7 @@ const SettingsPersonalization: Screen<"SettingsPersonalization"> = ({ navigation const { isTablet } = useScreenDimensions(); // These are regular items that stay within the settings navigation - const regularItems = [ + const regularItems: SettingsSubItem[] = [ { icon: , colors: ["#4CAF50", "#8BC34A"], @@ -34,7 +35,7 @@ const SettingsPersonalization: Screen<"SettingsPersonalization"> = ({ navigation ]; // These are special items that close the settings modal and show a full-screen view - const specialItems = [ + const specialItems: SettingsSubItem[] = [ { icon: , colors: ["#9C27B0", "#BA68C8"], diff --git a/src/views/settings/SettingsProject.tsx b/src/views/settings/SettingsProject.tsx index 57f8bab0c..2e21c4e33 100644 --- a/src/views/settings/SettingsProject.tsx +++ b/src/views/settings/SettingsProject.tsx @@ -7,6 +7,7 @@ import { NativeIconGradient, NativeItem, NativeList, NativeText } from "@/compon import { Scroll, HelpCircle, Bug, Info } from "lucide-react-native"; import * as WebBrowser from "expo-web-browser"; import { WebBrowserPresentationStyle } from "expo-web-browser"; +import { SettingsSubItem } from "./Settings"; const SettingsProject: Screen<"SettingsProject"> = ({ navigation }) => { const theme = useTheme(); @@ -19,7 +20,7 @@ const SettingsProject: Screen<"SettingsProject"> = ({ navigation }) => { }); }; - const items = [ + const items: SettingsSubItem[] = [ { icon: , colors: ["#FF5722", "#FF8A65"], // Deep orange to light orange @@ -69,11 +70,11 @@ const SettingsProject: Screen<"SettingsProject"> = ({ navigation }) => { {item.label} - {"description" in item && item.description && + {item.description && ( {item.description} - } + )} ))} From 2477ba8d66043e29060e61904c03f3a39431201f Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Thu, 3 Apr 2025 08:53:11 +0200 Subject: [PATCH 1132/1144] feat: remplacement de TouchableOpacity par NativeItem pour le menu debug --- src/views/account/Home/ModalContent.tsx | 29 +++++++++++++------------ 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/views/account/Home/ModalContent.tsx b/src/views/account/Home/ModalContent.tsx index 67921b8c9..a346cd967 100644 --- a/src/views/account/Home/ModalContent.tsx +++ b/src/views/account/Home/ModalContent.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useState, useMemo, memo } from "react"; -import { NativeList, NativeText } from "@/components/Global/NativeComponents"; +import { NativeItem, NativeList, NativeText } from "@/components/Global/NativeComponents"; import Reanimated, { FadeInUp, FadeOutDown, LinearTransition } from "react-native-reanimated"; import { Bug, Sparkles, X } from "lucide-react-native"; import { usePapillonTheme as useTheme } from "@/utils/ui/theme"; @@ -130,28 +130,29 @@ const ModalContent: React.FC = ({ navigation, refresh, endRef {(defined("force_debugmode") || __DEV__) && ( - navigation.navigate("DevMenu")} + - - - + navigation.navigate("DevMenu")} + chevron={true} + leading={} + style={{ + paddingHorizontal: 0, + }} + > + Mode debug - - - Tu es actuellement en mode debug, tu peux accéder au menu debug en appuyant sur cette carte. - - - + + )} From b7750155886beb7b8b50a5acf7b2a3d586e8c487 Mon Sep 17 00:00:00 2001 From: Pixel Date: Thu, 3 Apr 2025 10:45:33 +0200 Subject: [PATCH 1133/1144] Update src/components/Settings/NotificationContainerCard.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 𝕂𝕪𝕝𝕚𝕒𝕟 <164187100+Kgeek33@users.noreply.github.com> --- src/components/Settings/NotificationContainerCard.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/Settings/NotificationContainerCard.tsx b/src/components/Settings/NotificationContainerCard.tsx index 15fcc771f..78cc1cfbb 100644 --- a/src/components/Settings/NotificationContainerCard.tsx +++ b/src/components/Settings/NotificationContainerCard.tsx @@ -140,10 +140,11 @@ const NotificationContainerCard = ({ Date: Thu, 3 Apr 2025 10:45:54 +0200 Subject: [PATCH 1134/1144] Update src/components/Settings/NotificationContainerCard.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 𝕂𝕪𝕝𝕚𝕒𝕟 <164187100+Kgeek33@users.noreply.github.com> --- src/components/Settings/NotificationContainerCard.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/Settings/NotificationContainerCard.tsx b/src/components/Settings/NotificationContainerCard.tsx index 78cc1cfbb..41d086946 100644 --- a/src/components/Settings/NotificationContainerCard.tsx +++ b/src/components/Settings/NotificationContainerCard.tsx @@ -190,10 +190,11 @@ const NotificationContainerCard = ({ Date: Thu, 3 Apr 2025 10:46:29 +0200 Subject: [PATCH 1135/1144] Update src/views/settings/SettingsMagic.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 𝕂𝕪𝕝𝕚𝕒𝕟 <164187100+Kgeek33@users.noreply.github.com> --- src/views/settings/SettingsMagic.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/settings/SettingsMagic.tsx b/src/views/settings/SettingsMagic.tsx index c195acb2b..582cd57be 100644 --- a/src/views/settings/SettingsMagic.tsx +++ b/src/views/settings/SettingsMagic.tsx @@ -31,7 +31,8 @@ const SettingsMagic: Screen<"SettingsMagic"> = ({ navigation }) => { Date: Thu, 3 Apr 2025 10:46:41 +0200 Subject: [PATCH 1136/1144] Update src/views/settings/SettingsMagic.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 𝕂𝕪𝕝𝕚𝕒𝕟 <164187100+Kgeek33@users.noreply.github.com> --- src/views/settings/SettingsMagic.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/settings/SettingsMagic.tsx b/src/views/settings/SettingsMagic.tsx index 582cd57be..b9c4e0ed8 100644 --- a/src/views/settings/SettingsMagic.tsx +++ b/src/views/settings/SettingsMagic.tsx @@ -63,7 +63,8 @@ const SettingsMagic: Screen<"SettingsMagic"> = ({ navigation }) => { Date: Thu, 3 Apr 2025 10:46:51 +0200 Subject: [PATCH 1137/1144] Update src/views/settings/SettingsMultiService.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 𝕂𝕪𝕝𝕚𝕒𝕟 <164187100+Kgeek33@users.noreply.github.com> --- src/views/settings/SettingsMultiService.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index 0648b1e9e..4583d1523 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -154,7 +154,8 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => Date: Thu, 3 Apr 2025 10:47:14 +0200 Subject: [PATCH 1138/1144] Update src/views/settings/SettingsNotifications.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 𝕂𝕪𝕝𝕚𝕒𝕟 <164187100+Kgeek33@users.noreply.github.com> --- src/views/settings/SettingsNotifications.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 8eccdf9bb..7c0876f0f 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -178,10 +178,11 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ Date: Thu, 3 Apr 2025 10:47:35 +0200 Subject: [PATCH 1139/1144] Update src/views/settings/SettingsProfile.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 𝕂𝕪𝕝𝕚𝕒𝕟 <164187100+Kgeek33@users.noreply.github.com> --- src/views/settings/SettingsProfile.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index cee94d2e3..6e975418f 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -307,7 +307,8 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { onValueChange={() => setHideNameOnHomeScreen(!hideNameOnHomeScreen)} trackColor={ { - false: theme.colors.border, true: theme.colors.primary + false: theme.colors.border, + true: theme.colors.primary } } thumbColor={theme.dark ? theme.colors.text : theme.colors.background} From 0709d225dce619e06eda6d30b143d2dd0c156136 Mon Sep 17 00:00:00 2001 From: Pixel Date: Thu, 3 Apr 2025 10:47:42 +0200 Subject: [PATCH 1140/1144] Update src/views/settings/SettingsProfile.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 𝕂𝕪𝕝𝕚𝕒𝕟 <164187100+Kgeek33@users.noreply.github.com> --- src/views/settings/SettingsProfile.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index 6e975418f..05e13ebbd 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -333,7 +333,8 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { onValueChange={() => setHideProfilePicOnHomeScreen(!hideProfilePicOnHomeScreen)} trackColor={ { - false: theme.colors.border, true: theme.colors.primary + false: theme.colors.border, + true: theme.colors.primary } } thumbColor={theme.dark ? theme.colors.text : theme.colors.background} From 9cfe5f09fd3065a6ba20fb21feb633d7685e57d2 Mon Sep 17 00:00:00 2001 From: Pixel Date: Thu, 3 Apr 2025 10:47:51 +0200 Subject: [PATCH 1141/1144] Update src/views/settings/SettingsTabs.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 𝕂𝕪𝕝𝕚𝕒𝕟 <164187100+Kgeek33@users.noreply.github.com> --- src/views/settings/SettingsTabs.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/settings/SettingsTabs.tsx b/src/views/settings/SettingsTabs.tsx index 25e8bd5cf..ec60b16f8 100644 --- a/src/views/settings/SettingsTabs.tsx +++ b/src/views/settings/SettingsTabs.tsx @@ -512,7 +512,8 @@ const SettingsTabs = () => { Date: Thu, 3 Apr 2025 10:48:00 +0200 Subject: [PATCH 1142/1144] Update src/views/settings/SettingsTabs.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 𝕂𝕪𝕝𝕚𝕒𝕟 <164187100+Kgeek33@users.noreply.github.com> --- src/views/settings/SettingsTabs.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/settings/SettingsTabs.tsx b/src/views/settings/SettingsTabs.tsx index ec60b16f8..6dc062bf6 100644 --- a/src/views/settings/SettingsTabs.tsx +++ b/src/views/settings/SettingsTabs.tsx @@ -540,7 +540,8 @@ const SettingsTabs = () => { Date: Thu, 3 Apr 2025 19:49:30 +0200 Subject: [PATCH 1143/1144] lint --- src/views/welcome/ChangelogScreen.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/welcome/ChangelogScreen.tsx b/src/views/welcome/ChangelogScreen.tsx index 8286e47d8..bb21b7d1b 100644 --- a/src/views/welcome/ChangelogScreen.tsx +++ b/src/views/welcome/ChangelogScreen.tsx @@ -330,7 +330,7 @@ const ChangelogFeature: React.FC<{ feature: Feature, navigation: any, theme: any navigation.goBack(); navigation.navigate(feature.navigation); } - catch {} + catch { /* empty */ } } } : undefined} > From 740cadf52d11e705178dde1001ee21463beb1b5c Mon Sep 17 00:00:00 2001 From: Tryon Date: Sun, 6 Apr 2025 22:15:17 +0200 Subject: [PATCH 1144/1144] =?UTF-8?q?feat:=20ajout=20d'une=20nouvelle=20im?= =?UTF-8?q?age=20d'en-t=C3=AAte=20"star"=20dans=20CustomizeHeader?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/headers/star.png | Bin 0 -> 9098 bytes src/views/account/Home/Modal/CustomizeHeader.tsx | 4 ++++ 2 files changed, 4 insertions(+) create mode 100644 assets/headers/star.png diff --git a/assets/headers/star.png b/assets/headers/star.png new file mode 100644 index 0000000000000000000000000000000000000000..b11d0d93a9889c5005908e4f4e76fc262f89b6b4 GIT binary patch literal 9098 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 z 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 literal 0 HcmV?d00001 diff --git a/assets/headers/topography.png b/assets/headers/topography.png new file mode 100644 index 0000000000000000000000000000000000000000..baa4eaf337f9a52642ef87d77d2618acd19ece5f GIT binary patch literal 58758 zcmXVXWmFqn(=G1qE`j33-Q9{5D^lFup-`;2yE_Dz7NKT|LM&IH)0XhNaxk2IhxzFjf-H&muP`9TP8zZSjeEd1e~z6jxqae7?r?4z};Kle8$E8(UO98z~ASpBKGMGtF;Q_^Dl}C`{TvU(N%#~D`N?W6qhAg zz^sNXDUTosn=uqeygW&Q{x@2D?GIKP2-n6UI(t(|f9?xY+`DM1$n$aWnH+O4-+H<4d|>S3xZQf- zMXR2`g?BmErZcrU89Mjx(CCFGQX{C)I@|w*RdZQll|k6Fkb;_qkp?qoY4?H7W9?zV zNTFpJSf#52OQ9Zb0I)*|SN-MI7jhY*Fs?sD1fd1sO@91*w1>WjUa`x+Jz zCVP|plT3CzOW5mR8=e(@s7G0fJSh1O>pMBgt21QUcw15O@LN;bo7ew@CskKwgT&Yl zhG**;T*Hs7=F}7kW=R{j73gz%<8J8q!_w9XpY2CazMw?s5#+ZMZjs@(;Api`%PCx* z5kZ42WCMr$aGw*)oLJi74bl4=fqg+V@Gm1zTw23BYdOqd+L&kCHj=JH1-k~^_L2i(le`+*BP4+q&DDVn84sU4fWH+-gyqs}G2rlld!KkQ`<{&Jq~=#5hhcNjw zxJ)aPJOcjc5y=VXU00DA$`PU+9D$1K3A0bzgSx}vs#XKz1;5}6GWBZGX~cALJWJeh z|Ii21Mm@a}+Yk!;4b!21%0pbXr@YQISKOsx`PVY2HmEOyQqgfE)NdGj%I{@Oob#FE z`(4Birg{YxT3<|=D~&szNNsF5JCY-c8OzU`U&<5&@j$oHvbzDJ8axJe13NqDbawT@ z(r?kFX_s(pa+_uTuvr~aRR-}ya*yQ;L=JiMduIJoCr(DdeOS7ltRcKD(PikBT?K3A zwAvu4G>R$Up0W$Fe~~q-_WEx}LND(z--Ak?ByY{>UWQEIEN)=s(-;U3)M3JKuej3= z+QjQ)lkdc?T$eIY>g#KhDzlTz<%Zq`zh%e|+)@AdE>($Z=Bp^M-_ffTd5ho@p>uSZ zs+Mo+E#{X+h>9yQigoz{MXlTI}|LM*|%r zqViT0jDxsXE{_dvH_%cq@TUP#r9Q-cFa9(y`76NZs@R3L-hZ0;uJ6XXpS|XEhk^`x zQ@*4b!MC^LFP7IYuBx#}`=P5Q%l5Ll-GQW51c4SKgNY+fA1YYj z_|S`yLWcZOBc$h!0zRN|J#$((q{DLFSgv3xS4v(vYl)&8aNVo%5dO%C< zvc``(YLJGIhBUf}#32*Mitl1XvMr>&$d7iOfOIE6y&Xk1mvF58R1KfWq?w)_B0Ui> zu7@umP7fs)zJf-9r_?N0S$#{?mOv5%T}hynrO^HZ8f9p&lkwccJus8?WU zmqH?FCHNUn`6}=WPN)A_ANn_=vixI-L;tFC!uCE$osf}gal!OS$u$UX8|@i!y}Xbl zaQ8-Thz#tFv8ezE=khf^kY2k3lOI#7*;7QmAXg^euIlqs&aIE>F9t6Mkfo( z&Sy{u#9Pc$no%76i$@4)x9sLND&hxdt$!Y<_P2itcV^Nnc;9>Rn{0|ZvgjkLf`C7o zI#=jJ3EsYUt5c+fUk{s=A5&aQ3xj zP}jC^{YqR1aQ5JK;7gzoNd~{uvVY*SFe3zXXVw~zev7wDtwM5jc1zNDMbpzsw~UV{Vrtzenl)a!;a7kPG*Jr*4({rO?92aa=M zh$vUr05JE0U{^=DUilL?{)-w-XaW|jZa&qKRfXQv_G^69>!ukZ_3W~bbv%u;i2kml z+|hhow$p&w^4@VEdkl(3%>0><*AzZ5<|$W}H!N89tumF_DA#|RIJPp@F(R00%Jzxv z*~ho7)-9;X3mx->}Py9%i*#)l3b!QcMQT6~3SQwo=fEc4%@&0UqEnw;nk}F5OvKl!?EP0$lgXp%?U^{d;kF_W%umYc$)J3j|J5E8)7xt&1suXmZ*mg@Yh1l zgSF^ky}(GOi6)($XjPmt;BkwUcIgL&eMEvzk*!*2vucoCu+n`MDXG?E$;CoVAD5cF z=zIQXJbK!QFBdOYIJ@>7J}+s0TI!oKQ<4&t+dt0$*Lt-XoA@E9hb`}B1gRQ-#lU%x>qvKu!5rgBXSRG1TkpXnu;AKjC;GpH-G#0m zois$gA{?@TUv$qjppn8aDp$^nLfy>QwPg|4LLHegFC#2_wrF$HTfai^?IxGP50J4VH&3Bj}bel6%?Add-O$unD4}Q4oi#38S zCS|}(bJ?z+?i$J$2ildkiwv8mZKr^JQuV~RMz;R!9q&Sj_ay~?`TR=|w?R%w`U(;u zf{mmKR02uEXHUL7{jIH0;k5$+78u^#oV+itqZCJ172F~BdT|60C1YCNJ5OF6_rP5q zO1FvIEyt{MU9%;m`7HXR13o5UErl+n(p5@WmvNbQ7{};sxJUyj1bHouD5=8hk7w9 z)%)>`Rnb&Z-7TEw51V2Zx2Xk=FDG|1>SR@K^-1yJbhoI!lX7mE6MkB^h-d{PhX?Fuln=QU0Dq{O3Ym7@_SO1(Do){Lt3Vv zS#h?FqT-L}W@u~1?v{Ojekl-f`;i=NGj>G~zb|3wO?j;ukUC_<+dX*0Cv)D>zL2}8 z%+XFbH}7KnT4D3U6!EWYdTp>u1Q4}glBfWSeFk4rnyIR8TS8;s>Q1UtIkn$%7Iplv9_gLh#3?gs$Gs&&R)5bNkocLYzYMd-v|BYaREZ zLoextO_(kXM`{TASh6(xj>BiS7mAn28ZlqEr>f`VSw5VJApi=W8<0b8{4tL*!nhI@vQ^)sCh&dI5 zaIeOO6DvkZzrVhhNz*`JzW7ILX(g$i2cnv!@Fvv?x`h65b>5LwTSBXSi^`Omu>*6% z5f(iKVwArZ^wI7GV?^RVZxU=@L#)Uzk2zi(>~b_~K4_osmuF!s$+h&8t+QY4eW#*) z{Oe7xDXlbwuRhDsUWsy6dtn`U4mV}(;b#1AOrYaxd&M7rU^+}Epux(rD34I_d3+DR z94Vtm#j}7eUcj-Kij)94aogGa$Mkb-G+8l-m;rOlz}mA>&0hQGiP%@?efi3r&;a$X zLzA9_?ms1k^(xj@xLqiU$`w#wW6scp-nTQ?eR#L0YaP3Kwt6uZ4+^m|Ne3yuI!>Slf#Lj~)3 z!bIgsoebNWlUHOdpx^^5n*up>Sur}n1^QCjyeYjGpR7V#u&ZRBXfdajYA%}2(D{%- zs5s~Jt&#Ner!YcFqx2FRH$?zSPGD~OGQ;xYVDQ|h!y@c_2dVma7Pyd>Oy?i}TCldV z3sc71RRcAan}y`LXG{=ZPyAV9Wz4M14zt@CE@sS0=mae7B8LI7qL&}Cd$b&#)0skwJa9=Eyvz43S5*=y8Wqa{xU>q8YK zXN0C%=?o}J^%-T^6NB&$#HXMAM~3FaYbc;m89ZEK*zsa*WIbU^W92`7SH}|hDr$Ad zQ;?n$maYQIhH(MY&5mBaWYt)e*hA@}DT5d&3dnCMB^hew3|EGaVHxKxj9{4)@`(w1PkyB-TBVh9Pl@Q+fe*`zjNG!TMpL4rVYpVkOvw~bm zZ}C)cLgLaL6wy$1m5_pdp9Het!->c{CI86op8WZV9eX5eipjZh3g%RVERCPJrv-%J zg7_|kN+B(2731u$C^dKrDSnSsxq6PGgcssQ_>~hLeL%zg_p1@hYv4F~s&hG|H@Hvg zT3O#g$*Nq8^lO5q@9p>X9PfMub2O?xFzHcCDGy8@GjRK%l8(a(HZzqru%8PR6KLfA z;`DyT*=4Evj$HL?=gy5}bI4mkTHwmby|C)TJ2?+s6^@UVSusGCy55oyqf)l7@x$}S zWA)ne^Lcb{&ZR(;)nAQY~=@4^_*NsV>TQNZ1{Iss6Ar6~QjT(Wuc)j@QF=5c$;_LE`=!?3BH;oi#Rn@}ChbW#Fg^QNvXY1O zS8}|gHz=a`O3c8jTtaY1wPH#qYHeRA)6#zOXI9jaX|<@ycDg)#ti1sVMf|oko!J=ddG8VI+g$I4%D>lQ=hZAf)pyx7VmTY zp>r_>*pe~#^6VwmHkC?b!6Y5tCq^)|dvlWGS$kd< zI;pAw51?zeErkIms901^))8@T`3*5*tIAVUDk=*s)}ZS_Z*1g4o^F-ff3;PEQV^F3 z3Py=~R68U%9FB>P($o1FwPORY0=Fw=6XfXUU&z)U1bbb7SCN^0(>?E)bCg<+{UDTe zsKd3}+nX&bXn9>io$oK)cDH>9pp+J!Q_q&TltMS z0&oY+eZTnTisyj|w!HRn=--%mwlwOg_4o92Xm|^9FN}1C3nsH+rTlg!ve@gZKBqq9 zJy=%&$4ksl6Zu-XD+dSzyj8!KvB02ymAwI=hhAY?N~)(?7+XKuKl#$<4s~USD0AeU z;GljA9yY7XNF%=wn;K%?Q<80=ueDq2czT+rJwHT?wf2fIZ@gU$?(LKTw=+5GUZmLR zNT6arvr!K5rq%%enWQn78d|>s#7{b|Bzo`%%

`j_*BWL;SqrYKT9+DN_Zf}{2 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 Date: Thu, 2 Jan 2025 11:01:24 +0100 Subject: [PATCH 0082/1144] feat: default pfp for multiservice --- src/utils/ui/default-profile-picture.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/utils/ui/default-profile-picture.ts b/src/utils/ui/default-profile-picture.ts index a661c384f..3e300875f 100644 --- a/src/utils/ui/default-profile-picture.ts +++ b/src/utils/ui/default-profile-picture.ts @@ -23,7 +23,9 @@ export const defaultProfilePicture = (service: AccountService, accountProvider: return require("../../../assets/images/service_skolengo.png"); case AccountService.Local: return require("../../../assets/images/service_unknown.png"); + case AccountService.PapillonMultiService: + return require("../../../assets/images/multiservice.png"); } return require("../../../assets/images/service_unknown.png"); -}; \ No newline at end of file +}; From 18216846e366dc6b39b63e6dc303fc6c90c9a915 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 11:02:04 +0100 Subject: [PATCH 0083/1144] feat: added new route for space setting --- src/router/helpers/types.ts | 2 ++ src/router/screens/settings/index.ts | 4 ++++ src/views/settings/SettingsMultiService.tsx | 1 + 3 files changed, 7 insertions(+) diff --git a/src/router/helpers/types.ts b/src/router/helpers/types.ts index c9dd79d6d..b797f783a 100644 --- a/src/router/helpers/types.ts +++ b/src/router/helpers/types.ts @@ -14,6 +14,7 @@ import {Client} from "pawrd"; import { Host } from "turboself-api"; import {Evaluation} from "@/services/shared/Evaluation"; import { ThemesMeta } from "@/utils/chat/themes/Themes.types"; +import {MultiServiceSpace} from "@/stores/multiService/types"; export type RouteParameters = { // welcome.index @@ -128,6 +129,7 @@ export type RouteParameters = { SettingsExternalServices: undefined; SettingsMagic: undefined; SettingsMultiService: undefined; + SettingsMultiServiceSpace: { space: MultiServiceSpace }; SettingsFlags: undefined; SettingsFlagsInfos: { title: string; value: any }; SettingsAddons: undefined; diff --git a/src/router/screens/settings/index.ts b/src/router/screens/settings/index.ts index 10f0e4a3b..3ff7aceff 100644 --- a/src/router/screens/settings/index.ts +++ b/src/router/screens/settings/index.ts @@ -28,6 +28,7 @@ import TurboselfAccountSelector from "@/views/settings/ExternalAccount/Turboself import SettingsApparence from "@/views/settings/SettingsApparence"; import ExternalAliseLogin from "@/views/settings/ExternalAccount/Alise"; import SettingsMultiService from "@/views/settings/SettingsMultiService"; +import SettingsMultiServiceSpace from "@/views/settings/SettingsMultiServiceSpace"; const settingsScreens = [ createScreen("Settings", Settings, { @@ -67,6 +68,9 @@ const settingsScreens = [ createScreen("SettingsMultiService", SettingsMultiService, { headerTitle: "Multiservice", }), + createScreen("SettingsMultiServiceSpace", SettingsMultiServiceSpace, { + headerTitle: "Gérer l'environnement multi-service", + }), createScreen("SettingsAddons", SettingsAddons, { headerTitle: "Extensions", }), diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index 46c581f0d..88e6bcc75 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -143,6 +143,7 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => {multiServiceSpaces.map(space => ( navigation.navigate("SettingsMultiServiceSpace", { space })} leading={ Date: Thu, 2 Jan 2025 12:05:50 +0100 Subject: [PATCH 0084/1144] fix: store property cannot be set to undefined --- src/stores/multiService/index.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/stores/multiService/index.ts b/src/stores/multiService/index.ts index e092b7e92..90d940482 100644 --- a/src/stores/multiService/index.ts +++ b/src/stores/multiService/index.ts @@ -59,21 +59,20 @@ export const useMultiService = create()( let spaceMutated: MultiServiceSpace; // Mutate only featureServices and name properties. - if (key in ["featureServices", "name"]) { + if (key === "featureServices" || key === "name") { spaceMutated = { ...space, [key]: value }; + // Save the update in the store and storage. + set((state) => ({ + spaces: state.spaces.map((space) => + space.accountLocalID === localID + ? spaceMutated + : space + ) + })); } - - // Save the update in the store and storage. - set((state) => ({ - spaces: state.spaces.map((space) => - space.accountLocalID === localID - ? spaceMutated - : space - ) - })); }, getFeatureAccount: (feature, spaceLocalID) => { From 1f87d2c62cd5ed787837083cefa600acbfe2df26 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 12:06:31 +0100 Subject: [PATCH 0085/1144] feat: added multi service space personalization --- src/views/settings/SettingsMultiService.tsx | 37 +-- .../settings/SettingsMultiServiceSpace.tsx | 237 ++++++++++++++++++ 2 files changed, 260 insertions(+), 14 deletions(-) create mode 100644 src/views/settings/SettingsMultiServiceSpace.tsx diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index 88e6bcc75..3901bd99a 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -5,7 +5,7 @@ import type {Screen} from "@/router/helpers/types"; import MultiServiceContainerCard from "@/components/Settings/MultiServiceContainerCard"; import {NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText} from "@/components/Global/NativeComponents"; import {ImageIcon, PlugZap, Plus, Type, Trash2} from "lucide-react-native"; -import {useAccounts} from "@/stores/account"; +import {useAccounts, useCurrentAccount} from "@/stores/account"; import {useMultiService} from "@/stores/multiService"; import BottomSheet from "@/components/Modals/PapillonBottomSheet"; import * as ImagePicker from "expo-image-picker"; @@ -25,6 +25,7 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => const createMultiServiceSpace = useMultiService(store => store.create); const deleteMultiServiceSpace = useMultiService(store => store.remove); const accounts = useAccounts(); + const currentAccount = useCurrentAccount(); const [spaceCreationSheetOpened, setSpaceCreationSheetOpened] = useState(false); const [spaceName, setSpaceName] = useState(""); @@ -62,13 +63,14 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => const localID = uuid(); + // TODO: have student name and space title const linkedAccount: PapillonMultiServiceSpace = { isExternal: false, linkedExternalLocalIDs: [], authentication: null, identity: {}, identityProvider: { - name: "Environnement multiservice Papillon" + name: spaceName }, instance: null, localID: localID, @@ -78,8 +80,8 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => }, service: AccountService.PapillonMultiService, studentName: { // TODO - first: spaceName, - last: "" + first: currentAccount.account?.studentName.first || "", + last: currentAccount.account?.studentName.last || "" } }; @@ -102,6 +104,13 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => setSelectedImage(null); setSpaceName(""); }; + + const deleteAllSpaces = () => { + for (const space of multiServiceSpaces) { + accounts.remove(space.accountLocalID); + deleteMultiServiceSpace(space.accountLocalID); + } + }; return ( = ({ navigation }) => trailing={ toggleMultiService()} + onValueChange={() => { + if (multiServiceEnabled) { + Alert.alert("Attention", "La désactivation du multi-service entrainera la suppression de vos environnement multi-services créés.", [ { text: "Annuler", style: "cancel" }, { text: "Confirmer", style: "destructive", onPress: () => { + deleteAllSpaces(); + toggleMultiService(); + } } ]); + } else { + toggleMultiService(); + } + }} /> } leading={ @@ -156,15 +174,6 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => }} /> } - trailing={ - { - accounts.remove(space.accountLocalID); - deleteMultiServiceSpace(space.accountLocalID); - }} - color="#CF0029" - /> - } > {space.name} diff --git a/src/views/settings/SettingsMultiServiceSpace.tsx b/src/views/settings/SettingsMultiServiceSpace.tsx new file mode 100644 index 000000000..b2afaae0e --- /dev/null +++ b/src/views/settings/SettingsMultiServiceSpace.tsx @@ -0,0 +1,237 @@ +import React, {useRef, useState} from "react"; +import {ActivityIndicator, Image, ScrollView, Switch, TextInput, KeyboardAvoidingView, Alert} from "react-native"; +import {useTheme} from "@react-navigation/native"; +import type {Screen} from "@/router/helpers/types"; +import {NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText} from "@/components/Global/NativeComponents"; +import {Camera, PlugZap, TextCursorInput, User2, Type, Trash2} from "lucide-react-native"; +import {useAccounts} from "@/stores/account"; +import { AccountService, PrimaryAccount} from "@/stores/account/types"; +import * as ImagePicker from "expo-image-picker"; +import {useSafeAreaInsets} from "react-native-safe-area-context"; +import {useMultiService} from "@/stores/multiService"; + +const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ navigation, route }) => { + const theme = useTheme(); + const insets = useSafeAreaInsets(); + const space = route.params.space; + const accounts = useAccounts(); + const availableAccounts = accounts.accounts.filter(account => !account.isExternal && !(account.service == AccountService.PapillonMultiService)); + const deleteMultiServiceSpace = useMultiService(store => store.remove); + const updateMultiServiceSpace = useMultiService(store => store.update); + + const linkedAccount = accounts.accounts.find(account => account.localID === space.accountLocalID) as PrimaryAccount | undefined; + + const firstNameRef = useRef(null); + const lastNameRef = useRef(null); + const spaceNameRef = useRef(null); + + const [firstName, setFirstName] = useState(linkedAccount?.studentName?.first ?? ""); + const [lastName, setLastName] = useState(linkedAccount?.studentName?.last ?? ""); + const [spaceName, setSpaceName] = useState(space.name); + + const [loadingImage, setLoadingImage] = useState(false); + const [selectedImage, setSelectedImage] = useState(null); + const selectPicture = async () => { + setLoadingImage(true); + + let result = await ImagePicker.launchImageLibraryAsync({ + mediaTypes: ImagePicker.MediaTypeOptions.Images, + allowsEditing: true, + aspect: [1, 1], + quality: 1, + base64: true, + }); + + if (!result.canceled) { + const img = "data:image/jpeg;base64," + result.assets[0].base64; + setSelectedImage(img); + } + + setLoadingImage(false); + }; + + const deleteSpace = () => { + Alert.alert("Êtes-vous sur ?", "Cette action entrainera la suppression de votre espace multi-service.", [ { text: "Annuler", style: "cancel" }, { text: "Confirmer", style: "destructive", onPress: () => { + accounts.remove(space.accountLocalID); + deleteMultiServiceSpace(space.accountLocalID); + navigation.goBack(); + }}]); + }; + return ( + + + + + + + + selectPicture()} + leading={selectedImage && + + } + icon={!selectedImage && } + trailing={ + + } + > + + {selectedImage ? "Changer la photo" : "Ajouter une photo"} + + {!selectedImage ? ( + + Personnalisez votre espace en ajoutant une photo. + + ) : ( + + La photo de l'espace reste sur votre appareil. + + )} + + + + + + + + spaceNameRef.current?.focus()} + chevron={false} + icon={} + > + + Titre + + { + updateMultiServiceSpace(space.accountLocalID, "name", text); + setSpaceName(text); + }} + ref={spaceNameRef} + /> + + + + + + + + + + firstNameRef.current?.focus()} + chevron={false} + icon={} + > + + Prénom + + { + // @ts-expect-error + accounts.update(space.accountLocalID, "studentName", { + first: text, + last: lastName + }); + setFirstName(text); + }} + ref={firstNameRef} + /> + + + lastNameRef.current?.focus()} + chevron={false} + icon={} + > + + Nom de famille + + { + // @ts-expect-error + accounts.update(space.accountLocalID, "studentName", { + first: firstName, + last: text + }); + setLastName(text); + }} + ref={lastNameRef} + /> + + + + + + + + deleteSpace()} + leading={ + + } + > + + Supprimer + + + Supprimer l'environnement + + + + + + ); +}; + + + +export default SettingsMultiServiceSpace; From 3328b24fa6a6ba260f688a130eab05e4cf18b5fe Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 12:08:07 +0100 Subject: [PATCH 0086/1144] feat: added config section (WIP) --- src/views/settings/SettingsMultiServiceSpace.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/views/settings/SettingsMultiServiceSpace.tsx b/src/views/settings/SettingsMultiServiceSpace.tsx index b2afaae0e..3d05ddfde 100644 --- a/src/views/settings/SettingsMultiServiceSpace.tsx +++ b/src/views/settings/SettingsMultiServiceSpace.tsx @@ -211,6 +211,11 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga + + + WIP + + Date: Thu, 2 Jan 2025 12:40:35 +0100 Subject: [PATCH 0087/1144] fix: showing space name in account switcher context menu --- src/components/Home/AccountSwitcherContextMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Home/AccountSwitcherContextMenu.tsx b/src/components/Home/AccountSwitcherContextMenu.tsx index b0f4d4e5b..ef176b1a4 100644 --- a/src/components/Home/AccountSwitcherContextMenu.tsx +++ b/src/components/Home/AccountSwitcherContextMenu.tsx @@ -209,7 +209,7 @@ const ContextMenu: React.FC<{ numberOfLines={1} ellipsizeMode="tail" > - {AccountService[account.service] !== "Local" ? + {AccountService[account.service] !== "Local" && account.service !== AccountService.PapillonMultiService ? AccountService[account.service] : account.identityProvider ? account.identityProvider.name : From 2ce6e566f14cccdb93f57a4aa113c14d5d9b9104 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 13:35:38 +0100 Subject: [PATCH 0088/1144] fix: possibility to mutate image key --- src/stores/multiService/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stores/multiService/index.ts b/src/stores/multiService/index.ts index 90d940482..745d1ac88 100644 --- a/src/stores/multiService/index.ts +++ b/src/stores/multiService/index.ts @@ -59,7 +59,7 @@ export const useMultiService = create()( let spaceMutated: MultiServiceSpace; // Mutate only featureServices and name properties. - if (key === "featureServices" || key === "name") { + if (key === "featureServices" || key === "name" || key === "image") { spaceMutated = { ...space, [key]: value From 6d35bdcfb0bfd9b4b2889d7839052404c7d0c4b0 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 13:42:59 +0100 Subject: [PATCH 0089/1144] fix: better typing and setFeatureAccount function added to store --- src/stores/multiService/index.ts | 25 +++++++++++++++++++++++-- src/stores/multiService/types.ts | 5 +++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/stores/multiService/index.ts b/src/stores/multiService/index.ts index 745d1ac88..345344dec 100644 --- a/src/stores/multiService/index.ts +++ b/src/stores/multiService/index.ts @@ -58,8 +58,8 @@ export const useMultiService = create()( let spaceMutated: MultiServiceSpace; - // Mutate only featureServices and name properties. - if (key === "featureServices" || key === "name" || key === "image") { + // Mutate only image and name properties. + if (key === "name" || key === "image") { spaceMutated = { ...space, [key]: value @@ -75,6 +75,27 @@ export const useMultiService = create()( } }, + setFeatureAccount: (spaceLocalID, feature, account) => { + const space = get().spaces.find((space) => space.accountLocalID === spaceLocalID); + if (!space) return; + + let mutatedFeatureServices = space.featuresServices; + mutatedFeatureServices[feature] = account; + + const spaceMutated: MultiServiceSpace = { + ...space, + featuresServices: mutatedFeatureServices + }; + // Save the update in the store and storage. + set((state) => ({ + spaces: state.spaces.map((space) => + space.accountLocalID === spaceLocalID + ? spaceMutated + : space + ) + })); + }, + getFeatureAccount: (feature, spaceLocalID) => { // Find the account to update in the storage. const space = get().spaces.find((space) => space.accountLocalID === spaceLocalID); diff --git a/src/stores/multiService/types.ts b/src/stores/multiService/types.ts index 32245616d..b436fb560 100644 --- a/src/stores/multiService/types.ts +++ b/src/stores/multiService/types.ts @@ -13,7 +13,7 @@ export interface MultiServiceSpace { name: string image?: string featuresServices: { - [key in keyof typeof MultiServiceFeature]: PrimaryAccount | null + [key in MultiServiceFeature]?: PrimaryAccount | null } } @@ -24,5 +24,6 @@ export interface MultiServiceStore { remove: (localID: string) => void update: (localID: string, key: T, value: A[T]) => void toggleEnabledState: () => void - getFeatureAccount: (feature: keyof typeof MultiServiceFeature, spaceLocalID: string) => PrimaryAccount | null | undefined + setFeatureAccount: (spaceLocalID: string, feature: MultiServiceFeature, account: PrimaryAccount) => void + getFeatureAccount: (feature: MultiServiceFeature, spaceLocalID: string) => PrimaryAccount | null | undefined } From 2d0ece1d19d0c4c240d7eb2ce8ea4a8f03e218a6 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 13:47:28 +0100 Subject: [PATCH 0090/1144] fix: image settings --- src/views/settings/SettingsMultiServiceSpace.tsx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/views/settings/SettingsMultiServiceSpace.tsx b/src/views/settings/SettingsMultiServiceSpace.tsx index 3d05ddfde..b1b77d0b9 100644 --- a/src/views/settings/SettingsMultiServiceSpace.tsx +++ b/src/views/settings/SettingsMultiServiceSpace.tsx @@ -44,6 +44,11 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga if (!result.canceled) { const img = "data:image/jpeg;base64," + result.assets[0].base64; + updateMultiServiceSpace(space.accountLocalID, "image", img); + // @ts-expect-error + accounts.update(space.accountLocalID, "personalization", { + profilePictureB64: img + }); setSelectedImage(img); } @@ -78,9 +83,9 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga selectPicture()} - leading={selectedImage && + leading={(selectedImage || space.image) && = ({ naviga }} /> } - icon={!selectedImage && } + icon={!(selectedImage || space.image) && } trailing={ } @@ -135,6 +140,10 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga value={spaceName} onChangeText={(text: string) => { updateMultiServiceSpace(space.accountLocalID, "name", text); + // @ts-expect-error + accounts.update(space.accountLocalID, "identityProvider", { + name: text + }); setSpaceName(text); }} ref={spaceNameRef} From 41ceadecbabf2a8368f19fe320ebf2dead31cf3d Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 14:32:43 +0100 Subject: [PATCH 0091/1144] feat: configuration interface idea --- .../settings/SettingsMultiServiceSpace.tsx | 122 +++++++++++++++++- 1 file changed, 116 insertions(+), 6 deletions(-) diff --git a/src/views/settings/SettingsMultiServiceSpace.tsx b/src/views/settings/SettingsMultiServiceSpace.tsx index b1b77d0b9..cf8ec0c77 100644 --- a/src/views/settings/SettingsMultiServiceSpace.tsx +++ b/src/views/settings/SettingsMultiServiceSpace.tsx @@ -1,14 +1,27 @@ import React, {useRef, useState} from "react"; -import {ActivityIndicator, Image, ScrollView, Switch, TextInput, KeyboardAvoidingView, Alert} from "react-native"; +import { + ActivityIndicator, + Image, + ScrollView, + Switch, + TextInput, + KeyboardAvoidingView, + Alert, + FlatList +} from "react-native"; import {useTheme} from "@react-navigation/native"; import type {Screen} from "@/router/helpers/types"; import {NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText} from "@/components/Global/NativeComponents"; -import {Camera, PlugZap, TextCursorInput, User2, Type, Trash2} from "lucide-react-native"; +import {Camera, ChevronDown, TextCursorInput, User2, Type, Trash2, CircleAlert} from "lucide-react-native"; import {useAccounts} from "@/stores/account"; import { AccountService, PrimaryAccount} from "@/stores/account/types"; import * as ImagePicker from "expo-image-picker"; import {useSafeAreaInsets} from "react-native-safe-area-context"; import {useMultiService} from "@/stores/multiService"; +import {MultiServiceFeature} from "@/stores/multiService/types"; +import LottieView from "lottie-react-native"; +import {anim2Papillon} from "@/utils/ui/animations"; +import Reanimated, {FadeOut, ZoomIn} from "react-native-reanimated"; const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ navigation, route }) => { const theme = useTheme(); @@ -62,6 +75,42 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga navigation.goBack(); }}]); }; + + const selectFeatureAccount = (feature: MultiServiceFeature) => { + + }; + + + const lottieRef = React.useRef(null); + + const features = [ + { + name: "Notes", + feature: MultiServiceFeature.Grades, + icon: require("@/../assets/lottie/tab_chart.json") + }, + { + name: "Emploi du temps", + feature: MultiServiceFeature.Timetable, + icon: require("@/../assets/lottie/tab_calendar.json") + }, + { + name: "Devoirs", + feature: MultiServiceFeature.Homeworks, + icon: require("@/../assets/lottie/tab_book_2.json") + }, + { + name: "Vie scolaire", + feature: MultiServiceFeature.Attendance, + icon: require("@/../assets/lottie/tab_check.json") + }, + { + name: "Actualités", + feature: MultiServiceFeature.News, + icon: require("@/../assets/lottie/tab_news.json") + } + ]; + return ( = ({ naviga ref={lastNameRef} /> - + Accède à plus d'options en sélectionnant l'espace virtuel, et en personnalisant ton profil dans les paramètres. - - WIP - + <> + {features.map(feature => ( + + + + } + onPress={() => selectFeatureAccount(feature.feature)} + trailing={} + chevron={false} + > + {feature.name} + + {space.featuresServices[feature.feature] ? ( + + Mettre le compte connecté ici + + ): ( + } + style={{ + width: "100%", + padding: 0 + }} + > + Pas de service sélectionné + + )} + + ))} + From 759dde85aff83bf850a3bcad83bc71f96b5eae89 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 14:48:23 +0100 Subject: [PATCH 0092/1144] fix: styles --- .../settings/SettingsMultiServiceSpace.tsx | 71 +++++++++++++++---- 1 file changed, 56 insertions(+), 15 deletions(-) diff --git a/src/views/settings/SettingsMultiServiceSpace.tsx b/src/views/settings/SettingsMultiServiceSpace.tsx index cf8ec0c77..1cf9fb602 100644 --- a/src/views/settings/SettingsMultiServiceSpace.tsx +++ b/src/views/settings/SettingsMultiServiceSpace.tsx @@ -7,7 +7,7 @@ import { TextInput, KeyboardAvoidingView, Alert, - FlatList + FlatList, View, Text } from "react-native"; import {useTheme} from "@react-navigation/native"; import type {Screen} from "@/router/helpers/types"; @@ -22,6 +22,7 @@ import {MultiServiceFeature} from "@/stores/multiService/types"; import LottieView from "lottie-react-native"; import {anim2Papillon} from "@/utils/ui/animations"; import Reanimated, {FadeOut, ZoomIn} from "react-native-reanimated"; +import {defaultProfilePicture} from "@/utils/ui/default-profile-picture"; const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ navigation, route }) => { const theme = useTheme(); @@ -270,9 +271,12 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga - <> - {features.map(feature => ( - + + {features.map((feature, index) => ( + <> = ({ naviga {space.featuresServices[feature.feature] ? ( } + separator={true} > - Mettre le compte connecté ici + + + {space.featuresServices[feature.feature]?.studentName?.first || "Utilisateur"}{" "} + {space.featuresServices[feature.feature]?.studentName?.last || ""} + + + + + {AccountService[space.featuresServices[feature.feature]?.service || AccountService.Local] !== "Local" ? + AccountService[space.featuresServices[feature.feature]?.service || AccountService.Local] : + space.featuresServices[feature.feature]?.identityProvider ? + space.featuresServices[feature.feature]?.identityProvider?.name : + "Compte local" + } + ): ( } - style={{ - width: "100%", - padding: 0 - }} + icon={} + separator={true} > Pas de service sélectionné )} - + ))} - + From f9c70eac36e1f5ac0cbb439f6dfd21b6653b80f9 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 15:30:40 +0100 Subject: [PATCH 0093/1144] feat: added AccountItem component --- src/components/Global/AccountItem.tsx | 116 ++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 src/components/Global/AccountItem.tsx diff --git a/src/components/Global/AccountItem.tsx b/src/components/Global/AccountItem.tsx new file mode 100644 index 000000000..9cc942ca7 --- /dev/null +++ b/src/components/Global/AccountItem.tsx @@ -0,0 +1,116 @@ +import {Image, StyleProp, Text, View, ViewStyle} from "react-native"; +import { animPapillon } from "@/utils/ui/animations"; + +import Reanimated, { + FadeOut, + ZoomIn +} from "react-native-reanimated"; +import {PrimaryAccount, AccountService} from "@/stores/account/types"; +import {defaultProfilePicture} from "@/utils/ui/default-profile-picture"; +import {Check} from "lucide-react-native"; +import React from "react"; +import {useTheme} from "@react-navigation/native"; + +const AccountItem: React.FC<{ account: PrimaryAccount, additionalStyles?: StyleProp, endCheckMark: boolean }> = ({ + account, + additionalStyles, + endCheckMark +}) => { + const theme = useTheme(); + return ( + + + + + + + + {account.studentName?.first || "Utilisateur"}{" "} + {account.studentName?.last || ""} + + + + + {AccountService[account.service] !== "Local" ? + AccountService[account.service] : + account.identityProvider ? + account.identityProvider?.name : + "Compte local" + } + + + {endCheckMark && + + + + } + + ); +}; + +export default AccountItem; From 3026da675ce7a8068b0214f74e7879c39dd9ee02 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 15:31:00 +0100 Subject: [PATCH 0094/1144] feat: functionnals settings for multiservices --- .../settings/SettingsMultiServiceSpace.tsx | 122 +++++++++--------- 1 file changed, 64 insertions(+), 58 deletions(-) diff --git a/src/views/settings/SettingsMultiServiceSpace.tsx b/src/views/settings/SettingsMultiServiceSpace.tsx index 1cf9fb602..0b6d200c8 100644 --- a/src/views/settings/SettingsMultiServiceSpace.tsx +++ b/src/views/settings/SettingsMultiServiceSpace.tsx @@ -1,28 +1,32 @@ import React, {useRef, useState} from "react"; import { ActivityIndicator, + Alert, Image, + KeyboardAvoidingView, + Pressable, ScrollView, - Switch, + Text, TextInput, - KeyboardAvoidingView, - Alert, - FlatList, View, Text + View } from "react-native"; import {useTheme} from "@react-navigation/native"; import type {Screen} from "@/router/helpers/types"; -import {NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText} from "@/components/Global/NativeComponents"; -import {Camera, ChevronDown, TextCursorInput, User2, Type, Trash2, CircleAlert} from "lucide-react-native"; +import {NativeItem, NativeList, NativeListHeader, NativeText} from "@/components/Global/NativeComponents"; +import {Camera, Check, ChevronDown, CircleAlert, TextCursorInput, Trash2, Type, User2} from "lucide-react-native"; import {useAccounts} from "@/stores/account"; -import { AccountService, PrimaryAccount} from "@/stores/account/types"; +import {AccountService, PrimaryAccount} from "@/stores/account/types"; import * as ImagePicker from "expo-image-picker"; import {useSafeAreaInsets} from "react-native-safe-area-context"; import {useMultiService} from "@/stores/multiService"; import {MultiServiceFeature} from "@/stores/multiService/types"; import LottieView from "lottie-react-native"; -import {anim2Papillon} from "@/utils/ui/animations"; +import {anim2Papillon, animPapillon} from "@/utils/ui/animations"; import Reanimated, {FadeOut, ZoomIn} from "react-native-reanimated"; import {defaultProfilePicture} from "@/utils/ui/default-profile-picture"; +import PapillonBottomSheet from "@/components/Modals/PapillonBottomSheet"; +import * as Haptics from "expo-haptics"; +import AccountItem from "@/components/Global/AccountItem"; const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ navigation, route }) => { const theme = useTheme(); @@ -32,6 +36,7 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga const availableAccounts = accounts.accounts.filter(account => !account.isExternal && !(account.service == AccountService.PapillonMultiService)); const deleteMultiServiceSpace = useMultiService(store => store.remove); const updateMultiServiceSpace = useMultiService(store => store.update); + const setMultiServiceSpaceAccountFeature = useMultiService(store => store.setFeatureAccount); const linkedAccount = accounts.accounts.find(account => account.localID === space.accountLocalID) as PrimaryAccount | undefined; @@ -69,6 +74,10 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga setLoadingImage(false); }; + const [accountSelectorOpened, setAccountSelectorOpened] = useState(false); + const [selectedAccount, setSelectedAccount] = useState(null); + const [featureSelection, setFeatureSelection] = useState(MultiServiceFeature.Grades); + const deleteSpace = () => { Alert.alert("Êtes-vous sur ?", "Cette action entrainera la suppression de votre espace multi-service.", [ { text: "Annuler", style: "cancel" }, { text: "Confirmer", style: "destructive", onPress: () => { accounts.remove(space.accountLocalID); @@ -77,10 +86,17 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga }}]); }; - const selectFeatureAccount = (feature: MultiServiceFeature) => { - + const openAccountSelector = (feature: MultiServiceFeature) => { + setSelectedAccount(space.featuresServices[feature] || null); + setFeatureSelection(feature); + setAccountSelectorOpened(true); }; + const setAccountFeature = (account: PrimaryAccount, feature: MultiServiceFeature) => { + setMultiServiceSpaceAccountFeature(space.accountLocalID, feature, account); + setAccountSelectorOpened(false); + setSelectedAccount(null); + }; const lottieRef = React.useRef(null); @@ -310,60 +326,19 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga ]} /> } - onPress={() => selectFeatureAccount(feature.feature)} + onPress={() => openAccountSelector(feature.feature)} trailing={} chevron={false} > {feature.name} {space.featuresServices[feature.feature] ? ( - } - separator={true} - > - - - {space.featuresServices[feature.feature]?.studentName?.first || "Utilisateur"}{" "} - {space.featuresServices[feature.feature]?.studentName?.last || ""} - - - - - {AccountService[space.featuresServices[feature.feature]?.service || AccountService.Local] !== "Local" ? - AccountService[space.featuresServices[feature.feature]?.service || AccountService.Local] : - space.featuresServices[feature.feature]?.identityProvider ? - space.featuresServices[feature.feature]?.identityProvider?.name : - "Compte local" - } - - + ): ( } @@ -376,6 +351,37 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga ))} + setAccountSelectorOpened(opened)}> + + + + {availableAccounts.map((account, index) => ( + { + Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Soft); + setAccountFeature(account, featureSelection); + }} + > + + + ))} + + + + Date: Thu, 2 Jan 2025 15:39:20 +0100 Subject: [PATCH 0095/1144] feat: redirecting to service for each functionality --- src/services/attendance.ts | 16 ++++++++++++++++ src/services/grades.ts | 16 ++++++++++++++++ src/services/homework.ts | 16 ++++++++++++++++ src/services/news.ts | 16 ++++++++++++++++ src/services/timetable.ts | 9 +++++++++ 5 files changed, 73 insertions(+) diff --git a/src/services/attendance.ts b/src/services/attendance.ts index 0e9739273..d61a1910e 100644 --- a/src/services/attendance.ts +++ b/src/services/attendance.ts @@ -4,6 +4,8 @@ import { useAttendanceStore } from "@/stores/attendance"; import { Attendance } from "./shared/Attendance"; import { checkIfSkoSupported } from "./skolengo/default-personalization"; import { error } from "@/utils/logger/logger"; +import {useMultiService} from "@/stores/multiService"; +import {MultiServiceFeature} from "@/stores/multiService/types"; export async function updateAttendancePeriodsInCache (account: T): Promise { let periods: Period[] = []; @@ -61,6 +63,13 @@ export async function updateAttendancePeriodsInCache (accoun break; } + case AccountService.PapillonMultiService: { + const service = useMultiService().getFeatureAccount(MultiServiceFeature.Attendance, account.localID); + if (!service) { + throw new Error("No service set in multi-service space"); + } + return updateAttendancePeriodsInCache(service); + } default: throw new Error("Service not implemented"); } @@ -116,6 +125,13 @@ export async function updateAttendanceInCache (account: T, p break; } + case AccountService.PapillonMultiService: { + const service = useMultiService().getFeatureAccount(MultiServiceFeature.Attendance, account.localID); + if (!service) { + throw new Error("No service set in multi-service space"); + } + return updateAttendanceInCache(service, periodName); + } default: throw new Error("Service not implemented"); } diff --git a/src/services/grades.ts b/src/services/grades.ts index 771ef39fb..25c2116ff 100644 --- a/src/services/grades.ts +++ b/src/services/grades.ts @@ -4,6 +4,8 @@ import type { Period } from "./shared/Period"; import type { AverageOverview, Grade } from "./shared/Grade"; import {error } from "@/utils/logger/logger"; import { checkIfSkoSupported } from "./skolengo/default-personalization"; +import {useMultiService} from "@/stores/multiService"; +import {MultiServiceFeature} from "@/stores/multiService/types"; const getDefaultPeriod = (periods: Period[]): string => { const now = Date.now(); @@ -54,6 +56,13 @@ export async function updateGradesPeriodsInCache (account: T break; } + case AccountService.PapillonMultiService: { + const service = useMultiService().getFeatureAccount(MultiServiceFeature.Grades, account.localID); + if (!service) { + throw new Error("No service set in multi-service space"); + } + return updateGradesPeriodsInCache(service); + } default: throw new Error("Service not implemented"); } @@ -120,6 +129,13 @@ export async function updateGradesAndAveragesInCache (accoun break; } + case AccountService.PapillonMultiService: { + const service = useMultiService().getFeatureAccount(MultiServiceFeature.Grades, account.localID); + if (!service) { + throw new Error("No service set in multi-service space"); + } + return updateGradesAndAveragesInCache(service, periodName); + } default: throw new Error(`Service (${AccountService[account.service]}) not implemented for this request`); } diff --git a/src/services/homework.ts b/src/services/homework.ts index e2bfd14a3..f91f72775 100644 --- a/src/services/homework.ts +++ b/src/services/homework.ts @@ -7,6 +7,8 @@ import { pronoteFirstDate } from "./pronote/timetable"; import { dateToEpochWeekNumber, epochWNToPronoteWN } from "@/utils/epochWeekNumber"; import { checkIfSkoSupported } from "./skolengo/default-personalization"; import { useClassSubjectStore } from "@/stores/classSubject"; +import {useMultiService} from "@/stores/multiService"; +import {MultiServiceFeature} from "@/stores/multiService/types"; /** * Updates the state and cache for the homework of given week number. @@ -44,6 +46,13 @@ export async function updateHomeworkForWeekInCache (account: homeworks = []; break; } + case AccountService.PapillonMultiService: { + const service = useMultiService().getFeatureAccount(MultiServiceFeature.Homeworks, account.localID); + if (!service) { + throw new Error("No service set in multi-service space"); + } + return updateHomeworkForWeekInCache(service, date); + } default: console.info(`[updateHomeworkForWeekInCache]: updating to empty since ${account.service} not implemented.`); } @@ -70,6 +79,13 @@ export async function toggleHomeworkState (account: T, homew case AccountService.Local: { break; } + case AccountService.PapillonMultiService: { + const service = useMultiService().getFeatureAccount(MultiServiceFeature.Homeworks, account.localID); + if (!service) { + throw new Error("No service set in multi-service space"); + } + return toggleHomeworkState(service, homework); + } default: { throw new Error("Service not implemented"); } diff --git a/src/services/news.ts b/src/services/news.ts index 0d8f0a4e2..bcc11702c 100644 --- a/src/services/news.ts +++ b/src/services/news.ts @@ -5,6 +5,8 @@ import { checkIfSkoSupported } from "./skolengo/default-personalization"; import { error } from "@/utils/logger/logger"; import { newsRead } from "pawnote"; import { ca } from "date-fns/locale"; +import {useMultiService} from "@/stores/multiService"; +import {MultiServiceFeature} from "@/stores/multiService/types"; /** * Updates the state and cache for the news. @@ -43,6 +45,13 @@ export async function updateNewsInCache (account: T): Promis useNewsStore.getState().updateInformations(informations); break; } + case AccountService.PapillonMultiService: { + const service = useMultiService().getFeatureAccount(MultiServiceFeature.News, account.localID); + if (!service) { + throw new Error("No service set in multi-service space"); + } + return updateNewsInCache(service); + } default: { throw new Error("Service not implemented."); } @@ -70,6 +79,13 @@ export async function setNewsRead (account: T, message: Info } case AccountService.Multi: break; + case AccountService.PapillonMultiService: { + const service = useMultiService().getFeatureAccount(MultiServiceFeature.News, account.localID); + if (!service) { + throw new Error("No service set in multi-service space"); + } + return setNewsRead(service, message, read); + } default: { throw new Error("Service not implemented."); } diff --git a/src/services/timetable.ts b/src/services/timetable.ts index cc48c7d9c..ca442e0a5 100644 --- a/src/services/timetable.ts +++ b/src/services/timetable.ts @@ -4,6 +4,8 @@ import { epochWNToPronoteWN, weekNumberToDateRange } from "@/utils/epochWeekNumb import { checkIfSkoSupported } from "./skolengo/default-personalization"; import { error } from "@/utils/logger/logger"; import { fetchIcalData } from "./local/ical"; +import {useMultiService} from "@/stores/multiService"; +import {MultiServiceFeature} from "@/stores/multiService/types"; /** * Updates the state and cache for the timetable of given week number. @@ -45,6 +47,13 @@ export async function updateTimetableForWeekInCache (account useTimetableStore.getState().updateClasses(epochWeekNumber, timetable); break; } + case AccountService.PapillonMultiService: { + const service = useMultiService().getFeatureAccount(MultiServiceFeature.Timetable, account.localID); + if (!service) { + throw new Error("No service set in multi-service space"); + } + return updateTimetableForWeekInCache(service, epochWeekNumber, force); + } default: { throw new Error("Service not implemented."); } From 444833fc6f56d1a05d052796f68fbb6122385dcf Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 18:16:30 +0100 Subject: [PATCH 0096/1144] fix: using new function to retreive account object in services --- src/services/attendance.ts | 6 +++--- src/services/grades.ts | 6 +++--- src/services/homework.ts | 9 +++++---- src/services/news.ts | 6 +++--- src/services/timetable.ts | 4 ++-- src/utils/multiservice/index.ts | 17 +++++++++++++++++ 6 files changed, 33 insertions(+), 15 deletions(-) create mode 100644 src/utils/multiservice/index.ts diff --git a/src/services/attendance.ts b/src/services/attendance.ts index d61a1910e..821f82047 100644 --- a/src/services/attendance.ts +++ b/src/services/attendance.ts @@ -4,8 +4,8 @@ import { useAttendanceStore } from "@/stores/attendance"; import { Attendance } from "./shared/Attendance"; import { checkIfSkoSupported } from "./skolengo/default-personalization"; import { error } from "@/utils/logger/logger"; -import {useMultiService} from "@/stores/multiService"; import {MultiServiceFeature} from "@/stores/multiService/types"; +import {getFeatureAccount} from "@/utils/multiservice"; export async function updateAttendancePeriodsInCache (account: T): Promise { let periods: Period[] = []; @@ -64,7 +64,7 @@ export async function updateAttendancePeriodsInCache (accoun break; } case AccountService.PapillonMultiService: { - const service = useMultiService().getFeatureAccount(MultiServiceFeature.Attendance, account.localID); + const service = getFeatureAccount(MultiServiceFeature.Attendance, account.localID); if (!service) { throw new Error("No service set in multi-service space"); } @@ -126,7 +126,7 @@ export async function updateAttendanceInCache (account: T, p break; } case AccountService.PapillonMultiService: { - const service = useMultiService().getFeatureAccount(MultiServiceFeature.Attendance, account.localID); + const service = getFeatureAccount(MultiServiceFeature.Attendance, account.localID); if (!service) { throw new Error("No service set in multi-service space"); } diff --git a/src/services/grades.ts b/src/services/grades.ts index 25c2116ff..ebc25bbb8 100644 --- a/src/services/grades.ts +++ b/src/services/grades.ts @@ -4,8 +4,8 @@ import type { Period } from "./shared/Period"; import type { AverageOverview, Grade } from "./shared/Grade"; import {error } from "@/utils/logger/logger"; import { checkIfSkoSupported } from "./skolengo/default-personalization"; -import {useMultiService} from "@/stores/multiService"; import {MultiServiceFeature} from "@/stores/multiService/types"; +import {getFeatureAccount} from "@/utils/multiservice"; const getDefaultPeriod = (periods: Period[]): string => { const now = Date.now(); @@ -57,7 +57,7 @@ export async function updateGradesPeriodsInCache (account: T break; } case AccountService.PapillonMultiService: { - const service = useMultiService().getFeatureAccount(MultiServiceFeature.Grades, account.localID); + const service = getFeatureAccount(MultiServiceFeature.Grades, account.localID); if (!service) { throw new Error("No service set in multi-service space"); } @@ -130,7 +130,7 @@ export async function updateGradesAndAveragesInCache (accoun break; } case AccountService.PapillonMultiService: { - const service = useMultiService().getFeatureAccount(MultiServiceFeature.Grades, account.localID); + const service = getFeatureAccount(MultiServiceFeature.Grades, account.localID); if (!service) { throw new Error("No service set in multi-service space"); } diff --git a/src/services/homework.ts b/src/services/homework.ts index f91f72775..57b3beff3 100644 --- a/src/services/homework.ts +++ b/src/services/homework.ts @@ -7,8 +7,8 @@ import { pronoteFirstDate } from "./pronote/timetable"; import { dateToEpochWeekNumber, epochWNToPronoteWN } from "@/utils/epochWeekNumber"; import { checkIfSkoSupported } from "./skolengo/default-personalization"; import { useClassSubjectStore } from "@/stores/classSubject"; -import {useMultiService} from "@/stores/multiService"; import {MultiServiceFeature} from "@/stores/multiService/types"; +import {getFeatureAccount} from "@/utils/multiservice"; /** * Updates the state and cache for the homework of given week number. @@ -47,9 +47,10 @@ export async function updateHomeworkForWeekInCache (account: break; } case AccountService.PapillonMultiService: { - const service = useMultiService().getFeatureAccount(MultiServiceFeature.Homeworks, account.localID); + const service = getFeatureAccount(MultiServiceFeature.Homeworks, account.localID); if (!service) { - throw new Error("No service set in multi-service space"); + console.info(`[updateHomeworkForWeekInCache]: updating to empty since multi-service space has no account set for homeworks.`); + break; } return updateHomeworkForWeekInCache(service, date); } @@ -80,7 +81,7 @@ export async function toggleHomeworkState (account: T, homew break; } case AccountService.PapillonMultiService: { - const service = useMultiService().getFeatureAccount(MultiServiceFeature.Homeworks, account.localID); + const service = getFeatureAccount(MultiServiceFeature.Homeworks, account.localID); if (!service) { throw new Error("No service set in multi-service space"); } diff --git a/src/services/news.ts b/src/services/news.ts index bcc11702c..c05bcb538 100644 --- a/src/services/news.ts +++ b/src/services/news.ts @@ -5,8 +5,8 @@ import { checkIfSkoSupported } from "./skolengo/default-personalization"; import { error } from "@/utils/logger/logger"; import { newsRead } from "pawnote"; import { ca } from "date-fns/locale"; -import {useMultiService} from "@/stores/multiService"; import {MultiServiceFeature} from "@/stores/multiService/types"; +import {getFeatureAccount} from "@/utils/multiservice"; /** * Updates the state and cache for the news. @@ -46,7 +46,7 @@ export async function updateNewsInCache (account: T): Promis break; } case AccountService.PapillonMultiService: { - const service = useMultiService().getFeatureAccount(MultiServiceFeature.News, account.localID); + const service = getFeatureAccount(MultiServiceFeature.News, account.localID); if (!service) { throw new Error("No service set in multi-service space"); } @@ -80,7 +80,7 @@ export async function setNewsRead (account: T, message: Info case AccountService.Multi: break; case AccountService.PapillonMultiService: { - const service = useMultiService().getFeatureAccount(MultiServiceFeature.News, account.localID); + const service = getFeatureAccount(MultiServiceFeature.News, account.localID); if (!service) { throw new Error("No service set in multi-service space"); } diff --git a/src/services/timetable.ts b/src/services/timetable.ts index ca442e0a5..3db8f4793 100644 --- a/src/services/timetable.ts +++ b/src/services/timetable.ts @@ -4,8 +4,8 @@ import { epochWNToPronoteWN, weekNumberToDateRange } from "@/utils/epochWeekNumb import { checkIfSkoSupported } from "./skolengo/default-personalization"; import { error } from "@/utils/logger/logger"; import { fetchIcalData } from "./local/ical"; -import {useMultiService} from "@/stores/multiService"; import {MultiServiceFeature} from "@/stores/multiService/types"; +import {getFeatureAccount} from "@/utils/multiservice"; /** * Updates the state and cache for the timetable of given week number. @@ -48,7 +48,7 @@ export async function updateTimetableForWeekInCache (account break; } case AccountService.PapillonMultiService: { - const service = useMultiService().getFeatureAccount(MultiServiceFeature.Timetable, account.localID); + const service = getFeatureAccount(MultiServiceFeature.Timetable, account.localID); if (!service) { throw new Error("No service set in multi-service space"); } diff --git a/src/utils/multiservice/index.ts b/src/utils/multiservice/index.ts new file mode 100644 index 000000000..071d6151e --- /dev/null +++ b/src/utils/multiservice/index.ts @@ -0,0 +1,17 @@ +import {MultiServiceFeature} from "@/stores/multiService/types"; +import {useAccounts} from "@/stores/account"; +import {useMultiService} from "@/stores/multiService"; +import {PrimaryAccount} from "@/stores/account/types"; + +export function getFeatureAccount (feature: MultiServiceFeature, spaceLocalID: string) { + const accounts = useAccounts().accounts; + const accountId = useMultiService().getFeatureAccountId(feature, spaceLocalID); + const space = useMultiService().spaces.find(space => space.accountLocalID === spaceLocalID); + const featureAccount = accounts.find(account => account.localID === accountId) as PrimaryAccount; + + if (!space || !featureAccount || !accountId) return undefined; + + featureAccount.authentication = space.authentication[accountId].authentication; + featureAccount.instance = space.authentication[accountId].instance; + return featureAccount; +} From 2732a0ae799129a7d2b9d45473ae6078f4f1dfa5 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 18:19:45 +0100 Subject: [PATCH 0097/1144] fix: new types and structure: using accounts ids instead of copying account object --- src/stores/account/index.ts | 26 +++++++++++++++++++++++++- src/stores/multiService/index.ts | 8 ++++---- src/stores/multiService/types.ts | 17 +++++++++++++++-- src/utils/multiservice/index.ts | 4 ++-- 4 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index 77e9fc695..d96795f4f 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -18,6 +18,7 @@ import { useGradesStore } from "../grades"; import { useNewsStore } from "../news"; import { useAttendanceStore } from "../attendance"; import { info, log } from "@/utils/logger/logger"; +import {useMultiService} from "@/stores/multiService"; /** * Store for the currently selected account. @@ -82,7 +83,30 @@ export const useCurrentAccount = create()((set, get) => ({ })); // Account is currently not authenticated, - if (typeof account.instance === "undefined") { + if (account.service === AccountService.PapillonMultiService) { + log("switching to virtual space, reloading all accounts...", "[switchTo]"); + const space = useMultiService().spaces.find(space => space.accountLocalID === account.localID); + if (!space) { + log("Uh oh, no space found, cannot switch to...", "[switchTo]"); + return; + } + let seenAccountsIds: string[] = []; + const spaceAccountsIds: string[] = Object.values(space.featuresServices); + const accounts = useAccounts().accounts.filter(account => spaceAccountsIds.includes(account.localID)); + for (const account of accounts) { + if (account) { + if (!seenAccountsIds.includes(account.localID) && typeof account.instance === "undefined") { + log(`instance undefined for ${account.localID} reloading...`, "[switchTo]"); + const { instance, authentication } = await reload(account); + // TODO: set instance by account + // get().mutateProperty("authentication", authentication); + // get().mutateProperty("instance", instance); + log("instance reload done !", "[switchTo]"); + } + seenAccountsIds.push(account.localID); + } + } + } else if (typeof account.instance === "undefined") { log("instance undefined, reloading...", "[switchTo]"); // Automatically reconnect the main instance. const { instance, authentication } = await reload(account); diff --git a/src/stores/multiService/index.ts b/src/stores/multiService/index.ts index 345344dec..30350ae9f 100644 --- a/src/stores/multiService/index.ts +++ b/src/stores/multiService/index.ts @@ -80,7 +80,7 @@ export const useMultiService = create()( if (!space) return; let mutatedFeatureServices = space.featuresServices; - mutatedFeatureServices[feature] = account; + mutatedFeatureServices[feature] = account.localID; const spaceMutated: MultiServiceSpace = { ...space, @@ -96,10 +96,10 @@ export const useMultiService = create()( })); }, - getFeatureAccount: (feature, spaceLocalID) => { - // Find the account to update in the storage. + getFeatureAccountId: (feature, spaceLocalID) => { + // Find the account associated to the feature const space = get().spaces.find((space) => space.accountLocalID === spaceLocalID); - if (!space) return null; + if (!space) return undefined; return space.featuresServices[feature]; }, diff --git a/src/stores/multiService/types.ts b/src/stores/multiService/types.ts index b436fb560..6218be6fb 100644 --- a/src/stores/multiService/types.ts +++ b/src/stores/multiService/types.ts @@ -12,8 +12,21 @@ export interface MultiServiceSpace { accountLocalID: string name: string image?: string + // TODO use account ids instead: less disk space + supports changes + /* + Each feature returns its linked account localID + */ featuresServices: { - [key in MultiServiceFeature]?: PrimaryAccount | null + [key in MultiServiceFeature]?: string + } + /* + Each key represents an account id, and is associated to its instance and authentication objects + */ + authentication: { + [key: string]: { + authentication: any + instance: any + } | undefined } } @@ -25,5 +38,5 @@ export interface MultiServiceStore { update: (localID: string, key: T, value: A[T]) => void toggleEnabledState: () => void setFeatureAccount: (spaceLocalID: string, feature: MultiServiceFeature, account: PrimaryAccount) => void - getFeatureAccount: (feature: MultiServiceFeature, spaceLocalID: string) => PrimaryAccount | null | undefined + getFeatureAccountId: (feature: MultiServiceFeature, spaceLocalID: string) => string | undefined } diff --git a/src/utils/multiservice/index.ts b/src/utils/multiservice/index.ts index 071d6151e..b666e2949 100644 --- a/src/utils/multiservice/index.ts +++ b/src/utils/multiservice/index.ts @@ -11,7 +11,7 @@ export function getFeatureAccount (feature: MultiServiceFeature, spaceLocalID: s if (!space || !featureAccount || !accountId) return undefined; - featureAccount.authentication = space.authentication[accountId].authentication; - featureAccount.instance = space.authentication[accountId].instance; + featureAccount.authentication = space.authentication[accountId]?.authentication; + featureAccount.instance = space.authentication[accountId]?.instance; return featureAccount; } From be64ca2e9837223ea4a06c96c30bef62b8a299ea Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 18:27:05 +0100 Subject: [PATCH 0098/1144] fix: adapted settings to new types and structure --- src/views/settings/SettingsMultiService.tsx | 11 ++++++----- src/views/settings/SettingsMultiServiceSpace.tsx | 12 +++++------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index 3901bd99a..1d69e2f18 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -88,12 +88,13 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => const space: MultiServiceSpace = { accountLocalID: localID, featuresServices: { - Grades: null, - Timetable: null, - Homeworks: null, - Attendance: null, - News: null + grades: undefined, + timetable: undefined, + news: undefined, + homeworks: undefined, + attendance: undefined }, + authentication: {}, name: spaceName, image: selectedImage || undefined }; diff --git a/src/views/settings/SettingsMultiServiceSpace.tsx b/src/views/settings/SettingsMultiServiceSpace.tsx index 0b6d200c8..c0933122a 100644 --- a/src/views/settings/SettingsMultiServiceSpace.tsx +++ b/src/views/settings/SettingsMultiServiceSpace.tsx @@ -6,14 +6,13 @@ import { KeyboardAvoidingView, Pressable, ScrollView, - Text, TextInput, View } from "react-native"; import {useTheme} from "@react-navigation/native"; import type {Screen} from "@/router/helpers/types"; import {NativeItem, NativeList, NativeListHeader, NativeText} from "@/components/Global/NativeComponents"; -import {Camera, Check, ChevronDown, CircleAlert, TextCursorInput, Trash2, Type, User2} from "lucide-react-native"; +import {Camera, ChevronDown, CircleAlert, TextCursorInput, Trash2, Type, User2} from "lucide-react-native"; import {useAccounts} from "@/stores/account"; import {AccountService, PrimaryAccount} from "@/stores/account/types"; import * as ImagePicker from "expo-image-picker"; @@ -21,9 +20,8 @@ import {useSafeAreaInsets} from "react-native-safe-area-context"; import {useMultiService} from "@/stores/multiService"; import {MultiServiceFeature} from "@/stores/multiService/types"; import LottieView from "lottie-react-native"; -import {anim2Papillon, animPapillon} from "@/utils/ui/animations"; +import {anim2Papillon} from "@/utils/ui/animations"; import Reanimated, {FadeOut, ZoomIn} from "react-native-reanimated"; -import {defaultProfilePicture} from "@/utils/ui/default-profile-picture"; import PapillonBottomSheet from "@/components/Modals/PapillonBottomSheet"; import * as Haptics from "expo-haptics"; import AccountItem from "@/components/Global/AccountItem"; @@ -87,7 +85,7 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga }; const openAccountSelector = (feature: MultiServiceFeature) => { - setSelectedAccount(space.featuresServices[feature] || null); + setSelectedAccount(availableAccounts.find(account => account.localID === space.featuresServices[feature]) || null); setFeatureSelection(feature); setAccountSelectorOpened(true); }; @@ -171,7 +169,7 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga {!selectedImage ? ( - Personnalisez votre espace en ajoutant une photo. + Personnalise ton espace en ajoutant une photo. ) : ( @@ -333,7 +331,7 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga {feature.name} {space.featuresServices[feature.feature] ? ( - account.localID === space.featuresServices[feature.feature]) as PrimaryAccount} endCheckMark={false} additionalStyles={{ paddingStart: 10, borderBottomWidth: 1, backgroundColor: theme.dark ? theme.colors.primary + "09" : theme.colors.primary + "11", From e3a4f0ae2d848a1201525cb55eee2278a16feab6 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 19:01:21 +0100 Subject: [PATCH 0099/1144] fix: removed breaking hooks code --- src/stores/account/index.ts | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index d96795f4f..b73ad49f6 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -18,7 +18,6 @@ import { useGradesStore } from "../grades"; import { useNewsStore } from "../news"; import { useAttendanceStore } from "../attendance"; import { info, log } from "@/utils/logger/logger"; -import {useMultiService} from "@/stores/multiService"; /** * Store for the currently selected account. @@ -82,31 +81,10 @@ export const useCurrentAccount = create()((set, get) => ({ return store.persist.rehydrate(); })); - // Account is currently not authenticated, + // Special case for spaces if (account.service === AccountService.PapillonMultiService) { - log("switching to virtual space, reloading all accounts...", "[switchTo]"); - const space = useMultiService().spaces.find(space => space.accountLocalID === account.localID); - if (!space) { - log("Uh oh, no space found, cannot switch to...", "[switchTo]"); - return; - } - let seenAccountsIds: string[] = []; - const spaceAccountsIds: string[] = Object.values(space.featuresServices); - const accounts = useAccounts().accounts.filter(account => spaceAccountsIds.includes(account.localID)); - for (const account of accounts) { - if (account) { - if (!seenAccountsIds.includes(account.localID) && typeof account.instance === "undefined") { - log(`instance undefined for ${account.localID} reloading...`, "[switchTo]"); - const { instance, authentication } = await reload(account); - // TODO: set instance by account - // get().mutateProperty("authentication", authentication); - // get().mutateProperty("instance", instance); - log("instance reload done !", "[switchTo]"); - } - seenAccountsIds.push(account.localID); - } - } - } else if (typeof account.instance === "undefined") { + log("switching to virtual space, skipping account reload...", "[switchTo]"); + } else if (typeof account.instance === "undefined") { // Account is currently not authenticated, log("instance undefined, reloading...", "[switchTo]"); // Automatically reconnect the main instance. const { instance, authentication } = await reload(account); From d743e730e13f3b8ed9d9ce2d7bbe714ebf849a55 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 19:01:23 +0100 Subject: [PATCH 0100/1144] fix: removed breaking hooks code --- src/utils/multiservice/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/multiservice/index.ts b/src/utils/multiservice/index.ts index b666e2949..d83150559 100644 --- a/src/utils/multiservice/index.ts +++ b/src/utils/multiservice/index.ts @@ -4,9 +4,9 @@ import {useMultiService} from "@/stores/multiService"; import {PrimaryAccount} from "@/stores/account/types"; export function getFeatureAccount (feature: MultiServiceFeature, spaceLocalID: string) { - const accounts = useAccounts().accounts; - const accountId = useMultiService().getFeatureAccountId(feature, spaceLocalID); - const space = useMultiService().spaces.find(space => space.accountLocalID === spaceLocalID); + const accounts = useAccounts.getState().accounts; + const accountId = useMultiService.getState().getFeatureAccountId(feature, spaceLocalID); + const space = useMultiService.getState().spaces.find(space => space.accountLocalID === spaceLocalID); const featureAccount = accounts.find(account => account.localID === accountId) as PrimaryAccount; if (!space || !featureAccount || !accountId) return undefined; From 163b77a44036cf654b396393d4d0dd7ffe5f5c4b Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 19:01:52 +0100 Subject: [PATCH 0101/1144] fix: changed space account type --- src/stores/account/types.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/stores/account/types.ts b/src/stores/account/types.ts index 6a44ec53e..4af189660 100644 --- a/src/stores/account/types.ts +++ b/src/stores/account/types.ts @@ -247,7 +247,8 @@ export interface PapillonMultiServiceSpace extends BaseAccount { authentication: null identityProvider: { name: string - } + }, + linkedExternalLocalIDs: string[] } export type PrimaryAccount = ( From 442b50b6ba0cc8605e84073ba709a09b1592a194 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 19:04:15 +0100 Subject: [PATCH 0102/1144] fix: set accounts ids as external accounts to reload --- src/stores/multiService/types.ts | 9 --------- src/views/settings/SettingsMultiServiceSpace.tsx | 5 +++++ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/stores/multiService/types.ts b/src/stores/multiService/types.ts index 6218be6fb..344afde40 100644 --- a/src/stores/multiService/types.ts +++ b/src/stores/multiService/types.ts @@ -19,15 +19,6 @@ export interface MultiServiceSpace { featuresServices: { [key in MultiServiceFeature]?: string } - /* - Each key represents an account id, and is associated to its instance and authentication objects - */ - authentication: { - [key: string]: { - authentication: any - instance: any - } | undefined - } } export interface MultiServiceStore { diff --git a/src/views/settings/SettingsMultiServiceSpace.tsx b/src/views/settings/SettingsMultiServiceSpace.tsx index c0933122a..ccb23b3c9 100644 --- a/src/views/settings/SettingsMultiServiceSpace.tsx +++ b/src/views/settings/SettingsMultiServiceSpace.tsx @@ -92,6 +92,10 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga const setAccountFeature = (account: PrimaryAccount, feature: MultiServiceFeature) => { setMultiServiceSpaceAccountFeature(space.accountLocalID, feature, account); + let linkedAccountsIds = [...(linkedAccount?.linkedExternalLocalIDs || []), account.localID]; + linkedAccountsIds = linkedAccountsIds.filter((value, index) => linkedAccountsIds.indexOf(value) === index); // Remove duplicates + // Putting the space's associated accounts ids in linkedExternalLocalIDs permits the reload of their instance / authentication fields (like externals accounts) + accounts.update(space.accountLocalID, "linkedExternalLocalIDs", linkedAccountsIds); setAccountSelectorOpened(false); setSelectedAccount(null); }; @@ -292,6 +296,7 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga {features.map((feature, index) => ( <> Date: Thu, 2 Jan 2025 19:10:43 +0100 Subject: [PATCH 0103/1144] fix: set accounts ids as external accounts to reload --- src/views/settings/SettingsMultiService.tsx | 22 ++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index 1d69e2f18..375a6a4d2 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -16,6 +16,7 @@ import {MultiServiceSpace} from "@/stores/multiService/types"; import {AccountService, PapillonMultiServiceSpace} from "@/stores/account/types"; import uuid from "@/utils/uuid-v4"; import {defaultProfilePicture} from "@/utils/ui/default-profile-picture"; +import {defaultTabs} from "@/consts/DefaultTabs"; const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => { const theme = useTheme(); @@ -63,7 +64,15 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => const localID = uuid(); - // TODO: have student name and space title + const defaultSpaceTabs = [ + "Home", + "Lessons", + "Homeworks", + "Grades", + "News", + "Attendance" + ] as typeof defaultTabs[number]["tab"][]; + const linkedAccount: PapillonMultiServiceSpace = { isExternal: false, linkedExternalLocalIDs: [], @@ -76,7 +85,11 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => localID: localID, name: spaceName, personalization: { - profilePictureB64: selectedImage || undefined + profilePictureB64: selectedImage || undefined, + tabs: defaultTabs.filter(current => defaultSpaceTabs.includes(current.tab)).map((tab, index) => ({ + name: tab.tab, + enabled: index <= 4 + })) }, service: AccountService.PapillonMultiService, studentName: { // TODO @@ -94,7 +107,6 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => homeworks: undefined, attendance: undefined }, - authentication: {}, name: spaceName, image: selectedImage || undefined }; @@ -159,9 +171,9 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => <> - {multiServiceSpaces.map(space => ( + {multiServiceSpaces.map((space, index) => ( navigation.navigate("SettingsMultiServiceSpace", { space })} leading={ Date: Thu, 2 Jan 2025 23:17:43 +0100 Subject: [PATCH 0104/1144] fix: updated account type to support new property `associatedAccountsIds`, to store space's associated accounts --- src/stores/account/index.ts | 24 +++++++++++++++++++----- src/stores/account/types.ts | 28 ++++++++++++++++++---------- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index b73ad49f6..dc388ec54 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -26,6 +26,8 @@ import { info, log } from "@/utils/logger/logger"; export const useCurrentAccount = create()((set, get) => ({ account: null, linkedAccounts: [], + // For multi service, to not mix Primary & External accounts + associatedAccounts: [], mutateProperty: (key: T, value: PrimaryAccount[T]) => { log(`mutate property ${key} in storage`, "current:update"); @@ -81,9 +83,11 @@ export const useCurrentAccount = create()((set, get) => ({ return store.persist.rehydrate(); })); + const accounts = useAccounts.getState().accounts; + // Special case for spaces if (account.service === AccountService.PapillonMultiService) { - log("switching to virtual space, skipping account reload...", "[switchTo]"); + log("switching to virtual space, reloading associated accounts...", "[switchTo]"); } else if (typeof account.instance === "undefined") { // Account is currently not authenticated, log("instance undefined, reloading...", "[switchTo]"); // Automatically reconnect the main instance. @@ -93,12 +97,15 @@ export const useCurrentAccount = create()((set, get) => ({ log("instance reload done !", "[switchTo]"); } - const accounts = useAccounts.getState().accounts; const linkedAccounts = account.linkedExternalLocalIDs.map((linkedID) => { return {...accounts.find((acc) => acc.localID === linkedID)}; }).filter(Boolean) as ExternalAccount[] ?? []; - info(`found ${linkedAccounts.length} external accounts`, "switchTo"); + const associatedAccounts = account.associatedAccountsLocalIDs?.map((associatedID) => { + return {...accounts.find((acc) => acc.localID === associatedID)}; + }).filter(Boolean) as PrimaryAccount[] ?? []; + + info(`found ${linkedAccounts.length} external accounts and ${associatedAccounts.length} associated accounts`, "switchTo"); for (const linkedAccount of linkedAccounts) { const { instance, authentication } = await reload(linkedAccount); @@ -107,9 +114,16 @@ export const useCurrentAccount = create()((set, get) => ({ log("reloaded external", "[switchTo]"); } - log("reloaded all external accounts", "[switchTo]"); + for (const associatedAccount of associatedAccounts) { + const { instance, authentication } = await reload(associatedAccount); + associatedAccount.instance = instance; + associatedAccount.authentication = authentication; + log("reloaded associated account", "[switchTo]"); + } + + log("reloaded all external and associated accounts", "[switchTo]"); - set({ linkedAccounts }); + set({ linkedAccounts, associatedAccounts }); log(`done reading ${account.name} and rehydrating stores.`, "[switchTo]"); }, diff --git a/src/stores/account/types.ts b/src/stores/account/types.ts index 4af189660..1acdf5cbc 100644 --- a/src/stores/account/types.ts +++ b/src/stores/account/types.ts @@ -86,6 +86,7 @@ export interface CurrentAccountStore { /** Si un compte est en cours d'utilisation, on obtient l'ID, sinon `null`. */ account: PrimaryAccount | null linkedAccounts: ExternalAccount[] + associatedAccounts: PrimaryAccount[] mutateProperty: (key: T, value: PrimaryAccount[T]) => void linkExistingExternalAccount: (account: ExternalAccount) => void switchTo: (account: PrimaryAccount) => Promise @@ -146,6 +147,7 @@ export interface PronoteAccount extends BaseAccount { deviceUUID: string } identityProvider?: undefined + associatedAccountsLocalIDs?: undefined } export interface EcoleDirecteAccount extends BaseAccount { @@ -156,6 +158,7 @@ export interface EcoleDirecteAccount extends BaseAccount { account: PawdirecteAccount } identityProvider?: undefined + associatedAccountsLocalIDs?: undefined } export interface SkolengoAccount extends BaseAccount { @@ -164,6 +167,7 @@ export interface SkolengoAccount extends BaseAccount { authentication: SkolengoAuthConfig userInfo: ScolengoAPIUser identityProvider?: undefined + associatedAccountsLocalIDs?: undefined } export interface MultiAccount extends BaseAccount { @@ -174,6 +178,7 @@ export interface MultiAccount extends BaseAccount { refreshAuthToken: string } identityProvider?: undefined + associatedAccountsLocalIDs?: undefined } export interface LocalAccount extends BaseAccount { @@ -193,8 +198,21 @@ export interface LocalAccount extends BaseAccount { username: string password: string } + + associatedAccountsLocalIDs?: undefined +} + +export interface PapillonMultiServiceSpace extends BaseAccount { + service: AccountService.PapillonMultiService + instance: null + authentication: null + identityProvider: { + name: string + }, + associatedAccountsLocalIDs: string[] } + export interface TurboselfAccount extends BaseExternalAccount { service: AccountService.Turboself instance: undefined @@ -241,16 +259,6 @@ export interface IzlyAccount extends BaseExternalAccount { } } -export interface PapillonMultiServiceSpace extends BaseAccount { - service: AccountService.PapillonMultiService - instance: null - authentication: null - identityProvider: { - name: string - }, - linkedExternalLocalIDs: string[] -} - export type PrimaryAccount = ( | PronoteAccount | EcoleDirecteAccount From a011ba8cd0513938fbb01572af0b8bd5cfc81dd2 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 23:18:13 +0100 Subject: [PATCH 0105/1144] fix: updated getAccountFeature to dynamiclly add instance & authentication fields --- src/utils/multiservice/index.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/utils/multiservice/index.ts b/src/utils/multiservice/index.ts index d83150559..9be274fa2 100644 --- a/src/utils/multiservice/index.ts +++ b/src/utils/multiservice/index.ts @@ -1,17 +1,16 @@ import {MultiServiceFeature} from "@/stores/multiService/types"; -import {useAccounts} from "@/stores/account"; +import {useAccounts, useCurrentAccount} from "@/stores/account"; import {useMultiService} from "@/stores/multiService"; import {PrimaryAccount} from "@/stores/account/types"; export function getFeatureAccount (feature: MultiServiceFeature, spaceLocalID: string) { const accounts = useAccounts.getState().accounts; const accountId = useMultiService.getState().getFeatureAccountId(feature, spaceLocalID); - const space = useMultiService.getState().spaces.find(space => space.accountLocalID === spaceLocalID); - const featureAccount = accounts.find(account => account.localID === accountId) as PrimaryAccount; - - if (!space || !featureAccount || !accountId) return undefined; - - featureAccount.authentication = space.authentication[accountId]?.authentication; - featureAccount.instance = space.authentication[accountId]?.instance; + const featureAccount = accounts.find(account => account.localID === accountId) as PrimaryAccount | undefined; + if (featureAccount) { + const linkedAccount = useCurrentAccount.getState().associatedAccounts.find(account => account.localID === accountId) as PrimaryAccount; + featureAccount.instance = linkedAccount?.instance; + featureAccount.authentication = linkedAccount?.authentication; + } return featureAccount; } From ddd29d2ca0638212bebe4bc018e33e0d2535af57 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 23:18:28 +0100 Subject: [PATCH 0106/1144] fix: changes to support new types --- src/views/settings/SettingsMultiService.tsx | 3 ++- src/views/settings/SettingsMultiServiceSpace.tsx | 12 +++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index 375a6a4d2..559dcd051 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -76,6 +76,7 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => const linkedAccount: PapillonMultiServiceSpace = { isExternal: false, linkedExternalLocalIDs: [], + associatedAccountsLocalIDs: [], authentication: null, identity: {}, identityProvider: { @@ -92,7 +93,7 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => })) }, service: AccountService.PapillonMultiService, - studentName: { // TODO + studentName: { first: currentAccount.account?.studentName.first || "", last: currentAccount.account?.studentName.last || "" } diff --git a/src/views/settings/SettingsMultiServiceSpace.tsx b/src/views/settings/SettingsMultiServiceSpace.tsx index ccb23b3c9..33c704f73 100644 --- a/src/views/settings/SettingsMultiServiceSpace.tsx +++ b/src/views/settings/SettingsMultiServiceSpace.tsx @@ -95,7 +95,8 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga let linkedAccountsIds = [...(linkedAccount?.linkedExternalLocalIDs || []), account.localID]; linkedAccountsIds = linkedAccountsIds.filter((value, index) => linkedAccountsIds.indexOf(value) === index); // Remove duplicates // Putting the space's associated accounts ids in linkedExternalLocalIDs permits the reload of their instance / authentication fields (like externals accounts) - accounts.update(space.accountLocalID, "linkedExternalLocalIDs", linkedAccountsIds); + // @ts-expect-error + accounts.update(space.accountLocalID, "associatedAccountsLocalIDs", linkedAccountsIds); setAccountSelectorOpened(false); setSelectedAccount(null); }; @@ -149,6 +150,7 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga selectPicture()} leading={(selectedImage || space.image) && @@ -190,6 +192,7 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga spaceNameRef.current?.focus()} chevron={false} icon={} @@ -228,6 +231,7 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga firstNameRef.current?.focus()} chevron={false} icon={} @@ -257,6 +261,7 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga lastNameRef.current?.focus()} chevron={false} icon={} @@ -296,7 +301,7 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga {features.map((feature, index) => ( <> = ({ naviga > {feature.name} - {space.featuresServices[feature.feature] ? ( + {accounts.accounts.find(account => account.localID === space.featuresServices[feature.feature]) ? ( account.localID === space.featuresServices[feature.feature]) as PrimaryAccount} endCheckMark={false} additionalStyles={{ paddingStart: 10, borderBottomWidth: 1, @@ -344,6 +349,7 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga }}/> ): ( } separator={true} > From a2f740bb472126e1853cd768104a61601bbf7e55 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Thu, 2 Jan 2025 23:31:00 +0100 Subject: [PATCH 0107/1144] fix: wrong property --- src/views/settings/SettingsMultiServiceSpace.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/settings/SettingsMultiServiceSpace.tsx b/src/views/settings/SettingsMultiServiceSpace.tsx index 33c704f73..a6e05fe1c 100644 --- a/src/views/settings/SettingsMultiServiceSpace.tsx +++ b/src/views/settings/SettingsMultiServiceSpace.tsx @@ -92,7 +92,7 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga const setAccountFeature = (account: PrimaryAccount, feature: MultiServiceFeature) => { setMultiServiceSpaceAccountFeature(space.accountLocalID, feature, account); - let linkedAccountsIds = [...(linkedAccount?.linkedExternalLocalIDs || []), account.localID]; + let linkedAccountsIds = [...(linkedAccount?.associatedAccountsLocalIDs || []), account.localID]; linkedAccountsIds = linkedAccountsIds.filter((value, index) => linkedAccountsIds.indexOf(value) === index); // Remove duplicates // Putting the space's associated accounts ids in linkedExternalLocalIDs permits the reload of their instance / authentication fields (like externals accounts) // @ts-expect-error From 680bae351673548892f20fa105b1d06fa2de4412 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Fri, 3 Jan 2025 12:51:04 +0100 Subject: [PATCH 0108/1144] fix: removed TODO --- src/stores/multiService/types.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/stores/multiService/types.ts b/src/stores/multiService/types.ts index 344afde40..311ca3816 100644 --- a/src/stores/multiService/types.ts +++ b/src/stores/multiService/types.ts @@ -12,7 +12,6 @@ export interface MultiServiceSpace { accountLocalID: string name: string image?: string - // TODO use account ids instead: less disk space + supports changes /* Each feature returns its linked account localID */ From 9b51668df3ad9e6cc67fba05a297c6b441a1b91c Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Fri, 3 Jan 2025 12:56:49 +0100 Subject: [PATCH 0109/1144] feat: added more features --- src/stores/multiService/types.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/stores/multiService/types.ts b/src/stores/multiService/types.ts index 311ca3816..e135b33b2 100644 --- a/src/stores/multiService/types.ts +++ b/src/stores/multiService/types.ts @@ -5,7 +5,9 @@ export enum MultiServiceFeature { Timetable = "timetable", Homeworks = "homeworks", Attendance = "attendance", - News = "news" + News = "news", + Evaluations = "evaluations", + Messages = "messages" } export interface MultiServiceSpace { From 7446453d9aeb8aada89ce4a8a1f762d572af1c8d Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Fri, 3 Jan 2025 12:56:59 +0100 Subject: [PATCH 0110/1144] feat: features in settings --- src/views/settings/SettingsMultiServiceSpace.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/views/settings/SettingsMultiServiceSpace.tsx b/src/views/settings/SettingsMultiServiceSpace.tsx index a6e05fe1c..5b45ef740 100644 --- a/src/views/settings/SettingsMultiServiceSpace.tsx +++ b/src/views/settings/SettingsMultiServiceSpace.tsx @@ -109,6 +109,11 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga feature: MultiServiceFeature.Grades, icon: require("@/../assets/lottie/tab_chart.json") }, + { + name: "Compétences", + feature: MultiServiceFeature.Evaluations, + icon: require("@/../assets/lottie/tab_evaluations.json") + }, { name: "Emploi du temps", feature: MultiServiceFeature.Timetable, @@ -128,6 +133,11 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga name: "Actualités", feature: MultiServiceFeature.News, icon: require("@/../assets/lottie/tab_news.json") + }, + { + name: "Messages", + feature: MultiServiceFeature.Messages, + icon: require("@/../assets/lottie/tab_chat.json") } ]; From c4c8af32cdf38750422e7ffc75e5fd97787c2130 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Fri, 3 Jan 2025 12:58:20 +0100 Subject: [PATCH 0111/1144] feat: space support for evaluations --- src/services/evaluation.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/services/evaluation.ts b/src/services/evaluation.ts index 351881bd2..c15b99e16 100644 --- a/src/services/evaluation.ts +++ b/src/services/evaluation.ts @@ -3,6 +3,8 @@ import type { Period } from "./shared/Period"; import {useEvaluationStore} from "@/stores/evaluation"; import {Evaluation} from "@/services/shared/Evaluation"; import {error} from "@/utils/logger/logger"; +import {getFeatureAccount} from "@/utils/multiservice"; +import {MultiServiceFeature} from "@/stores/multiService/types"; const getDefaultPeriod = (periods: Period[]): string => { const now = Date.now(); @@ -24,6 +26,13 @@ export async function updateEvaluationPeriodsInCache (accoun break; } + case AccountService.PapillonMultiService: { + const service = getFeatureAccount(MultiServiceFeature.Evaluations, account.localID); + if (!service) { + throw new Error("No service set in multi-service space"); + } + return await updateEvaluationPeriodsInCache(service, periodName); + } default: throw new Error("Service not implemented"); } @@ -41,6 +50,13 @@ export async function updateEvaluationsInCache (account: T, evaluations = await getEvaluations(account, periodName); break; } + case AccountService.PapillonMultiService: { + const service = getFeatureAccount(MultiServiceFeature.Evaluations, account.localID); + if (!service) { + throw new Error("No service set in multi-service space"); + } + return await updateEvaluationsInCache(service, periodName); + } default: throw new Error(`Service (${AccountService[account.service]}) not implemented for this request`); } @@ -49,4 +65,4 @@ export async function updateEvaluationsInCache (account: T, catch (err) { error(`not updated, see:${err}`, "updateGradesAndAveragesInCache"); } -} \ No newline at end of file +} From 3254b96e53059050c88d0e7cad224e010ce08c6c Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Fri, 3 Jan 2025 12:59:58 +0100 Subject: [PATCH 0112/1144] fix: changed feature name --- src/stores/multiService/types.ts | 2 +- src/views/settings/SettingsMultiServiceSpace.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/stores/multiService/types.ts b/src/stores/multiService/types.ts index e135b33b2..42795b55a 100644 --- a/src/stores/multiService/types.ts +++ b/src/stores/multiService/types.ts @@ -7,7 +7,7 @@ export enum MultiServiceFeature { Attendance = "attendance", News = "news", Evaluations = "evaluations", - Messages = "messages" + Chats = "chats" } export interface MultiServiceSpace { diff --git a/src/views/settings/SettingsMultiServiceSpace.tsx b/src/views/settings/SettingsMultiServiceSpace.tsx index 5b45ef740..9d9a46a96 100644 --- a/src/views/settings/SettingsMultiServiceSpace.tsx +++ b/src/views/settings/SettingsMultiServiceSpace.tsx @@ -135,8 +135,8 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga icon: require("@/../assets/lottie/tab_news.json") }, { - name: "Messages", - feature: MultiServiceFeature.Messages, + name: "Discussions", + feature: MultiServiceFeature.Chats, icon: require("@/../assets/lottie/tab_chat.json") } ]; From 445901d1030c23739b38024f949bfd7969861b45 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Fri, 3 Jan 2025 13:01:00 +0100 Subject: [PATCH 0113/1144] fix: too many parameters for `updateEvaluationPeriodsInCache` --- src/services/evaluation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/evaluation.ts b/src/services/evaluation.ts index c15b99e16..1656bd596 100644 --- a/src/services/evaluation.ts +++ b/src/services/evaluation.ts @@ -31,7 +31,7 @@ export async function updateEvaluationPeriodsInCache (accoun if (!service) { throw new Error("No service set in multi-service space"); } - return await updateEvaluationPeriodsInCache(service, periodName); + return await updateEvaluationPeriodsInCache(service); } default: throw new Error("Service not implemented"); From 772e3ce99b3bfd513624fa4c91a5956def4cc20d Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Fri, 3 Jan 2025 13:54:33 +0100 Subject: [PATCH 0114/1144] fix: modified account types to resolve type errors --- src/stores/account/types.ts | 4 +++- src/views/settings/SettingsMultiService.tsx | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/stores/account/types.ts b/src/stores/account/types.ts index 1acdf5cbc..8a28d7422 100644 --- a/src/stores/account/types.ts +++ b/src/stores/account/types.ts @@ -207,7 +207,9 @@ export interface PapillonMultiServiceSpace extends BaseAccount { instance: null authentication: null identityProvider: { - name: string + name: string, + identifier: undefined, + rawData: undefined }, associatedAccountsLocalIDs: string[] } diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index 559dcd051..c6598378e 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -80,7 +80,9 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => authentication: null, identity: {}, identityProvider: { - name: spaceName + name: spaceName, + identifier: undefined, + rawData: undefined }, instance: null, localID: localID, From da40057a0e5d5db01d720dae4ec6ffb3684ecb76 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Fri, 3 Jan 2025 13:54:39 +0100 Subject: [PATCH 0115/1144] feat: multi-service support for chats --- src/services/chats.ts | 44 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/services/chats.ts b/src/services/chats.ts index c6d581274..b3ac838df 100644 --- a/src/services/chats.ts +++ b/src/services/chats.ts @@ -1,6 +1,8 @@ import { type Account, AccountService } from "@/stores/account/types"; import type { Chat, ChatMessage, ChatRecipient } from "./shared/Chat"; import type { Recipient } from "./shared/Recipient"; +import {getFeatureAccount} from "@/utils/multiservice"; +import {MultiServiceFeature} from "@/stores/multiService/types"; export const getChats = async (account: T): Promise> => { switch (account.service) { @@ -12,6 +14,13 @@ export const getChats = async (account: T): Promise (account: T, chat: Ch // TODO return []; } + case AccountService.PapillonMultiService: { + const service = getFeatureAccount(MultiServiceFeature.Chats, account.localID); + if (!service) { + throw new Error("No service set in multi-service space"); + } + return await getChatRecipients(service, chat); + } default: console.info(`[getChatRecipients]: returning empty since ${account.service} not implemented.`); return []; @@ -43,6 +59,13 @@ export const sendMessageInChat = async (account: T, chat: Ch case AccountService.EcoleDirecte: { // TODO } + case AccountService.PapillonMultiService: { + const service = getFeatureAccount(MultiServiceFeature.Chats, account.localID); + if (!service) { + throw new Error("No service set in multi-service space"); + } + return await sendMessageInChat(service, chat, content); + } default: console.info("[sendMessageInChat]: Not Implementend."); } @@ -58,6 +81,13 @@ export const getChatMessages = async (account: T, chat: Chat const { getChatMessages } = await import("./ecoledirecte/chats"); return [await getChatMessages(account, chat)]; } + case AccountService.PapillonMultiService: { + const service = getFeatureAccount(MultiServiceFeature.Chats, account.localID); + if (!service) { + throw new Error("No service set in multi-service space"); + } + return await getChatMessages(service, chat); + } default: console.info(`[getChatMessages]: returning empty since ${account.service} not implemented.`); return []; @@ -70,6 +100,13 @@ export const createDiscussionRecipients = async (account: T) const { createDiscussionRecipients } = await import("./pronote/chats"); return createDiscussionRecipients(account); } + case AccountService.PapillonMultiService: { + const service = getFeatureAccount(MultiServiceFeature.Chats, account.localID); + if (!service) { + throw new Error("No service set in multi-service space"); + } + return await createDiscussionRecipients(service); + } default: console.info(`[createDiscussionRecipients]: returning empty since ${account.service} not implemented.`); return []; @@ -83,6 +120,13 @@ export const createDiscussion = async (account: T, subject: createDiscussion(account, subject, content, recipients); break; } + case AccountService.PapillonMultiService: { + const service = getFeatureAccount(MultiServiceFeature.Chats, account.localID); + if (!service) { + throw new Error("No service set in multi-service space"); + } + return await createDiscussion(service, subject, content, recipients); + } default: console.info(`[createDiscussion]: doing nothing since ${account.service} is not implemented.`); } From 75b44a20d9d1edb29ce4ec5aae9cebb1ee9dc15e Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Fri, 3 Jan 2025 13:55:52 +0100 Subject: [PATCH 0116/1144] fix: linter --- src/services/homework.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/homework.ts b/src/services/homework.ts index 57b3beff3..a5d8d65a0 100644 --- a/src/services/homework.ts +++ b/src/services/homework.ts @@ -49,7 +49,7 @@ export async function updateHomeworkForWeekInCache (account: case AccountService.PapillonMultiService: { const service = getFeatureAccount(MultiServiceFeature.Homeworks, account.localID); if (!service) { - console.info(`[updateHomeworkForWeekInCache]: updating to empty since multi-service space has no account set for homeworks.`); + console.info("[updateHomeworkForWeekInCache]: updating to empty since multi-service space has no account set for homeworks."); break; } return updateHomeworkForWeekInCache(service, date); From e89f62a2d90693870887e44ceee46cbeb7280093 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Fri, 3 Jan 2025 13:59:31 +0100 Subject: [PATCH 0117/1144] fix: better implementation in grades --- src/services/grades.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/grades.ts b/src/services/grades.ts index ebc25bbb8..5b04c4dfe 100644 --- a/src/services/grades.ts +++ b/src/services/grades.ts @@ -61,7 +61,7 @@ export async function updateGradesPeriodsInCache (account: T if (!service) { throw new Error("No service set in multi-service space"); } - return updateGradesPeriodsInCache(service); + return await updateGradesPeriodsInCache(service); } default: throw new Error("Service not implemented"); @@ -134,7 +134,7 @@ export async function updateGradesAndAveragesInCache (accoun if (!service) { throw new Error("No service set in multi-service space"); } - return updateGradesAndAveragesInCache(service, periodName); + return await updateGradesAndAveragesInCache(service, periodName); } default: throw new Error(`Service (${AccountService[account.service]}) not implemented for this request`); From 3519dde875ce90be35064fba4119133746d50aae Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Fri, 3 Jan 2025 14:04:37 +0100 Subject: [PATCH 0118/1144] fix: prevent infinite loading and account interpreted as "disconnected" --- src/stores/account/index.ts | 3 ++- src/stores/account/types.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index dc388ec54..4a6c14b16 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -87,7 +87,8 @@ export const useCurrentAccount = create()((set, get) => ({ // Special case for spaces if (account.service === AccountService.PapillonMultiService) { - log("switching to virtual space, reloading associated accounts...", "[switchTo]"); + log("switching to virtual space, setting instance to a non-null value and reloading associated accounts...", "[switchTo]"); + account.instance = "PapillonPrime"; // Une chaine random, juste pour que l'instance ne soit pas "undefined" (ou null) et que l'espace multiservice soit interprété comme "déconnecté" } else if (typeof account.instance === "undefined") { // Account is currently not authenticated, log("instance undefined, reloading...", "[switchTo]"); // Automatically reconnect the main instance. diff --git a/src/stores/account/types.ts b/src/stores/account/types.ts index 8a28d7422..514da8be5 100644 --- a/src/stores/account/types.ts +++ b/src/stores/account/types.ts @@ -204,7 +204,7 @@ export interface LocalAccount extends BaseAccount { export interface PapillonMultiServiceSpace extends BaseAccount { service: AccountService.PapillonMultiService - instance: null + instance: null | string authentication: null identityProvider: { name: string, From cf986f0895db8429f8eac3416569ce880db22641 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Fri, 3 Jan 2025 14:55:25 +0100 Subject: [PATCH 0119/1144] fix: added warn to `reload-account.ts` --- src/services/reload-account.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/services/reload-account.ts b/src/services/reload-account.ts index 3370b84ab..ed8046bf6 100644 --- a/src/services/reload-account.ts +++ b/src/services/reload-account.ts @@ -61,6 +61,9 @@ export async function reload (account: T): Promise; } + case AccountService.PapillonMultiService: { + console.warn("PapillonMultiService space should never be reloaded."); + } default: { console.warn("Service not implemented"); return { instance: undefined, authentication: undefined }; From 57e8e3f3df1c24e3f972130dd4e2a2d459a7d0f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 3 Jan 2025 16:09:21 +0100 Subject: [PATCH 0120/1144] =?UTF-8?q?cr=C3=A9ation=20de=20la=20page=20"Son?= =?UTF-8?q?=20et=20vibrations"=20(issue=20de=20la=20page=20affichage)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/helpers/types.ts | 1 + src/router/screens/settings/index.ts | 6 +- src/views/settings/Settings.tsx | 7 ++ src/views/settings/SettingsSoundHaptics.tsx | 128 ++++++++++++++++++++ 4 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 src/views/settings/SettingsSoundHaptics.tsx diff --git a/src/router/helpers/types.ts b/src/router/helpers/types.ts index ede29094a..3bad76557 100644 --- a/src/router/helpers/types.ts +++ b/src/router/helpers/types.ts @@ -133,6 +133,7 @@ export type RouteParameters = { SettingsDevLogs: undefined; SettingsDonorsList: undefined; SettingsApparence: undefined; + SettingsSoundHaptics: undefined; Menu?: undefined; RestaurantQrCode: { diff --git a/src/router/screens/settings/index.ts b/src/router/screens/settings/index.ts index 68a83b5cf..70bf3a285 100644 --- a/src/router/screens/settings/index.ts +++ b/src/router/screens/settings/index.ts @@ -25,6 +25,7 @@ import SettingsFlagsInfos from "@/views/settings/SettingsFlagsInfos"; import ExternalIzlyLogin from "@/views/settings/ExternalAccount/Izly"; import IzlyActivation from "@/views/settings/ExternalAccount/IzlyActivation"; import TurboselfAccountSelector from "@/views/settings/ExternalAccount/TurboselfAccountSelector"; +import SettingsSoundHaptics from "@/views/settings/SettingsSoundHaptics"; import SettingsApparence from "@/views/settings/SettingsApparence"; import ExternalAliseLogin from "@/views/settings/ExternalAccount/Alise"; @@ -131,7 +132,10 @@ const settingsScreens = [ }), createScreen("SettingsApparence", SettingsApparence, { headerTitle: "Mode d'affichage", - }) + }), + createScreen("SettingsSoundHaptics", SettingsSoundHaptics, { + headerTitle: "Son et vibrations", + }), ] as const; export default settingsScreens; diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index 8d46498b0..9e6f75418 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -30,6 +30,7 @@ import { Settings as SettingsLucide, Sparkles, SunMoon, SwatchBook, + Volume2, WandSparkles, X } from "lucide-react-native"; @@ -147,6 +148,12 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { }, 10); } }, + { + icon: , + color: "#FF0075", + label: "Son et vibrations", + onPress: () => navigation.navigate("SettingsSoundHaptics"), + }, { icon: , color: "#1e316a", diff --git a/src/views/settings/SettingsSoundHaptics.tsx b/src/views/settings/SettingsSoundHaptics.tsx new file mode 100644 index 000000000..5f262db88 --- /dev/null +++ b/src/views/settings/SettingsSoundHaptics.tsx @@ -0,0 +1,128 @@ +import React, { useEffect, useState } from "react"; +import { ScrollView } from "react-native"; +import type { Screen } from "@/router/helpers/types"; +import { useTheme } from "@react-navigation/native"; +import {Moon, RefreshCw, Sun, SunMoon} from "lucide-react-native"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { NativeList, NativeItem, NativeListHeader } from "@/components/Global/NativeComponents"; +import { NativeText } from "@/components/Global/NativeComponents"; +import PapillonCheckbox from "@/components/Global/PapillonCheckbox"; +import AsyncStorage from "@react-native-async-storage/async-storage"; +import Animated, {FadeInDown, FadeOutDown} from "react-native-reanimated"; +import ApparenceContainerCard from "@/components/Settings/ApparenceContainerCard"; + +const SettingsApparence: Screen<"SettingsSoundHaptics"> = () => { + const theme = useTheme(); + const insets = useSafeAreaInsets(); + + const [selectedTheme, setSelectedTheme] = useState(0); + const [hasUserChangedTheme, setHasUserChangedTheme] = useState(false); + const [defaultTheme, setDefaultTheme] = useState(0); + + useEffect(() => { + AsyncStorage.getItem("theme").then((value) => { + if (value) + { + setDefaultTheme(parseInt(value)); + setSelectedTheme(parseInt(value)); + } + }); + }, []); + + useEffect(() => { + setHasUserChangedTheme(selectedTheme !== defaultTheme); + AsyncStorage.setItem("theme", selectedTheme.toString()); + }, [selectedTheme]); + + return ( + + + + + + + } + trailing={ + { + setSelectedTheme(0); + }} + style={{marginRight: 5}} + /> + } + onPress={() => {setSelectedTheme(0);}} + chevron={false} + > + Suivre le thème du système + + } + trailing={ + { + setSelectedTheme(1); + }} + style={{marginRight: 5}} + /> + } + onPress={() => {setSelectedTheme(1);}} + chevron={false} + > + Mode clair + + } + trailing={ + { + setSelectedTheme(2); + }} + style={{marginRight: 5}} + /> + } + onPress={() => {setSelectedTheme(2);}} + chevron={false} + > + Mode sombre + + + + {hasUserChangedTheme && ( + + + } + style={{backgroundColor: "#C53424"}} + > + Redémarrer l'application + Cette option nécessite un redémarrage de l'application pour être appliquée. + + + + )} + + ); +}; + +export default SettingsApparence; From 10ec90a4298a6798dcc7ee73e19c511206d425d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 3 Jan 2025 16:47:00 +0100 Subject: [PATCH 0121/1144] =?UTF-8?q?feat(Settings):=20ajout=20de=20la=20g?= =?UTF-8?q?estion=20des=20sons=20et=20vibrations=20dans=20les=20param?= =?UTF-8?q?=C3=A8tres?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Settings/SoundHapticsContainerCard.tsx | 31 ++++ src/views/settings/SettingsSoundHaptics.tsx | 152 +++++++++--------- 2 files changed, 110 insertions(+), 73 deletions(-) create mode 100644 src/components/Settings/SoundHapticsContainerCard.tsx diff --git a/src/components/Settings/SoundHapticsContainerCard.tsx b/src/components/Settings/SoundHapticsContainerCard.tsx new file mode 100644 index 000000000..a8ece8e46 --- /dev/null +++ b/src/components/Settings/SoundHapticsContainerCard.tsx @@ -0,0 +1,31 @@ +import React from "react"; + +import { View } from "react-native"; +import { NativeItem, NativeList, NativeText } from "../Global/NativeComponents"; + +const ApparenceContainerCard = () => { + return ( + + + 🔊 + + + Son et vibrations + + Par défaut, Papillon joue des sons et des vibrations mais cela peut être changé. + + + + ); +}; + +export default ApparenceContainerCard; diff --git a/src/views/settings/SettingsSoundHaptics.tsx b/src/views/settings/SettingsSoundHaptics.tsx index 5f262db88..60d592e8b 100644 --- a/src/views/settings/SettingsSoundHaptics.tsx +++ b/src/views/settings/SettingsSoundHaptics.tsx @@ -1,38 +1,53 @@ import React, { useEffect, useState } from "react"; -import { ScrollView } from "react-native"; +import { ScrollView, Switch } from "react-native"; import type { Screen } from "@/router/helpers/types"; -import { useTheme } from "@react-navigation/native"; -import {Moon, RefreshCw, Sun, SunMoon} from "lucide-react-native"; +import { RefreshCw, Vibrate, Volume2 } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import { NativeList, NativeItem, NativeListHeader } from "@/components/Global/NativeComponents"; +import { + NativeList, + NativeItem, + NativeListHeader, + NativeIconGradient, +} from "@/components/Global/NativeComponents"; import { NativeText } from "@/components/Global/NativeComponents"; -import PapillonCheckbox from "@/components/Global/PapillonCheckbox"; import AsyncStorage from "@react-native-async-storage/async-storage"; -import Animated, {FadeInDown, FadeOutDown} from "react-native-reanimated"; -import ApparenceContainerCard from "@/components/Settings/ApparenceContainerCard"; +import Animated, { FadeInDown, FadeOutDown } from "react-native-reanimated"; +import SoundHapticsContainerCard from "@/components/Settings/SoundHapticsContainerCard"; const SettingsApparence: Screen<"SettingsSoundHaptics"> = () => { - const theme = useTheme(); const insets = useSafeAreaInsets(); - const [selectedTheme, setSelectedTheme] = useState(0); - const [hasUserChangedTheme, setHasUserChangedTheme] = useState(false); - const [defaultTheme, setDefaultTheme] = useState(0); + const [defaultSon, setDefaultSon] = useState(true); + const [playSon, setPlaySon] = useState(true); + const [defaultHaptics, setDefaultHaptics] = useState(true); + const [playHaptics, setPlayHaptics] = useState(true); + const [hasUserChanged, setHasUserChanged] = useState(false); useEffect(() => { - AsyncStorage.getItem("theme").then((value) => { - if (value) - { - setDefaultTheme(parseInt(value)); - setSelectedTheme(parseInt(value)); + AsyncStorage.getItem("son").then((value) => { + if (value) { + setDefaultSon(Boolean(value)); + setPlaySon(Boolean(value)); + } + }); + + AsyncStorage.getItem("haptics").then((value) => { + if (value) { + setDefaultHaptics(Boolean(value)); + setPlayHaptics(Boolean(value)); } }); }, []); useEffect(() => { - setHasUserChangedTheme(selectedTheme !== defaultTheme); - AsyncStorage.setItem("theme", selectedTheme.toString()); - }, [selectedTheme]); + setHasUserChanged(playSon !== defaultSon); + AsyncStorage.setItem("son", playSon.toString()); + }, [playSon]); + + useEffect(() => { + setHasUserChanged(playHaptics !== defaultHaptics); + AsyncStorage.setItem("haptics", playHaptics.toString()); + }, [playHaptics]); return ( = () => { paddingBottom: insets.bottom + 16, }} > - - - + + } trailing={ - { - setSelectedTheme(0); - }} - style={{marginRight: 5}} + setPlaySon(value)} /> } - onPress={() => {setSelectedTheme(0);}} - chevron={false} - > - Suivre le thème du système - - } - trailing={ - { - setSelectedTheme(1); - }} - style={{marginRight: 5}} + leading={ + } + colors={["#04ACDC", "#6FE3CD"]} /> } - onPress={() => {setSelectedTheme(1);}} - chevron={false} > - Mode clair + Jouer du son + + Un son est joué lors de l'ouverture de différentes pages + + + + + } trailing={ - { - setSelectedTheme(2); - }} - style={{marginRight: 5}} + setPlayHaptics(value)} + /> + } + leading={ + } + colors={["#FFD700", "#FF8C00"]} /> } - onPress={() => {setSelectedTheme(2);}} - chevron={false} > - Mode sombre + Jouer des vibrations + + Des vibrations ont lieu lors de la navigation, lorsqu'on coche des + devoirs... + - {hasUserChangedTheme && ( - + {hasUserChanged && ( + } - style={{backgroundColor: "#C53424"}} + leading={ + } + colors={["#000", "#000"]} + /> + } + style={{ backgroundColor: "#C53424" }} > - Redémarrer l'application - Cette option nécessite un redémarrage de l'application pour être appliquée. + Redémarrer l'application + + Pour que les modifications soient prises en compte, un + redémarrage de l'application est recommandé + From 22a7d2eb326c70836e0b4e460ffb057d8052f09b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 3 Jan 2025 17:32:48 +0100 Subject: [PATCH 0122/1144] =?UTF-8?q?fix(Settings):=20am=C3=A9lioration=20?= =?UTF-8?q?de=20la=20gestion=20des=20sons=20et=20vibrations=20avec=20des?= =?UTF-8?q?=20=C3=A9tats=20ind=C3=A9finis=20et=20ajout=20de=20spinners=20d?= =?UTF-8?q?e=20chargement=20+=20pas=20besoin=20de=20red=C3=A9marrage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/SettingsSoundHaptics.tsx | 93 +++++++++------------ 1 file changed, 40 insertions(+), 53 deletions(-) diff --git a/src/views/settings/SettingsSoundHaptics.tsx b/src/views/settings/SettingsSoundHaptics.tsx index 60d592e8b..eda34a86b 100644 --- a/src/views/settings/SettingsSoundHaptics.tsx +++ b/src/views/settings/SettingsSoundHaptics.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from "react"; import { ScrollView, Switch } from "react-native"; import type { Screen } from "@/router/helpers/types"; -import { RefreshCw, Vibrate, Volume2 } from "lucide-react-native"; +import { Vibrate, Volume2 } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { NativeList, @@ -11,42 +11,43 @@ import { } from "@/components/Global/NativeComponents"; import { NativeText } from "@/components/Global/NativeComponents"; import AsyncStorage from "@react-native-async-storage/async-storage"; -import Animated, { FadeInDown, FadeOutDown } from "react-native-reanimated"; import SoundHapticsContainerCard from "@/components/Settings/SoundHapticsContainerCard"; +import PapillonSpinner from "@/components/Global/PapillonSpinner"; const SettingsApparence: Screen<"SettingsSoundHaptics"> = () => { const insets = useSafeAreaInsets(); - const [defaultSon, setDefaultSon] = useState(true); - const [playSon, setPlaySon] = useState(true); - const [defaultHaptics, setDefaultHaptics] = useState(true); - const [playHaptics, setPlayHaptics] = useState(true); - const [hasUserChanged, setHasUserChanged] = useState(false); + const [playSon, setPlaySon] = useState(undefined); + const [playHaptics, setPlayHaptics] = useState( + undefined + ); useEffect(() => { - AsyncStorage.getItem("son").then((value) => { - if (value) { - setDefaultSon(Boolean(value)); - setPlaySon(Boolean(value)); - } - }); + if (playHaptics === undefined) { + AsyncStorage.getItem("son").then((value) => { + if (value) { + setPlaySon(value === "true"); + } + }); + } - AsyncStorage.getItem("haptics").then((value) => { - if (value) { - setDefaultHaptics(Boolean(value)); - setPlayHaptics(Boolean(value)); - } - }); + if (playHaptics === undefined) { + AsyncStorage.getItem("haptics").then((value) => { + if (value) { + setPlayHaptics(value === "true"); + } + }); + } }, []); useEffect(() => { - setHasUserChanged(playSon !== defaultSon); - AsyncStorage.setItem("son", playSon.toString()); + if (playSon === undefined) return; + AsyncStorage.setItem("son", String(playSon)); }, [playSon]); useEffect(() => { - setHasUserChanged(playHaptics !== defaultHaptics); - AsyncStorage.setItem("haptics", playHaptics.toString()); + if (playHaptics === undefined) return; + AsyncStorage.setItem("haptics", String(playHaptics)); }, [playHaptics]); return ( @@ -63,10 +64,14 @@ const SettingsApparence: Screen<"SettingsSoundHaptics"> = () => { setPlaySon(value)} - /> + playSon !== undefined ? ( + setPlaySon(value)} + /> + ) : ( + + ) } leading={ = () => { setPlayHaptics(value)} - /> + playHaptics !== undefined ? ( + setPlayHaptics(value)} + /> + ) : ( + + ) } leading={ = () => { - - {hasUserChanged && ( - - - } - colors={["#000", "#000"]} - /> - } - style={{ backgroundColor: "#C53424" }} - > - Redémarrer l'application - - Pour que les modifications soient prises en compte, un - redémarrage de l'application est recommandé - - - - - )} ); }; From feca5104ee6c59dd363e0f7a41c3729fb54ee9fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 3 Jan 2025 17:36:34 +0100 Subject: [PATCH 0123/1144] =?UTF-8?q?fix(Settings):=20d=C3=A9finir=20les?= =?UTF-8?q?=20valeurs=20par=20d=C3=A9faut=20pour=20les=20sons=20et=20vibra?= =?UTF-8?q?tions=20si=20non=20sp=C3=A9cifi=C3=A9es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/SettingsSoundHaptics.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/views/settings/SettingsSoundHaptics.tsx b/src/views/settings/SettingsSoundHaptics.tsx index eda34a86b..084475b0d 100644 --- a/src/views/settings/SettingsSoundHaptics.tsx +++ b/src/views/settings/SettingsSoundHaptics.tsx @@ -27,6 +27,8 @@ const SettingsApparence: Screen<"SettingsSoundHaptics"> = () => { AsyncStorage.getItem("son").then((value) => { if (value) { setPlaySon(value === "true"); + } else { + setPlaySon(true); } }); } @@ -35,6 +37,8 @@ const SettingsApparence: Screen<"SettingsSoundHaptics"> = () => { AsyncStorage.getItem("haptics").then((value) => { if (value) { setPlayHaptics(value === "true"); + } else { + setPlayHaptics(true); } }); } From 72361739299afabd2b6c38cd5673f05556ee88ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 3 Jan 2025 17:38:21 +0100 Subject: [PATCH 0124/1144] fix(Settings): correction du nom de la variable pour la gestion des sons --- src/views/settings/SettingsSoundHaptics.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/settings/SettingsSoundHaptics.tsx b/src/views/settings/SettingsSoundHaptics.tsx index 084475b0d..d97b84b3a 100644 --- a/src/views/settings/SettingsSoundHaptics.tsx +++ b/src/views/settings/SettingsSoundHaptics.tsx @@ -23,7 +23,7 @@ const SettingsApparence: Screen<"SettingsSoundHaptics"> = () => { ); useEffect(() => { - if (playHaptics === undefined) { + if (playSon === undefined) { AsyncStorage.getItem("son").then((value) => { if (value) { setPlaySon(value === "true"); From 219387469f0199f3bc8cf3f13a65c5db3b0d0255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 3 Jan 2025 17:51:45 +0100 Subject: [PATCH 0125/1144] =?UTF-8?q?suppression=20du=20son=20non=20utilis?= =?UTF-8?q?=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../skolengo/SkolengoInstanceSelector.tsx | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/src/views/login/skolengo/SkolengoInstanceSelector.tsx b/src/views/login/skolengo/SkolengoInstanceSelector.tsx index c3d94611a..0c561689c 100644 --- a/src/views/login/skolengo/SkolengoInstanceSelector.tsx +++ b/src/views/login/skolengo/SkolengoInstanceSelector.tsx @@ -38,7 +38,6 @@ const SkolengoInstanceSelector: Screen<"SkolengoInstanceSelector"> = ({ const [keyboardOpen, setKeyboardOpen] = useState(false); const [keyboardHeight, setKeyboardHeight] = useState(0); - const [sound, setSound] = useState(null); const keyboardDidShow = (event: KeyboardEvent) => { setKeyboardOpen(true); @@ -60,25 +59,6 @@ const SkolengoInstanceSelector: Screen<"SkolengoInstanceSelector"> = ({ }; }, []); - useEffect(() => { - const loadSound = async () => { - const { sound } = await Audio.Sound.createAsync( - require("@/../assets/sound/3.wav") - ); - setSound(sound); - }; - - loadSound(); - - return () => { - if (sound) { - sound.unloadAsync(); - } - }; - }, []); - - const playSound = () => sound?.replayAsync(); - useEffect(() => { if (params && params.pos && params.pos !== null) { void async function () { From 3133e9ac394f06e7990414cb3497668365223d00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 3 Jan 2025 18:05:28 +0100 Subject: [PATCH 0126/1144] =?UTF-8?q?suppression=20du=20son=20non=20utilis?= =?UTF-8?q?=C3=A9=20sur=20`FirstInstallation`=20et=20suppression=20de=20l'?= =?UTF-8?q?import=20non=20utilis=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../skolengo/SkolengoInstanceSelector.tsx | 1 - src/views/welcome/FirstInstallation.tsx | 26 ------------------- 2 files changed, 27 deletions(-) diff --git a/src/views/login/skolengo/SkolengoInstanceSelector.tsx b/src/views/login/skolengo/SkolengoInstanceSelector.tsx index 0c561689c..1857ccb1f 100644 --- a/src/views/login/skolengo/SkolengoInstanceSelector.tsx +++ b/src/views/login/skolengo/SkolengoInstanceSelector.tsx @@ -11,7 +11,6 @@ import { useTheme } from "@react-navigation/native"; import { Search, X, GraduationCap, } from "lucide-react-native"; import { useAlert } from "@/providers/AlertProvider"; -import { Audio } from "expo-av"; import type { School } from "scolengo-api/types/models/School"; import { Skolengo } from "scolengo-api"; import { useDebounce } from "@/hooks/debounce"; diff --git a/src/views/welcome/FirstInstallation.tsx b/src/views/welcome/FirstInstallation.tsx index e73ff095b..f7fa61556 100644 --- a/src/views/welcome/FirstInstallation.tsx +++ b/src/views/welcome/FirstInstallation.tsx @@ -11,7 +11,6 @@ import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { useTheme } from "@react-navigation/native"; import * as WebBrowser from "expo-web-browser"; -import { Audio } from "expo-av"; import * as SplashScreen from "expo-splash-screen"; const PRIVACY_POLICY_URL = "https://docs.papillon.bzh/legal/privacy"; @@ -20,7 +19,6 @@ const TERMS_OF_SERVICE_URL = "https://docs.papillon.bzh/legal/terms"; const FirstInstallation: Screen<"FirstInstallation"> = ({ navigation }) => { const theme = useTheme(); const { colors } = theme; - const [sound, setSound] = useState(null); const openUrl = async (url: string) => { await WebBrowser.openBrowserAsync(url, { @@ -29,30 +27,6 @@ const FirstInstallation: Screen<"FirstInstallation"> = ({ navigation }) => { }); }; - React.useEffect(() => { - const loadSound = async () => { - const { sound } = await Audio.Sound.createAsync( - require("@/../assets/sound/5.wav") - ); - setSound(sound); - }; - - loadSound(); - SplashScreen.hideAsync(); - - return () => { - if (sound) { - sound.unloadAsync(); - } - }; - }, []); - - const playSound = async () => { - if (sound) { - await sound.replayAsync(); - } - }; - return ( From f21828cc251ea9569006235e1203c0164df6010c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 3 Jan 2025 18:12:38 +0100 Subject: [PATCH 0127/1144] =?UTF-8?q?feat(SoundAndHaptics):=20ajout=20d'un?= =?UTF-8?q?=20hook=20pour=20g=C3=A9rer=20les=20sons=20et=20les=20vibration?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/SoundAndHaptics.ts | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/hooks/SoundAndHaptics.ts diff --git a/src/hooks/SoundAndHaptics.ts b/src/hooks/SoundAndHaptics.ts new file mode 100644 index 000000000..dafe74866 --- /dev/null +++ b/src/hooks/SoundAndHaptics.ts @@ -0,0 +1,36 @@ +import AsyncStorage from "@react-native-async-storage/async-storage"; +import { useState, useEffect } from "react"; + +export default function useSoundAndHaptics () { + const [enableSon, setEnableSon] = useState(undefined); + const [enableHaptics, setEnableHaptics] = useState( + undefined + ); + + useEffect(() => { + if (enableSon === undefined) { + AsyncStorage.getItem("son").then((value) => { + if (value) { + setEnableSon(value === "true"); + } else { + setEnableSon(true); + } + }); + } + + if (enableHaptics === undefined) { + AsyncStorage.getItem("haptics").then((value) => { + if (value) { + setEnableHaptics(value === "true"); + } else { + setEnableHaptics(true); + } + }); + } + }, []); + + return { + enableSon, + enableHaptics, + }; +} \ No newline at end of file From b03c8cf12a85b7cc36cc58893f6c7dc1c65787d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 3 Jan 2025 18:12:57 +0100 Subject: [PATCH 0128/1144] =?UTF-8?q?feat(SoundAndHaptics):=20int=C3=A9gre?= =?UTF-8?q?r=20la=20gestion=20des=20sons=20et=20vibrations=20dans=20plusie?= =?UTF-8?q?urs=20composants?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/FirstInstallation/ButtonCta.tsx | 4 +++- .../FirstInstallation/DuoListPressable.tsx | 7 +++++-- src/components/Global/PapillonCheckbox.tsx | 6 ++++-- .../Home/AccountSwitcherContextMenu.tsx | 6 ++++-- src/router/navigator/atoms/MenuItem.tsx | 5 +++-- src/router/navigator/atoms/TabItem.tsx | 4 +++- .../account/Grades/Graph/GradesAverage.tsx | 4 +++- src/views/account/Home/Home.tsx | 4 +++- src/views/account/Homeworks/Homeworks.tsx | 4 +++- .../Lessons/Atoms/LessonsDatePicker.tsx | 4 +++- src/views/login/ServiceSelector.tsx | 11 +++++++---- .../pronote/PronoteAuthenticationSelector.tsx | 5 ++++- src/views/login/pronote/PronoteQRCode.tsx | 7 +++++-- src/views/login/pronote/PronoteWebview.tsx | 7 +++++-- .../SkolengoAuthenticationSelector.tsx | 5 ++++- src/views/login/skolengo/SkolengoWebview.tsx | 4 +++- .../settings/ExternalAccount/QrcodeScanner.tsx | 5 +++-- src/views/welcome/AccountCreated.tsx | 18 +++++++++++------- src/views/welcome/ColorSelector.tsx | 8 +++++--- 19 files changed, 81 insertions(+), 37 deletions(-) diff --git a/src/components/FirstInstallation/ButtonCta.tsx b/src/components/FirstInstallation/ButtonCta.tsx index 088e8379f..d2e463f81 100644 --- a/src/components/FirstInstallation/ButtonCta.tsx +++ b/src/components/FirstInstallation/ButtonCta.tsx @@ -3,6 +3,7 @@ import { Text, Pressable, StyleSheet, type StyleProp, type ViewStyle } from "rea import Reanimated, { Easing, useSharedValue, withTiming } from "react-native-reanimated"; import { useTheme } from "@react-navigation/native"; import * as Haptics from "expo-haptics"; +import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; const ButtonCta: React.FC<{ value: string @@ -26,6 +27,7 @@ const ButtonCta: React.FC<{ const [pressed, setPressed] = useState(false); const scale = useSharedValue(1); const opacity = useSharedValue(1); + const { enableHaptics } = useSoundAndHaptics(); if (!backgroundColor) { backgroundColor = primary ? colors.primary : "transparent"; @@ -41,7 +43,7 @@ const ButtonCta: React.FC<{ scale.value = withTiming(1, { duration: 0, easing: Easing.linear }); scale.value = withTiming(0.95, { duration: 50, easing: Easing.linear }); opacity.value = withTiming(0.7, { duration: 10, easing: Easing.linear }); - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy); + if (enableHaptics) Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy); } else { scale.value = withTiming(1, { duration: 100, easing: Easing.linear }); diff --git a/src/components/FirstInstallation/DuoListPressable.tsx b/src/components/FirstInstallation/DuoListPressable.tsx index e1b95fa19..1096bc740 100644 --- a/src/components/FirstInstallation/DuoListPressable.tsx +++ b/src/components/FirstInstallation/DuoListPressable.tsx @@ -6,6 +6,7 @@ import * as Haptics from "expo-haptics"; import { Audio } from "expo-av"; import Reanimated, { Easing, useSharedValue, withTiming } from "react-native-reanimated"; +import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; const DuoListPressable: React.FC<{ children?: JSX.Element, @@ -31,6 +32,8 @@ const DuoListPressable: React.FC<{ const scale = useSharedValue(1); const opacity = useSharedValue(1); + const { enableSon, enableHaptics } = useSoundAndHaptics(); + const playSound = useCallback(async () => { const { sound } = await Audio.Sound.createAsync( require("@/../assets/sound/click_003.wav") @@ -44,8 +47,8 @@ const DuoListPressable: React.FC<{ scale.value = withTiming(1, { duration: 0, easing: Easing.linear }); scale.value = withTiming(0.95, { duration: 50, easing: Easing.linear }); opacity.value = withTiming(0.7, { duration: 10, easing: Easing.linear }); - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); - playSound(); + if (enableHaptics) Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); + if (enableSon) playSound(); } else { scale.value = withTiming(1, { duration: 100, easing: Easing.linear }); diff --git a/src/components/Global/PapillonCheckbox.tsx b/src/components/Global/PapillonCheckbox.tsx index 3e9fe1ac6..b8a1fad78 100644 --- a/src/components/Global/PapillonCheckbox.tsx +++ b/src/components/Global/PapillonCheckbox.tsx @@ -9,6 +9,7 @@ import { Check } from "lucide-react-native"; import * as Haptics from "expo-haptics"; import PapillonSpinner from "./PapillonSpinner"; import { animPapillon } from "@/utils/ui/animations"; +import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; interface CheckboxProps { checked?: boolean @@ -29,6 +30,7 @@ const PapillonCheckbox: React.FC = ({ }) => { const theme = useTheme(); const firstRender = useRef(true); + const { enableHaptics } = useSoundAndHaptics(); useEffect(() => { if (firstRender.current) { @@ -41,13 +43,13 @@ const PapillonCheckbox: React.FC = ({ const pressAction = () => { onPress(); - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); + if (enableHaptics) Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); setHasPressed(true); }; // on checked change useEffect(() => { - if (checked && hasPressed && loaded) { + if (checked && hasPressed && loaded && enableHaptics) { Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); } }, [checked, hasPressed]); diff --git a/src/components/Home/AccountSwitcherContextMenu.tsx b/src/components/Home/AccountSwitcherContextMenu.tsx index b0f4d4e5b..6b97bfd22 100644 --- a/src/components/Home/AccountSwitcherContextMenu.tsx +++ b/src/components/Home/AccountSwitcherContextMenu.tsx @@ -27,6 +27,7 @@ import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; import { useTheme } from "@react-navigation/native"; import { BlurView } from "expo-blur"; import { Check, CirclePlus, Cog, Plus } from "lucide-react-native"; +import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; const ContextMenu: React.FC<{ style?: any; @@ -38,6 +39,7 @@ const ContextMenu: React.FC<{ const theme = useTheme(); const { colors } = theme; const navigation = useNavigation(); + const { enableHaptics } = useSoundAndHaptics(); const [opened, setOpened] = useState(false); // État pour gérer l'ouverture du menu contextuel @@ -74,7 +76,7 @@ const ContextMenu: React.FC<{ { setOpened(!opened); - openEffects(); + if (enableHaptics) openEffects(); }} // @ts-expect-error pointerEvents="auto" @@ -90,7 +92,7 @@ const ContextMenu: React.FC<{ { setOpened(!opened); - openEffects(); + if (enableHaptics) openEffects(); }} useForeground={true} style={{ diff --git a/src/router/navigator/atoms/MenuItem.tsx b/src/router/navigator/atoms/MenuItem.tsx index 2a9b548ce..9c14c5724 100644 --- a/src/router/navigator/atoms/MenuItem.tsx +++ b/src/router/navigator/atoms/MenuItem.tsx @@ -9,6 +9,7 @@ import { Pressable } from "react-native-gesture-handler"; import * as Haptics from "expo-haptics"; import Reanimated, { FadeIn, FadeOut, LinearTransition, ZoomIn } from "react-native-reanimated"; import { anim2Papillon } from "@/utils/ui/animations"; +import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; const MenuItem: React.FC<{ route: any; @@ -17,6 +18,7 @@ const MenuItem: React.FC<{ isFocused: boolean; }> = ({ route, descriptor, navigation, isFocused }) => { const theme = useTheme(); + const { enableHaptics } = useSoundAndHaptics(); const { options } = descriptor; const label = options.tabBarLabel !== undefined ? options.tabBarLabel : options.title !== undefined ? options.title : route.name; @@ -33,8 +35,7 @@ const MenuItem: React.FC<{ } lottieRef.current?.play(); - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); - + if (enableHaptics) Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); }; const onLongPress = () => { diff --git a/src/router/navigator/atoms/TabItem.tsx b/src/router/navigator/atoms/TabItem.tsx index b6dc67545..db1868ec2 100644 --- a/src/router/navigator/atoms/TabItem.tsx +++ b/src/router/navigator/atoms/TabItem.tsx @@ -9,6 +9,7 @@ import { Pressable } from "react-native-gesture-handler"; import * as Haptics from "expo-haptics"; import Reanimated, { FadeIn, FadeOut, LinearTransition, ZoomIn } from "react-native-reanimated"; import { anim2Papillon } from "@/utils/ui/animations"; +import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; const TabItem: React.FC<{ route: any; @@ -18,6 +19,7 @@ const TabItem: React.FC<{ settings: any; }> = ({ route, descriptor, navigation, isFocused, settings }) => { const theme = useTheme(); + const { enableHaptics } = useSoundAndHaptics(); const { options } = descriptor; const label = options.tabBarLabel !== undefined ? options.tabBarLabel : options.title !== undefined ? options.title : route.name; @@ -37,7 +39,7 @@ const TabItem: React.FC<{ lottieRef.current.play(); } - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); + if (enableHaptics) Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); }; const onLongPress = () => { diff --git a/src/views/account/Grades/Graph/GradesAverage.tsx b/src/views/account/Grades/Graph/GradesAverage.tsx index c3bdde0dd..ce48588bc 100644 --- a/src/views/account/Grades/Graph/GradesAverage.tsx +++ b/src/views/account/Grades/Graph/GradesAverage.tsx @@ -33,6 +33,7 @@ import AnimatedNumber from "@/components/Global/AnimatedNumber"; import type { Grade } from "@/services/shared/Grade"; import { AlertTriangle, Calculator, Check, ExternalLink, PieChart, Spline, SquareRadical, TrendingUp } from "lucide-react-native"; import { useAlert } from "@/providers/AlertProvider"; +import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; interface GradesAverageGraphProps { grades: Grade[]; @@ -48,6 +49,7 @@ const GradesAverageGraph: React.FC = ({ const theme = useTheme(); const account = useCurrentAccount((store) => store.account!); const { showAlert } = useAlert(); + const { enableHaptics } = useSoundAndHaptics(); const [gradesHistory, setGradesHistory] = useState([]); const [hLength, setHLength] = useState(0); @@ -66,7 +68,7 @@ const GradesAverageGraph: React.FC = ({ const gradesHistoryRef = useRef(gradesHistory); useEffect(() => { - if (currentAvg !== originalCurrentAvg) { + if (currentAvg !== originalCurrentAvg && enableHaptics) { Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); } }, [currentAvg]); diff --git a/src/views/account/Home/Home.tsx b/src/views/account/Home/Home.tsx index 699d56195..f196817e0 100644 --- a/src/views/account/Home/Home.tsx +++ b/src/views/account/Home/Home.tsx @@ -61,12 +61,14 @@ import * as Haptics from "expo-haptics"; import ModalContent from "@/views/account/Home/ModalContent"; import {AnimatedScrollView} from "react-native-reanimated/lib/typescript/reanimated2/component/ScrollView"; import useScreenDimensions from "@/hooks/useScreenDimensions"; +import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; const Home: Screen<"HomeScreen"> = ({ navigation }) => { const { colors } = useTheme(); const insets = useSafeAreaInsets(); const corners = useMemo(() => getCorners(), []); const focused = useIsFocused(); + const { enableHaptics } = useSoundAndHaptics(); const {isTablet} = useScreenDimensions(); @@ -251,7 +253,7 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { }} onScroll={(e) => { if (e.nativeEvent.contentOffset.y > 125 && canHaptics) { - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); + if (enableHaptics) Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); setCanHaptics(false); } else if (e.nativeEvent.contentOffset.y < 125 && !canHaptics) { setCanHaptics(true); diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 0888bebd5..af0f85d96 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -44,6 +44,7 @@ import {NativeSyntheticEvent} from "react-native/Libraries/Types/CoreEventTypes" import {NativeScrollEvent, ScrollViewProps} from "react-native/Libraries/Components/ScrollView/ScrollView"; import {SearchBar} from "react-native-screens"; import AsyncStorage from "@react-native-async-storage/async-storage"; +import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; type HomeworksPageProps = { index: number; @@ -71,6 +72,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { 320 ) : 0); const insets = useSafeAreaInsets(); + const { enableHaptics } = useSoundAndHaptics(); const outsideNav = route.params?.outsideNav; @@ -415,7 +417,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { onPress={() => setShowPickerButtons(!showPickerButtons)} onLongPress={() => { setHideDone(!hideDone); - Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); + if (enableHaptics) Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); }} delayLongPress={200} > diff --git a/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx b/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx index 74f0a4bd2..653361513 100644 --- a/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx +++ b/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx @@ -17,6 +17,7 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import {Theme} from "@react-navigation/native/src/types"; import {NativeScrollEvent, ScrollViewProps} from "react-native/Libraries/Components/ScrollView/ScrollView"; import {NativeSyntheticEvent} from "react-native/Libraries/Types/CoreEventTypes"; +import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; const { width: SCREEN_WIDTH } = Dimensions.get("window"); const ITEM_WIDTH = 104; @@ -109,6 +110,7 @@ const HorizontalDatePicker = ({ onDateSelect, onCurrentDatePress, initialDate = const flatListRef = useRef(null); const scrollX = useSharedValue(0); const lastItemIndex = useSharedValue(0); + const { enableHaptics } = useSoundAndHaptics(); const { colors } = useTheme(); const insets = useSafeAreaInsets(); @@ -173,7 +175,7 @@ const HorizontalDatePicker = ({ onDateSelect, onCurrentDatePress, initialDate = if (currentItemIndex !== lastItemIndex.value) { lastItemIndex.value = currentItemIndex; runOnJS(setIsProgrammaticScroll)(false); - if (!isProgrammaticScroll) { + if (!isProgrammaticScroll && enableHaptics) { runOnJS(triggerHaptic)(); } } diff --git a/src/views/login/ServiceSelector.tsx b/src/views/login/ServiceSelector.tsx index 9fa1f9854..8bcc93036 100644 --- a/src/views/login/ServiceSelector.tsx +++ b/src/views/login/ServiceSelector.tsx @@ -18,6 +18,7 @@ import Constants from "expo-constants"; import { LinearGradient } from "expo-linear-gradient"; import { sr } from "date-fns/locale"; import { sub } from "date-fns"; +import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { const theme = useTheme(); @@ -31,6 +32,8 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { const [v6Data, setV6Data] = useState(null); + const { enableSon } = useSoundAndHaptics(); + useEffect(() => { setTimeout(async () => { const v6Data = await GetV6Data(); @@ -54,7 +57,7 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { image: require("../../../assets/images/service_pronote.png"), login: () => { navigation.navigate("PronoteAuthenticationSelector"); - playSound(); + if (enableSon) playSound(); }, }, { @@ -63,7 +66,7 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { image: require("../../../assets/images/service_ed.png"), login: () => { navigation.navigate("EcoleDirecteCredentials"); - playSound(); + if (enableSon) playSound(); } }, { @@ -72,7 +75,7 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { image: require("../../../assets/images/service_skolengo.png"), login: () => { navigation.navigate("SkolengoAuthenticationSelector"); - playSound(); + if (enableSon) playSound(); } }, { @@ -83,7 +86,7 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { icon: , login: () => { navigation.navigate("IdentityProviderSelector"); - playSound(); + if (enableSon) playSound(); } }, ]; diff --git a/src/views/login/pronote/PronoteAuthenticationSelector.tsx b/src/views/login/pronote/PronoteAuthenticationSelector.tsx index a787bc68e..9de60af44 100644 --- a/src/views/login/pronote/PronoteAuthenticationSelector.tsx +++ b/src/views/login/pronote/PronoteAuthenticationSelector.tsx @@ -13,6 +13,7 @@ import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import { Audio } from "expo-av"; import { NativeText } from "@/components/Global/NativeComponents"; import { LinearGradient } from "expo-linear-gradient"; +import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; const PronoteAuthenticationSelector: Screen<"PronoteAuthenticationSelector"> = ({ navigation }) => { const theme = useTheme(); @@ -21,6 +22,8 @@ const PronoteAuthenticationSelector: Screen<"PronoteAuthenticationSelector"> = ( const [method, setMethod] = useState(null); const [sound, setSound] = useState(null); + const { enableSon } = useSoundAndHaptics(); + const loadSound = async () => { const { sound } = await Audio.Sound.createAsync( require("@/../assets/sound/2.wav") @@ -55,7 +58,7 @@ const PronoteAuthenticationSelector: Screen<"PronoteAuthenticationSelector"> = ( break; } - playSound(); + if (enableSon) playSound(); }; return ( diff --git a/src/views/login/pronote/PronoteQRCode.tsx b/src/views/login/pronote/PronoteQRCode.tsx index add8fbaee..cc59efbca 100644 --- a/src/views/login/pronote/PronoteQRCode.tsx +++ b/src/views/login/pronote/PronoteQRCode.tsx @@ -19,6 +19,7 @@ import { Account, AccountService } from "@/stores/account/types"; import { Audio } from "expo-av"; import defaultPersonalization from "@/services/pronote/default-personalization"; import extract_pronote_name from "@/utils/format/extract_pronote_name"; +import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; const makeUUID = (): string => { let dt = new Date().getTime(); @@ -55,6 +56,8 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { const codeInput = React.createRef(); const [QRData, setQRData] = useState(null); + const { enableSon, enableHaptics } = useSoundAndHaptics(); + async function loginQR () { setScanned(false); setLoadingModalVisible(true); @@ -130,7 +133,7 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { queueMicrotask(() => { // Reset the navigation stack to the "Home" screen. // Prevents the user from going back to the login screen. - playSound(); + if (enableSon) playSound(); navigation.reset({ index: 0, routes: [{ name: "AccountCreated" }], @@ -184,7 +187,7 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { data: string; }) => { setScanned(true); - Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); + if (enableHaptics) Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); setQRData(data); setPinModalVisible(true); }; diff --git a/src/views/login/pronote/PronoteWebview.tsx b/src/views/login/pronote/PronoteWebview.tsx index 41408d9e9..b68cc8744 100644 --- a/src/views/login/pronote/PronoteWebview.tsx +++ b/src/views/login/pronote/PronoteWebview.tsx @@ -34,6 +34,7 @@ import extract_pronote_name from "@/utils/format/extract_pronote_name"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { animPapillon } from "@/utils/ui/animations"; import { useAlert } from "@/providers/AlertProvider"; +import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; const PronoteWebview: Screen<"PronoteWebview"> = ({ route, navigation }) => { const theme = useTheme(); @@ -52,6 +53,8 @@ const PronoteWebview: Screen<"PronoteWebview"> = ({ route, navigation }) => { const [loginStep, setLoginStep] = useState("Préparation de la connexion"); + const { enableSon } = useSoundAndHaptics(); + const instanceURL = route.params.instanceURL.toLowerCase(); const infoMobileURL = @@ -105,7 +108,7 @@ const PronoteWebview: Screen<"PronoteWebview"> = ({ route, navigation }) => { }; useEffect(() => { - playSound(); + if (enableSon) playSound(); }, []); const INJECT_PRONOTE_JSON = ` @@ -357,7 +360,7 @@ const PronoteWebview: Screen<"PronoteWebview"> = ({ route, navigation }) => { queueMicrotask(() => { // Reset the navigation stack to the "Home" screen. // Prevents the user from going back to the login screen. - playSound(); + if (enableSon) playSound(); navigation.reset({ index: 0, routes: [{ name: "AccountCreated" }], diff --git a/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx b/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx index 01a513bdb..a3876ad9b 100644 --- a/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx +++ b/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx @@ -12,6 +12,7 @@ import Reanimated, { LinearTransition, FlipInXDown } from "react-native-reanimat import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import { Audio } from "expo-av"; import { NativeText } from "@/components/Global/NativeComponents"; +import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; const SkolengoAuthenticationSelector: Screen<"SkolengoAuthenticationSelector"> = ({ navigation }) => { const theme = useTheme(); @@ -20,6 +21,8 @@ const SkolengoAuthenticationSelector: Screen<"SkolengoAuthenticationSelector"> = const [method, setMethod] = useState(null); const [sound, setSound] = useState(null); + const { enableSon } = useSoundAndHaptics(); + const loadSound = async () => { const { sound } = await Audio.Sound.createAsync( require("@/../assets/sound/2.wav") @@ -48,7 +51,7 @@ const SkolengoAuthenticationSelector: Screen<"SkolengoAuthenticationSelector"> = break; } - playSound(); + if (enableSon) playSound(); }; return ( diff --git a/src/views/login/skolengo/SkolengoWebview.tsx b/src/views/login/skolengo/SkolengoWebview.tsx index bad9148a9..44fe10cff 100644 --- a/src/views/login/skolengo/SkolengoWebview.tsx +++ b/src/views/login/skolengo/SkolengoWebview.tsx @@ -28,6 +28,7 @@ import { authTokenToSkolengoTokenSet } from "@/services/skolengo/skolengo-types" import { getSkolengoAccount } from "@/services/skolengo/skolengo-account"; import { log } from "@/utils/logger/logger"; import { wait } from "@/services/skolengo/data/utils"; +import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; // TODO : When the app is not started with Expo Go (so with a prebuild or a release build), use the expo auth-session module completely with the deeplink and without the webview. @@ -42,6 +43,7 @@ const SkolengoWebview: Screen<"SkolengoWebview"> = ({ route, navigation }) => { const [pageUrl, setPageUrl] = useState(null); const [discovery, setDiscovery] = useState(null); + const { enableSon } = useSoundAndHaptics(); const createStoredAccount = useAccounts((store) => store.create); const switchTo = useCurrentAccount((store) => store.switchTo); @@ -93,7 +95,7 @@ const SkolengoWebview: Screen<"SkolengoWebview"> = ({ route, navigation }) => { }; useEffect(() => { - playSound(); + if (enableSon) playSound(); }, []); return ( diff --git a/src/views/settings/ExternalAccount/QrcodeScanner.tsx b/src/views/settings/ExternalAccount/QrcodeScanner.tsx index 8786479da..bb723b31e 100644 --- a/src/views/settings/ExternalAccount/QrcodeScanner.tsx +++ b/src/views/settings/ExternalAccount/QrcodeScanner.tsx @@ -19,6 +19,7 @@ import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { BarCodeScanner } from "expo-barcode-scanner"; import MaskedView from "@react-native-masked-view/masked-view"; import * as Haptics from "expo-haptics"; +import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; type Props = { navigation: any; @@ -33,7 +34,7 @@ const QrcodeScanner: Screen<"QrcodeScanner"> = ({ navigation, route }) => { const accountID = route.params?.accountID; const [hasPermission, setHasPermission] = React.useState(null); const [scanned, setScanned] = React.useState(false); - + const { enableHaptics } = useSoundAndHaptics(); useEffect(() => { const getBarCodeScannerPermissions = async () => { @@ -51,7 +52,7 @@ const QrcodeScanner: Screen<"QrcodeScanner"> = ({ navigation, route }) => { data: string; }) => { setScanned(true); - Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); + if (enableHaptics) Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); update(accountID, "data", { "qrcodedata": data, "qrcodetype": type }); navigation.navigate("PriceDetectionOnboarding", { accountID }); }; diff --git a/src/views/welcome/AccountCreated.tsx b/src/views/welcome/AccountCreated.tsx index 94589b834..927e2ce41 100644 --- a/src/views/welcome/AccountCreated.tsx +++ b/src/views/welcome/AccountCreated.tsx @@ -14,12 +14,14 @@ import * as Haptics from "expo-haptics"; import { useCurrentAccount } from "@/stores/account"; import { Audio } from "expo-av"; +import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; const AccountCreated: Screen<"AccountCreated"> = ({ navigation }) => { const [sound, setSound] = useState(null); const [sound2, setSound2] = useState(null); const account = useCurrentAccount((state) => state.account!); + const { enableSon, enableHaptics } = useSoundAndHaptics(); const loadSound = async () => { const { sound } = await Audio.Sound.createAsync( @@ -72,11 +74,13 @@ const AccountCreated: Screen<"AccountCreated"> = ({ navigation }) => { const unsubscribe = navigation.addListener("focus", () => { setShowAnimation(true); - // loop 20 times - for (let i = 0; i < 15; i++) { - setTimeout(() => { - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium); - }, i * 20); + if (enableHaptics) { + // loop 20 times + for (let i = 0; i < 15; i++) { + setTimeout(() => { + Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium); + }, i * 20); + } } }); @@ -130,14 +134,14 @@ const AccountCreated: Screen<"AccountCreated"> = ({ navigation }) => { primary onPress={() => { navigation.navigate("ColorSelector"); - playSound(); + if (enableSon) playSound(); }} /> { navigation.navigate("AccountStack", { onboard: true }); - playSound2(); + if (enableSon) playSound2(); }} /> diff --git a/src/views/welcome/ColorSelector.tsx b/src/views/welcome/ColorSelector.tsx index a8d7a50e1..18a8f7533 100644 --- a/src/views/welcome/ColorSelector.tsx +++ b/src/views/welcome/ColorSelector.tsx @@ -16,6 +16,7 @@ import { getIconName, setIconName } from "@candlefinance/app-icon"; import colorsList from "@/utils/data/colors.json"; import { removeColor } from "../settings/SettingsIcons"; import { expoGoWrapper } from "@/utils/native/expoGoAlert"; +import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; type Color = typeof colorsList[number]; @@ -26,6 +27,7 @@ const ColorSelector: Screen<"ColorSelector"> = ({ route, navigation }) => { const account = useCurrentAccount(store => store.account); const mutateProperty = useCurrentAccount(store => store.mutateProperty); const settings = route.params?.settings || false; + const { enableSon, enableHaptics } = useSoundAndHaptics(); const [sound, setSound] = useState(null); const [sound2, setSound2] = useState(null); @@ -81,8 +83,8 @@ const ColorSelector: Screen<"ColorSelector"> = ({ route, navigation }) => { const selectColor = (color: Color) => { mutateProperty("personalization", { color }); - Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); - playSound2(); + if (enableHaptics) Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); + if (enableSon) playSound2(); expoGoWrapper(() => { getIconName().then((currentIcon) => { @@ -215,7 +217,7 @@ const ColorSelector: Screen<"ColorSelector"> = ({ route, navigation }) => { primary value="Finaliser" onPress={async () => { - if (!settings) { + if (!settings && enableSon) { await playSound(); } navigation.navigate("AccountStack", {onboard: true}); From 65a6364aba9c198d794c565d2c17c6d1e74f95e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 3 Jan 2025 18:36:26 +0100 Subject: [PATCH 0129/1144] =?UTF-8?q?adaptation=20du=20hook=20pour=20une?= =?UTF-8?q?=20actualisation=20automatique=20des=20param=C3=A8tres=20!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FirstInstallation/ButtonCta.tsx | 2 +- .../FirstInstallation/DuoListPressable.tsx | 2 +- src/components/Global/PapillonCheckbox.tsx | 2 +- .../Home/AccountSwitcherContextMenu.tsx | 2 +- src/hooks/SoundAndHaptics.ts | 36 ---------- src/hooks/SoundAndHaptics.tsx | 45 ++++++++++++ src/router/index.tsx | 17 +++-- src/router/navigator/atoms/MenuItem.tsx | 2 +- src/router/navigator/atoms/TabItem.tsx | 2 +- .../account/Grades/Graph/GradesAverage.tsx | 2 +- src/views/account/Home/Home.tsx | 2 +- src/views/account/Homeworks/Homeworks.tsx | 2 +- .../Lessons/Atoms/LessonsDatePicker.tsx | 2 +- src/views/login/ServiceSelector.tsx | 2 +- .../pronote/PronoteAuthenticationSelector.tsx | 2 +- src/views/login/pronote/PronoteQRCode.tsx | 2 +- src/views/login/pronote/PronoteWebview.tsx | 2 +- .../SkolengoAuthenticationSelector.tsx | 2 +- src/views/login/skolengo/SkolengoWebview.tsx | 2 +- .../ExternalAccount/QrcodeScanner.tsx | 2 +- src/views/settings/SettingsSoundHaptics.tsx | 68 ++++--------------- src/views/welcome/AccountCreated.tsx | 2 +- src/views/welcome/ColorSelector.tsx | 2 +- 23 files changed, 86 insertions(+), 118 deletions(-) delete mode 100644 src/hooks/SoundAndHaptics.ts create mode 100644 src/hooks/SoundAndHaptics.tsx diff --git a/src/components/FirstInstallation/ButtonCta.tsx b/src/components/FirstInstallation/ButtonCta.tsx index d2e463f81..965a4efe8 100644 --- a/src/components/FirstInstallation/ButtonCta.tsx +++ b/src/components/FirstInstallation/ButtonCta.tsx @@ -3,7 +3,7 @@ import { Text, Pressable, StyleSheet, type StyleProp, type ViewStyle } from "rea import Reanimated, { Easing, useSharedValue, withTiming } from "react-native-reanimated"; import { useTheme } from "@react-navigation/native"; import * as Haptics from "expo-haptics"; -import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; +import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; const ButtonCta: React.FC<{ value: string diff --git a/src/components/FirstInstallation/DuoListPressable.tsx b/src/components/FirstInstallation/DuoListPressable.tsx index 1096bc740..116018819 100644 --- a/src/components/FirstInstallation/DuoListPressable.tsx +++ b/src/components/FirstInstallation/DuoListPressable.tsx @@ -6,7 +6,7 @@ import * as Haptics from "expo-haptics"; import { Audio } from "expo-av"; import Reanimated, { Easing, useSharedValue, withTiming } from "react-native-reanimated"; -import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; +import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; const DuoListPressable: React.FC<{ children?: JSX.Element, diff --git a/src/components/Global/PapillonCheckbox.tsx b/src/components/Global/PapillonCheckbox.tsx index b8a1fad78..a0f3f0d43 100644 --- a/src/components/Global/PapillonCheckbox.tsx +++ b/src/components/Global/PapillonCheckbox.tsx @@ -9,7 +9,7 @@ import { Check } from "lucide-react-native"; import * as Haptics from "expo-haptics"; import PapillonSpinner from "./PapillonSpinner"; import { animPapillon } from "@/utils/ui/animations"; -import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; +import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; interface CheckboxProps { checked?: boolean diff --git a/src/components/Home/AccountSwitcherContextMenu.tsx b/src/components/Home/AccountSwitcherContextMenu.tsx index 6b97bfd22..f07b01b8f 100644 --- a/src/components/Home/AccountSwitcherContextMenu.tsx +++ b/src/components/Home/AccountSwitcherContextMenu.tsx @@ -27,7 +27,7 @@ import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; import { useTheme } from "@react-navigation/native"; import { BlurView } from "expo-blur"; import { Check, CirclePlus, Cog, Plus } from "lucide-react-native"; -import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; +import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; const ContextMenu: React.FC<{ style?: any; diff --git a/src/hooks/SoundAndHaptics.ts b/src/hooks/SoundAndHaptics.ts deleted file mode 100644 index dafe74866..000000000 --- a/src/hooks/SoundAndHaptics.ts +++ /dev/null @@ -1,36 +0,0 @@ -import AsyncStorage from "@react-native-async-storage/async-storage"; -import { useState, useEffect } from "react"; - -export default function useSoundAndHaptics () { - const [enableSon, setEnableSon] = useState(undefined); - const [enableHaptics, setEnableHaptics] = useState( - undefined - ); - - useEffect(() => { - if (enableSon === undefined) { - AsyncStorage.getItem("son").then((value) => { - if (value) { - setEnableSon(value === "true"); - } else { - setEnableSon(true); - } - }); - } - - if (enableHaptics === undefined) { - AsyncStorage.getItem("haptics").then((value) => { - if (value) { - setEnableHaptics(value === "true"); - } else { - setEnableHaptics(true); - } - }); - } - }, []); - - return { - enableSon, - enableHaptics, - }; -} \ No newline at end of file diff --git a/src/hooks/SoundAndHaptics.tsx b/src/hooks/SoundAndHaptics.tsx new file mode 100644 index 000000000..4710bb3bb --- /dev/null +++ b/src/hooks/SoundAndHaptics.tsx @@ -0,0 +1,45 @@ +import React, { createContext, useContext, useState, useEffect } from "react"; +import AsyncStorage from "@react-native-async-storage/async-storage"; + +const SoundHapticsContext = createContext({ + enableSon: true, + enableHaptics: true, + setEnableSon: (value: boolean) => {}, + setEnableHaptics: (value: boolean) => {}, +}); + +export const SoundHapticsProvider = ({ + children, +}: { + children: React.ReactNode; +}) => { + const [enableSon, setEnableSon] = useState(true); + const [enableHaptics, setEnableHaptics] = useState(true); + + useEffect(() => { + AsyncStorage.getItem("son").then((value) => + setEnableSon(value === "true" || value === null) + ); + AsyncStorage.getItem("haptics").then((value) => + setEnableHaptics(value === "true" || value === null) + ); + }, []); + + useEffect(() => { + AsyncStorage.setItem("son", String(enableSon)); + }, [enableSon]); + + useEffect(() => { + AsyncStorage.setItem("haptics", String(enableHaptics)); + }, [enableHaptics]); + + return ( + + {children} + + ); +}; + +export const useSoundAndHaptics = () => useContext(SoundHapticsContext); diff --git a/src/router/index.tsx b/src/router/index.tsx index bc31b96e2..9757e327a 100644 --- a/src/router/index.tsx +++ b/src/router/index.tsx @@ -16,6 +16,7 @@ import { navigatorScreenOptions } from "./helpers/create-screen"; import {navigate} from "@/utils/logger/logger"; import { PapillonNavigation } from "./refs"; import AsyncStorage from "@react-native-async-storage/async-storage"; +import { SoundHapticsProvider } from "@/hooks/SoundAndHaptics"; export const Stack = createNativeStackNavigator(); @@ -117,14 +118,16 @@ const Router: React.FC = () => { navigate(str); }} > - - - {screens.map((screen) => ( + + + + {screens.map((screen) => ( // @ts-expect-error : type not compatible, but it works fine. - - ))} - - + + ))} + + + diff --git a/src/router/navigator/atoms/MenuItem.tsx b/src/router/navigator/atoms/MenuItem.tsx index 9c14c5724..dda4f38e7 100644 --- a/src/router/navigator/atoms/MenuItem.tsx +++ b/src/router/navigator/atoms/MenuItem.tsx @@ -9,7 +9,7 @@ import { Pressable } from "react-native-gesture-handler"; import * as Haptics from "expo-haptics"; import Reanimated, { FadeIn, FadeOut, LinearTransition, ZoomIn } from "react-native-reanimated"; import { anim2Papillon } from "@/utils/ui/animations"; -import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; +import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; const MenuItem: React.FC<{ route: any; diff --git a/src/router/navigator/atoms/TabItem.tsx b/src/router/navigator/atoms/TabItem.tsx index db1868ec2..0c51fe620 100644 --- a/src/router/navigator/atoms/TabItem.tsx +++ b/src/router/navigator/atoms/TabItem.tsx @@ -9,7 +9,7 @@ import { Pressable } from "react-native-gesture-handler"; import * as Haptics from "expo-haptics"; import Reanimated, { FadeIn, FadeOut, LinearTransition, ZoomIn } from "react-native-reanimated"; import { anim2Papillon } from "@/utils/ui/animations"; -import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; +import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; const TabItem: React.FC<{ route: any; diff --git a/src/views/account/Grades/Graph/GradesAverage.tsx b/src/views/account/Grades/Graph/GradesAverage.tsx index ce48588bc..f81db10ee 100644 --- a/src/views/account/Grades/Graph/GradesAverage.tsx +++ b/src/views/account/Grades/Graph/GradesAverage.tsx @@ -33,7 +33,7 @@ import AnimatedNumber from "@/components/Global/AnimatedNumber"; import type { Grade } from "@/services/shared/Grade"; import { AlertTriangle, Calculator, Check, ExternalLink, PieChart, Spline, SquareRadical, TrendingUp } from "lucide-react-native"; import { useAlert } from "@/providers/AlertProvider"; -import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; +import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; interface GradesAverageGraphProps { grades: Grade[]; diff --git a/src/views/account/Home/Home.tsx b/src/views/account/Home/Home.tsx index f196817e0..9fac6300c 100644 --- a/src/views/account/Home/Home.tsx +++ b/src/views/account/Home/Home.tsx @@ -61,7 +61,7 @@ import * as Haptics from "expo-haptics"; import ModalContent from "@/views/account/Home/ModalContent"; import {AnimatedScrollView} from "react-native-reanimated/lib/typescript/reanimated2/component/ScrollView"; import useScreenDimensions from "@/hooks/useScreenDimensions"; -import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; +import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; const Home: Screen<"HomeScreen"> = ({ navigation }) => { const { colors } = useTheme(); diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index af0f85d96..3e2095632 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -44,7 +44,7 @@ import {NativeSyntheticEvent} from "react-native/Libraries/Types/CoreEventTypes" import {NativeScrollEvent, ScrollViewProps} from "react-native/Libraries/Components/ScrollView/ScrollView"; import {SearchBar} from "react-native-screens"; import AsyncStorage from "@react-native-async-storage/async-storage"; -import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; +import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; type HomeworksPageProps = { index: number; diff --git a/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx b/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx index 653361513..0b4c3c064 100644 --- a/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx +++ b/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx @@ -17,7 +17,7 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import {Theme} from "@react-navigation/native/src/types"; import {NativeScrollEvent, ScrollViewProps} from "react-native/Libraries/Components/ScrollView/ScrollView"; import {NativeSyntheticEvent} from "react-native/Libraries/Types/CoreEventTypes"; -import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; +import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; const { width: SCREEN_WIDTH } = Dimensions.get("window"); const ITEM_WIDTH = 104; diff --git a/src/views/login/ServiceSelector.tsx b/src/views/login/ServiceSelector.tsx index 8bcc93036..800db0cc0 100644 --- a/src/views/login/ServiceSelector.tsx +++ b/src/views/login/ServiceSelector.tsx @@ -18,7 +18,7 @@ import Constants from "expo-constants"; import { LinearGradient } from "expo-linear-gradient"; import { sr } from "date-fns/locale"; import { sub } from "date-fns"; -import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; +import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { const theme = useTheme(); diff --git a/src/views/login/pronote/PronoteAuthenticationSelector.tsx b/src/views/login/pronote/PronoteAuthenticationSelector.tsx index 9de60af44..0703c1abd 100644 --- a/src/views/login/pronote/PronoteAuthenticationSelector.tsx +++ b/src/views/login/pronote/PronoteAuthenticationSelector.tsx @@ -13,7 +13,7 @@ import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import { Audio } from "expo-av"; import { NativeText } from "@/components/Global/NativeComponents"; import { LinearGradient } from "expo-linear-gradient"; -import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; +import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; const PronoteAuthenticationSelector: Screen<"PronoteAuthenticationSelector"> = ({ navigation }) => { const theme = useTheme(); diff --git a/src/views/login/pronote/PronoteQRCode.tsx b/src/views/login/pronote/PronoteQRCode.tsx index cc59efbca..8264310a7 100644 --- a/src/views/login/pronote/PronoteQRCode.tsx +++ b/src/views/login/pronote/PronoteQRCode.tsx @@ -19,7 +19,7 @@ import { Account, AccountService } from "@/stores/account/types"; import { Audio } from "expo-av"; import defaultPersonalization from "@/services/pronote/default-personalization"; import extract_pronote_name from "@/utils/format/extract_pronote_name"; -import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; +import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; const makeUUID = (): string => { let dt = new Date().getTime(); diff --git a/src/views/login/pronote/PronoteWebview.tsx b/src/views/login/pronote/PronoteWebview.tsx index b68cc8744..52c2225eb 100644 --- a/src/views/login/pronote/PronoteWebview.tsx +++ b/src/views/login/pronote/PronoteWebview.tsx @@ -34,7 +34,7 @@ import extract_pronote_name from "@/utils/format/extract_pronote_name"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { animPapillon } from "@/utils/ui/animations"; import { useAlert } from "@/providers/AlertProvider"; -import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; +import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; const PronoteWebview: Screen<"PronoteWebview"> = ({ route, navigation }) => { const theme = useTheme(); diff --git a/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx b/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx index a3876ad9b..ea08168cf 100644 --- a/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx +++ b/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx @@ -12,7 +12,7 @@ import Reanimated, { LinearTransition, FlipInXDown } from "react-native-reanimat import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import { Audio } from "expo-av"; import { NativeText } from "@/components/Global/NativeComponents"; -import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; +import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; const SkolengoAuthenticationSelector: Screen<"SkolengoAuthenticationSelector"> = ({ navigation }) => { const theme = useTheme(); diff --git a/src/views/login/skolengo/SkolengoWebview.tsx b/src/views/login/skolengo/SkolengoWebview.tsx index 44fe10cff..454ed4bbd 100644 --- a/src/views/login/skolengo/SkolengoWebview.tsx +++ b/src/views/login/skolengo/SkolengoWebview.tsx @@ -28,7 +28,7 @@ import { authTokenToSkolengoTokenSet } from "@/services/skolengo/skolengo-types" import { getSkolengoAccount } from "@/services/skolengo/skolengo-account"; import { log } from "@/utils/logger/logger"; import { wait } from "@/services/skolengo/data/utils"; -import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; +import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; // TODO : When the app is not started with Expo Go (so with a prebuild or a release build), use the expo auth-session module completely with the deeplink and without the webview. diff --git a/src/views/settings/ExternalAccount/QrcodeScanner.tsx b/src/views/settings/ExternalAccount/QrcodeScanner.tsx index bb723b31e..101755b3c 100644 --- a/src/views/settings/ExternalAccount/QrcodeScanner.tsx +++ b/src/views/settings/ExternalAccount/QrcodeScanner.tsx @@ -19,7 +19,7 @@ import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { BarCodeScanner } from "expo-barcode-scanner"; import MaskedView from "@react-native-masked-view/masked-view"; import * as Haptics from "expo-haptics"; -import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; +import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; type Props = { navigation: any; diff --git a/src/views/settings/SettingsSoundHaptics.tsx b/src/views/settings/SettingsSoundHaptics.tsx index d97b84b3a..77d0c1d92 100644 --- a/src/views/settings/SettingsSoundHaptics.tsx +++ b/src/views/settings/SettingsSoundHaptics.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React from "react"; import { ScrollView, Switch } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { Vibrate, Volume2 } from "lucide-react-native"; @@ -10,49 +10,13 @@ import { NativeIconGradient, } from "@/components/Global/NativeComponents"; import { NativeText } from "@/components/Global/NativeComponents"; -import AsyncStorage from "@react-native-async-storage/async-storage"; import SoundHapticsContainerCard from "@/components/Settings/SoundHapticsContainerCard"; -import PapillonSpinner from "@/components/Global/PapillonSpinner"; +import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; const SettingsApparence: Screen<"SettingsSoundHaptics"> = () => { const insets = useSafeAreaInsets(); - - const [playSon, setPlaySon] = useState(undefined); - const [playHaptics, setPlayHaptics] = useState( - undefined - ); - - useEffect(() => { - if (playSon === undefined) { - AsyncStorage.getItem("son").then((value) => { - if (value) { - setPlaySon(value === "true"); - } else { - setPlaySon(true); - } - }); - } - - if (playHaptics === undefined) { - AsyncStorage.getItem("haptics").then((value) => { - if (value) { - setPlayHaptics(value === "true"); - } else { - setPlayHaptics(true); - } - }); - } - }, []); - - useEffect(() => { - if (playSon === undefined) return; - AsyncStorage.setItem("son", String(playSon)); - }, [playSon]); - - useEffect(() => { - if (playHaptics === undefined) return; - AsyncStorage.setItem("haptics", String(playHaptics)); - }, [playHaptics]); + const { enableSon, setEnableSon, enableHaptics, setEnableHaptics } = + useSoundAndHaptics(); return ( = () => { setPlaySon(value)} - /> - ) : ( - - ) + setEnableSon(value)} + /> } leading={ = () => { setPlayHaptics(value)} - /> - ) : ( - - ) + setEnableHaptics(value)} + /> } leading={ = ({ navigation }) => { const [sound, setSound] = useState(null); diff --git a/src/views/welcome/ColorSelector.tsx b/src/views/welcome/ColorSelector.tsx index 18a8f7533..7b9c4a4e9 100644 --- a/src/views/welcome/ColorSelector.tsx +++ b/src/views/welcome/ColorSelector.tsx @@ -16,7 +16,7 @@ import { getIconName, setIconName } from "@candlefinance/app-icon"; import colorsList from "@/utils/data/colors.json"; import { removeColor } from "../settings/SettingsIcons"; import { expoGoWrapper } from "@/utils/native/expoGoAlert"; -import useSoundAndHaptics from "@/hooks/SoundAndHaptics"; +import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; type Color = typeof colorsList[number]; From b476572b9f16ea293eef946c44d85605874f9754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 3 Jan 2025 19:02:38 +0100 Subject: [PATCH 0130/1144] =?UTF-8?q?la=20page=20des=20param=C3=A8tres=20p?= =?UTF-8?q?our=20changer=20le=20th=C3=A8me=20est=20d=C3=A9sormais=20natif?= =?UTF-8?q?=20!=20quand=20la=20valeur=20est=20chang=C3=A9e,=20le=20th?= =?UTF-8?q?=C3=A8me=20l'est=20=C3=A9galement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.tsx | 7 +- .../FirstInstallation/ButtonCta.tsx | 4 +- .../FirstInstallation/DuoListPressable.tsx | 4 +- src/components/Global/PapillonCheckbox.tsx | 4 +- .../Home/AccountSwitcherContextMenu.tsx | 4 +- ...AndHaptics.tsx => Theme_Sound_Haptics.tsx} | 21 ++++- src/router/index.tsx | 37 ++++----- src/router/navigator/atoms/MenuItem.tsx | 4 +- src/router/navigator/atoms/TabItem.tsx | 4 +- .../account/Grades/Graph/GradesAverage.tsx | 4 +- src/views/account/Home/Home.tsx | 4 +- src/views/account/Homeworks/Homeworks.tsx | 4 +- .../Lessons/Atoms/LessonsDatePicker.tsx | 4 +- src/views/login/ServiceSelector.tsx | 4 +- .../pronote/PronoteAuthenticationSelector.tsx | 4 +- src/views/login/pronote/PronoteQRCode.tsx | 4 +- src/views/login/pronote/PronoteWebview.tsx | 4 +- .../SkolengoAuthenticationSelector.tsx | 4 +- src/views/login/skolengo/SkolengoWebview.tsx | 4 +- .../ExternalAccount/QrcodeScanner.tsx | 4 +- src/views/settings/SettingsApparence.tsx | 78 +++++-------------- src/views/settings/SettingsSoundHaptics.tsx | 4 +- src/views/welcome/AccountCreated.tsx | 4 +- src/views/welcome/ColorSelector.tsx | 4 +- 24 files changed, 97 insertions(+), 126 deletions(-) rename src/hooks/{SoundAndHaptics.tsx => Theme_Sound_Haptics.tsx} (67%) diff --git a/App.tsx b/App.tsx index eecd9e105..c2c77fec9 100644 --- a/App.tsx +++ b/App.tsx @@ -9,6 +9,7 @@ import { AccountService } from "@/stores/account/types"; import { log } from "@/utils/logger/logger"; import { expoGoWrapper } from "@/utils/native/expoGoAlert"; import { atobPolyfill, btoaPolyfill } from "js-base64"; +import { SoundHapticsProvider } from "@/hooks/Theme_Sound_Haptics"; SplashScreen.preventAutoHideAsync(); @@ -128,5 +129,9 @@ export default function App () { return null; } - return ; + return ( + + + + ); } \ No newline at end of file diff --git a/src/components/FirstInstallation/ButtonCta.tsx b/src/components/FirstInstallation/ButtonCta.tsx index 965a4efe8..d4c2b0ef4 100644 --- a/src/components/FirstInstallation/ButtonCta.tsx +++ b/src/components/FirstInstallation/ButtonCta.tsx @@ -3,7 +3,7 @@ import { Text, Pressable, StyleSheet, type StyleProp, type ViewStyle } from "rea import Reanimated, { Easing, useSharedValue, withTiming } from "react-native-reanimated"; import { useTheme } from "@react-navigation/native"; import * as Haptics from "expo-haptics"; -import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; +import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; const ButtonCta: React.FC<{ value: string @@ -27,7 +27,7 @@ const ButtonCta: React.FC<{ const [pressed, setPressed] = useState(false); const scale = useSharedValue(1); const opacity = useSharedValue(1); - const { enableHaptics } = useSoundAndHaptics(); + const { enableHaptics } = useThemeSoundHaptics(); if (!backgroundColor) { backgroundColor = primary ? colors.primary : "transparent"; diff --git a/src/components/FirstInstallation/DuoListPressable.tsx b/src/components/FirstInstallation/DuoListPressable.tsx index 116018819..34f9e5a45 100644 --- a/src/components/FirstInstallation/DuoListPressable.tsx +++ b/src/components/FirstInstallation/DuoListPressable.tsx @@ -6,7 +6,7 @@ import * as Haptics from "expo-haptics"; import { Audio } from "expo-av"; import Reanimated, { Easing, useSharedValue, withTiming } from "react-native-reanimated"; -import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; +import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; const DuoListPressable: React.FC<{ children?: JSX.Element, @@ -32,7 +32,7 @@ const DuoListPressable: React.FC<{ const scale = useSharedValue(1); const opacity = useSharedValue(1); - const { enableSon, enableHaptics } = useSoundAndHaptics(); + const { enableSon, enableHaptics } = useThemeSoundHaptics(); const playSound = useCallback(async () => { const { sound } = await Audio.Sound.createAsync( diff --git a/src/components/Global/PapillonCheckbox.tsx b/src/components/Global/PapillonCheckbox.tsx index a0f3f0d43..b39f8719a 100644 --- a/src/components/Global/PapillonCheckbox.tsx +++ b/src/components/Global/PapillonCheckbox.tsx @@ -9,7 +9,7 @@ import { Check } from "lucide-react-native"; import * as Haptics from "expo-haptics"; import PapillonSpinner from "./PapillonSpinner"; import { animPapillon } from "@/utils/ui/animations"; -import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; +import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; interface CheckboxProps { checked?: boolean @@ -30,7 +30,7 @@ const PapillonCheckbox: React.FC = ({ }) => { const theme = useTheme(); const firstRender = useRef(true); - const { enableHaptics } = useSoundAndHaptics(); + const { enableHaptics } = useThemeSoundHaptics(); useEffect(() => { if (firstRender.current) { diff --git a/src/components/Home/AccountSwitcherContextMenu.tsx b/src/components/Home/AccountSwitcherContextMenu.tsx index f07b01b8f..371f8ee21 100644 --- a/src/components/Home/AccountSwitcherContextMenu.tsx +++ b/src/components/Home/AccountSwitcherContextMenu.tsx @@ -27,7 +27,7 @@ import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; import { useTheme } from "@react-navigation/native"; import { BlurView } from "expo-blur"; import { Check, CirclePlus, Cog, Plus } from "lucide-react-native"; -import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; +import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; const ContextMenu: React.FC<{ style?: any; @@ -39,7 +39,7 @@ const ContextMenu: React.FC<{ const theme = useTheme(); const { colors } = theme; const navigation = useNavigation(); - const { enableHaptics } = useSoundAndHaptics(); + const { enableHaptics } = useThemeSoundHaptics(); const [opened, setOpened] = useState(false); // État pour gérer l'ouverture du menu contextuel diff --git a/src/hooks/SoundAndHaptics.tsx b/src/hooks/Theme_Sound_Haptics.tsx similarity index 67% rename from src/hooks/SoundAndHaptics.tsx rename to src/hooks/Theme_Sound_Haptics.tsx index 4710bb3bb..601ddc106 100644 --- a/src/hooks/SoundAndHaptics.tsx +++ b/src/hooks/Theme_Sound_Haptics.tsx @@ -2,8 +2,10 @@ import React, { createContext, useContext, useState, useEffect } from "react"; import AsyncStorage from "@react-native-async-storage/async-storage"; const SoundHapticsContext = createContext({ + whatTheme: 0, enableSon: true, enableHaptics: true, + setWhatTheme: (value: number) => {}, setEnableSon: (value: boolean) => {}, setEnableHaptics: (value: boolean) => {}, }); @@ -13,10 +15,14 @@ export const SoundHapticsProvider = ({ }: { children: React.ReactNode; }) => { + const [whatTheme, setWhatTheme] = useState(0); const [enableSon, setEnableSon] = useState(true); const [enableHaptics, setEnableHaptics] = useState(true); useEffect(() => { + AsyncStorage.getItem("theme").then((value) => + setWhatTheme(parseInt(value ?? "0")) + ); AsyncStorage.getItem("son").then((value) => setEnableSon(value === "true" || value === null) ); @@ -25,6 +31,10 @@ export const SoundHapticsProvider = ({ ); }, []); + useEffect(() => { + AsyncStorage.setItem("theme", String(whatTheme)); + }, [whatTheme]); + useEffect(() => { AsyncStorage.setItem("son", String(enableSon)); }, [enableSon]); @@ -35,11 +45,18 @@ export const SoundHapticsProvider = ({ return ( {children} ); }; -export const useSoundAndHaptics = () => useContext(SoundHapticsContext); +export const useThemeSoundHaptics = () => useContext(SoundHapticsContext); diff --git a/src/router/index.tsx b/src/router/index.tsx index 9757e327a..d1ad48254 100644 --- a/src/router/index.tsx +++ b/src/router/index.tsx @@ -15,13 +15,13 @@ import { useCurrentAccount } from "@/stores/account"; import { navigatorScreenOptions } from "./helpers/create-screen"; import {navigate} from "@/utils/logger/logger"; import { PapillonNavigation } from "./refs"; -import AsyncStorage from "@react-native-async-storage/async-storage"; -import { SoundHapticsProvider } from "@/hooks/SoundAndHaptics"; +import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; export const Stack = createNativeStackNavigator(); const Router: React.FC = () => { const scheme = useColorScheme(); + const { whatTheme } = useThemeSoundHaptics(); useEffect(() => { async function setNavigationBar () { @@ -48,19 +48,10 @@ const Router: React.FC = () => { config, }; - const [themeValue, setThemeValue] = React.useState(0); - const [theme, setTheme] = React.useState(scheme === "dark" ? PapillonDark : PapillonLight); useEffect(() => { - AsyncStorage.getItem("theme").then((value) => { - if (value) - setThemeValue(parseInt(value)); - }); - }, []); - - useEffect(() => { - switch (themeValue) { + switch (whatTheme) { case 0: setTheme(scheme === "dark" ? PapillonDark : PapillonLight); break; @@ -71,7 +62,7 @@ const Router: React.FC = () => { setTheme(PapillonDark); break; } - }, [scheme, themeValue]); + }, [scheme, whatTheme]); const account = useCurrentAccount(store => store.account!); @@ -89,13 +80,13 @@ const Router: React.FC = () => { backgroundColor={"transparent"} translucent={true} barStyle={ - themeValue == 0 ? + whatTheme === 0 ? scheme === "dark" ? "light-content" : "dark-content" : - themeValue == 1 ? + whatTheme === 1 ? "dark-content" : "light-content" @@ -118,16 +109,14 @@ const Router: React.FC = () => { navigate(str); }} > - - - - {screens.map((screen) => ( + + + {screens.map((screen) => ( // @ts-expect-error : type not compatible, but it works fine. - - ))} - - - + + ))} + + diff --git a/src/router/navigator/atoms/MenuItem.tsx b/src/router/navigator/atoms/MenuItem.tsx index dda4f38e7..3d57628b0 100644 --- a/src/router/navigator/atoms/MenuItem.tsx +++ b/src/router/navigator/atoms/MenuItem.tsx @@ -9,7 +9,7 @@ import { Pressable } from "react-native-gesture-handler"; import * as Haptics from "expo-haptics"; import Reanimated, { FadeIn, FadeOut, LinearTransition, ZoomIn } from "react-native-reanimated"; import { anim2Papillon } from "@/utils/ui/animations"; -import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; +import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; const MenuItem: React.FC<{ route: any; @@ -18,7 +18,7 @@ const MenuItem: React.FC<{ isFocused: boolean; }> = ({ route, descriptor, navigation, isFocused }) => { const theme = useTheme(); - const { enableHaptics } = useSoundAndHaptics(); + const { enableHaptics } = useThemeSoundHaptics(); const { options } = descriptor; const label = options.tabBarLabel !== undefined ? options.tabBarLabel : options.title !== undefined ? options.title : route.name; diff --git a/src/router/navigator/atoms/TabItem.tsx b/src/router/navigator/atoms/TabItem.tsx index 0c51fe620..402a9e2db 100644 --- a/src/router/navigator/atoms/TabItem.tsx +++ b/src/router/navigator/atoms/TabItem.tsx @@ -9,7 +9,7 @@ import { Pressable } from "react-native-gesture-handler"; import * as Haptics from "expo-haptics"; import Reanimated, { FadeIn, FadeOut, LinearTransition, ZoomIn } from "react-native-reanimated"; import { anim2Papillon } from "@/utils/ui/animations"; -import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; +import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; const TabItem: React.FC<{ route: any; @@ -19,7 +19,7 @@ const TabItem: React.FC<{ settings: any; }> = ({ route, descriptor, navigation, isFocused, settings }) => { const theme = useTheme(); - const { enableHaptics } = useSoundAndHaptics(); + const { enableHaptics } = useThemeSoundHaptics(); const { options } = descriptor; const label = options.tabBarLabel !== undefined ? options.tabBarLabel : options.title !== undefined ? options.title : route.name; diff --git a/src/views/account/Grades/Graph/GradesAverage.tsx b/src/views/account/Grades/Graph/GradesAverage.tsx index f81db10ee..a3d45f19e 100644 --- a/src/views/account/Grades/Graph/GradesAverage.tsx +++ b/src/views/account/Grades/Graph/GradesAverage.tsx @@ -33,7 +33,7 @@ import AnimatedNumber from "@/components/Global/AnimatedNumber"; import type { Grade } from "@/services/shared/Grade"; import { AlertTriangle, Calculator, Check, ExternalLink, PieChart, Spline, SquareRadical, TrendingUp } from "lucide-react-native"; import { useAlert } from "@/providers/AlertProvider"; -import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; +import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; interface GradesAverageGraphProps { grades: Grade[]; @@ -49,7 +49,7 @@ const GradesAverageGraph: React.FC = ({ const theme = useTheme(); const account = useCurrentAccount((store) => store.account!); const { showAlert } = useAlert(); - const { enableHaptics } = useSoundAndHaptics(); + const { enableHaptics } = useThemeSoundHaptics(); const [gradesHistory, setGradesHistory] = useState([]); const [hLength, setHLength] = useState(0); diff --git a/src/views/account/Home/Home.tsx b/src/views/account/Home/Home.tsx index 9fac6300c..3fdf81370 100644 --- a/src/views/account/Home/Home.tsx +++ b/src/views/account/Home/Home.tsx @@ -61,14 +61,14 @@ import * as Haptics from "expo-haptics"; import ModalContent from "@/views/account/Home/ModalContent"; import {AnimatedScrollView} from "react-native-reanimated/lib/typescript/reanimated2/component/ScrollView"; import useScreenDimensions from "@/hooks/useScreenDimensions"; -import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; +import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; const Home: Screen<"HomeScreen"> = ({ navigation }) => { const { colors } = useTheme(); const insets = useSafeAreaInsets(); const corners = useMemo(() => getCorners(), []); const focused = useIsFocused(); - const { enableHaptics } = useSoundAndHaptics(); + const { enableHaptics } = useThemeSoundHaptics(); const {isTablet} = useScreenDimensions(); diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 3e2095632..1ccb9d2b1 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -44,7 +44,7 @@ import {NativeSyntheticEvent} from "react-native/Libraries/Types/CoreEventTypes" import {NativeScrollEvent, ScrollViewProps} from "react-native/Libraries/Components/ScrollView/ScrollView"; import {SearchBar} from "react-native-screens"; import AsyncStorage from "@react-native-async-storage/async-storage"; -import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; +import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; type HomeworksPageProps = { index: number; @@ -72,7 +72,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { 320 ) : 0); const insets = useSafeAreaInsets(); - const { enableHaptics } = useSoundAndHaptics(); + const { enableHaptics } = useThemeSoundHaptics(); const outsideNav = route.params?.outsideNav; diff --git a/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx b/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx index 0b4c3c064..15c829d15 100644 --- a/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx +++ b/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx @@ -17,7 +17,7 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import {Theme} from "@react-navigation/native/src/types"; import {NativeScrollEvent, ScrollViewProps} from "react-native/Libraries/Components/ScrollView/ScrollView"; import {NativeSyntheticEvent} from "react-native/Libraries/Types/CoreEventTypes"; -import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; +import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; const { width: SCREEN_WIDTH } = Dimensions.get("window"); const ITEM_WIDTH = 104; @@ -110,7 +110,7 @@ const HorizontalDatePicker = ({ onDateSelect, onCurrentDatePress, initialDate = const flatListRef = useRef(null); const scrollX = useSharedValue(0); const lastItemIndex = useSharedValue(0); - const { enableHaptics } = useSoundAndHaptics(); + const { enableHaptics } = useThemeSoundHaptics(); const { colors } = useTheme(); const insets = useSafeAreaInsets(); diff --git a/src/views/login/ServiceSelector.tsx b/src/views/login/ServiceSelector.tsx index 800db0cc0..ee42b926a 100644 --- a/src/views/login/ServiceSelector.tsx +++ b/src/views/login/ServiceSelector.tsx @@ -18,7 +18,7 @@ import Constants from "expo-constants"; import { LinearGradient } from "expo-linear-gradient"; import { sr } from "date-fns/locale"; import { sub } from "date-fns"; -import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; +import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { const theme = useTheme(); @@ -32,7 +32,7 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { const [v6Data, setV6Data] = useState(null); - const { enableSon } = useSoundAndHaptics(); + const { enableSon } = useThemeSoundHaptics(); useEffect(() => { setTimeout(async () => { diff --git a/src/views/login/pronote/PronoteAuthenticationSelector.tsx b/src/views/login/pronote/PronoteAuthenticationSelector.tsx index 0703c1abd..f907f6f78 100644 --- a/src/views/login/pronote/PronoteAuthenticationSelector.tsx +++ b/src/views/login/pronote/PronoteAuthenticationSelector.tsx @@ -13,7 +13,7 @@ import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import { Audio } from "expo-av"; import { NativeText } from "@/components/Global/NativeComponents"; import { LinearGradient } from "expo-linear-gradient"; -import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; +import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; const PronoteAuthenticationSelector: Screen<"PronoteAuthenticationSelector"> = ({ navigation }) => { const theme = useTheme(); @@ -22,7 +22,7 @@ const PronoteAuthenticationSelector: Screen<"PronoteAuthenticationSelector"> = ( const [method, setMethod] = useState(null); const [sound, setSound] = useState(null); - const { enableSon } = useSoundAndHaptics(); + const { enableSon } = useThemeSoundHaptics(); const loadSound = async () => { const { sound } = await Audio.Sound.createAsync( diff --git a/src/views/login/pronote/PronoteQRCode.tsx b/src/views/login/pronote/PronoteQRCode.tsx index 8264310a7..eef265169 100644 --- a/src/views/login/pronote/PronoteQRCode.tsx +++ b/src/views/login/pronote/PronoteQRCode.tsx @@ -19,7 +19,7 @@ import { Account, AccountService } from "@/stores/account/types"; import { Audio } from "expo-av"; import defaultPersonalization from "@/services/pronote/default-personalization"; import extract_pronote_name from "@/utils/format/extract_pronote_name"; -import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; +import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; const makeUUID = (): string => { let dt = new Date().getTime(); @@ -56,7 +56,7 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { const codeInput = React.createRef(); const [QRData, setQRData] = useState(null); - const { enableSon, enableHaptics } = useSoundAndHaptics(); + const { enableSon, enableHaptics } = useThemeSoundHaptics(); async function loginQR () { setScanned(false); diff --git a/src/views/login/pronote/PronoteWebview.tsx b/src/views/login/pronote/PronoteWebview.tsx index 52c2225eb..3731e6b69 100644 --- a/src/views/login/pronote/PronoteWebview.tsx +++ b/src/views/login/pronote/PronoteWebview.tsx @@ -34,7 +34,7 @@ import extract_pronote_name from "@/utils/format/extract_pronote_name"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { animPapillon } from "@/utils/ui/animations"; import { useAlert } from "@/providers/AlertProvider"; -import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; +import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; const PronoteWebview: Screen<"PronoteWebview"> = ({ route, navigation }) => { const theme = useTheme(); @@ -53,7 +53,7 @@ const PronoteWebview: Screen<"PronoteWebview"> = ({ route, navigation }) => { const [loginStep, setLoginStep] = useState("Préparation de la connexion"); - const { enableSon } = useSoundAndHaptics(); + const { enableSon } = useThemeSoundHaptics(); const instanceURL = route.params.instanceURL.toLowerCase(); diff --git a/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx b/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx index ea08168cf..7f206172b 100644 --- a/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx +++ b/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx @@ -12,7 +12,7 @@ import Reanimated, { LinearTransition, FlipInXDown } from "react-native-reanimat import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import { Audio } from "expo-av"; import { NativeText } from "@/components/Global/NativeComponents"; -import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; +import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; const SkolengoAuthenticationSelector: Screen<"SkolengoAuthenticationSelector"> = ({ navigation }) => { const theme = useTheme(); @@ -21,7 +21,7 @@ const SkolengoAuthenticationSelector: Screen<"SkolengoAuthenticationSelector"> = const [method, setMethod] = useState(null); const [sound, setSound] = useState(null); - const { enableSon } = useSoundAndHaptics(); + const { enableSon } = useThemeSoundHaptics(); const loadSound = async () => { const { sound } = await Audio.Sound.createAsync( diff --git a/src/views/login/skolengo/SkolengoWebview.tsx b/src/views/login/skolengo/SkolengoWebview.tsx index 454ed4bbd..13394c9ee 100644 --- a/src/views/login/skolengo/SkolengoWebview.tsx +++ b/src/views/login/skolengo/SkolengoWebview.tsx @@ -28,7 +28,7 @@ import { authTokenToSkolengoTokenSet } from "@/services/skolengo/skolengo-types" import { getSkolengoAccount } from "@/services/skolengo/skolengo-account"; import { log } from "@/utils/logger/logger"; import { wait } from "@/services/skolengo/data/utils"; -import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; +import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; // TODO : When the app is not started with Expo Go (so with a prebuild or a release build), use the expo auth-session module completely with the deeplink and without the webview. @@ -43,7 +43,7 @@ const SkolengoWebview: Screen<"SkolengoWebview"> = ({ route, navigation }) => { const [pageUrl, setPageUrl] = useState(null); const [discovery, setDiscovery] = useState(null); - const { enableSon } = useSoundAndHaptics(); + const { enableSon } = useThemeSoundHaptics(); const createStoredAccount = useAccounts((store) => store.create); const switchTo = useCurrentAccount((store) => store.switchTo); diff --git a/src/views/settings/ExternalAccount/QrcodeScanner.tsx b/src/views/settings/ExternalAccount/QrcodeScanner.tsx index 101755b3c..401247b6b 100644 --- a/src/views/settings/ExternalAccount/QrcodeScanner.tsx +++ b/src/views/settings/ExternalAccount/QrcodeScanner.tsx @@ -19,7 +19,7 @@ import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { BarCodeScanner } from "expo-barcode-scanner"; import MaskedView from "@react-native-masked-view/masked-view"; import * as Haptics from "expo-haptics"; -import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; +import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; type Props = { navigation: any; @@ -34,7 +34,7 @@ const QrcodeScanner: Screen<"QrcodeScanner"> = ({ navigation, route }) => { const accountID = route.params?.accountID; const [hasPermission, setHasPermission] = React.useState(null); const [scanned, setScanned] = React.useState(false); - const { enableHaptics } = useSoundAndHaptics(); + const { enableHaptics } = useThemeSoundHaptics(); useEffect(() => { const getBarCodeScannerPermissions = async () => { diff --git a/src/views/settings/SettingsApparence.tsx b/src/views/settings/SettingsApparence.tsx index dc017dd94..0bd6bc680 100644 --- a/src/views/settings/SettingsApparence.tsx +++ b/src/views/settings/SettingsApparence.tsx @@ -1,38 +1,21 @@ -import React, { useEffect, useState } from "react"; +import React from "react"; import { ScrollView } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; -import {Moon, RefreshCw, Sun, SunMoon} from "lucide-react-native"; +import {Moon, Sun, SunMoon} from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { NativeList, NativeItem, NativeListHeader } from "@/components/Global/NativeComponents"; import { NativeText } from "@/components/Global/NativeComponents"; import PapillonCheckbox from "@/components/Global/PapillonCheckbox"; -import AsyncStorage from "@react-native-async-storage/async-storage"; import Animated, {FadeInDown, FadeOutDown} from "react-native-reanimated"; import ApparenceContainerCard from "@/components/Settings/ApparenceContainerCard"; +import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; const SettingsApparence: Screen<"SettingsApparence"> = () => { const theme = useTheme(); const insets = useSafeAreaInsets(); - const [selectedTheme, setSelectedTheme] = useState(0); - const [hasUserChangedTheme, setHasUserChangedTheme] = useState(false); - const [defaultTheme, setDefaultTheme] = useState(0); - - useEffect(() => { - AsyncStorage.getItem("theme").then((value) => { - if (value) - { - setDefaultTheme(parseInt(value)); - setSelectedTheme(parseInt(value)); - } - }); - }, []); - - useEffect(() => { - setHasUserChangedTheme(selectedTheme !== defaultTheme); - AsyncStorage.setItem("theme", selectedTheme.toString()); - }, [selectedTheme]); + const { whatTheme, setWhatTheme } = useThemeSoundHaptics(); return ( = () => { } trailing={ { - setSelectedTheme(0); - }} - style={{marginRight: 5}} + checked={whatTheme === 0} + onPress={() => setWhatTheme(0)} + style={{ marginRight: 5 }} /> } - onPress={() => {setSelectedTheme(0);}} + onPress={() => setWhatTheme(0)} chevron={false} > Suivre le thème du système } trailing={ { - setSelectedTheme(1); - }} - style={{marginRight: 5}} + checked={whatTheme === 1} + onPress={() => setWhatTheme(1)} + style={{ marginRight: 5 }} /> } - onPress={() => {setSelectedTheme(1);}} + onPress={() => setWhatTheme(1)} chevron={false} > Mode clair } trailing={ { - setSelectedTheme(2); - }} - style={{marginRight: 5}} + checked={whatTheme === 2} + onPress={() => setWhatTheme(2)} + style={{ marginRight: 5 }} /> } - onPress={() => {setSelectedTheme(2);}} + onPress={() => setWhatTheme(2)} chevron={false} > Mode sombre - - {hasUserChangedTheme && ( - - - } - style={{backgroundColor: "#C53424"}} - > - Redémarrer l'application - Cette option nécessite un redémarrage de l'application pour être appliquée. - - - - )} ); }; diff --git a/src/views/settings/SettingsSoundHaptics.tsx b/src/views/settings/SettingsSoundHaptics.tsx index 77d0c1d92..eb771a928 100644 --- a/src/views/settings/SettingsSoundHaptics.tsx +++ b/src/views/settings/SettingsSoundHaptics.tsx @@ -11,12 +11,12 @@ import { } from "@/components/Global/NativeComponents"; import { NativeText } from "@/components/Global/NativeComponents"; import SoundHapticsContainerCard from "@/components/Settings/SoundHapticsContainerCard"; -import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; +import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; const SettingsApparence: Screen<"SettingsSoundHaptics"> = () => { const insets = useSafeAreaInsets(); const { enableSon, setEnableSon, enableHaptics, setEnableHaptics } = - useSoundAndHaptics(); + useThemeSoundHaptics(); return ( = ({ navigation }) => { const [sound, setSound] = useState(null); const [sound2, setSound2] = useState(null); const account = useCurrentAccount((state) => state.account!); - const { enableSon, enableHaptics } = useSoundAndHaptics(); + const { enableSon, enableHaptics } = useThemeSoundHaptics(); const loadSound = async () => { const { sound } = await Audio.Sound.createAsync( diff --git a/src/views/welcome/ColorSelector.tsx b/src/views/welcome/ColorSelector.tsx index 7b9c4a4e9..c8d9d7136 100644 --- a/src/views/welcome/ColorSelector.tsx +++ b/src/views/welcome/ColorSelector.tsx @@ -16,7 +16,7 @@ import { getIconName, setIconName } from "@candlefinance/app-icon"; import colorsList from "@/utils/data/colors.json"; import { removeColor } from "../settings/SettingsIcons"; import { expoGoWrapper } from "@/utils/native/expoGoAlert"; -import { useSoundAndHaptics } from "@/hooks/SoundAndHaptics"; +import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; type Color = typeof colorsList[number]; @@ -27,7 +27,7 @@ const ColorSelector: Screen<"ColorSelector"> = ({ route, navigation }) => { const account = useCurrentAccount(store => store.account); const mutateProperty = useCurrentAccount(store => store.mutateProperty); const settings = route.params?.settings || false; - const { enableSon, enableHaptics } = useSoundAndHaptics(); + const { enableSon, enableHaptics } = useThemeSoundHaptics(); const [sound, setSound] = useState(null); const [sound2, setSound2] = useState(null); From 05680135b176032228d0c062dfbb09a88756b7d9 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Fri, 3 Jan 2025 23:27:16 +0100 Subject: [PATCH 0131/1144] fix: modified store to enable featuresServices value erasing --- src/stores/multiService/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stores/multiService/index.ts b/src/stores/multiService/index.ts index 30350ae9f..5e32018b5 100644 --- a/src/stores/multiService/index.ts +++ b/src/stores/multiService/index.ts @@ -59,7 +59,7 @@ export const useMultiService = create()( let spaceMutated: MultiServiceSpace; // Mutate only image and name properties. - if (key === "name" || key === "image") { + if (["name", "image", "featuresServices"].includes(key as string)) { spaceMutated = { ...space, [key]: value From b3eff7090d607ef60f1af3cd0111bef0332ec6aa Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Fri, 3 Jan 2025 23:41:19 +0100 Subject: [PATCH 0132/1144] fix: added some logic to account deletion for PapillonMultiServiceSpace - Deleting an account now checks if it is associated to a space (and delete it from the space) - The space is deleted if no more accounts are associated to it --- src/stores/account/index.ts | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index 4a6c14b16..a42e0e249 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -18,6 +18,8 @@ import { useGradesStore } from "../grades"; import { useNewsStore } from "../news"; import { useAttendanceStore } from "../attendance"; import { info, log } from "@/utils/logger/logger"; +import {useMultiService} from "@/stores/multiService"; +import {MultiServiceFeature, MultiServiceSpace} from "@/stores/multiService/types"; /** * Store for the currently selected account. @@ -191,6 +193,44 @@ export const useAccounts = create()( ) })); + // On met à jour les espace multi-service, pour que : + // 1. Si le compte supprimé était lié à un espace: on le supprime de l'espace + // 2. Si il l'espace n'a donc plus aucun compté associé, on le supprime (car un espace est une sorte de groupement de comptes, sans comptes il cesse de fonctionner) + + // On récupère les comptes correspondant aux espaces + const spacesAccounts = get().accounts.filter(account => account.service === AccountService.PapillonMultiService); + for (const spaceAccount of spacesAccounts) { + + // Le compte que l'on a supprimé est lié à cet espace + if (spaceAccount.associatedAccountsLocalIDs.includes(localID)) { + log(`found ${localID} in PapillonMultiServiceSpace ${spaceAccount.name}`, "accounts:remove"); + + // On supprime la liaison du compte (ainsi que de chaque fonctionnalité à laquelle il est associé) + spaceAccount.associatedAccountsLocalIDs.splice(spaceAccount.associatedAccountsLocalIDs.indexOf(localID), 1); + const space = useMultiService.getState().spaces.find(space => space.accountLocalID === spaceAccount.localID) as MultiServiceSpace; + Object.entries(space.featuresServices).map(([key, value]) => { + if (value === localID) { + space.featuresServices[key as MultiServiceFeature] = undefined; + } + }); + useMultiService.getState().update(spaceAccount.localID, "featuresServices", space.featuresServices); + + log(`removed ${localID} from PapillonMultiServiceSpace ${spaceAccount.name}`, "accounts:remove"); + } + + // Si l'espace est vide, on le supprime + if (spaceAccount.associatedAccountsLocalIDs.length === 0) { + log(`PapillonMultiServiceSpace ${spaceAccount.name} is now empty, removing it`, "accounts:remove"); + useMultiService.getState().remove(spaceAccount.localID); + set((state) => ({ + accounts: state.accounts.filter( + (account) => account.localID !== spaceAccount.localID + ) + })); + log(`deleted PapillonMultiServiceSpace ${spaceAccount.name}`, "accounts:remove"); + } + } + log(`removed ${localID}`, "accounts:remove"); }, From 8f4253bcd22c0ab25ade1c8186f2a93ded2116a7 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Fri, 3 Jan 2025 23:46:16 +0100 Subject: [PATCH 0133/1144] fix: typo --- src/stores/account/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index a42e0e249..0e3057ae5 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -195,7 +195,7 @@ export const useAccounts = create()( // On met à jour les espace multi-service, pour que : // 1. Si le compte supprimé était lié à un espace: on le supprime de l'espace - // 2. Si il l'espace n'a donc plus aucun compté associé, on le supprime (car un espace est une sorte de groupement de comptes, sans comptes il cesse de fonctionner) + // 2. Si l'espace n'a donc plus aucun compté associé, on le supprime (car un espace est une sorte de groupement de comptes, sans comptes il cesse de fonctionner) // On récupère les comptes correspondant aux espaces const spacesAccounts = get().accounts.filter(account => account.service === AccountService.PapillonMultiService); From e27ad2a8871cb6c079343eee0fabfbb4dfa90b22 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 4 Jan 2025 10:06:06 +0100 Subject: [PATCH 0134/1144] fix: using Papillon logger Co-authored-by: BORDIER-KERVRAN Gabriel --- src/services/attendance.ts | 8 +++++--- src/services/chats.ts | 19 +++++++++++++------ src/services/evaluation.ts | 8 +++++--- src/services/grades.ts | 8 +++++--- src/services/homework.ts | 7 ++++--- src/services/news.ts | 8 +++++--- src/services/reload-account.ts | 3 ++- src/services/timetable.ts | 5 +++-- 8 files changed, 42 insertions(+), 24 deletions(-) diff --git a/src/services/attendance.ts b/src/services/attendance.ts index 821f82047..2ae38a9d6 100644 --- a/src/services/attendance.ts +++ b/src/services/attendance.ts @@ -3,7 +3,7 @@ import type { Period } from "./shared/Period"; import { useAttendanceStore } from "@/stores/attendance"; import { Attendance } from "./shared/Attendance"; import { checkIfSkoSupported } from "./skolengo/default-personalization"; -import { error } from "@/utils/logger/logger"; +import {error, log} from "@/utils/logger/logger"; import {MultiServiceFeature} from "@/stores/multiService/types"; import {getFeatureAccount} from "@/utils/multiservice"; @@ -66,7 +66,8 @@ export async function updateAttendancePeriodsInCache (accoun case AccountService.PapillonMultiService: { const service = getFeatureAccount(MultiServiceFeature.Attendance, account.localID); if (!service) { - throw new Error("No service set in multi-service space"); + log("No service set in multi-service space for feature \"Attendance\"", "multiservice"); + return; } return updateAttendancePeriodsInCache(service); } @@ -128,7 +129,8 @@ export async function updateAttendanceInCache (account: T, p case AccountService.PapillonMultiService: { const service = getFeatureAccount(MultiServiceFeature.Attendance, account.localID); if (!service) { - throw new Error("No service set in multi-service space"); + log("No service set in multi-service space for feature \"Attendance\"", "multiservice"); + break; } return updateAttendanceInCache(service, periodName); } diff --git a/src/services/chats.ts b/src/services/chats.ts index b3ac838df..51baa0760 100644 --- a/src/services/chats.ts +++ b/src/services/chats.ts @@ -3,6 +3,7 @@ import type { Chat, ChatMessage, ChatRecipient } from "./shared/Chat"; import type { Recipient } from "./shared/Recipient"; import {getFeatureAccount} from "@/utils/multiservice"; import {MultiServiceFeature} from "@/stores/multiService/types"; +import {log} from "@/utils/logger/logger"; export const getChats = async (account: T): Promise> => { switch (account.service) { @@ -17,7 +18,8 @@ export const getChats = async (account: T): Promise (account: T, chat: Ch case AccountService.PapillonMultiService: { const service = getFeatureAccount(MultiServiceFeature.Chats, account.localID); if (!service) { - throw new Error("No service set in multi-service space"); + log("No service set in multi-service space for feature \"Chats\"", "multiservice"); + return []; } return await getChatRecipients(service, chat); } @@ -62,7 +65,8 @@ export const sendMessageInChat = async (account: T, chat: Ch case AccountService.PapillonMultiService: { const service = getFeatureAccount(MultiServiceFeature.Chats, account.localID); if (!service) { - throw new Error("No service set in multi-service space"); + log("No service set in multi-service space for feature \"Chats\"", "multiservice"); + break; } return await sendMessageInChat(service, chat, content); } @@ -84,7 +88,8 @@ export const getChatMessages = async (account: T, chat: Chat case AccountService.PapillonMultiService: { const service = getFeatureAccount(MultiServiceFeature.Chats, account.localID); if (!service) { - throw new Error("No service set in multi-service space"); + log("No service set in multi-service space for feature \"Chats\"", "multiservice"); + return []; } return await getChatMessages(service, chat); } @@ -103,7 +108,8 @@ export const createDiscussionRecipients = async (account: T) case AccountService.PapillonMultiService: { const service = getFeatureAccount(MultiServiceFeature.Chats, account.localID); if (!service) { - throw new Error("No service set in multi-service space"); + log("No service set in multi-service space for feature \"Chats\"", "multiservice"); + return []; } return await createDiscussionRecipients(service); } @@ -123,7 +129,8 @@ export const createDiscussion = async (account: T, subject: case AccountService.PapillonMultiService: { const service = getFeatureAccount(MultiServiceFeature.Chats, account.localID); if (!service) { - throw new Error("No service set in multi-service space"); + log("No service set in multi-service space for feature \"Chats\"", "multiservice"); + break; } return await createDiscussion(service, subject, content, recipients); } diff --git a/src/services/evaluation.ts b/src/services/evaluation.ts index 1656bd596..24f0c63ee 100644 --- a/src/services/evaluation.ts +++ b/src/services/evaluation.ts @@ -2,7 +2,7 @@ import { type Account, AccountService } from "@/stores/account/types"; import type { Period } from "./shared/Period"; import {useEvaluationStore} from "@/stores/evaluation"; import {Evaluation} from "@/services/shared/Evaluation"; -import {error} from "@/utils/logger/logger"; +import {error, log} from "@/utils/logger/logger"; import {getFeatureAccount} from "@/utils/multiservice"; import {MultiServiceFeature} from "@/stores/multiService/types"; @@ -29,7 +29,8 @@ export async function updateEvaluationPeriodsInCache (accoun case AccountService.PapillonMultiService: { const service = getFeatureAccount(MultiServiceFeature.Evaluations, account.localID); if (!service) { - throw new Error("No service set in multi-service space"); + log("No service set in multi-service space for feature \"Evaluations\"", "multiservice"); + break; } return await updateEvaluationPeriodsInCache(service); } @@ -53,7 +54,8 @@ export async function updateEvaluationsInCache (account: T, case AccountService.PapillonMultiService: { const service = getFeatureAccount(MultiServiceFeature.Evaluations, account.localID); if (!service) { - throw new Error("No service set in multi-service space"); + log("No service set in multi-service space for feature \"Evaluations\"", "multiservice"); + break; } return await updateEvaluationsInCache(service, periodName); } diff --git a/src/services/grades.ts b/src/services/grades.ts index 5b04c4dfe..011678052 100644 --- a/src/services/grades.ts +++ b/src/services/grades.ts @@ -2,7 +2,7 @@ import { type Account, AccountService } from "@/stores/account/types"; import { useGradesStore } from "@/stores/grades"; import type { Period } from "./shared/Period"; import type { AverageOverview, Grade } from "./shared/Grade"; -import {error } from "@/utils/logger/logger"; +import {error, log} from "@/utils/logger/logger"; import { checkIfSkoSupported } from "./skolengo/default-personalization"; import {MultiServiceFeature} from "@/stores/multiService/types"; import {getFeatureAccount} from "@/utils/multiservice"; @@ -59,7 +59,8 @@ export async function updateGradesPeriodsInCache (account: T case AccountService.PapillonMultiService: { const service = getFeatureAccount(MultiServiceFeature.Grades, account.localID); if (!service) { - throw new Error("No service set in multi-service space"); + log("No service set in multi-service space for feature \"Grades\"", "multiservice"); + break; } return await updateGradesPeriodsInCache(service); } @@ -132,7 +133,8 @@ export async function updateGradesAndAveragesInCache (accoun case AccountService.PapillonMultiService: { const service = getFeatureAccount(MultiServiceFeature.Grades, account.localID); if (!service) { - throw new Error("No service set in multi-service space"); + log("No service set in multi-service space for feature \"Grades\"", "multiservice"); + break; } return await updateGradesAndAveragesInCache(service, periodName); } diff --git a/src/services/homework.ts b/src/services/homework.ts index a5d8d65a0..a383f084b 100644 --- a/src/services/homework.ts +++ b/src/services/homework.ts @@ -1,7 +1,7 @@ import { type Account, AccountService } from "@/stores/account/types"; import { useHomeworkStore } from "@/stores/homework"; import type { Homework } from "./shared/Homework"; -import { error } from "@/utils/logger/logger"; +import {error, log} from "@/utils/logger/logger"; import { translateToWeekNumber } from "pawnote"; import { pronoteFirstDate } from "./pronote/timetable"; import { dateToEpochWeekNumber, epochWNToPronoteWN } from "@/utils/epochWeekNumber"; @@ -49,7 +49,7 @@ export async function updateHomeworkForWeekInCache (account: case AccountService.PapillonMultiService: { const service = getFeatureAccount(MultiServiceFeature.Homeworks, account.localID); if (!service) { - console.info("[updateHomeworkForWeekInCache]: updating to empty since multi-service space has no account set for homeworks."); + log("No service set in multi-service space for feature \"Homeworks\"", "multiservice"); break; } return updateHomeworkForWeekInCache(service, date); @@ -83,7 +83,8 @@ export async function toggleHomeworkState (account: T, homew case AccountService.PapillonMultiService: { const service = getFeatureAccount(MultiServiceFeature.Homeworks, account.localID); if (!service) { - throw new Error("No service set in multi-service space"); + log("No service set in multi-service space for feature \"Homeworks\"", "multiservice"); + break; } return toggleHomeworkState(service, homework); } diff --git a/src/services/news.ts b/src/services/news.ts index c05bcb538..f8fc32cb0 100644 --- a/src/services/news.ts +++ b/src/services/news.ts @@ -2,7 +2,7 @@ import { type Account, AccountService } from "@/stores/account/types"; import { useNewsStore } from "@/stores/news"; import type { Information } from "./shared/Information"; import { checkIfSkoSupported } from "./skolengo/default-personalization"; -import { error } from "@/utils/logger/logger"; +import {error, log} from "@/utils/logger/logger"; import { newsRead } from "pawnote"; import { ca } from "date-fns/locale"; import {MultiServiceFeature} from "@/stores/multiService/types"; @@ -48,7 +48,8 @@ export async function updateNewsInCache (account: T): Promis case AccountService.PapillonMultiService: { const service = getFeatureAccount(MultiServiceFeature.News, account.localID); if (!service) { - throw new Error("No service set in multi-service space"); + log("No service set in multi-service space for feature \"News\"", "multiservice"); + break; } return updateNewsInCache(service); } @@ -82,7 +83,8 @@ export async function setNewsRead (account: T, message: Info case AccountService.PapillonMultiService: { const service = getFeatureAccount(MultiServiceFeature.News, account.localID); if (!service) { - throw new Error("No service set in multi-service space"); + log("No service set in multi-service space for feature \"News\"", "multiservice"); + break; } return setNewsRead(service, message, read); } diff --git a/src/services/reload-account.ts b/src/services/reload-account.ts index ed8046bf6..f10dfa744 100644 --- a/src/services/reload-account.ts +++ b/src/services/reload-account.ts @@ -1,4 +1,5 @@ import {type Account, AccountService} from "@/stores/account/types"; +import {warn} from "@/utils/logger/logger"; export interface Reconnected { instance: T["instance"] authentication: T["authentication"] @@ -62,7 +63,7 @@ export async function reload (account: T): Promise; } case AccountService.PapillonMultiService: { - console.warn("PapillonMultiService space should never be reloaded."); + warn("PapillonMultiService space should never be reloaded.", "multiservice"); } default: { console.warn("Service not implemented"); diff --git a/src/services/timetable.ts b/src/services/timetable.ts index 3db8f4793..9574b7397 100644 --- a/src/services/timetable.ts +++ b/src/services/timetable.ts @@ -2,7 +2,7 @@ import { type Account, AccountService } from "@/stores/account/types"; import { useTimetableStore } from "@/stores/timetable"; import { epochWNToPronoteWN, weekNumberToDateRange } from "@/utils/epochWeekNumber"; import { checkIfSkoSupported } from "./skolengo/default-personalization"; -import { error } from "@/utils/logger/logger"; +import { error, log } from "@/utils/logger/logger"; import { fetchIcalData } from "./local/ical"; import {MultiServiceFeature} from "@/stores/multiService/types"; import {getFeatureAccount} from "@/utils/multiservice"; @@ -50,7 +50,8 @@ export async function updateTimetableForWeekInCache (account case AccountService.PapillonMultiService: { const service = getFeatureAccount(MultiServiceFeature.Timetable, account.localID); if (!service) { - throw new Error("No service set in multi-service space"); + log("No service set in multi-service space for feature \"Timetable\"", "multiservice"); + break; } return updateTimetableForWeekInCache(service, epochWeekNumber, force); } From 59c93dea718dc4af7a960f831fc04693de892330 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 4 Jan 2025 10:18:47 +0100 Subject: [PATCH 0135/1144] fix: translated comments to english (I hope it's understandable :)) Co-authored-by: BORDIER-KERVRAN Gabriel --- src/stores/account/index.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index 0e3057ae5..0c1ff0cc9 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -90,7 +90,7 @@ export const useCurrentAccount = create()((set, get) => ({ // Special case for spaces if (account.service === AccountService.PapillonMultiService) { log("switching to virtual space, setting instance to a non-null value and reloading associated accounts...", "[switchTo]"); - account.instance = "PapillonPrime"; // Une chaine random, juste pour que l'instance ne soit pas "undefined" (ou null) et que l'espace multiservice soit interprété comme "déconnecté" + account.instance = "PapillonPrime"; // A random string, so the instance is not "undefined" or "null", to prevent creating infinite loading (an undefined instance is interpreted as a loading or disconnected account...) } else if (typeof account.instance === "undefined") { // Account is currently not authenticated, log("instance undefined, reloading...", "[switchTo]"); // Automatically reconnect the main instance. @@ -193,19 +193,19 @@ export const useAccounts = create()( ) })); - // On met à jour les espace multi-service, pour que : - // 1. Si le compte supprimé était lié à un espace: on le supprime de l'espace - // 2. Si l'espace n'a donc plus aucun compté associé, on le supprime (car un espace est une sorte de groupement de comptes, sans comptes il cesse de fonctionner) + // Update of multi-service environments to prevent : + // 1. If the deleted account was associated to a space : its reference need to be removed from this space + // 2. If a multi-service has no more associated accounts, it must be deleted (because a space is like a "group" of accounts, and without any associated accounts it does not work anymore⁾ - // On récupère les comptes correspondant aux espaces + // Fetching the accounts corresponding to spaces const spacesAccounts = get().accounts.filter(account => account.service === AccountService.PapillonMultiService); for (const spaceAccount of spacesAccounts) { - // Le compte que l'on a supprimé est lié à cet espace + // The account deleted above is associated to this space if (spaceAccount.associatedAccountsLocalIDs.includes(localID)) { log(`found ${localID} in PapillonMultiServiceSpace ${spaceAccount.name}`, "accounts:remove"); - // On supprime la liaison du compte (ainsi que de chaque fonctionnalité à laquelle il est associé) + // Remove the link to the account (and to every feature to which it is linked) spaceAccount.associatedAccountsLocalIDs.splice(spaceAccount.associatedAccountsLocalIDs.indexOf(localID), 1); const space = useMultiService.getState().spaces.find(space => space.accountLocalID === spaceAccount.localID) as MultiServiceSpace; Object.entries(space.featuresServices).map(([key, value]) => { @@ -218,7 +218,7 @@ export const useAccounts = create()( log(`removed ${localID} from PapillonMultiServiceSpace ${spaceAccount.name}`, "accounts:remove"); } - // Si l'espace est vide, on le supprime + // If the space is now empty; deleting it if (spaceAccount.associatedAccountsLocalIDs.length === 0) { log(`PapillonMultiServiceSpace ${spaceAccount.name} is now empty, removing it`, "accounts:remove"); useMultiService.getState().remove(spaceAccount.localID); From 04ac970798731bd012ec224dc8440404fee4fc8c Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 4 Jan 2025 10:31:12 +0100 Subject: [PATCH 0136/1144] fix: simplified ternary condition Co-authored-by: BORDIER-KERVRAN Gabriel --- src/components/Global/AccountItem.tsx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/components/Global/AccountItem.tsx b/src/components/Global/AccountItem.tsx index 9cc942ca7..8c847a355 100644 --- a/src/components/Global/AccountItem.tsx +++ b/src/components/Global/AccountItem.tsx @@ -19,14 +19,8 @@ const AccountItem: React.FC<{ account: PrimaryAccount, additionalStyles?: StyleP const theme = useTheme(); return ( Date: Sat, 4 Jan 2025 10:41:59 +0100 Subject: [PATCH 0137/1144] fix: repaired my enter key & indentation Co-authored-by: BORDIER-KERVRAN Gabriel --- src/components/Global/AccountItem.tsx | 33 +++---- src/views/settings/SettingsMultiService.tsx | 21 ++++- .../settings/SettingsMultiServiceSpace.tsx | 90 ++++++++++++------- 3 files changed, 92 insertions(+), 52 deletions(-) diff --git a/src/components/Global/AccountItem.tsx b/src/components/Global/AccountItem.tsx index 8c847a355..0173add2b 100644 --- a/src/components/Global/AccountItem.tsx +++ b/src/components/Global/AccountItem.tsx @@ -37,7 +37,10 @@ const AccountItem: React.FC<{ account: PrimaryAccount, additionalStyles?: StyleP }} > {endCheckMark && - - - + + + } ); diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index c6598378e..022abb690 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -144,10 +144,23 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => value={multiServiceEnabled ?? false} onValueChange={() => { if (multiServiceEnabled) { - Alert.alert("Attention", "La désactivation du multi-service entrainera la suppression de vos environnement multi-services créés.", [ { text: "Annuler", style: "cancel" }, { text: "Confirmer", style: "destructive", onPress: () => { - deleteAllSpaces(); - toggleMultiService(); - } } ]); + Alert.alert( + "Attention", + "La désactivation du multi-service entrainera la suppression de vos environnement multi-services créés.", + [ + { + text: "Annuler", + style: "cancel" + }, + { + text: "Confirmer", + style: "destructive", + onPress: () => { + deleteAllSpaces(); + toggleMultiService(); + } + } + ]); } else { toggleMultiService(); } diff --git a/src/views/settings/SettingsMultiServiceSpace.tsx b/src/views/settings/SettingsMultiServiceSpace.tsx index 9d9a46a96..d8281f949 100644 --- a/src/views/settings/SettingsMultiServiceSpace.tsx +++ b/src/views/settings/SettingsMultiServiceSpace.tsx @@ -77,11 +77,23 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga const [featureSelection, setFeatureSelection] = useState(MultiServiceFeature.Grades); const deleteSpace = () => { - Alert.alert("Êtes-vous sur ?", "Cette action entrainera la suppression de votre espace multi-service.", [ { text: "Annuler", style: "cancel" }, { text: "Confirmer", style: "destructive", onPress: () => { - accounts.remove(space.accountLocalID); - deleteMultiServiceSpace(space.accountLocalID); - navigation.goBack(); - }}]); + Alert.alert( + "Êtes-vous sur ?", + "Cette action entrainera la suppression de votre espace multi-service.", + [ + { + text: "Annuler", + style: "cancel" + }, + { text: "Confirmer", + style: "destructive", + onPress: () => { + accounts.remove(space.accountLocalID); + deleteMultiServiceSpace(space.accountLocalID); + navigation.goBack(); + } + } + ]); }; const openAccountSelector = (feature: MultiServiceFeature) => { @@ -164,16 +176,16 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga chevron={true} onPress={() => selectPicture()} leading={(selectedImage || space.image) && - + } icon={!(selectedImage || space.image) && } trailing={ @@ -300,8 +312,12 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga /> - Accède à plus d'options en sélectionnant l'espace virtuel, et en personnalisant ton profil dans les paramètres. - + + Accède à plus d'options en sélectionnant l'espace virtuel, et en personnalisant ton profil dans les paramètres. + = ({ naviga > {feature.name} - {accounts.accounts.find(account => account.localID === space.featuresServices[feature.feature]) ? ( - account.localID === space.featuresServices[feature.feature]) as PrimaryAccount} endCheckMark={false} additionalStyles={{ - paddingStart: 10, - borderBottomWidth: 1, - backgroundColor: theme.dark ? theme.colors.primary + "09" : theme.colors.primary + "11", - borderColor: theme.colors.text + "20" - }}/> - ): ( - } - separator={true} - > - Pas de service sélectionné - - )} + {accounts.accounts.find(account => + account.localID === space.featuresServices[feature.feature]) ? + ( + account.localID === space.featuresServices[feature.feature]) as PrimaryAccount} + endCheckMark={false} + additionalStyles={{ + paddingStart: 10, + borderBottomWidth: 1, + backgroundColor: theme.dark ? theme.colors.primary + "09" : theme.colors.primary + "11", + borderColor: theme.colors.text + "20" + }} + /> + ) : + ( + } + separator={true} + > + Pas de service sélectionné + + )} ))} From 999375b055873885eb7df5eeb83570e8218026ef Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 4 Jan 2025 10:58:30 +0100 Subject: [PATCH 0138/1144] fix: more code property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 𝕂𝕪𝕝𝕚𝕒𝕟 --- src/components/Global/AccountItem.tsx | 13 ++++++++++-- src/views/settings/SettingsMultiService.tsx | 20 +++++++++++++------ .../settings/SettingsMultiServiceSpace.tsx | 10 +++++----- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/components/Global/AccountItem.tsx b/src/components/Global/AccountItem.tsx index 0173add2b..4cd7634d5 100644 --- a/src/components/Global/AccountItem.tsx +++ b/src/components/Global/AccountItem.tsx @@ -11,7 +11,11 @@ import {Check} from "lucide-react-native"; import React from "react"; import {useTheme} from "@react-navigation/native"; -const AccountItem: React.FC<{ account: PrimaryAccount, additionalStyles?: StyleProp, endCheckMark: boolean }> = ({ +const AccountItem: React.FC<{ + account: PrimaryAccount, + additionalStyles?: StyleProp, + endCheckMark: boolean +}> = ({ account, additionalStyles, endCheckMark @@ -55,7 +59,12 @@ const AccountItem: React.FC<{ account: PrimaryAccount, additionalStyles?: StyleP gap: 2, }} > - + = ({ navigation }) => name: spaceName, personalization: { profilePictureB64: selectedImage || undefined, - tabs: defaultTabs.filter(current => defaultSpaceTabs.includes(current.tab)).map((tab, index) => ({ - name: tab.tab, - enabled: index <= 4 - })) + tabs: defaultTabs + .filter(current => defaultSpaceTabs.includes(current.tab)) + .map((tab, index) => ({ + name: tab.tab, + enabled: index <= 4 + })) }, service: AccountService.PapillonMultiService, studentName: { @@ -193,7 +195,9 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => onPress={() => navigation.navigate("SettingsMultiServiceSpace", { space })} leading={ = ({ navigation }) => - opened} opened={spaceCreationSheetOpened} contentContainerStyle={{ paddingHorizontal: 16 }}> + setSpaceCreationSheetOpened(opened)} + opened={spaceCreationSheetOpened} + contentContainerStyle={{ paddingHorizontal: 16 }} + > = ({ naviga Accède à plus d'options en sélectionnant l'espace virtuel, et en personnalisant ton profil dans les paramètres. - + {features.map((feature, index) => ( <> Date: Sat, 4 Jan 2025 11:00:40 +0100 Subject: [PATCH 0139/1144] fix: simplified ternary condition Co-authored-by: BORDIER-KERVRAN Gabriel --- src/components/Global/AccountItem.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/Global/AccountItem.tsx b/src/components/Global/AccountItem.tsx index 4cd7634d5..06a027d4b 100644 --- a/src/components/Global/AccountItem.tsx +++ b/src/components/Global/AccountItem.tsx @@ -93,9 +93,7 @@ const AccountItem: React.FC<{ > {AccountService[account.service] !== "Local" ? AccountService[account.service] : - account.identityProvider ? - account.identityProvider?.name : - "Compte local" + account.identityProvider?.name ?? "Compte local" } From 3bd062e76c3041ed130a983392bd05e2545dc28b Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 4 Jan 2025 11:41:42 +0100 Subject: [PATCH 0140/1144] fix: simplified function --- src/utils/multiservice/index.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/utils/multiservice/index.ts b/src/utils/multiservice/index.ts index 9be274fa2..71e2947b1 100644 --- a/src/utils/multiservice/index.ts +++ b/src/utils/multiservice/index.ts @@ -1,16 +1,9 @@ import {MultiServiceFeature} from "@/stores/multiService/types"; -import {useAccounts, useCurrentAccount} from "@/stores/account"; +import {useCurrentAccount} from "@/stores/account"; import {useMultiService} from "@/stores/multiService"; import {PrimaryAccount} from "@/stores/account/types"; export function getFeatureAccount (feature: MultiServiceFeature, spaceLocalID: string) { - const accounts = useAccounts.getState().accounts; const accountId = useMultiService.getState().getFeatureAccountId(feature, spaceLocalID); - const featureAccount = accounts.find(account => account.localID === accountId) as PrimaryAccount | undefined; - if (featureAccount) { - const linkedAccount = useCurrentAccount.getState().associatedAccounts.find(account => account.localID === accountId) as PrimaryAccount; - featureAccount.instance = linkedAccount?.instance; - featureAccount.authentication = linkedAccount?.authentication; - } - return featureAccount; + return useCurrentAccount.getState().associatedAccounts.find(account => account.localID === accountId) as PrimaryAccount; } From fad82e60506a47c94ab5ee34e5706929347bf830 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 4 Jan 2025 11:51:35 +0100 Subject: [PATCH 0141/1144] fix: bug & enhancements in account store logic - fixed a bug in account deletion for spaces - added a catch statement to instance reloading (for associated accounts) - setting instance to a non-null value after associated accounts reload for space accounts --- src/stores/account/index.ts | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index 0c1ff0cc9..91c9d8f9a 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -17,7 +17,7 @@ import { useHomeworkStore } from "../homework"; import { useGradesStore } from "../grades"; import { useNewsStore } from "../news"; import { useAttendanceStore } from "../attendance"; -import { info, log } from "@/utils/logger/logger"; +import {error, info, log} from "@/utils/logger/logger"; import {useMultiService} from "@/stores/multiService"; import {MultiServiceFeature, MultiServiceSpace} from "@/stores/multiService/types"; @@ -89,8 +89,7 @@ export const useCurrentAccount = create()((set, get) => ({ // Special case for spaces if (account.service === AccountService.PapillonMultiService) { - log("switching to virtual space, setting instance to a non-null value and reloading associated accounts...", "[switchTo]"); - account.instance = "PapillonPrime"; // A random string, so the instance is not "undefined" or "null", to prevent creating infinite loading (an undefined instance is interpreted as a loading or disconnected account...) + log("switching to virtual space, skipping main account reload and reloading associated accounts...", "[switchTo]"); } else if (typeof account.instance === "undefined") { // Account is currently not authenticated, log("instance undefined, reloading...", "[switchTo]"); // Automatically reconnect the main instance. @@ -118,12 +117,22 @@ export const useCurrentAccount = create()((set, get) => ({ } for (const associatedAccount of associatedAccounts) { - const { instance, authentication } = await reload(associatedAccount); + const { instance, authentication } = await reload(associatedAccount).catch(err => { + error(`failed to reload associated account: ${err} !`, "[switchTo]"); + return { + instance: associatedAccount.instance, + authentication: associatedAccount.authentication + }; + }); associatedAccount.instance = instance; associatedAccount.authentication = authentication; log("reloaded associated account", "[switchTo]"); } + // Setting instance to a non-null value after associated accounts reload, to keep the loading icon while instances are reloading... + if (account.service === AccountService.PapillonMultiService) + account.instance = "PapillonPrime"; // A random string, so the instance is not "undefined" or "null", to prevent creating infinite loading (an undefined instance is interpreted as a loading or disconnected account...) + log("reloaded all external and associated accounts", "[switchTo]"); set({ linkedAccounts, associatedAccounts }); @@ -214,7 +223,11 @@ export const useAccounts = create()( } }); useMultiService.getState().update(spaceAccount.localID, "featuresServices", space.featuresServices); - + set((state) => ({ + accounts: state.accounts.map(account => + account.localID === spaceAccount.localID ? spaceAccount : account + ) + })); log(`removed ${localID} from PapillonMultiServiceSpace ${spaceAccount.name}`, "accounts:remove"); } From 4d8837b071a6066bd853278bfd31fd09ab06510f Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 4 Jan 2025 13:01:51 +0100 Subject: [PATCH 0142/1144] fix: reloading instances only if it is undefined (should fix pronote reconnection issue) --- src/stores/account/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index 91c9d8f9a..d8c978d4f 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -117,6 +117,8 @@ export const useCurrentAccount = create()((set, get) => ({ } for (const associatedAccount of associatedAccounts) { + if (!(typeof associatedAccount.instance === "undefined")) + continue; const { instance, authentication } = await reload(associatedAccount).catch(err => { error(`failed to reload associated account: ${err} !`, "[switchTo]"); return { From 1defd2c2ef90e7bf12715520a6c4ff8b9233ad95 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 4 Jan 2025 14:03:33 +0100 Subject: [PATCH 0143/1144] fix(ts): wrong route name & missing arguments in `DevMenu.tsx` --- src/views/welcome/DevMenu.tsx | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/views/welcome/DevMenu.tsx b/src/views/welcome/DevMenu.tsx index 7cf7a8aa1..913170f53 100644 --- a/src/views/welcome/DevMenu.tsx +++ b/src/views/welcome/DevMenu.tsx @@ -101,7 +101,30 @@ const DevMenu: Screen<"DevMenu"> = ({ navigation }) => { navigation.navigate("NoteReaction")} + onPress={() => navigation.navigate("GradeReaction", { + grade: { + id: "mmmhm", + subjectName: "Cours de ts avec Armand", + description: "Note de test", + timestamp: 0, + average: { + value: 0.2 + }, + coefficient: 40, + min: { + value: 0 + }, + max: { + value: 20 + }, + student: { + value: 0 + }, + outOf: { + value: 20 + } + } + })} > NoteReaction @@ -205,4 +228,4 @@ const DevMenu: Screen<"DevMenu"> = ({ navigation }) => { ); }; -export default DevMenu; \ No newline at end of file +export default DevMenu; From d589162007b8f25be6cd7b405a29d0174f1846de Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 4 Jan 2025 14:04:49 +0100 Subject: [PATCH 0144/1144] fix: lint --- src/views/settings/Settings.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index 1ae6b5805..7d6655e14 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -28,8 +28,8 @@ import { Route, Scroll, Settings as SettingsLucide, - Sparkles, - SunMoon, + Sparkles, + SunMoon, Smile, SwatchBook, WandSparkles, From b18fab929cf3f5bc69f99dfa9238e6049bdab20b Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 4 Jan 2025 14:10:48 +0100 Subject: [PATCH 0145/1144] fix: better joke in dev menu --- src/views/welcome/DevMenu.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/welcome/DevMenu.tsx b/src/views/welcome/DevMenu.tsx index 913170f53..748252963 100644 --- a/src/views/welcome/DevMenu.tsx +++ b/src/views/welcome/DevMenu.tsx @@ -104,9 +104,9 @@ const DevMenu: Screen<"DevMenu"> = ({ navigation }) => { onPress={() => navigation.navigate("GradeReaction", { grade: { id: "mmmhm", - subjectName: "Cours de ts avec Armand", + subjectName: "Éval ts avec Armand", description: "Note de test", - timestamp: 0, + timestamp: 1735996215, average: { value: 0.2 }, From 01a463103adde22a5bdf760609afc72ba6838d66 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 4 Jan 2025 14:10:48 +0100 Subject: [PATCH 0146/1144] fix: better joke in dev menu --- src/views/welcome/DevMenu.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/welcome/DevMenu.tsx b/src/views/welcome/DevMenu.tsx index 913170f53..0d6fb07f9 100644 --- a/src/views/welcome/DevMenu.tsx +++ b/src/views/welcome/DevMenu.tsx @@ -104,9 +104,9 @@ const DevMenu: Screen<"DevMenu"> = ({ navigation }) => { onPress={() => navigation.navigate("GradeReaction", { grade: { id: "mmmhm", - subjectName: "Cours de ts avec Armand", + subjectName: "Éval TS avec Armand", description: "Note de test", - timestamp: 0, + timestamp: 1735996215000, average: { value: 0.2 }, From 3b3160fe8b06e7e016e33cff0b4a3805bf0e86cd Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 4 Jan 2025 14:50:29 +0100 Subject: [PATCH 0147/1144] fix: PapillonSpace profile picture was changing randomly --- src/stores/account/index.ts | 7 ++++++- src/stores/account/types.ts | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index d8c978d4f..f3d7d2551 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -31,9 +31,14 @@ export const useCurrentAccount = create()((set, get) => ({ // For multi service, to not mix Primary & External accounts associatedAccounts: [], - mutateProperty: (key: T, value: PrimaryAccount[T]) => { + mutateProperty: (key: T, value: PrimaryAccount[T], forceMutation = false) => { log(`mutate property ${key} in storage`, "current:update"); + // Special case to keep Papillon Space custom image + if (get().account?.service === AccountService.PapillonMultiService && key === "personalization" && !forceMutation) { + delete (value as PrimaryAccount["personalization"]).profilePictureB64; + } + // Since "instance" is a runtime only key, // we mutate the property only in this memory store and not in the persisted one. if (key === "instance") { diff --git a/src/stores/account/types.ts b/src/stores/account/types.ts index 514da8be5..37eb1d0e6 100644 --- a/src/stores/account/types.ts +++ b/src/stores/account/types.ts @@ -87,7 +87,7 @@ export interface CurrentAccountStore { account: PrimaryAccount | null linkedAccounts: ExternalAccount[] associatedAccounts: PrimaryAccount[] - mutateProperty: (key: T, value: PrimaryAccount[T]) => void + mutateProperty: (key: T, value: PrimaryAccount[T], forceMutation?: boolean) => void linkExistingExternalAccount: (account: ExternalAccount) => void switchTo: (account: PrimaryAccount) => Promise logout: () => void From de54c04526b1e796982367a61a4ba559566e9fbf Mon Sep 17 00:00:00 2001 From: Vince Linise Date: Sat, 4 Jan 2025 18:35:07 +0100 Subject: [PATCH 0148/1144] =?UTF-8?q?fix(Settings):=20mettre=20=C3=A0=20jo?= =?UTF-8?q?ur=20la=20couleur=20de=20l'ic=C3=B4ne=20Son=20et=20vibrations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/Settings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index 9e6f75418..b1e4ffb20 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -150,7 +150,7 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { }, { icon: , - color: "#FF0075", + color: "#800077", label: "Son et vibrations", onPress: () => navigation.navigate("SettingsSoundHaptics"), }, From 2739c4097fc917ef9355ffb96442ca14fa8dbace Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 4 Jan 2025 19:31:28 +0100 Subject: [PATCH 0149/1144] fix: forcing pfp change in `SettingsProfile.tsx` --- src/views/settings/SettingsProfile.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index 453e2ea28..e7f6aa0c5 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -66,7 +66,7 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { mutateProperty("personalization", { ...account.personalization, profilePictureB64: img, - }); + }, true); } setLoadingPic(false); @@ -322,4 +322,4 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { ); }; -export default SettingsProfile; \ No newline at end of file +export default SettingsProfile; From 3880934e018e05fbc465d7f1f09ea955f733a372 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sat, 4 Jan 2025 19:37:22 +0100 Subject: [PATCH 0150/1144] fix: lint & types --- src/components/Global/AnimatedNumber.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Global/AnimatedNumber.tsx b/src/components/Global/AnimatedNumber.tsx index f6aff6614..412d0282a 100644 --- a/src/components/Global/AnimatedNumber.tsx +++ b/src/components/Global/AnimatedNumber.tsx @@ -50,7 +50,7 @@ const AnimatedNumber: React.FC = ({ }, contentContainerStyle]} layout={animPapillon(LinearTransition)} > - {value.toString().split("").map((n, i) => ( + {value.toString().split("").map((n: string, i: number) => ( = ({ ); }; -export default AnimatedNumber; \ No newline at end of file +export default AnimatedNumber; From 23681f964b945d96e21341db7fd12595f0798c73 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sun, 5 Jan 2025 20:27:33 +0100 Subject: [PATCH 0151/1144] fix: better UI: using native buttons --- src/views/settings/SettingsMultiService.tsx | 63 ++++++++------------- 1 file changed, 23 insertions(+), 40 deletions(-) diff --git a/src/views/settings/SettingsMultiService.tsx b/src/views/settings/SettingsMultiService.tsx index 1ac8b9ef7..115553149 100644 --- a/src/views/settings/SettingsMultiService.tsx +++ b/src/views/settings/SettingsMultiService.tsx @@ -17,6 +17,7 @@ import {AccountService, PapillonMultiServiceSpace} from "@/stores/account/types" import uuid from "@/utils/uuid-v4"; import {defaultProfilePicture} from "@/utils/ui/default-profile-picture"; import {defaultTabs} from "@/consts/DefaultTabs"; +import ButtonCta from "@/components/FirstInstallation/ButtonCta"; const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => { const theme = useTheme(); @@ -53,12 +54,9 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => setLoadingImage(false); }; - const [loadingCreation, setLoading] = useState(false); const createSpace = () => { - setLoading(true); if (spaceName == "") { Alert.alert("Aucun titre défini", "Vous devez définir un titre à l'environnement multi service pour pouvoir le créer."); - setLoading(false); return; } @@ -118,7 +116,6 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => createMultiServiceSpace(space, linkedAccount); accounts.create(linkedAccount); setSpaceCreationSheetOpened(false); - setLoading(false); setSelectedImage(null); setSpaceName(""); }; @@ -300,42 +297,28 @@ const SettingsMultiService: Screen<"SettingsMultiService"> = ({ navigation }) => - - - - createSpace()} - trailing={loadingCreation && } - > - - Créer l'espace - - - setSpaceCreationSheetOpened(false)} - > - - Annuler - - - - - + + createSpace()} + value="Créer l'espace" + /> + setSpaceCreationSheetOpened(false)} + /> + )} From fa036043149c86af9faf2f891625d8e837e815ae Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sun, 5 Jan 2025 20:27:48 +0100 Subject: [PATCH 0152/1144] fix: UX improvement --- src/views/settings/SettingsMultiServiceSpace.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/views/settings/SettingsMultiServiceSpace.tsx b/src/views/settings/SettingsMultiServiceSpace.tsx index 53d3ef71f..da6f35678 100644 --- a/src/views/settings/SettingsMultiServiceSpace.tsx +++ b/src/views/settings/SettingsMultiServiceSpace.tsx @@ -75,6 +75,7 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga const [accountSelectorOpened, setAccountSelectorOpened] = useState(false); const [selectedAccount, setSelectedAccount] = useState(null); const [featureSelection, setFeatureSelection] = useState(MultiServiceFeature.Grades); + const [featureSelectionName, setFeatureSelectionName] = useState(""); const deleteSpace = () => { Alert.alert( @@ -96,9 +97,10 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga ]); }; - const openAccountSelector = (feature: MultiServiceFeature) => { + const openAccountSelector = (feature: MultiServiceFeature, name: string) => { setSelectedAccount(availableAccounts.find(account => account.localID === space.featuresServices[feature]) || null); setFeatureSelection(feature); + setFeatureSelectionName(name); setAccountSelectorOpened(true); }; @@ -360,7 +362,7 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga ]} /> } - onPress={() => openAccountSelector(feature.feature)} + onPress={() => openAccountSelector(feature.feature, feature.name)} trailing={} chevron={false} > @@ -400,7 +402,7 @@ const SettingsMultiServiceSpace: Screen<"SettingsMultiServiceSpace"> = ({ naviga paddingHorizontal: 10 }} > - + {availableAccounts.map((account, index) => ( Date: Sun, 5 Jan 2025 21:49:16 +0100 Subject: [PATCH 0153/1144] fix(PronoteAuthenticationSelector): Centrage du texte "Papillon n'est pas..." --- src/views/login/pronote/PronoteAuthenticationSelector.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/login/pronote/PronoteAuthenticationSelector.tsx b/src/views/login/pronote/PronoteAuthenticationSelector.tsx index a787bc68e..b13507d18 100644 --- a/src/views/login/pronote/PronoteAuthenticationSelector.tsx +++ b/src/views/login/pronote/PronoteAuthenticationSelector.tsx @@ -186,7 +186,7 @@ const PronoteAuthenticationSelector: Screen<"PronoteAuthenticationSelector"> = ( = ( /> Date: Mon, 6 Jan 2025 21:41:31 +0100 Subject: [PATCH 0154/1144] utilisation du logger de Papillon pour conserver une trace des logs --- src/background/BackgroundTasks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index aaf05ef3e..aa3bafd35 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -13,7 +13,7 @@ import {PrimaryAccount} from "@/stores/account/types"; * @returns BackgroundFetchResult.NewData */ const backgroundFetch = async () => { - console.log("[background fetch] Running background fetch"); + log("[background fetch]", "Running background fetch"); const accounts = useAccounts((store) => store.accounts).filter(account => !account.isExternal) as PrimaryAccount[]; // Get all primary accounts const switchTo = useCurrentAccount(store => store.switchTo); // Get the switchTo function From cc7db41503aa91d063b26267bdb98bd658b5ad5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Mon, 6 Jan 2025 21:52:18 +0100 Subject: [PATCH 0155/1144] =?UTF-8?q?fix(BackgroundTasks):=20Adaptation=20?= =?UTF-8?q?du=20fichier=20pour=20=C3=A9viter=20les=20erreurs=20de=20hook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/BackgroundTasks.ts | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index aa3bafd35..31201c4e9 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -6,6 +6,16 @@ import { useAccounts, useCurrentAccount } from "@/stores/account"; import { fetchNews } from "./data/News"; import {PrimaryAccount} from "@/stores/account/types"; +import { log } from "@/utils/logger/logger"; + +const getAccounts = () => { + return useAccounts.getState().accounts.filter((account) => !account.isExternal); +}; + +const getSwitchToFunction = () => { + return useCurrentAccount.getState().switchTo; +}; + /** * Background fetch function that fetches all the data @@ -15,22 +25,23 @@ import {PrimaryAccount} from "@/stores/account/types"; const backgroundFetch = async () => { log("[background fetch]", "Running background fetch"); - const accounts = useAccounts((store) => store.accounts).filter(account => !account.isExternal) as PrimaryAccount[]; // Get all primary accounts - const switchTo = useCurrentAccount(store => store.switchTo); // Get the switchTo function + const accounts = getAccounts() as PrimaryAccount[]; + const switchTo = getSwitchToFunction(); for (const account of accounts) { await switchTo(account); - await Promise.all([fetchNews()]); + await fetchNews(); } + return BackgroundFetchResult.NewData; }; const registerBackgroundTasks = async () => { expoGoWrapper(async () => { - TaskManager.defineTask("background-fetch", () => backgroundFetch()); + TaskManager.defineTask("background-fetch", backgroundFetch); BackgroundFetch?.registerTaskAsync("background-fetch", { - minimumInterval: 60 * 15, // 15 minutes + minimumInterval: 60, // 15 minutes stopOnTerminate: false, // android only, startOnBoot: true, // android only }); From a48c697742d9f1ee3efe210d780b02b911a5c676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Mon, 6 Jan 2025 22:03:01 +0100 Subject: [PATCH 0156/1144] =?UTF-8?q?refactor(BackgroundTasks):=20Extracti?= =?UTF-8?q?on=20des=20fonctions=20de=20gestion=20des=20comptes=20et=20des?= =?UTF-8?q?=20nouvelles=20dans=20des=20utilitaires=20d=C3=A9di=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/BackgroundTasks.ts | 14 ++-------- src/background/data/News.ts | 46 ++++++++++++++----------------- src/background/utils/accounts.ts | 16 +++++++++++ src/background/utils/news.ts | 11 ++++++++ 4 files changed, 49 insertions(+), 38 deletions(-) create mode 100644 src/background/utils/accounts.ts create mode 100644 src/background/utils/news.ts diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 31201c4e9..5b61c9dc1 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -2,20 +2,10 @@ import * as BackgroundFetch from "expo-background-fetch"; import * as TaskManager from "expo-task-manager"; import { BackgroundFetchResult } from "expo-background-fetch"; import { expoGoWrapper } from "@/utils/native/expoGoAlert"; -import { useAccounts, useCurrentAccount } from "@/stores/account"; import { fetchNews } from "./data/News"; -import {PrimaryAccount} from "@/stores/account/types"; import { log } from "@/utils/logger/logger"; - -const getAccounts = () => { - return useAccounts.getState().accounts.filter((account) => !account.isExternal); -}; - -const getSwitchToFunction = () => { - return useCurrentAccount.getState().switchTo; -}; - +import { getAccounts, getSwitchToFunction } from "./utils/accounts"; /** * Background fetch function that fetches all the data @@ -25,7 +15,7 @@ const getSwitchToFunction = () => { const backgroundFetch = async () => { log("[background fetch]", "Running background fetch"); - const accounts = getAccounts() as PrimaryAccount[]; + const accounts = getAccounts(); const switchTo = getSwitchToFunction(); for (const account of accounts) { diff --git a/src/background/data/News.ts b/src/background/data/News.ts index 886ad12cc..73a72d6e2 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -1,58 +1,52 @@ -import { updateNewsInCache } from "@/services/news"; -import { Information } from "@/services/shared/Information"; -import { useCurrentAccount } from "@/stores/account"; -import { useNewsStore } from "@/stores/news"; +import { updateNewsState, getNews } from "../utils/news"; +import { getCurrentAccount } from "../utils/accounts"; import { papillonNotify } from "../Notifications"; import uuid from "@/utils/uuid-v4"; import parse_news_resume from "@/utils/format/format_pronote_news"; +import { Information } from "@/services/shared/Information"; -// Function to compare the differences between two arrays of objects const getDifferences = (currentNews: Information[], updatedNews: Information[]): Information[] => { - // Indentify the differences - const differences = updatedNews.filter((updatedItem) => { + return updatedNews.filter((updatedItem) => { const currentItem = currentNews.find((item) => item.id === updatedItem.id); return !currentItem || JSON.stringify(currentItem) !== JSON.stringify(updatedItem); }); - - return differences; }; const fetchNews = async (): Promise => { - // Account informations - const account = useCurrentAccount((store) => store.account!); - const notificationsTypesPermissions = account.personalization.notifications; + const account = getCurrentAccount(); + const notificationsTypesPermissions = account?.personalization.notifications; - // Informations - const currentNews = await useNewsStore((store) => store.informations); // Get the news before the update - await updateNewsInCache(account); // Update the news - const updatedNews = await useNewsStore((store) => store.informations); // Get the news after the update + const currentNews = getNews(); + await updateNewsState(account); + const updatedNews = getNews(); const differences = getDifferences(currentNews, updatedNews); - // Notify the user if there are new informations and if the notifications are enabled if (notificationsTypesPermissions?.enabled && notificationsTypesPermissions?.news) { switch (differences.length) { case 0: break; case 1: papillonNotify({ - id: `${account.localID}-${differences[0].id}-news`, - title: `[${account.name}] Nouvelle information`, + id: `${account?.localID}-${differences[0].id}-news`, + title: `[${account?.name}] Nouvelle information`, subtitle: differences[0].title, - body: differences[0].content ? parse_news_resume(differences[0].content) : "Aucun resumé disponible.", + body: differences[0].content + ? parse_news_resume(differences[0].content) + : "Aucun résumé disponible.", ios: { - categoryId: account.localID, - } + categoryId: account?.localID, + }, }); break; default: papillonNotify({ - id: `${account.localID}-${uuid()}-news`, - title: `[${account.name}] Nouvelles informations`, + id: `${account?.localID}-${uuid()}-news`, + title: `[${account?.name}] Nouvelles informations`, body: `Vous avez ${differences.length} nouvelles informations.`, ios: { - categoryId: account.localID, - } + categoryId: account?.localID, + }, }); break; } diff --git a/src/background/utils/accounts.ts b/src/background/utils/accounts.ts new file mode 100644 index 000000000..e3d6945ea --- /dev/null +++ b/src/background/utils/accounts.ts @@ -0,0 +1,16 @@ +import { useAccounts, useCurrentAccount } from "@/stores/account"; +import { PrimaryAccount } from "@/stores/account/types"; + +export const getAccounts = (): PrimaryAccount[] => { + return useAccounts + .getState() + .accounts.filter((account) => !account.isExternal); +}; + +export const getSwitchToFunction = () => { + return useCurrentAccount.getState().switchTo; +}; + +export const getCurrentAccount = (): PrimaryAccount => { + return useCurrentAccount.getState().account!; +}; diff --git a/src/background/utils/news.ts b/src/background/utils/news.ts new file mode 100644 index 000000000..1fddf923b --- /dev/null +++ b/src/background/utils/news.ts @@ -0,0 +1,11 @@ +import { useNewsStore } from "@/stores/news"; +import { updateNewsInCache } from "@/services/news"; +import { PrimaryAccount } from "@/stores/account/types"; + +export const getNews = () => { + return useNewsStore.getState().informations; +}; + +export const updateNewsState = async (account: PrimaryAccount) => { + await updateNewsInCache(account); +}; From cc83e1b562607f63f0234826c2f10c28ca8077c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Mon, 6 Jan 2025 22:05:35 +0100 Subject: [PATCH 0157/1144] =?UTF-8?q?retour=20=C3=A0=20une=20fr=C3=A9quenc?= =?UTF-8?q?e=20en=20arri=C3=A8re-plan=20de=2015=20minutes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/BackgroundTasks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 5b61c9dc1..fb703eb00 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -31,7 +31,7 @@ const registerBackgroundTasks = async () => { TaskManager.defineTask("background-fetch", backgroundFetch); BackgroundFetch?.registerTaskAsync("background-fetch", { - minimumInterval: 60, // 15 minutes + minimumInterval: 60 * 15, // 15 minutes stopOnTerminate: false, // android only, startOnBoot: true, // android only }); From a1fd4746aefa37bcaa6722b4044c19346ad79b14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Mon, 6 Jan 2025 22:24:50 +0100 Subject: [PATCH 0158/1144] =?UTF-8?q?ajout=20de=202=20cases=20=C3=A0=20ajo?= =?UTF-8?q?uter=20(sugg=C3=A9r=C3=A9=20par=20@Bulgus,=20merci)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/PULL_REQUEST_TEMPLATE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 32e2eb867..c5041236b 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -11,7 +11,9 @@ Pour nous aider à tester ta PR, merci de cocher une des cases suivantes (_en ra Type de pull request : - [ ] **Breaking change** (_des modifications avec un impact sur les fonctionnalités actuelles_) + - [ ] J'ai fait une build de Papillon pour m'assurer que je n'ai rien cassé - [ ] **Feat** (_ajoute une amélioration/nouveauté_) + - [ ] Je m'assure que j'utilise le langage informel (tutoiement) - [ ] **Fix** (_permet de corriger un bug_) - [ ] **Chore** (_des modifications en dehors du dossier `src`_) - [ ] **Styles** (_change/ajoute du style_) From c0c96167b45cb712beb527c03e6842fbe49532e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Tue, 7 Jan 2025 13:27:48 +0100 Subject: [PATCH 0159/1144] =?UTF-8?q?fix(News):=20Suppression=20des=20op?= =?UTF-8?q?=C3=A9rateurs=20de=20coalescence=20pour=20acc=C3=A9der=20aux=20?= =?UTF-8?q?propri=C3=A9t=C3=A9s=20de=20l'objet=20compte?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/News.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/background/data/News.ts b/src/background/data/News.ts index 73a72d6e2..4c43489a7 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -14,7 +14,7 @@ const getDifferences = (currentNews: Information[], updatedNews: Information[]): const fetchNews = async (): Promise => { const account = getCurrentAccount(); - const notificationsTypesPermissions = account?.personalization.notifications; + const notificationsTypesPermissions = account.personalization.notifications; const currentNews = getNews(); await updateNewsState(account); @@ -28,24 +28,24 @@ const fetchNews = async (): Promise => { break; case 1: papillonNotify({ - id: `${account?.localID}-${differences[0].id}-news`, - title: `[${account?.name}] Nouvelle information`, + id: `${account.localID}-${differences[0].id}-news`, + title: `[${account.name}] Nouvelle information`, subtitle: differences[0].title, body: differences[0].content ? parse_news_resume(differences[0].content) : "Aucun résumé disponible.", ios: { - categoryId: account?.localID, + categoryId: account.localID, }, }); break; default: papillonNotify({ - id: `${account?.localID}-${uuid()}-news`, - title: `[${account?.name}] Nouvelles informations`, + id: `${account.localID}-${uuid()}-news`, + title: `[${account.name}] Nouvelles informations`, body: `Vous avez ${differences.length} nouvelles informations.`, ios: { - categoryId: account?.localID, + categoryId: account.localID, }, }); break; From e3ad7a654adda02b99f8c0cc9bb659f797e372c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Tue, 7 Jan 2025 13:40:05 +0100 Subject: [PATCH 0160/1144] =?UTF-8?q?fix(BackgroundTasks):=20Suppression?= =?UTF-8?q?=20de=20l'appel=20=C3=A0=20la=20fonction=20backgroundFetch=20lo?= =?UTF-8?q?rs=20de=20l'ouverture=20de=20l'app?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/BackgroundTasks.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index fb703eb00..5eae7c1f3 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -36,8 +36,6 @@ const registerBackgroundTasks = async () => { startOnBoot: true, // android only }); - backgroundFetch(); - console.log("[background fetch] Registered background fetch"); }); }; From 3368d2bf19d2e20d830f51909698d8c309367ced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Tue, 7 Jan 2025 13:42:30 +0100 Subject: [PATCH 0161/1144] fix(Notifications): Import direct de `notifee` + ajout de code requis par Notifee pour un bon fonctionnement de l'envoi des notifications --- src/background/Notifications.ts | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index 61921d120..6a106ce82 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -1,19 +1,33 @@ import { expoGoWrapper } from "@/utils/native/expoGoAlert"; -import {Notification} from "@notifee/react-native"; +import notifee, { Notification } from "@notifee/react-native"; const requestNotificationPermission = async () => { return expoGoWrapper(async () => { - const notifee = (await import("@notifee/react-native")).default; - await notifee.requestPermission(); + return await notifee.requestPermission(); }, true); }; +const createChannelNotification = async () => { + return await notifee.createChannel({ + id: "default", + name: "Default Channel", + }); +}; + const papillonNotify = async (props: Notification) => { - expoGoWrapper(async () => { - const notifee = (await import("@notifee/react-native")).default; + return expoGoWrapper(async () => { + // Required for iOS + await requestNotificationPermission(); + + // Channel, required for Android + const channelId = await createChannelNotification(); + + // Display a notification await notifee.displayNotification({ ...props, - title: props.title || "Coucou, c'est Papillon 👋", + android: { + channelId, + } }); }); }; From e8d47eff9eb7c43eec5ce5e2c9341ba0777c9a20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Tue, 7 Jan 2025 14:11:37 +0100 Subject: [PATCH 0162/1144] =?UTF-8?q?fix(News):=20Mise=20=C3=A0=20jour=20d?= =?UTF-8?q?es=20id=20pour=20une=20actualisation=20des=20notifications=20et?= =?UTF-8?q?=20non=20une=20duplication?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/News.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/background/data/News.ts b/src/background/data/News.ts index 4c43489a7..ca6af1c61 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -1,7 +1,6 @@ import { updateNewsState, getNews } from "../utils/news"; import { getCurrentAccount } from "../utils/accounts"; import { papillonNotify } from "../Notifications"; -import uuid from "@/utils/uuid-v4"; import parse_news_resume from "@/utils/format/format_pronote_news"; import { Information } from "@/services/shared/Information"; @@ -28,24 +27,24 @@ const fetchNews = async (): Promise => { break; case 1: papillonNotify({ - id: `${account.localID}-${differences[0].id}-news`, + id: `${account.name}-news`, title: `[${account.name}] Nouvelle information`, subtitle: differences[0].title, body: differences[0].content ? parse_news_resume(differences[0].content) : "Aucun résumé disponible.", ios: { - categoryId: account.localID, + categoryId: account.name, }, }); break; default: papillonNotify({ - id: `${account.localID}-${uuid()}-news`, + id: `${account.name}-news`, title: `[${account.name}] Nouvelles informations`, body: `Vous avez ${differences.length} nouvelles informations.`, ios: { - categoryId: account.localID, + categoryId: account.name, }, }); break; From e44be32383437942ee707b90e931ddf1e6d79172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Tue, 7 Jan 2025 14:11:52 +0100 Subject: [PATCH 0163/1144] fix(Notifications): Ajout de la gestion des permissions pour iOS et ajout d'un timestamp pour les notifications Android --- src/background/Notifications.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index 6a106ce82..686aac5e0 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -1,5 +1,6 @@ import { expoGoWrapper } from "@/utils/native/expoGoAlert"; import notifee, { Notification } from "@notifee/react-native"; +import { Platform } from "react-native"; const requestNotificationPermission = async () => { return expoGoWrapper(async () => { @@ -16,17 +17,22 @@ const createChannelNotification = async () => { const papillonNotify = async (props: Notification) => { return expoGoWrapper(async () => { - // Required for iOS - await requestNotificationPermission(); + // Required for iOS, not work in Android + if (Platform.OS === "ios") await requestNotificationPermission(); // Channel, required for Android const channelId = await createChannelNotification(); + // Add timestamp for Android + const timestamp = new Date().getTime(); + // Display a notification await notifee.displayNotification({ ...props, android: { channelId, + timestamp, + showTimestamp: true, } }); }); From 12f6d23337450e108b70b7599c4903fca5d5a631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Tue, 7 Jan 2025 19:12:42 +0100 Subject: [PATCH 0164/1144] =?UTF-8?q?fix(News):=20Am=C3=A9lioration=20de?= =?UTF-8?q?=20la=20logique=20de=20filtrage=20(l'id=20d'une=20actualit?= =?UTF-8?q?=C3=A9=20est=20automatiquement=20reg=C3=A9n=C3=A9r=C3=A9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/News.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/background/data/News.ts b/src/background/data/News.ts index ca6af1c61..64e9031e5 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -5,10 +5,13 @@ import parse_news_resume from "@/utils/format/format_pronote_news"; import { Information } from "@/services/shared/Information"; const getDifferences = (currentNews: Information[], updatedNews: Information[]): Information[] => { - return updatedNews.filter((updatedItem) => { - const currentItem = currentNews.find((item) => item.id === updatedItem.id); - return !currentItem || JSON.stringify(currentItem) !== JSON.stringify(updatedItem); - }); + return updatedNews.filter((updatedItem) => + !currentNews.some( + (item) => + item.author === updatedItem.author && + item.content === updatedItem.content + ) + ); }; const fetchNews = async (): Promise => { From e939a9b982cbfd1d6c2c8b337ae08bb858a6720d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Tue, 7 Jan 2025 19:48:22 +0100 Subject: [PATCH 0165/1144] =?UTF-8?q?feat(Homeworks):=20Ajout=20de=20la=20?= =?UTF-8?q?r=C3=A9cup=C3=A9ration=20et=20de=20la=20notification=20des=20no?= =?UTF-8?q?uveaux=20devoirs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/BackgroundTasks.ts | 6 +- src/background/data/HomeworksUpdate.ts | 68 ++++++++++++++++++++ src/background/utils/homeworksUpdate.ts | 25 +++++++ src/views/settings/SettingsNotifications.tsx | 6 ++ 4 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 src/background/data/HomeworksUpdate.ts create mode 100644 src/background/utils/homeworksUpdate.ts diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 5eae7c1f3..2053ca93f 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -6,6 +6,7 @@ import { expoGoWrapper } from "@/utils/native/expoGoAlert"; import { fetchNews } from "./data/News"; import { log } from "@/utils/logger/logger"; import { getAccounts, getSwitchToFunction } from "./utils/accounts"; +import { fetchHomeworks } from "./data/HomeworksUpdate"; /** * Background fetch function that fetches all the data @@ -20,7 +21,10 @@ const backgroundFetch = async () => { for (const account of accounts) { await switchTo(account); - await fetchNews(); + await Promise.all([ + fetchNews(), + fetchHomeworks(), + ]); } return BackgroundFetchResult.NewData; diff --git a/src/background/data/HomeworksUpdate.ts b/src/background/data/HomeworksUpdate.ts new file mode 100644 index 000000000..7c90f077a --- /dev/null +++ b/src/background/data/HomeworksUpdate.ts @@ -0,0 +1,68 @@ +import { getCurrentAccount } from "../utils/accounts"; +import { papillonNotify } from "../Notifications"; +import parse_homeworks_resume from "@/utils/format/format_pronote_news"; +import { getHomeworks, updateHomeworksState } from "../utils/homeworksUpdate"; +import { Homework } from "@/services/shared/Homework"; +import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; + +const getDifferences = ( + currentHomeworks: Homework[], + updatedHomeworks: Homework[] +): Homework[] => { + return updatedHomeworks.filter( + (updatedItem) => + !currentHomeworks.some( + (item) => + item.due === updatedItem.due && item.content === updatedItem.content + ) + ); +}; + +const fetchHomeworks = async (): Promise => { + const account = getCurrentAccount(); + const notificationsTypesPermissions = account.personalization.notifications; + + const SemaineAct = dateToEpochWeekNumber(new Date()); + const currentHomeworks = getHomeworks()[SemaineAct]; + await updateHomeworksState(account); + const updatedHomeworks = getHomeworks()[SemaineAct]; + + const differences = getDifferences(currentHomeworks, updatedHomeworks); + + if ( + notificationsTypesPermissions?.enabled && + notificationsTypesPermissions?.homeworks + ) { + switch (differences.length) { + case 0: + break; + case 1: + papillonNotify({ + id: `${account.name}-homeworks`, + title: `[${account.name}] Nouveau devoir`, + subtitle: differences[0].subject, + body: differences[0].content + ? parse_homeworks_resume(differences[0].content) + : "Aucun résumé disponible.", + ios: { + categoryId: account.name, + }, + }); + break; + default: + papillonNotify({ + id: `${account.name}-homeworks`, + title: `[${account.name}] Nouveaux devoirs`, + body: `Vous avez ${differences.length} nouveaux devoirs.`, + ios: { + categoryId: account.name, + }, + }); + break; + } + } + + return updatedHomeworks; +}; + +export { fetchHomeworks }; diff --git a/src/background/utils/homeworksUpdate.ts b/src/background/utils/homeworksUpdate.ts new file mode 100644 index 000000000..1550ef138 --- /dev/null +++ b/src/background/utils/homeworksUpdate.ts @@ -0,0 +1,25 @@ +import { PrimaryAccount } from "@/stores/account/types"; +import { useHomeworkStore } from "@/stores/homework"; +import { updateHomeworkForWeekInCache } from "@/services/homework"; +import { epochWNToDate } from "@/utils/epochWeekNumber"; + +const getCurrentWeekNumber = () => { + const now = new Date(); + now.setHours(0, 0, 0, 0); + const start = new Date(1970, 0, 0); + start.setHours(0, 0, 0, 0); + const diff = now.getTime() - start.getTime(); + const oneWeek = 1000 * 60 * 60 * 24 * 7; + return Math.floor(diff / oneWeek); +}; + +export const getHomeworks = () => { + return useHomeworkStore.getState().homeworks; +}; + +export const updateHomeworksState = async (account: PrimaryAccount) => { + await updateHomeworkForWeekInCache( + account, + epochWNToDate(getCurrentWeekNumber()) + ); +}; diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index e1723360c..1589bdb19 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -59,6 +59,12 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = () => { // message: "Cours de mathématiques annulé dans 10 minutes", // personalizationValue: "timeTable", // }, + { + icon: } color={colors.primary} />, + title: "Nouveau devoir", + message: "Nouveau devoir : \"Apporter le manuel\"", + personalizationValue: "homeworksUpdate", + }, // { // icon: } color={colors.primary} />, // title: "Travail à faire pour demain", From 46598d7cef45854ae0d831714661b0ccf0a7ba8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Tue, 7 Jan 2025 20:53:26 +0100 Subject: [PATCH 0166/1144] le typage --- src/stores/account/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stores/account/types.ts b/src/stores/account/types.ts index ebce54985..f1ef4156a 100644 --- a/src/stores/account/types.ts +++ b/src/stores/account/types.ts @@ -50,6 +50,7 @@ export interface Personalization { enabled?: boolean news?: boolean homeworks?: boolean + homeworksUpdate?: boolean grades?: boolean timetable?: boolean attendance?: boolean From 82149b38b4024cea08689fbd9d58257af85892f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Tue, 7 Jan 2025 21:07:19 +0100 Subject: [PATCH 0167/1144] =?UTF-8?q?feat(Plugins):=20Ajout=20du=20plugin?= =?UTF-8?q?=20Notifee=20pour=20la=20gestion=20des=20notifications=20=3D>?= =?UTF-8?q?=20permet=20d'=C3=A9viter=20une=20erreur=20lors=20de=20la=20bui?= =?UTF-8?q?ld?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.json | 1 + plugins/notifee-mod.js | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 plugins/notifee-mod.js diff --git a/app.json b/app.json index 6746c2fcf..6f6398930 100644 --- a/app.json +++ b/app.json @@ -77,6 +77,7 @@ ] }, "plugins": [ + "./plugins/notifee-mod.js", [ "expo-font", { diff --git a/plugins/notifee-mod.js b/plugins/notifee-mod.js new file mode 100644 index 000000000..0c957eafb --- /dev/null +++ b/plugins/notifee-mod.js @@ -0,0 +1,17 @@ +const { withProjectBuildGradle } = require("expo/config-plugins"); + +module.exports = function withNotifeeRepo (config) { + return withProjectBuildGradle(config, async config => { + const contents = config.modResults.contents; + + if (!contents.includes("@notifee/react-native")) { + const replacement = `maven { url 'https://www.jitpack.io' } + maven { + url "$rootDir/../node_modules/@notifee/react-native/android/libs" + }`; + config.modResults.contents = contents.replace("maven { url 'https://www.jitpack.io' }", replacement); + } + + return config; + }); +}; \ No newline at end of file From b6d28ff8e2d43b89a599021686367b0c34000399 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Tue, 7 Jan 2025 21:17:15 +0100 Subject: [PATCH 0168/1144] fix(ts) in `DevMenu.tsx` by @camarm-dev --- src/views/welcome/DevMenu.tsx | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/views/welcome/DevMenu.tsx b/src/views/welcome/DevMenu.tsx index 7cf7a8aa1..6ee137cdb 100644 --- a/src/views/welcome/DevMenu.tsx +++ b/src/views/welcome/DevMenu.tsx @@ -101,7 +101,30 @@ const DevMenu: Screen<"DevMenu"> = ({ navigation }) => { navigation.navigate("NoteReaction")} + onPress={() => navigation.navigate("GradeReaction", { + grade: { + id: "mmmhm", + subjectName: "Cours de ts avec Armand", + description: "Note de test", + timestamp: 0, + average: { + value: 0.2 + }, + coefficient: 40, + min: { + value: 0 + }, + max: { + value: 20 + }, + student: { + value: 0 + }, + outOf: { + value: 20 + } + } + })} > NoteReaction From a43f6aa070f93a676bc6c9795c339586ba6a4d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Tue, 7 Jan 2025 21:22:07 +0100 Subject: [PATCH 0169/1144] fix(ts) in `AnimatedNumber` --- src/components/Global/AnimatedNumber.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Global/AnimatedNumber.tsx b/src/components/Global/AnimatedNumber.tsx index f6aff6614..d2a7edc9f 100644 --- a/src/components/Global/AnimatedNumber.tsx +++ b/src/components/Global/AnimatedNumber.tsx @@ -50,7 +50,7 @@ const AnimatedNumber: React.FC = ({ }, contentContainerStyle]} layout={animPapillon(LinearTransition)} > - {value.toString().split("").map((n, i) => ( + {value.toString().split("").map((n: string, i: number) => ( Date: Tue, 7 Jan 2025 21:23:46 +0100 Subject: [PATCH 0170/1144] j'avais pas vu le dernier commit d'Armand --- src/views/welcome/DevMenu.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/welcome/DevMenu.tsx b/src/views/welcome/DevMenu.tsx index 6ee137cdb..8f1133b99 100644 --- a/src/views/welcome/DevMenu.tsx +++ b/src/views/welcome/DevMenu.tsx @@ -104,9 +104,9 @@ const DevMenu: Screen<"DevMenu"> = ({ navigation }) => { onPress={() => navigation.navigate("GradeReaction", { grade: { id: "mmmhm", - subjectName: "Cours de ts avec Armand", + subjectName: "Éval TS", description: "Note de test", - timestamp: 0, + timestamp: 1735996215000, average: { value: 0.2 }, From 6bd5f6cfc1385a44a66d671e050baa356149d453 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Tue, 7 Jan 2025 21:27:34 +0100 Subject: [PATCH 0171/1144] feat(background): transformal to informal --- src/background/data/HomeworksUpdate.ts | 2 +- src/background/data/News.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/background/data/HomeworksUpdate.ts b/src/background/data/HomeworksUpdate.ts index 7c90f077a..11dd8c282 100644 --- a/src/background/data/HomeworksUpdate.ts +++ b/src/background/data/HomeworksUpdate.ts @@ -53,7 +53,7 @@ const fetchHomeworks = async (): Promise => { papillonNotify({ id: `${account.name}-homeworks`, title: `[${account.name}] Nouveaux devoirs`, - body: `Vous avez ${differences.length} nouveaux devoirs.`, + body: `Tu as ${differences.length} nouveaux devoirs.`, ios: { categoryId: account.name, }, diff --git a/src/background/data/News.ts b/src/background/data/News.ts index 64e9031e5..ea052e49d 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -45,7 +45,7 @@ const fetchNews = async (): Promise => { papillonNotify({ id: `${account.name}-news`, title: `[${account.name}] Nouvelles informations`, - body: `Vous avez ${differences.length} nouvelles informations.`, + body: `Tu as ${differences.length} nouvelles informations.`, ios: { categoryId: account.name, }, From 75f09d663f1680fa70fe21915f7039deebed8098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Wed, 8 Jan 2025 18:51:41 +0100 Subject: [PATCH 0172/1144] =?UTF-8?q?refactor:=20am=C3=A9liorer=20la=20lis?= =?UTF-8?q?ibilit=C3=A9=20et=20la=20structure=20du=20code=20dans=20plusieu?= =?UTF-8?q?rs=20fichiers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/News.ts | 23 ++++++---- src/components/Global/AnimatedNumber.tsx | 48 +++++++++++--------- src/stores/account/types.ts | 22 ++++++--- src/views/settings/SettingsNotifications.tsx | 26 ++++++++--- src/views/welcome/DevMenu.tsx | 9 +++- 5 files changed, 85 insertions(+), 43 deletions(-) diff --git a/src/background/data/News.ts b/src/background/data/News.ts index ea052e49d..5ddec79df 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -4,13 +4,17 @@ import { papillonNotify } from "../Notifications"; import parse_news_resume from "@/utils/format/format_pronote_news"; import { Information } from "@/services/shared/Information"; -const getDifferences = (currentNews: Information[], updatedNews: Information[]): Information[] => { - return updatedNews.filter((updatedItem) => - !currentNews.some( - (item) => - item.author === updatedItem.author && - item.content === updatedItem.content - ) +const getDifferences = ( + currentNews: Information[], + updatedNews: Information[] +): Information[] => { + return updatedNews.filter( + (updatedItem) => + !currentNews.some( + (item) => + item.author === updatedItem.author && + item.content === updatedItem.content + ) ); }; @@ -24,7 +28,10 @@ const fetchNews = async (): Promise => { const differences = getDifferences(currentNews, updatedNews); - if (notificationsTypesPermissions?.enabled && notificationsTypesPermissions?.news) { + if ( + notificationsTypesPermissions?.enabled && + notificationsTypesPermissions?.news + ) { switch (differences.length) { case 0: break; diff --git a/src/components/Global/AnimatedNumber.tsx b/src/components/Global/AnimatedNumber.tsx index d2a7edc9f..4aadc40f9 100644 --- a/src/components/Global/AnimatedNumber.tsx +++ b/src/components/Global/AnimatedNumber.tsx @@ -39,29 +39,35 @@ const AnimatedNumber: React.FC = ({ }) => { return ( - {value.toString().split("").map((n: string, i: number) => ( - - - {n} - - - ))} + {value.toString().split("") + .map((n: string, i: number) => ( + + + {n} + + + ))} ); }; diff --git a/src/stores/account/types.ts b/src/stores/account/types.ts index f1ef4156a..18880027b 100644 --- a/src/stores/account/types.ts +++ b/src/stores/account/types.ts @@ -1,14 +1,17 @@ import type pronote from "pawnote"; -import type { Account as PawdirecteAccount, Session as PawdirecteSession } from "pawdirecte"; -import type { Client as ARDClient, Client as PawrdClient } from "pawrd"; +import type { + Account as PawdirecteAccount, + Session as PawdirecteSession +} from "pawdirecte"; +import type { Client as ARDClient } from "pawrd"; import { Client as TurboselfClient } from "turboself-api"; import { Client as AliseClient, BookingDay } from "alise-api"; import type ScolengoAPI from "scolengo-api"; -import {Configuration, Identification} from "ezly"; +import { Configuration, Identification } from "ezly"; import type MultiAPI from "esup-multi.js"; import { SkolengoAuthConfig } from "@/services/skolengo/skolengo-types"; import { User as ScolengoAPIUser } from "scolengo-api/types/models/Common"; -import {OnlinePayments} from "pawrd/dist"; +import { OnlinePayments } from "pawrd/dist"; export interface Tab { name: string @@ -87,7 +90,10 @@ export interface CurrentAccountStore { /** Si un compte est en cours d'utilisation, on obtient l'ID, sinon `null`. */ account: PrimaryAccount | null linkedAccounts: ExternalAccount[] - mutateProperty: (key: T, value: PrimaryAccount[T]) => void + mutateProperty: ( + key: T, + value: PrimaryAccount[T] + ) => void linkExistingExternalAccount: (account: ExternalAccount) => void switchTo: (account: PrimaryAccount) => Promise logout: () => void @@ -265,5 +271,9 @@ export interface AccountsStore { accounts: Account[] create: (account: Account) => void remove: (localID: string) => void - update: (localID: string, key: T, value: A[T]) => Account | null + update: ( + localID: string, + key: T, + value: A[T] + ) => Account | null } diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 1589bdb19..e7b962d29 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -1,11 +1,21 @@ import React, { useEffect, useState } from "react"; -import { Text, ScrollView, View, TouchableOpacity, StyleSheet, Image, Switch } from "react-native"; +import { ScrollView, Switch } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; -import { Bell, ChevronLeft, MegaphoneOff, CalendarCheck, BookCheck, TrendingUp, Backpack, ChefHat, Newspaper } from "lucide-react-native"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; -import Reanimated, { useSharedValue, useAnimatedStyle, withTiming, LinearTransition } from "react-native-reanimated"; -import { NativeIcon, NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; +import { + CalendarCheck, + BookCheck, + TrendingUp, + Newspaper +} from "lucide-react-native"; +import { useSharedValue, withTiming } from "react-native-reanimated"; +import { + NativeIcon, + NativeItem, + NativeList, + NativeListHeader, + NativeText +} from "@/components/Global/NativeComponents"; import NotificationContainerCard from "@/components/Settings/NotificationContainerCard"; import { requestNotificationPermission } from "@/background/Notifications"; import { alertExpoGo, isExpoGo } from "@/utils/native/expoGoAlert"; @@ -112,7 +122,11 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = () => { false: colors.border, true: colors.primary, }} - value={account.personalization.notifications?.[notification.personalizationValue as keyof typeof notifications] ?? false} + value={ + account.personalization.notifications?.[ + notification.personalizationValue as keyof typeof notifications + ] ?? false + } onValueChange={(value) => { mutateProperty("personalization", { notifications: { diff --git a/src/views/welcome/DevMenu.tsx b/src/views/welcome/DevMenu.tsx index 8f1133b99..f63f7d9a4 100644 --- a/src/views/welcome/DevMenu.tsx +++ b/src/views/welcome/DevMenu.tsx @@ -1,9 +1,14 @@ import { useTheme } from "@react-navigation/native"; import { ChevronRight } from "lucide-react-native"; import React, { useLayoutEffect } from "react"; -import { View, Text, TouchableOpacity, ScrollView, Button, Alert } from "react-native"; +import { View, Text, TouchableOpacity, ScrollView, Alert } from "react-native"; import type { Screen } from "@/router/helpers/types"; -import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; +import { + NativeItem, + NativeList, + NativeListHeader, + NativeText +} from "@/components/Global/NativeComponents"; import AsyncStorage from "@react-native-async-storage/async-storage"; const DevMenu: Screen<"DevMenu"> = ({ navigation }) => { From ca3bd7c532c3d432135e73482230f67ba0288b94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Wed, 8 Jan 2025 19:25:40 +0100 Subject: [PATCH 0173/1144] run prebuild with the new plugin --- android/build.gradle | 3 ++ .../AppIcon.appiconset/Contents.json | 42 +++++++++++++++---- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 9ae2aca2b..031f875ea 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -37,6 +37,9 @@ allprojects { google() mavenCentral() maven { url 'https://www.jitpack.io' } + maven { + url "$rootDir/../node_modules/@notifee/react-native/android/libs" + } } } // @generated begin expo-camera-import - expo prebuild (DO NOT MODIFY) sync-f244f4f3d8bf7229102e8f992b525b8602c74770 diff --git a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json index 90d8d4c2a..e91cbd33d 100644 --- a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,14 +1,38 @@ { - "images": [ + "images" : [ { - "filename": "App-Icon-1024x1024@1x.png", - "idiom": "universal", - "platform": "ios", - "size": "1024x1024" + "filename" : "Icon-Light-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Dark-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "Icon-Tinted-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" } ], - "info": { - "version": 1, - "author": "expo" + "info" : { + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} From ed475994324d1caaf35db2557a362c32c5732c3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Wed, 8 Jan 2025 19:48:11 +0100 Subject: [PATCH 0174/1144] fix(ts) errors --- src/components/Global/AnimatedNumber.tsx | 2 +- src/views/welcome/DevMenu.tsx | 25 +++++++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/components/Global/AnimatedNumber.tsx b/src/components/Global/AnimatedNumber.tsx index f6aff6614..d2a7edc9f 100644 --- a/src/components/Global/AnimatedNumber.tsx +++ b/src/components/Global/AnimatedNumber.tsx @@ -50,7 +50,7 @@ const AnimatedNumber: React.FC = ({ }, contentContainerStyle]} layout={animPapillon(LinearTransition)} > - {value.toString().split("").map((n, i) => ( + {value.toString().split("").map((n: string, i: number) => ( = ({ navigation }) => { navigation.navigate("NoteReaction")} + onPress={() => navigation.navigate("GradeReaction", { + grade: { + id: "mmmhm", + subjectName: "Éval TS", + description: "Note de test", + timestamp: 1735996215000, + average: { + value: 0.2 + }, + coefficient: 40, + min: { + value: 0 + }, + max: { + value: 20 + }, + student: { + value: 0 + }, + outOf: { + value: 20 + } + } + })} > NoteReaction From bb5ee98049a5ef66f8b949cc92044a9d53df11f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Wed, 8 Jan 2025 19:52:19 +0100 Subject: [PATCH 0175/1144] fix(ts) errors --- src/components/Global/AnimatedNumber.tsx | 2 +- src/views/welcome/DevMenu.tsx | 25 +++++++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/components/Global/AnimatedNumber.tsx b/src/components/Global/AnimatedNumber.tsx index f6aff6614..d2a7edc9f 100644 --- a/src/components/Global/AnimatedNumber.tsx +++ b/src/components/Global/AnimatedNumber.tsx @@ -50,7 +50,7 @@ const AnimatedNumber: React.FC = ({ }, contentContainerStyle]} layout={animPapillon(LinearTransition)} > - {value.toString().split("").map((n, i) => ( + {value.toString().split("").map((n: string, i: number) => ( = ({ navigation }) => { navigation.navigate("NoteReaction")} + onPress={() => navigation.navigate("GradeReaction", { + grade: { + id: "mmmhm", + subjectName: "Éval TS", + description: "Note de test", + timestamp: 1735996215000, + average: { + value: 0.2 + }, + coefficient: 40, + min: { + value: 0 + }, + max: { + value: 20 + }, + student: { + value: 0 + }, + outOf: { + value: 20 + } + } + })} > NoteReaction From 92e3689a5b30757ed617698d5aac5bcfaaa2bbe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Wed, 8 Jan 2025 19:53:29 +0100 Subject: [PATCH 0176/1144] fix(ts) errors --- src/components/Global/AnimatedNumber.tsx | 2 +- src/views/welcome/DevMenu.tsx | 25 +++++++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/components/Global/AnimatedNumber.tsx b/src/components/Global/AnimatedNumber.tsx index f6aff6614..d2a7edc9f 100644 --- a/src/components/Global/AnimatedNumber.tsx +++ b/src/components/Global/AnimatedNumber.tsx @@ -50,7 +50,7 @@ const AnimatedNumber: React.FC = ({ }, contentContainerStyle]} layout={animPapillon(LinearTransition)} > - {value.toString().split("").map((n, i) => ( + {value.toString().split("").map((n: string, i: number) => ( = ({ navigation }) => { navigation.navigate("NoteReaction")} + onPress={() => navigation.navigate("GradeReaction", { + grade: { + id: "mmmhm", + subjectName: "Éval TS", + description: "Note de test", + timestamp: 1735996215000, + average: { + value: 0.2 + }, + coefficient: 40, + min: { + value: 0 + }, + max: { + value: 20 + }, + student: { + value: 0 + }, + outOf: { + value: 20 + } + } + })} > NoteReaction From b821cfcf15c10b8435a685e44daf5f6dfeba0e47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 10 Jan 2025 19:18:07 +0100 Subject: [PATCH 0177/1144] =?UTF-8?q?fix(app):=20mise=20=C3=A0=20jour=20de?= =?UTF-8?q?=20la=20version=20=C3=A0=207.7.1=20dans=20package-lock.json?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index bae181206..d28fec4df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.7.0", + "version": "7.7.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.7.0", + "version": "7.7.1", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.3", "@candlefinance/app-icon": "^0.4.5", From 0460d26629c6c8a477c6febc75a1f7c50c6abbd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 10 Jan 2025 19:36:23 +0100 Subject: [PATCH 0178/1144] fix(settings): correction de la gestion de la permission de notification --- src/views/settings/SettingsNotifications.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index e7b962d29..b399d29e8 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -55,7 +55,7 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = () => { return; } - if (await requestNotificationPermission()) return; + await requestNotificationPermission(); await mutateProperty("personalization", { notifications: { ...notifications, enabled: newValue } }); setEnabled(newValue); From ab3986a1f4f5cac6bb3002739f1d5279c6dde5f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 10 Jan 2025 19:44:34 +0100 Subject: [PATCH 0179/1144] =?UTF-8?q?changement=20en=20actualit=C3=A9=20pl?= =?UTF-8?q?ut=C3=B4t=20que=20information?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/News.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/background/data/News.ts b/src/background/data/News.ts index 5ddec79df..85e06c64b 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -38,7 +38,7 @@ const fetchNews = async (): Promise => { case 1: papillonNotify({ id: `${account.name}-news`, - title: `[${account.name}] Nouvelle information`, + title: `[${account.name}] Nouvelle actualité`, subtitle: differences[0].title, body: differences[0].content ? parse_news_resume(differences[0].content) @@ -51,8 +51,8 @@ const fetchNews = async (): Promise => { default: papillonNotify({ id: `${account.name}-news`, - title: `[${account.name}] Nouvelles informations`, - body: `Tu as ${differences.length} nouvelles informations.`, + title: `[${account.name}] Nouvelles actualités`, + body: `Tu as ${differences.length} nouvelles actualités.`, ios: { categoryId: account.name, }, From 3aa92d0000d428ca05d6bee829ac2326e24a1a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 10 Jan 2025 20:25:04 +0100 Subject: [PATCH 0180/1144] =?UTF-8?q?feat(notifications):=20ajout=20d'une?= =?UTF-8?q?=20ic=C3=B4ne=20et=20d'une=20couleur=20pour=20les=20notificatio?= =?UTF-8?q?ns=20(uniquement=20sur=20Android)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/Notifications.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index 686aac5e0..0e1f740a8 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -33,6 +33,8 @@ const papillonNotify = async (props: Notification) => { channelId, timestamp, showTimestamp: true, + smallIcon: "@mipmap/ic_launcher_foreground", + color: "#32AB8E", } }); }); From d474acba6156ac1e3841b2bed798f8a7606c98bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 10 Jan 2025 23:34:02 +0100 Subject: [PATCH 0181/1144] =?UTF-8?q?fix(news):=20am=C3=A9lioration=20du?= =?UTF-8?q?=20r=C3=A9sum=C3=A9=20des=20actualit=C3=A9s=20+=20adaptation=20?= =?UTF-8?q?pour=20les=20comptes=20Ecole=20Directe?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/News.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/background/data/News.ts b/src/background/data/News.ts index 85e06c64b..c858d5f9b 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -40,9 +40,10 @@ const fetchNews = async (): Promise => { id: `${account.name}-news`, title: `[${account.name}] Nouvelle actualité`, subtitle: differences[0].title, - body: differences[0].content - ? parse_news_resume(differences[0].content) - : "Aucun résumé disponible.", + body: + differences[0].content && !differences[0].content.includes(" => { papillonNotify({ id: `${account.name}-news`, title: `[${account.name}] Nouvelles actualités`, - body: `Tu as ${differences.length} nouvelles actualités.`, + body: differences.flatMap((element) => { + return element.title ?? "Sans titre"; + }).join("
"), ios: { categoryId: account.name, }, From ee7db9e53ddf97a1a11add996ddedfe239ef9cf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 10 Jan 2025 23:45:31 +0100 Subject: [PATCH 0182/1144] =?UTF-8?q?fix(news):=20am=C3=A9lioration=20du?= =?UTF-8?q?=20format=20des=20notifications=20d'actualit=C3=A9s=20en=20ajoy?= =?UTF-8?q?tant=20un=20`-`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/News.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/background/data/News.ts b/src/background/data/News.ts index c858d5f9b..984457edc 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -53,9 +53,11 @@ const fetchNews = async (): Promise => { papillonNotify({ id: `${account.name}-news`, title: `[${account.name}] Nouvelles actualités`, - body: differences.flatMap((element) => { - return element.title ?? "Sans titre"; - }).join("
"), + body: differences + .flatMap((element) => { + return `- ${element.title ?? "Sans titre"}`; + }) + .join("
"), ios: { categoryId: account.name, }, From 02d3d56ae9bcb6e9df07c360bd4563573c895e1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 11 Jan 2025 00:12:58 +0100 Subject: [PATCH 0183/1144] fix(settings): demande de la permission de notification uniquement lors de l'activation des notifs + suppression await inutile et indentation --- src/views/settings/SettingsNotifications.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index b399d29e8..877042859 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -55,9 +55,11 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = () => { return; } - await requestNotificationPermission(); + if (newValue) await requestNotificationPermission(); - await mutateProperty("personalization", { notifications: { ...notifications, enabled: newValue } }); + mutateProperty("personalization", { + notifications: { ...notifications, enabled: newValue } + }); setEnabled(newValue); }; From 3dcf63a3c4844f9d51e2c5caf7b52a921a02302f Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Sat, 11 Jan 2025 01:29:57 +0100 Subject: [PATCH 0184/1144] Bump to the latest version of Papillon --- .github/ISSUE_TEMPLATE/bug.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index c991b2396..fdd6399d2 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -61,7 +61,7 @@ body: attributes: label: Version utilisée description: Paramètres (de Papillon) -> Version affichée en bas de la page - placeholder: "7.6.0" + placeholder: "7.7.1" validations: required: true From 067eb2d1e98d755854fdb60a4d3a978e0ea3025a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 11 Jan 2025 14:59:29 +0100 Subject: [PATCH 0185/1144] =?UTF-8?q?feat(notifications):=20am=C3=A9liorat?= =?UTF-8?q?ion=20de=20la=20gestion=20des=20notifications=20avec=20cr=C3=A9?= =?UTF-8?q?ation=20de=20groupes=20et=20canaux=20sp=C3=A9cifiques=20pour=20?= =?UTF-8?q?les=20devoirs=20et=20actualit=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/Notifications.ts | 84 +++++++++++++++++--------- src/background/data/HomeworksUpdate.ts | 40 ++++++------ src/background/data/News.ts | 53 +++++++++------- 3 files changed, 111 insertions(+), 66 deletions(-) diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index 0e1f740a8..39b097338 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -1,46 +1,76 @@ import { expoGoWrapper } from "@/utils/native/expoGoAlert"; -import notifee, { Notification } from "@notifee/react-native"; +import notifee, { + AuthorizationStatus, + Notification, +} from "@notifee/react-native"; import { Platform } from "react-native"; const requestNotificationPermission = async () => { - return expoGoWrapper(async () => { - return await notifee.requestPermission(); - }, true); + const settings = await notifee.requestPermission(); + if (Platform.OS === "ios") { + if (settings.authorizationStatus >= AuthorizationStatus.AUTHORIZED) { + return true; + } + return false; + } else { + if (settings.authorizationStatus === AuthorizationStatus.AUTHORIZED) { + return true; + } + return false; + } }; const createChannelNotification = async () => { - return await notifee.createChannel({ - id: "default", - name: "Default Channel", + await notifee.createChannelGroup({ + id: "Papillon", + name: "Notifications Scolaires", + description: "Permet de ne rien rater de ta vie scolaire", }); -}; -const papillonNotify = async (props: Notification) => { - return expoGoWrapper(async () => { - // Required for iOS, not work in Android - if (Platform.OS === "ios") await requestNotificationPermission(); + await notifee.createChannel({ + id: "News", + groupId: "Papillon", + name: "Actualités", + description: "Te notifie lorsque tu as de nouvelles actualités", + sound: "default", + }); - // Channel, required for Android - const channelId = await createChannelNotification(); + await notifee.createChannel({ + id: "Homeworks", + groupId: "Papillon", + name: "Nouveau Devoir", + description: "Te notifie lorsque tu as de nouveaux devoirs", + sound: "default", + }); +}; - // Add timestamp for Android - const timestamp = new Date().getTime(); +const papillonNotify = async ( + props: Notification, + channelId: "News" | "Homeworks" +) => { + return expoGoWrapper(async () => { + const statut = await requestNotificationPermission(); + if (statut) { + // Add timestamp for Android + const timestamp = new Date().getTime(); - // Display a notification - await notifee.displayNotification({ - ...props, - android: { - channelId, - timestamp, - showTimestamp: true, - smallIcon: "@mipmap/ic_launcher_foreground", - color: "#32AB8E", - } - }); + // Display a notification + await notifee.displayNotification({ + ...props, + android: { + channelId, + timestamp, + showTimestamp: true, + smallIcon: "@mipmap/ic_launcher_foreground", + color: "#32AB8E", + }, + }); + } }); }; export { requestNotificationPermission, + createChannelNotification, papillonNotify, }; diff --git a/src/background/data/HomeworksUpdate.ts b/src/background/data/HomeworksUpdate.ts index 11dd8c282..964e61c1b 100644 --- a/src/background/data/HomeworksUpdate.ts +++ b/src/background/data/HomeworksUpdate.ts @@ -37,27 +37,33 @@ const fetchHomeworks = async (): Promise => { case 0: break; case 1: - papillonNotify({ - id: `${account.name}-homeworks`, - title: `[${account.name}] Nouveau devoir`, - subtitle: differences[0].subject, - body: differences[0].content - ? parse_homeworks_resume(differences[0].content) - : "Aucun résumé disponible.", - ios: { - categoryId: account.name, + papillonNotify( + { + id: `${account.name}-homeworks`, + title: `[${account.name}] Nouveau devoir`, + subtitle: differences[0].subject, + body: differences[0].content + ? parse_homeworks_resume(differences[0].content) + : "Aucun résumé disponible.", + ios: { + categoryId: account.name, + }, }, - }); + "Homeworks" + ); break; default: - papillonNotify({ - id: `${account.name}-homeworks`, - title: `[${account.name}] Nouveaux devoirs`, - body: `Tu as ${differences.length} nouveaux devoirs.`, - ios: { - categoryId: account.name, + papillonNotify( + { + id: `${account.name}-homeworks`, + title: `[${account.name}] Nouveaux devoirs`, + body: `Tu as ${differences.length} nouveaux devoirs.`, + ios: { + categoryId: account.name, + }, }, - }); + "Homeworks" + ); break; } } diff --git a/src/background/data/News.ts b/src/background/data/News.ts index 984457edc..4132bcdb3 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -36,32 +36,41 @@ const fetchNews = async (): Promise => { case 0: break; case 1: - papillonNotify({ - id: `${account.name}-news`, - title: `[${account.name}] Nouvelle actualité`, - subtitle: differences[0].title, - body: - differences[0].content && !differences[0].content.includes(" { - return `- ${element.title ?? "Sans titre"}`; - }) - .join("
"), - ios: { - categoryId: account.name, + papillonNotify( + { + id: `${account.name}-news`, + title: `[${account.name}] Nouvelles actualités`, + body: differences + .flatMap((element) => { + return `- ${element.title ?? "Sans titre"}`; + }) + .join("
"), + ios: { + categoryId: account.name, + }, }, - }); + "News" + ); break; } } From 863b46ce723cefcb03ba5cc84e140225d62c3316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 11 Jan 2025 15:00:42 +0100 Subject: [PATCH 0186/1144] =?UTF-8?q?feat(notifications):=20int=C3=A9grati?= =?UTF-8?q?on=20d'une=20requ=C3=AAte=20dans=20les=20param=C3=A8tres=20lors?= =?UTF-8?q?que=20les=20notifications=20sont=20d=C3=A9sactiv=C3=A9es=20volo?= =?UTF-8?q?ntairement=20dans=20le=20syst=C3=A8me=20(notamment=20iOS)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Settings/NotificationContainerCard.tsx | 145 +++++++++++++++--- src/views/settings/SettingsNotifications.tsx | 40 +++-- 2 files changed, 157 insertions(+), 28 deletions(-) diff --git a/src/components/Settings/NotificationContainerCard.tsx b/src/components/Settings/NotificationContainerCard.tsx index 05b039b7f..830d8ae7c 100644 --- a/src/components/Settings/NotificationContainerCard.tsx +++ b/src/components/Settings/NotificationContainerCard.tsx @@ -1,16 +1,51 @@ import React, { useEffect } from "react"; -import { View, Text, StyleSheet, DimensionValue, Switch } from "react-native"; +import { + View, + StyleSheet, + DimensionValue, + Switch, + Pressable, + Platform, + Alert, + Linking, +} from "react-native"; import LottieView from "lottie-react-native"; -import Reanimated, { useSharedValue, useAnimatedStyle, withTiming } from "react-native-reanimated"; +import Reanimated, { + useSharedValue, + useAnimatedStyle, + withTiming, +} from "react-native-reanimated"; import { NativeItem, NativeList, NativeText } from "../Global/NativeComponents"; +import { Settings, X } from "lucide-react-native"; +import { useAlert } from "@/providers/AlertProvider"; +import { NativeStackNavigationProp } from "@react-navigation/native-stack"; +import { RouteParameters } from "@/router/helpers/types"; type NotificationContainerCardProps = { theme: any; - isEnable?: boolean; - setEnabled?: (value: boolean) => void; + isEnable: boolean | null; + setEnabled: (value: boolean) => void; + navigation: NativeStackNavigationProp< + RouteParameters, + "SettingsNotifications", + undefined + >; }; -const NotificationContainerCard = ({ theme, isEnable = false, setEnabled }: NotificationContainerCardProps) => { +const openNotificationSettings = () => { + if (Platform.OS === "ios") { + Linking.openURL("app-settings:"); + } else { + Linking.openSettings(); + } +}; + +const NotificationContainerCard = ({ + theme, + isEnable, + setEnabled, + navigation +}: NotificationContainerCardProps) => { const { colors } = theme; const opacity = useSharedValue(0); @@ -49,6 +84,8 @@ const NotificationContainerCard = ({ theme, isEnable = false, setEnabled }: Noti opacity: invertedOpacity.value, })); + const { showAlert } = useAlert(); + return ( @@ -96,22 +133,92 @@ const NotificationContainerCard = ({ theme, isEnable = false, setEnabled }: Noti + isEnable !== null ? ( + + ) : ( + { + if (Platform.OS === "ios") { + Alert.alert( + "Notifications désactivées", + "Il faut activer les notifications dans les paramètres du téléphone pour pouvoir les activer dans Papillon.", + [ + { + text: "Annuler", + style: "cancel", + }, + { + text: "Paramètres système", + style: "default", + onPress: () => { + openNotificationSettings(); + setTimeout(() => { + navigation.reset({ + index: 0, + routes: [{ name: "SettingsNotifications" }], + }); + }, 1000); + }, + }, + ] + ); + } else { + showAlert({ + title: "Notifications désactivées", + message: + "Il faut activer les notifications dans les paramètres du téléphone pour pouvoir les activer dans Papillon.", + actions: [ + { + title: "Annuler", + onPress: () => {}, + backgroundColor: colors.card, + icon: , + }, + { + title: "Paramètres système", + onPress: () => { + openNotificationSettings(); + setTimeout(() => { + navigation.reset({ + index: 0, + routes: [{ name: "SettingsNotifications" }], + }); + }, 1000); + }, + primary: true, + backgroundColor: "#888", + icon: , + }, + ], + }); + } + }} + > + + + ) } > - - Activer les notifications - + Activer les notifications Reçois des notifications pour ne rien rater de ta vie scolaire. diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 877042859..95b8191a9 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -17,11 +17,13 @@ import { NativeText } from "@/components/Global/NativeComponents"; import NotificationContainerCard from "@/components/Settings/NotificationContainerCard"; -import { requestNotificationPermission } from "@/background/Notifications"; +import { createChannelNotification, requestNotificationPermission } from "@/background/Notifications"; import { alertExpoGo, isExpoGo } from "@/utils/native/expoGoAlert"; import { useCurrentAccount } from "@/stores/account"; -const SettingsNotifications: Screen<"SettingsNotifications"> = () => { +const SettingsNotifications: Screen<"SettingsNotifications"> = ({ + navigation +}) => { const theme = useTheme(); const { colors } = theme; @@ -31,7 +33,31 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = () => { const notifications = account.personalization.notifications; // Global state - const [enabled, setEnabled] = useState(notifications?.enabled || false); + const [enabled, setEnabled] = useState( + notifications?.enabled || false + ); + useEffect(() => { + const test = async () => { + const statut = await requestNotificationPermission(); + if (!statut) { + setEnabled(null); + setTimeout(() => { + mutateProperty("personalization", { + notifications: { ...notifications, enabled: false } + }); + }, 1500); + } else if (enabled !== null) { + if (enabled) createChannelNotification(); + setTimeout(() => { + mutateProperty("personalization", { + notifications: { ...notifications, enabled } + }); + }, 1500); + } + }; + + test(); + }, [enabled]); // Animation states const opacity = useSharedValue(0); @@ -55,11 +81,6 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = () => { return; } - if (newValue) await requestNotificationPermission(); - - mutateProperty("personalization", { - notifications: { ...notifications, enabled: newValue } - }); setEnabled(newValue); }; @@ -108,9 +129,10 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = () => { theme={theme} isEnable={enabled} setEnabled={askEnabled} + navigation={navigation} /> - {notifications?.enabled && ( + {enabled && ( <> From b955d2b41108561c30a843ec83204a42238d36f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 11 Jan 2025 15:11:13 +0100 Subject: [PATCH 0187/1144] fix(epochWeekNumber): mauvaise semaine des devoirs --- src/utils/epochWeekNumber.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/epochWeekNumber.ts b/src/utils/epochWeekNumber.ts index 10de59d99..406f1b8f6 100644 --- a/src/utils/epochWeekNumber.ts +++ b/src/utils/epochWeekNumber.ts @@ -52,7 +52,7 @@ export const dateToEpochWeekNumber = (date: Date): number => { const commonDay = dayToWeekCommonDay(date); const epochWeekNumber = Math.floor((commonDay.getTime() + EPOCH_WN_CONFIG.adjustEpochInitialDate - ( (EPOCH_WN_CONFIG.setMiddleDay - 1) /7 ) * EPOCH_WN_CONFIG.numberOfMsInAWeek) / EPOCH_WN_CONFIG.numberOfMsInAWeek); // this is the opposite of the weekNumberToMiddleDate function - return epochWeekNumber; + return epochWeekNumber + 1; }; /** From f659e4f339f23ae2cfeaca85ace10108776b7aa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 11 Jan 2025 15:48:46 +0100 Subject: [PATCH 0188/1144] fix(homeworks): mauvaise variable --- src/background/data/HomeworksUpdate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/background/data/HomeworksUpdate.ts b/src/background/data/HomeworksUpdate.ts index 964e61c1b..c752f6c81 100644 --- a/src/background/data/HomeworksUpdate.ts +++ b/src/background/data/HomeworksUpdate.ts @@ -31,7 +31,7 @@ const fetchHomeworks = async (): Promise => { if ( notificationsTypesPermissions?.enabled && - notificationsTypesPermissions?.homeworks + notificationsTypesPermissions?.homeworksUpdate ) { switch (differences.length) { case 0: From 4285e0aee2c4cebf7d6fdaaf3363a80630aae793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 11 Jan 2025 16:27:47 +0100 Subject: [PATCH 0189/1144] =?UTF-8?q?feat(homeworks):=20tronquer=20le=20r?= =?UTF-8?q?=C3=A9sum=C3=A9=20des=20devoirs=20=C3=A0=20100=20caract=C3=A8re?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/HomeworksUpdate.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/background/data/HomeworksUpdate.ts b/src/background/data/HomeworksUpdate.ts index c752f6c81..8be21b5b9 100644 --- a/src/background/data/HomeworksUpdate.ts +++ b/src/background/data/HomeworksUpdate.ts @@ -43,7 +43,10 @@ const fetchHomeworks = async (): Promise => { title: `[${account.name}] Nouveau devoir`, subtitle: differences[0].subject, body: differences[0].content - ? parse_homeworks_resume(differences[0].content) + ? `${parse_homeworks_resume(differences[0].content).slice( + 0, + 100 + )}...` : "Aucun résumé disponible.", ios: { categoryId: account.name, From 995e53b0047e372e9cf771feb53d927364789340 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 11 Jan 2025 19:23:31 +0100 Subject: [PATCH 0190/1144] feat(grades): ajout de la gestion des notifications pour les nouvelles notes --- App.tsx | 7 +- src/background/Notifications.ts | 10 ++- src/background/data/Grades.ts | 74 ++++++++++++++++++++ src/background/utils/grades.ts | 17 +++++ src/views/settings/SettingsNotifications.tsx | 12 ++-- 5 files changed, 108 insertions(+), 12 deletions(-) create mode 100644 src/background/data/Grades.ts create mode 100644 src/background/utils/grades.ts diff --git a/App.tsx b/App.tsx index 403afa524..07b02be2c 100644 --- a/App.tsx +++ b/App.tsx @@ -7,8 +7,8 @@ import AsyncStorage from "@react-native-async-storage/async-storage"; import { useAccounts, useCurrentAccount } from "@/stores/account"; import {AccountService, PrimaryAccount} from "@/stores/account/types"; import { log } from "@/utils/logger/logger"; -import { expoGoWrapper } from "@/utils/native/expoGoAlert"; import { atobPolyfill, btoaPolyfill } from "js-base64"; +import { registerBackgroundTasks } from "@/background/BackgroundTasks"; SplashScreen.preventAutoHideAsync(); @@ -104,10 +104,7 @@ export default function App () { "Linking found multiple possible" ]); - expoGoWrapper(async () => { - const { registerBackgroundTasks } = await import("@/background/BackgroundTasks"); - registerBackgroundTasks(); - }); + registerBackgroundTasks(); }, []); const applyGlobalPolyfills = useCallback(() => { diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index 39b097338..074c3c845 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -42,11 +42,19 @@ const createChannelNotification = async () => { description: "Te notifie lorsque tu as de nouveaux devoirs", sound: "default", }); + + await notifee.createChannel({ + id: "Grades", + groupId: "Papillon", + name: "Notes", + description: "Te notifie lorsque tu as de nouvelles notes", + sound: "default", + }); }; const papillonNotify = async ( props: Notification, - channelId: "News" | "Homeworks" + channelId: "News" | "Homeworks" | "Grades" ) => { return expoGoWrapper(async () => { const statut = await requestNotificationPermission(); diff --git a/src/background/data/Grades.ts b/src/background/data/Grades.ts new file mode 100644 index 000000000..42714468b --- /dev/null +++ b/src/background/data/Grades.ts @@ -0,0 +1,74 @@ +import { getCurrentAccount } from "../utils/accounts"; +import { papillonNotify } from "../Notifications"; +import { Grade } from "@/services/shared/Grade"; +import { getGrades, updateGradeState } from "../utils/grades"; + +const getDifferences = ( + currentGrade: Grade[], + updatedGrade: Grade[] +): Grade[] => { + return updatedGrade.filter( + (updatedItem) => + !currentGrade.some( + (item) => + item.student.value === updatedItem.student.value && + item.coefficient === updatedItem.coefficient + ) + ); +}; + +const fetchGrade = async (): Promise => { + const account = getCurrentAccount(); + const notificationsTypesPermissions = account.personalization.notifications; + + const { defaultPeriod, grades } = getGrades(); + await updateGradeState(account, defaultPeriod); + const updatedGrade = getGrades().grades[defaultPeriod]; + + const differences = getDifferences(grades[defaultPeriod], updatedGrade); + + if ( + notificationsTypesPermissions?.enabled && + notificationsTypesPermissions?.grades + ) { + switch (differences.length) { + case 0: + break; + case 1: + papillonNotify( + { + id: `${account.name}-grades`, + title: `[${account.name}] Nouvelle note`, + // subtitle: differences[0].title, + body: `Une nouvelle note en ${differences[0].subjectName} a été publiée`, + ios: { + categoryId: account.name, + }, + }, + "Grades" + ); + break; + default: + papillonNotify( + { + id: `${account.name}-grades`, + title: `[${account.name}] Nouvelles notes`, + body: `${differences.length} nouvelles notes (${differences + .flatMap((element) => { + return `- ${element.subjectName}`; + }) + .join("/")}) ont été publiées`, + ios: { + categoryId: account.name, + }, + }, + "Grades" + ); + break; + } + } + + return updatedGrade; +}; + +export { fetchGrade }; diff --git a/src/background/utils/grades.ts b/src/background/utils/grades.ts new file mode 100644 index 000000000..2979ce6bb --- /dev/null +++ b/src/background/utils/grades.ts @@ -0,0 +1,17 @@ +import { updateGradesAndAveragesInCache } from "@/services/grades"; +import { PrimaryAccount } from "@/stores/account/types"; +import { useGradesStore } from "@/stores/grades"; + +export const getGrades = () => { + return { + defaultPeriod: useGradesStore.getState().defaultPeriod, + grades: useGradesStore.getState().grades, + }; +}; + +export const updateGradeState = async ( + account: PrimaryAccount, + period: string +) => { + await updateGradesAndAveragesInCache(account, period); +}; diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 95b8191a9..025a7055c 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -104,12 +104,12 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ // message: "N’oublie pas de terminer ton devoir de français pour demain", // personalizationValue: "homework", // }, - // { - // icon: } color={colors.primary} />, - // title: "Nouvelle note", - // message: "Nouvelle note disponible : 18/20 en histoire", - // personalizationValue: "grades", - // }, + { + icon: } color={colors.primary} />, + title: "Nouvelle note", + message: "Nouvelle note publiée en histoire", + personalizationValue: "grades", + }, { icon: } color={colors.primary} />, title: "Nouvelle actualité", From 3072d526409538ecfb55e31d9d95eb1dd26a5626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 11 Jan 2025 19:28:17 +0100 Subject: [PATCH 0191/1144] appel de la fonction `fetchGrade` --- src/background/BackgroundTasks.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 2053ca93f..607870ab8 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -7,6 +7,7 @@ import { fetchNews } from "./data/News"; import { log } from "@/utils/logger/logger"; import { getAccounts, getSwitchToFunction } from "./utils/accounts"; import { fetchHomeworks } from "./data/HomeworksUpdate"; +import { fetchGrade } from "./data/Grades"; /** * Background fetch function that fetches all the data @@ -24,6 +25,7 @@ const backgroundFetch = async () => { await Promise.all([ fetchNews(), fetchHomeworks(), + fetchGrade(), ]); } From 3fc974813f08c481573676b62f4a04654d40dcbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 11 Jan 2025 22:30:15 +0100 Subject: [PATCH 0192/1144] =?UTF-8?q?fix(notifications):=20suppression=20d?= =?UTF-8?q?e=20la=20v=C3=A9rification=20en=20supprimant=20l'appel=20de=20l?= =?UTF-8?q?a=20fonction=20`requestNotificationPermission`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/Notifications.ts | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index 074c3c845..b9a375903 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -1,4 +1,4 @@ -import { expoGoWrapper } from "@/utils/native/expoGoAlert"; +import { isExpoGo } from "@/utils/native/expoGoAlert"; import notifee, { AuthorizationStatus, Notification, @@ -6,6 +6,8 @@ import notifee, { import { Platform } from "react-native"; const requestNotificationPermission = async () => { + if (isExpoGo()) return false; + const settings = await notifee.requestPermission(); if (Platform.OS === "ios") { if (settings.authorizationStatus >= AuthorizationStatus.AUTHORIZED) { @@ -56,24 +58,19 @@ const papillonNotify = async ( props: Notification, channelId: "News" | "Homeworks" | "Grades" ) => { - return expoGoWrapper(async () => { - const statut = await requestNotificationPermission(); - if (statut) { - // Add timestamp for Android - const timestamp = new Date().getTime(); + // Add timestamp for Android + const timestamp = new Date().getTime(); - // Display a notification - await notifee.displayNotification({ - ...props, - android: { - channelId, - timestamp, - showTimestamp: true, - smallIcon: "@mipmap/ic_launcher_foreground", - color: "#32AB8E", - }, - }); - } + // Display a notification + await notifee.displayNotification({ + ...props, + android: { + channelId, + timestamp, + showTimestamp: true, + smallIcon: "@mipmap/ic_launcher_foreground", + color: "#32AB8E", + }, }); }; From 5b9c7cc4d0d2d913e2b49f2c622595a30c2ef27e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 11 Jan 2025 22:30:58 +0100 Subject: [PATCH 0193/1144] fix(grades): suppression du tiret dans le corps de la notification des nouvelles notes --- src/background/data/Grades.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/background/data/Grades.ts b/src/background/data/Grades.ts index 42714468b..7bf29ee51 100644 --- a/src/background/data/Grades.ts +++ b/src/background/data/Grades.ts @@ -55,7 +55,7 @@ const fetchGrade = async (): Promise => { title: `[${account.name}] Nouvelles notes`, body: `${differences.length} nouvelles notes (${differences .flatMap((element) => { - return `- ${element.subjectName}`; + return element.subjectName; }) .join("/")}) ont été publiées`, ios: { From d0f06819ae61dc69cd1ecd5a1c03cfdb41a7f336 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 11 Jan 2025 23:13:41 +0100 Subject: [PATCH 0194/1144] feat(Grades/Document): show `??` instead of `-1.00` Signed-off-by: Gabriel29306 --- src/views/account/Grades/Modals/Subject.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/views/account/Grades/Modals/Subject.tsx b/src/views/account/Grades/Modals/Subject.tsx index b1dba5936..f084a0135 100644 --- a/src/views/account/Grades/Modals/Subject.tsx +++ b/src/views/account/Grades/Modals/Subject.tsx @@ -6,12 +6,12 @@ import { } from "@/components/Global/NativeComponents"; import { getSubjectData } from "@/services/shared/Subject"; import { getCourseSpeciality } from "@/utils/format/format_cours_name"; -import {AverageDiffGrade, getAverageDiffGrade} from "@/utils/grades/getAverages"; +import { AverageDiffGrade, getAverageDiffGrade } from "@/utils/grades/getAverages"; import { useTheme } from "@react-navigation/native"; import { User, UserMinus, UserPlus, Users } from "lucide-react-native"; import React, { useEffect, useLayoutEffect, useState } from "react"; import { View, ScrollView } from "react-native"; -import {Screen} from "@/router/helpers/types"; +import { Screen } from "@/router/helpers/types"; const GradeSubjectScreen: Screen<"GradeSubject"> = ({ route, navigation }) => { @@ -42,12 +42,16 @@ const GradeSubjectScreen: Screen<"GradeSubject"> = ({ route, navigation }) => { { icon: , label: "Moy. de classe", - value: parseFloat((subject.average?.classAverage?.value || -1).toString()).toFixed(2), + value: parseFloat((subject.average?.classAverage?.value || -1).toString()).toFixed(2) !== "-1.00" + ? parseFloat((subject.average?.classAverage?.value || -1).toString()).toFixed(2) + : "??", }, { icon: , label: "Moy. la plus haute", - value: parseFloat((subject.average?.max?.value || -1).toString()).toFixed(2), + value: parseFloat((subject.average?.max?.value || -1).toString()).toFixed(2) !== "-1.00" + ? parseFloat((subject.average?.max?.value || -1).toString()).toFixed(2) + : "??", }, { icon: , From da1caf1564fe871043b1e01a174b7ac8e1428816 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 11 Jan 2025 23:13:57 +0100 Subject: [PATCH 0195/1144] chore(deps): Pawnote to 1.3.6 Signed-off-by: Gabriel29306 --- package-lock.json | 12 ++++++------ package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4355f5e0d..abb873240 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "papillonvex", - "version": "7.7.0", + "version": "7.7.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "papillonvex", - "version": "7.7.0", + "version": "7.7.1", "dependencies": { "@birdwingo/react-native-reanimated-graph": "^1.1.3", "@candlefinance/app-icon": "^0.4.5", @@ -71,7 +71,7 @@ "openid-client": "^5.7.0", "pawdirecte": "^1.7.1", "pawnilim": "^0.2.0", - "pawnote": "^1.3.4", + "pawnote": "^1.3.6", "pawrd": "^0.6.1", "react": "18.2.0", "react-native": "^0.74.3", @@ -13938,9 +13938,9 @@ } }, "node_modules/pawnote": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/pawnote/-/pawnote-1.3.4.tgz", - "integrity": "sha512-8VMb3vA09z9kM9V2oMCJDNMBOK71gn9p5r2QqoGy/IE5g5Pn2vYR9gaVBmpEgahETz7mGw1mUMzXZeSm212s9w==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/pawnote/-/pawnote-1.3.6.tgz", + "integrity": "sha512-dLJUmvgUaKmX4D98KqabD6Od1Tr3R7TZl2U1AzXBgmNjoANIdMyvYRQYFbWNgVT44KSk1DRj44nqbJhZN8Exzw==", "license": "GPL-3.0-or-later", "dependencies": { "@literate.ink/utilities": "1.0.0-10641118381.1", diff --git a/package.json b/package.json index 2d5a6ce95..56460eab3 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "openid-client": "^5.7.0", "pawdirecte": "^1.7.1", "pawnilim": "^0.2.0", - "pawnote": "^1.3.4", + "pawnote": "^1.3.6", "pawrd": "^0.6.1", "react": "18.2.0", "react-native": "^0.74.3", From bc4556904cd1914700b0f28ed89950cdd1f3a032 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 11 Jan 2025 23:14:51 +0100 Subject: [PATCH 0196/1144] fix: Pressable import Signed-off-by: Gabriel29306 --- src/router/navigator/atoms/TabItem.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/router/navigator/atoms/TabItem.tsx b/src/router/navigator/atoms/TabItem.tsx index 90da917e3..8a40cdf79 100644 --- a/src/router/navigator/atoms/TabItem.tsx +++ b/src/router/navigator/atoms/TabItem.tsx @@ -1,11 +1,8 @@ import * as React from "react"; -import { useCurrentAccount } from "@/stores/account"; -import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, View, Text, Platform } from "react-native"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { useTheme } from "@react-navigation/native"; +import { StyleSheet, Platform, Pressable } from "react-native"; import LottieView from "lottie-react-native"; import colorsList from "@/utils/data/colors.json"; -import { Pressable } from "react-native-gesture-handler"; import * as Haptics from "expo-haptics"; import Reanimated, { FadeIn, FadeOut, LinearTransition, ZoomIn } from "react-native-reanimated"; import { anim2Papillon } from "@/utils/ui/animations"; From ad5509c1dab133823825b68457de20655061f8d8 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 11 Jan 2025 23:15:32 +0100 Subject: [PATCH 0197/1144] chore(dev): remove dev logs Signed-off-by: Gabriel29306 --- src/views/settings/SettingsAbout.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/views/settings/SettingsAbout.tsx b/src/views/settings/SettingsAbout.tsx index 988beeb92..6c0be7ffe 100644 --- a/src/views/settings/SettingsAbout.tsx +++ b/src/views/settings/SettingsAbout.tsx @@ -26,7 +26,6 @@ const SettingsAbout: Screen<"SettingsAbout"> = ({ navigation }) => { const fetchContributors = async () => { const fetchedContributors = await getContributors(); setContributors(fetchedContributors); - console.log(fetchedContributors[0]); }; useEffect(() => { From 4a57dd3e9e558ebacb9c026e57d51b5ccfd53df7 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 11 Jan 2025 23:15:54 +0100 Subject: [PATCH 0198/1144] lint: correct type for `AnimatedNumber` Signed-off-by: Gabriel29306 --- src/components/Global/AnimatedNumber.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Global/AnimatedNumber.tsx b/src/components/Global/AnimatedNumber.tsx index f6aff6614..72e608acc 100644 --- a/src/components/Global/AnimatedNumber.tsx +++ b/src/components/Global/AnimatedNumber.tsx @@ -15,7 +15,7 @@ interface AnimatedNumberProps { * d'animer chaque chiffre et d'avoir un * flottant fixé, par exemple. */ - value: string | any; + value: string | number; /** * Style du texte du nombre. @@ -66,4 +66,4 @@ const AnimatedNumber: React.FC = ({ ); }; -export default AnimatedNumber; \ No newline at end of file +export default AnimatedNumber; From f4ec49431394b682113ca4b3c72943dbfc682420 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 11 Jan 2025 23:16:22 +0100 Subject: [PATCH 0199/1144] fix: grade reaction in dev menu Signed-off-by: Gabriel29306 --- src/views/welcome/DevMenu.tsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/views/welcome/DevMenu.tsx b/src/views/welcome/DevMenu.tsx index cfd7cd302..6cb919bd1 100644 --- a/src/views/welcome/DevMenu.tsx +++ b/src/views/welcome/DevMenu.tsx @@ -101,10 +101,23 @@ const DevMenu: Screen<"DevMenu"> = ({ navigation }) => { navigation.navigate("NoteReaction")} + onPress={() => navigation.navigate("GradeReaction", { + grade: { + id: "devGrade", + subjectName: "Développement", + description: "Typage avec Vince", + timestamp: new Date().getTime(), + outOf: { value: 7 }, + coefficient: 7, + student: { value: 7 }, + average: { value: 7 }, + max: { value: 7 }, + min: { value: 1 } + } + })} > - NoteReaction + GradeReaction @@ -205,4 +218,4 @@ const DevMenu: Screen<"DevMenu"> = ({ navigation }) => { ); }; -export default DevMenu; \ No newline at end of file +export default DevMenu; From 7def8aa93d984f33d28c8760e2d1cbb09f658680 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 11 Jan 2025 23:17:07 +0100 Subject: [PATCH 0200/1144] feat(errors): better description for `evaluation.ts`, `grades.ts`, `homework.ts` Signed-off-by: Gabriel29306 --- src/services/evaluation.ts | 10 +++++----- src/services/grades.ts | 4 ++-- src/services/homework.ts | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/services/evaluation.ts b/src/services/evaluation.ts index 351881bd2..39aad99d9 100644 --- a/src/services/evaluation.ts +++ b/src/services/evaluation.ts @@ -1,8 +1,8 @@ import { type Account, AccountService } from "@/stores/account/types"; import type { Period } from "./shared/Period"; -import {useEvaluationStore} from "@/stores/evaluation"; -import {Evaluation} from "@/services/shared/Evaluation"; -import {error} from "@/utils/logger/logger"; +import { useEvaluationStore } from "@/stores/evaluation"; +import { Evaluation } from "@/services/shared/Evaluation"; +import { error } from "@/utils/logger/logger"; const getDefaultPeriod = (periods: Period[]): string => { const now = Date.now(); @@ -47,6 +47,6 @@ export async function updateEvaluationsInCache (account: T, useEvaluationStore.getState().updateEvaluations(periodName, evaluations); } catch (err) { - error(`not updated, see:${err}`, "updateGradesAndAveragesInCache"); + error(`evaluations not updated, see:${err}`, "updateGradesAndAveragesInCache"); } -} \ No newline at end of file +} diff --git a/src/services/grades.ts b/src/services/grades.ts index 771ef39fb..4b2f9539b 100644 --- a/src/services/grades.ts +++ b/src/services/grades.ts @@ -2,7 +2,7 @@ import { type Account, AccountService } from "@/stores/account/types"; import { useGradesStore } from "@/stores/grades"; import type { Period } from "./shared/Period"; import type { AverageOverview, Grade } from "./shared/Grade"; -import {error } from "@/utils/logger/logger"; +import { error } from "@/utils/logger/logger"; import { checkIfSkoSupported } from "./skolengo/default-personalization"; const getDefaultPeriod = (periods: Period[]): string => { @@ -126,6 +126,6 @@ export async function updateGradesAndAveragesInCache (accoun useGradesStore.getState().updateGradesAndAverages(periodName, grades, averages); } catch (err) { - error(`not updated, see:${err}`, "updateGradesAndAveragesInCache"); + error(`grades not updated, see:${err}`, "updateGradesAndAveragesInCache"); } } diff --git a/src/services/homework.ts b/src/services/homework.ts index 51512c694..e62576eb9 100644 --- a/src/services/homework.ts +++ b/src/services/homework.ts @@ -51,7 +51,7 @@ export async function updateHomeworkForWeekInCache (account: useHomeworkStore.getState().updateHomeworks(dateToEpochWeekNumber(date), homeworks); } catch (err) { - error(`not updated, see:${err}`, "updateHomeworkForWeekInCache"); + error(`homeworks not updated, see:${err}`, "updateHomeworkForWeekInCache"); } } From 914b41936670d1137324adb87a5bc62ce4c1809e Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 11 Jan 2025 23:22:42 +0100 Subject: [PATCH 0201/1144] lint Signed-off-by: Gabriel29306 --- src/components/Grades/AnimatedEmoji.tsx | 2 +- src/components/Grades/GradeModal.tsx | 6 ++-- src/router/navigator/atoms/MenuItem.tsx | 12 ++++---- src/router/navigator/menu.tsx | 12 +++----- src/router/navigator/navigator.tsx | 5 ++-- src/router/navigator/tabs.tsx | 6 ++-- src/views/account/Chat/Modals/Chat.tsx | 30 +++++++++---------- src/views/account/Grades/Document.tsx | 15 ++++------ .../actions/BackgroundIUTLannion.tsx | 1 - .../login/pronote/PronoteInstanceSelector.tsx | 1 - .../PriceDetectionOnboarding.tsx | 3 +- .../ExternalAccount/ServiceSelector.tsx | 4 +-- src/views/settings/SettingsReactions.tsx | 5 ++-- src/views/welcome/DevMenu.tsx | 2 +- 14 files changed, 42 insertions(+), 62 deletions(-) diff --git a/src/components/Grades/AnimatedEmoji.tsx b/src/components/Grades/AnimatedEmoji.tsx index 7e12350d4..5e3d9d1a4 100644 --- a/src/components/Grades/AnimatedEmoji.tsx +++ b/src/components/Grades/AnimatedEmoji.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { View, Text } from "react-native"; +import { View } from "react-native"; import Animated, { useAnimatedStyle, useSharedValue, diff --git a/src/components/Grades/GradeModal.tsx b/src/components/Grades/GradeModal.tsx index a022b3822..61c7a5f54 100644 --- a/src/components/Grades/GradeModal.tsx +++ b/src/components/Grades/GradeModal.tsx @@ -5,13 +5,11 @@ import { Image, TouchableOpacity, Text, - Platform, Alert } from "react-native"; -import { Download, Trash, Maximize2, Share, Delete } from "lucide-react-native"; +import { Download, Trash, Share } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { BlurView } from "expo-blur"; -import { ScrollView } from "react-native-gesture-handler"; import * as Sharing from "expo-sharing"; import * as FileSystem from "expo-file-system"; import * as MediaLibrary from "expo-media-library"; @@ -235,4 +233,4 @@ const GradeModal: React.FC = ({ ); }; -export default GradeModal; \ No newline at end of file +export default GradeModal; diff --git a/src/router/navigator/atoms/MenuItem.tsx b/src/router/navigator/atoms/MenuItem.tsx index 2a9b548ce..893d34827 100644 --- a/src/router/navigator/atoms/MenuItem.tsx +++ b/src/router/navigator/atoms/MenuItem.tsx @@ -1,13 +1,11 @@ import * as React from "react"; -import { useCurrentAccount } from "@/stores/account"; -import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, View, Text, Platform } from "react-native"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { useTheme } from "@react-navigation/native"; +import { StyleSheet, Platform } from "react-native"; import LottieView from "lottie-react-native"; import colorsList from "@/utils/data/colors.json"; import { Pressable } from "react-native-gesture-handler"; import * as Haptics from "expo-haptics"; -import Reanimated, { FadeIn, FadeOut, LinearTransition, ZoomIn } from "react-native-reanimated"; +import Reanimated, { FadeIn, FadeOut, LinearTransition } from "react-native-reanimated"; import { anim2Papillon } from "@/utils/ui/animations"; const MenuItem: React.FC<{ @@ -19,7 +17,7 @@ const MenuItem: React.FC<{ const theme = useTheme(); const { options } = descriptor; - const label = options.tabBarLabel !== undefined ? options.tabBarLabel : options.title !== undefined ? options.title : route.name; + const label = options.tabBarLabel !== undefined ? options.tabBarLabel : options.title ?? route.name; const onPress = () => { const event = navigation.emit({ @@ -142,4 +140,4 @@ const styles = StyleSheet.create({ }, }); -export default React.memo(MenuItem); \ No newline at end of file +export default React.memo(MenuItem); diff --git a/src/router/navigator/menu.tsx b/src/router/navigator/menu.tsx index d12793b8b..3c5331d50 100644 --- a/src/router/navigator/menu.tsx +++ b/src/router/navigator/menu.tsx @@ -1,17 +1,13 @@ -import React, { useEffect, useMemo, useState } from "react"; +import React, { useMemo, useState } from "react"; import { useCurrentAccount } from "@/stores/account"; import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, View, ScrollView, Platform, Image, Text, StatusBar } from "react-native"; +import { StyleSheet, Platform, Image, Text, StatusBar } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import TabItem from "./atoms/TabItem"; -import Reanimated, { LinearTransition, useSharedValue } from "react-native-reanimated"; +import Reanimated from "react-native-reanimated"; import MenuItem from "./atoms/MenuItem"; -import AccountSwitcher from "@/components/Home/AccountSwitcher"; import ContextMenu from "@/components/Home/AccountSwitcherContextMenu"; -import { NativeText } from "@/components/Global/NativeComponents"; import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; -import { he } from "date-fns/locale"; const PapillonNavigatorMenu: React.FC, "NavigationContent">> = ({ state, descriptors, navigation }) => { const theme = useTheme(); @@ -127,4 +123,4 @@ const styles = StyleSheet.create({ menuBarIOS: {}, }); -export default PapillonNavigatorMenu; \ No newline at end of file +export default PapillonNavigatorMenu; diff --git a/src/router/navigator/navigator.tsx b/src/router/navigator/navigator.tsx index 027e57dec..639dff1a2 100644 --- a/src/router/navigator/navigator.tsx +++ b/src/router/navigator/navigator.tsx @@ -1,8 +1,7 @@ import { BottomTabView } from "@react-navigation/bottom-tabs"; import { createNavigatorFactory, TabRouter, useNavigationBuilder } from "@react-navigation/native"; import PapillonNavigatorTabs from "./tabs"; -import { memo, useEffect, useMemo, useState } from "react"; -import { Dimensions, View } from "react-native"; +import { View } from "react-native"; import PapillonNavigatorMenu from "./menu"; import useScreenDimensions from "@/hooks/useScreenDimensions"; @@ -60,4 +59,4 @@ const BottomTabNavigator: React.ComponentType = ({ ); }; -export default createNavigatorFactory(BottomTabNavigator); \ No newline at end of file +export default createNavigatorFactory(BottomTabNavigator); diff --git a/src/router/navigator/tabs.tsx b/src/router/navigator/tabs.tsx index a82d23b88..b1ca993e3 100644 --- a/src/router/navigator/tabs.tsx +++ b/src/router/navigator/tabs.tsx @@ -1,11 +1,11 @@ import React, { useEffect, useMemo, useState } from "react"; import { useCurrentAccount } from "@/stores/account"; import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, View, Platform } from "react-native"; +import { StyleSheet, Platform } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import TabItem from "./atoms/TabItem"; -import Reanimated, { LinearTransition } from "react-native-reanimated"; +import Reanimated from "react-native-reanimated"; const PapillonNavigatorTabs: React.FC, "NavigationContent">> = ({ state, descriptors, navigation }) => { const theme = useTheme(); @@ -69,4 +69,4 @@ const styles = StyleSheet.create({ tabBarIOS: {}, }); -export default PapillonNavigatorTabs; \ No newline at end of file +export default PapillonNavigatorTabs; diff --git a/src/views/account/Chat/Modals/Chat.tsx b/src/views/account/Chat/Modals/Chat.tsx index 90f2d5538..ef761319e 100644 --- a/src/views/account/Chat/Modals/Chat.tsx +++ b/src/views/account/Chat/Modals/Chat.tsx @@ -1,30 +1,30 @@ -import React, { useEffect, useRef, useState} from "react"; -import { Image, ActivityIndicator, FlatList, ImageBackground, Platform, StyleSheet, Text, TextInput, TouchableOpacity, View, KeyboardAvoidingView,} from "react-native"; -import { useTheme} from "@react-navigation/native"; +import React, { useEffect, useRef, useState } from "react"; +import { Image, ActivityIndicator, FlatList, ImageBackground, Platform, StyleSheet, Text, TextInput, TouchableOpacity, View, KeyboardAvoidingView, } from "react-native"; +import { useTheme } from "@react-navigation/native"; -import type {Screen} from "@/router/helpers/types"; -import { NativeText,} from "@/components/Global/NativeComponents"; -import {useCurrentAccount} from "@/stores/account"; -import type {ChatMessage, ChatRecipient} from "@/services/shared/Chat"; -import {ChevronLeft, File, Link, Send} from "lucide-react-native"; +import type { Screen } from "@/router/helpers/types"; +import { NativeText, } from "@/components/Global/NativeComponents"; +import { useCurrentAccount } from "@/stores/account"; +import type { ChatMessage, ChatRecipient } from "@/services/shared/Chat"; +import { ChevronLeft, Send } from "lucide-react-native"; import parse_initials from "@/utils/format/format_pronote_initials"; import InitialIndicator from "@/components/News/InitialIndicator"; -import {PapillonModernHeader} from "@/components/Global/PapillonModernHeader"; -import {useSafeAreaInsets} from "react-native-safe-area-context"; +import { PapillonModernHeader } from "@/components/Global/PapillonModernHeader"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; import HTMLView from "react-native-htmlview"; -import Reanimated, {FadeIn, FadeInDown, FadeOut} from "react-native-reanimated"; -import {AccountService} from "@/stores/account/types"; +import Reanimated, { FadeIn, FadeInDown, FadeOut } from "react-native-reanimated"; +import { AccountService } from "@/stores/account/types"; import * as WebBrowser from "expo-web-browser"; -import {WebBrowserPresentationStyle} from "expo-web-browser"; +import { WebBrowserPresentationStyle } from "expo-web-browser"; import getAndOpenFile from "@/utils/files/getAndOpenFile"; -import {getProfileColorByName} from "@/services/local/default-personalization"; +import { getProfileColorByName } from "@/services/local/default-personalization"; import { getChatMessages, sendMessageInChat, getChatRecipients } from "@/services/chats"; import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; import MissingItem from "@/components/Global/MissingItem"; import { animPapillon } from "@/utils/ui/animations"; import GetThemeForChatId from "@/utils/chat/themes/GetThemeForChat"; import { Theme } from "@/utils/chat/themes/Themes.types"; -import {type Attachment, AttachmentType} from "@/services/shared/Attachment"; +import { type Attachment, AttachmentType } from "@/services/shared/Attachment"; import { AutoFileIcon } from "@/components/Global/FileIcon"; import LinkFavicon from "@/components/Global/LinkFavicon"; diff --git a/src/views/account/Grades/Document.tsx b/src/views/account/Grades/Document.tsx index 2e6481f20..6da2486fa 100644 --- a/src/views/account/Grades/Document.tsx +++ b/src/views/account/Grades/Document.tsx @@ -7,21 +7,16 @@ import { import { getSubjectData } from "@/services/shared/Subject"; import { useTheme } from "@react-navigation/native"; import React, { useCallback, useEffect, useLayoutEffect, useState } from "react"; -import { Image, ScrollView, Text, View, Platform, TouchableOpacity, Modal } from "react-native"; +import { Image, ScrollView, Text, View, Platform, TouchableOpacity } from "react-native"; import * as StoreReview from "expo-store-review"; import { Asterisk, Calculator, - Download, - Expand, Maximize2, Scale, - School, - SmilePlus, - Trash, - UserMinus, + School, UserMinus, UserPlus, - Users, + Users } from "lucide-react-native"; import { getAverageDiffGrade } from "@/utils/grades/getAverages"; import type { AverageDiffGrade } from "@/utils/grades/getAverages"; @@ -116,12 +111,12 @@ const GradeDocument: Screen<"GradeDocument"> = ({ route, navigation }) => { [grade], allGrades, "student" - ) as AverageDiffGrade; + ); const cD = getAverageDiffGrade( [grade], allGrades, "average" - ) as AverageDiffGrade; + ); setGradeDiff(gD); setClassDiff(cD); diff --git a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx index 6d3c1c177..8fd88a3d9 100644 --- a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx +++ b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx @@ -11,7 +11,6 @@ import { WebView } from "react-native-webview"; import type { Screen } from "@/router/helpers/types"; import { FadeInDown, FadeOutUp } from "react-native-reanimated"; import { animPapillon } from "@/utils/ui/animations"; -import { da } from "date-fns/locale"; const capitalizeFirst = (str: string) => { str = str.toLowerCase(); diff --git a/src/views/login/pronote/PronoteInstanceSelector.tsx b/src/views/login/pronote/PronoteInstanceSelector.tsx index 0ada3ff71..a2290ebfe 100644 --- a/src/views/login/pronote/PronoteInstanceSelector.tsx +++ b/src/views/login/pronote/PronoteInstanceSelector.tsx @@ -8,7 +8,6 @@ import { ActivityIndicator, Keyboard, KeyboardEvent, - Text } from "react-native"; import pronote from "pawnote"; import Reanimated, { LinearTransition, FlipInXDown, FadeInUp, FadeOutUp, ZoomIn, ZoomOut, Easing, ZoomInEasyDown } from "react-native-reanimated"; diff --git a/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx b/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx index 36bc869d0..0930cb49a 100644 --- a/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx +++ b/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx @@ -1,11 +1,10 @@ -import React, { useState } from "react"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { BellRing } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { Image, View, StyleSheet, Text } from "react-native"; import { NativeText, } from "@/components/Global/NativeComponents"; -import { useAccounts, useCurrentAccount } from "@/stores/account"; +import { useCurrentAccount } from "@/stores/account"; import { TouchableOpacity } from "react-native-gesture-handler"; import BetaIndicator from "@/components/News/Beta"; diff --git a/src/views/settings/ExternalAccount/ServiceSelector.tsx b/src/views/settings/ExternalAccount/ServiceSelector.tsx index 6f751bd9f..09fdcaf12 100644 --- a/src/views/settings/ExternalAccount/ServiceSelector.tsx +++ b/src/views/settings/ExternalAccount/ServiceSelector.tsx @@ -1,16 +1,14 @@ import React, { useState } from "react"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { SafeAreaView, useSafeAreaInsets } from "react-native-safe-area-context"; import { Image, View, StyleSheet } from "react-native"; -import { SafeAreaView } from "react-native-safe-area-context"; import Reanimated, { LinearTransition, FlipInXDown } from "react-native-reanimated"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; import { AccountService } from "@/stores/account/types"; import { useCurrentAccount } from "@/stores/account"; import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; -import { LinearGradient } from "expo-linear-gradient"; const ExternalAccountSelector: Screen<"ExternalAccountSelector"> = ({ navigation, route }) => { const theme = useTheme(); diff --git a/src/views/settings/SettingsReactions.tsx b/src/views/settings/SettingsReactions.tsx index 49400a566..100680d87 100644 --- a/src/views/settings/SettingsReactions.tsx +++ b/src/views/settings/SettingsReactions.tsx @@ -1,11 +1,10 @@ import React from "react"; -import { ScrollView, Text, View } from "react-native"; +import { ScrollView, View } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { useGradesStore } from "@/stores/grades"; import ReelGallery from "@/components/Settings/ReelGallery"; import MissingItem from "@/components/Global/MissingItem"; -import AnimatedEmoji from "@/components/Grades/AnimatedEmoji"; const SettingsReactions: Screen<"SettingsReactions"> = () => { const theme = useTheme(); @@ -40,4 +39,4 @@ const SettingsReactions: Screen<"SettingsReactions"> = () => { ); }; -export default SettingsReactions; \ No newline at end of file +export default SettingsReactions; diff --git a/src/views/welcome/DevMenu.tsx b/src/views/welcome/DevMenu.tsx index 6cb919bd1..0b36a131f 100644 --- a/src/views/welcome/DevMenu.tsx +++ b/src/views/welcome/DevMenu.tsx @@ -117,7 +117,7 @@ const DevMenu: Screen<"DevMenu"> = ({ navigation }) => { })} > - GradeReaction + GradeReaction
From 65afcea5065f58ee2e65c5e94b8f61b5334b8ba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 11 Jan 2025 23:45:37 +0100 Subject: [PATCH 0202/1144] =?UTF-8?q?feat(lessons):=20ajout=20de=20la=20ge?= =?UTF-8?q?stion=20des=20notifications=20pour=20les=20cours=20modifi=C3=A9?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/BackgroundTasks.ts | 2 + src/background/Notifications.ts | 10 +- src/background/data/Lessons.ts | 128 ++++++++++++++++++ src/background/utils/lessons.ts | 14 ++ .../Settings/NotificationContainerCard.tsx | 2 +- src/views/settings/SettingsNotifications.tsx | 12 +- 6 files changed, 160 insertions(+), 8 deletions(-) create mode 100644 src/background/data/Lessons.ts create mode 100644 src/background/utils/lessons.ts diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 607870ab8..53c92ef71 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -8,6 +8,7 @@ import { log } from "@/utils/logger/logger"; import { getAccounts, getSwitchToFunction } from "./utils/accounts"; import { fetchHomeworks } from "./data/HomeworksUpdate"; import { fetchGrade } from "./data/Grades"; +import { fetchLessons } from "./data/Lessons"; /** * Background fetch function that fetches all the data @@ -26,6 +27,7 @@ const backgroundFetch = async () => { fetchNews(), fetchHomeworks(), fetchGrade(), + fetchLessons(), ]); } diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index b9a375903..dcc0491b0 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -52,11 +52,19 @@ const createChannelNotification = async () => { description: "Te notifie lorsque tu as de nouvelles notes", sound: "default", }); + + await notifee.createChannel({ + id: "Lessons", + groupId: "Papillon", + name: "Emploi du temps", + description: "Te notifie lorsque ton emploi du temps du jour est modifié", + sound: "default", + }); }; const papillonNotify = async ( props: Notification, - channelId: "News" | "Homeworks" | "Grades" + channelId: "News" | "Homeworks" | "Grades" | "Lessons" ) => { // Add timestamp for Android const timestamp = new Date().getTime(); diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts new file mode 100644 index 000000000..0d637dea7 --- /dev/null +++ b/src/background/data/Lessons.ts @@ -0,0 +1,128 @@ +import { getCurrentAccount } from "../utils/accounts"; +import { papillonNotify } from "../Notifications"; +import { getLessons, updateLessonsState } from "../utils/lessons"; +import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; +import { Timetable, TimetableClassStatus } from "@/services/shared/Timetable"; + +const getAllLessonsForDay = (lessons: Record) => { + const date = new Date(); + const week = dateToEpochWeekNumber(date); + const timetable = lessons[week] || []; + + const newDate = new Date(date); + newDate.setHours(0, 0, 0, 0); + + const day = timetable.filter((lesson) => { + const lessonDate = new Date(lesson.startTimestamp); + lessonDate.setHours(0, 0, 0, 0); + + return lessonDate.getTime() === newDate.getTime(); + }); + + return day; +}; + +const fetchLessons = async (): Promise => { + const account = getCurrentAccount(); + const notificationsTypesPermissions = account.personalization.notifications; + + const weekNumber = dateToEpochWeekNumber(new Date()); + await updateLessonsState(account, weekNumber); + const updatedLessons = getLessons(); + const lessonsDay = getAllLessonsForDay(updatedLessons); + const lessonsEvent = lessonsDay.filter((element) => element.statusText); + + // Notifie 15 minutes avant le début du 1er cours + // De base, je voulais faire 1 heure avant, mais la notif sera modifiée à chaque fois + // comme la fréquence du background est de 15 minutes + const now = new Date().getTime(); + const oneHourBefore = lessonsDay[0]?.startTimestamp - 15 * 60 * 1000; + + if ( + notificationsTypesPermissions?.enabled && + notificationsTypesPermissions?.timetable && + now >= oneHourBefore && + now < lessonsDay[0]?.startTimestamp + ) { + switch (lessonsEvent.length) { + case 0: + break; + case 1: + const dateLessonsDebut = `${new Date( + lessonsEvent[0].startTimestamp + ).getHours()}h${new Date( + lessonsEvent[0].startTimestamp + ).getMinutes()}min`; + + const dateLessonsFin = `${new Date( + lessonsEvent[0].endTimestamp + ).getHours()}h${new Date( + lessonsEvent[0].endTimestamp + ).getMinutes()}min`; + + let statut: string = ""; + + switch (lessonsEvent[0].status) { + case TimetableClassStatus.CANCELED: + statut = "a été annulé"; + break; + case TimetableClassStatus.TEST: + statut = "est un devoir surveillé"; + case TimetableClassStatus.MODIFIED: + statut = + "est un cours modifié, consulte l'emploi du temps pour plus de détails"; + default: + if (lessonsEvent[0].statusText === "Changement de Salle") { + statut = "a un changement de salle ! "; + if (lessonsEvent[0].room) { + if (lessonsEvent[0].room.includes(",")) { + statut += + "Consulte l'emploi du temps pour regarder les salles de cours"; + } else { + statut += `Tu dois aller en salle ${lessonsEvent[0].room}`; + } + } + } else { + statut = `a un statut : ${lessonsEvent[0].statusText}`; + } + break; + } + + papillonNotify( + { + id: `${account.name}-lessons`, + title: `[${account.name}] Emploi du temps du jour`, + // subtitle: lessonsEvent[0].title, + body: `Le cours de ${lessonsEvent[0].title} (${dateLessonsDebut}-${dateLessonsFin}) ${statut}`, + ios: { + categoryId: account.name, + }, + }, + "Lessons" + ); + break; + default: + papillonNotify( + { + id: `${account.name}-lessons`, + title: `[${account.name}] Emploi du temps du jour`, + body: `Les cours suivants ont été modifiés, consulte l'emploi du temps pour plus de détails.
+ ${lessonsEvent + .flatMap((element) => { + return `- ${element.title ?? "Sans titre"}`; + }) + .join("
")}`, + ios: { + categoryId: account.name, + }, + }, + "Lessons" + ); + break; + } + } + + return lessonsEvent; +}; + +export { fetchLessons }; diff --git a/src/background/utils/lessons.ts b/src/background/utils/lessons.ts new file mode 100644 index 000000000..b1c3b4f6d --- /dev/null +++ b/src/background/utils/lessons.ts @@ -0,0 +1,14 @@ +import { PrimaryAccount } from "@/stores/account/types"; +import { useTimetableStore } from "@/stores/timetable"; +import { updateTimetableForWeekInCache } from "@/services/timetable"; + +export const getLessons = () => { + return useTimetableStore.getState().timetables; +}; + +export const updateLessonsState = async ( + account: PrimaryAccount, + weekNumber: number +) => { + await updateTimetableForWeekInCache(account, weekNumber, true); +}; diff --git a/src/components/Settings/NotificationContainerCard.tsx b/src/components/Settings/NotificationContainerCard.tsx index ffd66ba51..700039c18 100644 --- a/src/components/Settings/NotificationContainerCard.tsx +++ b/src/components/Settings/NotificationContainerCard.tsx @@ -122,7 +122,7 @@ const NotificationContainerCard = ({ - Tu as cours en salle B03 avec M. Perruche dans 5 minutes. + Le cours de géographie (16h-17h) a un changement de salle ! Tu dois aller en salle B106 diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 025a7055c..7ad0dc201 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -86,12 +86,12 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ // Schoolary notifications const notificationSchoolary = [ - // { - // icon: } color={colors.primary} />, - // title: "Modification de cours", - // message: "Cours de mathématiques annulé dans 10 minutes", - // personalizationValue: "timeTable", - // }, + { + icon: } color={colors.primary} />, + title: "Modification de cours", + message: "Le cours de mathématiques (10h-11h) a été annulé", + personalizationValue: "timetable", + }, { icon: } color={colors.primary} />, title: "Nouveau devoir", From 48991668748b6dfa622bdadcfc664d16149299cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 12 Jan 2025 00:49:18 +0100 Subject: [PATCH 0203/1144] =?UTF-8?q?fix:=20Am=C3=A9lioration=20du=20workf?= =?UTF-8?q?low=20de=20v=C3=A9rification=20en=20combinant=20les=20logs=20Ty?= =?UTF-8?q?peScript=20et=20ESLint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/checks.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 017b40e5b..4ad293763 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -26,7 +26,9 @@ jobs: - name: 🔍 Run TypeScript and ESLint checks id: lint run: | - npm run lint > lint.log 2>&1 + tsc > tsc.log 2>&1 || true + eslint . > eslint.log 2>&1 || true + cat tsc.log eslint.log > lint.log continue-on-error: true - name: 👁️‍🗨️ Comment on Pull Request @@ -46,4 +48,4 @@ jobs: - name: 🧹 Clean up if: always() - run: rm -f lint.log + run: rm -f lint.log tsc.log eslint.log From 8f6021c993220979c1e6398e9e97da83055a3567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 12 Jan 2025 00:58:02 +0100 Subject: [PATCH 0204/1144] =?UTF-8?q?fix:=20Ajout=20de=20v=C3=A9rification?= =?UTF-8?q?s=20TypeScript=20et=20ESLint=20pour=20les=20branches=20main=20e?= =?UTF-8?q?t=20development=20du=20repo=20principal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/checks.yml | 67 +++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 12 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 4ad293763..8e22657bd 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -1,15 +1,60 @@ name: Checks (TypeScript + ESLint) on: + push: + branches: + - main + - development pull_request: - branches: [ "main" ] + branches: + - main + - development jobs: - lint: - permissions: write-all - name: 🛠️ TypeScript and ESLint + lint-main: + name: 🛠️ TypeScript and ESLint on Main runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' + steps: + - name: 📥 Checkout code + uses: actions/checkout@v3 + - name: ⚙️ Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: 18 + cache: 'npm' + + - name: 💾 Install dependencies + run: npm ci + + - name: 🔍 Run TypeScript and ESLint checks + run: | + tsc > tsc.log 2>&1 || true + eslint . > eslint.log 2>&1 || true + cat tsc.log eslint.log > lint.log + continue-on-error: true + + - name: 📄 Output Logs + run: cat lint.log + + - name: 🛠️ Commit and Push Fixes (Main) + if: success() || failure() + run: | + git config user.name "GitHub Actions" + git config user.email "actions@github.com" + if git diff --quiet; then + echo "No changes to commit." + else + git add . + git commit -m "🔧 Auto-fix ESLint issues" + git push + fi + + lint-development: + name: 🛠️ TypeScript and ESLint on Development + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/development' steps: - name: 📥 Checkout code uses: actions/checkout@v3 @@ -24,15 +69,17 @@ jobs: run: npm ci - name: 🔍 Run TypeScript and ESLint checks - id: lint run: | tsc > tsc.log 2>&1 || true eslint . > eslint.log 2>&1 || true cat tsc.log eslint.log > lint.log continue-on-error: true - - name: 👁️‍🗨️ Comment on Pull Request - if: steps.lint.outcome == 'failure' + - name: 📄 Output Logs + run: cat lint.log + + - name: 👁️‍🗨️ Comment on Pull Request (Development) + if: github.event_name == 'pull_request' && failure() uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} @@ -43,9 +90,5 @@ jobs: issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, - body: `⚠️ Erreur(s) détectée(s) par TypeScript/ESLint !\n\`\`\`\n${lintLogContent}\n\`\`\`` + body: `⚠️ Erreur(s) détectée(s) sur la branche development !\n\`\`\`\n${lintLogContent}\n\`\`\`` }); - - - name: 🧹 Clean up - if: always() - run: rm -f lint.log tsc.log eslint.log From 3c1348181badded13de331de8d2fc2b018d50316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 12 Jan 2025 01:02:38 +0100 Subject: [PATCH 0205/1144] =?UTF-8?q?fix:=20Mise=20=C3=A0=20jour=20du=20wo?= =?UTF-8?q?rkflow=20de=20v=C3=A9rification=20pour=20cibler=20uniquement=20?= =?UTF-8?q?la=20branche=20main=20et=20les=20pull=20requests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/checks.yml | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 8e22657bd..c9f079ba6 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -2,19 +2,15 @@ name: Checks (TypeScript + ESLint) on: push: - branches: - - main - - development + branches: [ "main" ] pull_request: - branches: - - main - - development + branches: [ "main" ] jobs: lint-main: name: 🛠️ TypeScript and ESLint on Main runs-on: ubuntu-latest - if: github.ref == 'refs/heads/main' + if: github.ref == 'refs/remotes/origin/main' steps: - name: 📥 Checkout code uses: actions/checkout@v3 @@ -52,9 +48,9 @@ jobs: fi lint-development: - name: 🛠️ TypeScript and ESLint on Development + name: 🛠️ TypeScript and ESLint on Pull Request runs-on: ubuntu-latest - if: github.ref == 'refs/heads/development' + if: github.ref != 'refs/remotes/origin/main' steps: - name: 📥 Checkout code uses: actions/checkout@v3 From cea69301e3bdbf0af6c0b5cc231ab8ccc03eefa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 12 Jan 2025 01:06:16 +0100 Subject: [PATCH 0206/1144] =?UTF-8?q?fix:=20Mise=20=C3=A0=20jour=20de=20la?= =?UTF-8?q?=20condition=20d'affichage=20des=20erreurs=20dans=20le=20workfl?= =?UTF-8?q?ow=20de=20v=C3=A9rification?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/checks.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index c9f079ba6..1ede77e5c 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -75,7 +75,7 @@ jobs: run: cat lint.log - name: 👁️‍🗨️ Comment on Pull Request (Development) - if: github.event_name == 'pull_request' && failure() + if: steps.lint.outcome == 'failure' uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} @@ -86,5 +86,5 @@ jobs: issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, - body: `⚠️ Erreur(s) détectée(s) sur la branche development !\n\`\`\`\n${lintLogContent}\n\`\`\`` + body: `⚠️ Erreur(s) détectée(s) par TypeScript/ESLint !\n\`\`\`\n${lintLogContent}\n\`\`\`` }); From b0d6fd8c040e36a2dcc21a801e8aac4b6e582066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 12 Jan 2025 01:08:15 +0100 Subject: [PATCH 0207/1144] fix: Change to `npm i` to use cache of Node --- .github/workflows/checks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 1ede77e5c..e956dedd4 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -62,7 +62,7 @@ jobs: cache: 'npm' - name: 💾 Install dependencies - run: npm ci + run: npm i - name: 🔍 Run TypeScript and ESLint checks run: | From c7630d7b71de31b524e565d6619b01d424c53bf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 12 Jan 2025 01:08:32 +0100 Subject: [PATCH 0208/1144] =?UTF-8?q?fix:=20Utilisation=20de=20npx=20pour?= =?UTF-8?q?=20ex=C3=A9cuter=20les=20v=C3=A9rifications=20TypeScript=20et?= =?UTF-8?q?=20ESLint=20dans=20le=20workflow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/checks.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index e956dedd4..76daaf0ef 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -66,8 +66,8 @@ jobs: - name: 🔍 Run TypeScript and ESLint checks run: | - tsc > tsc.log 2>&1 || true - eslint . > eslint.log 2>&1 || true + npx tsc > tsc.log 2>&1 || true + npx eslint . > eslint.log 2>&1 || true cat tsc.log eslint.log > lint.log continue-on-error: true From 765a8c03df4fa82cebd9e7a04427a7e84c70d7cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 12 Jan 2025 01:10:17 +0100 Subject: [PATCH 0209/1144] =?UTF-8?q?fix:=20Mise=20=C3=A0=20jour=20du=20co?= =?UTF-8?q?mmentaire=20sur=20la=20Pull=20Request=20pour=20inclure=20toutes?= =?UTF-8?q?=20les=20erreurs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/checks.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 76daaf0ef..e93d543f2 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -74,8 +74,8 @@ jobs: - name: 📄 Output Logs run: cat lint.log - - name: 👁️‍🗨️ Comment on Pull Request (Development) - if: steps.lint.outcome == 'failure' + - name: 👁️‍🗨️ Comment on Pull Request + if: failure() uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} From b069891a8651e309b6068ee9cd3b141f9730a45b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 12 Jan 2025 01:21:35 +0100 Subject: [PATCH 0210/1144] =?UTF-8?q?fix:=20Ajout=20d'une=20=C3=A9tape=20d?= =?UTF-8?q?e=20nettoyage=20pour=20supprimer=20les=20fichiers=20de=20log=20?= =?UTF-8?q?dans=20le=20workflow=20de=20v=C3=A9rification?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/checks.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index e93d543f2..bd13f159b 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -46,6 +46,10 @@ jobs: git commit -m "🔧 Auto-fix ESLint issues" git push fi + + - name: 🧹 Clean up + if: always() + run: rm -f lint.log tsc.log eslint.log lint-development: name: 🛠️ TypeScript and ESLint on Pull Request @@ -75,7 +79,9 @@ jobs: run: cat lint.log - name: 👁️‍🗨️ Comment on Pull Request - if: failure() + if: env.LINT_EMPTY != 'true' + env: + LINT_EMPTY: ${{ steps.check_logs.outputs.is_empty }} uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} @@ -88,3 +94,7 @@ jobs: repo: context.repo.repo, body: `⚠️ Erreur(s) détectée(s) par TypeScript/ESLint !\n\`\`\`\n${lintLogContent}\n\`\`\`` }); + + - name: 🧹 Clean up + if: always() + run: rm -f lint.log tsc.log eslint.log From bc95266c2b598dc31a285dde50ff2212057aacc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 12 Jan 2025 01:21:56 +0100 Subject: [PATCH 0211/1144] =?UTF-8?q?retour=20=C3=A0=20npm=20ci?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/checks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index bd13f159b..e9c82173e 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -66,7 +66,7 @@ jobs: cache: 'npm' - name: 💾 Install dependencies - run: npm i + run: npm ci - name: 🔍 Run TypeScript and ESLint checks run: | From bbf9b32931cf579cab25f65ed8e63f0b77180b85 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sun, 12 Jan 2025 12:10:11 +0100 Subject: [PATCH 0212/1144] feat(Grades/Subject): Show `N.Not` instead of `N/A` Signed-off-by: Gabriel29306 --- src/views/account/Grades/Subject/SubjectTitle.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/views/account/Grades/Subject/SubjectTitle.tsx b/src/views/account/Grades/Subject/SubjectTitle.tsx index 86391531b..37c800a5b 100644 --- a/src/views/account/Grades/Subject/SubjectTitle.tsx +++ b/src/views/account/Grades/Subject/SubjectTitle.tsx @@ -5,9 +5,9 @@ import { useTheme } from "@react-navigation/native"; import React, { useEffect } from "react"; import { View } from "react-native"; import { TouchableOpacity } from "react-native-gesture-handler"; -import {type RouteParameters} from "@/router/helpers/types"; -import type {NativeStackNavigationProp} from "@react-navigation/native-stack"; -import type {Grade, GradesPerSubject} from "@/services/shared/Grade"; +import { type RouteParameters } from "@/router/helpers/types"; +import type { NativeStackNavigationProp } from "@react-navigation/native-stack"; +import type { Grade, GradesPerSubject } from "@/services/shared/Grade"; import { getSubjectAverage } from "@/utils/grades/getAverages"; type SubjectTitleParameters = { @@ -98,7 +98,13 @@ const SubjectTitle = ({ navigation, subject, subjectData, allGrades }: SubjectTi }} > Date: Sun, 12 Jan 2025 12:37:51 +0100 Subject: [PATCH 0213/1144] fix(Grade/Graph): ignore -1 in grade history Signed-off-by: Gabriel29306 --- src/utils/grades/getAverages.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/utils/grades/getAverages.ts b/src/utils/grades/getAverages.ts index 717875670..8a8d306ae 100644 --- a/src/utils/grades/getAverages.ts +++ b/src/utils/grades/getAverages.ts @@ -208,9 +208,7 @@ const getAveragesHistory = ( }); // remove NaN values - return history.filter((x) => !isNaN(x.value)); - - return history; // Retourner l'historique généré + return history.filter((x) => !isNaN(x.value) || x.value < 0); } catch(e) { return []; From a3fbe49d9a1dd96584ea499695c371208f0ceb66 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sun, 12 Jan 2025 12:40:25 +0100 Subject: [PATCH 0214/1144] fix(Grades/SubjectDocument): Show `N.Not` instead of `-1.00` Signed-off-by: Gabriel29306 --- src/views/account/Grades/Modals/Subject.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/views/account/Grades/Modals/Subject.tsx b/src/views/account/Grades/Modals/Subject.tsx index f084a0135..d6b6c679f 100644 --- a/src/views/account/Grades/Modals/Subject.tsx +++ b/src/views/account/Grades/Modals/Subject.tsx @@ -37,7 +37,9 @@ const GradeSubjectScreen: Screen<"GradeSubject"> = ({ route, navigation }) => { { icon: , label: "Ta moyenne", - value: parseFloat((subject.average?.average?.value || -1).toString()).toFixed(2), + value: parseFloat((subject.average?.average?.value || -1).toString()).toFixed(2) !== "-1.00" + ? parseFloat((subject.average?.average?.value || -1).toString()).toFixed(2) + : "N.Not", }, { icon: , From e0dfcc229682d6560792ceb5b2e05af1d5120765 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sun, 12 Jan 2025 15:48:44 +0100 Subject: [PATCH 0215/1144] lint: code simplification Signed-off-by: Gabriel29306 --- src/router/index.tsx | 10 +++++----- src/router/navigator/atoms/MenuItem.tsx | 2 +- src/router/navigator/atoms/TabItem.tsx | 2 +- src/views/account/Attendance/Attendance.tsx | 2 +- src/views/account/Grades/Grades.tsx | 2 +- src/views/account/News/Atoms/Item.tsx | 2 +- src/views/settings/SettingsProfile.tsx | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/router/index.tsx b/src/router/index.tsx index bc31b96e2..2253da5b7 100644 --- a/src/router/index.tsx +++ b/src/router/index.tsx @@ -1,7 +1,7 @@ import React, { useEffect } from "react"; import { createNativeStackNavigator } from "@react-navigation/native-stack"; -import {NavigationContainer, NavigationState, PartialState, Theme} from "@react-navigation/native"; -import {Platform, StatusBar, View, useColorScheme } from "react-native"; +import { NavigationContainer, NavigationState, PartialState, Theme } from "@react-navigation/native"; +import { Platform, StatusBar, View, useColorScheme } from "react-native"; import * as Linking from "expo-linking"; import screens from "@/router/screens"; import type { RouteParameters } from "@/router/helpers/types"; @@ -13,7 +13,7 @@ import * as NavigationBar from "expo-navigation-bar"; import { GestureHandlerRootView } from "react-native-gesture-handler"; import { useCurrentAccount } from "@/stores/account"; import { navigatorScreenOptions } from "./helpers/create-screen"; -import {navigate} from "@/utils/logger/logger"; +import { navigate } from "@/utils/logger/logger"; import { PapillonNavigation } from "./refs"; import AsyncStorage from "@react-native-async-storage/async-storage"; @@ -74,9 +74,9 @@ const Router: React.FC = () => { const account = useCurrentAccount(store => store.account!); - if (account && account.personalization?.color !== undefined) { + if (account?.personalization?.color) { - if (account.personalization?.color?.hex?.primary !== undefined) { + if (account.personalization?.color?.hex?.primary) { theme.colors.primary = account.personalization.color.hex.primary; } } diff --git a/src/router/navigator/atoms/MenuItem.tsx b/src/router/navigator/atoms/MenuItem.tsx index 893d34827..f53a74ad5 100644 --- a/src/router/navigator/atoms/MenuItem.tsx +++ b/src/router/navigator/atoms/MenuItem.tsx @@ -17,7 +17,7 @@ const MenuItem: React.FC<{ const theme = useTheme(); const { options } = descriptor; - const label = options.tabBarLabel !== undefined ? options.tabBarLabel : options.title ?? route.name; + const label = options.tabBarLabel ?? options.title ?? route.name; const onPress = () => { const event = navigation.emit({ diff --git a/src/router/navigator/atoms/TabItem.tsx b/src/router/navigator/atoms/TabItem.tsx index 8a40cdf79..c750bd6f0 100644 --- a/src/router/navigator/atoms/TabItem.tsx +++ b/src/router/navigator/atoms/TabItem.tsx @@ -17,7 +17,7 @@ const TabItem: React.FC<{ const theme = useTheme(); const { options } = descriptor; - const label = options.tabBarLabel !== undefined ? options.tabBarLabel : options.title !== undefined ? options.title : route.name; + const label: string = options.tabBarLabel ?? options.title ?? route.name; const onPress = () => { const event = navigation.emit({ diff --git a/src/views/account/Attendance/Attendance.tsx b/src/views/account/Attendance/Attendance.tsx index bd82ccca0..c2f376b26 100644 --- a/src/views/account/Attendance/Attendance.tsx +++ b/src/views/account/Attendance/Attendance.tsx @@ -231,7 +231,7 @@ const Attendance: Screen<"Attendance"> = ({ route, navigation }) => { refreshing={isRefreshing} onRefresh={() => { setIsRefreshing(true); - if(account.identityProvider?.identifier !== undefined) { + if(account.identityProvider?.identifier) { navigation.navigate("BackgroundIdentityProvider"); updateAttendanceInCache(account, selectedPeriod).then(() => setIsRefreshing(false)); } diff --git a/src/views/account/Grades/Grades.tsx b/src/views/account/Grades/Grades.tsx index 0ca372969..0b0d04f71 100644 --- a/src/views/account/Grades/Grades.tsx +++ b/src/views/account/Grades/Grades.tsx @@ -89,7 +89,7 @@ const Grades: Screen<"Grades"> = ({ route, navigation }) => { setIsLoading(true); await updateData(); - if(isRefreshing && account.identityProvider?.identifier !== undefined) { + if(isRefreshing && account.identityProvider?.identifier) { navigation.navigate("BackgroundIdentityProvider"); } diff --git a/src/views/account/News/Atoms/Item.tsx b/src/views/account/News/Atoms/Item.tsx index e67293df0..c20d4fbc9 100644 --- a/src/views/account/News/Atoms/Item.tsx +++ b/src/views/account/News/Atoms/Item.tsx @@ -25,7 +25,7 @@ const NewsListItem: React.FC = ({ index, message, navigation, onPress={() => { navigation.navigate("NewsItem", { message: JSON.stringify(message), - important: message.important !== undefined, + important: !!message.important, isED }); }} diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index 7900011d1..fc2354cf6 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -274,7 +274,7 @@ const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { - {account.identity && Object.keys(account.identity) !== undefined && Object.keys(account.identity).length > 0 && ( + {Object.keys(account.identity ?? {})?.length > 0 && ( = ({ navigation }) => { ); }; -export default SettingsProfile; \ No newline at end of file +export default SettingsProfile; From 193b4342e117641e73ef0221e723e5262def47d4 Mon Sep 17 00:00:00 2001 From: camarm-dev Date: Sun, 12 Jan 2025 21:33:32 +0100 Subject: [PATCH 0216/1144] fix: make authentication value persistant for associated accounts --- src/stores/account/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index f3d7d2551..11eb6482c 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -133,6 +133,8 @@ export const useCurrentAccount = create()((set, get) => ({ }); associatedAccount.instance = instance; associatedAccount.authentication = authentication; + // Persist authentification value (f.e if token was renewed, it's important to make it persistant) + useAccounts.getState().update(associatedAccount.localID, "authentication", authentication); log("reloaded associated account", "[switchTo]"); } From 0c69a5185806b60e8c14994d4730d5360cb414c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Mon, 13 Jan 2025 18:13:22 +0100 Subject: [PATCH 0217/1144] =?UTF-8?q?fix:=20Suppression=20qui=20posait=20p?= =?UTF-8?q?robl=C3=A8me?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/epochWeekNumber.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/epochWeekNumber.ts b/src/utils/epochWeekNumber.ts index 406f1b8f6..10de59d99 100644 --- a/src/utils/epochWeekNumber.ts +++ b/src/utils/epochWeekNumber.ts @@ -52,7 +52,7 @@ export const dateToEpochWeekNumber = (date: Date): number => { const commonDay = dayToWeekCommonDay(date); const epochWeekNumber = Math.floor((commonDay.getTime() + EPOCH_WN_CONFIG.adjustEpochInitialDate - ( (EPOCH_WN_CONFIG.setMiddleDay - 1) /7 ) * EPOCH_WN_CONFIG.numberOfMsInAWeek) / EPOCH_WN_CONFIG.numberOfMsInAWeek); // this is the opposite of the weekNumberToMiddleDate function - return epochWeekNumber + 1; + return epochWeekNumber; }; /** From 0442f3dc4afd7748e0720ee363a61185de2dd2ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Mon, 13 Jan 2025 18:16:50 +0100 Subject: [PATCH 0218/1144] fix: Optimisation du code --- src/background/data/Lessons.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index 0d637dea7..0c4d273cc 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -6,17 +6,15 @@ import { Timetable, TimetableClassStatus } from "@/services/shared/Timetable"; const getAllLessonsForDay = (lessons: Record) => { const date = new Date(); + date.setHours(0, 0, 0, 0); const week = dateToEpochWeekNumber(date); const timetable = lessons[week] || []; - const newDate = new Date(date); - newDate.setHours(0, 0, 0, 0); - const day = timetable.filter((lesson) => { const lessonDate = new Date(lesson.startTimestamp); lessonDate.setHours(0, 0, 0, 0); - return lessonDate.getTime() === newDate.getTime(); + return lessonDate.getTime() === date.getTime(); }); return day; From beb0d5e2f49a64344a47589b7442e08107b616e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Mon, 13 Jan 2025 18:17:17 +0100 Subject: [PATCH 0219/1144] fix: Correction de la casse dans le message de changement de salle --- src/background/data/Lessons.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index 0c4d273cc..cd72efd6b 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -70,7 +70,7 @@ const fetchLessons = async (): Promise => { statut = "est un cours modifié, consulte l'emploi du temps pour plus de détails"; default: - if (lessonsEvent[0].statusText === "Changement de Salle") { + if (lessonsEvent[0].statusText === "Changement de salle") { statut = "a un changement de salle ! "; if (lessonsEvent[0].room) { if (lessonsEvent[0].room.includes(",")) { From 5f958d1f578a78b8b9de4c8c33f54aa7ee478541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Mon, 13 Jan 2025 18:19:15 +0100 Subject: [PATCH 0220/1144] =?UTF-8?q?fix:=20Suppression=20de=20la=20valeur?= =?UTF-8?q?=20par=20d=C3=A9faut=20"Sans=20titre"=20dans=20le=20message=20d?= =?UTF-8?q?e=20notification=20des=20cours=20modifi=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Lessons.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index cd72efd6b..b278da9f4 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -107,7 +107,7 @@ const fetchLessons = async (): Promise => { body: `Les cours suivants ont été modifiés, consulte l'emploi du temps pour plus de détails.
${lessonsEvent .flatMap((element) => { - return `- ${element.title ?? "Sans titre"}`; + return `- ${element.title}`; }) .join("
")}`, ios: { From 8962fa95b93c1b22f7b8cf9f5045e4cfa0551c32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Mon, 13 Jan 2025 18:39:16 +0100 Subject: [PATCH 0221/1144] =?UTF-8?q?fix:=20Formatage=20des=20heures=20de?= =?UTF-8?q?=20d=C3=A9but=20et=20de=20fin=20des=20le=C3=A7ons=20avec=20des?= =?UTF-8?q?=20z=C3=A9ros=20=C3=A0=20gauche?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Lessons.ts | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index b278da9f4..13809780e 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -46,17 +46,21 @@ const fetchLessons = async (): Promise => { case 0: break; case 1: - const dateLessonsDebut = `${new Date( - lessonsEvent[0].startTimestamp - ).getHours()}h${new Date( - lessonsEvent[0].startTimestamp - ).getMinutes()}min`; + const dateLessonsDebut = `${new Date(lessonsEvent[0].startTimestamp) + .getHours() + .toString() + .padStart(2, "0")}:${new Date(lessonsEvent[0].startTimestamp) + .getMinutes() + .toString() + .padStart(2, "0")}`; - const dateLessonsFin = `${new Date( - lessonsEvent[0].endTimestamp - ).getHours()}h${new Date( - lessonsEvent[0].endTimestamp - ).getMinutes()}min`; + const dateLessonsFin = `${new Date(lessonsEvent[0].endTimestamp) + .getHours() + .toString() + .padStart(2, "0")}:${new Date(lessonsEvent[0].endTimestamp) + .getMinutes() + .toString() + .padStart(2, "0")}`; let statut: string = ""; From fa822ed0f1758c886ee54d983d8d410a8df83577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Mon, 13 Jan 2025 19:00:53 +0100 Subject: [PATCH 0222/1144] =?UTF-8?q?fix:=20Ajout=20d'une=20alerte=20d'inf?= =?UTF-8?q?ormation=20sur=20la=20progression=20dint=C3=A9gration=20pour=20?= =?UTF-8?q?les=20notifications=20d'emploi=20du=20temps?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/SettingsNotifications.tsx | 47 ++++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 7ad0dc201..99a3b7136 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -1,12 +1,13 @@ import React, { useEffect, useState } from "react"; -import { ScrollView, Switch } from "react-native"; +import { Alert, Platform, ScrollView, Switch } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { CalendarCheck, BookCheck, TrendingUp, - Newspaper + Newspaper, + Info } from "lucide-react-native"; import { useSharedValue, withTiming } from "react-native-reanimated"; import { @@ -20,6 +21,8 @@ import NotificationContainerCard from "@/components/Settings/NotificationContain import { createChannelNotification, requestNotificationPermission } from "@/background/Notifications"; import { alertExpoGo, isExpoGo } from "@/utils/native/expoGoAlert"; import { useCurrentAccount } from "@/stores/account"; +import { PressableScale } from "react-native-pressable-scale"; +import { useAlert } from "@/providers/AlertProvider"; const SettingsNotifications: Screen<"SettingsNotifications"> = ({ navigation @@ -118,6 +121,8 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ }, ]; + const { showAlert } = useAlert(); + return ( = ({ /> } > - {notification.title} + + {notification.title} + {notification.personalizationValue === "timetable" && ( + { + if (Platform.OS === "ios") { + Alert.alert("Information", "Pour le moment, tu es prévenu de ton emploi du temps uniquement 15 minutes avant le 1er cours de la journée.", [ + { + text: "OK", + }, + ]); + } else { + showAlert({ + title: "Information", + message: "Pour le moment, tu es prévenu de ton emploi du temps uniquement 15 minutes avant le 1er cours de la journée.", + actions: [ + { + title: "OK", + onPress: () => {}, + backgroundColor: theme.colors.card, + }, + ], + }); + } + }} + > + + + )} + Date: Mon, 13 Jan 2025 19:02:15 +0100 Subject: [PATCH 0223/1144] fix: Correction du format des heures dans le message de changement de salle --- src/components/Settings/NotificationContainerCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Settings/NotificationContainerCard.tsx b/src/components/Settings/NotificationContainerCard.tsx index 700039c18..828479974 100644 --- a/src/components/Settings/NotificationContainerCard.tsx +++ b/src/components/Settings/NotificationContainerCard.tsx @@ -122,7 +122,7 @@ const NotificationContainerCard = ({ - Le cours de géographie (16h-17h) a un changement de salle ! Tu dois aller en salle B106 + Le cours de géographie (16:00-17:00) a un changement de salle ! Tu dois aller en salle B106 From cff83684a6823e7a3eab36bfc0571ead1660aa08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Mon, 13 Jan 2025 19:10:16 +0100 Subject: [PATCH 0224/1144] =?UTF-8?q?suppression=20notification=20sur=20le?= =?UTF-8?q?s=20devoirs=20=C3=A0=20faire=20pour=20le=20lendemain=20(perso?= =?UTF-8?q?=20j'aime=20pas)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/BackgroundTasks.ts | 2 +- src/background/data/{HomeworksUpdate.ts => Homeworks.ts} | 4 ++-- src/background/utils/{homeworksUpdate.ts => homeworks.ts} | 0 src/stores/account/types.ts | 1 - src/views/settings/SettingsNotifications.tsx | 8 +------- 5 files changed, 4 insertions(+), 11 deletions(-) rename src/background/data/{HomeworksUpdate.ts => Homeworks.ts} (97%) rename src/background/utils/{homeworksUpdate.ts => homeworks.ts} (100%) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 53c92ef71..58187e17d 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -6,7 +6,7 @@ import { expoGoWrapper } from "@/utils/native/expoGoAlert"; import { fetchNews } from "./data/News"; import { log } from "@/utils/logger/logger"; import { getAccounts, getSwitchToFunction } from "./utils/accounts"; -import { fetchHomeworks } from "./data/HomeworksUpdate"; +import { fetchHomeworks } from "./data/Homeworks"; import { fetchGrade } from "./data/Grades"; import { fetchLessons } from "./data/Lessons"; diff --git a/src/background/data/HomeworksUpdate.ts b/src/background/data/Homeworks.ts similarity index 97% rename from src/background/data/HomeworksUpdate.ts rename to src/background/data/Homeworks.ts index 8be21b5b9..4d4d0254e 100644 --- a/src/background/data/HomeworksUpdate.ts +++ b/src/background/data/Homeworks.ts @@ -1,7 +1,7 @@ import { getCurrentAccount } from "../utils/accounts"; import { papillonNotify } from "../Notifications"; import parse_homeworks_resume from "@/utils/format/format_pronote_news"; -import { getHomeworks, updateHomeworksState } from "../utils/homeworksUpdate"; +import { getHomeworks, updateHomeworksState } from "../utils/homeworks"; import { Homework } from "@/services/shared/Homework"; import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; @@ -31,7 +31,7 @@ const fetchHomeworks = async (): Promise => { if ( notificationsTypesPermissions?.enabled && - notificationsTypesPermissions?.homeworksUpdate + notificationsTypesPermissions?.homeworks ) { switch (differences.length) { case 0: diff --git a/src/background/utils/homeworksUpdate.ts b/src/background/utils/homeworks.ts similarity index 100% rename from src/background/utils/homeworksUpdate.ts rename to src/background/utils/homeworks.ts diff --git a/src/stores/account/types.ts b/src/stores/account/types.ts index 1100ba10a..a83ec585b 100644 --- a/src/stores/account/types.ts +++ b/src/stores/account/types.ts @@ -50,7 +50,6 @@ export interface Personalization { enabled?: boolean news?: boolean homeworks?: boolean - homeworksUpdate?: boolean grades?: boolean timetable?: boolean attendance?: boolean diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 99a3b7136..22bfc80ed 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -99,14 +99,8 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ icon: } color={colors.primary} />, title: "Nouveau devoir", message: "Nouveau devoir : \"Apporter le manuel\"", - personalizationValue: "homeworksUpdate", + personalizationValue: "homeworks", }, - // { - // icon: } color={colors.primary} />, - // title: "Travail à faire pour demain", - // message: "N’oublie pas de terminer ton devoir de français pour demain", - // personalizationValue: "homework", - // }, { icon: } color={colors.primary} />, title: "Nouvelle note", From f360614558c96e38b7d6978eb1388eabc65b30ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Mon, 13 Jan 2025 22:35:29 +0100 Subject: [PATCH 0225/1144] feat: Ajout de la gestion des notifications d'absence et de retard dans la Vie Scolaire --- src/background/BackgroundTasks.ts | 2 + src/background/Notifications.ts | 11 +- src/background/data/Attendance.ts | 138 +++++++++++++++++++ src/background/utils/attendance.ts | 17 +++ src/views/settings/SettingsNotifications.tsx | 9 +- 5 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 src/background/data/Attendance.ts create mode 100644 src/background/utils/attendance.ts diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 58187e17d..8838b131a 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -9,6 +9,7 @@ import { getAccounts, getSwitchToFunction } from "./utils/accounts"; import { fetchHomeworks } from "./data/Homeworks"; import { fetchGrade } from "./data/Grades"; import { fetchLessons } from "./data/Lessons"; +import { fetchAttendance } from "./data/Attendance"; /** * Background fetch function that fetches all the data @@ -28,6 +29,7 @@ const backgroundFetch = async () => { fetchHomeworks(), fetchGrade(), fetchLessons(), + fetchAttendance(), ]); } diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index dcc0491b0..4e6662a27 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -60,11 +60,20 @@ const createChannelNotification = async () => { description: "Te notifie lorsque ton emploi du temps du jour est modifié", sound: "default", }); + + await notifee.createChannel({ + id: "Attendance", + groupId: "Papillon", + name: "Vie Scolaire", + description: + "Te notifie lorsque tu as de nouvelles absences/retards/observations/punitions", + sound: "default", + }); }; const papillonNotify = async ( props: Notification, - channelId: "News" | "Homeworks" | "Grades" | "Lessons" + channelId: "News" | "Homeworks" | "Grades" | "Lessons" | "Attendance" ) => { // Add timestamp for Android const timestamp = new Date().getTime(); diff --git a/src/background/data/Attendance.ts b/src/background/data/Attendance.ts new file mode 100644 index 000000000..73c66d81c --- /dev/null +++ b/src/background/data/Attendance.ts @@ -0,0 +1,138 @@ +import { getCurrentAccount } from "../utils/accounts"; +import { papillonNotify } from "../Notifications"; +import { Attendance } from "@/services/shared/Attendance"; +import { getAttendance, updateAttendanceState } from "../utils/attendance"; + +const getDifferences = ( + currentAttendance: Attendance, + updatedAttendance: Attendance +): Attendance => { + const absFilter = updatedAttendance.absences.filter( + (updatedItem) => + !currentAttendance.absences.some( + (item) => + item.fromTimestamp === updatedItem.fromTimestamp && + item.toTimestamp === updatedItem.toTimestamp + ) + ); + + const delFilter = updatedAttendance.delays.filter( + (updatedItem) => + !currentAttendance.delays.some( + (item) => + item.timestamp === updatedItem.timestamp && + item.duration === updatedItem.duration + ) + ); + + const obsFilter = updatedAttendance.observations.filter( + (updatedItem) => + !currentAttendance.observations.some( + (item) => + item.timestamp === updatedItem.timestamp && + item.sectionName === updatedItem.sectionName + ) + ); + + const punFilter = updatedAttendance.punishments.filter( + (updatedItem) => + !currentAttendance.punishments.some( + (item) => + item.timestamp === updatedItem.timestamp && + item.duration === updatedItem.duration + ) + ); + + return { + absences: absFilter, + delays: delFilter, + observations: obsFilter, + punishments: punFilter, + }; +}; + +const fetchAttendance = async (): Promise => { + const account = getCurrentAccount(); + const notificationsTypesPermissions = account.personalization.notifications; + + const { defaultPeriod, attendances } = getAttendance(); + await updateAttendanceState(account, defaultPeriod); + const updatedAttendance = getAttendance().attendances[defaultPeriod]; + + const differences = getDifferences( + attendances[defaultPeriod], + updatedAttendance + ); + const LAdifference = + differences.absences.length + + differences.delays.length + + differences.observations.length + + differences.punishments.length; + + if ( + notificationsTypesPermissions?.enabled && + notificationsTypesPermissions?.attendance + ) { + switch (LAdifference) { + case 0: + break; + case 1: + let thenewevent = ""; + let explication = ""; + + if (differences.absences.length === 1) { + thenewevent = "Nouvelle absence"; + explication = `Tu as été absent de ${differences.absences[0].fromTimestamp} à ${differences.absences[0].toTimestamp}.`; + } else if (differences.delays.length === 1) { + thenewevent = "Nouveau retard"; + explication = `Tu as été en retard de ${differences.delays[0].duration} à ${differences.delays[0].timestamp}.`; + } else if (differences.observations.length === 1) { + thenewevent = "Nouvelle observation"; + explication = `Tu as eu une observation en ${ + differences.observations[0].subjectName ?? "Matière inconnue" + } à ${differences.observations[0].timestamp}.`; + } else { + thenewevent = "Nouvelle punition"; + explication = ""; + } + + papillonNotify( + { + id: `${account.name}-attendance`, + title: `[${account.name}] ${thenewevent}`, + subtitle: defaultPeriod, + body: explication, + ios: { + categoryId: account.name, + }, + }, + "Attendance" + ); + break; + default: + papillonNotify( + { + id: `${account.name}-attendance`, + title: `[${account.name}] Vie Scolaire`, + subtitle: defaultPeriod, + body: ` + De nouveaux événements ont été publiés, consulte la vie scolaire pour plus de détails :
+ - Nouvelle(s) absence(s) : ${differences.absences.length}
+ - Nouveau(x) retard(s) : ${differences.delays.length}
+ - Nouvelle(s) observation(s) : ${differences.observations.length}
+ - Nouvelle(s) punition(s) : ${differences.punishments.length} + `, + ios: { + categoryId: account.name, + }, + }, + "Attendance" + ); + break; + } + } + + return updatedAttendance; +}; + +export { fetchAttendance }; diff --git a/src/background/utils/attendance.ts b/src/background/utils/attendance.ts new file mode 100644 index 000000000..53434fa6b --- /dev/null +++ b/src/background/utils/attendance.ts @@ -0,0 +1,17 @@ +import { updateAttendanceInCache } from "@/services/attendance"; +import { PrimaryAccount } from "@/stores/account/types"; +import { useAttendanceStore } from "@/stores/attendance"; + +export const getAttendance = () => { + return { + defaultPeriod: useAttendanceStore.getState().defaultPeriod, + attendances: useAttendanceStore.getState().attendances, + }; +}; + +export const updateAttendanceState = async ( + account: PrimaryAccount, + period: string +) => { + await updateAttendanceInCache(account, period); +}; diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 22bfc80ed..4fdddd17b 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -7,7 +7,8 @@ import { BookCheck, TrendingUp, Newspaper, - Info + Info, + NotepadText } from "lucide-react-native"; import { useSharedValue, withTiming } from "react-native-reanimated"; import { @@ -113,6 +114,12 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ message: "Nouvelle actualité : \"Les élèves de 3ème partent en voyage scolaire\"", personalizationValue: "news", }, + { + icon: } color={colors.primary} />, + title: "Nouvelle événement sur la Vie Scolaire", + message: "Tu as été en retard de 5 min à 11h10", + personalizationValue: "attendance", + }, ]; const { showAlert } = useAlert(); From 1fc48b51075c2d6e29298fd29d39857d9afed820 Mon Sep 17 00:00:00 2001 From: Kgeek33 Date: Wed, 15 Jan 2025 07:43:23 +0100 Subject: [PATCH 0226/1144] Add subtitle for Grades notification --- src/background/data/Grades.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/background/data/Grades.ts b/src/background/data/Grades.ts index 7bf29ee51..53e175f3b 100644 --- a/src/background/data/Grades.ts +++ b/src/background/data/Grades.ts @@ -39,7 +39,7 @@ const fetchGrade = async (): Promise => { { id: `${account.name}-grades`, title: `[${account.name}] Nouvelle note`, - // subtitle: differences[0].title, + subtitle: defaultPeriod, body: `Une nouvelle note en ${differences[0].subjectName} a été publiée`, ios: { categoryId: account.name, @@ -52,6 +52,7 @@ const fetchGrade = async (): Promise => { papillonNotify( { id: `${account.name}-grades`, + subtitle: defaultPeriod, title: `[${account.name}] Nouvelles notes`, body: `${differences.length} nouvelles notes (${differences .flatMap((element) => { From 24eb88de47461c2031dab703f57fe3f512753b87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Wed, 15 Jan 2025 15:54:43 +0100 Subject: [PATCH 0227/1144] fix eslint error --- src/background/data/Grades.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/background/data/Grades.ts b/src/background/data/Grades.ts index 53e175f3b..6f4187dd2 100644 --- a/src/background/data/Grades.ts +++ b/src/background/data/Grades.ts @@ -52,7 +52,7 @@ const fetchGrade = async (): Promise => { papillonNotify( { id: `${account.name}-grades`, - subtitle: defaultPeriod, + subtitle: defaultPeriod, title: `[${account.name}] Nouvelles notes`, body: `${differences.length} nouvelles notes (${differences .flatMap((element) => { From f147ad8bc96ac354244f6cb097cdca3059d06ced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Wed, 15 Jan 2025 16:17:50 +0100 Subject: [PATCH 0228/1144] feat(Homeworks): update body and subtitle notification --- src/background/data/Homeworks.ts | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/background/data/Homeworks.ts b/src/background/data/Homeworks.ts index 4d4d0254e..0abb4937e 100644 --- a/src/background/data/Homeworks.ts +++ b/src/background/data/Homeworks.ts @@ -1,6 +1,5 @@ import { getCurrentAccount } from "../utils/accounts"; import { papillonNotify } from "../Notifications"; -import parse_homeworks_resume from "@/utils/format/format_pronote_news"; import { getHomeworks, updateHomeworksState } from "../utils/homeworks"; import { Homework } from "@/services/shared/Homework"; import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; @@ -22,6 +21,15 @@ const fetchHomeworks = async (): Promise => { const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; + // @ts-expect-error + let firstDate = account.instance?.instance?.firstDate || null; + if (!firstDate) { + firstDate = new Date(); + firstDate.setMonth(8); + firstDate.setDate(1); + } + const firstDateEpoch = dateToEpochWeekNumber(firstDate); + const SemaineAct = dateToEpochWeekNumber(new Date()); const currentHomeworks = getHomeworks()[SemaineAct]; await updateHomeworksState(account); @@ -41,13 +49,11 @@ const fetchHomeworks = async (): Promise => { { id: `${account.name}-homeworks`, title: `[${account.name}] Nouveau devoir`, - subtitle: differences[0].subject, - body: differences[0].content - ? `${parse_homeworks_resume(differences[0].content).slice( - 0, - 100 - )}...` - : "Aucun résumé disponible.", + subtitle: `Semaine ${( + ((SemaineAct - (firstDateEpoch % 52)) % 52) + + 1 + ).toString()}`, + body: `Un nouveau devoir en ${differences[0].subject} a été publié`, ios: { categoryId: account.name, }, @@ -60,7 +66,15 @@ const fetchHomeworks = async (): Promise => { { id: `${account.name}-homeworks`, title: `[${account.name}] Nouveaux devoirs`, - body: `Tu as ${differences.length} nouveaux devoirs.`, + subtitle: `Semaine ${( + ((SemaineAct - (firstDateEpoch % 52)) % 52) + + 1 + ).toString()}`, + body: `${differences.length} nouveaux devoirs (${differences + .flatMap((element) => { + return element.subject; + }) + .join("/")}) ont été publiés`, ios: { categoryId: account.name, }, From 741b929c9b2d65eac7fdf0bb59f835f3bc555347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aim=C3=A9=20Capela-Coyral?= Date: Wed, 15 Jan 2025 19:14:16 +0100 Subject: [PATCH 0229/1144] Add translation logic and the function call in the text --- src/components/Templates/LoginView.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/components/Templates/LoginView.tsx b/src/components/Templates/LoginView.tsx index ca4746957..decbccea5 100644 --- a/src/components/Templates/LoginView.tsx +++ b/src/components/Templates/LoginView.tsx @@ -90,6 +90,17 @@ const LoginView: React.FC<{ onLogin(username, password, customFieldsDict); }; + const translateError = (error: string | null): string | null => { + if (!error) return null; + + if (error.includes("challenge")) { + return "Un CAPTCHA est requis pour se connecter, utilise l'ENT pour te connecter."; + } + + // Add other error translations here + return error; + }; + return ( }> - {error} + {translateError(error)} )} From 98743be20b5640231ccc6fb445b947d9c99067f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Wed, 15 Jan 2025 21:53:04 +0100 Subject: [PATCH 0230/1144] fix(Lessons): update subtitle notification --- src/background/data/Lessons.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index 13809780e..1864d44dd 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -84,6 +84,8 @@ const fetchLessons = async (): Promise => { statut += `Tu dois aller en salle ${lessonsEvent[0].room}`; } } + } else if (lessonsEvent[0].statusText === "Devoir Surveillé") { + statut = "est un devoir surveillé"; } else { statut = `a un statut : ${lessonsEvent[0].statusText}`; } @@ -94,7 +96,13 @@ const fetchLessons = async (): Promise => { { id: `${account.name}-lessons`, title: `[${account.name}] Emploi du temps du jour`, - // subtitle: lessonsEvent[0].title, + subtitle: new Date( + lessonsEvent[0].startTimestamp + ).toLocaleDateString("fr-FR", { + weekday: "long", + day: "numeric", + month: "long", + }), body: `Le cours de ${lessonsEvent[0].title} (${dateLessonsDebut}-${dateLessonsFin}) ${statut}`, ios: { categoryId: account.name, @@ -108,6 +116,13 @@ const fetchLessons = async (): Promise => { { id: `${account.name}-lessons`, title: `[${account.name}] Emploi du temps du jour`, + subtitle: new Date( + lessonsEvent[0].startTimestamp + ).toLocaleDateString("fr-FR", { + weekday: "long", + day: "numeric", + month: "long", + }), body: `Les cours suivants ont été modifiés, consulte l'emploi du temps pour plus de détails.
${lessonsEvent .flatMap((element) => { From 58ed9a3984385d8b18d1836610f172a84c4cdf22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Wed, 15 Jan 2025 22:07:59 +0100 Subject: [PATCH 0231/1144] fix(News): update body when many differencies --- src/background/data/News.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/background/data/News.ts b/src/background/data/News.ts index 4132bcdb3..822ef09b0 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -60,11 +60,15 @@ const fetchNews = async (): Promise => { { id: `${account.name}-news`, title: `[${account.name}] Nouvelles actualités`, - body: differences + body: ` + ${differences.length} nouvelles actualités ont été publiées :
+ ${differences .flatMap((element) => { return `- ${element.title ?? "Sans titre"}`; }) - .join("
"), + .join("
") + .slice(0, 200)}... + `, ios: { categoryId: account.name, }, From cf75726ff7cc9624e43268a203b02997c17678a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Wed, 15 Jan 2025 22:35:28 +0100 Subject: [PATCH 0232/1144] feat(Attendance): update body + show locale time and not timestamp --- src/background/data/Attendance.ts | 49 ++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/src/background/data/Attendance.ts b/src/background/data/Attendance.ts index 73c66d81c..acf4674f4 100644 --- a/src/background/data/Attendance.ts +++ b/src/background/data/Attendance.ts @@ -81,19 +81,60 @@ const fetchAttendance = async (): Promise => { let explication = ""; if (differences.absences.length === 1) { + const dateAbsencesDebut = `${new Date( + differences.absences[0].fromTimestamp + ) + .getHours() + .toString() + .padStart(2, "0")}:${new Date(differences.absences[0].fromTimestamp) + .getMinutes() + .toString() + .padStart(2, "0")}`; + + const dateAbsencesFin = `${new Date( + differences.absences[0].toTimestamp + ) + .getHours() + .toString() + .padStart(2, "0")}:${new Date(differences.absences[0].toTimestamp) + .getMinutes() + .toString() + .padStart(2, "0")}`; + thenewevent = "Nouvelle absence"; - explication = `Tu as été absent de ${differences.absences[0].fromTimestamp} à ${differences.absences[0].toTimestamp}.`; + explication = `Tu as été absent de ${dateAbsencesDebut} à ${dateAbsencesFin}.`; } else if (differences.delays.length === 1) { + const dateRetard = `${new Date(differences.delays[0].timestamp) + .getHours() + .toString() + .padStart(2, "0")}:${new Date(differences.delays[0].timestamp) + .getMinutes() + .toString() + .padStart(2, "0")}`; + thenewevent = "Nouveau retard"; - explication = `Tu as été en retard de ${differences.delays[0].duration} à ${differences.delays[0].timestamp}.`; + explication = `Tu as été en retard de ${differences.delays[0].duration} à ${dateRetard}.`; } else if (differences.observations.length === 1) { + const dateObservations = `${new Date( + differences.observations[0].timestamp + ) + .getHours() + .toString() + .padStart(2, "0")}:${new Date(differences.observations[0].timestamp) + .getMinutes() + .toString() + .padStart(2, "0")}`; + thenewevent = "Nouvelle observation"; explication = `Tu as eu une observation en ${ differences.observations[0].subjectName ?? "Matière inconnue" - } à ${differences.observations[0].timestamp}.`; + } à ${dateObservations}.`; } else { thenewevent = "Nouvelle punition"; - explication = ""; + explication = ` + Tu as eu une punition en ${differences.punishments[0].duringLesson}.
+ Raison : ${differences.punishments[0].reason} + `; } papillonNotify( From 521c10bb4ab611cc8fc14ea0433eff122c06c5bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Wed, 15 Jan 2025 22:49:34 +0100 Subject: [PATCH 0233/1144] =?UTF-8?q?feat(Evaluation):=20ajouter=20la=20r?= =?UTF-8?q?=C3=A9cup=C3=A9ration=20et=20les=20notifications=20pour=20les?= =?UTF-8?q?=20comp=C3=A9tences?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/BackgroundTasks.ts | 2 + src/background/Notifications.ts | 10 ++- src/background/data/Evaluation.ts | 75 ++++++++++++++++++++ src/background/utils/evaluation.ts | 17 +++++ src/stores/account/types.ts | 1 + src/views/settings/SettingsNotifications.tsx | 9 ++- 6 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 src/background/data/Evaluation.ts create mode 100644 src/background/utils/evaluation.ts diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 8838b131a..4bea0f4d2 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -10,6 +10,7 @@ import { fetchHomeworks } from "./data/Homeworks"; import { fetchGrade } from "./data/Grades"; import { fetchLessons } from "./data/Lessons"; import { fetchAttendance } from "./data/Attendance"; +import { fetchEvaluation } from "./data/Evaluation"; /** * Background fetch function that fetches all the data @@ -30,6 +31,7 @@ const backgroundFetch = async () => { fetchGrade(), fetchLessons(), fetchAttendance(), + fetchEvaluation(), ]); } diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index 4e6662a27..fb1870c62 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -69,11 +69,19 @@ const createChannelNotification = async () => { "Te notifie lorsque tu as de nouvelles absences/retards/observations/punitions", sound: "default", }); + + await notifee.createChannel({ + id: "Evaluation", + groupId: "Papillon", + name: "Compétences", + description: "Te notifie lorsque tu as de nouvelles compétences", + sound: "default", + }); }; const papillonNotify = async ( props: Notification, - channelId: "News" | "Homeworks" | "Grades" | "Lessons" | "Attendance" + channelId: "News" | "Homeworks" | "Grades" | "Lessons" | "Attendance" | "Evaluation" ) => { // Add timestamp for Android const timestamp = new Date().getTime(); diff --git a/src/background/data/Evaluation.ts b/src/background/data/Evaluation.ts new file mode 100644 index 000000000..52c7167fe --- /dev/null +++ b/src/background/data/Evaluation.ts @@ -0,0 +1,75 @@ +import { getCurrentAccount } from "../utils/accounts"; +import { papillonNotify } from "../Notifications"; +import { Evaluation } from "@/services/shared/Evaluation"; +import { getEvaluation, updateEvaluationState } from "../utils/evaluation"; + +const getDifferences = ( + currentEvaluation: Evaluation[], + updatedEvaluation: Evaluation[] +): Evaluation[] => { + return updatedEvaluation.filter( + (updatedItem) => + !currentEvaluation.some( + (item) => + item.levels === updatedItem.levels && + item.coefficient === updatedItem.coefficient + ) + ); +}; + +const fetchEvaluation = async (): Promise => { + const account = getCurrentAccount(); + const notificationsTypesPermissions = account.personalization.notifications; + + const { defaultPeriod, evaluation } = getEvaluation(); + await updateEvaluationState(account, defaultPeriod); + const updatedEvaluation = getEvaluation().evaluation[defaultPeriod]; + + const differences = getDifferences(evaluation[defaultPeriod], updatedEvaluation); + + if ( + notificationsTypesPermissions?.enabled && + notificationsTypesPermissions?.evaluation + ) { + switch (differences.length) { + case 0: + break; + case 1: + papillonNotify( + { + id: `${account.name}-evaluation`, + title: `[${account.name}] Nouvelle compétence`, + subtitle: defaultPeriod, + body: `Une nouvelle compétence en ${differences[0].subjectName} a été publiée`, + ios: { + categoryId: account.name, + }, + }, + "Evaluation" + ); + break; + default: + papillonNotify( + { + id: `${account.name}-evaluation`, + subtitle: defaultPeriod, + title: `[${account.name}] Nouvelles compétences`, + body: `${differences.length} nouvelles compétences (${differences + .flatMap((element) => { + return element.subjectName; + }) + .join("/")}) ont été publiées`, + ios: { + categoryId: account.name, + }, + }, + "Evaluation" + ); + break; + } + } + + return updatedEvaluation; +}; + +export { fetchEvaluation }; diff --git a/src/background/utils/evaluation.ts b/src/background/utils/evaluation.ts new file mode 100644 index 000000000..36c169ccb --- /dev/null +++ b/src/background/utils/evaluation.ts @@ -0,0 +1,17 @@ +import { updateEvaluationsInCache } from "@/services/evaluation"; +import { PrimaryAccount } from "@/stores/account/types"; +import { useEvaluationStore } from "@/stores/evaluation"; + +export const getEvaluation = () => { + return { + defaultPeriod: useEvaluationStore.getState().defaultPeriod, + evaluation: useEvaluationStore.getState().evaluations, + }; +}; + +export const updateEvaluationState = async ( + account: PrimaryAccount, + period: string +) => { + await updateEvaluationsInCache(account, period); +}; diff --git a/src/stores/account/types.ts b/src/stores/account/types.ts index a83ec585b..1e000384c 100644 --- a/src/stores/account/types.ts +++ b/src/stores/account/types.ts @@ -53,6 +53,7 @@ export interface Personalization { grades?: boolean timetable?: boolean attendance?: boolean + evaluation?: boolean } icalURLs: PapillonIcalURL[], tabs: Tab[], diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 4fdddd17b..c34a59761 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -8,7 +8,8 @@ import { TrendingUp, Newspaper, Info, - NotepadText + NotepadText, + BookPlus } from "lucide-react-native"; import { useSharedValue, withTiming } from "react-native-reanimated"; import { @@ -120,6 +121,12 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ message: "Tu as été en retard de 5 min à 11h10", personalizationValue: "attendance", }, + { + icon: } color={colors.primary} />, + title: "Nouvelle compétence", + message: "Nouvelle compétence publiée en histoire", + personalizationValue: "evaluation", + }, ]; const { showAlert } = useAlert(); From 794e100d9282b8c44ce1ee475c4bd0cd6c20b198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Wed, 15 Jan 2025 23:06:53 +0100 Subject: [PATCH 0234/1144] =?UTF-8?q?feat(Notifications):=20ajouter=20la?= =?UTF-8?q?=20gestion=20des=20notifications=20en=20premier=20plan=20et=20e?= =?UTF-8?q?n=20arri=C3=A8re-plan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.tsx | 33 +++++++++++++++++++++++++++++++ src/background/BackgroundTasks.ts | 27 +++++++++++++++++++++++++ src/background/Notifications.ts | 9 ++++++++- 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/App.tsx b/App.tsx index 07b02be2c..8fc694b20 100644 --- a/App.tsx +++ b/App.tsx @@ -1,3 +1,4 @@ +import notifee, { EventType } from "@notifee/react-native"; import Router from "@/router"; import { useFonts } from "expo-font"; import * as SplashScreen from "expo-splash-screen"; @@ -21,6 +22,38 @@ const BACKGROUND_LIMITS: Partial> = { }; export default function App () { + useEffect(() => { + // Gestion des notifs quand app en premier plan + const unsubscribe = notifee.onForegroundEvent(({ type, detail }) => { + const { notification, pressAction } = detail; + + switch (type) { + case EventType.ACTION_PRESS: + console.log(`[Notifee] Action press: ${pressAction?.id}`); + /* + Ici on va gérer les redirections vers une page de l'app + par exemple quand on clique sur une notification + + if (pressAction?.id === "open_lessons") { + console.log("Open lessons screen"); + } + */ + break; + + case EventType.DISMISSED: + console.log(`[Notifee] Notification dismissed: ${notification?.id}`); + break; + + default: + console.log(`[Notifee] Foreground event type: ${type}`); + } + }); + + return () => { + unsubscribe(); + }; + }, []); + const [appState, setAppState] = useState(AppState.currentState); const backgroundStartTime = useRef(null); const switchTo = useCurrentAccount((store) => store.switchTo); diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index 4bea0f4d2..ab212fa5c 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -1,3 +1,4 @@ +import notifee, { EventType } from "@notifee/react-native"; import * as BackgroundFetch from "expo-background-fetch"; import * as TaskManager from "expo-task-manager"; import { BackgroundFetchResult } from "expo-background-fetch"; @@ -12,6 +13,32 @@ import { fetchLessons } from "./data/Lessons"; import { fetchAttendance } from "./data/Attendance"; import { fetchEvaluation } from "./data/Evaluation"; +// Gestion des notifs quand app en arrière-plan +notifee.onBackgroundEvent(async ({ type, detail }) => { + const { notification, pressAction } = detail; + + switch (type) { + case EventType.ACTION_PRESS: + console.log(`[Notifee] Action press: ${pressAction?.id}`); + /* + Ici on va gérer les redirections vers une page de l'app + par exemple quand on clique sur une notification + + if (pressAction?.id === "open_lessons") { + console.log("Open lessons screen"); + } + */ + break; + + case EventType.DISMISSED: + console.log(`[Notifee] Notification dismissed: ${notification?.id}`); + break; + + default: + console.log(`[Notifee] Background event type: ${type}`); + } +}); + /** * Background fetch function that fetches all the data * @warning This task should not last more than 30 seconds diff --git a/src/background/Notifications.ts b/src/background/Notifications.ts index fb1870c62..9dc4cf3c3 100644 --- a/src/background/Notifications.ts +++ b/src/background/Notifications.ts @@ -81,7 +81,13 @@ const createChannelNotification = async () => { const papillonNotify = async ( props: Notification, - channelId: "News" | "Homeworks" | "Grades" | "Lessons" | "Attendance" | "Evaluation" + channelId: + | "News" + | "Homeworks" + | "Grades" + | "Lessons" + | "Attendance" + | "Evaluation" ) => { // Add timestamp for Android const timestamp = new Date().getTime(); @@ -95,6 +101,7 @@ const papillonNotify = async ( showTimestamp: true, smallIcon: "@mipmap/ic_launcher_foreground", color: "#32AB8E", + // à intégrer => `actions` }, }); }; From 20aac1b875d6742a69f08ce0277733928459229c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Wed, 15 Jan 2025 23:35:35 +0100 Subject: [PATCH 0235/1144] =?UTF-8?q?feat(Evaluation,=20Grades,=20Attendan?= =?UTF-8?q?ce):=20g=C3=A9rer=20les=20p=C3=A9riodes=20d'=C3=A9valuation=20e?= =?UTF-8?q?t=20de=20notes=20dans=20le=20cache?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Evaluation.ts | 5 ++++- src/background/data/Grades.ts | 5 ++++- src/background/utils/attendance.ts | 3 ++- src/background/utils/evaluation.ts | 3 ++- src/background/utils/grades.ts | 3 ++- 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/background/data/Evaluation.ts b/src/background/data/Evaluation.ts index 52c7167fe..bab13dfe0 100644 --- a/src/background/data/Evaluation.ts +++ b/src/background/data/Evaluation.ts @@ -25,7 +25,10 @@ const fetchEvaluation = async (): Promise => { await updateEvaluationState(account, defaultPeriod); const updatedEvaluation = getEvaluation().evaluation[defaultPeriod]; - const differences = getDifferences(evaluation[defaultPeriod], updatedEvaluation); + const differences = getDifferences( + evaluation[defaultPeriod] ?? [], + updatedEvaluation ?? [] + ); if ( notificationsTypesPermissions?.enabled && diff --git a/src/background/data/Grades.ts b/src/background/data/Grades.ts index 6f4187dd2..3ae85ce72 100644 --- a/src/background/data/Grades.ts +++ b/src/background/data/Grades.ts @@ -25,7 +25,10 @@ const fetchGrade = async (): Promise => { await updateGradeState(account, defaultPeriod); const updatedGrade = getGrades().grades[defaultPeriod]; - const differences = getDifferences(grades[defaultPeriod], updatedGrade); + const differences = getDifferences( + grades[defaultPeriod] ?? [], + updatedGrade ?? [] + ); if ( notificationsTypesPermissions?.enabled && diff --git a/src/background/utils/attendance.ts b/src/background/utils/attendance.ts index 53434fa6b..3c61c7ca1 100644 --- a/src/background/utils/attendance.ts +++ b/src/background/utils/attendance.ts @@ -1,4 +1,4 @@ -import { updateAttendanceInCache } from "@/services/attendance"; +import { updateAttendanceInCache, updateAttendancePeriodsInCache } from "@/services/attendance"; import { PrimaryAccount } from "@/stores/account/types"; import { useAttendanceStore } from "@/stores/attendance"; @@ -13,5 +13,6 @@ export const updateAttendanceState = async ( account: PrimaryAccount, period: string ) => { + await updateAttendancePeriodsInCache(account); await updateAttendanceInCache(account, period); }; diff --git a/src/background/utils/evaluation.ts b/src/background/utils/evaluation.ts index 36c169ccb..54ee4053e 100644 --- a/src/background/utils/evaluation.ts +++ b/src/background/utils/evaluation.ts @@ -1,4 +1,4 @@ -import { updateEvaluationsInCache } from "@/services/evaluation"; +import { updateEvaluationPeriodsInCache, updateEvaluationsInCache } from "@/services/evaluation"; import { PrimaryAccount } from "@/stores/account/types"; import { useEvaluationStore } from "@/stores/evaluation"; @@ -13,5 +13,6 @@ export const updateEvaluationState = async ( account: PrimaryAccount, period: string ) => { + await updateEvaluationPeriodsInCache(account); await updateEvaluationsInCache(account, period); }; diff --git a/src/background/utils/grades.ts b/src/background/utils/grades.ts index 2979ce6bb..5c89eb61c 100644 --- a/src/background/utils/grades.ts +++ b/src/background/utils/grades.ts @@ -1,4 +1,4 @@ -import { updateGradesAndAveragesInCache } from "@/services/grades"; +import { updateGradesAndAveragesInCache, updateGradesPeriodsInCache } from "@/services/grades"; import { PrimaryAccount } from "@/stores/account/types"; import { useGradesStore } from "@/stores/grades"; @@ -13,5 +13,6 @@ export const updateGradeState = async ( account: PrimaryAccount, period: string ) => { + await updateGradesPeriodsInCache(account); await updateGradesAndAveragesInCache(account, period); }; From 5987811472d1076b7103edca84220b14cbe3233e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Thu, 16 Jan 2025 00:13:21 +0100 Subject: [PATCH 0236/1144] fix: eslint errors --- src/components/Grades/AnimatedEmoji.tsx | 2 +- src/components/Grades/GradeModal.tsx | 4 +--- src/router/navigator/atoms/MenuItem.tsx | 8 +++----- src/router/navigator/atoms/TabItem.tsx | 6 ++---- src/router/navigator/menu.tsx | 10 +++------- src/router/navigator/navigator.tsx | 3 +-- src/router/navigator/tabs.tsx | 4 ++-- src/views/account/Chat/Modals/Chat.tsx | 2 +- src/views/account/Grades/Document.tsx | 6 +----- .../IdentityProvider/actions/BackgroundIUTLannion.tsx | 1 - src/views/login/pronote/PronoteInstanceSelector.tsx | 1 - .../ExternalAccount/PriceDetectionOnboarding.tsx | 4 ++-- src/views/settings/ExternalAccount/ServiceSelector.tsx | 3 +-- src/views/settings/SettingsReactions.tsx | 3 +-- 14 files changed, 19 insertions(+), 38 deletions(-) diff --git a/src/components/Grades/AnimatedEmoji.tsx b/src/components/Grades/AnimatedEmoji.tsx index 7e12350d4..5e3d9d1a4 100644 --- a/src/components/Grades/AnimatedEmoji.tsx +++ b/src/components/Grades/AnimatedEmoji.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { View, Text } from "react-native"; +import { View } from "react-native"; import Animated, { useAnimatedStyle, useSharedValue, diff --git a/src/components/Grades/GradeModal.tsx b/src/components/Grades/GradeModal.tsx index a022b3822..6b09b29d3 100644 --- a/src/components/Grades/GradeModal.tsx +++ b/src/components/Grades/GradeModal.tsx @@ -5,13 +5,11 @@ import { Image, TouchableOpacity, Text, - Platform, Alert } from "react-native"; -import { Download, Trash, Maximize2, Share, Delete } from "lucide-react-native"; +import { Download, Trash, Share } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { BlurView } from "expo-blur"; -import { ScrollView } from "react-native-gesture-handler"; import * as Sharing from "expo-sharing"; import * as FileSystem from "expo-file-system"; import * as MediaLibrary from "expo-media-library"; diff --git a/src/router/navigator/atoms/MenuItem.tsx b/src/router/navigator/atoms/MenuItem.tsx index 2a9b548ce..ff64a6de5 100644 --- a/src/router/navigator/atoms/MenuItem.tsx +++ b/src/router/navigator/atoms/MenuItem.tsx @@ -1,13 +1,11 @@ import * as React from "react"; -import { useCurrentAccount } from "@/stores/account"; -import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, View, Text, Platform } from "react-native"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { useTheme } from "@react-navigation/native"; +import { StyleSheet, Platform } from "react-native"; import LottieView from "lottie-react-native"; import colorsList from "@/utils/data/colors.json"; import { Pressable } from "react-native-gesture-handler"; import * as Haptics from "expo-haptics"; -import Reanimated, { FadeIn, FadeOut, LinearTransition, ZoomIn } from "react-native-reanimated"; +import Reanimated, { FadeIn, FadeOut, LinearTransition } from "react-native-reanimated"; import { anim2Papillon } from "@/utils/ui/animations"; const MenuItem: React.FC<{ diff --git a/src/router/navigator/atoms/TabItem.tsx b/src/router/navigator/atoms/TabItem.tsx index 90da917e3..cc8fe2536 100644 --- a/src/router/navigator/atoms/TabItem.tsx +++ b/src/router/navigator/atoms/TabItem.tsx @@ -1,8 +1,6 @@ import * as React from "react"; -import { useCurrentAccount } from "@/stores/account"; -import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, View, Text, Platform } from "react-native"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { useTheme } from "@react-navigation/native"; +import { StyleSheet, Platform } from "react-native"; import LottieView from "lottie-react-native"; import colorsList from "@/utils/data/colors.json"; import { Pressable } from "react-native-gesture-handler"; diff --git a/src/router/navigator/menu.tsx b/src/router/navigator/menu.tsx index d12793b8b..12082b86c 100644 --- a/src/router/navigator/menu.tsx +++ b/src/router/navigator/menu.tsx @@ -1,17 +1,13 @@ -import React, { useEffect, useMemo, useState } from "react"; +import React, { useMemo, useState } from "react"; import { useCurrentAccount } from "@/stores/account"; import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, View, ScrollView, Platform, Image, Text, StatusBar } from "react-native"; +import { StyleSheet, Platform, Image, Text, StatusBar } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import TabItem from "./atoms/TabItem"; -import Reanimated, { LinearTransition, useSharedValue } from "react-native-reanimated"; +import Reanimated from "react-native-reanimated"; import MenuItem from "./atoms/MenuItem"; -import AccountSwitcher from "@/components/Home/AccountSwitcher"; import ContextMenu from "@/components/Home/AccountSwitcherContextMenu"; -import { NativeText } from "@/components/Global/NativeComponents"; import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; -import { he } from "date-fns/locale"; const PapillonNavigatorMenu: React.FC, "NavigationContent">> = ({ state, descriptors, navigation }) => { const theme = useTheme(); diff --git a/src/router/navigator/navigator.tsx b/src/router/navigator/navigator.tsx index 027e57dec..888137f44 100644 --- a/src/router/navigator/navigator.tsx +++ b/src/router/navigator/navigator.tsx @@ -1,8 +1,7 @@ import { BottomTabView } from "@react-navigation/bottom-tabs"; import { createNavigatorFactory, TabRouter, useNavigationBuilder } from "@react-navigation/native"; import PapillonNavigatorTabs from "./tabs"; -import { memo, useEffect, useMemo, useState } from "react"; -import { Dimensions, View } from "react-native"; +import { View } from "react-native"; import PapillonNavigatorMenu from "./menu"; import useScreenDimensions from "@/hooks/useScreenDimensions"; diff --git a/src/router/navigator/tabs.tsx b/src/router/navigator/tabs.tsx index a82d23b88..257f2c0a3 100644 --- a/src/router/navigator/tabs.tsx +++ b/src/router/navigator/tabs.tsx @@ -1,11 +1,11 @@ import React, { useEffect, useMemo, useState } from "react"; import { useCurrentAccount } from "@/stores/account"; import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, View, Platform } from "react-native"; +import { StyleSheet, Platform } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import TabItem from "./atoms/TabItem"; -import Reanimated, { LinearTransition } from "react-native-reanimated"; +import Reanimated from "react-native-reanimated"; const PapillonNavigatorTabs: React.FC, "NavigationContent">> = ({ state, descriptors, navigation }) => { const theme = useTheme(); diff --git a/src/views/account/Chat/Modals/Chat.tsx b/src/views/account/Chat/Modals/Chat.tsx index 90f2d5538..6f8bb8d36 100644 --- a/src/views/account/Chat/Modals/Chat.tsx +++ b/src/views/account/Chat/Modals/Chat.tsx @@ -6,7 +6,7 @@ import type {Screen} from "@/router/helpers/types"; import { NativeText,} from "@/components/Global/NativeComponents"; import {useCurrentAccount} from "@/stores/account"; import type {ChatMessage, ChatRecipient} from "@/services/shared/Chat"; -import {ChevronLeft, File, Link, Send} from "lucide-react-native"; +import {ChevronLeft, Send} from "lucide-react-native"; import parse_initials from "@/utils/format/format_pronote_initials"; import InitialIndicator from "@/components/News/InitialIndicator"; import {PapillonModernHeader} from "@/components/Global/PapillonModernHeader"; diff --git a/src/views/account/Grades/Document.tsx b/src/views/account/Grades/Document.tsx index 2e6481f20..9d148bda8 100644 --- a/src/views/account/Grades/Document.tsx +++ b/src/views/account/Grades/Document.tsx @@ -7,18 +7,14 @@ import { import { getSubjectData } from "@/services/shared/Subject"; import { useTheme } from "@react-navigation/native"; import React, { useCallback, useEffect, useLayoutEffect, useState } from "react"; -import { Image, ScrollView, Text, View, Platform, TouchableOpacity, Modal } from "react-native"; +import { Image, ScrollView, Text, View, Platform, TouchableOpacity } from "react-native"; import * as StoreReview from "expo-store-review"; import { Asterisk, Calculator, - Download, - Expand, Maximize2, Scale, School, - SmilePlus, - Trash, UserMinus, UserPlus, Users, diff --git a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx index 6d3c1c177..8fd88a3d9 100644 --- a/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx +++ b/src/views/login/IdentityProvider/actions/BackgroundIUTLannion.tsx @@ -11,7 +11,6 @@ import { WebView } from "react-native-webview"; import type { Screen } from "@/router/helpers/types"; import { FadeInDown, FadeOutUp } from "react-native-reanimated"; import { animPapillon } from "@/utils/ui/animations"; -import { da } from "date-fns/locale"; const capitalizeFirst = (str: string) => { str = str.toLowerCase(); diff --git a/src/views/login/pronote/PronoteInstanceSelector.tsx b/src/views/login/pronote/PronoteInstanceSelector.tsx index c2bfd27df..62158b4d4 100644 --- a/src/views/login/pronote/PronoteInstanceSelector.tsx +++ b/src/views/login/pronote/PronoteInstanceSelector.tsx @@ -8,7 +8,6 @@ import { ActivityIndicator, Keyboard, KeyboardEvent, - Text, } from "react-native"; import pronote from "pawnote"; import Reanimated, { diff --git a/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx b/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx index 36bc869d0..a22b06d77 100644 --- a/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx +++ b/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx @@ -1,11 +1,11 @@ -import React, { useState } from "react"; +import React from "react"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { BellRing } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { Image, View, StyleSheet, Text } from "react-native"; import { NativeText, } from "@/components/Global/NativeComponents"; -import { useAccounts, useCurrentAccount } from "@/stores/account"; +import { useCurrentAccount } from "@/stores/account"; import { TouchableOpacity } from "react-native-gesture-handler"; import BetaIndicator from "@/components/News/Beta"; diff --git a/src/views/settings/ExternalAccount/ServiceSelector.tsx b/src/views/settings/ExternalAccount/ServiceSelector.tsx index 6f751bd9f..6c148cfba 100644 --- a/src/views/settings/ExternalAccount/ServiceSelector.tsx +++ b/src/views/settings/ExternalAccount/ServiceSelector.tsx @@ -10,8 +10,7 @@ import { AccountService } from "@/stores/account/types"; import { useCurrentAccount } from "@/stores/account"; import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; -import { LinearGradient } from "expo-linear-gradient"; - + const ExternalAccountSelector: Screen<"ExternalAccountSelector"> = ({ navigation, route }) => { const theme = useTheme(); const { colors } = theme; diff --git a/src/views/settings/SettingsReactions.tsx b/src/views/settings/SettingsReactions.tsx index 49400a566..6b691d7f8 100644 --- a/src/views/settings/SettingsReactions.tsx +++ b/src/views/settings/SettingsReactions.tsx @@ -1,11 +1,10 @@ import React from "react"; -import { ScrollView, Text, View } from "react-native"; +import { ScrollView, View } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { useGradesStore } from "@/stores/grades"; import ReelGallery from "@/components/Settings/ReelGallery"; import MissingItem from "@/components/Global/MissingItem"; -import AnimatedEmoji from "@/components/Grades/AnimatedEmoji"; const SettingsReactions: Screen<"SettingsReactions"> = () => { const theme = useTheme(); From 52148b569223cfa8a9081141fdcd9fb7e1db7a30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Thu, 16 Jan 2025 13:27:47 +0100 Subject: [PATCH 0237/1144] fix: eslint indentation --- .../account/Grades/Graph/GradesAverage.tsx | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/views/account/Grades/Graph/GradesAverage.tsx b/src/views/account/Grades/Graph/GradesAverage.tsx index d69d95739..f6e96a7d0 100644 --- a/src/views/account/Grades/Graph/GradesAverage.tsx +++ b/src/views/account/Grades/Graph/GradesAverage.tsx @@ -329,26 +329,26 @@ const GradesAverageGraph: React.FC = ({
- Moyenne classe - - {classAvg !== null ? ( - <> - - - /20 - - - ) : ( - Inconnue - )} - - + Moyenne classe + + {classAvg !== null ? ( + <> + + + /20 + + + ) : ( + Inconnue + )} + + {showDetails && maxAvg > 0 && minAvg > 0 ? ( From f2b09ca14ed8d0ac963ad84886abcf09d3c84804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Thu, 16 Jan 2025 19:13:40 +0100 Subject: [PATCH 0238/1144] fix(Attendance): petit changement de mauvaises variables + ajout de "min" --- src/background/data/Attendance.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/background/data/Attendance.ts b/src/background/data/Attendance.ts index acf4674f4..a7cfdae55 100644 --- a/src/background/data/Attendance.ts +++ b/src/background/data/Attendance.ts @@ -113,7 +113,7 @@ const fetchAttendance = async (): Promise => { .padStart(2, "0")}`; thenewevent = "Nouveau retard"; - explication = `Tu as été en retard de ${differences.delays[0].duration} à ${dateRetard}.`; + explication = `Tu as été en retard de ${differences.delays[0].duration} min à ${dateRetard}.`; } else if (differences.observations.length === 1) { const dateObservations = `${new Date( differences.observations[0].timestamp @@ -132,8 +132,8 @@ const fetchAttendance = async (): Promise => { } else { thenewevent = "Nouvelle punition"; explication = ` - Tu as eu une punition en ${differences.punishments[0].duringLesson}.
- Raison : ${differences.punishments[0].reason} + Tu as eu une punition de ${differences.punishments[0].givenBy}.
+ Raison : ${differences.punishments[0].reason.circumstances} `; } From 4e1e9fc56a2c395335224fbd41304025c6336ee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Thu, 16 Jan 2025 19:19:46 +0100 Subject: [PATCH 0239/1144] =?UTF-8?q?fix(Lessons):=20plus=20de=20pr=C3=A9c?= =?UTF-8?q?ision?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Lessons.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index 1864d44dd..4b4c57a42 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -95,7 +95,7 @@ const fetchLessons = async (): Promise => { papillonNotify( { id: `${account.name}-lessons`, - title: `[${account.name}] Emploi du temps du jour`, + title: `[${account.name}] Emploi du temps du jour modifié`, subtitle: new Date( lessonsEvent[0].startTimestamp ).toLocaleDateString("fr-FR", { From 498ad1ae63705ca892e8dad10185d5506efcb316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Thu, 16 Jan 2025 19:25:20 +0100 Subject: [PATCH 0240/1144] feat(Settings): update settings notifications --- src/views/settings/SettingsNotifications.tsx | 42 +++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index c34a59761..cf39fbeb1 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { Alert, Platform, ScrollView, Switch } from "react-native"; +import { Alert, Platform, ScrollView, Switch, View } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { @@ -93,38 +93,38 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ const notificationSchoolary = [ { icon: } color={colors.primary} />, - title: "Modification de cours", - message: "Le cours de mathématiques (10h-11h) a été annulé", + title: "Emploi du temps du jour modifié", + message: "Le cours de Musique (10:00-11:00) a été annulé", personalizationValue: "timetable", }, { icon: } color={colors.primary} />, title: "Nouveau devoir", - message: "Nouveau devoir : \"Apporter le manuel\"", + message: "Un nouveau devoir en Mathématiques a été publié", personalizationValue: "homeworks", }, { icon: } color={colors.primary} />, title: "Nouvelle note", - message: "Nouvelle note publiée en histoire", + message: "Une nouvelle note en Anglais a été publiée", personalizationValue: "grades", }, { icon: } color={colors.primary} />, title: "Nouvelle actualité", - message: "Nouvelle actualité : \"Les élèves de 3ème partent en voyage scolaire\"", + message: "Chers élèves, chers collègues, Dans le cadre du prix \"Non au harcèlement\", 9 affiches ont été réa...", personalizationValue: "news", }, { icon: } color={colors.primary} />, title: "Nouvelle événement sur la Vie Scolaire", - message: "Tu as été en retard de 5 min à 11h10", + message: "Tu as été en retard de 5 min à 11:10", personalizationValue: "attendance", }, { icon: } color={colors.primary} />, title: "Nouvelle compétence", - message: "Nouvelle compétence publiée en histoire", + message: "Une nouvelle compétence en Histoire a été publiée", personalizationValue: "evaluation", }, ]; @@ -147,6 +147,16 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ {enabled && ( <> + + + }> + + Toutes les 15 minutes, Papillon va se connecter à ton compte + et te notifie en fonction des paramètres ci-dessous + + + + {notificationSchoolary.map((notification, index) => ( @@ -184,15 +194,19 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ { if (Platform.OS === "ios") { - Alert.alert("Information", "Pour le moment, tu es prévenu de ton emploi du temps uniquement 15 minutes avant le 1er cours de la journée.", [ - { - text: "OK", - }, - ]); + Alert.alert( + "Information", + "Pour le moment, tu es prévenu de ton emploi du temps modifié uniquement 15 minutes avant le 1er cours de la journée.", + [ + { + text: "OK", + } + ] + ); } else { showAlert({ title: "Information", - message: "Pour le moment, tu es prévenu de ton emploi du temps uniquement 15 minutes avant le 1er cours de la journée.", + message: "Pour le moment, tu es prévenu de ton emploi du temps modifié uniquement 15 minutes avant le 1er cours de la journée.", actions: [ { title: "OK", From f7524dccf185b607b60d5bf6e178db265f1e7631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Thu, 16 Jan 2025 19:43:39 +0100 Subject: [PATCH 0241/1144] feat(Attendance): update notification when many differencies (by @NathanBnm) --- src/background/data/Attendance.ts | 50 ++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/src/background/data/Attendance.ts b/src/background/data/Attendance.ts index a7cfdae55..ca8b8e1a0 100644 --- a/src/background/data/Attendance.ts +++ b/src/background/data/Attendance.ts @@ -151,18 +151,54 @@ const fetchAttendance = async (): Promise => { ); break; default: + let LesExplication: string[] = []; + + if (differences.absences.length > 0) { + if (differences.absences.length === 1) { + LesExplication.push("1 nouvelle absence"); + } else { + LesExplication.push( + `${differences.absences.length} nouvelles absences` + ); + } + } + + if (differences.delays.length > 0) { + if (differences.delays.length === 1) { + LesExplication.push("1 nouveau retard"); + } else { + LesExplication.push( + `${differences.delays.length} nouveaux retards` + ); + } + } + + if (differences.observations.length > 0) { + if (differences.observations.length === 1) { + LesExplication.push("1 nouvelle observation"); + } else { + LesExplication.push( + `${differences.observations.length} nouvelles observations` + ); + } + } + + if (differences.punishments.length > 0) { + if (differences.absences.length === 1) { + LesExplication.push("1 nouvelle punition"); + } else { + LesExplication.push( + `${differences.punishments.length} nouvelles punitions` + ); + } + } + papillonNotify( { id: `${account.name}-attendance`, title: `[${account.name}] Vie Scolaire`, subtitle: defaultPeriod, - body: ` - De nouveaux événements ont été publiés, consulte la vie scolaire pour plus de détails :
- - Nouvelle(s) absence(s) : ${differences.absences.length}
- - Nouveau(x) retard(s) : ${differences.delays.length}
- - Nouvelle(s) observation(s) : ${differences.observations.length}
- - Nouvelle(s) punition(s) : ${differences.punishments.length} - `, + body: `De nouveaux événements ont été publiés, consulte la vie scolaire pour plus de détails : ${LesExplication.join(", ")}.`, ios: { categoryId: account.name, }, From c114795534b35032bdb084222d86f6ce2fb8c47e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Thu, 16 Jan 2025 19:57:08 +0100 Subject: [PATCH 0242/1144] feat: update notifications when many differencies (by @NathanBnm) --- src/background/data/Evaluation.ts | 9 ++++++--- src/background/data/Grades.ts | 9 ++++++--- src/background/data/Homeworks.ts | 9 ++++++--- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/background/data/Evaluation.ts b/src/background/data/Evaluation.ts index bab13dfe0..d0fe6961d 100644 --- a/src/background/data/Evaluation.ts +++ b/src/background/data/Evaluation.ts @@ -57,11 +57,14 @@ const fetchEvaluation = async (): Promise => { id: `${account.name}-evaluation`, subtitle: defaultPeriod, title: `[${account.name}] Nouvelles compétences`, - body: `${differences.length} nouvelles compétences (${differences + body: ` + ${differences.length} nouvelles compétences ont été publiées :
+ ${differences .flatMap((element) => { - return element.subjectName; + return `- ${element.subjectName}`; }) - .join("/")}) ont été publiées`, + .join("
")} + `, ios: { categoryId: account.name, }, diff --git a/src/background/data/Grades.ts b/src/background/data/Grades.ts index 3ae85ce72..dac1917d6 100644 --- a/src/background/data/Grades.ts +++ b/src/background/data/Grades.ts @@ -57,11 +57,14 @@ const fetchGrade = async (): Promise => { id: `${account.name}-grades`, subtitle: defaultPeriod, title: `[${account.name}] Nouvelles notes`, - body: `${differences.length} nouvelles notes (${differences + body: ` + ${differences.length} nouvelles notes ont été publiées :
+ ${differences .flatMap((element) => { - return element.subjectName; + return `- ${element.subjectName}`; }) - .join("/")}) ont été publiées`, + .join("
")} + `, ios: { categoryId: account.name, }, diff --git a/src/background/data/Homeworks.ts b/src/background/data/Homeworks.ts index 0abb4937e..d6ee82114 100644 --- a/src/background/data/Homeworks.ts +++ b/src/background/data/Homeworks.ts @@ -70,11 +70,14 @@ const fetchHomeworks = async (): Promise => { ((SemaineAct - (firstDateEpoch % 52)) % 52) + 1 ).toString()}`, - body: `${differences.length} nouveaux devoirs (${differences + body: ` + ${differences.length} nouveaux devoirs ont été publiés :
+ ${differences .flatMap((element) => { - return element.subject; + return `- ${element.subject}`; }) - .join("/")}) ont été publiés`, + .join("
")} + `, ios: { categoryId: account.name, }, From 8c10b3fcb619ff4b4b007b51ab2498d862a5671c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Thu, 16 Jan 2025 20:05:36 +0100 Subject: [PATCH 0243/1144] feat(News): update notifications when many differencies (by @JyhuKo) --- src/background/data/News.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/background/data/News.ts b/src/background/data/News.ts index 822ef09b0..b114ac311 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -61,13 +61,12 @@ const fetchNews = async (): Promise => { id: `${account.name}-news`, title: `[${account.name}] Nouvelles actualités`, body: ` - ${differences.length} nouvelles actualités ont été publiées :
+ ${differences.length} nouvelles actualités ont été publiées par :
${differences .flatMap((element) => { - return `- ${element.title ?? "Sans titre"}`; + return `- ${element.author}`; }) - .join("
") - .slice(0, 200)}... + .join("
")} `, ios: { categoryId: account.name, From 4a1e218c2e99833214fdfa227ba80b697982460e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Thu, 16 Jan 2025 22:25:19 +0100 Subject: [PATCH 0244/1144] Update src/views/settings/SettingsNotifications.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Raphaël <41128238+raphckrman@users.noreply.github.com> --- src/views/settings/SettingsNotifications.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index cf39fbeb1..b5601d1ad 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -42,7 +42,7 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ notifications?.enabled || false ); useEffect(() => { - const test = async () => { + const handleNotificationPermission = async () => { const statut = await requestNotificationPermission(); if (!statut) { setEnabled(null); From 8fcc9a8fb38e7f4807d8b1c174fa73894fb3900d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Thu, 16 Jan 2025 22:25:42 +0100 Subject: [PATCH 0245/1144] Update src/views/settings/SettingsNotifications.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Raphaël <41128238+raphckrman@users.noreply.github.com> --- src/views/settings/SettingsNotifications.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index b5601d1ad..d24966dac 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -61,7 +61,7 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ } }; - test(); + handleNotificationPermission(); }, [enabled]); // Animation states From 839940f4e3cdaf4acc1669912807b9ba06582eab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Thu, 16 Jan 2025 22:26:18 +0100 Subject: [PATCH 0246/1144] Update src/views/settings/SettingsNotifications.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Raphaël <41128238+raphckrman@users.noreply.github.com> --- src/views/settings/SettingsNotifications.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index d24966dac..3bad62402 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -219,11 +219,11 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ }} >
From 9359a6774cd81650b0aa5a5b327ee7fcb9a566c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Thu, 16 Jan 2025 22:33:41 +0100 Subject: [PATCH 0247/1144] Update src/views/settings/SettingsNotifications.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Raphaël <41128238+raphckrman@users.noreply.github.com> --- src/views/settings/SettingsNotifications.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 3bad62402..1b731306b 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -211,7 +211,7 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ { title: "OK", onPress: () => {}, - backgroundColor: theme.colors.card, + primary: true, }, ], }); From 98bf34e385ab29e7daf84486bba5d0d3094bd5c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Fri, 17 Jan 2025 07:37:27 +0100 Subject: [PATCH 0248/1144] feat(BackgroundTasks): utilisation du logger de Papillon --- src/background/BackgroundTasks.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index ab212fa5c..d9f96473e 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -75,13 +75,13 @@ const registerBackgroundTasks = async () => { startOnBoot: true, // android only }); - console.log("[background fetch] Registered background fetch"); + log("[background fetch]", "Registered background fetch"); }); }; const unsetBackgroundFetch = async () => { BackgroundFetch.unregisterTaskAsync("background-fetch"); - console.log("[background fetch] Unregistered background fetch"); + log("[background fetch]", "Unregistered background fetch"); }; export { From 27a5d6881b22615c9bb212499f98e6ed5eab405e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= <164187100+Kgeek33@users.noreply.github.com> Date: Fri, 17 Jan 2025 13:55:08 +0100 Subject: [PATCH 0249/1144] Update src/views/settings/SettingsNotifications.tsx Co-authored-by: JyhuKo --- src/views/settings/SettingsNotifications.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 1b731306b..4c278eb83 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -117,7 +117,7 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ }, { icon: } color={colors.primary} />, - title: "Nouvelle événement sur la Vie Scolaire", + title: "Nouvel événement sur la Vie Scolaire", message: "Tu as été en retard de 5 min à 11:10", personalizationValue: "attendance", }, From 3d1a454c071be0fd4ed6ba4090fb5d61d914efde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 17 Jan 2025 13:58:15 +0100 Subject: [PATCH 0250/1144] =?UTF-8?q?fix(SettingsNotifications):=20simplif?= =?UTF-8?q?ication=20du=20titre=20de=20la=20notification=20"Nouvel=20?= =?UTF-8?q?=C3=A9v=C3=A9nement=20sur=20la=20Vie=20Scolaire"=20=C3=A0=20"Vi?= =?UTF-8?q?e=20Scolaire"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/SettingsNotifications.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/settings/SettingsNotifications.tsx b/src/views/settings/SettingsNotifications.tsx index 4c278eb83..1ab6e8d39 100644 --- a/src/views/settings/SettingsNotifications.tsx +++ b/src/views/settings/SettingsNotifications.tsx @@ -117,7 +117,7 @@ const SettingsNotifications: Screen<"SettingsNotifications"> = ({ }, { icon: } color={colors.primary} />, - title: "Nouvel événement sur la Vie Scolaire", + title: "Vie Scolaire", message: "Tu as été en retard de 5 min à 11:10", personalizationValue: "attendance", }, From 3ee9415d7a254d8efe45729f3795ac422e132850 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 17 Jan 2025 21:17:43 +0100 Subject: [PATCH 0251/1144] =?UTF-8?q?feat(BackgroundTasks):=20ajout=20de?= =?UTF-8?q?=20logs=20pour=20le=20suivi=20des=20t=C3=A2ches=20en=20arri?= =?UTF-8?q?=C3=A8re-plan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/BackgroundTasks.ts | 1 + src/background/data/Attendance.ts | 2 ++ src/background/data/Evaluation.ts | 2 ++ src/background/data/Grades.ts | 2 ++ src/background/data/Homeworks.ts | 2 ++ src/background/data/Lessons.ts | 2 ++ src/background/data/News.ts | 2 ++ 7 files changed, 13 insertions(+) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index d9f96473e..c9ede1dc6 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -62,6 +62,7 @@ const backgroundFetch = async () => { ]); } + log("[background fetch]", "Finish background fetch"); return BackgroundFetchResult.NewData; }; diff --git a/src/background/data/Attendance.ts b/src/background/data/Attendance.ts index ca8b8e1a0..7c2abe6e5 100644 --- a/src/background/data/Attendance.ts +++ b/src/background/data/Attendance.ts @@ -2,6 +2,7 @@ import { getCurrentAccount } from "../utils/accounts"; import { papillonNotify } from "../Notifications"; import { Attendance } from "@/services/shared/Attendance"; import { getAttendance, updateAttendanceState } from "../utils/attendance"; +import { log } from "@/utils/logger/logger"; const getDifferences = ( currentAttendance: Attendance, @@ -52,6 +53,7 @@ const getDifferences = ( }; const fetchAttendance = async (): Promise => { + log("[background fetch]", "Running background Attendance"); const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; diff --git a/src/background/data/Evaluation.ts b/src/background/data/Evaluation.ts index d0fe6961d..745af211c 100644 --- a/src/background/data/Evaluation.ts +++ b/src/background/data/Evaluation.ts @@ -2,6 +2,7 @@ import { getCurrentAccount } from "../utils/accounts"; import { papillonNotify } from "../Notifications"; import { Evaluation } from "@/services/shared/Evaluation"; import { getEvaluation, updateEvaluationState } from "../utils/evaluation"; +import { log } from "@/utils/logger/logger"; const getDifferences = ( currentEvaluation: Evaluation[], @@ -18,6 +19,7 @@ const getDifferences = ( }; const fetchEvaluation = async (): Promise => { + log("[background fetch]", "Running background Evaluation"); const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; diff --git a/src/background/data/Grades.ts b/src/background/data/Grades.ts index dac1917d6..ead257230 100644 --- a/src/background/data/Grades.ts +++ b/src/background/data/Grades.ts @@ -2,6 +2,7 @@ import { getCurrentAccount } from "../utils/accounts"; import { papillonNotify } from "../Notifications"; import { Grade } from "@/services/shared/Grade"; import { getGrades, updateGradeState } from "../utils/grades"; +import { log } from "@/utils/logger/logger"; const getDifferences = ( currentGrade: Grade[], @@ -18,6 +19,7 @@ const getDifferences = ( }; const fetchGrade = async (): Promise => { + log("[background fetch]", "Running background Grade"); const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; diff --git a/src/background/data/Homeworks.ts b/src/background/data/Homeworks.ts index d6ee82114..b90d9c079 100644 --- a/src/background/data/Homeworks.ts +++ b/src/background/data/Homeworks.ts @@ -3,6 +3,7 @@ import { papillonNotify } from "../Notifications"; import { getHomeworks, updateHomeworksState } from "../utils/homeworks"; import { Homework } from "@/services/shared/Homework"; import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; +import { log } from "@/utils/logger/logger"; const getDifferences = ( currentHomeworks: Homework[], @@ -18,6 +19,7 @@ const getDifferences = ( }; const fetchHomeworks = async (): Promise => { + log("[background fetch]", "Running background Homeworks"); const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index 4b4c57a42..a17a03ba2 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -3,6 +3,7 @@ import { papillonNotify } from "../Notifications"; import { getLessons, updateLessonsState } from "../utils/lessons"; import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; import { Timetable, TimetableClassStatus } from "@/services/shared/Timetable"; +import { log } from "@/utils/logger/logger"; const getAllLessonsForDay = (lessons: Record) => { const date = new Date(); @@ -21,6 +22,7 @@ const getAllLessonsForDay = (lessons: Record) => { }; const fetchLessons = async (): Promise => { + log("[background fetch]", "Running background Lessons"); const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; diff --git a/src/background/data/News.ts b/src/background/data/News.ts index b114ac311..02b058ea3 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -3,6 +3,7 @@ import { getCurrentAccount } from "../utils/accounts"; import { papillonNotify } from "../Notifications"; import parse_news_resume from "@/utils/format/format_pronote_news"; import { Information } from "@/services/shared/Information"; +import { log } from "@/utils/logger/logger"; const getDifferences = ( currentNews: Information[], @@ -19,6 +20,7 @@ const getDifferences = ( }; const fetchNews = async (): Promise => { + log("[background fetch]", "Running background News"); const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; From d06587426fac75bf0a9547ca8f6eb54489140bab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 17 Jan 2025 21:52:54 +0100 Subject: [PATCH 0252/1144] =?UTF-8?q?feat(BackgroundTasks):=20am=C3=A9lior?= =?UTF-8?q?ation=20de=20la=20gestion=20des=20t=C3=A2ches=20en=20arri=C3=A8?= =?UTF-8?q?re-plan=20avec=20des=20logs=20d'erreur=20et=20des=20avertisseme?= =?UTF-8?q?nts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.tsx | 1 + src/background/BackgroundTasks.ts | 76 +++++++++++++++++-------------- 2 files changed, 44 insertions(+), 33 deletions(-) diff --git a/App.tsx b/App.tsx index 8fc694b20..0261b7f2b 100644 --- a/App.tsx +++ b/App.tsx @@ -1,3 +1,4 @@ +import "@/background/BackgroundTasks"; import notifee, { EventType } from "@notifee/react-native"; import Router from "@/router"; import { useFonts } from "expo-font"; diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index c9ede1dc6..ebbba3caf 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -5,7 +5,7 @@ import { BackgroundFetchResult } from "expo-background-fetch"; import { expoGoWrapper } from "@/utils/native/expoGoAlert"; import { fetchNews } from "./data/News"; -import { log } from "@/utils/logger/logger"; +import { log, error, warn } from "@/utils/logger/logger"; import { getAccounts, getSwitchToFunction } from "./utils/accounts"; import { fetchHomeworks } from "./data/Homeworks"; import { fetchGrade } from "./data/Grades"; @@ -45,47 +45,57 @@ notifee.onBackgroundEvent(async ({ type, detail }) => { * @returns BackgroundFetchResult.NewData */ const backgroundFetch = async () => { - log("[background fetch]", "Running background fetch"); + log("Running background fetch", "BackgroundEvent"); - const accounts = getAccounts(); - const switchTo = getSwitchToFunction(); + try { + const accounts = getAccounts(); + const switchTo = getSwitchToFunction(); - for (const account of accounts) { - await switchTo(account); - await Promise.all([ - fetchNews(), - fetchHomeworks(), - fetchGrade(), - fetchLessons(), - fetchAttendance(), - fetchEvaluation(), - ]); - } + for (const account of accounts) { + await switchTo(account); + await Promise.all([ + fetchNews(), + fetchHomeworks(), + fetchGrade(), + fetchLessons(), + fetchAttendance(), + fetchEvaluation(), + ]); + } - log("[background fetch]", "Finish background fetch"); - return BackgroundFetchResult.NewData; + log("✅ Finish background fetch", "BackgroundEvent"); + return BackgroundFetchResult.NewData; + } catch (ERRfatal) { + error(`❌ Task failed: ${ERRfatal}`, "BackgroundEvent"); + return BackgroundFetchResult.Failed; + } }; -const registerBackgroundTasks = async () => { - expoGoWrapper(async () => { - TaskManager.defineTask("background-fetch", backgroundFetch); +TaskManager.defineTask("background-fetch", backgroundFetch); - BackgroundFetch?.registerTaskAsync("background-fetch", { - minimumInterval: 60 * 15, // 15 minutes - stopOnTerminate: false, // android only, - startOnBoot: true, // android only - }); +const registerBackgroundTasks = async () => { + await TaskManager.isTaskRegisteredAsync("background-fetch").then( + (isRegistered) => { + if (!isRegistered) { + expoGoWrapper(async () => { + await BackgroundFetch.registerTaskAsync("background-fetch", { + minimumInterval: 60 * 1, // 15 minutes + stopOnTerminate: false, // Maintenir après fermeture (Android) + startOnBoot: true, // Redémarrer au démarrage (Android) + }); - log("[background fetch]", "Registered background fetch"); - }); + log("✅ Background task registered", "BackgroundEvent"); + }); + } else { + warn("⚠️ Background task already registered", "BackgroundEvent"); + } + } + ); }; const unsetBackgroundFetch = async () => { - BackgroundFetch.unregisterTaskAsync("background-fetch"); - log("[background fetch]", "Unregistered background fetch"); + await BackgroundFetch.unregisterTaskAsync("background-fetch"); + log("✅ Background task unregistered", "BackgroundEvent"); }; -export { - registerBackgroundTasks, - unsetBackgroundFetch, -}; +export { registerBackgroundTasks, unsetBackgroundFetch }; From 5664056caca04e88370cfa7819d54a0fbb20d8c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 17 Jan 2025 21:54:07 +0100 Subject: [PATCH 0253/1144] =?UTF-8?q?refactor(BackgroundTasks):=20mise=20?= =?UTF-8?q?=C3=A0=20jour=20des=20logs=20pour=20les=20=C3=A9v=C3=A9nements?= =?UTF-8?q?=20en=20arri=C3=A8re-plan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/data/Attendance.ts | 2 +- src/background/data/Evaluation.ts | 2 +- src/background/data/Grades.ts | 2 +- src/background/data/Homeworks.ts | 2 +- src/background/data/Lessons.ts | 2 +- src/background/data/News.ts | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/background/data/Attendance.ts b/src/background/data/Attendance.ts index 7c2abe6e5..eac97b02d 100644 --- a/src/background/data/Attendance.ts +++ b/src/background/data/Attendance.ts @@ -53,7 +53,7 @@ const getDifferences = ( }; const fetchAttendance = async (): Promise => { - log("[background fetch]", "Running background Attendance"); + log("Running background Attendance", "BackgroundEvent"); const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; diff --git a/src/background/data/Evaluation.ts b/src/background/data/Evaluation.ts index 745af211c..d3a7cee69 100644 --- a/src/background/data/Evaluation.ts +++ b/src/background/data/Evaluation.ts @@ -19,7 +19,7 @@ const getDifferences = ( }; const fetchEvaluation = async (): Promise => { - log("[background fetch]", "Running background Evaluation"); + log("Running background Evaluation", "BackgroundEvent"); const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; diff --git a/src/background/data/Grades.ts b/src/background/data/Grades.ts index ead257230..b9226fbaa 100644 --- a/src/background/data/Grades.ts +++ b/src/background/data/Grades.ts @@ -19,7 +19,7 @@ const getDifferences = ( }; const fetchGrade = async (): Promise => { - log("[background fetch]", "Running background Grade"); + log("Running background Grade", "BackgroundEvent"); const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; diff --git a/src/background/data/Homeworks.ts b/src/background/data/Homeworks.ts index b90d9c079..d238d1954 100644 --- a/src/background/data/Homeworks.ts +++ b/src/background/data/Homeworks.ts @@ -19,7 +19,7 @@ const getDifferences = ( }; const fetchHomeworks = async (): Promise => { - log("[background fetch]", "Running background Homeworks"); + log("Running background Homeworks", "BackgroundEvent"); const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; diff --git a/src/background/data/Lessons.ts b/src/background/data/Lessons.ts index a17a03ba2..5ae4f466b 100644 --- a/src/background/data/Lessons.ts +++ b/src/background/data/Lessons.ts @@ -22,7 +22,7 @@ const getAllLessonsForDay = (lessons: Record) => { }; const fetchLessons = async (): Promise => { - log("[background fetch]", "Running background Lessons"); + log("Running background Lessons", "BackgroundEvent"); const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; diff --git a/src/background/data/News.ts b/src/background/data/News.ts index 02b058ea3..7d7c420a1 100644 --- a/src/background/data/News.ts +++ b/src/background/data/News.ts @@ -20,7 +20,7 @@ const getDifferences = ( }; const fetchNews = async (): Promise => { - log("[background fetch]", "Running background News"); + log("Running background News", "BackgroundEvent"); const account = getCurrentAccount(); const notificationsTypesPermissions = account.personalization.notifications; From fafdd79d9181c8eabcb12e373b619c5e82e240ea Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Fri, 17 Jan 2025 22:00:16 +0100 Subject: [PATCH 0254/1144] fix: bad comparaison Signed-off-by: Gabriel29306 --- src/utils/grades/getAverages.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/grades/getAverages.ts b/src/utils/grades/getAverages.ts index 8a8d306ae..75ebc6693 100644 --- a/src/utils/grades/getAverages.ts +++ b/src/utils/grades/getAverages.ts @@ -208,7 +208,7 @@ const getAveragesHistory = ( }); // remove NaN values - return history.filter((x) => !isNaN(x.value) || x.value < 0); + return history.filter((x) => !isNaN(x.value) || x.value !== -1); } catch(e) { return []; From a58bdba3e5a1814d376873b7d35b2ecfe0f3da8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 17 Jan 2025 22:05:42 +0100 Subject: [PATCH 0255/1144] =?UTF-8?q?fix(BackgroundTasks):=20retour=20?= =?UTF-8?q?=C3=A0=20un=20background=20de=2015=20minutes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/BackgroundTasks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/background/BackgroundTasks.ts b/src/background/BackgroundTasks.ts index ebbba3caf..1943dc97a 100644 --- a/src/background/BackgroundTasks.ts +++ b/src/background/BackgroundTasks.ts @@ -79,7 +79,7 @@ const registerBackgroundTasks = async () => { if (!isRegistered) { expoGoWrapper(async () => { await BackgroundFetch.registerTaskAsync("background-fetch", { - minimumInterval: 60 * 1, // 15 minutes + minimumInterval: 60 * 15, // 15 minutes stopOnTerminate: false, // Maintenir après fermeture (Android) startOnBoot: true, // Redémarrer au démarrage (Android) }); From a41fd1c85bbb0e8084654a0a48c1d074248c9714 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Fri, 17 Jan 2025 22:15:58 +0100 Subject: [PATCH 0256/1144] lint: wrong indentation Signed-off-by: Gabriel29306 --- .../account/Grades/Graph/GradesAverage.tsx | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/views/account/Grades/Graph/GradesAverage.tsx b/src/views/account/Grades/Graph/GradesAverage.tsx index d69d95739..7af1e585f 100644 --- a/src/views/account/Grades/Graph/GradesAverage.tsx +++ b/src/views/account/Grades/Graph/GradesAverage.tsx @@ -26,13 +26,13 @@ import { animPapillon } from "@/utils/ui/animations"; import * as Haptics from "expo-haptics"; import { PressableScale } from "react-native-pressable-scale"; import { ReanimatedGraphProps, ReanimatedGraphPublicMethods } from "@birdwingo/react-native-reanimated-graph/src/core/dto/graphDTO"; -// Using require to set custom types bc module types are broken -const ReanimatedGraph: React.ForwardRefExoticComponent> = require("@birdwingo/react-native-reanimated-graph").default; import { useCurrentAccount } from "@/stores/account"; import AnimatedNumber from "@/components/Global/AnimatedNumber"; import type { Grade } from "@/services/shared/Grade"; import { AlertTriangle, Check, ExternalLink, PieChart, TrendingUp } from "lucide-react-native"; import { useAlert } from "@/providers/AlertProvider"; +// Using require to set custom types bc module types are broken +const ReanimatedGraph: React.ForwardRefExoticComponent> = require("@birdwingo/react-native-reanimated-graph").default; interface GradesAverageGraphProps { grades: Grade[]; @@ -329,26 +329,26 @@ const GradesAverageGraph: React.FC = ({ - Moyenne classe - - {classAvg !== null ? ( - <> - - - /20 - - - ) : ( - Inconnue - )} - - + Moyenne classe + + {classAvg !== null ? ( + <> + + + /20 + + + ) : ( + Inconnue + )} + + {showDetails && maxAvg > 0 && minAvg > 0 ? ( From 242496dc20a12ffdf085ed4282006944390dd090 Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Fri, 17 Jan 2025 22:16:37 +0100 Subject: [PATCH 0257/1144] chore(deps): update pawnote to 1.4.0 Signed-off-by: Gabriel29306 --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 471e14703..157f20574 100644 --- a/package-lock.json +++ b/package-lock.json @@ -71,7 +71,7 @@ "openid-client": "^5.7.0", "pawdirecte": "^1.7.1", "pawnilim": "^0.2.0", - "pawnote": "^1.3.6", + "pawnote": "^1.4.0", "pawrd": "^0.6.1", "react": "18.2.0", "react-native": "^0.74.3", @@ -13938,9 +13938,9 @@ } }, "node_modules/pawnote": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/pawnote/-/pawnote-1.3.6.tgz", - "integrity": "sha512-dLJUmvgUaKmX4D98KqabD6Od1Tr3R7TZl2U1AzXBgmNjoANIdMyvYRQYFbWNgVT44KSk1DRj44nqbJhZN8Exzw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pawnote/-/pawnote-1.4.0.tgz", + "integrity": "sha512-AXf7jzf48oeTSwjo+7e2xBaAFitlZE3m4uc8Hpr3zEKiP+F7sZmLzZZozaV2fmvSVKE4PMXjAnYtfKPmE2TWQg==", "license": "GPL-3.0-or-later", "dependencies": { "@literate.ink/utilities": "1.0.0-10641118381.1", diff --git a/package.json b/package.json index b01b44fdf..f37432c3c 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "openid-client": "^5.7.0", "pawdirecte": "^1.7.1", "pawnilim": "^0.2.0", - "pawnote": "^1.3.6", + "pawnote": "^1.4.0", "pawrd": "^0.6.1", "react": "18.2.0", "react-native": "^0.74.3", From 967c9874a82c6fa50495d464d086ebfafacac7ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 17 Jan 2025 22:43:52 +0100 Subject: [PATCH 0258/1144] fix: eslint errors --- src/components/Grades/AnimatedEmoji.tsx | 2 +- src/components/Grades/GradeModal.tsx | 4 +- src/router/navigator/atoms/MenuItem.tsx | 8 ++-- src/router/navigator/atoms/TabItem.tsx | 6 +-- src/router/navigator/menu.tsx | 10 ++--- src/router/navigator/navigator.tsx | 3 +- src/router/navigator/tabs.tsx | 4 +- src/views/account/Chat/Modals/Chat.tsx | 2 +- src/views/account/Grades/Document.tsx | 6 +-- .../account/Grades/Graph/GradesAverage.tsx | 40 +++++++++---------- .../login/pronote/PronoteInstanceSelector.tsx | 1 - .../PriceDetectionOnboarding.tsx | 4 +- .../ExternalAccount/ServiceSelector.tsx | 3 +- src/views/settings/SettingsApparence.tsx | 1 - src/views/settings/SettingsReactions.tsx | 3 +- src/views/welcome/FirstInstallation.tsx | 5 +-- 16 files changed, 41 insertions(+), 61 deletions(-) diff --git a/src/components/Grades/AnimatedEmoji.tsx b/src/components/Grades/AnimatedEmoji.tsx index 7e12350d4..5e3d9d1a4 100644 --- a/src/components/Grades/AnimatedEmoji.tsx +++ b/src/components/Grades/AnimatedEmoji.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { View, Text } from "react-native"; +import { View } from "react-native"; import Animated, { useAnimatedStyle, useSharedValue, diff --git a/src/components/Grades/GradeModal.tsx b/src/components/Grades/GradeModal.tsx index a022b3822..6b09b29d3 100644 --- a/src/components/Grades/GradeModal.tsx +++ b/src/components/Grades/GradeModal.tsx @@ -5,13 +5,11 @@ import { Image, TouchableOpacity, Text, - Platform, Alert } from "react-native"; -import { Download, Trash, Maximize2, Share, Delete } from "lucide-react-native"; +import { Download, Trash, Share } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { BlurView } from "expo-blur"; -import { ScrollView } from "react-native-gesture-handler"; import * as Sharing from "expo-sharing"; import * as FileSystem from "expo-file-system"; import * as MediaLibrary from "expo-media-library"; diff --git a/src/router/navigator/atoms/MenuItem.tsx b/src/router/navigator/atoms/MenuItem.tsx index 3d57628b0..5d7976b31 100644 --- a/src/router/navigator/atoms/MenuItem.tsx +++ b/src/router/navigator/atoms/MenuItem.tsx @@ -1,13 +1,11 @@ import * as React from "react"; -import { useCurrentAccount } from "@/stores/account"; -import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, View, Text, Platform } from "react-native"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { useTheme } from "@react-navigation/native"; +import { StyleSheet, Platform } from "react-native"; import LottieView from "lottie-react-native"; import colorsList from "@/utils/data/colors.json"; import { Pressable } from "react-native-gesture-handler"; import * as Haptics from "expo-haptics"; -import Reanimated, { FadeIn, FadeOut, LinearTransition, ZoomIn } from "react-native-reanimated"; +import Reanimated, { FadeIn, FadeOut, LinearTransition } from "react-native-reanimated"; import { anim2Papillon } from "@/utils/ui/animations"; import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; diff --git a/src/router/navigator/atoms/TabItem.tsx b/src/router/navigator/atoms/TabItem.tsx index 790b97d2e..290a36e56 100644 --- a/src/router/navigator/atoms/TabItem.tsx +++ b/src/router/navigator/atoms/TabItem.tsx @@ -1,8 +1,6 @@ import * as React from "react"; -import { useCurrentAccount } from "@/stores/account"; -import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, View, Text, Platform } from "react-native"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { useTheme } from "@react-navigation/native"; +import { StyleSheet, Platform } from "react-native"; import LottieView from "lottie-react-native"; import colorsList from "@/utils/data/colors.json"; import { Pressable } from "react-native-gesture-handler"; diff --git a/src/router/navigator/menu.tsx b/src/router/navigator/menu.tsx index d12793b8b..12082b86c 100644 --- a/src/router/navigator/menu.tsx +++ b/src/router/navigator/menu.tsx @@ -1,17 +1,13 @@ -import React, { useEffect, useMemo, useState } from "react"; +import React, { useMemo, useState } from "react"; import { useCurrentAccount } from "@/stores/account"; import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, View, ScrollView, Platform, Image, Text, StatusBar } from "react-native"; +import { StyleSheet, Platform, Image, Text, StatusBar } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import TabItem from "./atoms/TabItem"; -import Reanimated, { LinearTransition, useSharedValue } from "react-native-reanimated"; +import Reanimated from "react-native-reanimated"; import MenuItem from "./atoms/MenuItem"; -import AccountSwitcher from "@/components/Home/AccountSwitcher"; import ContextMenu from "@/components/Home/AccountSwitcherContextMenu"; -import { NativeText } from "@/components/Global/NativeComponents"; import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; -import { he } from "date-fns/locale"; const PapillonNavigatorMenu: React.FC, "NavigationContent">> = ({ state, descriptors, navigation }) => { const theme = useTheme(); diff --git a/src/router/navigator/navigator.tsx b/src/router/navigator/navigator.tsx index 027e57dec..888137f44 100644 --- a/src/router/navigator/navigator.tsx +++ b/src/router/navigator/navigator.tsx @@ -1,8 +1,7 @@ import { BottomTabView } from "@react-navigation/bottom-tabs"; import { createNavigatorFactory, TabRouter, useNavigationBuilder } from "@react-navigation/native"; import PapillonNavigatorTabs from "./tabs"; -import { memo, useEffect, useMemo, useState } from "react"; -import { Dimensions, View } from "react-native"; +import { View } from "react-native"; import PapillonNavigatorMenu from "./menu"; import useScreenDimensions from "@/hooks/useScreenDimensions"; diff --git a/src/router/navigator/tabs.tsx b/src/router/navigator/tabs.tsx index a82d23b88..257f2c0a3 100644 --- a/src/router/navigator/tabs.tsx +++ b/src/router/navigator/tabs.tsx @@ -1,11 +1,11 @@ import React, { useEffect, useMemo, useState } from "react"; import { useCurrentAccount } from "@/stores/account"; import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, View, Platform } from "react-native"; +import { StyleSheet, Platform } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import TabItem from "./atoms/TabItem"; -import Reanimated, { LinearTransition } from "react-native-reanimated"; +import Reanimated from "react-native-reanimated"; const PapillonNavigatorTabs: React.FC, "NavigationContent">> = ({ state, descriptors, navigation }) => { const theme = useTheme(); diff --git a/src/views/account/Chat/Modals/Chat.tsx b/src/views/account/Chat/Modals/Chat.tsx index 90f2d5538..6f8bb8d36 100644 --- a/src/views/account/Chat/Modals/Chat.tsx +++ b/src/views/account/Chat/Modals/Chat.tsx @@ -6,7 +6,7 @@ import type {Screen} from "@/router/helpers/types"; import { NativeText,} from "@/components/Global/NativeComponents"; import {useCurrentAccount} from "@/stores/account"; import type {ChatMessage, ChatRecipient} from "@/services/shared/Chat"; -import {ChevronLeft, File, Link, Send} from "lucide-react-native"; +import {ChevronLeft, Send} from "lucide-react-native"; import parse_initials from "@/utils/format/format_pronote_initials"; import InitialIndicator from "@/components/News/InitialIndicator"; import {PapillonModernHeader} from "@/components/Global/PapillonModernHeader"; diff --git a/src/views/account/Grades/Document.tsx b/src/views/account/Grades/Document.tsx index 2e6481f20..9d148bda8 100644 --- a/src/views/account/Grades/Document.tsx +++ b/src/views/account/Grades/Document.tsx @@ -7,18 +7,14 @@ import { import { getSubjectData } from "@/services/shared/Subject"; import { useTheme } from "@react-navigation/native"; import React, { useCallback, useEffect, useLayoutEffect, useState } from "react"; -import { Image, ScrollView, Text, View, Platform, TouchableOpacity, Modal } from "react-native"; +import { Image, ScrollView, Text, View, Platform, TouchableOpacity } from "react-native"; import * as StoreReview from "expo-store-review"; import { Asterisk, Calculator, - Download, - Expand, Maximize2, Scale, School, - SmilePlus, - Trash, UserMinus, UserPlus, Users, diff --git a/src/views/account/Grades/Graph/GradesAverage.tsx b/src/views/account/Grades/Graph/GradesAverage.tsx index afbef6d32..384e20db7 100644 --- a/src/views/account/Grades/Graph/GradesAverage.tsx +++ b/src/views/account/Grades/Graph/GradesAverage.tsx @@ -331,26 +331,26 @@ const GradesAverageGraph: React.FC = ({ - Moyenne classe - - {classAvg !== null ? ( - <> - - - /20 - - - ) : ( - Inconnue - )} - - + Moyenne classe + + {classAvg !== null ? ( + <> + + + /20 + + + ) : ( + Inconnue + )} + + {showDetails && maxAvg > 0 && minAvg > 0 ? ( diff --git a/src/views/login/pronote/PronoteInstanceSelector.tsx b/src/views/login/pronote/PronoteInstanceSelector.tsx index c2bfd27df..62158b4d4 100644 --- a/src/views/login/pronote/PronoteInstanceSelector.tsx +++ b/src/views/login/pronote/PronoteInstanceSelector.tsx @@ -8,7 +8,6 @@ import { ActivityIndicator, Keyboard, KeyboardEvent, - Text, } from "react-native"; import pronote from "pawnote"; import Reanimated, { diff --git a/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx b/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx index 36bc869d0..a22b06d77 100644 --- a/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx +++ b/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx @@ -1,11 +1,11 @@ -import React, { useState } from "react"; +import React from "react"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { BellRing } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { Image, View, StyleSheet, Text } from "react-native"; import { NativeText, } from "@/components/Global/NativeComponents"; -import { useAccounts, useCurrentAccount } from "@/stores/account"; +import { useCurrentAccount } from "@/stores/account"; import { TouchableOpacity } from "react-native-gesture-handler"; import BetaIndicator from "@/components/News/Beta"; diff --git a/src/views/settings/ExternalAccount/ServiceSelector.tsx b/src/views/settings/ExternalAccount/ServiceSelector.tsx index 6f751bd9f..6c148cfba 100644 --- a/src/views/settings/ExternalAccount/ServiceSelector.tsx +++ b/src/views/settings/ExternalAccount/ServiceSelector.tsx @@ -10,8 +10,7 @@ import { AccountService } from "@/stores/account/types"; import { useCurrentAccount } from "@/stores/account"; import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; -import { LinearGradient } from "expo-linear-gradient"; - + const ExternalAccountSelector: Screen<"ExternalAccountSelector"> = ({ navigation, route }) => { const theme = useTheme(); const { colors } = theme; diff --git a/src/views/settings/SettingsApparence.tsx b/src/views/settings/SettingsApparence.tsx index 0bd6bc680..84e8fc2e8 100644 --- a/src/views/settings/SettingsApparence.tsx +++ b/src/views/settings/SettingsApparence.tsx @@ -7,7 +7,6 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import { NativeList, NativeItem, NativeListHeader } from "@/components/Global/NativeComponents"; import { NativeText } from "@/components/Global/NativeComponents"; import PapillonCheckbox from "@/components/Global/PapillonCheckbox"; -import Animated, {FadeInDown, FadeOutDown} from "react-native-reanimated"; import ApparenceContainerCard from "@/components/Settings/ApparenceContainerCard"; import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; diff --git a/src/views/settings/SettingsReactions.tsx b/src/views/settings/SettingsReactions.tsx index 49400a566..6b691d7f8 100644 --- a/src/views/settings/SettingsReactions.tsx +++ b/src/views/settings/SettingsReactions.tsx @@ -1,11 +1,10 @@ import React from "react"; -import { ScrollView, Text, View } from "react-native"; +import { ScrollView, View } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { useGradesStore } from "@/stores/grades"; import ReelGallery from "@/components/Settings/ReelGallery"; import MissingItem from "@/components/Global/MissingItem"; -import AnimatedEmoji from "@/components/Grades/AnimatedEmoji"; const SettingsReactions: Screen<"SettingsReactions"> = () => { const theme = useTheme(); diff --git a/src/views/welcome/FirstInstallation.tsx b/src/views/welcome/FirstInstallation.tsx index 2bfe903d5..c7f8d9be5 100644 --- a/src/views/welcome/FirstInstallation.tsx +++ b/src/views/welcome/FirstInstallation.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React from "react"; import { Image, Text, View, StyleSheet } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; @@ -11,8 +11,7 @@ import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { useTheme } from "@react-navigation/native"; import * as WebBrowser from "expo-web-browser"; -import * as SplashScreen from "expo-splash-screen"; - + const PRIVACY_POLICY_URL = "https://docs.papillon.bzh/legal/privacy"; const TERMS_OF_SERVICE_URL = "https://docs.papillon.bzh/legal/terms"; From 8d481558fa0b8dd06973305025223b798c4e5308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 17 Jan 2025 22:59:41 +0100 Subject: [PATCH 0259/1144] feat: ajouter un wrapper pour le son et les retours haptiques --- src/utils/native/playSoundHaptics.ts | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/utils/native/playSoundHaptics.ts diff --git a/src/utils/native/playSoundHaptics.ts b/src/utils/native/playSoundHaptics.ts new file mode 100644 index 000000000..8f91820af --- /dev/null +++ b/src/utils/native/playSoundHaptics.ts @@ -0,0 +1,31 @@ +import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; +import { Sound } from "expo-av/build/Audio"; +import * as Haptics from "expo-haptics"; + +const useSoundHapticsWrapper = () => { + const { enableHaptics, enableSon } = useThemeSoundHaptics(); + + const triggerHapticFeedback = async ( + type: "impact" | "notification", + // Objet pour éviter les erreurs TypeScript + haptic: { + impact?: Haptics.ImpactFeedbackStyle, + notification?: Haptics.NotificationFeedbackType + } + ) => { + if (enableHaptics) { + if (type === "impact") await Haptics.impactAsync(haptic.impact); + else await Haptics.notificationAsync(haptic.notification); + } + }; + + const playSound = (sound: Sound) => { + if (enableSon) { + sound.replayAsync(); + } + }; + + return { triggerHapticFeedback, playSound }; +}; + +export default useSoundHapticsWrapper; From 7b04ff088f6d36771371c9e8dc950452882c8f98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 17 Jan 2025 23:01:10 +0100 Subject: [PATCH 0260/1144] =?UTF-8?q?refactor:=20renommer=20triggerHapticF?= =?UTF-8?q?eedback=20en=20playHaptics=20pour=20plus=20de=20clart=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/native/playSoundHaptics.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/native/playSoundHaptics.ts b/src/utils/native/playSoundHaptics.ts index 8f91820af..d322e02de 100644 --- a/src/utils/native/playSoundHaptics.ts +++ b/src/utils/native/playSoundHaptics.ts @@ -5,7 +5,7 @@ import * as Haptics from "expo-haptics"; const useSoundHapticsWrapper = () => { const { enableHaptics, enableSon } = useThemeSoundHaptics(); - const triggerHapticFeedback = async ( + const playHaptics = async ( type: "impact" | "notification", // Objet pour éviter les erreurs TypeScript haptic: { @@ -25,7 +25,7 @@ const useSoundHapticsWrapper = () => { } }; - return { triggerHapticFeedback, playSound }; + return { playHaptics, playSound }; }; export default useSoundHapticsWrapper; From e63cee41b6a0b4620cbe5f08dc29da1811f66b40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Fri, 17 Jan 2025 23:10:51 +0100 Subject: [PATCH 0261/1144] =?UTF-8?q?refactor:=20am=C3=A9liorer=20la=20fon?= =?UTF-8?q?ction=20playSound=20pour=20utiliser=20un=20chemin=20de=20source?= =?UTF-8?q?=20de=20son?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/native/playSoundHaptics.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/utils/native/playSoundHaptics.ts b/src/utils/native/playSoundHaptics.ts index d322e02de..d4a177815 100644 --- a/src/utils/native/playSoundHaptics.ts +++ b/src/utils/native/playSoundHaptics.ts @@ -9,8 +9,8 @@ const useSoundHapticsWrapper = () => { type: "impact" | "notification", // Objet pour éviter les erreurs TypeScript haptic: { - impact?: Haptics.ImpactFeedbackStyle, - notification?: Haptics.NotificationFeedbackType + impact?: Haptics.ImpactFeedbackStyle; + notification?: Haptics.NotificationFeedbackType; } ) => { if (enableHaptics) { @@ -19,9 +19,11 @@ const useSoundHapticsWrapper = () => { } }; - const playSound = (sound: Sound) => { + const playSound = async (srcSound: string) => { if (enableSon) { - sound.replayAsync(); + const { sound } = await Sound.createAsync(require(srcSound)); + await sound.setPositionAsync(0); + await sound.playAsync(); } }; From be25edcbeaef4c14c29d626a2eab87d39960d545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 18 Jan 2025 00:33:50 +0100 Subject: [PATCH 0262/1144] refactor: utilisation du wrapper --- .../FirstInstallation/ButtonCta.tsx | 8 ++-- .../FirstInstallation/DuoListPressable.tsx | 21 +++------ src/components/Global/PapillonCheckbox.tsx | 14 +++--- .../Home/AccountSwitcherContextMenu.tsx | 17 ++++---- src/router/navigator/atoms/MenuItem.tsx | 8 ++-- src/router/navigator/atoms/TabItem.tsx | 8 ++-- .../account/Grades/Graph/GradesAverage.tsx | 10 +++-- src/views/account/Home/Home.tsx | 8 ++-- src/views/account/Homeworks/Homeworks.tsx | 8 ++-- .../Lessons/Atoms/LessonsDatePicker.tsx | 14 +++--- .../pronote/PronoteAuthenticationSelector.tsx | 28 ++---------- src/views/login/pronote/PronoteQRCode.tsx | 35 +++------------ src/views/login/pronote/PronoteWebview.tsx | 43 ++----------------- .../SkolengoAuthenticationSelector.tsx | 28 ++---------- src/views/login/skolengo/SkolengoWebview.tsx | 41 ++---------------- .../ExternalAccount/QrcodeScanner.tsx | 12 +++--- 16 files changed, 89 insertions(+), 214 deletions(-) diff --git a/src/components/FirstInstallation/ButtonCta.tsx b/src/components/FirstInstallation/ButtonCta.tsx index d4c2b0ef4..cca330764 100644 --- a/src/components/FirstInstallation/ButtonCta.tsx +++ b/src/components/FirstInstallation/ButtonCta.tsx @@ -3,7 +3,7 @@ import { Text, Pressable, StyleSheet, type StyleProp, type ViewStyle } from "rea import Reanimated, { Easing, useSharedValue, withTiming } from "react-native-reanimated"; import { useTheme } from "@react-navigation/native"; import * as Haptics from "expo-haptics"; -import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; +import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; const ButtonCta: React.FC<{ value: string @@ -22,12 +22,12 @@ const ButtonCta: React.FC<{ backgroundColor, icon, }) => { + const { playHaptics } = useSoundHapticsWrapper(); const { colors } = useTheme(); const [pressed, setPressed] = useState(false); const scale = useSharedValue(1); const opacity = useSharedValue(1); - const { enableHaptics } = useThemeSoundHaptics(); if (!backgroundColor) { backgroundColor = primary ? colors.primary : "transparent"; @@ -43,7 +43,9 @@ const ButtonCta: React.FC<{ scale.value = withTiming(1, { duration: 0, easing: Easing.linear }); scale.value = withTiming(0.95, { duration: 50, easing: Easing.linear }); opacity.value = withTiming(0.7, { duration: 10, easing: Easing.linear }); - if (enableHaptics) Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy); + playHaptics("impact", { + impact: Haptics.ImpactFeedbackStyle.Heavy, + }); } else { scale.value = withTiming(1, { duration: 100, easing: Easing.linear }); diff --git a/src/components/FirstInstallation/DuoListPressable.tsx b/src/components/FirstInstallation/DuoListPressable.tsx index 34f9e5a45..99b8a2f65 100644 --- a/src/components/FirstInstallation/DuoListPressable.tsx +++ b/src/components/FirstInstallation/DuoListPressable.tsx @@ -1,12 +1,11 @@ -import React, { useCallback, useEffect, useState } from "react"; +import React, { useEffect, useState } from "react"; import { View, Text, Pressable, StyleSheet } from "react-native"; import { useTheme } from "@react-navigation/native"; import * as Haptics from "expo-haptics"; -import { Audio } from "expo-av"; import Reanimated, { Easing, useSharedValue, withTiming } from "react-native-reanimated"; -import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; +import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; const DuoListPressable: React.FC<{ children?: JSX.Element, @@ -32,23 +31,17 @@ const DuoListPressable: React.FC<{ const scale = useSharedValue(1); const opacity = useSharedValue(1); - const { enableSon, enableHaptics } = useThemeSoundHaptics(); - - const playSound = useCallback(async () => { - const { sound } = await Audio.Sound.createAsync( - require("@/../assets/sound/click_003.wav") - ); - await sound.setPositionAsync(0); - await sound.playAsync(); - }, []); + const { playHaptics, playSound } = useSoundHapticsWrapper(); useEffect(() => { if (pressed) { scale.value = withTiming(1, { duration: 0, easing: Easing.linear }); scale.value = withTiming(0.95, { duration: 50, easing: Easing.linear }); opacity.value = withTiming(0.7, { duration: 10, easing: Easing.linear }); - if (enableHaptics) Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); - if (enableSon) playSound(); + playHaptics("impact", { + impact: Haptics.ImpactFeedbackStyle.Light, + }); + playSound("@/../assets/sound/click_003.wav"); } else { scale.value = withTiming(1, { duration: 100, easing: Easing.linear }); diff --git a/src/components/Global/PapillonCheckbox.tsx b/src/components/Global/PapillonCheckbox.tsx index da1b90757..1ba2296b2 100644 --- a/src/components/Global/PapillonCheckbox.tsx +++ b/src/components/Global/PapillonCheckbox.tsx @@ -8,7 +8,7 @@ import { Check } from "lucide-react-native"; import * as Haptics from "expo-haptics"; import PapillonSpinner from "./PapillonSpinner"; import { animPapillon } from "@/utils/ui/animations"; -import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; +import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; interface CheckboxProps { checked?: boolean @@ -29,7 +29,7 @@ const PapillonCheckbox: React.FC = ({ }) => { const theme = useTheme(); const firstRender = useRef(true); - const { enableHaptics } = useThemeSoundHaptics(); + const { playHaptics } = useSoundHapticsWrapper(); useEffect(() => { if (firstRender.current) { @@ -42,14 +42,18 @@ const PapillonCheckbox: React.FC = ({ const pressAction = () => { onPress(); - if (enableHaptics) Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); + playHaptics("impact", { + impact: Haptics.ImpactFeedbackStyle.Light, + }); setHasPressed(true); }; // on checked change useEffect(() => { - if (checked && hasPressed && loaded && enableHaptics) { - Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); + if (checked && hasPressed && loaded) { + playHaptics("notification", { + notification: Haptics.NotificationFeedbackType.Success, + }); } }, [checked, hasPressed]); diff --git a/src/components/Home/AccountSwitcherContextMenu.tsx b/src/components/Home/AccountSwitcherContextMenu.tsx index 914ebef71..cc9946f3f 100644 --- a/src/components/Home/AccountSwitcherContextMenu.tsx +++ b/src/components/Home/AccountSwitcherContextMenu.tsx @@ -27,7 +27,7 @@ import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; import { useTheme } from "@react-navigation/native"; import { BlurView } from "expo-blur"; import { Check, Cog, Plus } from "lucide-react-native"; -import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; +import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; const ContextMenu: React.FC<{ style?: any; @@ -39,7 +39,7 @@ const ContextMenu: React.FC<{ const theme = useTheme(); const { colors } = theme; const navigation = useNavigation(); - const { enableHaptics } = useThemeSoundHaptics(); + const { playHaptics } = useSoundHapticsWrapper(); const [opened, setOpened] = useState(false); // État pour gérer l'ouverture du menu contextuel @@ -55,11 +55,6 @@ const ContextMenu: React.FC<{ } }, [shouldOpenContextMenu]); - // Fonction pour activer un effet haptique à l'ouverture du menu - const openEffects = () => { - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); - }; - return ( <> { setOpened(!opened); - if (enableHaptics) openEffects(); + playHaptics("impact", { + impact: Haptics.ImpactFeedbackStyle.Light, + }); }} // @ts-expect-error pointerEvents="auto" @@ -92,7 +89,9 @@ const ContextMenu: React.FC<{ { setOpened(!opened); - if (enableHaptics) openEffects(); + playHaptics("impact", { + impact: Haptics.ImpactFeedbackStyle.Light, + }); }} useForeground={true} style={{ diff --git a/src/router/navigator/atoms/MenuItem.tsx b/src/router/navigator/atoms/MenuItem.tsx index 5d7976b31..1598a4c88 100644 --- a/src/router/navigator/atoms/MenuItem.tsx +++ b/src/router/navigator/atoms/MenuItem.tsx @@ -7,7 +7,7 @@ import { Pressable } from "react-native-gesture-handler"; import * as Haptics from "expo-haptics"; import Reanimated, { FadeIn, FadeOut, LinearTransition } from "react-native-reanimated"; import { anim2Papillon } from "@/utils/ui/animations"; -import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; +import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; const MenuItem: React.FC<{ route: any; @@ -16,7 +16,7 @@ const MenuItem: React.FC<{ isFocused: boolean; }> = ({ route, descriptor, navigation, isFocused }) => { const theme = useTheme(); - const { enableHaptics } = useThemeSoundHaptics(); + const { playHaptics } = useSoundHapticsWrapper(); const { options } = descriptor; const label = options.tabBarLabel !== undefined ? options.tabBarLabel : options.title !== undefined ? options.title : route.name; @@ -33,7 +33,9 @@ const MenuItem: React.FC<{ } lottieRef.current?.play(); - if (enableHaptics) Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); + playHaptics("impact", { + impact: Haptics.ImpactFeedbackStyle.Light, + }); }; const onLongPress = () => { diff --git a/src/router/navigator/atoms/TabItem.tsx b/src/router/navigator/atoms/TabItem.tsx index 290a36e56..bd34f6d66 100644 --- a/src/router/navigator/atoms/TabItem.tsx +++ b/src/router/navigator/atoms/TabItem.tsx @@ -7,7 +7,7 @@ import { Pressable } from "react-native-gesture-handler"; import * as Haptics from "expo-haptics"; import Reanimated, { FadeIn, FadeOut, LinearTransition, ZoomIn } from "react-native-reanimated"; import { anim2Papillon } from "@/utils/ui/animations"; -import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; +import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; const TabItem: React.FC<{ route: any; @@ -17,7 +17,7 @@ const TabItem: React.FC<{ settings: any; }> = ({ route, descriptor, navigation, isFocused, settings }) => { const theme = useTheme(); - const { enableHaptics } = useThemeSoundHaptics(); + const { playHaptics } = useSoundHapticsWrapper(); const { options } = descriptor; const label = options.tabBarLabel !== undefined ? options.tabBarLabel : options.title !== undefined ? options.title : route.name; @@ -37,7 +37,9 @@ const TabItem: React.FC<{ lottieRef.current.play(); } - if (enableHaptics) Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); + playHaptics("impact", { + impact: Haptics.ImpactFeedbackStyle.Light, + }); }; const onLongPress = () => { diff --git a/src/views/account/Grades/Graph/GradesAverage.tsx b/src/views/account/Grades/Graph/GradesAverage.tsx index 384e20db7..a28bbf572 100644 --- a/src/views/account/Grades/Graph/GradesAverage.tsx +++ b/src/views/account/Grades/Graph/GradesAverage.tsx @@ -33,7 +33,7 @@ import AnimatedNumber from "@/components/Global/AnimatedNumber"; import type { Grade } from "@/services/shared/Grade"; import { AlertTriangle, Check, ExternalLink, PieChart, TrendingUp } from "lucide-react-native"; import { useAlert } from "@/providers/AlertProvider"; -import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; +import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; interface GradesAverageGraphProps { grades: Grade[]; @@ -49,7 +49,7 @@ const GradesAverageGraph: React.FC = ({ const theme = useTheme(); const account = useCurrentAccount((store) => store.account!); const { showAlert } = useAlert(); - const { enableHaptics } = useThemeSoundHaptics(); + const { playHaptics } = useSoundHapticsWrapper(); const [gradesHistory, setGradesHistory] = useState([]); const [hLength, setHLength] = useState(0); @@ -68,8 +68,10 @@ const GradesAverageGraph: React.FC = ({ const gradesHistoryRef = useRef(gradesHistory); useEffect(() => { - if (currentAvg !== originalCurrentAvg && enableHaptics) { - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); + if (currentAvg !== originalCurrentAvg) { + playHaptics("impact", { + impact: Haptics.ImpactFeedbackStyle.Light, + }); } }, [currentAvg]); diff --git a/src/views/account/Home/Home.tsx b/src/views/account/Home/Home.tsx index 3fdf81370..16b330bdc 100644 --- a/src/views/account/Home/Home.tsx +++ b/src/views/account/Home/Home.tsx @@ -61,14 +61,14 @@ import * as Haptics from "expo-haptics"; import ModalContent from "@/views/account/Home/ModalContent"; import {AnimatedScrollView} from "react-native-reanimated/lib/typescript/reanimated2/component/ScrollView"; import useScreenDimensions from "@/hooks/useScreenDimensions"; -import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; +import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; const Home: Screen<"HomeScreen"> = ({ navigation }) => { const { colors } = useTheme(); const insets = useSafeAreaInsets(); const corners = useMemo(() => getCorners(), []); const focused = useIsFocused(); - const { enableHaptics } = useThemeSoundHaptics(); + const { playHaptics } = useSoundHapticsWrapper(); const {isTablet} = useScreenDimensions(); @@ -253,7 +253,9 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { }} onScroll={(e) => { if (e.nativeEvent.contentOffset.y > 125 && canHaptics) { - if (enableHaptics) Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); + playHaptics("impact", { + impact: Haptics.ImpactFeedbackStyle.Light, + }); setCanHaptics(false); } else if (e.nativeEvent.contentOffset.y < 125 && !canHaptics) { setCanHaptics(true); diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index ae6cba126..d2331cfd1 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -38,7 +38,7 @@ import {Screen} from "@/router/helpers/types"; import {NativeSyntheticEvent} from "react-native/Libraries/Types/CoreEventTypes"; import {NativeScrollEvent, ScrollViewProps} from "react-native/Libraries/Components/ScrollView/ScrollView"; import AsyncStorage from "@react-native-async-storage/async-storage"; -import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; +import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; type HomeworksPageProps = { index: number; @@ -66,7 +66,7 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { 320 ) : 0); const insets = useSafeAreaInsets(); - const { enableHaptics } = useThemeSoundHaptics(); + const { playHaptics } = useSoundHapticsWrapper(); const outsideNav = route.params?.outsideNav; @@ -411,7 +411,9 @@ const WeekView: Screen<"Homeworks"> = ({ route, navigation }) => { onPress={() => setShowPickerButtons(!showPickerButtons)} onLongPress={() => { setHideDone(!hideDone); - if (enableHaptics) Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); + playHaptics("notification", { + notification: Haptics.NotificationFeedbackType.Success, + }); }} delayLongPress={200} > diff --git a/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx b/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx index 5911b4754..bad66eccf 100644 --- a/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx +++ b/src/views/account/Lessons/Atoms/LessonsDatePicker.tsx @@ -17,7 +17,7 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import {Theme} from "@react-navigation/native/src/types"; import {NativeScrollEvent, ScrollViewProps} from "react-native/Libraries/Components/ScrollView/ScrollView"; import {NativeSyntheticEvent} from "react-native/Libraries/Types/CoreEventTypes"; -import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; +import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; const { width: SCREEN_WIDTH } = Dimensions.get("window"); const ITEM_WIDTH = 104; @@ -110,7 +110,7 @@ const HorizontalDatePicker = ({ onDateSelect, onCurrentDatePress, initialDate = const flatListRef = useRef(null); const scrollX = useSharedValue(0); const lastItemIndex = useSharedValue(0); - const { enableHaptics } = useThemeSoundHaptics(); + const { playHaptics } = useSoundHapticsWrapper(); const { colors } = useTheme(); const insets = useSafeAreaInsets(); @@ -164,10 +164,6 @@ const HorizontalDatePicker = ({ onDateSelect, onCurrentDatePress, initialDate = index, }), []); - const triggerHaptic = useCallback(() => { - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); - }, []); - const scrollHandler = useAnimatedScrollHandler({ onScroll: (event) => { scrollX.value = event.contentOffset.x; @@ -175,8 +171,10 @@ const HorizontalDatePicker = ({ onDateSelect, onCurrentDatePress, initialDate = if (currentItemIndex !== lastItemIndex.value) { lastItemIndex.value = currentItemIndex; runOnJS(setIsProgrammaticScroll)(false); - if (!isProgrammaticScroll && enableHaptics) { - runOnJS(triggerHaptic)(); + if (!isProgrammaticScroll) { + runOnJS(playHaptics)("impact", { + impact: Haptics.ImpactFeedbackStyle.Light, + }); } } }, diff --git a/src/views/login/pronote/PronoteAuthenticationSelector.tsx b/src/views/login/pronote/PronoteAuthenticationSelector.tsx index 29d2fc50c..bcaf8d3cb 100644 --- a/src/views/login/pronote/PronoteAuthenticationSelector.tsx +++ b/src/views/login/pronote/PronoteAuthenticationSelector.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useState } from "react"; import { StyleSheet, View } from "react-native"; import { QrCodeIcon, LinkIcon, MapPinIcon, SearchIcon, LockIcon } from "lucide-react-native"; @@ -10,37 +10,17 @@ import MaskStars from "@/components/FirstInstallation/MaskStars"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; import Reanimated, { LinearTransition, FlipInXDown } from "react-native-reanimated"; import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; -import { Audio } from "expo-av"; import { NativeText } from "@/components/Global/NativeComponents"; import { LinearGradient } from "expo-linear-gradient"; -import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; +import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; const PronoteAuthenticationSelector: Screen<"PronoteAuthenticationSelector"> = ({ navigation }) => { const theme = useTheme(); type Methods = "geolocation" | "manual-location" | "manual-url" | "qr-code"; const [method, setMethod] = useState(null); - const [sound, setSound] = useState(null); - const { enableSon } = useThemeSoundHaptics(); - - const loadSound = async () => { - const { sound } = await Audio.Sound.createAsync( - require("@/../assets/sound/2.wav") - ); - - setSound(sound); - }; - - useEffect(() => { - loadSound(); - - return () => { - sound?.unloadAsync(); - }; - }, []); - - const playSound = () => void sound?.replayAsync(); + const { playSound } = useSoundHapticsWrapper(); const handleConfirmation = () => { switch (method) { @@ -58,7 +38,7 @@ const PronoteAuthenticationSelector: Screen<"PronoteAuthenticationSelector"> = ( break; } - if (enableSon) playSound(); + playSound("@/../assets/sound/2.wav"); }; return ( diff --git a/src/views/login/pronote/PronoteQRCode.tsx b/src/views/login/pronote/PronoteQRCode.tsx index f9d9df556..4b4fbc4f2 100644 --- a/src/views/login/pronote/PronoteQRCode.tsx +++ b/src/views/login/pronote/PronoteQRCode.tsx @@ -16,10 +16,9 @@ import pronote from "pawnote"; import { useAccounts, useCurrentAccount } from "@/stores/account"; import { Account, AccountService } from "@/stores/account/types"; -import { Audio } from "expo-av"; import defaultPersonalization from "@/services/pronote/default-personalization"; import extract_pronote_name from "@/utils/format/extract_pronote_name"; -import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; +import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; const makeUUID = (): string => { let dt = new Date().getTime(); @@ -44,7 +43,6 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { const { colors } = theme; const [hasPermission, setHasPermission] = useState(null); const [scanned, setScanned] = useState(false); - const [sound, setSound] = useState(null); const [inputFocus, setInputFocus] = useState(false); @@ -56,7 +54,7 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { const codeInput = React.createRef(); const [QRData, setQRData] = useState(null); - const { enableSon, enableHaptics } = useThemeSoundHaptics(); + const { playHaptics, playSound } = useSoundHapticsWrapper(); async function loginQR () { setScanned(false); @@ -133,7 +131,7 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { queueMicrotask(() => { // Reset the navigation stack to the "Home" screen. // Prevents the user from going back to the login screen. - if (enableSon) playSound(); + playSound("@/../assets/sound/4.wav"); navigation.reset({ index: 0, routes: [{ name: "AccountCreated" }], @@ -148,29 +146,6 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { } } - React.useEffect(() => { - const loadSound = async () => { - const { sound } = await Audio.Sound.createAsync( - require("@/../assets/sound/4.wav") - ); - setSound(sound); - }; - - loadSound(); - - return () => { - if (sound) { - sound.unloadAsync(); - } - }; - }, []); - - const playSound = async () => { - if (sound) { - await sound.replayAsync(); - } - }; - useEffect(() => { const getBarCodeScannerPermissions = async () => { const { status } = await BarCodeScanner.requestPermissionsAsync(); @@ -187,7 +162,9 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { data: string; }) => { setScanned(true); - if (enableHaptics) Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); + playHaptics("notification", { + notification: Haptics.NotificationFeedbackType.Success, + }); setQRData(data); setPinModalVisible(true); }; diff --git a/src/views/login/pronote/PronoteWebview.tsx b/src/views/login/pronote/PronoteWebview.tsx index 43b9f55da..1d41e5ee7 100644 --- a/src/views/login/pronote/PronoteWebview.tsx +++ b/src/views/login/pronote/PronoteWebview.tsx @@ -24,8 +24,6 @@ import Reanimated, { import pronote from "pawnote"; -import { Audio } from "expo-av"; - import { useAccounts, useCurrentAccount } from "@/stores/account"; import { Account, AccountService } from "@/stores/account/types"; import uuid from "@/utils/uuid-v4"; @@ -34,7 +32,7 @@ import extract_pronote_name from "@/utils/format/extract_pronote_name"; import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { animPapillon } from "@/utils/ui/animations"; import { useAlert } from "@/providers/AlertProvider"; -import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; +import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; const PronoteWebview: Screen<"PronoteWebview"> = ({ route, navigation }) => { const theme = useTheme(); @@ -48,12 +46,10 @@ const PronoteWebview: Screen<"PronoteWebview"> = ({ route, navigation }) => { const [, setCurrentURL] = useState(""); const [deviceUUID] = useState(uuid()); - const [sound, setSound] = useState(null); - const [sound2, setSound2] = useState(null); const [loginStep, setLoginStep] = useState("Préparation de la connexion"); - const { enableSon } = useThemeSoundHaptics(); + const { playSound } = useSoundHapticsWrapper(); const instanceURL = route.params.instanceURL.toLowerCase(); @@ -77,38 +73,7 @@ const PronoteWebview: Screen<"PronoteWebview"> = ({ route, navigation }) => { ).toUTCString(); useEffect(() => { - const loadSound = async () => { - const { sound } = await Audio.Sound.createAsync( - require("@/../assets/sound/3.wav") - ); - setSound(sound); - const sound2 = await Audio.Sound.createAsync( - require("@/../assets/sound/4.wav") - ); - setSound2(sound2.sound); - await sound.replayAsync(); - }; - - loadSound(); - - return () => { - if (sound) { - sound.unloadAsync(); - } - if (sound2) { - sound2.unloadAsync(); - } - }; - }, []); - - const playSound = async () => { - if (sound) { - await sound2?.replayAsync(); - } - }; - - useEffect(() => { - if (enableSon) playSound(); + playSound("@/../assets/sound/3.wav"); }, []); const INJECT_PRONOTE_JSON = ` @@ -360,7 +325,7 @@ const PronoteWebview: Screen<"PronoteWebview"> = ({ route, navigation }) => { queueMicrotask(() => { // Reset the navigation stack to the "Home" screen. // Prevents the user from going back to the login screen. - if (enableSon) playSound(); + playSound("@/../assets/sound/4.wav"); navigation.reset({ index: 0, routes: [{ name: "AccountCreated" }], diff --git a/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx b/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx index 86d9b747c..6633b2320 100644 --- a/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx +++ b/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useState } from "react"; import { StyleSheet, View } from "react-native"; import { MapPinIcon, SearchIcon, LockIcon } from "lucide-react-native"; @@ -10,36 +10,16 @@ import MaskStars from "@/components/FirstInstallation/MaskStars"; import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBubble"; import Reanimated, { LinearTransition, FlipInXDown } from "react-native-reanimated"; import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; -import { Audio } from "expo-av"; import { NativeText } from "@/components/Global/NativeComponents"; -import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; +import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; const SkolengoAuthenticationSelector: Screen<"SkolengoAuthenticationSelector"> = ({ navigation }) => { const theme = useTheme(); type Methods = "geolocation" | "manual-location" | "manual-url" | "qr-code"; const [method, setMethod] = useState(null); - const [sound, setSound] = useState(null); - const { enableSon } = useThemeSoundHaptics(); - - const loadSound = async () => { - const { sound } = await Audio.Sound.createAsync( - require("@/../assets/sound/2.wav") - ); - - setSound(sound); - }; - - useEffect(() => { - loadSound(); - - return () => { - sound?.unloadAsync(); - }; - }, []); - - const playSound = () => void sound?.replayAsync(); + const { playSound } = useSoundHapticsWrapper(); const handleConfirmation = () => { switch (method) { @@ -51,7 +31,7 @@ const SkolengoAuthenticationSelector: Screen<"SkolengoAuthenticationSelector"> = break; } - if (enableSon) playSound(); + playSound("@/../assets/sound/2.wav"); }; return ( diff --git a/src/views/login/skolengo/SkolengoWebview.tsx b/src/views/login/skolengo/SkolengoWebview.tsx index 568c55a0c..4a64ba858 100644 --- a/src/views/login/skolengo/SkolengoWebview.tsx +++ b/src/views/login/skolengo/SkolengoWebview.tsx @@ -23,11 +23,10 @@ import * as AuthSession from "expo-auth-session"; import { OID_CLIENT_ID, OID_CLIENT_SECRET, REDIRECT_URI } from "scolengo-api"; import { useAlert } from "@/providers/AlertProvider"; import { useAccounts, useCurrentAccount } from "@/stores/account"; -import { Audio } from "expo-av"; import { authTokenToSkolengoTokenSet } from "@/services/skolengo/skolengo-types"; import { getSkolengoAccount } from "@/services/skolengo/skolengo-account"; import { wait } from "@/services/skolengo/data/utils"; -import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; +import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; // TODO : When the app is not started with Expo Go (so with a prebuild or a release build), use the expo auth-session module completely with the deeplink and without the webview. @@ -42,7 +41,7 @@ const SkolengoWebview: Screen<"SkolengoWebview"> = ({ route, navigation }) => { const [pageUrl, setPageUrl] = useState(null); const [discovery, setDiscovery] = useState(null); - const { enableSon } = useThemeSoundHaptics(); + const { playSound } = useSoundHapticsWrapper(); const createStoredAccount = useAccounts((store) => store.create); const switchTo = useCurrentAccount((store) => store.switchTo); @@ -59,42 +58,8 @@ const SkolengoWebview: Screen<"SkolengoWebview"> = ({ route, navigation }) => { let webViewRef = createRef(); - const [sound, setSound] = useState(null); - const [sound2, setSound2] = useState(null); - - useEffect(() => { - const loadSound = async () => { - const { sound } = await Audio.Sound.createAsync( - require("@/../assets/sound/3.wav") - ); - setSound(sound); - const sound2 = await Audio.Sound.createAsync( - require("@/../assets/sound/4.wav") - ); - setSound2(sound2.sound); - await sound.replayAsync(); - }; - - loadSound(); - - return () => { - if (sound) { - sound.unloadAsync(); - } - if (sound2) { - sound2.unloadAsync(); - } - }; - }, []); - - const playSound = async () => { - if (sound) { - await sound2?.replayAsync(); - } - }; - useEffect(() => { - if (enableSon) playSound(); + playSound("@/../assets/sound/3.wav"); }, []); return ( diff --git a/src/views/settings/ExternalAccount/QrcodeScanner.tsx b/src/views/settings/ExternalAccount/QrcodeScanner.tsx index 54b2b0b26..4af58033a 100644 --- a/src/views/settings/ExternalAccount/QrcodeScanner.tsx +++ b/src/views/settings/ExternalAccount/QrcodeScanner.tsx @@ -5,14 +5,14 @@ import { QrCode } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { View, StyleSheet, Text } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; - - + + import { ExternalAccount } from "@/stores/account/types"; import { useAccounts } from "@/stores/account"; import { BarCodeScanner } from "expo-barcode-scanner"; import MaskedView from "@react-native-masked-view/masked-view"; import * as Haptics from "expo-haptics"; -import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; +import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; type Props = { navigation: any; @@ -27,7 +27,7 @@ const QrcodeScanner: Screen<"QrcodeScanner"> = ({ navigation, route }) => { const accountID = route.params?.accountID; const [hasPermission, setHasPermission] = React.useState(null); const [scanned, setScanned] = React.useState(false); - const { enableHaptics } = useThemeSoundHaptics(); + const { playHaptics } = useSoundHapticsWrapper(); useEffect(() => { const getBarCodeScannerPermissions = async () => { @@ -45,7 +45,9 @@ const QrcodeScanner: Screen<"QrcodeScanner"> = ({ navigation, route }) => { data: string; }) => { setScanned(true); - if (enableHaptics) Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); + playHaptics("notification", { + notification: Haptics.NotificationFeedbackType.Success, + }); update(accountID, "data", { "qrcodedata": data, "qrcodetype": type }); navigation.navigate("PriceDetectionOnboarding", { accountID }); }; From 487d6a5d075d4883473350a86605a93bce332d4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 18 Jan 2025 00:49:03 +0100 Subject: [PATCH 0263/1144] fix: Invalid call in require --- src/components/FirstInstallation/DuoListPressable.tsx | 2 +- src/utils/native/playSoundHaptics.ts | 4 ++-- src/views/login/pronote/PronoteAuthenticationSelector.tsx | 2 +- src/views/login/pronote/PronoteQRCode.tsx | 2 +- src/views/login/pronote/PronoteWebview.tsx | 4 ++-- src/views/login/skolengo/SkolengoAuthenticationSelector.tsx | 2 +- src/views/login/skolengo/SkolengoWebview.tsx | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/FirstInstallation/DuoListPressable.tsx b/src/components/FirstInstallation/DuoListPressable.tsx index 99b8a2f65..fa8f4e8fa 100644 --- a/src/components/FirstInstallation/DuoListPressable.tsx +++ b/src/components/FirstInstallation/DuoListPressable.tsx @@ -41,7 +41,7 @@ const DuoListPressable: React.FC<{ playHaptics("impact", { impact: Haptics.ImpactFeedbackStyle.Light, }); - playSound("@/../assets/sound/click_003.wav"); + playSound(require("@/../assets/sound/click_003.wav")); } else { scale.value = withTiming(1, { duration: 100, easing: Easing.linear }); diff --git a/src/utils/native/playSoundHaptics.ts b/src/utils/native/playSoundHaptics.ts index d4a177815..acfdaedd6 100644 --- a/src/utils/native/playSoundHaptics.ts +++ b/src/utils/native/playSoundHaptics.ts @@ -19,9 +19,9 @@ const useSoundHapticsWrapper = () => { } }; - const playSound = async (srcSound: string) => { + const playSound = async (srcSound: any) => { if (enableSon) { - const { sound } = await Sound.createAsync(require(srcSound)); + const { sound } = await Sound.createAsync(srcSound); await sound.setPositionAsync(0); await sound.playAsync(); } diff --git a/src/views/login/pronote/PronoteAuthenticationSelector.tsx b/src/views/login/pronote/PronoteAuthenticationSelector.tsx index bcaf8d3cb..77d70daa6 100644 --- a/src/views/login/pronote/PronoteAuthenticationSelector.tsx +++ b/src/views/login/pronote/PronoteAuthenticationSelector.tsx @@ -38,7 +38,7 @@ const PronoteAuthenticationSelector: Screen<"PronoteAuthenticationSelector"> = ( break; } - playSound("@/../assets/sound/2.wav"); + playSound(require("@/../assets/sound/2.wav")); }; return ( diff --git a/src/views/login/pronote/PronoteQRCode.tsx b/src/views/login/pronote/PronoteQRCode.tsx index 4b4fbc4f2..3f5cbf2c6 100644 --- a/src/views/login/pronote/PronoteQRCode.tsx +++ b/src/views/login/pronote/PronoteQRCode.tsx @@ -131,7 +131,7 @@ const PronoteQRCode: Screen<"PronoteQRCode"> = ({ navigation }) => { queueMicrotask(() => { // Reset the navigation stack to the "Home" screen. // Prevents the user from going back to the login screen. - playSound("@/../assets/sound/4.wav"); + playSound(require("@/../assets/sound/4.wav")); navigation.reset({ index: 0, routes: [{ name: "AccountCreated" }], diff --git a/src/views/login/pronote/PronoteWebview.tsx b/src/views/login/pronote/PronoteWebview.tsx index 1d41e5ee7..965249ea2 100644 --- a/src/views/login/pronote/PronoteWebview.tsx +++ b/src/views/login/pronote/PronoteWebview.tsx @@ -73,7 +73,7 @@ const PronoteWebview: Screen<"PronoteWebview"> = ({ route, navigation }) => { ).toUTCString(); useEffect(() => { - playSound("@/../assets/sound/3.wav"); + playSound(require("@/../assets/sound/3.wav")); }, []); const INJECT_PRONOTE_JSON = ` @@ -325,7 +325,7 @@ const PronoteWebview: Screen<"PronoteWebview"> = ({ route, navigation }) => { queueMicrotask(() => { // Reset the navigation stack to the "Home" screen. // Prevents the user from going back to the login screen. - playSound("@/../assets/sound/4.wav"); + playSound(require("@/../assets/sound/4.wav")); navigation.reset({ index: 0, routes: [{ name: "AccountCreated" }], diff --git a/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx b/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx index 6633b2320..82385f944 100644 --- a/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx +++ b/src/views/login/skolengo/SkolengoAuthenticationSelector.tsx @@ -31,7 +31,7 @@ const SkolengoAuthenticationSelector: Screen<"SkolengoAuthenticationSelector"> = break; } - playSound("@/../assets/sound/2.wav"); + playSound(require("@/../assets/sound/2.wav")); }; return ( diff --git a/src/views/login/skolengo/SkolengoWebview.tsx b/src/views/login/skolengo/SkolengoWebview.tsx index 4a64ba858..f6b4acf93 100644 --- a/src/views/login/skolengo/SkolengoWebview.tsx +++ b/src/views/login/skolengo/SkolengoWebview.tsx @@ -59,7 +59,7 @@ const SkolengoWebview: Screen<"SkolengoWebview"> = ({ route, navigation }) => { let webViewRef = createRef(); useEffect(() => { - playSound("@/../assets/sound/3.wav"); + playSound(require("@/../assets/sound/3.wav")); }, []); return ( From db9c82dc8bb889953bb00b228dc8a6337557d9a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 18 Jan 2025 00:57:34 +0100 Subject: [PATCH 0264/1144] refractor: utilisation du wrapper V2 --- src/views/login/ServiceSelector.tsx | 34 +++------------ src/views/welcome/AccountCreated.tsx | 62 ++++++---------------------- src/views/welcome/ColorSelector.tsx | 57 ++++--------------------- 3 files changed, 27 insertions(+), 126 deletions(-) diff --git a/src/views/login/ServiceSelector.tsx b/src/views/login/ServiceSelector.tsx index 28a4d8d90..1f2a4e149 100644 --- a/src/views/login/ServiceSelector.tsx +++ b/src/views/login/ServiceSelector.tsx @@ -10,17 +10,15 @@ import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import MaskStars from "@/components/FirstInstallation/MaskStars"; import { useAlert } from "@/providers/AlertProvider"; -import { Audio } from "expo-av"; import { useTheme } from "@react-navigation/native"; import GetV6Data from "@/utils/login/GetV6Data"; import { School } from "lucide-react-native"; import { LinearGradient } from "expo-linear-gradient"; -import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; +import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { const theme = useTheme(); const { colors } = theme; - const [sound, setSound] = useState(null); const { showAlert } = useAlert(); @@ -29,7 +27,7 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { const [v6Data, setV6Data] = useState(null); - const { enableSon } = useThemeSoundHaptics(); + const { playSound } = useSoundHapticsWrapper(); useEffect(() => { setTimeout(async () => { @@ -54,7 +52,7 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { image: require("../../../assets/images/service_pronote.png"), login: () => { navigation.navigate("PronoteAuthenticationSelector"); - if (enableSon) playSound(); + playSound(require("@/../assets/sound/1.wav")); }, }, { @@ -63,7 +61,7 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { image: require("../../../assets/images/service_ed.png"), login: () => { navigation.navigate("EcoleDirecteCredentials"); - if (enableSon) playSound(); + playSound(require("@/../assets/sound/1.wav")); } }, { @@ -72,7 +70,7 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { image: require("../../../assets/images/service_skolengo.png"), login: () => { navigation.navigate("SkolengoAuthenticationSelector"); - if (enableSon) playSound(); + playSound(require("@/../assets/sound/1.wav")); } }, { @@ -83,7 +81,7 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { icon: , login: () => { navigation.navigate("IdentityProviderSelector"); - if (enableSon) playSound(); + playSound(require("@/../assets/sound/1.wav")); } }, ]; @@ -95,26 +93,6 @@ const ServiceSelector: Screen<"ServiceSelector"> = ({ navigation }) => { }); }; - useEffect(() => { - const loadSound = async () => { - const { sound } = await Audio.Sound.createAsync( - require("@/../assets/sound/1.wav") - ); - - setSound(sound); - }; - - loadSound(); - - return () => { - if (sound) { - sound.unloadAsync(); - } - }; - }, []); - - const playSound = () => sound?.replayAsync(); - return ( diff --git a/src/views/welcome/AccountCreated.tsx b/src/views/welcome/AccountCreated.tsx index 6d99d2c12..1434a1e6a 100644 --- a/src/views/welcome/AccountCreated.tsx +++ b/src/views/welcome/AccountCreated.tsx @@ -13,50 +13,11 @@ import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBub import * as Haptics from "expo-haptics"; import { useCurrentAccount } from "@/stores/account"; -import { Audio } from "expo-av"; -import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; +import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; const AccountCreated: Screen<"AccountCreated"> = ({ navigation }) => { - const [sound, setSound] = useState(null); - const [sound2, setSound2] = useState(null); - const account = useCurrentAccount((state) => state.account!); - const { enableSon, enableHaptics } = useThemeSoundHaptics(); - - const loadSound = async () => { - const { sound } = await Audio.Sound.createAsync( - require("@/../assets/sound/5.wav") - ); - - setSound(sound); - - const { sound: sound2 } = await Audio.Sound.createAsync( - require("@/../assets/sound/6.wav") - ); - - setSound2(sound2); - }; - - useEffect(() => { - loadSound(); - - return () => { - sound?.unloadAsync(); - sound2?.unloadAsync(); - }; - }, []); - - const playSound = async () => { - if (sound) { - await sound.replayAsync(); - } - }; - - const playSound2 = async () => { - if (sound) { - await sound2?.replayAsync(); - } - }; + const { playHaptics, playSound } = useSoundHapticsWrapper(); let name = !account.studentName?.first ? null : account.studentName?.first; @@ -74,13 +35,14 @@ const AccountCreated: Screen<"AccountCreated"> = ({ navigation }) => { const unsubscribe = navigation.addListener("focus", () => { setShowAnimation(true); - if (enableHaptics) { - // loop 20 times - for (let i = 0; i < 15; i++) { - setTimeout(() => { - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium); - }, i * 20); - } + // loop 20 times + for (let i = 0; i < 15; i++) { + setTimeout(() => { + Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium); + playHaptics("impact", { + impact: Haptics.ImpactFeedbackStyle.Medium, + }); + }, i * 20); } }); @@ -134,14 +96,14 @@ const AccountCreated: Screen<"AccountCreated"> = ({ navigation }) => { primary onPress={() => { navigation.navigate("ColorSelector"); - if (enableSon) playSound(); + playSound(require("@/../assets/sound/5.wav")); }} /> { navigation.navigate("AccountStack", { onboard: true }); - if (enableSon) playSound2(); + playSound(require("@/../assets/sound/6.wav")); }} /> diff --git a/src/views/welcome/ColorSelector.tsx b/src/views/welcome/ColorSelector.tsx index c8d9d7136..dc56994f3 100644 --- a/src/views/welcome/ColorSelector.tsx +++ b/src/views/welcome/ColorSelector.tsx @@ -1,4 +1,4 @@ -import React, { useLayoutEffect, useState, useEffect } from "react"; +import React, { useLayoutEffect } from "react"; import { View, StyleSheet, Pressable, Platform } from "react-native"; import MaskStarsColored from "@/components/FirstInstallation/MaskStarsColored"; import { useTheme } from "@react-navigation/native"; @@ -8,7 +8,6 @@ import ButtonCta from "@/components/FirstInstallation/ButtonCta"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useCurrentAccount } from "@/stores/account"; import { LinearGradient } from "expo-linear-gradient"; -import { Audio } from "expo-av"; import Reanimated, { ZoomIn, ZoomOut, LinearTransition, FadeIn, FadeOut, FlipInXDown, FadeOutUp } from "react-native-reanimated"; import * as Haptics from "expo-haptics"; import { getIconName, setIconName } from "@candlefinance/app-icon"; @@ -16,7 +15,7 @@ import { getIconName, setIconName } from "@candlefinance/app-icon"; import colorsList from "@/utils/data/colors.json"; import { removeColor } from "../settings/SettingsIcons"; import { expoGoWrapper } from "@/utils/native/expoGoAlert"; -import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; +import useSoundHapticsWrapper from "@/utils/native/playSoundHaptics"; type Color = typeof colorsList[number]; @@ -27,10 +26,7 @@ const ColorSelector: Screen<"ColorSelector"> = ({ route, navigation }) => { const account = useCurrentAccount(store => store.account); const mutateProperty = useCurrentAccount(store => store.mutateProperty); const settings = route.params?.settings || false; - const { enableSon, enableHaptics } = useThemeSoundHaptics(); - - const [sound, setSound] = useState(null); - const [sound2, setSound2] = useState(null); + const { playHaptics, playSound } = useSoundHapticsWrapper(); useLayoutEffect(() => { navigation.setOptions({ @@ -40,51 +36,16 @@ const ColorSelector: Screen<"ColorSelector"> = ({ route, navigation }) => { }); }, [navigation]); - useEffect(() => { - const loadSound = async () => { - const { sound } = await Audio.Sound.createAsync( - require("@/../assets/sound/6.wav") - ); - setSound(sound); - - const sound2 = await Audio.Sound.createAsync( - require("@/../assets/sound/click_003.wav") - ); - setSound2(sound2.sound); - }; - - loadSound(); - - return () => { - if (sound) { - sound.unloadAsync(); - } - if (sound2) { - sound2.unloadAsync(); - } - }; - }, []); - - const playSound = async () => { - if (sound) { - await sound.replayAsync(); - } - }; - - const playSound2 = async () => { - if (sound) { - await sound2?.replayAsync(); - } - }; - const messages = colorsList.map((color) => ({ [color.hex.primary]: color.description })).reduce((acc, cur) => ({ ...acc, ...cur }), {} as { [key: string]: string }); const selectColor = (color: Color) => { mutateProperty("personalization", { color }); - if (enableHaptics) Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); - if (enableSon) playSound2(); + playHaptics("notification", { + notification: Haptics.NotificationFeedbackType.Success, + }); + playSound(require("@/../assets/sound/click_003.wav")); expoGoWrapper(() => { getIconName().then((currentIcon) => { @@ -217,8 +178,8 @@ const ColorSelector: Screen<"ColorSelector"> = ({ route, navigation }) => { primary value="Finaliser" onPress={async () => { - if (!settings && enableSon) { - await playSound(); + if (!settings) { + playSound(require("@/../assets/sound/6.wav")); } navigation.navigate("AccountStack", {onboard: true}); }} From d5e3ac7c10fc9de24b13569151c64d043e9177ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 18 Jan 2025 01:11:03 +0100 Subject: [PATCH 0265/1144] fix: eslint errors --- src/views/settings/SettingsApparence.tsx | 1 - src/views/settings/SettingsDevLogs.tsx | 5 ----- src/views/settings/SettingsProfile.tsx | 1 - 3 files changed, 7 deletions(-) diff --git a/src/views/settings/SettingsApparence.tsx b/src/views/settings/SettingsApparence.tsx index 5ca77bf2c..dd17eeae4 100644 --- a/src/views/settings/SettingsApparence.tsx +++ b/src/views/settings/SettingsApparence.tsx @@ -8,7 +8,6 @@ import { NativeList, NativeItem, NativeListHeader } from "@/components/Global/Na import { NativeText } from "@/components/Global/NativeComponents"; import PapillonCheckbox from "@/components/Global/PapillonCheckbox"; import ApparenceContainerCard from "@/components/Settings/ApparenceContainerCard"; -import * as Brightness from "expo-brightness"; import { useThemeSoundHaptics } from "@/hooks/Theme_Sound_Haptics"; const SettingsApparence: Screen<"SettingsApparence"> = () => { diff --git a/src/views/settings/SettingsDevLogs.tsx b/src/views/settings/SettingsDevLogs.tsx index 70257a648..cc67637be 100644 --- a/src/views/settings/SettingsDevLogs.tsx +++ b/src/views/settings/SettingsDevLogs.tsx @@ -9,7 +9,6 @@ import { } from "@/components/Global/NativeComponents"; import React, { useEffect, useState } from "react"; import { - get_brute_logs, get_logs, Log, delete_logs, @@ -30,14 +29,10 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import { PressableScale } from "react-native-pressable-scale"; import { FadeInDown, - FadeInUp, - FadeOutDown, FadeOutUp, } from "react-native-reanimated"; import { animPapillon } from "@/utils/ui/animations"; -import News from "../account/News/News"; import { useTheme } from "@react-navigation/native"; -import Reanimated from "react-native-reanimated"; const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { const theme = useTheme(); diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index 56f717b6c..c7630b773 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -10,7 +10,6 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import * as Clipboard from "expo-clipboard"; import { TouchableOpacity } from "react-native-gesture-handler"; -import { th } from "date-fns/locale"; const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { const theme = useTheme(); From 2b48055afe459071b729408820b52b11b8c32abc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 18 Jan 2025 01:13:04 +0100 Subject: [PATCH 0266/1144] fix: eslint errors --- src/components/Grades/AnimatedEmoji.tsx | 2 +- src/components/Grades/GradeModal.tsx | 4 +- src/router/navigator/atoms/MenuItem.tsx | 8 ++-- src/router/navigator/atoms/TabItem.tsx | 6 +-- src/router/navigator/menu.tsx | 10 ++--- src/router/navigator/navigator.tsx | 3 +- src/router/navigator/tabs.tsx | 4 +- src/views/account/Chat/Modals/Chat.tsx | 2 +- src/views/account/Grades/Document.tsx | 6 +-- .../account/Grades/Graph/GradesAverage.tsx | 40 +++++++++---------- src/views/account/Homeworks/Homeworks.tsx | 1 - .../login/pronote/PronoteInstanceSelector.tsx | 1 - .../PriceDetectionOnboarding.tsx | 4 +- .../ExternalAccount/ServiceSelector.tsx | 3 +- src/views/settings/SettingsDevLogs.tsx | 5 --- src/views/settings/SettingsProfile.tsx | 1 - src/views/settings/SettingsReactions.tsx | 3 +- 17 files changed, 39 insertions(+), 64 deletions(-) diff --git a/src/components/Grades/AnimatedEmoji.tsx b/src/components/Grades/AnimatedEmoji.tsx index 7e12350d4..5e3d9d1a4 100644 --- a/src/components/Grades/AnimatedEmoji.tsx +++ b/src/components/Grades/AnimatedEmoji.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { View, Text } from "react-native"; +import { View } from "react-native"; import Animated, { useAnimatedStyle, useSharedValue, diff --git a/src/components/Grades/GradeModal.tsx b/src/components/Grades/GradeModal.tsx index a022b3822..6b09b29d3 100644 --- a/src/components/Grades/GradeModal.tsx +++ b/src/components/Grades/GradeModal.tsx @@ -5,13 +5,11 @@ import { Image, TouchableOpacity, Text, - Platform, Alert } from "react-native"; -import { Download, Trash, Maximize2, Share, Delete } from "lucide-react-native"; +import { Download, Trash, Share } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { BlurView } from "expo-blur"; -import { ScrollView } from "react-native-gesture-handler"; import * as Sharing from "expo-sharing"; import * as FileSystem from "expo-file-system"; import * as MediaLibrary from "expo-media-library"; diff --git a/src/router/navigator/atoms/MenuItem.tsx b/src/router/navigator/atoms/MenuItem.tsx index 2a9b548ce..ff64a6de5 100644 --- a/src/router/navigator/atoms/MenuItem.tsx +++ b/src/router/navigator/atoms/MenuItem.tsx @@ -1,13 +1,11 @@ import * as React from "react"; -import { useCurrentAccount } from "@/stores/account"; -import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, View, Text, Platform } from "react-native"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { useTheme } from "@react-navigation/native"; +import { StyleSheet, Platform } from "react-native"; import LottieView from "lottie-react-native"; import colorsList from "@/utils/data/colors.json"; import { Pressable } from "react-native-gesture-handler"; import * as Haptics from "expo-haptics"; -import Reanimated, { FadeIn, FadeOut, LinearTransition, ZoomIn } from "react-native-reanimated"; +import Reanimated, { FadeIn, FadeOut, LinearTransition } from "react-native-reanimated"; import { anim2Papillon } from "@/utils/ui/animations"; const MenuItem: React.FC<{ diff --git a/src/router/navigator/atoms/TabItem.tsx b/src/router/navigator/atoms/TabItem.tsx index 90da917e3..cc8fe2536 100644 --- a/src/router/navigator/atoms/TabItem.tsx +++ b/src/router/navigator/atoms/TabItem.tsx @@ -1,8 +1,6 @@ import * as React from "react"; -import { useCurrentAccount } from "@/stores/account"; -import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, View, Text, Platform } from "react-native"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { useTheme } from "@react-navigation/native"; +import { StyleSheet, Platform } from "react-native"; import LottieView from "lottie-react-native"; import colorsList from "@/utils/data/colors.json"; import { Pressable } from "react-native-gesture-handler"; diff --git a/src/router/navigator/menu.tsx b/src/router/navigator/menu.tsx index d12793b8b..12082b86c 100644 --- a/src/router/navigator/menu.tsx +++ b/src/router/navigator/menu.tsx @@ -1,17 +1,13 @@ -import React, { useEffect, useMemo, useState } from "react"; +import React, { useMemo, useState } from "react"; import { useCurrentAccount } from "@/stores/account"; import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, View, ScrollView, Platform, Image, Text, StatusBar } from "react-native"; +import { StyleSheet, Platform, Image, Text, StatusBar } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import TabItem from "./atoms/TabItem"; -import Reanimated, { LinearTransition, useSharedValue } from "react-native-reanimated"; +import Reanimated from "react-native-reanimated"; import MenuItem from "./atoms/MenuItem"; -import AccountSwitcher from "@/components/Home/AccountSwitcher"; import ContextMenu from "@/components/Home/AccountSwitcherContextMenu"; -import { NativeText } from "@/components/Global/NativeComponents"; import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; -import { he } from "date-fns/locale"; const PapillonNavigatorMenu: React.FC, "NavigationContent">> = ({ state, descriptors, navigation }) => { const theme = useTheme(); diff --git a/src/router/navigator/navigator.tsx b/src/router/navigator/navigator.tsx index 027e57dec..888137f44 100644 --- a/src/router/navigator/navigator.tsx +++ b/src/router/navigator/navigator.tsx @@ -1,8 +1,7 @@ import { BottomTabView } from "@react-navigation/bottom-tabs"; import { createNavigatorFactory, TabRouter, useNavigationBuilder } from "@react-navigation/native"; import PapillonNavigatorTabs from "./tabs"; -import { memo, useEffect, useMemo, useState } from "react"; -import { Dimensions, View } from "react-native"; +import { View } from "react-native"; import PapillonNavigatorMenu from "./menu"; import useScreenDimensions from "@/hooks/useScreenDimensions"; diff --git a/src/router/navigator/tabs.tsx b/src/router/navigator/tabs.tsx index a82d23b88..257f2c0a3 100644 --- a/src/router/navigator/tabs.tsx +++ b/src/router/navigator/tabs.tsx @@ -1,11 +1,11 @@ import React, { useEffect, useMemo, useState } from "react"; import { useCurrentAccount } from "@/stores/account"; import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, View, Platform } from "react-native"; +import { StyleSheet, Platform } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import TabItem from "./atoms/TabItem"; -import Reanimated, { LinearTransition } from "react-native-reanimated"; +import Reanimated from "react-native-reanimated"; const PapillonNavigatorTabs: React.FC, "NavigationContent">> = ({ state, descriptors, navigation }) => { const theme = useTheme(); diff --git a/src/views/account/Chat/Modals/Chat.tsx b/src/views/account/Chat/Modals/Chat.tsx index 90f2d5538..6f8bb8d36 100644 --- a/src/views/account/Chat/Modals/Chat.tsx +++ b/src/views/account/Chat/Modals/Chat.tsx @@ -6,7 +6,7 @@ import type {Screen} from "@/router/helpers/types"; import { NativeText,} from "@/components/Global/NativeComponents"; import {useCurrentAccount} from "@/stores/account"; import type {ChatMessage, ChatRecipient} from "@/services/shared/Chat"; -import {ChevronLeft, File, Link, Send} from "lucide-react-native"; +import {ChevronLeft, Send} from "lucide-react-native"; import parse_initials from "@/utils/format/format_pronote_initials"; import InitialIndicator from "@/components/News/InitialIndicator"; import {PapillonModernHeader} from "@/components/Global/PapillonModernHeader"; diff --git a/src/views/account/Grades/Document.tsx b/src/views/account/Grades/Document.tsx index fc1b941eb..fae7edee5 100644 --- a/src/views/account/Grades/Document.tsx +++ b/src/views/account/Grades/Document.tsx @@ -7,18 +7,14 @@ import { import { getSubjectData } from "@/services/shared/Subject"; import { useTheme } from "@react-navigation/native"; import React, { useCallback, useEffect, useLayoutEffect, useState } from "react"; -import { Image, ScrollView, Text, View, Platform, TouchableOpacity, Modal } from "react-native"; +import { Image, ScrollView, Text, View, Platform, TouchableOpacity } from "react-native"; import * as StoreReview from "expo-store-review"; import { Asterisk, Calculator, - Download, - Expand, Maximize2, Scale, School, - SmilePlus, - Trash, UserMinus, UserPlus, Users, diff --git a/src/views/account/Grades/Graph/GradesAverage.tsx b/src/views/account/Grades/Graph/GradesAverage.tsx index d69d95739..f6e96a7d0 100644 --- a/src/views/account/Grades/Graph/GradesAverage.tsx +++ b/src/views/account/Grades/Graph/GradesAverage.tsx @@ -329,26 +329,26 @@ const GradesAverageGraph: React.FC = ({ - Moyenne classe - - {classAvg !== null ? ( - <> - - - /20 - - - ) : ( - Inconnue - )} - - + Moyenne classe + + {classAvg !== null ? ( + <> + + + /20 + + + ) : ( + Inconnue + )} + + {showDetails && maxAvg > 0 && minAvg > 0 ? ( diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index af4ac16fb..3a9820817 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -32,7 +32,6 @@ import * as Haptics from "expo-haptics"; import MissingItem from "@/components/Global/MissingItem"; import { PapillonModernHeader } from "@/components/Global/PapillonModernHeader"; import {Homework} from "@/services/shared/Homework"; -import {Account} from "@/stores/account/types"; import {Screen} from "@/router/helpers/types"; import {NativeSyntheticEvent} from "react-native/Libraries/Types/CoreEventTypes"; import {NativeScrollEvent, ScrollViewProps} from "react-native/Libraries/Components/ScrollView/ScrollView"; diff --git a/src/views/login/pronote/PronoteInstanceSelector.tsx b/src/views/login/pronote/PronoteInstanceSelector.tsx index 71ebd7a8f..1c291ad17 100644 --- a/src/views/login/pronote/PronoteInstanceSelector.tsx +++ b/src/views/login/pronote/PronoteInstanceSelector.tsx @@ -9,7 +9,6 @@ import { Keyboard, KeyboardEvent, SafeAreaView, - Text, } from "react-native"; import pronote from "pawnote"; import Reanimated, { diff --git a/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx b/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx index 36bc869d0..a22b06d77 100644 --- a/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx +++ b/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx @@ -1,11 +1,11 @@ -import React, { useState } from "react"; +import React from "react"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { BellRing } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { Image, View, StyleSheet, Text } from "react-native"; import { NativeText, } from "@/components/Global/NativeComponents"; -import { useAccounts, useCurrentAccount } from "@/stores/account"; +import { useCurrentAccount } from "@/stores/account"; import { TouchableOpacity } from "react-native-gesture-handler"; import BetaIndicator from "@/components/News/Beta"; diff --git a/src/views/settings/ExternalAccount/ServiceSelector.tsx b/src/views/settings/ExternalAccount/ServiceSelector.tsx index 09d915b0a..df9cb9a1a 100644 --- a/src/views/settings/ExternalAccount/ServiceSelector.tsx +++ b/src/views/settings/ExternalAccount/ServiceSelector.tsx @@ -7,8 +7,7 @@ import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBub import { AccountService } from "@/stores/account/types"; import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; -import { LinearGradient } from "expo-linear-gradient"; - + const ExternalAccountSelector: Screen<"ExternalAccountSelector"> = ({ navigation }) => { type Service = AccountService | "Other"; diff --git a/src/views/settings/SettingsDevLogs.tsx b/src/views/settings/SettingsDevLogs.tsx index 70257a648..cc67637be 100644 --- a/src/views/settings/SettingsDevLogs.tsx +++ b/src/views/settings/SettingsDevLogs.tsx @@ -9,7 +9,6 @@ import { } from "@/components/Global/NativeComponents"; import React, { useEffect, useState } from "react"; import { - get_brute_logs, get_logs, Log, delete_logs, @@ -30,14 +29,10 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import { PressableScale } from "react-native-pressable-scale"; import { FadeInDown, - FadeInUp, - FadeOutDown, FadeOutUp, } from "react-native-reanimated"; import { animPapillon } from "@/utils/ui/animations"; -import News from "../account/News/News"; import { useTheme } from "@react-navigation/native"; -import Reanimated from "react-native-reanimated"; const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { const theme = useTheme(); diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index 56f717b6c..c7630b773 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -10,7 +10,6 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import * as Clipboard from "expo-clipboard"; import { TouchableOpacity } from "react-native-gesture-handler"; -import { th } from "date-fns/locale"; const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { const theme = useTheme(); diff --git a/src/views/settings/SettingsReactions.tsx b/src/views/settings/SettingsReactions.tsx index 49400a566..6b691d7f8 100644 --- a/src/views/settings/SettingsReactions.tsx +++ b/src/views/settings/SettingsReactions.tsx @@ -1,11 +1,10 @@ import React from "react"; -import { ScrollView, Text, View } from "react-native"; +import { ScrollView, View } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { useGradesStore } from "@/stores/grades"; import ReelGallery from "@/components/Settings/ReelGallery"; import MissingItem from "@/components/Global/MissingItem"; -import AnimatedEmoji from "@/components/Grades/AnimatedEmoji"; const SettingsReactions: Screen<"SettingsReactions"> = () => { const theme = useTheme(); From 5dedb4161dc1cbd8b30b6703ef98bf4a9ebea528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 18 Jan 2025 01:13:54 +0100 Subject: [PATCH 0267/1144] fix: eslint errors --- src/views/settings/SettingsDevLogs.tsx | 5 ----- src/views/settings/SettingsProfile.tsx | 1 - 2 files changed, 6 deletions(-) diff --git a/src/views/settings/SettingsDevLogs.tsx b/src/views/settings/SettingsDevLogs.tsx index 70257a648..cc67637be 100644 --- a/src/views/settings/SettingsDevLogs.tsx +++ b/src/views/settings/SettingsDevLogs.tsx @@ -9,7 +9,6 @@ import { } from "@/components/Global/NativeComponents"; import React, { useEffect, useState } from "react"; import { - get_brute_logs, get_logs, Log, delete_logs, @@ -30,14 +29,10 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import { PressableScale } from "react-native-pressable-scale"; import { FadeInDown, - FadeInUp, - FadeOutDown, FadeOutUp, } from "react-native-reanimated"; import { animPapillon } from "@/utils/ui/animations"; -import News from "../account/News/News"; import { useTheme } from "@react-navigation/native"; -import Reanimated from "react-native-reanimated"; const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { const theme = useTheme(); diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index 56f717b6c..c7630b773 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -10,7 +10,6 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import * as Clipboard from "expo-clipboard"; import { TouchableOpacity } from "react-native-gesture-handler"; -import { th } from "date-fns/locale"; const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { const theme = useTheme(); From 564d3bd8c45089145904d44e07a531fc42842371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 18 Jan 2025 01:14:58 +0100 Subject: [PATCH 0268/1144] fix: eslint errors --- src/components/Grades/AnimatedEmoji.tsx | 2 +- src/components/Grades/GradeModal.tsx | 4 +- src/router/navigator/atoms/MenuItem.tsx | 8 ++-- src/router/navigator/atoms/TabItem.tsx | 6 +-- src/router/navigator/menu.tsx | 10 ++--- src/router/navigator/navigator.tsx | 3 +- src/router/navigator/tabs.tsx | 4 +- src/views/account/Chat/Modals/Chat.tsx | 2 +- src/views/account/Grades/Document.tsx | 6 +-- .../account/Grades/Graph/GradesAverage.tsx | 40 +++++++++---------- .../login/pronote/PronoteInstanceSelector.tsx | 1 - .../PriceDetectionOnboarding.tsx | 4 +- .../ExternalAccount/ServiceSelector.tsx | 3 +- src/views/settings/SettingsDevLogs.tsx | 5 --- src/views/settings/SettingsProfile.tsx | 1 - src/views/settings/SettingsReactions.tsx | 3 +- 16 files changed, 39 insertions(+), 63 deletions(-) diff --git a/src/components/Grades/AnimatedEmoji.tsx b/src/components/Grades/AnimatedEmoji.tsx index 7e12350d4..5e3d9d1a4 100644 --- a/src/components/Grades/AnimatedEmoji.tsx +++ b/src/components/Grades/AnimatedEmoji.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { View, Text } from "react-native"; +import { View } from "react-native"; import Animated, { useAnimatedStyle, useSharedValue, diff --git a/src/components/Grades/GradeModal.tsx b/src/components/Grades/GradeModal.tsx index a022b3822..6b09b29d3 100644 --- a/src/components/Grades/GradeModal.tsx +++ b/src/components/Grades/GradeModal.tsx @@ -5,13 +5,11 @@ import { Image, TouchableOpacity, Text, - Platform, Alert } from "react-native"; -import { Download, Trash, Maximize2, Share, Delete } from "lucide-react-native"; +import { Download, Trash, Share } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { BlurView } from "expo-blur"; -import { ScrollView } from "react-native-gesture-handler"; import * as Sharing from "expo-sharing"; import * as FileSystem from "expo-file-system"; import * as MediaLibrary from "expo-media-library"; diff --git a/src/router/navigator/atoms/MenuItem.tsx b/src/router/navigator/atoms/MenuItem.tsx index 2a9b548ce..ff64a6de5 100644 --- a/src/router/navigator/atoms/MenuItem.tsx +++ b/src/router/navigator/atoms/MenuItem.tsx @@ -1,13 +1,11 @@ import * as React from "react"; -import { useCurrentAccount } from "@/stores/account"; -import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, View, Text, Platform } from "react-native"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { useTheme } from "@react-navigation/native"; +import { StyleSheet, Platform } from "react-native"; import LottieView from "lottie-react-native"; import colorsList from "@/utils/data/colors.json"; import { Pressable } from "react-native-gesture-handler"; import * as Haptics from "expo-haptics"; -import Reanimated, { FadeIn, FadeOut, LinearTransition, ZoomIn } from "react-native-reanimated"; +import Reanimated, { FadeIn, FadeOut, LinearTransition } from "react-native-reanimated"; import { anim2Papillon } from "@/utils/ui/animations"; const MenuItem: React.FC<{ diff --git a/src/router/navigator/atoms/TabItem.tsx b/src/router/navigator/atoms/TabItem.tsx index 90da917e3..cc8fe2536 100644 --- a/src/router/navigator/atoms/TabItem.tsx +++ b/src/router/navigator/atoms/TabItem.tsx @@ -1,8 +1,6 @@ import * as React from "react"; -import { useCurrentAccount } from "@/stores/account"; -import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, View, Text, Platform } from "react-native"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { useTheme } from "@react-navigation/native"; +import { StyleSheet, Platform } from "react-native"; import LottieView from "lottie-react-native"; import colorsList from "@/utils/data/colors.json"; import { Pressable } from "react-native-gesture-handler"; diff --git a/src/router/navigator/menu.tsx b/src/router/navigator/menu.tsx index d12793b8b..12082b86c 100644 --- a/src/router/navigator/menu.tsx +++ b/src/router/navigator/menu.tsx @@ -1,17 +1,13 @@ -import React, { useEffect, useMemo, useState } from "react"; +import React, { useMemo, useState } from "react"; import { useCurrentAccount } from "@/stores/account"; import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, View, ScrollView, Platform, Image, Text, StatusBar } from "react-native"; +import { StyleSheet, Platform, Image, Text, StatusBar } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import TabItem from "./atoms/TabItem"; -import Reanimated, { LinearTransition, useSharedValue } from "react-native-reanimated"; +import Reanimated from "react-native-reanimated"; import MenuItem from "./atoms/MenuItem"; -import AccountSwitcher from "@/components/Home/AccountSwitcher"; import ContextMenu from "@/components/Home/AccountSwitcherContextMenu"; -import { NativeText } from "@/components/Global/NativeComponents"; import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; -import { he } from "date-fns/locale"; const PapillonNavigatorMenu: React.FC, "NavigationContent">> = ({ state, descriptors, navigation }) => { const theme = useTheme(); diff --git a/src/router/navigator/navigator.tsx b/src/router/navigator/navigator.tsx index 027e57dec..888137f44 100644 --- a/src/router/navigator/navigator.tsx +++ b/src/router/navigator/navigator.tsx @@ -1,8 +1,7 @@ import { BottomTabView } from "@react-navigation/bottom-tabs"; import { createNavigatorFactory, TabRouter, useNavigationBuilder } from "@react-navigation/native"; import PapillonNavigatorTabs from "./tabs"; -import { memo, useEffect, useMemo, useState } from "react"; -import { Dimensions, View } from "react-native"; +import { View } from "react-native"; import PapillonNavigatorMenu from "./menu"; import useScreenDimensions from "@/hooks/useScreenDimensions"; diff --git a/src/router/navigator/tabs.tsx b/src/router/navigator/tabs.tsx index a82d23b88..257f2c0a3 100644 --- a/src/router/navigator/tabs.tsx +++ b/src/router/navigator/tabs.tsx @@ -1,11 +1,11 @@ import React, { useEffect, useMemo, useState } from "react"; import { useCurrentAccount } from "@/stores/account"; import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, View, Platform } from "react-native"; +import { StyleSheet, Platform } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import TabItem from "./atoms/TabItem"; -import Reanimated, { LinearTransition } from "react-native-reanimated"; +import Reanimated from "react-native-reanimated"; const PapillonNavigatorTabs: React.FC, "NavigationContent">> = ({ state, descriptors, navigation }) => { const theme = useTheme(); diff --git a/src/views/account/Chat/Modals/Chat.tsx b/src/views/account/Chat/Modals/Chat.tsx index 90f2d5538..6f8bb8d36 100644 --- a/src/views/account/Chat/Modals/Chat.tsx +++ b/src/views/account/Chat/Modals/Chat.tsx @@ -6,7 +6,7 @@ import type {Screen} from "@/router/helpers/types"; import { NativeText,} from "@/components/Global/NativeComponents"; import {useCurrentAccount} from "@/stores/account"; import type {ChatMessage, ChatRecipient} from "@/services/shared/Chat"; -import {ChevronLeft, File, Link, Send} from "lucide-react-native"; +import {ChevronLeft, Send} from "lucide-react-native"; import parse_initials from "@/utils/format/format_pronote_initials"; import InitialIndicator from "@/components/News/InitialIndicator"; import {PapillonModernHeader} from "@/components/Global/PapillonModernHeader"; diff --git a/src/views/account/Grades/Document.tsx b/src/views/account/Grades/Document.tsx index 2e6481f20..9d148bda8 100644 --- a/src/views/account/Grades/Document.tsx +++ b/src/views/account/Grades/Document.tsx @@ -7,18 +7,14 @@ import { import { getSubjectData } from "@/services/shared/Subject"; import { useTheme } from "@react-navigation/native"; import React, { useCallback, useEffect, useLayoutEffect, useState } from "react"; -import { Image, ScrollView, Text, View, Platform, TouchableOpacity, Modal } from "react-native"; +import { Image, ScrollView, Text, View, Platform, TouchableOpacity } from "react-native"; import * as StoreReview from "expo-store-review"; import { Asterisk, Calculator, - Download, - Expand, Maximize2, Scale, School, - SmilePlus, - Trash, UserMinus, UserPlus, Users, diff --git a/src/views/account/Grades/Graph/GradesAverage.tsx b/src/views/account/Grades/Graph/GradesAverage.tsx index d69d95739..f6e96a7d0 100644 --- a/src/views/account/Grades/Graph/GradesAverage.tsx +++ b/src/views/account/Grades/Graph/GradesAverage.tsx @@ -329,26 +329,26 @@ const GradesAverageGraph: React.FC = ({ - Moyenne classe - - {classAvg !== null ? ( - <> - - - /20 - - - ) : ( - Inconnue - )} - - + Moyenne classe + + {classAvg !== null ? ( + <> + + + /20 + + + ) : ( + Inconnue + )} + + {showDetails && maxAvg > 0 && minAvg > 0 ? ( diff --git a/src/views/login/pronote/PronoteInstanceSelector.tsx b/src/views/login/pronote/PronoteInstanceSelector.tsx index c2bfd27df..62158b4d4 100644 --- a/src/views/login/pronote/PronoteInstanceSelector.tsx +++ b/src/views/login/pronote/PronoteInstanceSelector.tsx @@ -8,7 +8,6 @@ import { ActivityIndicator, Keyboard, KeyboardEvent, - Text, } from "react-native"; import pronote from "pawnote"; import Reanimated, { diff --git a/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx b/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx index 36bc869d0..a22b06d77 100644 --- a/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx +++ b/src/views/settings/ExternalAccount/PriceDetectionOnboarding.tsx @@ -1,11 +1,11 @@ -import React, { useState } from "react"; +import React from "react"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { BellRing } from "lucide-react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { Image, View, StyleSheet, Text } from "react-native"; import { NativeText, } from "@/components/Global/NativeComponents"; -import { useAccounts, useCurrentAccount } from "@/stores/account"; +import { useCurrentAccount } from "@/stores/account"; import { TouchableOpacity } from "react-native-gesture-handler"; import BetaIndicator from "@/components/News/Beta"; diff --git a/src/views/settings/ExternalAccount/ServiceSelector.tsx b/src/views/settings/ExternalAccount/ServiceSelector.tsx index 6f751bd9f..6c148cfba 100644 --- a/src/views/settings/ExternalAccount/ServiceSelector.tsx +++ b/src/views/settings/ExternalAccount/ServiceSelector.tsx @@ -10,8 +10,7 @@ import { AccountService } from "@/stores/account/types"; import { useCurrentAccount } from "@/stores/account"; import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; -import { LinearGradient } from "expo-linear-gradient"; - + const ExternalAccountSelector: Screen<"ExternalAccountSelector"> = ({ navigation, route }) => { const theme = useTheme(); const { colors } = theme; diff --git a/src/views/settings/SettingsDevLogs.tsx b/src/views/settings/SettingsDevLogs.tsx index 70257a648..cc67637be 100644 --- a/src/views/settings/SettingsDevLogs.tsx +++ b/src/views/settings/SettingsDevLogs.tsx @@ -9,7 +9,6 @@ import { } from "@/components/Global/NativeComponents"; import React, { useEffect, useState } from "react"; import { - get_brute_logs, get_logs, Log, delete_logs, @@ -30,14 +29,10 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import { PressableScale } from "react-native-pressable-scale"; import { FadeInDown, - FadeInUp, - FadeOutDown, FadeOutUp, } from "react-native-reanimated"; import { animPapillon } from "@/utils/ui/animations"; -import News from "../account/News/News"; import { useTheme } from "@react-navigation/native"; -import Reanimated from "react-native-reanimated"; const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { const theme = useTheme(); diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index 56f717b6c..c7630b773 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -10,7 +10,6 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import * as Clipboard from "expo-clipboard"; import { TouchableOpacity } from "react-native-gesture-handler"; -import { th } from "date-fns/locale"; const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { const theme = useTheme(); diff --git a/src/views/settings/SettingsReactions.tsx b/src/views/settings/SettingsReactions.tsx index 49400a566..6b691d7f8 100644 --- a/src/views/settings/SettingsReactions.tsx +++ b/src/views/settings/SettingsReactions.tsx @@ -1,11 +1,10 @@ import React from "react"; -import { ScrollView, Text, View } from "react-native"; +import { ScrollView, View } from "react-native"; import type { Screen } from "@/router/helpers/types"; import { useTheme } from "@react-navigation/native"; import { useGradesStore } from "@/stores/grades"; import ReelGallery from "@/components/Settings/ReelGallery"; import MissingItem from "@/components/Global/MissingItem"; -import AnimatedEmoji from "@/components/Grades/AnimatedEmoji"; const SettingsReactions: Screen<"SettingsReactions"> = () => { const theme = useTheme(); From 89f1bffedf967c4ec805fffc73144de2652aa257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 18 Jan 2025 14:08:14 +0100 Subject: [PATCH 0269/1144] fix: add plugin to fix build error --- app.json | 1 + plugins/notifee-mod.js | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 plugins/notifee-mod.js diff --git a/app.json b/app.json index b52ff0132..0779b5b0d 100644 --- a/app.json +++ b/app.json @@ -76,6 +76,7 @@ ] }, "plugins": [ + "./plugins/notifee-mod.js", [ "expo-font", { diff --git a/plugins/notifee-mod.js b/plugins/notifee-mod.js new file mode 100644 index 000000000..0c957eafb --- /dev/null +++ b/plugins/notifee-mod.js @@ -0,0 +1,17 @@ +const { withProjectBuildGradle } = require("expo/config-plugins"); + +module.exports = function withNotifeeRepo (config) { + return withProjectBuildGradle(config, async config => { + const contents = config.modResults.contents; + + if (!contents.includes("@notifee/react-native")) { + const replacement = `maven { url 'https://www.jitpack.io' } + maven { + url "$rootDir/../node_modules/@notifee/react-native/android/libs" + }`; + config.modResults.contents = contents.replace("maven { url 'https://www.jitpack.io' }", replacement); + } + + return config; + }); +}; \ No newline at end of file From e0e5692d834ac8796fd758f56e48f1e0c85634a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 18 Jan 2025 14:10:32 +0100 Subject: [PATCH 0270/1144] run prebuild --- android/app/src/main/AndroidManifest.xml | 2 +- android/build.gradle | 3 ++ .../AppIcon.appiconset/Contents.json | 42 +++++++++++++++---- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 4f859e36e..bb8cf9f0e 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -24,7 +24,7 @@ - + diff --git a/android/build.gradle b/android/build.gradle index 9ae2aca2b..031f875ea 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -37,6 +37,9 @@ allprojects { google() mavenCentral() maven { url 'https://www.jitpack.io' } + maven { + url "$rootDir/../node_modules/@notifee/react-native/android/libs" + } } } // @generated begin expo-camera-import - expo prebuild (DO NOT MODIFY) sync-f244f4f3d8bf7229102e8f992b525b8602c74770 diff --git a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json index 90d8d4c2a..e91cbd33d 100644 --- a/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/ios/Papillon/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,14 +1,38 @@ { - "images": [ + "images" : [ { - "filename": "App-Icon-1024x1024@1x.png", - "idiom": "universal", - "platform": "ios", - "size": "1024x1024" + "filename" : "Icon-Light-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Dark-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "Icon-Tinted-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" } ], - "info": { - "version": 1, - "author": "expo" + "info" : { + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} From df00bb02d35ef89fbbd26d59df88c077bae3bbc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 18 Jan 2025 14:52:34 +0100 Subject: [PATCH 0271/1144] adapt `` for tablet --- src/components/Modals/PapillonBottomSheet.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/Modals/PapillonBottomSheet.tsx b/src/components/Modals/PapillonBottomSheet.tsx index 6c1e4681a..91f725e96 100644 --- a/src/components/Modals/PapillonBottomSheet.tsx +++ b/src/components/Modals/PapillonBottomSheet.tsx @@ -1,3 +1,4 @@ +import useScreenDimensions from "@/hooks/useScreenDimensions"; import { useTheme } from "@react-navigation/native"; import React, {useCallback} from "react"; import { KeyboardAvoidingView, Modal, Pressable } from "react-native"; @@ -26,6 +27,7 @@ const BottomSheet = ({ children, opened, setOpened, ...props }: BottomSheetProps const colors = useTheme().colors; const translateY = useSharedValue(0); const insets = useSafeAreaInsets(); + const { isTablet } = useScreenDimensions(); const closeModal = useCallback(() => { setOpened(false); @@ -69,6 +71,7 @@ const BottomSheet = ({ children, opened, setOpened, ...props }: BottomSheetProps style={{ flex: 1, justifyContent: "flex-end", + alignItems: "center", backgroundColor: "rgba(0, 0, 0, 0.4)", }} > @@ -90,6 +93,8 @@ const BottomSheet = ({ children, opened, setOpened, ...props }: BottomSheetProps borderTopRightRadius: 16, borderCurve: "continuous", paddingBottom: insets.bottom + 10 + 16, + width: "100%", + maxWidth: isTablet ? "50%" : "100%", }, animatedStyle, props.contentContainerStyle, From d65eb7c36abc14848d7103217d7ef3e3f5decfc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sat, 18 Jan 2025 14:58:23 +0100 Subject: [PATCH 0272/1144] fix: affichage des services de cantine incorrect --- src/views/settings/ExternalAccount/ServiceSelector.tsx | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/views/settings/ExternalAccount/ServiceSelector.tsx b/src/views/settings/ExternalAccount/ServiceSelector.tsx index df9cb9a1a..2c89b0cf1 100644 --- a/src/views/settings/ExternalAccount/ServiceSelector.tsx +++ b/src/views/settings/ExternalAccount/ServiceSelector.tsx @@ -7,7 +7,7 @@ import PapillonShineBubble from "@/components/FirstInstallation/PapillonShineBub import { AccountService } from "@/stores/account/types"; import DuoListPressable from "@/components/FirstInstallation/DuoListPressable"; import ButtonCta from "@/components/FirstInstallation/ButtonCta"; - + const ExternalAccountSelector: Screen<"ExternalAccountSelector"> = ({ navigation }) => { type Service = AccountService | "Other"; @@ -16,14 +16,10 @@ const ExternalAccountSelector: Screen<"ExternalAccountSelector"> = ({ navigation return ( Date: Sat, 18 Jan 2025 15:35:54 +0100 Subject: [PATCH 0273/1144] =?UTF-8?q?fix:=20d=C3=A9sactivation=20de=20l'on?= =?UTF-8?q?glet=20`Onglets=20&=20Navigation`=20pour=20les=20tablettes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/settings/Settings.tsx | 50 ++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index e166cc1ba..1662cafda 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -49,6 +49,7 @@ import PapillonSpinner from "@/components/Global/PapillonSpinner"; import { animPapillon } from "@/utils/ui/animations"; import * as WebBrowser from "expo-web-browser"; import { WebBrowserPresentationStyle } from "expo-web-browser"; +import useScreenDimensions from "@/hooks/useScreenDimensions"; const Settings: Screen<"Settings"> = ({ route, navigation }) => { const theme = useTheme(); @@ -59,6 +60,7 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { const [devModeEnabled, setDevModeEnabled] = useState(false); const defined = useFlagsStore(state => state.defined); const [click, setClick] = useState(false); + const { isTablet } = useScreenDimensions(); const removeAccount = useAccounts((store) => store.remove); @@ -167,28 +169,6 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { icon: , label: "Avancé", tabs: [ - { - icon: click ? ( - ) : , - color: "#7E1174", - label: "Onglets & Navigation", - onPress: async () => { - setClick(true); - setTimeout(() => { - if (Platform.OS === "ios") { - navigation.goBack(); - } - navigation.navigate("SettingsTabs"); - setClick(false); - }, 10); - }, - }, { icon: , color: "#bf547d", @@ -293,6 +273,32 @@ const Settings: Screen<"Settings"> = ({ route, navigation }) => { }); } + if (!isTablet) { + tabs[2].tabs.unshift({ + icon: click ? ( + ) : , + color: "#7E1174", + label: "Onglets & Navigation", + onPress: async () => { + setClick(true); + setTimeout(() => { + if (Platform.OS === "ios") { + navigation.goBack(); + } + navigation.navigate("SettingsTabs"); + setClick(false); + }, 10); + }, + description: "", + }); + } + const translationY = useSharedValue(0); const [scrolled, setScrolled] = useState(false); From 880c3ec34dde88c188b2ca552ad5ffc6a08ce5ac Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 18 Jan 2025 21:06:53 +0100 Subject: [PATCH 0274/1144] fix(merge): error during merge Signed-off-by: Gabriel29306 --- src/views/welcome/DevMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/welcome/DevMenu.tsx b/src/views/welcome/DevMenu.tsx index 030c81783..0b36a131f 100644 --- a/src/views/welcome/DevMenu.tsx +++ b/src/views/welcome/DevMenu.tsx @@ -119,7 +119,7 @@ const DevMenu: Screen<"DevMenu"> = ({ navigation }) => { GradeReaction - */} + navigation.navigate("ColorSelector")} From 6e9f305b5a63f6293c82bff69b1a5e209abce61f Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 18 Jan 2025 21:09:50 +0100 Subject: [PATCH 0275/1144] fix(lint): incompatble type Signed-off-by: Gabriel29306 --- src/components/Global/AnimatedNumber.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Global/AnimatedNumber.tsx b/src/components/Global/AnimatedNumber.tsx index 0f443bccb..72e608acc 100644 --- a/src/components/Global/AnimatedNumber.tsx +++ b/src/components/Global/AnimatedNumber.tsx @@ -50,7 +50,7 @@ const AnimatedNumber: React.FC = ({ }, contentContainerStyle]} layout={animPapillon(LinearTransition)} > - {value.toString().split("").map((n:number, i:number) => ( + {value.toString().split("").map((n, i) => ( Date: Sat, 18 Jan 2025 21:11:13 +0100 Subject: [PATCH 0276/1144] lint: unused imports Signed-off-by: Gabriel29306 --- src/views/settings/SettingsDevLogs.tsx | 5 ----- src/views/settings/SettingsProfile.tsx | 1 - 2 files changed, 6 deletions(-) diff --git a/src/views/settings/SettingsDevLogs.tsx b/src/views/settings/SettingsDevLogs.tsx index 70257a648..cc67637be 100644 --- a/src/views/settings/SettingsDevLogs.tsx +++ b/src/views/settings/SettingsDevLogs.tsx @@ -9,7 +9,6 @@ import { } from "@/components/Global/NativeComponents"; import React, { useEffect, useState } from "react"; import { - get_brute_logs, get_logs, Log, delete_logs, @@ -30,14 +29,10 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import { PressableScale } from "react-native-pressable-scale"; import { FadeInDown, - FadeInUp, - FadeOutDown, FadeOutUp, } from "react-native-reanimated"; import { animPapillon } from "@/utils/ui/animations"; -import News from "../account/News/News"; import { useTheme } from "@react-navigation/native"; -import Reanimated from "react-native-reanimated"; const SettingsDevLogs: Screen<"SettingsDevLogs"> = ({ navigation }) => { const theme = useTheme(); diff --git a/src/views/settings/SettingsProfile.tsx b/src/views/settings/SettingsProfile.tsx index 414902885..c7cf98ebe 100644 --- a/src/views/settings/SettingsProfile.tsx +++ b/src/views/settings/SettingsProfile.tsx @@ -10,7 +10,6 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import * as Clipboard from "expo-clipboard"; import { TouchableOpacity } from "react-native-gesture-handler"; -import { th } from "date-fns/locale"; const SettingsProfile: Screen<"SettingsProfile"> = ({ navigation }) => { const theme = useTheme(); From 8eaeee741e31115834cc1fe382c81e07d5a0b9bf Mon Sep 17 00:00:00 2001 From: Gabriel29306 Date: Sat, 18 Jan 2025 23:34:29 +0100 Subject: [PATCH 0277/1144] fix(average): enhance overall avarage calcul Co-authored-by: Kgeek33 <164187100+Kgeek33@users.noreply.github.com> Signed-off-by: Gabriel29306 --- src/utils/grades/getAverages.ts | 113 +++++++++++++++----------------- 1 file changed, 54 insertions(+), 59 deletions(-) diff --git a/src/utils/grades/getAverages.ts b/src/utils/grades/getAverages.ts index 75ebc6693..353f446e1 100644 --- a/src/utils/grades/getAverages.ts +++ b/src/utils/grades/getAverages.ts @@ -24,13 +24,13 @@ const getPronoteAverage = ( useMath: boolean = false ): number => { try { - // Si aucune note n'est fournie ou que la liste est vide, on retourne -1 + // Si aucune note n'est fournie ou que la liste est vide, on retourne -1 if (!grades || grades.length === 0) return -1; // Grouper les notes par matière const groupedBySubject = grades.reduce( (acc: Record, grade) => { - (acc[grade.subjectId || grade.subjectName] ||= []).push(grade); // Ajouter la note à la liste des notes pour la matière correspondante + (acc[grade.subjectId ?? grade.subjectName] ||= []).push(grade); // Ajouter la note à la liste des notes pour la matière correspondante return acc; }, {} @@ -43,10 +43,9 @@ const getPronoteAverage = ( (acc, subjectGrades) => { const nAvg = getSubjectAverage(subjectGrades, target, useMath); - if(nAvg !== -1) { + if (nAvg !== -1) { countedSubjects++; - } - else { + } else { return acc; } @@ -57,8 +56,7 @@ const getPronoteAverage = ( // Retourner la moyenne globale en divisant par le nombre de matières return totalAverage / countedSubjects; - } - catch(e) { + } catch (e) { return -1; } }; @@ -73,83 +71,75 @@ export const getSubjectAverage = ( try { let calcGradesSum = 0; // Somme cumulée des notes pondérées let calcOutOfSum = 0; // Somme cumulée des coefficients pondérés - let countedGrades = 0; - // Parcourir chaque note de la matière for (const grade of subject) { - const targetGrade = grade[target]; // Sélectionner la note selon la cible choisie + const targetGrade = grade[target]; - // Vérifier si la note est invalide ou si le coefficient est nul, et passer à la suivante si c'est le cas if ( !targetGrade || - targetGrade.disabled || - targetGrade.value === null || - targetGrade.value < 0 || - grade.coefficient === 0 || - typeof targetGrade.value !== "number" - ) + targetGrade.disabled || + targetGrade.value === null || + targetGrade.value < 0 || + grade.coefficient === 0 || + typeof targetGrade.value !== "number" + ) { continue; + } - const coefficient = grade.coefficient; // Coefficient de la note - const outOfValue = grade.outOf.value!; // Valeur maximale possible pour la note + const coefficient = grade.coefficient; + const outOfValue = grade.outOf.value!; - if(grade.isOptional && !loop) { - // get average without this grade (WARNING: this is a recursive call) - const avgWithout = getSubjectAverage(subject.filter((g) => JSON.stringify(g) !== JSON.stringify(grade)), target, useMath, true); + if (grade.isOptional && !loop) { + const avgWithout = getSubjectAverage( + subject.filter((g) => JSON.stringify(g) !== JSON.stringify(grade)), + target, + useMath, + true + ); - // get average with this grade const avgWith = getSubjectAverage(subject, target, useMath, true); - if(avgWithout > avgWith) { + if (avgWithout > avgWith) { continue; } } - // Si la note est un bonus if (grade.isBonus) { - const averageMoy = outOfValue / 2; // Calculer la moitié de la valeur maximale comme seuil de bonus - const newGradeValue = targetGrade.value - averageMoy; // Ajuster la note en soustrayant la moitié de la valeur maximale + const averageMoy = outOfValue / 2; + const newGradeValue = targetGrade.value - averageMoy; - if (newGradeValue < 0) continue; // Si la note ajustée est négative, passer à la suivante + if (newGradeValue < 0) continue; - calcGradesSum += newGradeValue; // Ajouter la note ajustée à la somme - calcOutOfSum += 1; // Incrémenter la somme de pondération (compte comme 1 ici) - } else if(useMath) { + calcGradesSum += newGradeValue; + calcOutOfSum += 1; + } else if (useMath) { calcGradesSum += targetGrade.value * coefficient; } else if ( - targetGrade.value > 20 || (coefficient < 1 && ((outOfValue - 20) >= -5) || outOfValue > 20) + targetGrade.value > 20 || + (coefficient < 1 && outOfValue - 20 >= -5) || + outOfValue > 20 ) { - // Si la note est supérieure à 20 ou si le coefficient est inférieur à 1, ajuster pour une base sur 20 - const gradeOn20 = (targetGrade.value / outOfValue) * 20; // Ajuster la note pour une base sur 20 - calcGradesSum += gradeOn20 * coefficient; // Ajouter la note ajustée et pondérée - calcOutOfSum += 20 * coefficient; // Ajouter le coefficient ajusté à la somme + const gradeOn20 = (targetGrade.value / outOfValue) * 20; + calcGradesSum += gradeOn20 * coefficient; + calcOutOfSum += 20 * coefficient; } else { - // Cas général pour une note normale - calcGradesSum += targetGrade.value * coefficient; // Ajouter la note pondérée à la somme - calcOutOfSum += outOfValue * coefficient; // Ajouter le coefficient pondéré à la somme + calcGradesSum += targetGrade.value * coefficient; + calcOutOfSum += outOfValue * coefficient; } - if(!useMath) { - countedGrades++; - } - else { - countedGrades = countedGrades + 1 * coefficient; - } + countedGrades += useMath ? 1 * coefficient : 1; } - if(useMath) { + if (useMath) { return calcGradesSum / countedGrades; } - // Si aucune somme de pondération n'est calculée, retourner 0 pour éviter la division par zéro if (calcOutOfSum === 0) return -1; - // Calculer la moyenne de la matière en ajustant pour s'assurer qu'elle ne dépasse pas 20 const subjectAverage = Math.min((calcGradesSum / calcOutOfSum) * 20, 20); - return isNaN(subjectAverage) ? -1 : subjectAverage; // Retourner la moyenne calculée - } - catch(e) { + return isNaN(subjectAverage) ? -1 : subjectAverage; + } catch (e) { return -1; } }; @@ -164,22 +154,28 @@ const getAverageDiffGrade = ( try { const baseAverage = getSubjectAverage(list, target); // Calculer la moyenne de base avec toutes les notes const baseWithoutGradeAverage = getSubjectAverage( - list.filter((grade) => JSON.stringify(grades[0]) !== JSON.stringify(grade)), + list.filter( + (grade) => + !grades.some( + (g) => + g.student.value === grade.student.value && + g.coefficient === grade.coefficient + ) + ), target, useMath - ); // Calculer la moyenne sans certaines notes + ); // Calculer la moyenne sans toutes les notes de `grades` return { difference: baseWithoutGradeAverage - baseAverage, // Calculer la différence entre les deux moyennes with: baseAverage, // Moyenne avec toutes les notes without: baseWithoutGradeAverage, // Moyenne sans certaines notes }; - } - catch(e) { + } catch (e) { return { difference: 0, with: 0, - without: 0 + without: 0, }; } }; @@ -192,7 +188,7 @@ const getAveragesHistory = ( useMath: boolean = false ): GradeHistory[] => { try { - // Générer l'historique des moyennes jusqu'à la date de chaque note + // Générer l'historique des moyennes jusqu'à la date de chaque note const history = grades.map((grade, index) => ({ value: getPronoteAverage(grades.slice(0, index + 1), target), // Moyenne jusqu'à ce point date: new Date(grade.timestamp).toISOString(), // Date de la note au format ISO @@ -209,8 +205,7 @@ const getAveragesHistory = ( // remove NaN values return history.filter((x) => !isNaN(x.value) || x.value !== -1); - } - catch(e) { + } catch (e) { return []; } }; From efbaea367f4ab056fa3c74cc34f766dbeb4c53ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=95=82=F0=9D=95=AA=F0=9D=95=9D=F0=9D=95=9A?= =?UTF-8?q?=F0=9D=95=92=F0=9D=95=9F?= Date: Sun, 19 Jan 2025 01:20:52 +0100 Subject: [PATCH 0278/1144] =?UTF-8?q?fix:=20ajout=20de=20flex:=201=20pour?= =?UTF-8?q?=20am=C3=A9liorer=20le=20rendu=20de=20NativeList?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Global/NativeComponents.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Global/NativeComponents.tsx b/src/components/Global/NativeComponents.tsx index b8374c1f5..f313cec9e 100644 --- a/src/components/Global/NativeComponents.tsx +++ b/src/components/Global/NativeComponents.tsx @@ -75,6 +75,7 @@ export const NativeList: React.FC = ({ > Date: Sun, 19 Jan 2025 01:35:56 +0100 Subject: [PATCH 0279/1144] =?UTF-8?q?fix:=20`AccountSelector`=20pr=C3=A9se?= =?UTF-8?q?nt=20uniquement=20sur=20la=20NavBar=20de=20la=20tablette=20en?= =?UTF-8?q?=20mode=20paysage=20+=20marginTop=20n=C3=A9gatif=20pour=20?= =?UTF-8?q?=C3=A9viter=20un=20espace=20vide?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/navigator/menu.tsx | 37 +++-------------------------- src/views/account/Home/Home.tsx | 41 +++++++++++++++++++-------------- 2 files changed, 27 insertions(+), 51 deletions(-) diff --git a/src/router/navigator/menu.tsx b/src/router/navigator/menu.tsx index 12082b86c..255e254d2 100644 --- a/src/router/navigator/menu.tsx +++ b/src/router/navigator/menu.tsx @@ -1,13 +1,13 @@ import React, { useMemo, useState } from "react"; import { useCurrentAccount } from "@/stores/account"; import { useNavigationBuilder, useTheme } from "@react-navigation/native"; -import { StyleSheet, Platform, Image, Text, StatusBar } from "react-native"; +import { StyleSheet, Platform, StatusBar } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import Reanimated from "react-native-reanimated"; import MenuItem from "./atoms/MenuItem"; import ContextMenu from "@/components/Home/AccountSwitcherContextMenu"; -import { defaultProfilePicture } from "@/utils/ui/default-profile-picture"; +import AccountSwitcher from "@/components/Home/AccountSwitcher"; const PapillonNavigatorMenu: React.FC, "NavigationContent">> = ({ state, descriptors, navigation }) => { const theme = useTheme(); @@ -48,7 +48,6 @@ const PapillonNavigatorMenu: React.FC - - - - - {account?.studentName ? ( - account.studentName?.first + " " + account.studentName.last - ) : "Mon compte"} - - + {tabs.map((route, index) => ( diff --git a/src/views/account/Home/Home.tsx b/src/views/account/Home/Home.tsx index 7371485af..f4193493c 100644 --- a/src/views/account/Home/Home.tsx +++ b/src/views/account/Home/Home.tsx @@ -11,7 +11,7 @@ // | ne contiendra pas grand-chose qui puisse t'intéresser. | // | | // | Heureusement pour toi, je suis magicien ! | -// | ╰( ͡° ͜ʖ ͡° )つ──☆*:・゚ | +// | ╰( ͡° ͜ʖ ͡° )つ──☆*:・゚ | // | | // | Si tu souhaites modifier les widgets : | // | ~/src/widgets | @@ -223,21 +223,23 @@ const Home: Screen<"HomeScreen"> = ({ navigation }) => { {!modalOpen && focused && !isTablet && ( )} - - - + {!isTablet && ( + + + + )} = ({ navigation }) => { showsVerticalScrollIndicator={false} >