Calendar page refactor

This commit is contained in:
Milan Paunovic
2024-10-19 17:20:34 +02:00
parent d2b46ad977
commit 3f7fc92952
21 changed files with 1580 additions and 1755 deletions

View File

@ -1,14 +1,11 @@
import React from "react";
import { CalendarProvider } from "@/contexts/CalendarContext"; // Import the new CalendarPage component
import CalendarPage from "@/components/pages/calendar/CalendarPage";
import { SettingsContextProvider } from "@/contexts/SettingsContext";
import {SettingsContextProvider} from "@/contexts/SettingsContext";
export default function Screen() {
return (
<SettingsContextProvider>
<CalendarProvider>
<CalendarPage />
</CalendarProvider>
<CalendarPage/>
</SettingsContextProvider>
);
}

View File

@ -1,7 +1,6 @@
import React, { useEffect } from "react";
import { DefaultTheme, ThemeProvider } from "@react-navigation/native";
import React, {useEffect} from "react";
import {DefaultTheme, ThemeProvider} from "@react-navigation/native";
import {
useFonts,
Manrope_200ExtraLight,
Manrope_300Light,
Manrope_400Regular,
@ -9,21 +8,22 @@ import {
Manrope_600SemiBold,
Manrope_700Bold,
Manrope_800ExtraBold,
useFonts,
} from "@expo-google-fonts/manrope";
import {
PlusJakartaSans_200ExtraLight,
PlusJakartaSans_300Light,
PlusJakartaSans_400Regular,
PlusJakartaSans_500Medium,
PlusJakartaSans_600SemiBold,
PlusJakartaSans_700Bold,
PlusJakartaSans_800ExtraBold,
PlusJakartaSans_200ExtraLight_Italic,
PlusJakartaSans_300Light,
PlusJakartaSans_300Light_Italic,
PlusJakartaSans_400Regular,
PlusJakartaSans_400Regular_Italic,
PlusJakartaSans_500Medium,
PlusJakartaSans_500Medium_Italic,
PlusJakartaSans_600SemiBold,
PlusJakartaSans_600SemiBold_Italic,
PlusJakartaSans_700Bold,
PlusJakartaSans_700Bold_Italic,
PlusJakartaSans_800ExtraBold,
PlusJakartaSans_800ExtraBold_Italic,
} from "@expo-google-fonts/plus-jakarta-sans";
import {
@ -46,29 +46,21 @@ import {
Poppins_900Black,
Poppins_900Black_Italic,
} from "@expo-google-fonts/poppins";
import { Stack } from "expo-router";
import {Stack} from "expo-router";
import * as SplashScreen from "expo-splash-screen";
import "react-native-reanimated";
import { AuthContextProvider } from "@/contexts/AuthContext";
import { QueryClient, QueryClientProvider } from "react-query";
import {
ThemeManager,
Typography,
Toast,
TextProps,
} from "react-native-ui-lib";
import functions from "@react-native-firebase/functions";
import auth from "@react-native-firebase/auth";
import firestore from "@react-native-firebase/firestore";
import {AuthContextProvider} from "@/contexts/AuthContext";
import {QueryClient, QueryClientProvider} from "react-query";
import {TextProps, ThemeManager, Toast, Typography,} from "react-native-ui-lib";
SplashScreen.preventAutoHideAsync();
const queryClient = new QueryClient();
if (__DEV__) {
functions().useEmulator("localhost", 5001);
firestore().useEmulator("localhost", 5471);
auth().useEmulator("http://localhost:9099");
// functions().useEmulator("localhost", 5001);
// firestore().useEmulator("localhost", 5471);
// auth().useEmulator("http://localhost:9099");
}
type TextStyleBase =
@ -160,7 +152,7 @@ const getManropeFontStyle = (style: TextStyle): FontStyle => {
fontSize = 16;
}
return { fontFamily, fontSize };
return {fontFamily, fontSize};
};
export default function RootLayout() {
@ -236,7 +228,7 @@ export default function RootLayout() {
ThemeManager.setComponentTheme(
"Text",
(props: ExtendedTextProps, context: unknown) => {
(props: ExtendedTextProps) => {
const textStyle = (
Object.keys(props) as Array<keyof ExtendedTextProps>
).find((key) => typographies[key as TextStyle]) as
@ -263,11 +255,11 @@ export default function RootLayout() {
<AuthContextProvider>
<ThemeProvider value={DefaultTheme}>
<Stack>
<Stack.Screen name="(auth)" options={{ headerShown: false }} />
<Stack.Screen name="(unauth)" options={{ headerShown: false }} />
<Stack.Screen name="+not-found" />
<Stack.Screen name="(auth)" options={{headerShown: false}}/>
<Stack.Screen name="(unauth)" options={{headerShown: false}}/>
<Stack.Screen name="+not-found"/>
</Stack>
<Toast />
<Toast/>
</ThemeProvider>
</AuthContextProvider>
</QueryClientProvider>

View File

@ -1,38 +1,26 @@
import React, { useState } from "react";
import {
AntDesign,
Feather,
MaterialCommunityIcons,
MaterialIcons,
} from "@expo/vector-icons";
import {
Button,
ButtonSize,
Card,
Dialog,
PanningProvider,
Text,
View,
} from "react-native-ui-lib";
import { StyleSheet, TouchableOpacity } from "react-native";
import { ManuallyAddEventModal } from "@/components/pages/calendar/ManuallyAddEventModal";
import React, {useState} from "react";
import {MaterialIcons,} from "@expo/vector-icons";
import {Button, Card, Dialog, PanningProvider, Text, View,} from "react-native-ui-lib";
import {StyleSheet, TouchableOpacity} from "react-native";
import AddChoreDialog from "../todos/AddChoreDialog";
import { ToDosContextProvider } from "@/contexts/ToDosContext";
import {ToDosContextProvider} from "@/contexts/ToDosContext";
import UploadImageDialog from "./UploadImageDialog";
import CameraIcon from "@/assets/svgs/CameraIcon";
import CalendarIcon from "@/assets/svgs/CalendarIcon";
import NavToDosIcon from "@/assets/svgs/NavToDosIcon";
import {useSetAtom} from "jotai";
import {selectedNewEventDateAtom} from "@/components/pages/calendar/atoms";
export const AddEventDialog = () => {
const [show, setShow] = useState(false);
const [showManualInputModal, setShowManualInputModal] = useState(false);
const [choreDialogVisible, setChoreDialogVisible] = useState<boolean>(false);
const [showUploadDialog, setShowUploadDialog] = useState<boolean>(false);
const setSelectedNewEndDate = useSetAtom(selectedNewEventDateAtom)
const handleOpenManualInputModal = () => {
setShow(false);
setTimeout(() => {
setShowManualInputModal(true);
setSelectedNewEndDate(new Date());
}, 500);
};
@ -62,8 +50,8 @@ export const AddEventDialog = () => {
onPress={() => setShow(true)}
>
<View row centerV centerH>
<MaterialIcons name="add" size={22} color={"white"} />
<Text white style={{ fontSize: 16, fontFamily: 'Manrope_600SemiBold' }}>
<MaterialIcons name="add" size={22} color={"white"}/>
<Text white style={{fontSize: 16, fontFamily: 'Manrope_600SemiBold'}}>
New
</Text>
</View>
@ -81,7 +69,7 @@ export const AddEventDialog = () => {
</Text>
<View
style={{ marginTop: 20, alignItems: "center", width: "100%" }}
style={{marginTop: 20, alignItems: "center", width: "100%"}}
>
<Button
style={{
@ -95,7 +83,7 @@ export const AddEventDialog = () => {
labelStyle={styles.btnLabel}
onPress={handleScanImageDialog}
iconSource={() => (
<CameraIcon color="white" style={styles.btnIcon} />
<CameraIcon color="white" style={styles.btnIcon}/>
)}
/>
@ -111,7 +99,7 @@ export const AddEventDialog = () => {
labelStyle={styles.btnLabel}
onPress={handleOpenManualInputModal}
iconSource={() => (
<CalendarIcon color={"white"} style={styles.btnIcon} />
<CalendarIcon color={"white"} style={styles.btnIcon}/>
)}
/>
@ -147,10 +135,6 @@ export const AddEventDialog = () => {
isVisible={choreDialogVisible}
setIsVisible={setChoreDialogVisible}
/>
<ManuallyAddEventModal
show={showManualInputModal}
close={() => setShowManualInputModal(false)}
/>
<UploadImageDialog
show={showUploadDialog}
setShow={setShowUploadDialog}
@ -184,5 +168,5 @@ const styles = StyleSheet.create({
fontSize: 15,
fontFamily: "PlusJakartaSans_500Medium",
},
btnIcon: { marginRight: 10 },
btnIcon: {marginRight: 10},
});

View File

@ -0,0 +1,102 @@
import React, {memo} from 'react';
import {Picker, PickerModes, SegmentedControl, Text, View} from "react-native-ui-lib";
import {MaterialIcons} from "@expo/vector-icons";
import {modeMap, months} from './constants';
import {StyleSheet} from "react-native";
import {useAtom} from "jotai";
import {modeAtom, selectedDateAtom} from "@/components/pages/calendar/atoms";
export const CalendarHeader = memo(() => {
const [selectedDate, setSelectedDate] = useAtom(selectedDateAtom)
const [mode, setMode] = useAtom(modeAtom)
const handleSegmentChange = (index: number) => {
const selectedMode = modeMap.get(index);
if (selectedMode) {
setMode(selectedMode as "day" | "week" | "month");
}
};
const handleMonthChange = (month: string) => {
const currentDay = selectedDate.getDate();
const currentYear = selectedDate.getFullYear();
const newMonthIndex = months.indexOf(month);
const updatedDate = new Date(currentYear, newMonthIndex, currentDay);
setSelectedDate(updatedDate);
};
return (
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingHorizontal: 10,
paddingVertical: 8,
borderRadius: 20,
borderBottomLeftRadius: 0,
borderBottomRightRadius: 0,
backgroundColor: "white",
marginBottom: 10,
}}
>
<View row centerV gap-3>
<Text style={{fontFamily: "Manrope_500Medium", fontSize: 17}}>
{selectedDate.getFullYear()}
</Text>
<Picker
value={months[selectedDate.getMonth()]}
placeholder={"Select Month"}
style={{fontFamily: "Manrope_500Medium", fontSize: 17}}
mode={PickerModes.SINGLE}
onChange={(itemValue) => handleMonthChange(itemValue as string)}
trailingAccessory={<MaterialIcons name={"keyboard-arrow-down"}/>}
topBarProps={{
title: selectedDate.getFullYear().toString(),
titleStyle: {fontFamily: "Manrope_500Medium", fontSize: 17},
}}
>
{months.map((month) => (
<Picker.Item key={month} label={month} value={month}/>
))}
</Picker>
</View>
<View>
<SegmentedControl
segments={[{label: "D"}, {label: "W"}, {label: "M"}]}
backgroundColor="#ececec"
inactiveColor="#919191"
activeBackgroundColor="#ea156c"
activeColor="white"
outlineColor="white"
outlineWidth={3}
segmentLabelStyle={styles.segmentslblStyle}
onChangeIndex={handleSegmentChange}
initialIndex={mode === "day" ? 0 : mode === "week" ? 1 : 2}
/>
</View>
</View>
);
});
const styles = StyleSheet.create({
segmentslblStyle: {
fontSize: 12,
fontFamily: "Manrope_600SemiBold",
},
calHeader: {
borderWidth: 0,
},
dayModeHeader: {
alignSelf: "flex-start",
justifyContent: "space-between",
alignContent: "center",
width: 38,
right: 42,
},
});

View File

@ -1,207 +1,20 @@
import React, { useRef, useState } from "react";
import { LayoutChangeEvent, StyleSheet } from "react-native";
import { Calendar } from "react-native-big-calendar";
import {
Picker,
PickerModes,
SegmentedControl,
View,
} from "react-native-ui-lib";
import { MaterialIcons } from "@expo/vector-icons";
import { AddEventDialog } from "@/components/pages/calendar/AddEventDialog";
import React from "react";
import {View,} from "react-native-ui-lib";
import HeaderTemplate from "@/components/shared/HeaderTemplate";
import CalendarViewSwitch from "@/components/pages/calendar/CalendarViewSwitch";
import { ManuallyAddEventModal } from "@/components/pages/calendar/ManuallyAddEventModal";
import { CalendarEvent } from "@/contexts/CalendarContext";
import { useSettingsContext } from "@/contexts/SettingsContext";
import EditEventDialog from "./EditEventDialog";
import { useGetEvents } from "@/hooks/firebase/useGetEvents";
import { Text } from "react-native-ui-lib";
const modeMap = new Map([
[0, "day"],
[1, "week"],
[2, "month"],
]);
const months = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
import {InnerCalendar} from "@/components/pages/calendar/InnerCalendar";
export default function CalendarPage() {
const { calendarColor } = useSettingsContext();
const [editVisible, setEditVisible] = useState<boolean>(false);
const [eventForEdit, setEventForEdit] = useState<CalendarEvent>();
const styles = StyleSheet.create({
segmentslblStyle: {
fontSize: 12,
fontFamily: "Manrope_600SemiBold",
},
calHeader: {
borderWidth: 0,
},
dayModeHeader: {
alignSelf: "flex-start",
justifyContent: "space-between",
alignContent: "center",
width: 38,
right: 42,
},
});
const [isFamilyView, setIsFamilyView] = useState<boolean>(false);
const [calendarHeight, setCalendarHeight] = useState(0);
const [mode, setMode] = useState<"week" | "month" | "day">("week");
const [selectedDate, setSelectedDate] = useState<Date>(new Date());
const [selectedNewEventDate, setSelectedNewEndDate] = useState<
Date | undefined
>(undefined);
const calendarContainerRef = useRef(null);
const { data: events } = useGetEvents(isFamilyView);
const onLayout = (event: LayoutChangeEvent) => {
const { height } = event.nativeEvent.layout;
setCalendarHeight(height);
};
const handleSegmentChange = (index: number) => {
const selectedMode = modeMap.get(index);
if (selectedMode) {
setMode(selectedMode as "day" | "week" | "month");
}
};
const handleMonthChange = (month: string) => {
const currentDay = selectedDate.getDate();
const currentYear = selectedDate.getFullYear();
const newMonthIndex = months.indexOf(month);
const updatedDate = new Date(currentYear, newMonthIndex, currentDay);
setSelectedDate(updatedDate);
};
return (
<View
style={{ flex: 1, height: "100%", padding: 10 }}
style={{flex: 1, height: "100%", padding: 10}}
paddingH-22
paddingT-0
>
<HeaderTemplate
message={"Let's get your week started!"}
isWelcome={true}
/>
<View
style={{ flex: 1, backgroundColor: "#fff", borderRadius: 30 }}
ref={calendarContainerRef}
onLayout={onLayout}
>
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingHorizontal: 10,
paddingVertical: 8,
borderRadius: 20,
borderBottomLeftRadius: 0,
borderBottomRightRadius: 0,
backgroundColor: "white",
marginBottom: 10,
}}
>
<View row centerV gap-3>
<Text style={{ fontFamily: "Manrope_500Medium", fontSize: 17 }}>
{selectedDate.getFullYear()}
</Text>
<Picker
value={months[selectedDate.getMonth()]} // Get the month from the date
placeholder={"Select Month"}
style={{ fontFamily: "Manrope_500Medium", fontSize: 17 }}
mode={PickerModes.SINGLE}
onChange={(itemValue) => handleMonthChange(itemValue as string)}
trailingAccessory={<MaterialIcons name={"keyboard-arrow-down"} />}
topBarProps={{
title: selectedDate.getFullYear().toString(),
titleStyle: { fontFamily: "Manrope_500Medium", fontSize: 17 },
}}
>
{months.map((month) => (
<Picker.Item key={month} label={month} value={month} />
))}
</Picker>
</View>
<View>
<SegmentedControl
segments={[{ label: "D" }, { label: "W" }, { label: "M" }]}
backgroundColor="#ececec"
inactiveColor="#919191"
activeBackgroundColor="#ea156c"
activeColor="white"
outlineColor="white"
outlineWidth={3}
segmentLabelStyle={styles.segmentslblStyle}
onChangeIndex={handleSegmentChange}
initialIndex={mode === "day" ? 0 : mode === "week" ? 1 : 2}
/>
</View>
</View>
{calendarHeight > 0 && (
<Calendar
bodyContainerStyle={styles.calHeader}
mode={mode}
events={isFamilyView ? events ?? [] : events ?? []}
eventCellStyle={(event) => ({ backgroundColor: event.eventColor })}
onPressEvent={(event) => {
setEditVisible(true);
setEventForEdit(event);
}}
height={calendarHeight}
activeDate={selectedDate}
date={selectedDate}
onPressCell={setSelectedNewEndDate}
headerContentStyle={mode === "day" ? styles.dayModeHeader : {}}
onSwipeEnd={(date) => {
setSelectedDate(date);
}}
/>
)}
</View>
<CalendarViewSwitch viewSwitch={setIsFamilyView} />
<AddEventDialog />
{eventForEdit && (
<EditEventDialog
isVisible={editVisible}
setIsVisible={() => {
setEditVisible(!editVisible);
}}
event={eventForEdit}
/>
)}
<ManuallyAddEventModal
key={`${selectedNewEventDate}`}
initialDate={selectedNewEventDate}
show={!!selectedNewEventDate}
close={() => setSelectedNewEndDate(undefined)}
isWelcome
/>
<InnerCalendar/>
</View>
);
}

View File

@ -1,13 +1,13 @@
import { View, Text, Button, TouchableOpacity } from "react-native-ui-lib";
import React, { useState } from "react";
import { MaterialIcons } from "@expo/vector-icons";
import { StyleSheet } from "react-native";
import {Text, TouchableOpacity, View} from "react-native-ui-lib";
import React, {useState} from "react";
import {StyleSheet} from "react-native";
import {useSetAtom} from "jotai";
import {isFamilyViewAtom} from "@/components/pages/calendar/atoms";
interface ICalendarViewProps {
viewSwitch: (value: boolean) => void;
}
const CalendarViewSwitch = (calendarViewProps: ICalendarViewProps) => {
const CalendarViewSwitch = () => {
const [calView, setCalView] = useState<boolean>(false);
const viewSwitch = useSetAtom(isFamilyViewAtom)
return (
<View
@ -23,7 +23,7 @@ const CalendarViewSwitch = (calendarViewProps: ICalendarViewProps) => {
justifyContent: "center",
// iOS shadow
shadowColor: "#000",
shadowOffset: { width: 0, height: 2 },
shadowOffset: {width: 0, height: 2},
shadowOpacity: 0.25,
shadowRadius: 3.84,
// Android shadow (elevation)
@ -34,7 +34,7 @@ const CalendarViewSwitch = (calendarViewProps: ICalendarViewProps) => {
<TouchableOpacity
onPress={() => {
setCalView(true);
calendarViewProps.viewSwitch(true);
viewSwitch(true);
}}
>
<View
@ -53,7 +53,7 @@ const CalendarViewSwitch = (calendarViewProps: ICalendarViewProps) => {
<TouchableOpacity
onPress={() => {
setCalView(false);
calendarViewProps.viewSwitch(false);
viewSwitch(false);
}}
>
<View
@ -83,7 +83,7 @@ const styles = StyleSheet.create({
backgroundColor: "white",
borderRadius: 50,
},
switchTxt:{
switchTxt: {
fontSize: 16,
fontFamily: 'Manrope_600SemiBold'
}

View File

@ -1,42 +1,31 @@
import { View, Text, Button, Switch } from "react-native-ui-lib";
import React, { useEffect, useState } from "react";
import { Feather, AntDesign, Ionicons } from "@expo/vector-icons";
import {
Dialog,
TextField,
DateTimePicker,
Picker,
ButtonSize,
} from "react-native-ui-lib";
import { PanningDirectionsEnum } from "react-native-ui-lib/src/incubator/panView";
import { StyleSheet } from "react-native";
import {Button, ButtonSize, DateTimePicker, Dialog, Switch, Text, TextField, View} from "react-native-ui-lib";
import React from "react";
import {AntDesign, Feather, Ionicons} from "@expo/vector-icons";
import {PanningDirectionsEnum} from "react-native-ui-lib/src/incubator/panView";
import {StyleSheet} from "react-native";
import DropModalIcon from "@/assets/svgs/DropModalIcon";
import { CalendarEvent } from "@/contexts/CalendarContext";
import ClockIcon from "@/assets/svgs/ClockIcon";
import LockIcon from "@/assets/svgs/LockIcon";
import MenuIcon from "@/assets/svgs/MenuIcon";
import { useUpdateEvent } from "@/hooks/firebase/useUpdateEvent";
import {useUpdateEvent} from "@/hooks/firebase/useUpdateEvent";
import {editVisibleAtom, eventForEditAtom} from "@/components/pages/calendar/atoms";
import {useAtom} from "jotai";
interface IEditEventDialog {
event: CalendarEvent;
isVisible: boolean;
setIsVisible: (value: boolean) => void;
}
const EditEventDialog = (editEventProps: IEditEventDialog) => {
const [event, setEvent] = useState<CalendarEvent>(editEventProps.event);
const { mutateAsync: updateEvent } = useUpdateEvent();
const EditEventDialog = () => {
const [isVisible, setIsVisible] = useAtom(editVisibleAtom)
const [event, setEvent] = useAtom(eventForEditAtom)
useEffect(() => {
setEvent(editEventProps.event);
}, [editEventProps.isVisible]);
const {mutateAsync: updateEvent} = useUpdateEvent();
if (!event) return null
return (
<Dialog
bottom={true}
height={"90%"}
panDirection={PanningDirectionsEnum.DOWN}
onDismiss={() => editEventProps.setIsVisible(false)}
onDismiss={() => setIsVisible(false)}
containerStyle={{
borderRadius: 10,
backgroundColor: "white",
@ -46,7 +35,7 @@ const EditEventDialog = (editEventProps: IEditEventDialog) => {
paddingTop: 4,
margin: 0,
}}
visible={editEventProps.isVisible}
visible={isVisible}
>
<View row spread>
<Button
@ -54,13 +43,13 @@ const EditEventDialog = (editEventProps: IEditEventDialog) => {
style={styles.topBtn}
label="Cancel"
onPress={() => {
editEventProps.setIsVisible(false);
setIsVisible(false);
}}
/>
<View marginT-12>
<DropModalIcon
onPress={() => {
editEventProps.setIsVisible(false);
setIsVisible(false);
}}
/>
</View>
@ -71,7 +60,7 @@ const EditEventDialog = (editEventProps: IEditEventDialog) => {
onPress={() => {
try {
if (event.id) {
updateEvent(event).then(() => editEventProps.setIsVisible(false));
updateEvent(event).then(() => setIsVisible(false));
}
} catch (error) {
console.error(error);
@ -85,7 +74,7 @@ const EditEventDialog = (editEventProps: IEditEventDialog) => {
value={event.title}
onChangeText={(text) => {
setEvent((prevEvent) => ({
...prevEvent,
...prevEvent!,
title: text,
}));
}}
@ -94,11 +83,11 @@ const EditEventDialog = (editEventProps: IEditEventDialog) => {
marginT-15
marginL-30
/>
<View style={styles.divider} marginT-8 />
<View style={styles.divider} marginT-8/>
<View row spread marginB-10 marginL-30 centerV>
<View row>
<AntDesign name="clockcircleo" size={24} color="#919191" />
<AntDesign name="clockcircleo" size={24} color="#919191"/>
<Text text70 marginL-10>
All day
</Text>
@ -110,7 +99,7 @@ const EditEventDialog = (editEventProps: IEditEventDialog) => {
marginL-10
value={event.allDay}
onValueChange={(value) =>
setEvent((prev) => ({ ...prev, allDay: value }))
setEvent((prev) => ({...prev!, allDay: value}))
}
/>
</View>
@ -118,14 +107,14 @@ const EditEventDialog = (editEventProps: IEditEventDialog) => {
<View marginL-30 centerV>
<View row marginB-10 spread>
<View row centerV>
<Feather name="calendar" size={25} color="#919191" />
<Feather name="calendar" size={25} color="#919191"/>
<DateTimePicker
value={event.start}
text70
marginL-8
maximumDate={event.end}
onChange={(date) => {
setEvent((prev) => ({ ...prev, start: date }));
setEvent((prev) => ({...prev!, start: date}));
}}
/>
</View>
@ -133,11 +122,12 @@ const EditEventDialog = (editEventProps: IEditEventDialog) => {
text70
value={event.start}
onChange={(date) => {
setEvent((prev) => ({ ...prev, start: date }));
setEvent((prev) => ({...prev!, start: date}));
}}
maximumDate={event.end}
dateTimeFormatter={(date, mode) => date.toLocaleTimeString("en-us",
{ hour: "numeric",
dateTimeFormatter={(date) => date.toLocaleTimeString("en-us",
{
hour: "numeric",
minute: "numeric"
})}
mode="time"
@ -148,14 +138,14 @@ const EditEventDialog = (editEventProps: IEditEventDialog) => {
{!event.allDay && (
<View row marginB-10 spread>
<View row centerV>
<Feather name="calendar" size={25} color="#919191" />
<Feather name="calendar" size={25} color="#919191"/>
<DateTimePicker
value={event.end}
minimumDate={event.start}
text70
marginL-8
onChange={(date) => {
setEvent((prev) => ({ ...prev, end: date }));
setEvent((prev) => ({...prev!, end: date}));
}}
/>
</View>
@ -164,10 +154,11 @@ const EditEventDialog = (editEventProps: IEditEventDialog) => {
value={event.end}
minimumDate={event.start}
onChange={(date) => {
setEvent((prev) => ({ ...prev, end: date }));
setEvent((prev) => ({...prev!, end: date}));
}}
dateTimeFormatter={(date, mode) => date.toLocaleTimeString("en-us",
{ hour: "numeric",
dateTimeFormatter={(date) => date.toLocaleTimeString("en-us",
{
hour: "numeric",
minute: "numeric"
})}
mode="time"
@ -176,10 +167,10 @@ const EditEventDialog = (editEventProps: IEditEventDialog) => {
</View>
)}
</View>
<View style={styles.divider} />
<View style={styles.divider}/>
<View marginH-30 marginB-10 row centerV>
<Ionicons name="person-circle-outline" size={28} color="#919191" />
<Ionicons name="person-circle-outline" size={28} color="#919191"/>
<Text text70R marginL-10>
Assignees
</Text>
@ -187,7 +178,7 @@ const EditEventDialog = (editEventProps: IEditEventDialog) => {
size={ButtonSize.small}
paddingH-8
iconSource={() => (
<Ionicons name="add-outline" size={20} color="#ea156c" />
<Ionicons name="add-outline" size={20} color="#ea156c"/>
)}
style={{
marginLeft: "auto",
@ -220,10 +211,10 @@ const EditEventDialog = (editEventProps: IEditEventDialog) => {
}}
/>
</View>
<View style={styles.divider} />
<View style={styles.divider}/>
<View marginH-30 marginB-0 row spread centerV>
<View row centerV>
<ClockIcon />
<ClockIcon/>
<Text text70 marginL-10>
Reminder
</Text>
@ -233,7 +224,7 @@ const EditEventDialog = (editEventProps: IEditEventDialog) => {
size={ButtonSize.small}
paddingH-8
iconSource={() => (
<Ionicons name="add-outline" size={20} color="#ea156c" />
<Ionicons name="add-outline" size={20} color="#ea156c"/>
)}
style={{
marginLeft: "auto",
@ -247,10 +238,10 @@ const EditEventDialog = (editEventProps: IEditEventDialog) => {
/>
</View>
</View>
<View style={styles.divider} />
<View style={styles.divider}/>
<View marginH-30 marginB-0 row spread centerV>
<View row>
<LockIcon />
<LockIcon/>
<Text text70 marginL-10>
Mark as Private
</Text>
@ -262,15 +253,15 @@ const EditEventDialog = (editEventProps: IEditEventDialog) => {
marginL-10
value={event.private}
onValueChange={(value) =>
setEvent((prev) => ({ ...prev, private: value }))
setEvent((prev) => ({...prev!, private: value}))
}
/>
</View>
</View>
<View style={styles.divider} />
<View style={styles.divider}/>
<View marginH-30 marginB-0 row spread centerV>
<View row centerV>
<MenuIcon />
<MenuIcon/>
<Text text70 marginL-10>
Add Details
</Text>
@ -284,7 +275,7 @@ const EditEventDialog = (editEventProps: IEditEventDialog) => {
export default EditEventDialog;
const styles = StyleSheet.create({
divider: { height: 1, backgroundColor: "#e4e4e4", marginVertical: 15 },
divider: {height: 1, backgroundColor: "#e4e4e4", marginVertical: 15},
gradient: {
height: "25%",
position: "absolute",

View File

@ -0,0 +1,63 @@
import React, {memo} from 'react';
import {Calendar} from "react-native-big-calendar";
import {StyleSheet} from "react-native";
import {useGetEvents} from "@/hooks/firebase/useGetEvents";
import {useAtom, useAtomValue, useSetAtom} from "jotai";
import {
editVisibleAtom,
eventForEditAtom,
modeAtom,
selectedDateAtom,
selectedNewEventDateAtom
} from "@/components/pages/calendar/atoms";
interface EventCalendarProps {
calendarHeight: number;
}
export const EventCalendar: React.FC<EventCalendarProps> = memo(({calendarHeight}) => {
const {data: events} = useGetEvents();
const [selectedDate, setSelectedDate] = useAtom(selectedDateAtom)
const mode = useAtomValue(modeAtom)
const setEditVisible = useSetAtom(editVisibleAtom)
const setEventForEdit = useSetAtom(eventForEditAtom)
const setSelectedNewEndDate = useSetAtom(selectedNewEventDateAtom)
return (
<Calendar
bodyContainerStyle={styles.calHeader}
mode={mode}
events={events ?? []}
eventCellStyle={(event) => ({backgroundColor: event.eventColor})}
onPressEvent={(event) => {
setEditVisible(true);
setEventForEdit(event);
}}
height={calendarHeight}
activeDate={selectedDate}
date={selectedDate}
onPressCell={setSelectedNewEndDate}
headerContentStyle={mode === "day" ? styles.dayModeHeader : {}}
onSwipeEnd={(date) => {
setSelectedDate(date);
}}
/>
);
});
const styles = StyleSheet.create({
segmentslblStyle: {
fontSize: 12,
fontFamily: "Manrope_600SemiBold",
},
calHeader: {
borderWidth: 0,
},
dayModeHeader: {
alignSelf: "flex-start",
justifyContent: "space-between",
alignContent: "center",
width: 38,
right: 42,
},
});

View File

@ -0,0 +1,41 @@
import {View} from "react-native-ui-lib";
import React, {useRef, useState} from "react";
import {LayoutChangeEvent} from "react-native";
import CalendarViewSwitch from "@/components/pages/calendar/CalendarViewSwitch";
import {AddEventDialog} from "@/components/pages/calendar/AddEventDialog";
import EditEventDialog from "@/components/pages/calendar/EditEventDialog";
import {ManuallyAddEventModal} from "@/components/pages/calendar/ManuallyAddEventModal";
import {CalendarHeader} from "@/components/pages/calendar/CalendarHeader";
import {EventCalendar} from "@/components/pages/calendar/EventCalendar";
export const InnerCalendar = () => {
const [calendarHeight, setCalendarHeight] = useState(0);
const calendarContainerRef = useRef(null);
const onLayout = (event: LayoutChangeEvent) => {
const {height} = event.nativeEvent.layout;
setCalendarHeight(height);
};
return (
<>
<View
style={{flex: 1, backgroundColor: "#fff", borderRadius: 30}}
ref={calendarContainerRef}
onLayout={onLayout}
>
<CalendarHeader/>
{calendarHeight > 0 && (
<EventCalendar
calendarHeight={calendarHeight}
/>
)}
</View>
<CalendarViewSwitch/>
<AddEventDialog/>
<EditEventDialog/>
<ManuallyAddEventModal/>
</>
)
}

View File

@ -1,65 +1,55 @@
import {
Avatar,
Button,
ButtonSize,
Colors,
DateTimePicker,
LoaderScreen,
Modal,
Picker,
Switch,
Text,
TextField,
TouchableOpacity,
View,
} from "react-native-ui-lib";
import { ScrollView } from "react-native-gesture-handler";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { useState } from "react";
import {
AntDesign,
Feather,
Ionicons,
MaterialIcons,
} from "@expo/vector-icons";
import { PickerMultiValue } from "react-native-ui-lib/src/components/picker/types";
import { useAuthContext } from "@/contexts/AuthContext";
import { useCreateEvent } from "@/hooks/firebase/useCreateEvent";
import { EventData } from "@/hooks/firebase/types/eventData";
import { addHours, setDate } from "date-fns";
import {ScrollView} from "react-native-gesture-handler";
import {useSafeAreaInsets} from "react-native-safe-area-context";
import {useState} from "react";
import {AntDesign, Feather, Ionicons,} from "@expo/vector-icons";
import {PickerMultiValue} from "react-native-ui-lib/src/components/picker/types";
import {useCreateEvent} from "@/hooks/firebase/useCreateEvent";
import {EventData} from "@/hooks/firebase/types/eventData";
import {addHours} from "date-fns";
import DropModalIcon from "@/assets/svgs/DropModalIcon";
import { CalendarEvent, useCalendarContext } from "@/contexts/CalendarContext";
import { repeatOptions } from "@/contexts/ToDosContext";
import { ImageBackground, StyleSheet } from "react-native";
import {StyleSheet} from "react-native";
import ClockIcon from "@/assets/svgs/ClockIcon";
import LockIcon from "@/assets/svgs/LockIcon";
import MenuIcon from "@/assets/svgs/MenuIcon";
import CameraIcon from "@/assets/svgs/CameraIcon";
import AssigneesDisplay from "@/components/shared/AssigneesDisplay";
import {useAtom} from "jotai";
import {selectedNewEventDateAtom} from "@/components/pages/calendar/atoms";
const daysOfWeek = [
{ label: "Monday", value: "monday" },
{ label: "Tuesday", value: "tuesday" },
{ label: "Wednesday", value: "wednesday" },
{ label: "Thursday", value: "thursday" },
{ label: "Friday", value: "friday" },
{ label: "Saturday", value: "saturday" },
{ label: "Sunday", value: "sunday" },
{label: "Monday", value: "monday"},
{label: "Tuesday", value: "tuesday"},
{label: "Wednesday", value: "wednesday"},
{label: "Thursday", value: "thursday"},
{label: "Friday", value: "friday"},
{label: "Saturday", value: "saturday"},
{label: "Sunday", value: "sunday"},
];
export const ManuallyAddEventModal = ({
show,
close,
initialDate,
}: {
show: boolean;
close: () => void;
initialDate?: Date;
}) => {
const { addEvent } = useCalendarContext();
const { user } = useAuthContext();
export const ManuallyAddEventModal = () => {
const insets = useSafeAreaInsets();
const [selectedNewEventDate, setSelectedNewEndDate] = useAtom(selectedNewEventDateAtom)
const {show, close, initialDate} = {
show: !!selectedNewEventDate,
close: () => setSelectedNewEndDate(undefined),
initialDate: selectedNewEventDate
}
const [title, setTitle] = useState<string>("");
const [isAllDay, setIsAllDay] = useState(false);
@ -82,7 +72,9 @@ export const ManuallyAddEventModal = ({
const [repeatInterval, setRepeatInterval] = useState<PickerMultiValue>([]);
const { mutateAsync: createEvent, isLoading, isError } = useCreateEvent();
const {mutateAsync: createEvent, isLoading, isError} = useCreateEvent();
if (!selectedNewEventDate) return null;
const formatDateTime = (date?: Date | string) => {
if (!date) return undefined;
@ -166,7 +158,7 @@ export const ManuallyAddEventModal = ({
onRequestClose={close}
transparent={false}
>
<LoaderScreen message={"Saving event..."} color={Colors.grey40} />
<LoaderScreen message={"Saving event..."} color={Colors.grey40}/>
</Modal>
);
}
@ -207,7 +199,7 @@ export const ManuallyAddEventModal = ({
Cancel
</Text>
</TouchableOpacity>
<DropModalIcon onPress={close} />
<DropModalIcon onPress={close}/>
<TouchableOpacity onPress={handleSave}>
<Text
style={{
@ -230,16 +222,16 @@ export const ManuallyAddEventModal = ({
setTitle(text);
}}
placeholderTextColor="#2d2d30"
style={{ fontFamily: "Manrope_500Medium", fontSize: 22 }}
style={{fontFamily: "Manrope_500Medium", fontSize: 22}}
paddingT-15
paddingL-30
returnKeyType="next"
/>
<View style={styles.divider} marginT-8 />
<View style={styles.divider} marginT-8/>
<View marginL-30 centerV>
<View row spread marginB-10 centerV>
<View row>
<AntDesign name="clockcircleo" size={24} color="#919191" />
<AntDesign name="clockcircleo" size={24} color="#919191"/>
<Text
style={{
fontFamily: "PlusJakartaSans_500Medium",
@ -262,7 +254,7 @@ export const ManuallyAddEventModal = ({
</View>
<View row marginB-10 spread>
<View row centerV>
<Feather name="calendar" size={25} color="#919191" />
<Feather name="calendar" size={25} color="#919191"/>
<DateTimePicker
value={startDate}
onChange={(date) => {
@ -299,7 +291,7 @@ export const ManuallyAddEventModal = ({
{!isAllDay && (
<View row marginB-10 spread>
<View row centerV>
<Feather name="calendar" size={25} color="#919191" />
<Feather name="calendar" size={25} color="#919191"/>
<DateTimePicker
value={endDate}
minimumDate={startDate}
@ -336,12 +328,12 @@ export const ManuallyAddEventModal = ({
)}
</View>
<View style={styles.divider} />
<View style={styles.divider}/>
<View marginH-30 marginB-10 row centerV>
<Ionicons name="person-circle-outline" size={28} color="#919191" />
<Ionicons name="person-circle-outline" size={28} color="#919191"/>
<Text
style={{ fontFamily: "Manrope_600SemiBold", fontSize: 18 }}
style={{fontFamily: "Manrope_600SemiBold", fontSize: 18}}
marginL-10
>
Attendees
@ -350,7 +342,7 @@ export const ManuallyAddEventModal = ({
size={ButtonSize.small}
paddingH-8
iconSource={() => (
<Ionicons name="add-outline" size={20} color="#ea156c" />
<Ionicons name="add-outline" size={20} color="#ea156c"/>
)}
style={{
marginLeft: "auto",
@ -361,16 +353,16 @@ export const ManuallyAddEventModal = ({
}}
color="#ea156c"
label="Add"
labelStyle={{ fontFamily: "Manrope_600SemiBold", fontSize: 14 }}
labelStyle={{fontFamily: "Manrope_600SemiBold", fontSize: 14}}
/>
</View>
<View marginL-35>
<AssigneesDisplay />
<AssigneesDisplay/>
</View>
<View style={styles.divider} />
<View style={styles.divider}/>
<View marginH-30 marginB-0 row spread centerV>
<View row centerV>
<ClockIcon />
<ClockIcon/>
<Text
style={{
fontFamily: "Manrope_600SemiBold",
@ -386,7 +378,7 @@ export const ManuallyAddEventModal = ({
size={ButtonSize.small}
paddingH-8
iconSource={() => (
<Ionicons name="add-outline" size={20} color="#ea156c" />
<Ionicons name="add-outline" size={20} color="#ea156c"/>
)}
style={{
marginLeft: "auto",
@ -395,16 +387,16 @@ export const ManuallyAddEventModal = ({
borderColor: "#ea156c",
borderWidth: 1,
}}
labelStyle={{ fontFamily: "Manrope_600SemiBold", fontSize: 14 }}
labelStyle={{fontFamily: "Manrope_600SemiBold", fontSize: 14}}
color="#ea156c"
label="Set Reminder"
/>
</View>
</View>
<View style={styles.divider} />
<View style={styles.divider}/>
<View marginH-30 marginB-0 row spread centerV>
<View row>
<LockIcon />
<LockIcon/>
<Text
style={{
fontFamily: "PlusJakartaSans_500Medium",
@ -425,10 +417,10 @@ export const ManuallyAddEventModal = ({
/>
</View>
</View>
<View style={styles.divider} />
<View style={styles.divider}/>
<View marginH-30 marginB-0 row spread centerV>
<View row centerV>
<MenuIcon />
<MenuIcon/>
<Text
style={{
fontFamily: "PlusJakartaSans_500Medium",
@ -447,12 +439,12 @@ export const ManuallyAddEventModal = ({
marginB-15
label="Create event from image"
text70
style={{ height: 47 }}
labelStyle={{ fontFamily: "PlusJakartaSans_500Medium", fontSize: 15 }}
style={{height: 47}}
labelStyle={{fontFamily: "PlusJakartaSans_500Medium", fontSize: 15}}
backgroundColor="#05a8b6"
iconSource={() => (
<View marginR-5>
<CameraIcon color="white" />
<CameraIcon color="white"/>
</View>
)}
/>
@ -462,7 +454,7 @@ export const ManuallyAddEventModal = ({
};
const styles = StyleSheet.create({
divider: { height: 1, backgroundColor: "#e4e4e4", marginVertical: 15 },
divider: {height: 1, backgroundColor: "#e4e4e4", marginVertical: 15},
gradient: {
height: "25%",
position: "absolute",

View File

@ -0,0 +1,9 @@
import { atom } from 'jotai';
import {CalendarEvent} from "@/components/pages/calendar/interfaces";
export const editVisibleAtom = atom<boolean>(false);
export const eventForEditAtom = atom<CalendarEvent | undefined>(undefined);
export const isFamilyViewAtom = atom<boolean>(false);
export const modeAtom = atom<"week" | "month" | "day">("week");
export const selectedDateAtom = atom<Date>(new Date());
export const selectedNewEventDateAtom = atom<Date | undefined>(undefined);

View File

@ -0,0 +1,20 @@
export const modeMap = new Map([
[0, "day"],
[1, "week"],
[2, "month"],
]);
export const months = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];

View File

@ -0,0 +1,13 @@
export interface CalendarEvent {
id?: number | string; // Unique identifier for the event
user?: string;
title: string; // Event title or name
description?: string; // Optional description for the event
start: Date; // Start date and time of the event
end: Date; // End date and time of the event
location?: string; // Optional event location
allDay?: boolean; // Specifies if the event lasts all day
eventColor?: string; // Optional color to represent the event
participants?: string[]; // Optional list of participants or attendees
private?: boolean;
}

View File

@ -1,17 +1,17 @@
import {Button, ButtonSize, Dialog, Text, TextField, View} from "react-native-ui-lib";
import React, {useEffect, useState} from "react";
import React, {useState} from "react";
import {useSignIn} from "@/hooks/firebase/useSignIn";
import {StyleSheet} from "react-native";
import Toast from 'react-native-toast-message';
import {useLoginWithQrCode} from "@/hooks/firebase/useLoginWithQrCode";
import {Camera, CameraView} from 'expo-camera';
import {BarCodeScanner} from "expo-barcode-scanner";
const SignInPage = ({setTab}: { setTab: React.Dispatch<React.SetStateAction<"register" | "login" | "reset-password">> }) => {
const SignInPage = ({setTab}: {
setTab: React.Dispatch<React.SetStateAction<"register" | "login" | "reset-password">>
}) => {
const [email, setEmail] = useState<string>("");
const [password, setPassword] = useState<string>("");
const [hasPermission, setHasPermission] = useState<boolean | null>(null);
const [scanned, setScanned] = useState<boolean>(false);
const [showCameraDialog, setShowCameraDialog] = useState<boolean>(false);
const {mutateAsync: signIn, error, isError} = useSignIn();
@ -19,7 +19,7 @@ const SignInPage = ({setTab}: { setTab: React.Dispatch<React.SetStateAction<"re
const handleSignIn = async () => {
await signIn({email, password});
if(!isError) {
if (!isError) {
Toast.show({
type: "success",
text1: "Login successful!"
@ -33,10 +33,10 @@ const SignInPage = ({setTab}: { setTab: React.Dispatch<React.SetStateAction<"re
}
};
const handleQrCodeScanned = async ({ data }: { data: string }) => {
const handleQrCodeScanned = async ({data}: { data: string }) => {
setShowCameraDialog(false);
try {
await signInWithQrCode({ userId: data });
await signInWithQrCode({userId: data});
Toast.show({
type: "success",
text1: "Login successful with QR code!"
@ -51,9 +51,9 @@ const SignInPage = ({setTab}: { setTab: React.Dispatch<React.SetStateAction<"re
};
const getCameraPermissions = async (callback: () => void) => {
const { status } = await Camera.requestCameraPermissionsAsync();
const {status} = await Camera.requestCameraPermissionsAsync();
setHasPermission(status === 'granted');
if(status === 'granted') {
if (status === 'granted') {
callback();
}
};
@ -88,7 +88,7 @@ const SignInPage = ({setTab}: { setTab: React.Dispatch<React.SetStateAction<"re
style={{marginBottom: 20}}
backgroundColor="#fd1775"
/>
{isError && <Text center style={{marginBottom: 20}}>{`${error}`}</Text>}
{isError && <Text center style={{marginBottom: 20}}>{`${error?.toString()?.split("]")?.[1]}`}</Text>}
<View row centerH marginB-5 gap-5>
<Text text70>
@ -131,7 +131,7 @@ const SignInPage = ({setTab}: { setTab: React.Dispatch<React.SetStateAction<"re
bottom
width="100%"
height="70%"
containerStyle={{ padding: 0 }}
containerStyle={{padding: 0}}
>
{hasPermission === null ? (
<Text>Requesting camera permissions...</Text>
@ -139,7 +139,7 @@ const SignInPage = ({setTab}: { setTab: React.Dispatch<React.SetStateAction<"re
<Text>No access to camera</Text>
) : (
<CameraView
style={{ flex: 1 }}
style={{flex: 1}}
onBarcodeScanned={handleQrCodeScanned}
barcodeScannerSettings={{
barcodeTypes: ["qr"],
@ -150,7 +150,7 @@ const SignInPage = ({setTab}: { setTab: React.Dispatch<React.SetStateAction<"re
label="Cancel"
onPress={() => setShowCameraDialog(false)}
backgroundColor="#fd1775"
style={{ margin: 10 }}
style={{margin: 10}}
/>
</Dialog>
</View>

View File

@ -1,11 +1,10 @@
import { View, Text, Button } from "react-native-ui-lib";
import React, { useState } from "react";
import { StyleSheet } from "react-native";
import { Entypo, Ionicons, Octicons } from "@expo/vector-icons";
import {Button, Text, View} from "react-native-ui-lib";
import React, {useState} from "react";
import {StyleSheet} from "react-native";
import {Octicons} from "@expo/vector-icons";
import CalendarSettingsPage from "./CalendarSettingsPage";
import ChoreRewardSettings from "./ChoreRewardSettings";
import UserSettings from "./UserSettings";
import { AuthContextProvider } from "@/contexts/AuthContext";
import ProfileIcon from "@/assets/svgs/ProfileIcon";
import CalendarIcon from "@/assets/svgs/CalendarIcon";
import PrivacyPolicyIcon from "@/assets/svgs/PrivacyPolicyIcon";
@ -18,6 +17,7 @@ const pageIndex = {
chore: 3,
policy: 4,
};
const SettingsPage = () => {
const [selectedPage, setSelectedPage] = useState<number>(0);
return (
@ -29,11 +29,11 @@ const SettingsPage = () => {
style={styles.mainBtn}
children={
<View row centerV width={"100%"}>
<ProfileIcon style={{ marginRight: 10 }} color="#07b9c8" />
<ProfileIcon style={{marginRight: 10}} color="#07b9c8"/>
<Text style={styles.label} color="#07b8c7">
Manage My Profile
</Text>
<ArrowRightIcon style={{ marginLeft: "auto" }} />
<ArrowRightIcon style={{marginLeft: "auto"}}/>
</View>
}
onPress={() => setSelectedPage(pageIndex.user)}
@ -43,11 +43,11 @@ const SettingsPage = () => {
style={styles.mainBtn}
children={
<View row centerV width={"100%"}>
<CalendarIcon style={{ marginRight: 10 }} />
<CalendarIcon style={{marginRight: 10}}/>
<Text style={styles.label} color="#fd1775">
Calendar Settings
</Text>
<ArrowRightIcon style={{ marginLeft: "auto" }} />
<ArrowRightIcon style={{marginLeft: "auto"}}/>
</View>
}
onPress={() => {
@ -63,12 +63,12 @@ const SettingsPage = () => {
name="gear"
size={24}
color="#ff9900"
style={{ marginRight: 10 }}
style={{marginRight: 10}}
/>
<Text style={styles.label} color="#ff9900">
To-Do Reward Settings
</Text>
<ArrowRightIcon style={{ marginLeft: "auto" }} />
<ArrowRightIcon style={{marginLeft: "auto"}}/>
</View>
}
onPress={() => setSelectedPage(pageIndex.chore)}
@ -78,24 +78,24 @@ const SettingsPage = () => {
style={styles.mainBtn}
children={
<View row centerV width={"100%"}>
<PrivacyPolicyIcon style={{ marginRight: 10 }} />
<PrivacyPolicyIcon style={{marginRight: 10}}/>
<Text style={styles.label} color="#6c645b">
Cally Privacy Policy
</Text>
<ArrowRightIcon style={{ marginLeft: "auto" }} />
<ArrowRightIcon style={{marginLeft: "auto"}}/>
</View>
}
/>
</View>
)}
{selectedPage == pageIndex.calendar && (
<CalendarSettingsPage setSelectedPage={setSelectedPage} />
<CalendarSettingsPage setSelectedPage={setSelectedPage}/>
)}
{selectedPage == pageIndex.chore && (
<ChoreRewardSettings setSelectedPage={setSelectedPage} />
<ChoreRewardSettings setSelectedPage={setSelectedPage}/>
)}
{selectedPage == pageIndex.user && (
<UserSettings setSelectedPage={setSelectedPage} />
<UserSettings setSelectedPage={setSelectedPage}/>
)}
</View>
);

View File

@ -99,6 +99,7 @@ export const AuthContextProvider: FC<{ children: ReactNode }> = ({children}) =>
const onAuthStateChangedHandler = async (authUser: FirebaseAuthTypes.User | null) => {
setUser(authUser);
if (authUser) {
await refreshProfileData(authUser);
const pushToken = await registerForPushNotificationsAsync();

View File

@ -1,194 +0,0 @@
// CalendarContext.tsx
import React, { createContext, useContext, useState, ReactNode } from "react";
// Define the CalendarEvent interface
export interface CalendarEvent {
id?: number | string; // Unique identifier for the event
user?: string;
title: string; // Event title or name
description?: string; // Optional description for the event
start: Date; // Start date and time of the event
end: Date; // End date and time of the event
location?: string; // Optional event location
allDay?: boolean; // Specifies if the event lasts all day
color?: string; // Optional color to represent the event
participants?: string[]; // Optional list of participants or attendees
private?: boolean;
}
// Define the context type
interface CalendarContextType {
events: CalendarEvent[];
familyEvents: CalendarEvent[];
addEvent: (event: CalendarEvent) => void; // Function to add an event
removeEvent: (id: number) => void; // Function to remove an event by ID
updateEvent: (changes: Partial<CalendarEvent>, id?: number) => void;
}
// Create the CalendarContext
const CalendarContext = createContext<CalendarContextType | undefined>(
undefined
);
// Create a provider component
export const CalendarProvider: React.FC<{ children: ReactNode }> = ({
children,
}) => {
const [events, setEvents] = useState<CalendarEvent[]>([
{
id: 1,
title: "Team Meeting",
description: "Discuss project milestones and deadlines.",
start: new Date("2024-09-15T10:00:00"),
end: new Date("2024-09-15T11:00:00"),
location: "Office Conference Room",
allDay: false,
color: "#FF5733",
participants: ["Alice", "Bob", "Charlie"],
},
{
id: 2,
title: "Doctor's Appointment",
description: "Annual check-up with Dr. Smith.",
start: new Date("2024-09-20T14:30:00"),
end: new Date("2024-09-20T15:30:00"),
location: "Health Clinic",
allDay: false,
color: "#33FF57",
participants: ["You"],
},
{
id: 3,
title: "Birthday Party",
description: "Celebrating Sarah's 30th birthday.",
start: new Date("2024-09-25T18:00:00"),
end: new Date("2024-09-25T21:00:00"),
location: "Sarah's House",
allDay: false,
color: "#3357FF",
participants: ["You", "Sarah", "Tom", "Lily"],
},
{
id: 4,
title: "Project Deadline",
description: "Final submission for the project.",
start: new Date("2024-10-01T00:00:00"),
end: new Date("2024-10-01T23:59:00"),
location: "Online",
allDay: false,
color: "#FF33A1",
participants: ["You"],
},
{
id: 5,
title: "Halloween Costume Party",
description: "Join us for a spooky night of fun!",
start: new Date("2024-10-31T19:00:00"),
end: new Date("2024-10-31T23:00:00"),
location: "Downtown Club",
allDay: false,
color: "#FFB733",
participants: ["You", "Friends"],
},
]);
const [familyEvents, setFamilyEvents] = useState<CalendarEvent[]>([
{
id: 1,
user: "jakesId",
title: "Team Meeting",
description: "Discuss project milestones and deadlines.",
start: new Date("2024-09-10T10:00:00"),
end: new Date("2024-09-10T11:00:00"),
location: "Office Conference Room",
allDay: false,
color: "#FF5733",
participants: ["Alice", "Bob", "Charlie"],
},
{
id: 2,
user: "mikesId",
title: "Doctor's Appointment",
description: "Annual check-up with Dr. Smith.",
start: new Date("2024-09-21T14:30:00"),
end: new Date("2024-09-21T15:30:00"),
location: "Health Clinic",
allDay: false,
color: "#33FF57",
participants: ["You"],
},
{
id: 3,
user: "jakesId",
title: "Birthday Party",
description: "Celebrating Sarah's 30th birthday.",
start: new Date("2024-09-5T18:00:00"),
end: new Date("2024-09-5T21:00:00"),
location: "Sarah's House",
allDay: false,
color: "#3357FF",
participants: ["You", "Sarah", "Tom", "Lily"],
},
{
id: 4,
user: "davidsId",
title: "Project Deadline",
description: "Final submission for the project.",
start: new Date("2024-10-03T00:00:00"),
end: new Date("2024-10-03T23:59:00"),
location: "Online",
allDay: false,
color: "#FF33A1",
participants: ["You"],
},
{
id: 5,
user: "jakesId",
title: "Halloween Costume Party",
description: "Join us for a spooky night of fun!",
start: new Date("2024-10-02T19:00:00"),
end: new Date("2024-10-02T23:00:00"),
location: "Downtown Club",
allDay: false,
color: "#FFB733",
participants: ["You", "Friends"],
},
]);
// Function to add an event
const addEvent = (event: CalendarEvent) => {
event.id = events.length + 1;
setEvents((prevEvents) => [...prevEvents, event]);
};
// Function to remove an event by ID
const removeEvent = (id: number) => {
setEvents((prevEvents) => prevEvents.filter((event) => event.id !== id));
};
// Function to update an event
const updateEvent = ( changes: Partial<CalendarEvent>, id?: number) => {
setEvents((prevEvents) =>
prevEvents.map((event) =>
event.id === id ? { ...event, ...changes } : event
)
);
};
return (
<CalendarContext.Provider
value={{ events, addEvent, removeEvent, updateEvent, familyEvents }}
>
{children}
</CalendarContext.Provider>
);
};
// Custom hook to use the CalendarContext
export const useCalendarContext = () => {
const context = useContext(CalendarContext);
if (!context) {
throw new Error("useCalendar must be used within a CalendarProvider");
}
return context;
};

View File

@ -10,5 +10,5 @@ export interface EventData {
surpriseEvent?: boolean,
notes?: string,
reminders?: string[]
id?: string,
id?: string | number,
}

View File

@ -2,9 +2,12 @@ import {useQuery} from "react-query";
import firestore from "@react-native-firebase/firestore";
import {useAuthContext} from "@/contexts/AuthContext";
import {colorMap} from "@/contexts/SettingsContext";
import {useAtomValue} from "jotai";
import {isFamilyViewAtom} from "@/components/pages/calendar/atoms";
export const useGetEvents = (isFamilyView: boolean) => {
export const useGetEvents = () => {
const { user, profileData } = useAuthContext();
const isFamilyView = useAtomValue(isFamilyViewAtom)
return useQuery({
queryKey: ["events", user?.uid, isFamilyView],

View File

@ -1,10 +1,8 @@
import {useAuthContext} from "@/contexts/AuthContext";
import {useMutation, useQueryClient} from "react-query";
import firestore from "@react-native-firebase/firestore";
import {EventData} from "@/hooks/firebase/types/eventData";
export const useUpdateEvent = () => {
const {user: currentUser} = useAuthContext()
const queryClients = useQueryClient()
return useMutation({
@ -13,7 +11,7 @@ export const useUpdateEvent = () => {
try {
await firestore()
.collection("Events")
.doc(eventData.id)
.doc(`${eventData.id}`)
.update(eventData);
} catch (e) {
console.error(e)

View File

@ -441,7 +441,7 @@
);
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = com.cally.app;
PRODUCT_NAME = "CallyFamilyPlanner";
PRODUCT_NAME = CallyFamilyPlanner;
SWIFT_OBJC_BRIDGING_HEADER = "cally/cally-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@ -472,7 +472,7 @@
);
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.cally.app;
PRODUCT_NAME = "CallyFamilyPlanner";
PRODUCT_NAME = CallyFamilyPlanner;
SWIFT_OBJC_BRIDGING_HEADER = "cally/cally-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";