Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 40 additions & 30 deletions app/(tabs)/calendar/hooks/useCalendarState.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState, useRef, useEffect, useCallback } from 'react';
import { Dimensions, FlatList } from 'react-native';
import { useState, useRef, useEffect, useCallback } from "react";
import { Dimensions, FlatList } from "react-native";
import { getWeekNumberFromDate } from "@/database/useHomework";
import { warn } from "@/utils/logger/logger";

Expand Down Expand Up @@ -28,17 +28,17 @@ export function useCalendarState() {
base.setHours(0, 0, 0, 0);
const target = new Date(d);
target.setHours(0, 0, 0, 0);
const diff = Math.round((target.getTime() - base.getTime()) / (1000 * 60 * 60 * 24));
const diff = Math.round(
(target.getTime() - base.getTime()) / (1000 * 60 * 60 * 24)
);
return INITIAL_INDEX + diff;
}, []);

const handleDateChange = useCallback((newDate: Date) => {
setDate(newDate);
const newWeekNumber = getWeekNumberFromDate(newDate);
if (newWeekNumber !== weekNumber) {
setWeekNumber(newWeekNumber);
}
}, [weekNumber]);
setWeekNumber(prev => (prev !== newWeekNumber ? newWeekNumber : prev));
}, []);

// Sync FlatList with date
useEffect(() => {
Expand All @@ -58,7 +58,7 @@ export function useCalendarState() {
animated: false,
});
} catch (e) {
warn(String(e))
warn(String(e));
}
}
}
Expand All @@ -68,31 +68,41 @@ export function useCalendarState() {
}
}, [date, getIndexFromDate, currentIndex, weekNumber]);

const onMomentumScrollEnd = useCallback((e: any) => {
const newIndex = Math.round(e.nativeEvent.contentOffset.x / windowWidth);
if (newIndex !== currentIndex) {
setCurrentIndex(newIndex);
const newDate = getDateFromIndex(newIndex);
setDate((prev) => prev.getTime() !== newDate.getTime() ? newDate : prev);
}
}, [windowWidth, currentIndex, getDateFromIndex]);
const onMomentumScrollEnd = useCallback(
(e: any) => {
const newIndex = Math.round(e.nativeEvent.contentOffset.x / windowWidth);
if (newIndex !== currentIndex) {
setCurrentIndex(newIndex);
const newDate = getDateFromIndex(newIndex);
setDate(prev =>
prev.getTime() !== newDate.getTime() ? newDate : prev
);
}
},
[windowWidth, currentIndex, getDateFromIndex]
);

const lastEmittedIndex = useRef(currentIndex);

const onScroll = useCallback((e: any) => {
const offsetX = e.nativeEvent.contentOffset.x;
const newIndex = Math.round(offsetX / windowWidth);
if (newIndex !== lastEmittedIndex.current) {
lastEmittedIndex.current = newIndex;
setCurrentIndex(newIndex);
const newDate = getDateFromIndex(newIndex);
setDate((prev) => prev.getTime() !== newDate.getTime() ? newDate : prev);
const newWeekNumber = getWeekNumberFromDate(newDate);
if (newWeekNumber !== weekNumber) {
setWeekNumber(newWeekNumber);
const onScroll = useCallback(
(e: any) => {
const offsetX = e.nativeEvent.contentOffset.x;
const newIndex = Math.round(offsetX / windowWidth);
if (newIndex !== lastEmittedIndex.current) {
lastEmittedIndex.current = newIndex;
setCurrentIndex(newIndex);
const newDate = getDateFromIndex(newIndex);
setDate(prev =>
prev.getTime() !== newDate.getTime() ? newDate : prev
);
const newWeekNumber = getWeekNumberFromDate(newDate);
if (newWeekNumber !== weekNumber) {
setWeekNumber(newWeekNumber);
}
}
}
}, [windowWidth, getDateFromIndex, weekNumber]);
},
[windowWidth, getDateFromIndex, weekNumber]
);

return {
date,
Expand All @@ -107,6 +117,6 @@ export function useCalendarState() {
onMomentumScrollEnd,
onScroll,
INITIAL_INDEX,
windowWidth
windowWidth,
};
}
50 changes: 36 additions & 14 deletions app/(tabs)/calendar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useRef, useCallback, useState } from "react";
import React, { useRef, useCallback, useState, useMemo } from "react";
import { View, FlatList, StyleSheet } from "react-native";
import { useTheme } from "@react-navigation/native";
import { useCalendarState } from "./hooks/useCalendarState";
Expand All @@ -20,7 +20,6 @@ export default function TabOneScreen() {
const {
date,
weekNumber,
currentIndex,
flatListRef,
getDateFromIndex,
handleDateChange,
Expand All @@ -37,15 +36,26 @@ export default function TabOneScreen() {
isLoading
} = useTimetableData(weekNumber, date);

const timetableMap = useMemo(() => {
const map = new Map<number, (typeof timetable)[number]["courses"]>();

timetable.forEach(day => {
const normalized = new Date(day.date);
normalized.setHours(0, 0, 0, 0);
const key = normalized.getTime();
map.set(key, day.courses);
});

return map;
}, [timetable]);

const renderDay = useCallback(({ index }: { index: number }) => {
const dayDate = getDateFromIndex(index);
const normalizedDate = new Date(dayDate);

normalizedDate.setHours(0, 0, 0, 0);
const dayCourses = timetable.find(d => {
const dDate = new Date(d.date);
dDate.setHours(0, 0, 0, 0);
return dDate.getTime() === normalizedDate.getTime();
})?.courses || [];

const dayCourses = timetableMap.get(normalizedDate.getTime()) || [];

return (
<CalendarDay
Expand All @@ -59,7 +69,14 @@ export default function TabOneScreen() {
tabBarHeight={tabBarHeight}
/>
);
}, [getDateFromIndex, timetable, manualRefreshing, handleRefresh, colors, headerHeight]);
}, [getDateFromIndex, timetableMap, manualRefreshing, handleRefresh, colors, headerHeight, insets, tabBarHeight]);

const extraData = useMemo(() => ({
manualRefreshing,
headerHeight,
colors,
timetableVersion: timetable.length
}), [manualRefreshing, headerHeight, colors, timetable.length]);

return (
<>
Expand All @@ -83,18 +100,23 @@ export default function TabOneScreen() {
renderItem={renderDay}
keyExtractor={(_, index) => "renderDay:" + String(index)}
onScroll={onScroll}
decelerationRate={0.98}
disableIntervalMomentum={true}

decelerationRate="fast"
disableIntervalMomentum={false}
scrollEventThrottle={16}
onMomentumScrollEnd={onMomentumScrollEnd}
snapToInterval={windowWidth}
snapToAlignment="start"
bounces={false}
windowSize={4}
maxToRenderPerBatch={3}
initialNumToRender={3}

windowSize={5}
maxToRenderPerBatch={2}
initialNumToRender={1}
updateCellsBatchingPeriod={50}

showsVerticalScrollIndicator={false}
removeClippedSubviews
extraData={{ manualRefreshing, headerHeight, colors, timetable }}
extraData={extraData}
/>
</View>
</>
Expand Down