This commit is contained in:
Milan Paunovic
2024-12-10 20:05:15 +01:00
parent 233427bf38
commit 35608e350f
2 changed files with 792 additions and 740 deletions

View File

@ -10,6 +10,7 @@ import NavToDosIcon from "@/assets/svgs/NavToDosIcon";
import {useSetAtom} from "jotai"; import {useSetAtom} from "jotai";
import {selectedNewEventDateAtom} from "@/components/pages/calendar/atoms"; import {selectedNewEventDateAtom} from "@/components/pages/calendar/atoms";
import PlusIcon from "@/assets/svgs/PlusIcon"; import PlusIcon from "@/assets/svgs/PlusIcon";
import {addMinutes, roundToNearestMinutes} from "date-fns";
export const AddEventDialog = () => { export const AddEventDialog = () => {
const [show, setShow] = useState(false); const [show, setShow] = useState(false);
@ -20,7 +21,8 @@ export const AddEventDialog = () => {
const handleOpenManualInputModal = () => { const handleOpenManualInputModal = () => {
setShow(false); setShow(false);
setTimeout(() => { setTimeout(() => {
setSelectedNewEndDate(new Date()); const roundedDate = roundToNearestMinutes(new Date(), {nearestTo: 5});
setSelectedNewEndDate(roundedDate);
}, 500); }, 500);
}; };
@ -50,7 +52,7 @@ export const AddEventDialog = () => {
onPress={() => setShow(true)} onPress={() => setShow(true)}
> >
<View row centerV centerH> <View row centerV centerH>
<PlusIcon /> <PlusIcon/>
<Text white style={{fontSize: 16, fontFamily: 'Manrope_600SemiBold', marginLeft: 5}}> <Text white style={{fontSize: 16, fontFamily: 'Manrope_600SemiBold', marginLeft: 5}}>
New New
</Text> </Text>

View File

@ -2,7 +2,7 @@ import {
Button, Button,
ButtonSize, ButtonSize,
Colors, Colors,
DateTimePicker, DateTimePicker, Dialog,
LoaderScreen, LoaderScreen,
Modal, Modal,
Picker, Picker,
@ -14,47 +14,48 @@ import {
TouchableOpacity, TouchableOpacity,
View, View,
} from "react-native-ui-lib"; } from "react-native-ui-lib";
import { ScrollView } from "react-native-gesture-handler"; import {ScrollView} from "react-native-gesture-handler";
import { useSafeAreaInsets } from "react-native-safe-area-context"; import {useSafeAreaInsets} from "react-native-safe-area-context";
import { useEffect, useRef, useState } from "react"; import {useEffect, useRef, useState} from "react";
import { AntDesign, Feather, Ionicons } from "@expo/vector-icons"; import {AntDesign, Feather, Ionicons} from "@expo/vector-icons";
import { PickerMultiValue } from "react-native-ui-lib/src/components/picker/types"; import {PickerMultiValue} from "react-native-ui-lib/src/components/picker/types";
import { useCreateEvent } from "@/hooks/firebase/useCreateEvent"; import {useCreateEvent} from "@/hooks/firebase/useCreateEvent";
import { EventData } from "@/hooks/firebase/types/eventData"; import {EventData} from "@/hooks/firebase/types/eventData";
import DropModalIcon from "@/assets/svgs/DropModalIcon"; import DropModalIcon from "@/assets/svgs/DropModalIcon";
import { Alert, StyleSheet } from "react-native"; import {Alert, StyleSheet} from "react-native";
import ClockIcon from "@/assets/svgs/ClockIcon"; import ClockIcon from "@/assets/svgs/ClockIcon";
import LockIcon from "@/assets/svgs/LockIcon"; import LockIcon from "@/assets/svgs/LockIcon";
import MenuIcon from "@/assets/svgs/MenuIcon"; import MenuIcon from "@/assets/svgs/MenuIcon";
import CameraIcon from "@/assets/svgs/CameraIcon"; import CameraIcon from "@/assets/svgs/CameraIcon";
import AssigneesDisplay from "@/components/shared/AssigneesDisplay"; import AssigneesDisplay from "@/components/shared/AssigneesDisplay";
import { useAtom } from "jotai"; import {useAtom} from "jotai";
import { import {
eventForEditAtom, eventForEditAtom,
selectedNewEventDateAtom, selectedNewEventDateAtom,
isAllDayAtom, isAllDayAtom,
} from "@/components/pages/calendar/atoms"; } from "@/components/pages/calendar/atoms";
import { useGetFamilyMembers } from "@/hooks/firebase/useGetFamilyMembers"; import {useGetFamilyMembers} from "@/hooks/firebase/useGetFamilyMembers";
import BinIcon from "@/assets/svgs/BinIcon"; import BinIcon from "@/assets/svgs/BinIcon";
import DeleteEventDialog from "./DeleteEventDialog"; import DeleteEventDialog from "./DeleteEventDialog";
import { useDeleteEvent } from "@/hooks/firebase/useDeleteEvent"; import {useDeleteEvent} from "@/hooks/firebase/useDeleteEvent";
import AddPersonIcon from "@/assets/svgs/AddPersonIcon"; import AddPersonIcon from "@/assets/svgs/AddPersonIcon";
import { addHours, startOfHour, startOfMinute } from "date-fns"; import {addHours, format, startOfHour, startOfMinute} from "date-fns";
import { useAuthContext } from "@/contexts/AuthContext"; import {useAuthContext} from "@/contexts/AuthContext";
import {Calendar} from "react-native-calendars";
const daysOfWeek = [ const daysOfWeek = [
{ label: "Monday", value: "monday" }, {label: "Monday", value: "monday"},
{ label: "Tuesday", value: "tuesday" }, {label: "Tuesday", value: "tuesday"},
{ label: "Wednesday", value: "wednesday" }, {label: "Wednesday", value: "wednesday"},
{ label: "Thursday", value: "thursday" }, {label: "Thursday", value: "thursday"},
{ label: "Friday", value: "friday" }, {label: "Friday", value: "friday"},
{ label: "Saturday", value: "saturday" }, {label: "Saturday", value: "saturday"},
{ label: "Sunday", value: "sunday" }, {label: "Sunday", value: "sunday"},
]; ];
export const ManuallyAddEventModal = () => { export const ManuallyAddEventModal = () => {
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const { user } = useAuthContext(); const {user} = useAuthContext();
const [selectedNewEventDate, setSelectedNewEndDate] = useAtom( const [selectedNewEventDate, setSelectedNewEndDate] = useAtom(
selectedNewEventDateAtom selectedNewEventDateAtom
@ -62,9 +63,9 @@ export const ManuallyAddEventModal = () => {
const [editEvent, setEditEvent] = useAtom(eventForEditAtom); const [editEvent, setEditEvent] = useAtom(eventForEditAtom);
const [allDayAtom, setAllDayAtom] = useAtom(isAllDayAtom); const [allDayAtom, setAllDayAtom] = useAtom(isAllDayAtom);
const [deleteModalVisible, setDeleteModalVisible] = useState<boolean>(false); const [deleteModalVisible, setDeleteModalVisible] = useState<boolean>(false);
const { mutateAsync: deleteEvent, isLoading: isDeleting } = useDeleteEvent(); const {mutateAsync: deleteEvent, isLoading: isDeleting} = useDeleteEvent();
const { show, close, initialDate } = { const {show, close, initialDate} = {
show: !!selectedNewEventDate || !!editEvent, show: !!selectedNewEventDate || !!editEvent,
close: () => { close: () => {
setDeleteModalVisible(false); setDeleteModalVisible(false);
@ -84,6 +85,8 @@ export const ManuallyAddEventModal = () => {
editEvent?.private || false editEvent?.private || false
); );
const [location, setLocation] = useState(editEvent?.location ?? ""); const [location, setLocation] = useState(editEvent?.location ?? "");
const [showStartDatePicker, setShowStartDatePicker] = useState(false);
const [showEndDatePicker, setShowEndDatePicker] = useState(false);
useEffect(() => { useEffect(() => {
if (allDayAtom === true) setIsAllDay(true); if (allDayAtom === true) setIsAllDay(true);
@ -126,7 +129,7 @@ export const ManuallyAddEventModal = () => {
isLoading: isAdding, isLoading: isAdding,
isError, isError,
} = useCreateEvent(); } = useCreateEvent();
const { data: members } = useGetFamilyMembers(true); const {data: members} = useGetFamilyMembers(true);
const titleRef = useRef<TextFieldRef>(null); const titleRef = useRef<TextFieldRef>(null);
const [creator, setCreator] = useState(""); const [creator, setCreator] = useState("");
@ -193,7 +196,7 @@ export const ManuallyAddEventModal = () => {
}; };
const handleDeleteEvent = async () => { const handleDeleteEvent = async () => {
await deleteEvent({ eventId: `${editEvent?.id}` }); await deleteEvent({eventId: `${editEvent?.id}`});
close(); close();
}; };
@ -305,7 +308,163 @@ export const ManuallyAddEventModal = () => {
); );
} }
const renderCalendarPicker = (
isStart: boolean,
visible: boolean,
onDismiss: () => void
) => {
const currentDate = isStart ? startDate : endDate;
const setDate = isStart ? setStartDate : setEndDate;
return ( return (
<Dialog
visible={visible}
onDismiss={onDismiss}
panDirection={"down"}
width="100%"
bottom
containerStyle={{
backgroundColor: Colors.white,
borderTopLeftRadius: 12,
borderTopRightRadius: 12,
paddingBottom: insets.bottom,
}}
>
<View padding-20>
<View row spread marginB-20>
<Text text70 style={{fontFamily: "Manrope_600SemiBold"}}>
Select Date
</Text>
<TouchableOpacity onPress={onDismiss}>
<Text style={{color: Colors.$textPrimary}}>Done</Text>
</TouchableOpacity>
</View>
<Calendar
firstDay={1}
current={format(currentDate, 'yyyy-MM-dd')}
minDate={isStart ? undefined : format(startDate, 'yyyy-MM-dd')}
markedDates={{
[format(currentDate, 'yyyy-MM-dd')]: {
selected: true,
selectedColor: '#ea156c'
}
}}
onDayPress={(day) => {
const newDate = new Date(day.timestamp);
newDate.setHours(currentDate.getHours());
newDate.setMinutes(currentDate.getMinutes());
setDate(newDate);
onDismiss();
}}
theme={{
selectedDayBackgroundColor: '#ea156c',
selectedDayTextColor: '#ffffff',
todayTextColor: '#ea156c',
arrowColor: '#ea156c',
}}
enableSwipeMonths={true}
/>
</View>
</Dialog>
);
};
const renderDateSection = () => (
<View marginL-30 centerV>
<View row spread marginB-10 centerV>
<View row>
<AntDesign name="clockcircleo" size={24} color="#919191"/>
<Text
style={{
fontFamily: "PlusJakartaSans_500Medium",
fontSize: 16,
}}
marginL-10
>
All day
</Text>
</View>
<View right marginR-30>
<Switch
onColor={"#ea156c"}
offColor={"#e1e1e2"}
marginL-10
value={isAllDay}
onValueChange={(value) => setIsAllDay(value)}
/>
</View>
</View>
<TouchableOpacity
onPress={() => setShowStartDatePicker(true)}
style={styles.dateButton}
>
<View row centerV spread paddingR-30>
<View row centerV>
<Feather name="calendar" size={25} color="#919191"/>
<Text marginL-8 style={styles.dateText}>
{format(startDate, 'MMM d, yyyy')}
</Text>
</View>
{!isAllDay && (
<DateTimePicker
value={startTime}
onChange={(time) => {
if (time <= endTime) {
setStartTime(time);
}
}}
minuteInterval={5}
mode="time"
timeFormat="HH:mm"
style={styles.timePicker}
/>
)}
</View>
</TouchableOpacity>
{!isAllDay && (
<TouchableOpacity
onPress={() => setShowEndDatePicker(true)}
style={styles.dateButton}
>
<View row centerV spread paddingR-30>
<View row centerV>
<Feather name="calendar" size={25} color="#919191"/>
<Text marginL-8 style={styles.dateText}>
{format(endDate, 'MMM d, yyyy')}
</Text>
</View>
<DateTimePicker
value={endTime}
onChange={(time) => {
setEndTime(time);
if (
endDate.getDate() === startDate.getDate() &&
time.getHours() < startTime.getHours()
) {
const newEndDate = new Date(endDate);
newEndDate.setDate(newEndDate.getDate() + 1);
setEndDate(newEndDate);
}
}}
minuteInterval={5}
mode="time"
timeFormat="HH:mm"
style={styles.timePicker}
/>
</View>
</TouchableOpacity>
)}
{renderCalendarPicker(true, showStartDatePicker, () => setShowStartDatePicker(false))}
{renderCalendarPicker(false, showEndDatePicker, () => setShowEndDatePicker(false))}
</View>)
return (
<>
<Modal <Modal
visible={show} visible={show}
animationType="slide" animationType="slide"
@ -375,7 +534,7 @@ export const ManuallyAddEventModal = () => {
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
<View row center> <View row center>
<DropModalIcon onPress={close} /> <DropModalIcon onPress={close}/>
</View> </View>
<View flexS row gap-10> <View flexS row gap-10>
<TouchableOpacity onPress={handleSave}> <TouchableOpacity onPress={handleSave}>
@ -394,14 +553,14 @@ export const ManuallyAddEventModal = () => {
<Button <Button
style={styles.topBtn} style={styles.topBtn}
marginL-5 marginL-5
iconSource={() => <BinIcon />} iconSource={() => <BinIcon/>}
onPress={showDeleteEventModal} onPress={showDeleteEventModal}
/> />
)} )}
</View> </View>
</View> </View>
{/*)}*/} {/*)}*/}
<ScrollView style={{ minHeight: "85%" }}> <ScrollView style={{minHeight: "85%"}}>
<TextField <TextField
placeholder="Add event title" placeholder="Add event title"
ref={titleRef} ref={titleRef}
@ -410,148 +569,38 @@ export const ManuallyAddEventModal = () => {
setTitle(text); setTitle(text);
}} }}
placeholderTextColor="#2d2d30" placeholderTextColor="#2d2d30"
style={{ fontFamily: "Manrope_500Medium", fontSize: 22 }} style={{fontFamily: "Manrope_500Medium", fontSize: 22}}
paddingT-15 paddingT-15
paddingL-30 paddingL-30
returnKeyType="next" returnKeyType="next"
/> />
<View style={styles.divider} marginT-8 /> <View style={styles.divider} marginT-8/>
<View marginL-30 centerV> {renderDateSection()}
<View row spread marginB-10 centerV>
<View row>
<AntDesign name="clockcircleo" size={24} color="#919191" />
<Text
style={{
fontFamily: "PlusJakartaSans_500Medium",
fontSize: 16,
}}
marginL-10
>
All day
</Text>
</View>
<View right marginR-30>
<Switch
onColor={"#ea156c"}
offColor={"#e1e1e2"}
marginL-10
value={isAllDay}
onValueChange={(value) => setIsAllDay(value)}
/>
</View>
</View>
<View row marginB-10 spread>
<View row centerV>
<Feather name="calendar" size={25} color="#919191" />
<DateTimePicker
value={startDate}
onChange={(date) => {
setStartDate(date);
}}
//maximumDate={endDate}
style={{
fontFamily: "PlusJakartaSans_500Medium",
fontSize: 16,
}}
marginL-8
/>
</View>
<DateTimePicker
value={startTime}
onChange={(time) => {
if (time <= endTime) {
setStartTime(time);
}
}}
minuteInterval={5}
dateTimeFormatter={(date, mode) =>
date.toLocaleTimeString("en-us", {
hour: "numeric",
minute: "numeric",
})
}
mode="time"
style={{
fontFamily: "PlusJakartaSans_500Medium",
fontSize: 16,
}}
marginR-30
/>
</View>
{!isAllDay && ( <View style={styles.divider}/>
<View row marginB-10 spread>
<View row centerV>
<Feather name="calendar" size={25} color="#919191" />
<DateTimePicker
value={endDate}
minimumDate={startDate}
text70
marginL-8
onChange={(date) => {
setEndDate(date);
}}
style={{
fontFamily: "PlusJakartaSans_500Medium",
fontSize: 16,
}}
/>
</View>
<DateTimePicker
value={endTime}
onChange={(time) => {
setEndTime(time);
if (
endDate.getDate() === startDate.getDate() &&
time.getHours() < startTime.getHours()
) {
const newEndDate = new Date(endDate);
newEndDate.setDate(newEndDate.getDate() + 1);
setEndDate(newEndDate);
}
}}
minuteInterval={5}
dateTimeFormatter={(date, mode) =>
date.toLocaleTimeString("en-us", {
hour: "numeric",
minute: "numeric",
})
}
mode="time"
style={{
fontFamily: "PlusJakartaSans_500Medium",
fontSize: 16,
}}
marginR-30
/>
</View>
)}
</View>
<View style={styles.divider} />
<View marginH-30 marginB-10 row centerV> <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 <Text
style={{ fontFamily: "Manrope_600SemiBold", fontSize: 16 }} style={{fontFamily: "Manrope_600SemiBold", fontSize: 16}}
marginL-10 marginL-10
> >
Attendees Attendees
</Text> </Text>
<View flex-1 /> <View flex-1/>
<Picker <Picker
value={selectedAttendees} value={selectedAttendees}
onChange={(value) => onChange={(value) =>
setSelectedAttendees((value as string[]) ?? []) setSelectedAttendees((value as string[]) ?? [])
} }
style={{ marginLeft: "auto" }} style={{marginLeft: "auto"}}
mode={PickerModes.MULTI} mode={PickerModes.MULTI}
renderInput={() => ( renderInput={() => (
<Button <Button
size={ButtonSize.small} size={ButtonSize.small}
paddingH-8 paddingH-8
iconSource={() => ( iconSource={() => (
<Ionicons name="add-outline" size={20} color="#ea156c" /> <Ionicons name="add-outline" size={20} color="#ea156c"/>
)} )}
style={{ style={{
marginLeft: "auto", marginLeft: "auto",
@ -586,10 +635,10 @@ export const ManuallyAddEventModal = () => {
/> />
</View> </View>
<View style={styles.divider} /> <View style={styles.divider}/>
<View marginH-30 marginB-0 row spread centerV> <View marginH-30 marginB-0 row spread centerV>
<View row centerV> <View row centerV>
<ClockIcon /> <ClockIcon/>
<Text <Text
style={{ style={{
fontFamily: "Manrope_600SemiBold", fontFamily: "Manrope_600SemiBold",
@ -605,7 +654,7 @@ export const ManuallyAddEventModal = () => {
size={ButtonSize.small} size={ButtonSize.small}
paddingH-8 paddingH-8
iconSource={() => ( iconSource={() => (
<Ionicons name="add-outline" size={20} color="#ea156c" /> <Ionicons name="add-outline" size={20} color="#ea156c"/>
)} )}
style={{ style={{
marginLeft: "auto", marginLeft: "auto",
@ -614,16 +663,16 @@ export const ManuallyAddEventModal = () => {
borderColor: "#ea156c", borderColor: "#ea156c",
borderWidth: 1, borderWidth: 1,
}} }}
labelStyle={{ fontFamily: "Manrope_600SemiBold", fontSize: 14 }} labelStyle={{fontFamily: "Manrope_600SemiBold", fontSize: 14}}
color="#ea156c" color="#ea156c"
label="Set Reminder" label="Set Reminder"
/> />
</View> </View>
</View> </View>
<View style={styles.divider} /> <View style={styles.divider}/>
<View marginH-30 marginB-0 row spread centerV> <View marginH-30 marginB-0 row spread centerV>
<View row center> <View row center>
<LockIcon /> <LockIcon/>
<Text <Text
style={{ style={{
fontFamily: "PlusJakartaSans_500Medium", fontFamily: "PlusJakartaSans_500Medium",
@ -645,10 +694,10 @@ export const ManuallyAddEventModal = () => {
/> />
</View> </View>
</View> </View>
<View style={styles.divider} /> <View style={styles.divider}/>
<View marginH-28 marginB-0 centerV flex-1> <View marginH-28 marginB-0 centerV flex-1>
<View row centerV style={{ flexGrow: 1 }}> <View row centerV style={{flexGrow: 1}}>
<Ionicons name="location-outline" size={25} color={"#919191"} /> <Ionicons name="location-outline" size={25} color={"#919191"}/>
<TextField <TextField
placeholder="Location" placeholder="Location"
value={location} value={location}
@ -668,10 +717,10 @@ export const ManuallyAddEventModal = () => {
</View> </View>
{editEvent && ( {editEvent && (
<> <>
<View style={styles.divider} /> <View style={styles.divider}/>
<View marginH-32 marginB-0 centerV flex-1> <View marginH-32 marginB-0 centerV flex-1>
<View row centerV style={{ flexGrow: 1 }}> <View row centerV style={{flexGrow: 1}}>
<AddPersonIcon /> <AddPersonIcon/>
<TextField <TextField
editable={false} editable={false}
value={creator} value={creator}
@ -689,11 +738,11 @@ export const ManuallyAddEventModal = () => {
</View> </View>
</> </>
)} )}
<View style={styles.divider} /> <View style={styles.divider}/>
<View marginH-30 marginB-0 spread centerV flex-1> <View marginH-30 marginB-0 spread centerV flex-1>
<TouchableOpacity onPress={() => detailsRef?.current?.focus()}> <TouchableOpacity onPress={() => detailsRef?.current?.focus()}>
<View row centerV> <View row centerV>
<MenuIcon /> <MenuIcon/>
<Text <Text
style={{ style={{
fontFamily: "PlusJakartaSans_500Medium", fontFamily: "PlusJakartaSans_500Medium",
@ -714,7 +763,7 @@ export const ManuallyAddEventModal = () => {
multiline multiline
numberOfLines={10} numberOfLines={10}
marginT-10 marginT-10
style={{ flex: 1, minHeight: 180 }} style={{flex: 1, minHeight: 180}}
/> />
</View> </View>
</ScrollView> </ScrollView>
@ -724,12 +773,12 @@ export const ManuallyAddEventModal = () => {
marginB-30 marginB-30
label="Create event from image" label="Create event from image"
text70 text70
style={{ height: 47 }} style={{height: 47}}
labelStyle={{ fontFamily: "PlusJakartaSans_500Medium", fontSize: 15 }} labelStyle={{fontFamily: "PlusJakartaSans_500Medium", fontSize: 15}}
backgroundColor="#05a8b6" backgroundColor="#05a8b6"
iconSource={() => ( iconSource={() => (
<View marginR-5> <View marginR-5>
<CameraIcon color="white" /> <CameraIcon color="white"/>
</View> </View>
)} )}
/> />
@ -743,11 +792,12 @@ export const ManuallyAddEventModal = () => {
/> />
)} )}
</Modal> </Modal>
</>
); );
}; };
const styles = StyleSheet.create({ const styles = StyleSheet.create({
divider: { height: 1, backgroundColor: "#e4e4e4", marginVertical: 15 }, divider: {height: 1, backgroundColor: "#e4e4e4", marginVertical: 15},
gradient: { gradient: {
height: "25%", height: "25%",
position: "absolute", position: "absolute",