Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
Dejan
2024-10-20 18:34:56 +02:00
13 changed files with 130 additions and 351 deletions

View File

@ -1,6 +1,6 @@
{
"expo": {
"name": "Cally.",
"name": "Cally ",
"slug": "cally",
"version": "1.0.0",
"orientation": "portrait",
@ -16,7 +16,7 @@
"supportsTablet": true,
"bundleIdentifier": "com.cally.app",
"googleServicesFile": "./ios/GoogleService-Info.plist",
"buildNumber": "31",
"buildNumber": "34",
"usesAppleSignIn": true
},
"android": {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -72,12 +72,14 @@ export const AddEventDialog = () => {
style={{marginTop: 20, alignItems: "center", width: "100%"}}
>
<Button
disabled
style={{
marginBottom: 10,
backgroundColor: "#ea156c",
// backgroundColor: "#ea156c",
justifyContent: "center",
width: "100%",
paddingVertical: 13,
opacity: 0.5
}}
label="Scan Image"
labelStyle={styles.btnLabel}
@ -104,12 +106,14 @@ export const AddEventDialog = () => {
/>
<Button
disabled
style={{
marginBottom: 10,
backgroundColor: "#05a8b6",
// backgroundColor: "#05a8b6",
justifyContent: "center",
width: "100%",
paddingVertical: 13,
opacity: 0.5
}}
label="Add To Do"
labelStyle={styles.btnLabel}

View File

@ -1,303 +0,0 @@
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 ClockIcon from "@/assets/svgs/ClockIcon";
import LockIcon from "@/assets/svgs/LockIcon";
import MenuIcon from "@/assets/svgs/MenuIcon";
import {useUpdateEvent} from "@/hooks/firebase/useUpdateEvent";
import {editVisibleAtom, eventForEditAtom} from "@/components/pages/calendar/atoms";
import {useAtom} from "jotai";
const EditEventDialog = () => {
const [isVisible, setIsVisible] = useAtom(editVisibleAtom)
const [event, setEvent] = useAtom(eventForEditAtom)
const {mutateAsync: updateEvent} = useUpdateEvent();
if (!event) return null
return (
<Dialog
bottom={true}
height={"90%"}
panDirection={PanningDirectionsEnum.DOWN}
onDismiss={() => setIsVisible(false)}
containerStyle={{
borderRadius: 10,
backgroundColor: "white",
width: "100%",
alignSelf: "stretch",
padding: 0,
paddingTop: 4,
margin: 0,
}}
visible={isVisible}
>
<View row spread>
<Button
color="#05a8b6"
style={styles.topBtn}
label="Cancel"
onPress={() => {
setIsVisible(false);
}}
/>
<View marginT-12>
<DropModalIcon
onPress={() => {
setIsVisible(false);
}}
/>
</View>
<Button
color="#05a8b6"
style={styles.topBtn}
label="Save"
onPress={() => {
try {
if (event.id) {
updateEvent(event).then(() => setIsVisible(false));
}
} catch (error) {
console.error(error);
}
}}
/>
</View>
<TextField
placeholder="Edit event title"
value={event.title}
onChangeText={(text) => {
setEvent((prevEvent) => ({
...prevEvent!,
title: text,
}));
}}
placeholderTextColor="#2d2d30"
text60R
marginT-15
marginL-30
/>
<View style={styles.divider} marginT-8/>
<View row spread marginB-10 marginL-30 centerV>
<View row>
<AntDesign name="clockcircleo" size={24} color="#919191"/>
<Text text70 marginL-10>
All day
</Text>
</View>
<View right marginR-30>
<Switch
onColor={"#ea156c"}
offColor={"#e1e1e2"}
marginL-10
value={event.allDay}
onValueChange={(value) =>
setEvent((prev) => ({...prev!, allDay: value}))
}
/>
</View>
</View>
<View marginL-30 centerV>
<View row marginB-10 spread>
<View row centerV>
<Feather name="calendar" size={25} color="#919191"/>
<DateTimePicker
value={event.start}
text70
marginL-8
maximumDate={event.end}
onChange={(date) => {
setEvent((prev) => ({...prev!, start: date}));
}}
/>
</View>
<DateTimePicker
text70
value={event.start}
onChange={(date) => {
setEvent((prev) => ({...prev!, start: date}));
}}
maximumDate={event.end}
dateTimeFormatter={(date) => date.toLocaleTimeString("en-us",
{
hour: "numeric",
minute: "numeric"
})}
mode="time"
marginR-30
/>
</View>
{!event.allDay && (
<View row marginB-10 spread>
<View row centerV>
<Feather name="calendar" size={25} color="#919191"/>
<DateTimePicker
value={event.end}
minimumDate={event.start}
text70
marginL-8
onChange={(date) => {
setEvent((prev) => ({...prev!, end: date}));
}}
/>
</View>
<DateTimePicker
text70
value={event.end}
minimumDate={event.start}
onChange={(date) => {
setEvent((prev) => ({...prev!, end: date}));
}}
dateTimeFormatter={(date) => date.toLocaleTimeString("en-us",
{
hour: "numeric",
minute: "numeric"
})}
mode="time"
marginR-30
/>
</View>
)}
</View>
<View style={styles.divider}/>
<View marginH-30 marginB-10 row centerV>
<Ionicons name="person-circle-outline" size={28} color="#919191"/>
<Text text70R marginL-10>
Assignees
</Text>
<Button
size={ButtonSize.small}
paddingH-8
iconSource={() => (
<Ionicons name="add-outline" size={20} color="#ea156c"/>
)}
style={{
marginLeft: "auto",
borderRadius: 8,
backgroundColor: "#ffe8f1",
borderColor: "#ea156c",
borderWidth: 1,
}}
color="#ea156c"
label="Assign"
/>
</View>
<View row marginH-13 marginT-13>
<View
marginL-30
style={{
aspectRatio: 1,
width: 50,
backgroundColor: "red",
borderRadius: 50,
}}
/>
<View
marginL-30
style={{
aspectRatio: 1,
width: 50,
backgroundColor: "red",
borderRadius: 50,
}}
/>
</View>
<View style={styles.divider}/>
<View marginH-30 marginB-0 row spread centerV>
<View row centerV>
<ClockIcon/>
<Text text70 marginL-10>
Reminder
</Text>
</View>
<View>
<Button
size={ButtonSize.small}
paddingH-8
iconSource={() => (
<Ionicons name="add-outline" size={20} color="#ea156c"/>
)}
style={{
marginLeft: "auto",
borderRadius: 8,
backgroundColor: "#ffe8f1",
borderColor: "#ea156c",
borderWidth: 1,
}}
color="#ea156c"
label="Set Reminder"
/>
</View>
</View>
<View style={styles.divider}/>
<View marginH-30 marginB-0 row spread centerV>
<View row>
<LockIcon/>
<Text text70 marginL-10>
Mark as Private
</Text>
</View>
<View>
<Switch
onColor={"#ea156c"}
offColor={"#e1e1e2"}
marginL-10
value={event.private}
onValueChange={(value) =>
setEvent((prev) => ({...prev!, private: value}))
}
/>
</View>
</View>
<View style={styles.divider}/>
<View marginH-30 marginB-0 row spread centerV>
<View row centerV>
<MenuIcon/>
<Text text70 marginL-10>
Add Details
</Text>
</View>
<View></View>
</View>
</Dialog>
);
};
export default EditEventDialog;
const styles = StyleSheet.create({
divider: {height: 1, backgroundColor: "#e4e4e4", marginVertical: 15},
gradient: {
height: "25%",
position: "absolute",
bottom: 0,
width: "100%",
},
buttonContainer: {
position: "absolute",
bottom: 25,
width: "100%",
},
button: {
backgroundColor: "rgb(253, 23, 117)",
paddingVertical: 20,
},
topBtn: {
backgroundColor: "white",
color: "#05a8b6",
},
rotateSwitch: {
marginLeft: 35,
marginBottom: 10,
marginTop: 25,
},
});

View File

@ -48,9 +48,15 @@ export const EventCalendar: React.FC<EventCalendarProps> = React.memo(({calendar
}, [events, mode]);
const handlePressEvent = useCallback((event: CalendarEvent) => {
if (mode === "day") {
setEditVisible(true);
console.log({event})
setEventForEdit(event);
}, [setEditVisible, setEventForEdit]);
} else {
setMode("day")
setSelectedDate(event.start);
}
}, [setEditVisible, setEventForEdit, mode]);
const handlePressCell = useCallback(
(date: Date) => {

View File

@ -3,7 +3,6 @@ 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";
@ -37,7 +36,6 @@ export const InnerCalendar = () => {
<CalendarViewSwitch/>
<AddEventDialog/>
<EditEventDialog/>
<ManuallyAddEventModal/>
</>
)

View File

@ -10,12 +10,13 @@ import {
Switch,
Text,
TextField,
TextFieldRef,
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 {useEffect, useRef, 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";
@ -29,7 +30,7 @@ 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";
import {eventForEditAtom, selectedNewEventDateAtom} from "@/components/pages/calendar/atoms";
import {useGetFamilyMembers} from "@/hooks/firebase/useGetFamilyMembers";
const daysOfWeek = [
@ -46,42 +47,77 @@ export const ManuallyAddEventModal = () => {
const insets = useSafeAreaInsets();
const [selectedNewEventDate, setSelectedNewEndDate] = useAtom(selectedNewEventDateAtom)
const [editEvent, setEditEvent] = useAtom(eventForEditAtom)
const {show, close, initialDate} = {
show: !!selectedNewEventDate,
close: () => setSelectedNewEndDate(undefined),
initialDate: selectedNewEventDate
show: !!selectedNewEventDate || !!editEvent,
close: () => {
setSelectedNewEndDate(undefined)
setEditEvent(undefined)
},
initialDate: selectedNewEventDate || editEvent?.start
}
const [title, setTitle] = useState<string>("");
const detailsRef = useRef<TextFieldRef>(null)
const [isAllDay, setIsAllDay] = useState(false);
const [isPrivate, setIsPrivate] = useState<boolean>(false);
const [title, setTitle] = useState<string>(editEvent?.title || "");
const [details, setDetails] = useState<string>(editEvent?.notes || "");
const [isAllDay, setIsAllDay] = useState(editEvent?.allDay || false);
const [isPrivate, setIsPrivate] = useState<boolean>(editEvent?.private || false);
const [startTime, setStartTime] = useState(() => {
const date = initialDate ?? new Date();
date.setSeconds(0, 0);
return date;
});
const [endTime, setEndTime] = useState(() => {
const date = initialDate
? addHours(initialDate, 1)
if (editEvent?.end) {
const date = new Date(editEvent.end);
date.setSeconds(0, 0);
return date;
}
const date = (editEvent?.end ?? initialDate)
? addHours((editEvent?.end ?? initialDate!), 1)
: addHours(new Date(), 1);
date.setSeconds(0, 0);
return date;
});
const [startDate, setStartDate] = useState(initialDate ?? new Date());
const [endDate, setEndDate] = useState(initialDate ?? new Date());
const [selectedAttendees, setSelectedAttendees] = useState<string[]>([]);
const [endDate, setEndDate] = useState(editEvent?.end ?? initialDate ?? new Date());
const [selectedAttendees, setSelectedAttendees] = useState<string[]>(editEvent?.participants ?? []);
const [repeatInterval, setRepeatInterval] = useState<PickerMultiValue>([]);
const {mutateAsync: createEvent, isLoading, isError} = useCreateEvent();
const {data: members} = useGetFamilyMembers(true)
const {data: members} = useGetFamilyMembers(true);
useEffect(() => {
setTitle(editEvent?.title || "");
setDetails(editEvent?.notes || "");
setIsAllDay(editEvent?.allDay || false);
setIsPrivate(editEvent?.private || false);
setStartTime(() => {
const date = initialDate ?? new Date();
date.setSeconds(0, 0);
return date;
});
setEndTime(() => {
if (editEvent?.end) {
const date = new Date(editEvent.end);
date.setSeconds(0, 0);
return date;
}
const date = (editEvent?.end ?? initialDate)
? addHours((editEvent?.end ?? initialDate!), 1)
: addHours(new Date(), 1);
date.setSeconds(0, 0);
return date;
});
setStartDate(initialDate ?? new Date());
setEndDate(editEvent?.end ?? initialDate ?? new Date());
setSelectedAttendees(editEvent?.participants ?? []);
setRepeatInterval([]);
}, [editEvent, selectedNewEventDate]);
if (!selectedNewEventDate) return null;
if (!show) return null;
const formatDateTime = (date?: Date | string) => {
if (!date) return undefined;
@ -119,8 +155,11 @@ export const ManuallyAddEventModal = () => {
endDate: finalEndDate,
allDay: isAllDay,
attendees: selectedAttendees,
notes: details
};
if (editEvent?.id) eventData.id = editEvent?.id
await createEvent(eventData);
close();
@ -221,7 +260,7 @@ export const ManuallyAddEventModal = () => {
</Text>
</TouchableOpacity>
</View>
<ScrollView>
<ScrollView style={{minHeight: "85%"}}>
<TextField
placeholder="Add event title"
value={title}
@ -382,7 +421,8 @@ export const ManuallyAddEventModal = () => {
</View>
<View marginL-35>
<AssigneesDisplay setSlectedAttendees={setSelectedAttendees} selectedAttendees={selectedAttendees}/>
<AssigneesDisplay setSlectedAttendees={setSelectedAttendees}
selectedAttendees={selectedAttendees}/>
</View>
<View style={styles.divider}/>
@ -421,7 +461,7 @@ export const ManuallyAddEventModal = () => {
</View>
<View style={styles.divider}/>
<View marginH-30 marginB-0 row spread centerV>
<View row>
<View row centerH>
<LockIcon/>
<Text
style={{
@ -444,7 +484,8 @@ export const ManuallyAddEventModal = () => {
</View>
</View>
<View style={styles.divider}/>
<View marginH-30 marginB-0 row spread centerV>
<View marginH-30 marginB-0 spread centerV flex-1>
<TouchableOpacity onPress={() => detailsRef?.current?.focus()}>
<View row centerV>
<MenuIcon/>
<Text
@ -457,10 +498,14 @@ export const ManuallyAddEventModal = () => {
Add Details
</Text>
</View>
<View></View>
</TouchableOpacity>
<TextField value={details} onChangeText={setDetails} ref={detailsRef} maxLength={2000} multiline
numberOfLines={10} marginT-10 style={{flex: 1, minHeight: 180}}/>
</View>
</ScrollView>
<Button
disabled
marginH-30
marginB-15
label="Create event from image"

View File

@ -10,4 +10,5 @@ export interface CalendarEvent {
eventColor?: string; // Optional color to represent the event
participants?: string[]; // Optional list of participants or attendees
private?: boolean;
notes?: string
}

View File

@ -11,11 +11,30 @@ export const useCreateEvent = () => {
mutationKey: ["createEvent"],
mutationFn: async (eventData: Partial<EventData>) => {
try {
if (eventData.id) {
const snapshot = await firestore()
.collection("Events")
.where("id", "==", eventData.id)
.get();
if (!snapshot.empty) {
const docId = snapshot.docs[0].id;
await firestore()
.collection("Events")
.add({...eventData, creatorId: currentUser?.uid, familyId: profileData?.familyId})
.doc(docId)
.set({
...eventData,
creatorId: currentUser?.uid,
familyId: profileData?.familyId
}, {merge: true});
return;
}
}
await firestore()
.collection("Events")
.add({...eventData, creatorId: currentUser?.uid, familyId: profileData?.familyId});
} catch (e) {
console.error(e)
console.error(e);
}
},
onSuccess: () => {

View File

@ -53,6 +53,13 @@ export const useGetEvents = () => {
index === self.findIndex(e => e.id === event.id)
);
allEvents = allEvents.filter(event => {
if (event.private) {
return event.creatorId === userId;
}
return true;
});
return await Promise.all(
allEvents.map(async (event) => {
const profileSnapshot = await db
@ -70,6 +77,7 @@ export const useGetEvents = () => {
end: new Date(event.endDate.seconds * 1000),
hideHours: event.allDay,
eventColor: eventColor,
notes: event.notes
};
})
);

View File

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

View File

@ -47,7 +47,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>31</string>
<string>34</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
@ -111,6 +111,7 @@
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
</array>
<key>UILaunchStoryboardName</key>
<string>SplashScreen</string>