Files
cally/components/pages/calendar/CalendarHeader.tsx
Milan Paunovic c184eb3293 adujstments
2025-02-16 01:07:12 +01:00

181 lines
5.9 KiB
TypeScript

import React, {memo, useCallback, useMemo, useState} from "react";
import {StyleSheet} from "react-native";
import {Button, Picker, PickerModes, SegmentedControl, Text, View} from "react-native-ui-lib";
import {MaterialIcons} from "@expo/vector-icons";
import {useAtom} from "jotai";
import {format} from "date-fns";
import * as Device from "expo-device";
import {useIsFetching} from "@tanstack/react-query";
import {modeAtom, selectedDateAtom} from "@/components/pages/calendar/atoms";
import {months} from "./constants";
import RefreshButton from "@/components/shared/RefreshButton";
import {useCalSync} from "@/hooks/useCalSync";
type ViewMode = "day" | "week" | "month" | "3days";
const isTablet = Device.deviceType === Device.DeviceType.TABLET;
const SEGMENTS = isTablet
? [{label: "D"}, {label: "W"}, {label: "M"}]
: [{label: "D"}, {label: "3D"}, {label: "M"}];
const MODE_MAP = {
tablet: ["day", "week", "month"],
mobile: ["day", "3days", "month"]
} as const;
export const CalendarHeader = memo(() => {
const [selectedDate, setSelectedDate] = useAtom(selectedDateAtom);
const [mode, setMode] = useAtom(modeAtom);
const [tempIndex, setTempIndex] = useState<number | null>(null);
const {resyncAllCalendars, isSyncing} = useCalSync();
const isFetching = useIsFetching({queryKey: ['events']}) > 0;
const isLoading = useMemo(() => isSyncing || isFetching, [isSyncing, isFetching]);
const handleSegmentChange = useCallback((index: number) => {
const modes = isTablet ? MODE_MAP.tablet : MODE_MAP.mobile;
const selectedMode = modes[index] as ViewMode;
setTempIndex(index);
setTimeout(() => {
setMode(selectedMode);
setTempIndex(null);
}, 150);
}, [setMode]);
const handleMonthChange = useCallback((month: string) => {
const newMonthIndex = months.indexOf(month);
const updatedDate = new Date(
selectedDate.getFullYear(),
newMonthIndex,
selectedDate.getDate()
);
setSelectedDate(updatedDate);
}, [selectedDate, setSelectedDate]);
const handleRefresh = useCallback(async () => {
try {
await resyncAllCalendars();
} catch (error) {
console.error("Refresh failed:", error);
}
}, [resyncAllCalendars]);
const getInitialIndex = useCallback(() => {
const modes = isTablet ? MODE_MAP.tablet : MODE_MAP.mobile;
//@ts-ignore
return modes.indexOf(mode);
}, [mode]);
const renderMonthPicker = () => (
<>
{isTablet && <View flexG/>}
<View row centerV gap-1 flexS>
{isTablet && (
<Text style={styles.yearText}>
{selectedDate.getFullYear()}
</Text>
)}
<Picker
value={months[selectedDate.getMonth()]}
placeholder="Select Month"
style={styles.monthPicker}
mode={PickerModes.SINGLE}
onChange={value => handleMonthChange(value as string)}
trailingAccessory={<MaterialIcons name="keyboard-arrow-down"/>}
topBarProps={{
title: selectedDate.getFullYear().toString(),
titleStyle: styles.yearText,
}}
>
{months.map(month => (
<Picker.Item key={month} label={month} value={month}/>
))}
</Picker>
</View>
</>
);
return (
<View style={styles.container} flexG centerV>
{mode !== "month" ? renderMonthPicker() : <View flexG/>}
<View row centerV flexS>
<Button
size="xSmall"
marginR-1
avoidInnerPadding
style={styles.todayButton}
onPress={() => setSelectedDate(new Date())}
>
<MaterialIcons name="calendar-today" size={30} color="#5f6368"/>
<Text style={styles.todayDate}>{format(new Date(), "d")}</Text>
</Button>
<View style={styles.segmentContainer}>
<SegmentedControl
segments={SEGMENTS}
backgroundColor="#ececec"
inactiveColor="#919191"
activeBackgroundColor="#ea156c"
activeColor="white"
outlineColor="white"
outlineWidth={3}
segmentLabelStyle={styles.segmentLabel}
onChangeIndex={handleSegmentChange}
initialIndex={tempIndex ?? getInitialIndex()}
/>
</View>
<RefreshButton onRefresh={handleRefresh} isSyncing={isLoading}/>
</View>
</View>
);
});
const styles = StyleSheet.create({
container: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingVertical: isTablet ? 8 : 0,
borderBottomLeftRadius: 0,
borderBottomRightRadius: 0,
paddingLeft: 10
},
yearText: {
fontFamily: "Manrope_500Medium",
fontSize: 17,
},
monthPicker: {
fontFamily: "Manrope_500Medium",
fontSize: 17,
width: 85,
},
segmentContainer: {
maxWidth: 120,
height: 40,
},
segmentLabel: {
fontSize: 12,
fontFamily: "Manrope_600SemiBold",
},
todayButton: {
backgroundColor: "transparent",
borderWidth: 0,
height: 30,
width: 30,
alignItems: 'center',
justifyContent: 'center',
},
todayDate: {
position: 'absolute',
fontSize: 12,
fontFamily: "Manrope_600SemiBold",
color: "#5f6368",
top: '30%',
},
});