From 293cfaa1a3ba44740f64185401f5d2b8f6ae05f1 Mon Sep 17 00:00:00 2001 From: NonozgYtb Date: Mon, 2 Sep 2024 21:46:34 +0200 Subject: [PATCH 1/2] fix: little fixes --- src/stores/account/index.ts | 2 +- src/views/login/skolengo/SkolengoInstanceSelector.tsx | 2 +- src/views/login/skolengo/SkolengoWebview.tsx | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/stores/account/index.ts b/src/stores/account/index.ts index 9241c5ba..f2bf9dcf 100644 --- a/src/stores/account/index.ts +++ b/src/stores/account/index.ts @@ -109,7 +109,7 @@ export const useCurrentAccount = create()((set, get) => ({ 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) => { diff --git a/src/views/login/skolengo/SkolengoInstanceSelector.tsx b/src/views/login/skolengo/SkolengoInstanceSelector.tsx index c7e506e2..fdfa7a2b 100644 --- a/src/views/login/skolengo/SkolengoInstanceSelector.tsx +++ b/src/views/login/skolengo/SkolengoInstanceSelector.tsx @@ -32,7 +32,7 @@ const SkolengoInstanceSelector: Screen<"SkolengoInstanceSelector"> = ({ const [search, setSearch] = useState(""); const searchInputRef = React.createRef(); - const debouncedSearch = useDebounce(search, 250); + const debouncedSearch = useDebounce(search, 1000); const [hasSearched, setHasSearched] = useState(false); diff --git a/src/views/login/skolengo/SkolengoWebview.tsx b/src/views/login/skolengo/SkolengoWebview.tsx index bdec3f7f..723e0cd4 100644 --- a/src/views/login/skolengo/SkolengoWebview.tsx +++ b/src/views/login/skolengo/SkolengoWebview.tsx @@ -26,6 +26,7 @@ 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 { log } 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. @@ -225,9 +226,9 @@ const SkolengoWebview: Screen<"SkolengoWebview"> = ({ route, navigation }) => { discovery: discovery! }); setLoginStep("Finalisation du compte..."); - /* await skolengoAccount.instance!.getUserInfo().then((info) => { - console.log("info", info); - }).catch(console.log); */ + await skolengoAccount.instance!.getUserInfo().then((info) => { + log("info", "Skolengo"); + }).catch(console.log); createStoredAccount(skolengoAccount); switchTo(skolengoAccount); From 2910bedcdc41f1d5fd27cfdd1b3debd5d64e087d Mon Sep 17 00:00:00 2001 From: NonozgYtb Date: Wed, 4 Sep 2024 01:04:09 +0200 Subject: [PATCH 2/2] feat: changing from weekNumber to epochWeekNumber We make this change for having a uniformized UNIX timebased date/week management in the app. So it's not only for Pronote now but we can transpose this epochWN to pronote's WN (weeknumber) with some methods. See src/utils/epochWeekNumber.ts for more informations --- src/services/homework.ts | 10 +- src/services/pronote/timetable.ts | 4 +- src/services/timetable.ts | 6 +- src/stores/homework/index.ts | 8 +- src/stores/homework/types.ts | 2 +- src/stores/timetable/index.ts | 2 +- src/stores/timetable/types.ts | 3 +- src/utils/epochWeekNumber.ts | 103 +++++++++++++++ .../Home/Elements/HomeworksElement.tsx | 47 ++----- .../Home/Elements/TimetableElement.tsx | 28 ++--- src/views/account/Homeworks/Atoms/Item.tsx | 4 +- src/views/account/Homeworks/Homeworks.tsx | 117 ++++++++---------- .../account/Homeworks/HomeworksHeader.tsx | 51 ++++---- src/views/account/Lessons/Atoms/Page.tsx | 12 +- src/views/account/Lessons/Lessons.tsx | 41 +++--- 15 files changed, 242 insertions(+), 196 deletions(-) create mode 100644 src/utils/epochWeekNumber.ts diff --git a/src/services/homework.ts b/src/services/homework.ts index b083bd8a..7dbeb887 100644 --- a/src/services/homework.ts +++ b/src/services/homework.ts @@ -1,18 +1,22 @@ 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 } from "@/utils/logger/logger"; +import { translateToWeekNumber } from "pawnote"; +import { pronoteFirstDate } from "./pronote/timetable"; +import { dateToEpochWeekNumber, epochWNToPronoteWN } from "@/utils/epochWeekNumber"; /** * Updates the state and cache for the homework of given week number. */ -export async function updateHomeworkForWeekInCache (account: T, weekNumber: number): Promise { +export async function updateHomeworkForWeekInCache (account: T, date: Date): Promise { let homeworks: Homework[] = []; try { switch (account.service) { case AccountService.Pronote: { const { getHomeworkForWeek } = await import("./pronote/homework"); + const weekNumber = translateToWeekNumber(date, account.instance?.instance.firstDate || pronoteFirstDate); homeworks = await getHomeworkForWeek(account, weekNumber); break; } @@ -20,7 +24,7 @@ export async function updateHomeworkForWeekInCache (account: console.info(`[updateHomeworkForWeekInCache]: updating to empty since ${account.service} not implemented.`); } - useHomeworkStore.getState().updateHomeworks(weekNumber, homeworks); + useHomeworkStore.getState().updateHomeworks(dateToEpochWeekNumber(date), homeworks); } catch (err) { error("not updated, see:" + err, "updateHomeworkForWeekInCache"); diff --git a/src/services/pronote/timetable.ts b/src/services/pronote/timetable.ts index f9c757fe..b93682ec 100644 --- a/src/services/pronote/timetable.ts +++ b/src/services/pronote/timetable.ts @@ -4,6 +4,8 @@ import { ErrorServiceUnauthenticated } from "../shared/errors"; import pronote from "pawnote"; import { info } from "@/utils/logger/logger"; +export const pronoteFirstDate = new Date("2024-09-01"); + const decodeTimetableClass = (c: pronote.TimetableClassLesson | pronote.TimetableClassDetention | pronote.TimetableClassActivity): TimetableClass => { const base = { startTimestamp: c.startDate.getTime(), @@ -58,4 +60,4 @@ export const getTimetableForWeek = async (account: PronoteAccount, weekNumber: n }); return timetable.classes.map(decodeTimetableClass); -}; +}; \ No newline at end of file diff --git a/src/services/timetable.ts b/src/services/timetable.ts index 1c94ccfa..2e4bff41 100644 --- a/src/services/timetable.ts +++ b/src/services/timetable.ts @@ -1,15 +1,17 @@ import { type Account, AccountService } from "@/stores/account/types"; import { useTimetableStore } from "@/stores/timetable"; +import { epochWNToPronoteWN } from "@/utils/epochWeekNumber"; /** * Updates the state and cache for the timetable of given week number. */ -export async function updateTimetableForWeekInCache (account: T, weekNumber: number): Promise { +export async function updateTimetableForWeekInCache (account: T, epochWeekNumber: number): Promise { switch (account.service) { case AccountService.Pronote: { const { getTimetableForWeek } = await import("./pronote/timetable"); + const weekNumber = epochWNToPronoteWN(epochWeekNumber, account); const timetable = await getTimetableForWeek(account, weekNumber); - useTimetableStore.getState().updateClasses(weekNumber, timetable); + useTimetableStore.getState().updateClasses(epochWeekNumber, timetable); break; } default: { diff --git a/src/stores/homework/index.ts b/src/stores/homework/index.ts index aa6b60e9..d60f27fe 100644 --- a/src/stores/homework/index.ts +++ b/src/stores/homework/index.ts @@ -9,19 +9,19 @@ export const useHomeworkStore = create()( persist( (set) => ({ homeworks: {}, - updateHomeworks: (weekNumber, homeworks) => { - log(`updating homeworks for week ${weekNumber}`, "homework:updateHomeworks"); + updateHomeworks: (epochWeekNumber, homeworks) => { + log(`updating homeworks for week ${epochWeekNumber}`, "homework:updateHomeworks"); set((state) => { return { homeworks: { ...state.homeworks, - [weekNumber]: homeworks + [epochWeekNumber]: homeworks } }; }); - log(`updated homeworks for week ${weekNumber}`, "homework:updateHomeworks"); + log(`updated homeworks for week ${epochWeekNumber}`, "homework:updateHomeworks"); } }), { diff --git a/src/stores/homework/types.ts b/src/stores/homework/types.ts index fc119206..461cec1e 100644 --- a/src/stores/homework/types.ts +++ b/src/stores/homework/types.ts @@ -2,5 +2,5 @@ import type { Homework } from "@/services/shared/Homework"; export interface HomeworkStore { homeworks: Record - updateHomeworks: (weekNumber: number, homeworks: Homework[]) => void + updateHomeworks: (epochWeekNumber: number, homeworks: Homework[]) => void } diff --git a/src/stores/timetable/index.ts b/src/stores/timetable/index.ts index 49a87cb5..6093d512 100644 --- a/src/stores/timetable/index.ts +++ b/src/stores/timetable/index.ts @@ -29,4 +29,4 @@ export const useTimetableStore = create()( storage: createJSONStorage(() => AsyncStorage) } ) -); +); \ No newline at end of file diff --git a/src/stores/timetable/types.ts b/src/stores/timetable/types.ts index 23d5638c..cccb1edc 100644 --- a/src/stores/timetable/types.ts +++ b/src/stores/timetable/types.ts @@ -2,5 +2,6 @@ import type { Timetable } from "@/services/shared/Timetable"; export interface TimetableStore { timetables: Record, - updateClasses: (weekNumber: number, classes: Timetable) => void + updateClasses: (epochWeekNumber: number, classes: Timetable) => void } + diff --git a/src/utils/epochWeekNumber.ts b/src/utils/epochWeekNumber.ts new file mode 100644 index 00000000..0f356d78 --- /dev/null +++ b/src/utils/epochWeekNumber.ts @@ -0,0 +1,103 @@ +/* + ----------------------------------------------------------------------------------------------- + + Papillon's custom system for week number since 01/01/1970 + + This part is for handleing the date conversion from JS date, Pronote's week number and our + "epochWeekNumber" (which try to represent the total number of weeks since the UNIX epoch, aka + 1st January 1970). It can be a little be messy but it's the best way I found to handle the date + conversion between the different systems. I tried to make it as simple as possible and modular, + and I added a ton of comments to help u. If you still have a question, feel free to ask me. - NonozgYtb ;) + + ----------------------------------------------------------------------------------------------- +*/ + +import { pronoteFirstDate } from "@/services/pronote/timetable"; +import type { PronoteAccount } from "@/stores/account/types"; +import { translateToWeekNumber } from "pawnote"; + +const EPOCH_WN_CONFIG = { + setHour: 6, // We are in Europe, so we set the hour to 6 UTC to avoid any problem with the timezone (= 2h in the morning in Summer Paris timezone) + setStartDay: 1, // We set the first day of the week to Monday to ensure that the week number is the same for the whole world + 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 +}; + +/** + * 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); + _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; +}; + +export const epochWNToDate = (epochWeekNumber: number)=>dayToWeekCommonDay(weekNumberToMiddleDate(epochWeekNumber)); + +export const epochWNToPronoteWN = (epochWeekNumber: number, account: PronoteAccount) => + translateToWeekNumber(epochWNToDate(epochWeekNumber), account.instance?.instance.firstDate || 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; +}; + +/** + * 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 => { + 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 { start, end }; +}; + +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 date; +}; + +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; +}; \ No newline at end of file diff --git a/src/views/account/Home/Elements/HomeworksElement.tsx b/src/views/account/Home/Elements/HomeworksElement.tsx index 77dae779..ca9d86b5 100644 --- a/src/views/account/Home/Elements/HomeworksElement.tsx +++ b/src/views/account/Home/Elements/HomeworksElement.tsx @@ -1,9 +1,7 @@ -import { NativeItem, NativeList, NativeListHeader, NativeText } from "@/components/Global/NativeComponents"; +import { NativeList, NativeListHeader } from "@/components/Global/NativeComponents"; import { useCurrentAccount } from "@/stores/account"; import { AccountService } from "@/stores/account/types"; -import { useTheme } from "@react-navigation/native"; import React, { useCallback, useEffect, useMemo, useState } from "react"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useHomeworkStore } from "@/stores/homework"; import { toggleHomeworkState, updateHomeworkForWeekInCache } from "@/services/homework"; import HomeworkItem from "../../Homeworks/Atoms/Item"; @@ -11,50 +9,23 @@ import { Homework } from "@/services/shared/Homework"; import { debounce } from "lodash"; import { PapillonNavigation } from "@/router/refs"; import RedirectButton from "@/components/Home/RedirectButton"; +import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; const HomeworksElement = () => { - const { colors } = useTheme(); - const insets = useSafeAreaInsets(); const account = useCurrentAccount(store => store.account!); const homeworks = useHomeworkStore(store => store.homeworks); - const [currentWeek, setCurrentWeek] = useState(0); - - const currentDay = new Date(/* "2024-05-27" */); - const [firstDate, setFirstDate] = useState(new Date("2024-09-01")); - - const [hwList, setHwList] = useState([]); - - useEffect(() => { - if (account.instance) { - if (account.service === AccountService.Pronote) { - setFirstDate(new Date(account.instance.instance.firstDate)); - } - } - }, [account]); - - const getWeekNumber = (date: Date) => { - const firstDayOfYear = new Date(firstDate); - const pastDaysOfYear = (date.getTime() - firstDayOfYear.getTime()) / 86400000; - return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7); - }; - - const [currentlyUpdating, setCurrentlyUpdating] = useState(false); + const actualDay = useMemo(()=>new Date(), []); const updateHomeworks = useCallback(async () => { - await updateHomeworkForWeekInCache(account, currentWeek); - }, [account, currentWeek]); + await updateHomeworkForWeekInCache(account, actualDay); + }, [account, actualDay]); const debouncedUpdateHomeworks = useMemo(() => debounce(updateHomeworks, 500), [updateHomeworks]); useEffect(() => { debouncedUpdateHomeworks(); - }, [account.instance, currentWeek]); - - - useEffect(() => { - setCurrentWeek(getWeekNumber(currentDay)); - }, [currentDay, currentlyUpdating]); + }, [account.instance, actualDay]); const handleDonePress = useCallback( async (homework: Homework) => { @@ -64,7 +35,7 @@ const HomeworksElement = () => { [account, updateHomeworks] ); - if (!homeworks[currentWeek] || homeworks[currentWeek]?.filter(hw => new Date(hw.due).getDate() === currentDay.getDate()).length === 0) { + if (!homeworks[dateToEpochWeekNumber(actualDay)] || homeworks[dateToEpochWeekNumber(actualDay)]?.filter(hw => new Date(hw.due).getDate() === actualDay.getDate()).length === 0) { return null; } @@ -76,12 +47,12 @@ const HomeworksElement = () => { )} /> - {homeworks[currentWeek]?.filter(hw => new Date(hw.due).getDate() === currentDay.getDate()).map((hw, index) => ( + {homeworks[dateToEpochWeekNumber(actualDay)]?.filter(hw => new Date(hw.due).getDate() === actualDay.getDate()).map((hw, index) => ( { handleDonePress(hw); }} diff --git a/src/views/account/Home/Elements/TimetableElement.tsx b/src/views/account/Home/Elements/TimetableElement.tsx index 85adbfaf..1f8f5970 100644 --- a/src/views/account/Home/Elements/TimetableElement.tsx +++ b/src/views/account/Home/Elements/TimetableElement.tsx @@ -1,10 +1,9 @@ import { NativeListHeader } from "@/components/Global/NativeComponents"; import { updateTimetableForWeekInCache } from "@/services/timetable"; import { useCurrentAccount } from "@/stores/account"; -import { AccountService } from "@/stores/account/types"; import { useTimetableStore } from "@/stores/timetable"; import { animPapillon } from "@/utils/ui/animations"; -import React, { useEffect, useMemo, useRef, useState } from "react"; +import React, { useEffect, useMemo, useState } from "react"; import Reanimated, { LinearTransition } from "react-native-reanimated"; @@ -12,39 +11,30 @@ import { TimetableItem } from "../../Lessons/Atoms/Item"; import { PapillonNavigation } from "@/router/refs"; import RedirectButton from "@/components/Home/RedirectButton"; import { TimetableClass } from "@/services/shared/Timetable"; -import { translateToWeekNumber } from "pawnote"; // actually a reusable function ! +import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; const TimetableElement = () => { const account = useCurrentAccount(store => store.account!); const timetables = useTimetableStore(store => store.timetables); - const [firstDate, setFirstDate] = useState(new Date("2024-09-01")); const [courses, setCourses] = useState([]); - useEffect(() => { - if (account.instance && account.service === AccountService.Pronote) { - setFirstDate(account.instance.instance.firstDate); - } - }, [account?.instance]); - - const weekNumber = useMemo(() => { - return translateToWeekNumber(new Date(), firstDate) || 1; - }, [firstDate]); + const epochWeekNumber = useMemo(()=>dateToEpochWeekNumber(new Date()),[]); const [currentlyUpdating, setCurrentlyUpdating] = useState(false); useEffect(() => { - if (!timetables[weekNumber] && !currentlyUpdating && account.instance) { + if (!timetables[epochWeekNumber] && !currentlyUpdating && account.instance) { setCurrentlyUpdating(true); - updateTimetableForWeekInCache(account, weekNumber); + updateTimetableForWeekInCache(account, epochWeekNumber); } - }, [weekNumber, currentlyUpdating, timetables, account?.instance]); + }, [epochWeekNumber, currentlyUpdating, timetables, account?.instance]); const nextCourseIndex = useMemo(() => { - if (timetables[weekNumber]) { + if (timetables[epochWeekNumber]) { const currentDay = new Date(); - const courses = timetables[weekNumber].filter(c => new Date(c.startTimestamp).getDay() === currentDay.getDay()); + const courses = timetables[epochWeekNumber].filter(c => new Date(c.startTimestamp).getDay() === currentDay.getDay()); setCourses(courses); const nextCourse = courses.find(c => new Date(c.startTimestamp) > currentDay); @@ -55,7 +45,7 @@ const TimetableElement = () => { } return null; - }, [timetables, weekNumber]); + }, [timetables, epochWeekNumber]); if (courses.length === 0) { return null; diff --git a/src/views/account/Homeworks/Atoms/Item.tsx b/src/views/account/Homeworks/Atoms/Item.tsx index 3ddc7c91..ae09a33c 100644 --- a/src/views/account/Homeworks/Atoms/Item.tsx +++ b/src/views/account/Homeworks/Atoms/Item.tsx @@ -15,7 +15,7 @@ const HomeworkItem = React.memo(({ homework, onDonePressHandler, index, total }: total: number }) => { const theme = useTheme(); - const [subjectData, setSubjectData] = useState({ color: "#888888", pretty: "Matière inconnue" }); + const [subjectData, setSubjectData] = useState(getSubjectData(homework.subject)); useEffect(() => { const data = getSubjectData(homework.subject); @@ -61,6 +61,6 @@ const HomeworkItem = React.memo(({ homework, onDonePressHandler, index, total }: ); -}); +}, (prevProps, nextProps) => prevProps.index === nextProps.index); export default HomeworkItem; \ No newline at end of file diff --git a/src/views/account/Homeworks/Homeworks.tsx b/src/views/account/Homeworks/Homeworks.tsx index 7e882ff4..cbf76bee 100644 --- a/src/views/account/Homeworks/Homeworks.tsx +++ b/src/views/account/Homeworks/Homeworks.tsx @@ -1,6 +1,6 @@ import { useTheme } from "@react-navigation/native"; import React, { useEffect, useRef, useCallback, useLayoutEffect, useMemo, useState } from "react"; -import { View, ScrollView } 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"; @@ -10,11 +10,12 @@ import HomeworkItem from "./Atoms/Item"; import { RefreshControl } from "react-native-gesture-handler"; import HomeworksNoHomeworksItem from "./Atoms/NoHomeworks"; import { Homework } from "@/services/shared/Homework"; -import InfinitePager from "react-native-infinite-pager"; import PagerView from "react-native-pager-view"; import { NativeList, NativeListHeader } from "@/components/Global/NativeComponents"; import { Account, AccountService } from "@/stores/account/types"; import { debounce } from "lodash"; +import { dateToEpochWeekNumber, epochWNToDate } from "@/utils/epochWeekNumber"; +import InfinitePager from "react-native-infinite-pager"; // Types pour les props du composant HomeworkList type HomeworkListProps = { @@ -55,7 +56,7 @@ const HomeworkList: React.FC = React.memo(({ groupedHomework, ))} ); -}); +}, (prevProps, nextProps) => prevProps.groupedHomework === nextProps.groupedHomework && prevProps.loading === nextProps.loading); // Types pour les props du composant HomeworksPage type HomeworksPageProps = { @@ -70,8 +71,18 @@ type HomeworksPageProps = { }; const HomeworksPage: React.FC = React.memo(({ index, isActive, loaded, homeworks, account, updateHomeworks, loading, getDayName }) => { + const [refreshing, setRefreshing] = useState(false); if (!loaded) { - return null; + return + + + + {index} + + + ; } const homeworksInWeek = homeworks[index] ?? []; @@ -106,8 +117,6 @@ const HomeworksPage: React.FC = React.memo(({ index, isActiv [account, updateHomeworks] ); - const [refreshing, setRefreshing] = useState(false); - const refreshAction = useCallback(async () => { setRefreshing(true); await updateHomeworks(); @@ -130,11 +139,14 @@ const HomeworksPage: React.FC = React.memo(({ index, isActiv onDonePressHandler={handleDonePress} /> - ); +}, (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!); @@ -143,65 +155,49 @@ const HomeworksScreen: Screen<"Homeworks"> = ({ navigation }) => { // NOTE: PagerRef is a pain to type, please help me... const PagerRef = useRef(null); - const [weekNumber, setWeekNumber] = useState(1); + const [epochWeekNumber, setEpochWeekNumber] = useState(initialIndex); const [loading, setLoading] = useState(false); - const [refreshing, setRefreshing] = useState(false); useEffect(() => { console.log("[Homeworks]: account instance changed"); if (account.instance) { - const firstDate = account.service === AccountService.Pronote ? account.instance.instance.firstDate : new Date("2024-09-01"); - const today = new Date(); - - // get week number - const diff = today.getTime() - firstDate.getTime(); - const oneWeek = 1000 * 60 * 60 * 24 * 7; - let weekNumber = Math.floor(diff / oneWeek); - - if(weekNumber <= 0) weekNumber = 1; - if(weekNumber >= 54) weekNumber = 53; - - console.log("[Homeworks]: setting week number to", weekNumber); - setWeekNumber(weekNumber); - - manuallyChangeWeek(weekNumber); + const WN = initialIndex; + manuallyChangeWeek(WN); } }, [account.instance]); const manuallyChangeWeek = (index: number) => { + setEpochWeekNumber(index); PagerRef.current?.setPage(index); }; const MemoizedHeaderCalendar = useMemo( () => ( { - /* Implement date picker logic here */ + // TODO: Implement date picker logic here }} changeIndex={(index: number) => manuallyChangeWeek(index)} /> ), - [weekNumber, manuallyChangeWeek] + [epochWeekNumber, manuallyChangeWeek] ); useLayoutEffect(() => { navigation.setOptions({ headerTitle: () => MemoizedHeaderCalendar, }); - }, [navigation, weekNumber]); - - const homeworkInCurrentWeek = useMemo(() => homeworks[weekNumber] ?? [], [homeworks, weekNumber]); + }, [navigation, epochWeekNumber]); const updateHomeworks = useCallback(async () => { - if (weekNumber < 0 || weekNumber > 53) return; setLoading(true); - console.log("[Homeworks]: updating cache..."); - await updateHomeworkForWeekInCache(account, weekNumber); - console.log("[Homeworks]: updated cache !"); + console.log("[Homeworks]: updating cache...",epochWeekNumber, epochWNToDate(epochWeekNumber)); + await updateHomeworkForWeekInCache(account, epochWNToDate(epochWeekNumber)); + console.log("[Homeworks]: updated cache !", epochWNToDate(epochWeekNumber)); setLoading(false); - }, [account, weekNumber]); + }, [account, epochWeekNumber]); const debouncedUpdateHomeworks = useMemo(() => debounce(updateHomeworks, 500), [updateHomeworks]); @@ -212,7 +208,7 @@ const HomeworksScreen: Screen<"Homeworks"> = ({ navigation }) => { useEffect(() => { debouncedUpdateHomeworks(); - }, [navigation, account.instance, weekNumber]); + }, [navigation, account.instance, epochWeekNumber]); return ( = ({ navigation }) => { }} > {account.instance && ( - { - if (nativeEvent.offset >= 0.5) - setWeekNumber(nativeEvent.position + 1); - else if (nativeEvent.offset <= -0.5) - setWeekNumber(nativeEvent.position - 1); - else { - setWeekNumber(nativeEvent.position); - } - }} - > - {Array.from({ length: 54 }, (_, i) => i).map((_, index) => ( - weekNumber - 2 && index < weekNumber + 2} - homeworks={homeworks} - account={account} - updateHomeworks={updateHomeworks} - loading={loading} - getDayName={getDayName} - /> - ))} - + initialIndex={initialIndex} + pageBuffer={3} + PageComponent={ + ({index, isActive}) => ( + + )} + style={{ flex: 1}} + onPageChange={setEpochWeekNumber} + /> )} ); diff --git a/src/views/account/Homeworks/HomeworksHeader.tsx b/src/views/account/Homeworks/HomeworksHeader.tsx index 2ae99c8d..919e7658 100644 --- a/src/views/account/Homeworks/HomeworksHeader.tsx +++ b/src/views/account/Homeworks/HomeworksHeader.tsx @@ -8,14 +8,15 @@ import Reanimated, { ZoomIn, ZoomOut } from "react-native-reanimated"; +import { epochWMToCalendarWeekNumber } from "@/utils/epochWeekNumber"; -const HeaderCalendar: React.FC<{ weekNumber: number, oldPageIndex: number, showPicker: () => void, changeIndex: (index: number) => void }> = ({ weekNumber, oldPageIndex, showPicker, changeIndex }) => { +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 index = oldPageIndex + weekNumber; + const index = epochWeekNumber; return ( - {weekNumber > 0 ? - changeIndex(weekNumber - 2)} - /> - : } - {weekNumber > 0 ? - changeIndex(weekNumber - 1)} - /> - : } changeIndex(epochWeekNumber - 2)} + /> + changeIndex(epochWeekNumber - 1)} + /> + changeIndex(weekNumber + 1)} + onPress={() => changeIndex(epochWeekNumber + 1)} /> changeIndex(weekNumber + 2)} + onPress={() => changeIndex(epochWeekNumber + 2)} /> ); }; -const HeaderWeekComponent: React.FC<{ weekNumber: number, active: boolean, location?: string, onPress?: () => void }> = ({ weekNumber, active, location, onPress }) => { +const HeaderWeekComponent: React.FC<{ epochWeekNumber: number, active: boolean, location?: string, onPress?: () => void }> = ({ epochWeekNumber, active, location, onPress }) => { const { colors } = useTheme(); return ( @@ -142,7 +139,7 @@ const HeaderWeekComponent: React.FC<{ weekNumber: number, active: boolean, locat }} layout={LinearTransition.duration(200)} > - Semaine {weekNumber} + Semaine {epochWMToCalendarWeekNumber(epochWeekNumber)} diff --git a/src/views/account/Lessons/Atoms/Page.tsx b/src/views/account/Lessons/Atoms/Page.tsx index aa4b7a2a..d3591f52 100644 --- a/src/views/account/Lessons/Atoms/Page.tsx +++ b/src/views/account/Lessons/Atoms/Page.tsx @@ -29,9 +29,9 @@ const lz = (num: number) => (num < 10 ? `0${num}` : num); interface Props { index: number timetables: Record - loadTimetableWeek: (weekNumber: number, force?: boolean) => Promise + loadTimetableWeek: (epochWeekNumber: number, force?: boolean) => Promise getWeekFromIndex: (index: number) => { - weekNumber: number; + epochWeekNumber: number; dayNumber: number; } current: boolean @@ -45,10 +45,10 @@ export const Page: React.FC = ({ getWeekFromIndex, }) => { const { colors } = useTheme(); - const { weekNumber, dayNumber } = useMemo(() => getWeekFromIndex(index), [index, getWeekFromIndex]); + const { epochWeekNumber, dayNumber } = useMemo(() => getWeekFromIndex(index), [index, getWeekFromIndex]); - const currentDayTimetable: Timetable = (!(weekNumber in timetables)) ? [] - : timetables[weekNumber] + const currentDayTimetable: Timetable = (!(epochWeekNumber in timetables)) ? [] + : timetables[epochWeekNumber] .filter(c => new Date(c.startTimestamp).getDay() === dayNumber) .sort((a, b) => a.startTimestamp - b.startTimestamp); @@ -56,7 +56,7 @@ export const Page: React.FC = ({ const handleRefresh = async () => { setIsRefreshing(true); - await loadTimetableWeek(weekNumber, true); + await loadTimetableWeek(epochWeekNumber, true); setIsRefreshing(false); }; diff --git a/src/views/account/Lessons/Lessons.tsx b/src/views/account/Lessons/Lessons.tsx index a9894b2a..5b6d339e 100644 --- a/src/views/account/Lessons/Lessons.tsx +++ b/src/views/account/Lessons/Lessons.tsx @@ -10,17 +10,17 @@ import { Page } from "./Atoms/Page"; import InfinitePager from "react-native-infinite-pager"; import { HeaderCalendar, LessonsDateModal } from "./LessonsHeader"; -import { AccountService } from "@/stores/account/types"; import type { Timetable as TTimetable } from "@/services/shared/Timetable"; +import { dateToEpochWeekNumber } from "@/utils/epochWeekNumber"; const RenderPage = ({ index, timetables, getWeekFromIndex, loadTimetableWeek, currentPageIndex } : { index: number timetables: Record getWeekFromIndex: (index: number) => { - weekNumber: number; + epochWeekNumber: number; dayNumber: number; } - loadTimetableWeek: (weekNumber: number) => Promise + loadTimetableWeek: (epochWeekNumber: number) => Promise currentPageIndex: number }) => ( @@ -50,16 +50,6 @@ const Timetable: Screen<"Lessons"> = ({ navigation }) => { const today = useMemo(() => new Date(), []); const defaultDate = useMemo(() => new Date(today), [today]); - const [firstDate, setFirstDate] = useState(new Date("2024-09-01")); - - useEffect(() => { - if (account.instance) { - if (account.service === AccountService.Pronote) { - setFirstDate(new Date(account.instance.instance.firstDate)); - } - } - }, [account]); - const getDateFromIndex = useCallback((index: number) => { const date = new Date(defaultDate); date.setDate(date.getDate() + index); @@ -68,25 +58,23 @@ const Timetable: Screen<"Lessons"> = ({ navigation }) => { const getWeekFromIndex = (index: number) => { const date = getDateFromIndex(index); - const firstDayOfYear = new Date(firstDate); - const pastDaysOfYear = (date.getTime() - firstDayOfYear.getTime()) / 86400000; - const weekNumber = Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7); + const epochWeekNumber = dateToEpochWeekNumber(date); const dayNumber = date.getDay(); - return { weekNumber, dayNumber }; + return { epochWeekNumber, dayNumber }; }; - const loadTimetableWeek = async (weekNumber: number, force = false) => { - if (currentlyLoadingWeeks.current.has(weekNumber)) return; - if (loadedWeeks.current.has(weekNumber) && !force) return; - currentlyLoadingWeeks.current.add(weekNumber); + const loadTimetableWeek = async (epochWeekNumber: number, force = false) => { + if (currentlyLoadingWeeks.current.has(epochWeekNumber)) return; + if (loadedWeeks.current.has(epochWeekNumber) && !force) return; + currentlyLoadingWeeks.current.add(epochWeekNumber); try { - await updateTimetableForWeekInCache(account, weekNumber); - loadedWeeks.current.add(weekNumber); + await updateTimetableForWeekInCache(account, epochWeekNumber); + loadedWeeks.current.add(epochWeekNumber); } catch (error) { console.error(error); } finally { - currentlyLoadingWeeks.current.delete(weekNumber); + currentlyLoadingWeeks.current.delete(epochWeekNumber); } }; @@ -97,8 +85,8 @@ const Timetable: Screen<"Lessons"> = ({ navigation }) => { }, [timetables]); useEffect(() => { - const { weekNumber } = getWeekFromIndex(currentPageIndex); - loadTimetableWeek(weekNumber); + const { epochWeekNumber } = getWeekFromIndex(currentPageIndex); + loadTimetableWeek(epochWeekNumber); }, [currentPageIndex]); useEffect(() => { @@ -130,7 +118,6 @@ const Timetable: Screen<"Lessons"> = ({ navigation }) => {