mirror of
https://github.com/urosran/cally.git
synced 2025-11-26 00:24:53 +00:00
Calendar page refactor
This commit is contained in:
@ -1,14 +1,11 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { CalendarProvider } from "@/contexts/CalendarContext"; // Import the new CalendarPage component
|
|
||||||
import CalendarPage from "@/components/pages/calendar/CalendarPage";
|
import CalendarPage from "@/components/pages/calendar/CalendarPage";
|
||||||
import { SettingsContextProvider } from "@/contexts/SettingsContext";
|
import {SettingsContextProvider} from "@/contexts/SettingsContext";
|
||||||
|
|
||||||
export default function Screen() {
|
export default function Screen() {
|
||||||
return (
|
return (
|
||||||
<SettingsContextProvider>
|
<SettingsContextProvider>
|
||||||
<CalendarProvider>
|
<CalendarPage/>
|
||||||
<CalendarPage />
|
</SettingsContextProvider>
|
||||||
</CalendarProvider>
|
);
|
||||||
</SettingsContextProvider>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
466
app/_layout.tsx
466
app/_layout.tsx
@ -1,170 +1,6 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, {useEffect} from "react";
|
||||||
import { DefaultTheme, ThemeProvider } from "@react-navigation/native";
|
import {DefaultTheme, ThemeProvider} from "@react-navigation/native";
|
||||||
import {
|
import {
|
||||||
useFonts,
|
|
||||||
Manrope_200ExtraLight,
|
|
||||||
Manrope_300Light,
|
|
||||||
Manrope_400Regular,
|
|
||||||
Manrope_500Medium,
|
|
||||||
Manrope_600SemiBold,
|
|
||||||
Manrope_700Bold,
|
|
||||||
Manrope_800ExtraBold,
|
|
||||||
} 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_Italic,
|
|
||||||
PlusJakartaSans_400Regular_Italic,
|
|
||||||
PlusJakartaSans_500Medium_Italic,
|
|
||||||
PlusJakartaSans_600SemiBold_Italic,
|
|
||||||
PlusJakartaSans_700Bold_Italic,
|
|
||||||
PlusJakartaSans_800ExtraBold_Italic,
|
|
||||||
} from "@expo-google-fonts/plus-jakarta-sans";
|
|
||||||
import {
|
|
||||||
Poppins_100Thin,
|
|
||||||
Poppins_100Thin_Italic,
|
|
||||||
Poppins_200ExtraLight,
|
|
||||||
Poppins_200ExtraLight_Italic,
|
|
||||||
Poppins_300Light,
|
|
||||||
Poppins_300Light_Italic,
|
|
||||||
Poppins_400Regular,
|
|
||||||
Poppins_400Regular_Italic,
|
|
||||||
Poppins_500Medium,
|
|
||||||
Poppins_500Medium_Italic,
|
|
||||||
Poppins_600SemiBold,
|
|
||||||
Poppins_600SemiBold_Italic,
|
|
||||||
Poppins_700Bold,
|
|
||||||
Poppins_700Bold_Italic,
|
|
||||||
Poppins_800ExtraBold,
|
|
||||||
Poppins_800ExtraBold_Italic,
|
|
||||||
Poppins_900Black,
|
|
||||||
Poppins_900Black_Italic,
|
|
||||||
} from "@expo-google-fonts/poppins";
|
|
||||||
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";
|
|
||||||
|
|
||||||
SplashScreen.preventAutoHideAsync();
|
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
|
||||||
|
|
||||||
if (__DEV__) {
|
|
||||||
functions().useEmulator("localhost", 5001);
|
|
||||||
firestore().useEmulator("localhost", 5471);
|
|
||||||
auth().useEmulator("http://localhost:9099");
|
|
||||||
}
|
|
||||||
|
|
||||||
type TextStyleBase =
|
|
||||||
| "text10"
|
|
||||||
| "text20"
|
|
||||||
| "text30"
|
|
||||||
| "text40"
|
|
||||||
| "text50"
|
|
||||||
| "text60"
|
|
||||||
| "text70"
|
|
||||||
| "text80"
|
|
||||||
| "text90"
|
|
||||||
| "text100";
|
|
||||||
type TextStyleModifier = "R" | "M" | "BO" | "H" | "BL" | "L";
|
|
||||||
type TextStyle = TextStyleBase | `${TextStyleBase}${TextStyleModifier}`;
|
|
||||||
|
|
||||||
type TextStyleProps = {
|
|
||||||
[K in TextStyle]?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
type ExtendedTextProps = TextProps & TextStyleProps;
|
|
||||||
|
|
||||||
interface FontStyle {
|
|
||||||
fontFamily: string;
|
|
||||||
fontSize: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getManropeFontStyle = (style: TextStyle): FontStyle => {
|
|
||||||
let fontFamily: string;
|
|
||||||
let fontSize: number;
|
|
||||||
|
|
||||||
if (style.includes("L") || style.includes("BL"))
|
|
||||||
fontFamily = "Manrope_300Light";
|
|
||||||
else if (style.includes("R")) fontFamily = "Manrope_400Regular";
|
|
||||||
else if (style.includes("M")) fontFamily = "Manrope_500Medium";
|
|
||||||
else if (style.includes("BO") || style.includes("H"))
|
|
||||||
fontFamily = "Manrope_700Bold";
|
|
||||||
else {
|
|
||||||
const baseStyle = style.slice(0, 6) as TextStyleBase;
|
|
||||||
switch (baseStyle) {
|
|
||||||
case "text10":
|
|
||||||
case "text20":
|
|
||||||
fontFamily = "Manrope_700Bold";
|
|
||||||
break;
|
|
||||||
case "text30":
|
|
||||||
case "text40":
|
|
||||||
fontFamily = "Manrope_600SemiBold";
|
|
||||||
break;
|
|
||||||
case "text50":
|
|
||||||
fontFamily = "Manrope_400Regular";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fontFamily = "Manrope_300Light";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (style.slice(0, 6) as TextStyleBase) {
|
|
||||||
case "text10":
|
|
||||||
fontSize = 64;
|
|
||||||
break;
|
|
||||||
case "text20":
|
|
||||||
fontSize = 50;
|
|
||||||
break;
|
|
||||||
case "text30":
|
|
||||||
fontSize = 36;
|
|
||||||
break;
|
|
||||||
case "text40":
|
|
||||||
fontSize = 28;
|
|
||||||
break;
|
|
||||||
case "text50":
|
|
||||||
fontSize = 24;
|
|
||||||
break;
|
|
||||||
case "text60":
|
|
||||||
fontSize = 20;
|
|
||||||
break;
|
|
||||||
case "text70":
|
|
||||||
fontSize = 16;
|
|
||||||
break;
|
|
||||||
case "text80":
|
|
||||||
fontSize = 14;
|
|
||||||
break;
|
|
||||||
case "text90":
|
|
||||||
fontSize = 12;
|
|
||||||
break;
|
|
||||||
case "text100":
|
|
||||||
fontSize = 10;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fontSize = 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { fontFamily, fontSize };
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function RootLayout() {
|
|
||||||
const [loaded] = useFonts({
|
|
||||||
Manrope_200ExtraLight,
|
Manrope_200ExtraLight,
|
||||||
Manrope_300Light,
|
Manrope_300Light,
|
||||||
Manrope_400Regular,
|
Manrope_400Regular,
|
||||||
@ -172,20 +8,25 @@ export default function RootLayout() {
|
|||||||
Manrope_600SemiBold,
|
Manrope_600SemiBold,
|
||||||
Manrope_700Bold,
|
Manrope_700Bold,
|
||||||
Manrope_800ExtraBold,
|
Manrope_800ExtraBold,
|
||||||
|
useFonts,
|
||||||
|
} from "@expo-google-fonts/manrope";
|
||||||
|
import {
|
||||||
PlusJakartaSans_200ExtraLight,
|
PlusJakartaSans_200ExtraLight,
|
||||||
PlusJakartaSans_300Light,
|
|
||||||
PlusJakartaSans_400Regular,
|
|
||||||
PlusJakartaSans_500Medium,
|
|
||||||
PlusJakartaSans_600SemiBold,
|
|
||||||
PlusJakartaSans_700Bold,
|
|
||||||
PlusJakartaSans_800ExtraBold,
|
|
||||||
PlusJakartaSans_200ExtraLight_Italic,
|
PlusJakartaSans_200ExtraLight_Italic,
|
||||||
|
PlusJakartaSans_300Light,
|
||||||
PlusJakartaSans_300Light_Italic,
|
PlusJakartaSans_300Light_Italic,
|
||||||
|
PlusJakartaSans_400Regular,
|
||||||
PlusJakartaSans_400Regular_Italic,
|
PlusJakartaSans_400Regular_Italic,
|
||||||
|
PlusJakartaSans_500Medium,
|
||||||
PlusJakartaSans_500Medium_Italic,
|
PlusJakartaSans_500Medium_Italic,
|
||||||
|
PlusJakartaSans_600SemiBold,
|
||||||
PlusJakartaSans_600SemiBold_Italic,
|
PlusJakartaSans_600SemiBold_Italic,
|
||||||
|
PlusJakartaSans_700Bold,
|
||||||
PlusJakartaSans_700Bold_Italic,
|
PlusJakartaSans_700Bold_Italic,
|
||||||
|
PlusJakartaSans_800ExtraBold,
|
||||||
PlusJakartaSans_800ExtraBold_Italic,
|
PlusJakartaSans_800ExtraBold_Italic,
|
||||||
|
} from "@expo-google-fonts/plus-jakarta-sans";
|
||||||
|
import {
|
||||||
Poppins_100Thin,
|
Poppins_100Thin,
|
||||||
Poppins_100Thin_Italic,
|
Poppins_100Thin_Italic,
|
||||||
Poppins_200ExtraLight,
|
Poppins_200ExtraLight,
|
||||||
@ -204,72 +45,223 @@ export default function RootLayout() {
|
|||||||
Poppins_800ExtraBold_Italic,
|
Poppins_800ExtraBold_Italic,
|
||||||
Poppins_900Black,
|
Poppins_900Black,
|
||||||
Poppins_900Black_Italic,
|
Poppins_900Black_Italic,
|
||||||
});
|
} from "@expo-google-fonts/poppins";
|
||||||
|
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 {TextProps, ThemeManager, Toast, Typography,} from "react-native-ui-lib";
|
||||||
|
|
||||||
useEffect(() => {
|
SplashScreen.preventAutoHideAsync();
|
||||||
if (loaded) {
|
|
||||||
SplashScreen.hideAsync();
|
|
||||||
|
|
||||||
const typographies: Partial<Record<TextStyle, FontStyle>> = {};
|
const queryClient = new QueryClient();
|
||||||
(
|
|
||||||
[
|
|
||||||
"text10",
|
|
||||||
"text20",
|
|
||||||
"text30",
|
|
||||||
"text40",
|
|
||||||
"text50",
|
|
||||||
"text60",
|
|
||||||
"text70",
|
|
||||||
"text80",
|
|
||||||
"text90",
|
|
||||||
"text100",
|
|
||||||
] as const
|
|
||||||
).forEach((baseStyle) => {
|
|
||||||
typographies[baseStyle] = getManropeFontStyle(baseStyle);
|
|
||||||
(["R", "M", "BO", "H", "BL", "L"] as const).forEach((modifier) => {
|
|
||||||
const style = `${baseStyle}${modifier}` as TextStyle;
|
|
||||||
typographies[style] = getManropeFontStyle(style);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Typography.loadTypographies(typographies);
|
if (__DEV__) {
|
||||||
|
// functions().useEmulator("localhost", 5001);
|
||||||
ThemeManager.setComponentTheme(
|
// firestore().useEmulator("localhost", 5471);
|
||||||
"Text",
|
// auth().useEmulator("http://localhost:9099");
|
||||||
(props: ExtendedTextProps, context: unknown) => {
|
}
|
||||||
const textStyle = (
|
|
||||||
Object.keys(props) as Array<keyof ExtendedTextProps>
|
type TextStyleBase =
|
||||||
).find((key) => typographies[key as TextStyle]) as
|
| "text10"
|
||||||
| TextStyle
|
| "text20"
|
||||||
| undefined;
|
| "text30"
|
||||||
|
| "text40"
|
||||||
return {
|
| "text50"
|
||||||
style: [
|
| "text60"
|
||||||
Typography.text50,
|
| "text70"
|
||||||
textStyle ? typographies[textStyle] : undefined,
|
| "text80"
|
||||||
],
|
| "text90"
|
||||||
};
|
| "text100";
|
||||||
}
|
type TextStyleModifier = "R" | "M" | "BO" | "H" | "BL" | "L";
|
||||||
);
|
type TextStyle = TextStyleBase | `${TextStyleBase}${TextStyleModifier}`;
|
||||||
}
|
|
||||||
}, [loaded]);
|
type TextStyleProps = {
|
||||||
|
[K in TextStyle]?: boolean;
|
||||||
if (!loaded) {
|
};
|
||||||
return null;
|
|
||||||
}
|
type ExtendedTextProps = TextProps & TextStyleProps;
|
||||||
|
|
||||||
return (
|
interface FontStyle {
|
||||||
<QueryClientProvider client={queryClient}>
|
fontFamily: string;
|
||||||
<AuthContextProvider>
|
fontSize: number;
|
||||||
<ThemeProvider value={DefaultTheme}>
|
}
|
||||||
<Stack>
|
|
||||||
<Stack.Screen name="(auth)" options={{ headerShown: false }} />
|
const getManropeFontStyle = (style: TextStyle): FontStyle => {
|
||||||
<Stack.Screen name="(unauth)" options={{ headerShown: false }} />
|
let fontFamily: string;
|
||||||
<Stack.Screen name="+not-found" />
|
let fontSize: number;
|
||||||
</Stack>
|
|
||||||
<Toast />
|
if (style.includes("L") || style.includes("BL"))
|
||||||
</ThemeProvider>
|
fontFamily = "Manrope_300Light";
|
||||||
</AuthContextProvider>
|
else if (style.includes("R")) fontFamily = "Manrope_400Regular";
|
||||||
</QueryClientProvider>
|
else if (style.includes("M")) fontFamily = "Manrope_500Medium";
|
||||||
);
|
else if (style.includes("BO") || style.includes("H"))
|
||||||
|
fontFamily = "Manrope_700Bold";
|
||||||
|
else {
|
||||||
|
const baseStyle = style.slice(0, 6) as TextStyleBase;
|
||||||
|
switch (baseStyle) {
|
||||||
|
case "text10":
|
||||||
|
case "text20":
|
||||||
|
fontFamily = "Manrope_700Bold";
|
||||||
|
break;
|
||||||
|
case "text30":
|
||||||
|
case "text40":
|
||||||
|
fontFamily = "Manrope_600SemiBold";
|
||||||
|
break;
|
||||||
|
case "text50":
|
||||||
|
fontFamily = "Manrope_400Regular";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fontFamily = "Manrope_300Light";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (style.slice(0, 6) as TextStyleBase) {
|
||||||
|
case "text10":
|
||||||
|
fontSize = 64;
|
||||||
|
break;
|
||||||
|
case "text20":
|
||||||
|
fontSize = 50;
|
||||||
|
break;
|
||||||
|
case "text30":
|
||||||
|
fontSize = 36;
|
||||||
|
break;
|
||||||
|
case "text40":
|
||||||
|
fontSize = 28;
|
||||||
|
break;
|
||||||
|
case "text50":
|
||||||
|
fontSize = 24;
|
||||||
|
break;
|
||||||
|
case "text60":
|
||||||
|
fontSize = 20;
|
||||||
|
break;
|
||||||
|
case "text70":
|
||||||
|
fontSize = 16;
|
||||||
|
break;
|
||||||
|
case "text80":
|
||||||
|
fontSize = 14;
|
||||||
|
break;
|
||||||
|
case "text90":
|
||||||
|
fontSize = 12;
|
||||||
|
break;
|
||||||
|
case "text100":
|
||||||
|
fontSize = 10;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fontSize = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {fontFamily, fontSize};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function RootLayout() {
|
||||||
|
const [loaded] = useFonts({
|
||||||
|
Manrope_200ExtraLight,
|
||||||
|
Manrope_300Light,
|
||||||
|
Manrope_400Regular,
|
||||||
|
Manrope_500Medium,
|
||||||
|
Manrope_600SemiBold,
|
||||||
|
Manrope_700Bold,
|
||||||
|
Manrope_800ExtraBold,
|
||||||
|
PlusJakartaSans_200ExtraLight,
|
||||||
|
PlusJakartaSans_300Light,
|
||||||
|
PlusJakartaSans_400Regular,
|
||||||
|
PlusJakartaSans_500Medium,
|
||||||
|
PlusJakartaSans_600SemiBold,
|
||||||
|
PlusJakartaSans_700Bold,
|
||||||
|
PlusJakartaSans_800ExtraBold,
|
||||||
|
PlusJakartaSans_200ExtraLight_Italic,
|
||||||
|
PlusJakartaSans_300Light_Italic,
|
||||||
|
PlusJakartaSans_400Regular_Italic,
|
||||||
|
PlusJakartaSans_500Medium_Italic,
|
||||||
|
PlusJakartaSans_600SemiBold_Italic,
|
||||||
|
PlusJakartaSans_700Bold_Italic,
|
||||||
|
PlusJakartaSans_800ExtraBold_Italic,
|
||||||
|
Poppins_100Thin,
|
||||||
|
Poppins_100Thin_Italic,
|
||||||
|
Poppins_200ExtraLight,
|
||||||
|
Poppins_200ExtraLight_Italic,
|
||||||
|
Poppins_300Light,
|
||||||
|
Poppins_300Light_Italic,
|
||||||
|
Poppins_400Regular,
|
||||||
|
Poppins_400Regular_Italic,
|
||||||
|
Poppins_500Medium,
|
||||||
|
Poppins_500Medium_Italic,
|
||||||
|
Poppins_600SemiBold,
|
||||||
|
Poppins_600SemiBold_Italic,
|
||||||
|
Poppins_700Bold,
|
||||||
|
Poppins_700Bold_Italic,
|
||||||
|
Poppins_800ExtraBold,
|
||||||
|
Poppins_800ExtraBold_Italic,
|
||||||
|
Poppins_900Black,
|
||||||
|
Poppins_900Black_Italic,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (loaded) {
|
||||||
|
SplashScreen.hideAsync();
|
||||||
|
|
||||||
|
const typographies: Partial<Record<TextStyle, FontStyle>> = {};
|
||||||
|
(
|
||||||
|
[
|
||||||
|
"text10",
|
||||||
|
"text20",
|
||||||
|
"text30",
|
||||||
|
"text40",
|
||||||
|
"text50",
|
||||||
|
"text60",
|
||||||
|
"text70",
|
||||||
|
"text80",
|
||||||
|
"text90",
|
||||||
|
"text100",
|
||||||
|
] as const
|
||||||
|
).forEach((baseStyle) => {
|
||||||
|
typographies[baseStyle] = getManropeFontStyle(baseStyle);
|
||||||
|
(["R", "M", "BO", "H", "BL", "L"] as const).forEach((modifier) => {
|
||||||
|
const style = `${baseStyle}${modifier}` as TextStyle;
|
||||||
|
typographies[style] = getManropeFontStyle(style);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Typography.loadTypographies(typographies);
|
||||||
|
|
||||||
|
ThemeManager.setComponentTheme(
|
||||||
|
"Text",
|
||||||
|
(props: ExtendedTextProps) => {
|
||||||
|
const textStyle = (
|
||||||
|
Object.keys(props) as Array<keyof ExtendedTextProps>
|
||||||
|
).find((key) => typographies[key as TextStyle]) as
|
||||||
|
| TextStyle
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
style: [
|
||||||
|
Typography.text50,
|
||||||
|
textStyle ? typographies[textStyle] : undefined,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [loaded]);
|
||||||
|
|
||||||
|
if (!loaded) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<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>
|
||||||
|
<Toast/>
|
||||||
|
</ThemeProvider>
|
||||||
|
</AuthContextProvider>
|
||||||
|
</QueryClientProvider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,188 +1,172 @@
|
|||||||
import React, { useState } from "react";
|
import React, {useState} from "react";
|
||||||
import {
|
import {MaterialIcons,} from "@expo/vector-icons";
|
||||||
AntDesign,
|
import {Button, Card, Dialog, PanningProvider, Text, View,} from "react-native-ui-lib";
|
||||||
Feather,
|
import {StyleSheet, TouchableOpacity} from "react-native";
|
||||||
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 AddChoreDialog from "../todos/AddChoreDialog";
|
import AddChoreDialog from "../todos/AddChoreDialog";
|
||||||
import { ToDosContextProvider } from "@/contexts/ToDosContext";
|
import {ToDosContextProvider} from "@/contexts/ToDosContext";
|
||||||
import UploadImageDialog from "./UploadImageDialog";
|
import UploadImageDialog from "./UploadImageDialog";
|
||||||
import CameraIcon from "@/assets/svgs/CameraIcon";
|
import CameraIcon from "@/assets/svgs/CameraIcon";
|
||||||
import CalendarIcon from "@/assets/svgs/CalendarIcon";
|
import CalendarIcon from "@/assets/svgs/CalendarIcon";
|
||||||
import NavToDosIcon from "@/assets/svgs/NavToDosIcon";
|
import NavToDosIcon from "@/assets/svgs/NavToDosIcon";
|
||||||
|
import {useSetAtom} from "jotai";
|
||||||
|
import {selectedNewEventDateAtom} from "@/components/pages/calendar/atoms";
|
||||||
|
|
||||||
export const AddEventDialog = () => {
|
export const AddEventDialog = () => {
|
||||||
const [show, setShow] = useState(false);
|
const [show, setShow] = useState(false);
|
||||||
const [showManualInputModal, setShowManualInputModal] = useState(false);
|
const [choreDialogVisible, setChoreDialogVisible] = useState<boolean>(false);
|
||||||
const [choreDialogVisible, setChoreDialogVisible] = useState<boolean>(false);
|
const [showUploadDialog, setShowUploadDialog] = useState<boolean>(false);
|
||||||
const [showUploadDialog, setShowUploadDialog] = useState<boolean>(false);
|
const setSelectedNewEndDate = useSetAtom(selectedNewEventDateAtom)
|
||||||
|
|
||||||
const handleOpenManualInputModal = () => {
|
const handleOpenManualInputModal = () => {
|
||||||
setShow(false);
|
setShow(false);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setShowManualInputModal(true);
|
setSelectedNewEndDate(new Date());
|
||||||
}, 500);
|
}, 500);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleScanImageDialog = () => {
|
const handleScanImageDialog = () => {
|
||||||
setShow(false);
|
setShow(false);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setShowUploadDialog(true);
|
setShowUploadDialog(true);
|
||||||
}, 100);
|
}, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToDosContextProvider>
|
<ToDosContextProvider>
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
bottom: 20,
|
bottom: 20,
|
||||||
right: 20,
|
right: 20,
|
||||||
height: 40,
|
height: 40,
|
||||||
borderRadius: 30,
|
borderRadius: 30,
|
||||||
backgroundColor: "#fd1775",
|
backgroundColor: "#fd1775",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
}}
|
}}
|
||||||
color="white"
|
color="white"
|
||||||
enableShadow
|
enableShadow
|
||||||
onPress={() => setShow(true)}
|
onPress={() => setShow(true)}
|
||||||
>
|
>
|
||||||
<View row centerV centerH>
|
<View row centerV centerH>
|
||||||
<MaterialIcons name="add" size={22} color={"white"} />
|
<MaterialIcons name="add" size={22} color={"white"}/>
|
||||||
<Text white style={{ fontSize: 16, fontFamily: 'Manrope_600SemiBold' }}>
|
<Text white style={{fontSize: 16, fontFamily: 'Manrope_600SemiBold'}}>
|
||||||
New
|
New
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
visible={show}
|
visible={show}
|
||||||
onDismiss={() => setShow(false)}
|
onDismiss={() => setShow(false)}
|
||||||
panDirection={PanningProvider.Directions.DOWN}
|
panDirection={PanningProvider.Directions.DOWN}
|
||||||
center
|
center
|
||||||
>
|
>
|
||||||
<Card style={styles.dialogCard}>
|
<Card style={styles.dialogCard}>
|
||||||
<Text text60 style={styles.modalTitle}>
|
<Text text60 style={styles.modalTitle}>
|
||||||
Create a new event
|
Create a new event
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
style={{ marginTop: 20, alignItems: "center", width: "100%" }}
|
style={{marginTop: 20, alignItems: "center", width: "100%"}}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
style={{
|
style={{
|
||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
backgroundColor: "#ea156c",
|
backgroundColor: "#ea156c",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
paddingVertical: 13,
|
paddingVertical: 13,
|
||||||
}}
|
}}
|
||||||
label="Scan Image"
|
label="Scan Image"
|
||||||
labelStyle={styles.btnLabel}
|
labelStyle={styles.btnLabel}
|
||||||
onPress={handleScanImageDialog}
|
onPress={handleScanImageDialog}
|
||||||
iconSource={() => (
|
iconSource={() => (
|
||||||
<CameraIcon color="white" style={styles.btnIcon} />
|
<CameraIcon color="white" style={styles.btnIcon}/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
style={{
|
style={{
|
||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
backgroundColor: "#e28800",
|
backgroundColor: "#e28800",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
paddingVertical: 13,
|
paddingVertical: 13,
|
||||||
}}
|
}}
|
||||||
label="Create Event"
|
label="Create Event"
|
||||||
labelStyle={styles.btnLabel}
|
labelStyle={styles.btnLabel}
|
||||||
onPress={handleOpenManualInputModal}
|
onPress={handleOpenManualInputModal}
|
||||||
iconSource={() => (
|
iconSource={() => (
|
||||||
<CalendarIcon color={"white"} style={styles.btnIcon} />
|
<CalendarIcon color={"white"} style={styles.btnIcon}/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
style={{
|
style={{
|
||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
backgroundColor: "#05a8b6",
|
backgroundColor: "#05a8b6",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
paddingVertical: 13,
|
paddingVertical: 13,
|
||||||
}}
|
}}
|
||||||
label="Add To Do"
|
label="Add To Do"
|
||||||
labelStyle={styles.btnLabel}
|
labelStyle={styles.btnLabel}
|
||||||
onPress={() => setChoreDialogVisible(true)}
|
onPress={() => setChoreDialogVisible(true)}
|
||||||
iconSource={() => (
|
iconSource={() => (
|
||||||
<NavToDosIcon
|
<NavToDosIcon
|
||||||
color={"white"}
|
color={"white"}
|
||||||
width={23}
|
width={23}
|
||||||
style={styles.btnIcon}
|
style={styles.btnIcon}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<TouchableOpacity onPress={() => setShow(false)}>
|
<TouchableOpacity onPress={() => setShow(false)}>
|
||||||
<Text style={styles.bottomText} text70>
|
<Text style={styles.bottomText} text70>
|
||||||
Go back to calendar
|
Go back to calendar
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</Card>
|
</Card>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
<AddChoreDialog
|
<AddChoreDialog
|
||||||
isVisible={choreDialogVisible}
|
isVisible={choreDialogVisible}
|
||||||
setIsVisible={setChoreDialogVisible}
|
setIsVisible={setChoreDialogVisible}
|
||||||
/>
|
/>
|
||||||
<ManuallyAddEventModal
|
<UploadImageDialog
|
||||||
show={showManualInputModal}
|
show={showUploadDialog}
|
||||||
close={() => setShowManualInputModal(false)}
|
setShow={setShowUploadDialog}
|
||||||
/>
|
/>
|
||||||
<UploadImageDialog
|
</>
|
||||||
show={showUploadDialog}
|
</ToDosContextProvider>
|
||||||
setShow={setShowUploadDialog}
|
);
|
||||||
/>
|
|
||||||
</>
|
|
||||||
</ToDosContextProvider>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
modalTitle: {
|
modalTitle: {
|
||||||
fontSize: 22,
|
fontSize: 22,
|
||||||
fontFamily: "Manrope_600SemiBold",
|
fontFamily: "Manrope_600SemiBold",
|
||||||
marginBottom: 16,
|
marginBottom: 16,
|
||||||
},
|
},
|
||||||
bottomText: {
|
bottomText: {
|
||||||
marginTop: 20,
|
marginTop: 20,
|
||||||
color: "#999999",
|
color: "#999999",
|
||||||
fontSize: 13.53,
|
fontSize: 13.53,
|
||||||
fontFamily: "Poppins_500Medium",
|
fontFamily: "Poppins_500Medium",
|
||||||
},
|
},
|
||||||
dialogCard: {
|
dialogCard: {
|
||||||
paddingHorizontal: 40,
|
paddingHorizontal: 40,
|
||||||
paddingTop: 35,
|
paddingTop: 35,
|
||||||
paddingBottom: 20,
|
paddingBottom: 20,
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
borderRadius: 20,
|
borderRadius: 20,
|
||||||
},
|
},
|
||||||
btnLabel: {
|
btnLabel: {
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
fontFamily: "PlusJakartaSans_500Medium",
|
fontFamily: "PlusJakartaSans_500Medium",
|
||||||
},
|
},
|
||||||
btnIcon: { marginRight: 10 },
|
btnIcon: {marginRight: 10},
|
||||||
});
|
});
|
||||||
|
|||||||
102
components/pages/calendar/CalendarHeader.tsx
Normal file
102
components/pages/calendar/CalendarHeader.tsx
Normal 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,
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -1,207 +1,20 @@
|
|||||||
import React, { useRef, useState } from "react";
|
import React from "react";
|
||||||
import { LayoutChangeEvent, StyleSheet } from "react-native";
|
import {View,} from "react-native-ui-lib";
|
||||||
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 HeaderTemplate from "@/components/shared/HeaderTemplate";
|
import HeaderTemplate from "@/components/shared/HeaderTemplate";
|
||||||
import CalendarViewSwitch from "@/components/pages/calendar/CalendarViewSwitch";
|
import {InnerCalendar} from "@/components/pages/calendar/InnerCalendar";
|
||||||
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",
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function CalendarPage() {
|
export default function CalendarPage() {
|
||||||
const { calendarColor } = useSettingsContext();
|
return (
|
||||||
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 }}
|
|
||||||
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
|
<View
|
||||||
style={{
|
style={{flex: 1, height: "100%", padding: 10}}
|
||||||
flexDirection: "row",
|
paddingH-22
|
||||||
justifyContent: "space-between",
|
paddingT-0
|
||||||
alignItems: "center",
|
|
||||||
paddingHorizontal: 10,
|
|
||||||
paddingVertical: 8,
|
|
||||||
borderRadius: 20,
|
|
||||||
borderBottomLeftRadius: 0,
|
|
||||||
borderBottomRightRadius: 0,
|
|
||||||
backgroundColor: "white",
|
|
||||||
marginBottom: 10,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<View row centerV gap-3>
|
<HeaderTemplate
|
||||||
<Text style={{ fontFamily: "Manrope_500Medium", fontSize: 17 }}>
|
message={"Let's get your week started!"}
|
||||||
{selectedDate.getFullYear()}
|
isWelcome
|
||||||
</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>
|
<InnerCalendar/>
|
||||||
</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)}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,90 +1,90 @@
|
|||||||
import { View, Text, Button, TouchableOpacity } from "react-native-ui-lib";
|
import {Text, TouchableOpacity, View} from "react-native-ui-lib";
|
||||||
import React, { useState } from "react";
|
import React, {useState} from "react";
|
||||||
import { MaterialIcons } from "@expo/vector-icons";
|
import {StyleSheet} from "react-native";
|
||||||
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 [calView, setCalView] = useState<boolean>(false);
|
|
||||||
|
|
||||||
return (
|
const CalendarViewSwitch = () => {
|
||||||
<View
|
const [calView, setCalView] = useState<boolean>(false);
|
||||||
row
|
const viewSwitch = useSetAtom(isFamilyViewAtom)
|
||||||
spread
|
|
||||||
style={{
|
return (
|
||||||
position: "absolute",
|
|
||||||
bottom: 20,
|
|
||||||
left: 20,
|
|
||||||
borderRadius: 30,
|
|
||||||
backgroundColor: "white",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
// iOS shadow
|
|
||||||
shadowColor: "#000",
|
|
||||||
shadowOffset: { width: 0, height: 2 },
|
|
||||||
shadowOpacity: 0.25,
|
|
||||||
shadowRadius: 3.84,
|
|
||||||
// Android shadow (elevation)
|
|
||||||
elevation: 6,
|
|
||||||
}}
|
|
||||||
centerV
|
|
||||||
>
|
|
||||||
<TouchableOpacity
|
|
||||||
onPress={() => {
|
|
||||||
setCalView(true);
|
|
||||||
calendarViewProps.viewSwitch(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<View
|
<View
|
||||||
centerV
|
row
|
||||||
centerH
|
spread
|
||||||
height={40}
|
style={{
|
||||||
paddingH-15
|
position: "absolute",
|
||||||
style={calView ? styles.switchBtnActive : styles.switchBtn}
|
bottom: 20,
|
||||||
|
left: 20,
|
||||||
|
borderRadius: 30,
|
||||||
|
backgroundColor: "white",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
// iOS shadow
|
||||||
|
shadowColor: "#000",
|
||||||
|
shadowOffset: {width: 0, height: 2},
|
||||||
|
shadowOpacity: 0.25,
|
||||||
|
shadowRadius: 3.84,
|
||||||
|
// Android shadow (elevation)
|
||||||
|
elevation: 6,
|
||||||
|
}}
|
||||||
|
centerV
|
||||||
>
|
>
|
||||||
<Text color={calView ? "white" : "#a1a1a1"} style={styles.switchTxt}>
|
<TouchableOpacity
|
||||||
Family View
|
onPress={() => {
|
||||||
</Text>
|
setCalView(true);
|
||||||
</View>
|
viewSwitch(true);
|
||||||
</TouchableOpacity>
|
}}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
centerV
|
||||||
|
centerH
|
||||||
|
height={40}
|
||||||
|
paddingH-15
|
||||||
|
style={calView ? styles.switchBtnActive : styles.switchBtn}
|
||||||
|
>
|
||||||
|
<Text color={calView ? "white" : "#a1a1a1"} style={styles.switchTxt}>
|
||||||
|
Family View
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
setCalView(false);
|
setCalView(false);
|
||||||
calendarViewProps.viewSwitch(false);
|
viewSwitch(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<View
|
<View
|
||||||
centerV
|
centerV
|
||||||
centerH
|
centerH
|
||||||
height={40}
|
height={40}
|
||||||
paddingH-15
|
paddingH-15
|
||||||
style={!calView ? styles.switchBtnActive : styles.switchBtn}
|
style={!calView ? styles.switchBtnActive : styles.switchBtn}
|
||||||
>
|
>
|
||||||
<Text color={!calView ? "white" : "#a1a1a1"} style={styles.switchTxt}>
|
<Text color={!calView ? "white" : "#a1a1a1"} style={styles.switchTxt}>
|
||||||
My View
|
My View
|
||||||
</Text>
|
</Text>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
);
|
||||||
</View>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CalendarViewSwitch;
|
export default CalendarViewSwitch;
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
switchBtnActive: {
|
switchBtnActive: {
|
||||||
backgroundColor: "#a1a1a1",
|
backgroundColor: "#a1a1a1",
|
||||||
borderRadius: 50,
|
borderRadius: 50,
|
||||||
},
|
},
|
||||||
switchBtn: {
|
switchBtn: {
|
||||||
backgroundColor: "white",
|
backgroundColor: "white",
|
||||||
borderRadius: 50,
|
borderRadius: 50,
|
||||||
},
|
},
|
||||||
switchTxt:{
|
switchTxt: {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontFamily: 'Manrope_600SemiBold'
|
fontFamily: 'Manrope_600SemiBold'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,312 +1,303 @@
|
|||||||
import { View, Text, Button, Switch } from "react-native-ui-lib";
|
import {Button, ButtonSize, DateTimePicker, Dialog, Switch, Text, TextField, View} from "react-native-ui-lib";
|
||||||
import React, { useEffect, useState } from "react";
|
import React from "react";
|
||||||
import { Feather, AntDesign, Ionicons } from "@expo/vector-icons";
|
import {AntDesign, Feather, Ionicons} from "@expo/vector-icons";
|
||||||
import {
|
import {PanningDirectionsEnum} from "react-native-ui-lib/src/incubator/panView";
|
||||||
Dialog,
|
import {StyleSheet} from "react-native";
|
||||||
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 DropModalIcon from "@/assets/svgs/DropModalIcon";
|
import DropModalIcon from "@/assets/svgs/DropModalIcon";
|
||||||
import { CalendarEvent } from "@/contexts/CalendarContext";
|
|
||||||
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 { 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(() => {
|
const {mutateAsync: updateEvent} = useUpdateEvent();
|
||||||
setEvent(editEventProps.event);
|
|
||||||
}, [editEventProps.isVisible]);
|
|
||||||
|
|
||||||
return (
|
if (!event) return null
|
||||||
<Dialog
|
|
||||||
bottom={true}
|
return (
|
||||||
height={"90%"}
|
<Dialog
|
||||||
panDirection={PanningDirectionsEnum.DOWN}
|
bottom={true}
|
||||||
onDismiss={() => editEventProps.setIsVisible(false)}
|
height={"90%"}
|
||||||
containerStyle={{
|
panDirection={PanningDirectionsEnum.DOWN}
|
||||||
borderRadius: 10,
|
onDismiss={() => setIsVisible(false)}
|
||||||
backgroundColor: "white",
|
containerStyle={{
|
||||||
width: "100%",
|
borderRadius: 10,
|
||||||
alignSelf: "stretch",
|
backgroundColor: "white",
|
||||||
padding: 0,
|
width: "100%",
|
||||||
paddingTop: 4,
|
alignSelf: "stretch",
|
||||||
margin: 0,
|
padding: 0,
|
||||||
}}
|
paddingTop: 4,
|
||||||
visible={editEventProps.isVisible}
|
margin: 0,
|
||||||
>
|
|
||||||
<View row spread>
|
|
||||||
<Button
|
|
||||||
color="#05a8b6"
|
|
||||||
style={styles.topBtn}
|
|
||||||
label="Cancel"
|
|
||||||
onPress={() => {
|
|
||||||
editEventProps.setIsVisible(false);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<View marginT-12>
|
|
||||||
<DropModalIcon
|
|
||||||
onPress={() => {
|
|
||||||
editEventProps.setIsVisible(false);
|
|
||||||
}}
|
}}
|
||||||
/>
|
visible={isVisible}
|
||||||
</View>
|
>
|
||||||
<Button
|
<View row spread>
|
||||||
color="#05a8b6"
|
<Button
|
||||||
style={styles.topBtn}
|
color="#05a8b6"
|
||||||
label="Save"
|
style={styles.topBtn}
|
||||||
onPress={() => {
|
label="Cancel"
|
||||||
try {
|
onPress={() => {
|
||||||
if (event.id) {
|
setIsVisible(false);
|
||||||
updateEvent(event).then(() => editEventProps.setIsVisible(false));
|
}}
|
||||||
}
|
/>
|
||||||
} catch (error) {
|
<View marginT-12>
|
||||||
console.error(error);
|
<DropModalIcon
|
||||||
}
|
onPress={() => {
|
||||||
}}
|
setIsVisible(false);
|
||||||
/>
|
}}
|
||||||
</View>
|
/>
|
||||||
|
</View>
|
||||||
<TextField
|
<Button
|
||||||
placeholder="Edit event title"
|
color="#05a8b6"
|
||||||
value={event.title}
|
style={styles.topBtn}
|
||||||
onChangeText={(text) => {
|
label="Save"
|
||||||
setEvent((prevEvent) => ({
|
onPress={() => {
|
||||||
...prevEvent,
|
try {
|
||||||
title: text,
|
if (event.id) {
|
||||||
}));
|
updateEvent(event).then(() => setIsVisible(false));
|
||||||
}}
|
}
|
||||||
placeholderTextColor="#2d2d30"
|
} catch (error) {
|
||||||
text60R
|
console.error(error);
|
||||||
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, mode) => 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>
|
</View>
|
||||||
<DateTimePicker
|
|
||||||
text70
|
|
||||||
value={event.end}
|
|
||||||
minimumDate={event.start}
|
|
||||||
onChange={(date) => {
|
|
||||||
setEvent((prev) => ({ ...prev, end: date }));
|
|
||||||
}}
|
|
||||||
dateTimeFormatter={(date, mode) => 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>
|
<TextField
|
||||||
<Ionicons name="person-circle-outline" size={28} color="#919191" />
|
placeholder="Edit event title"
|
||||||
<Text text70R marginL-10>
|
value={event.title}
|
||||||
Assignees
|
onChangeText={(text) => {
|
||||||
</Text>
|
setEvent((prevEvent) => ({
|
||||||
<Button
|
...prevEvent!,
|
||||||
size={ButtonSize.small}
|
title: text,
|
||||||
paddingH-8
|
}));
|
||||||
iconSource={() => (
|
}}
|
||||||
<Ionicons name="add-outline" size={20} color="#ea156c" />
|
placeholderTextColor="#2d2d30"
|
||||||
)}
|
text60R
|
||||||
style={{
|
marginT-15
|
||||||
marginLeft: "auto",
|
marginL-30
|
||||||
borderRadius: 8,
|
/>
|
||||||
backgroundColor: "#ffe8f1",
|
<View style={styles.divider} marginT-8/>
|
||||||
borderColor: "#ea156c",
|
|
||||||
borderWidth: 1,
|
<View row spread marginB-10 marginL-30 centerV>
|
||||||
}}
|
<View row>
|
||||||
color="#ea156c"
|
<AntDesign name="clockcircleo" size={24} color="#919191"/>
|
||||||
label="Assign"
|
<Text text70 marginL-10>
|
||||||
/>
|
All day
|
||||||
</View>
|
</Text>
|
||||||
<View row marginH-13 marginT-13>
|
</View>
|
||||||
<View
|
<View right marginR-30>
|
||||||
marginL-30
|
<Switch
|
||||||
style={{
|
onColor={"#ea156c"}
|
||||||
aspectRatio: 1,
|
offColor={"#e1e1e2"}
|
||||||
width: 50,
|
marginL-10
|
||||||
backgroundColor: "red",
|
value={event.allDay}
|
||||||
borderRadius: 50,
|
onValueChange={(value) =>
|
||||||
}}
|
setEvent((prev) => ({...prev!, allDay: value}))
|
||||||
/>
|
}
|
||||||
<View
|
/>
|
||||||
marginL-30
|
</View>
|
||||||
style={{
|
</View>
|
||||||
aspectRatio: 1,
|
<View marginL-30 centerV>
|
||||||
width: 50,
|
<View row marginB-10 spread>
|
||||||
backgroundColor: "red",
|
<View row centerV>
|
||||||
borderRadius: 50,
|
<Feather name="calendar" size={25} color="#919191"/>
|
||||||
}}
|
<DateTimePicker
|
||||||
/>
|
value={event.start}
|
||||||
</View>
|
text70
|
||||||
<View style={styles.divider} />
|
marginL-8
|
||||||
<View marginH-30 marginB-0 row spread centerV>
|
maximumDate={event.end}
|
||||||
<View row centerV>
|
onChange={(date) => {
|
||||||
<ClockIcon />
|
setEvent((prev) => ({...prev!, start: date}));
|
||||||
<Text text70 marginL-10>
|
}}
|
||||||
Reminder
|
/>
|
||||||
</Text>
|
</View>
|
||||||
</View>
|
<DateTimePicker
|
||||||
<View>
|
text70
|
||||||
<Button
|
value={event.start}
|
||||||
size={ButtonSize.small}
|
onChange={(date) => {
|
||||||
paddingH-8
|
setEvent((prev) => ({...prev!, start: date}));
|
||||||
iconSource={() => (
|
}}
|
||||||
<Ionicons name="add-outline" size={20} color="#ea156c" />
|
maximumDate={event.end}
|
||||||
)}
|
dateTimeFormatter={(date) => date.toLocaleTimeString("en-us",
|
||||||
style={{
|
{
|
||||||
marginLeft: "auto",
|
hour: "numeric",
|
||||||
borderRadius: 8,
|
minute: "numeric"
|
||||||
backgroundColor: "#ffe8f1",
|
})}
|
||||||
borderColor: "#ea156c",
|
mode="time"
|
||||||
borderWidth: 1,
|
marginR-30
|
||||||
}}
|
/>
|
||||||
color="#ea156c"
|
</View>
|
||||||
label="Set Reminder"
|
|
||||||
/>
|
{!event.allDay && (
|
||||||
</View>
|
<View row marginB-10 spread>
|
||||||
</View>
|
<View row centerV>
|
||||||
<View style={styles.divider} />
|
<Feather name="calendar" size={25} color="#919191"/>
|
||||||
<View marginH-30 marginB-0 row spread centerV>
|
<DateTimePicker
|
||||||
<View row>
|
value={event.end}
|
||||||
<LockIcon />
|
minimumDate={event.start}
|
||||||
<Text text70 marginL-10>
|
text70
|
||||||
Mark as Private
|
marginL-8
|
||||||
</Text>
|
onChange={(date) => {
|
||||||
</View>
|
setEvent((prev) => ({...prev!, end: date}));
|
||||||
<View>
|
}}
|
||||||
<Switch
|
/>
|
||||||
onColor={"#ea156c"}
|
</View>
|
||||||
offColor={"#e1e1e2"}
|
<DateTimePicker
|
||||||
marginL-10
|
text70
|
||||||
value={event.private}
|
value={event.end}
|
||||||
onValueChange={(value) =>
|
minimumDate={event.start}
|
||||||
setEvent((prev) => ({ ...prev, private: value }))
|
onChange={(date) => {
|
||||||
}
|
setEvent((prev) => ({...prev!, end: date}));
|
||||||
/>
|
}}
|
||||||
</View>
|
dateTimeFormatter={(date) => date.toLocaleTimeString("en-us",
|
||||||
</View>
|
{
|
||||||
<View style={styles.divider} />
|
hour: "numeric",
|
||||||
<View marginH-30 marginB-0 row spread centerV>
|
minute: "numeric"
|
||||||
<View row centerV>
|
})}
|
||||||
<MenuIcon />
|
mode="time"
|
||||||
<Text text70 marginL-10>
|
marginR-30
|
||||||
Add Details
|
/>
|
||||||
</Text>
|
</View>
|
||||||
</View>
|
)}
|
||||||
<View></View>
|
</View>
|
||||||
</View>
|
<View style={styles.divider}/>
|
||||||
</Dialog>
|
|
||||||
);
|
<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;
|
export default EditEventDialog;
|
||||||
|
|
||||||
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",
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
width: "100%",
|
width: "100%",
|
||||||
},
|
},
|
||||||
buttonContainer: {
|
buttonContainer: {
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
bottom: 25,
|
bottom: 25,
|
||||||
width: "100%",
|
width: "100%",
|
||||||
},
|
},
|
||||||
button: {
|
button: {
|
||||||
backgroundColor: "rgb(253, 23, 117)",
|
backgroundColor: "rgb(253, 23, 117)",
|
||||||
paddingVertical: 20,
|
paddingVertical: 20,
|
||||||
},
|
},
|
||||||
topBtn: {
|
topBtn: {
|
||||||
backgroundColor: "white",
|
backgroundColor: "white",
|
||||||
color: "#05a8b6",
|
color: "#05a8b6",
|
||||||
},
|
},
|
||||||
rotateSwitch: {
|
rotateSwitch: {
|
||||||
marginLeft: 35,
|
marginLeft: 35,
|
||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
marginTop: 25,
|
marginTop: 25,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
63
components/pages/calendar/EventCalendar.tsx
Normal file
63
components/pages/calendar/EventCalendar.tsx
Normal 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,
|
||||||
|
},
|
||||||
|
});
|
||||||
41
components/pages/calendar/InnerCalendar.tsx
Normal file
41
components/pages/calendar/InnerCalendar.tsx
Normal 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/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -1,490 +1,482 @@
|
|||||||
import {
|
import {
|
||||||
Avatar,
|
Button,
|
||||||
Button,
|
ButtonSize,
|
||||||
ButtonSize,
|
Colors,
|
||||||
Colors,
|
DateTimePicker,
|
||||||
DateTimePicker,
|
LoaderScreen,
|
||||||
LoaderScreen,
|
Modal,
|
||||||
Modal,
|
Switch,
|
||||||
Picker,
|
Text,
|
||||||
Switch,
|
TextField,
|
||||||
Text,
|
TouchableOpacity,
|
||||||
TextField,
|
View,
|
||||||
TouchableOpacity,
|
|
||||||
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 { useState } from "react";
|
import {useState} from "react";
|
||||||
import {
|
import {AntDesign, Feather, Ionicons,} from "@expo/vector-icons";
|
||||||
AntDesign,
|
import {PickerMultiValue} from "react-native-ui-lib/src/components/picker/types";
|
||||||
Feather,
|
import {useCreateEvent} from "@/hooks/firebase/useCreateEvent";
|
||||||
Ionicons,
|
import {EventData} from "@/hooks/firebase/types/eventData";
|
||||||
MaterialIcons,
|
import {addHours} from "date-fns";
|
||||||
} 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 DropModalIcon from "@/assets/svgs/DropModalIcon";
|
import DropModalIcon from "@/assets/svgs/DropModalIcon";
|
||||||
import { CalendarEvent, useCalendarContext } from "@/contexts/CalendarContext";
|
import {StyleSheet} from "react-native";
|
||||||
import { repeatOptions } from "@/contexts/ToDosContext";
|
|
||||||
import { ImageBackground, 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 {selectedNewEventDateAtom} from "@/components/pages/calendar/atoms";
|
||||||
|
|
||||||
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 = () => {
|
||||||
show,
|
const insets = useSafeAreaInsets();
|
||||||
close,
|
|
||||||
initialDate,
|
|
||||||
}: {
|
|
||||||
show: boolean;
|
|
||||||
close: () => void;
|
|
||||||
initialDate?: Date;
|
|
||||||
}) => {
|
|
||||||
const { addEvent } = useCalendarContext();
|
|
||||||
const { user } = useAuthContext();
|
|
||||||
const insets = useSafeAreaInsets();
|
|
||||||
|
|
||||||
const [title, setTitle] = useState<string>("");
|
const [selectedNewEventDate, setSelectedNewEndDate] = useAtom(selectedNewEventDateAtom)
|
||||||
|
|
||||||
const [isAllDay, setIsAllDay] = useState(false);
|
const {show, close, initialDate} = {
|
||||||
const [isPrivate, setIsPrivate] = useState<boolean>(false);
|
show: !!selectedNewEventDate,
|
||||||
const [startTime, setStartTime] = useState(() => {
|
close: () => setSelectedNewEndDate(undefined),
|
||||||
const date = initialDate ?? new Date();
|
initialDate: selectedNewEventDate
|
||||||
date.setSeconds(0, 0);
|
|
||||||
return date;
|
|
||||||
});
|
|
||||||
const [endTime, setEndTime] = useState(() => {
|
|
||||||
const date = initialDate
|
|
||||||
? addHours(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 [repeatInterval, setRepeatInterval] = useState<PickerMultiValue>([]);
|
|
||||||
|
|
||||||
const { mutateAsync: createEvent, isLoading, isError } = useCreateEvent();
|
|
||||||
|
|
||||||
const formatDateTime = (date?: Date | string) => {
|
|
||||||
if (!date) return undefined;
|
|
||||||
return new Date(date).toLocaleDateString("en-US", {
|
|
||||||
weekday: "long",
|
|
||||||
month: "short",
|
|
||||||
day: "numeric",
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const combineDateAndTime = (date: Date, time: Date): Date => {
|
|
||||||
const combined = new Date(date);
|
|
||||||
combined.setHours(time.getHours());
|
|
||||||
combined.setMinutes(time.getMinutes());
|
|
||||||
combined.setSeconds(0);
|
|
||||||
combined.setMilliseconds(0);
|
|
||||||
return combined;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSave = async () => {
|
|
||||||
let finalStartDate: Date;
|
|
||||||
let finalEndDate: Date;
|
|
||||||
|
|
||||||
if (isAllDay) {
|
|
||||||
finalStartDate = new Date(startDate.setHours(0, 0, 0, 0));
|
|
||||||
finalEndDate = new Date(startDate.setHours(0, 0, 0, 0));
|
|
||||||
} else {
|
|
||||||
finalStartDate = combineDateAndTime(startDate, startTime);
|
|
||||||
finalEndDate = combineDateAndTime(endDate, endTime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const eventData: Partial<EventData> = {
|
const [title, setTitle] = useState<string>("");
|
||||||
title: title,
|
|
||||||
startDate: finalStartDate,
|
const [isAllDay, setIsAllDay] = useState(false);
|
||||||
endDate: finalEndDate,
|
const [isPrivate, setIsPrivate] = useState<boolean>(false);
|
||||||
allDay: isAllDay,
|
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)
|
||||||
|
: 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 [repeatInterval, setRepeatInterval] = useState<PickerMultiValue>([]);
|
||||||
|
|
||||||
|
const {mutateAsync: createEvent, isLoading, isError} = useCreateEvent();
|
||||||
|
|
||||||
|
if (!selectedNewEventDate) return null;
|
||||||
|
|
||||||
|
const formatDateTime = (date?: Date | string) => {
|
||||||
|
if (!date) return undefined;
|
||||||
|
return new Date(date).toLocaleDateString("en-US", {
|
||||||
|
weekday: "long",
|
||||||
|
month: "short",
|
||||||
|
day: "numeric",
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
await createEvent(eventData);
|
const combineDateAndTime = (date: Date, time: Date): Date => {
|
||||||
|
const combined = new Date(date);
|
||||||
|
combined.setHours(time.getHours());
|
||||||
|
combined.setMinutes(time.getMinutes());
|
||||||
|
combined.setSeconds(0);
|
||||||
|
combined.setMilliseconds(0);
|
||||||
|
return combined;
|
||||||
|
};
|
||||||
|
|
||||||
close();
|
const handleSave = async () => {
|
||||||
};
|
let finalStartDate: Date;
|
||||||
|
let finalEndDate: Date;
|
||||||
|
|
||||||
const getRepeatLabel = () => {
|
if (isAllDay) {
|
||||||
const selectedDays = repeatInterval;
|
finalStartDate = new Date(startDate.setHours(0, 0, 0, 0));
|
||||||
const allDays = [
|
finalEndDate = new Date(startDate.setHours(0, 0, 0, 0));
|
||||||
"sunday",
|
} else {
|
||||||
"monday",
|
finalStartDate = combineDateAndTime(startDate, startTime);
|
||||||
"tuesday",
|
finalEndDate = combineDateAndTime(endDate, endTime);
|
||||||
"wednesday",
|
}
|
||||||
"thursday",
|
|
||||||
"friday",
|
|
||||||
"saturday",
|
|
||||||
];
|
|
||||||
const workDays = ["monday", "tuesday", "wednesday", "thursday", "friday"];
|
|
||||||
|
|
||||||
const isEveryWorkDay = workDays.every((day) => selectedDays.includes(day));
|
const eventData: Partial<EventData> = {
|
||||||
|
title: title,
|
||||||
|
startDate: finalStartDate,
|
||||||
|
endDate: finalEndDate,
|
||||||
|
allDay: isAllDay,
|
||||||
|
};
|
||||||
|
|
||||||
const isEveryDay = allDays.every((day) => selectedDays.includes(day));
|
await createEvent(eventData);
|
||||||
|
|
||||||
if (isEveryDay) {
|
close();
|
||||||
return "Every day";
|
};
|
||||||
} else if (
|
|
||||||
isEveryWorkDay &&
|
const getRepeatLabel = () => {
|
||||||
!selectedDays.includes("saturday") &&
|
const selectedDays = repeatInterval;
|
||||||
!selectedDays.includes("sunday")
|
const allDays = [
|
||||||
) {
|
"sunday",
|
||||||
return "Every work day";
|
"monday",
|
||||||
} else {
|
"tuesday",
|
||||||
return selectedDays
|
"wednesday",
|
||||||
.map((item) => daysOfWeek.find((day) => day.value === item)?.label)
|
"thursday",
|
||||||
.join(", ");
|
"friday",
|
||||||
|
"saturday",
|
||||||
|
];
|
||||||
|
const workDays = ["monday", "tuesday", "wednesday", "thursday", "friday"];
|
||||||
|
|
||||||
|
const isEveryWorkDay = workDays.every((day) => selectedDays.includes(day));
|
||||||
|
|
||||||
|
const isEveryDay = allDays.every((day) => selectedDays.includes(day));
|
||||||
|
|
||||||
|
if (isEveryDay) {
|
||||||
|
return "Every day";
|
||||||
|
} else if (
|
||||||
|
isEveryWorkDay &&
|
||||||
|
!selectedDays.includes("saturday") &&
|
||||||
|
!selectedDays.includes("sunday")
|
||||||
|
) {
|
||||||
|
return "Every work day";
|
||||||
|
} else {
|
||||||
|
return selectedDays
|
||||||
|
.map((item) => daysOfWeek.find((day) => day.value === item)?.label)
|
||||||
|
.join(", ");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isLoading && !isError) {
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
visible={show}
|
||||||
|
animationType="slide"
|
||||||
|
onRequestClose={close}
|
||||||
|
transparent={false}
|
||||||
|
>
|
||||||
|
<LoaderScreen message={"Saving event..."} color={Colors.grey40}/>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
if (isLoading && !isError) {
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
visible={show}
|
visible={show}
|
||||||
animationType="slide"
|
animationType="slide"
|
||||||
onRequestClose={close}
|
onRequestClose={close}
|
||||||
transparent={false}
|
transparent={false}
|
||||||
>
|
|
||||||
<LoaderScreen message={"Saving event..."} color={Colors.grey40} />
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
visible={show}
|
|
||||||
animationType="slide"
|
|
||||||
onRequestClose={close}
|
|
||||||
transparent={false}
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
flex: 1,
|
|
||||||
backgroundColor: "#fff",
|
|
||||||
paddingTop: insets.top, // Safe area inset for top
|
|
||||||
paddingBottom: insets.bottom, // Safe area inset for bottom
|
|
||||||
paddingLeft: insets.left, // Safe area inset for left
|
|
||||||
paddingRight: insets.right, // Safe area inset for right
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
padding: 16,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<TouchableOpacity onPress={close}>
|
<View
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
color: "#05a8b6",
|
|
||||||
fontFamily: "PlusJakartaSans_400Regular",
|
|
||||||
fontSize: 16,
|
|
||||||
}}
|
|
||||||
text70
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<DropModalIcon onPress={close} />
|
|
||||||
<TouchableOpacity onPress={handleSave}>
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
color: "#05a8b6",
|
|
||||||
fontFamily: "PlusJakartaSans_400Regular",
|
|
||||||
fontSize: 16,
|
|
||||||
}}
|
|
||||||
text70
|
|
||||||
>
|
|
||||||
Save
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
<ScrollView>
|
|
||||||
<TextField
|
|
||||||
placeholder="Add event title"
|
|
||||||
value={title}
|
|
||||||
autoFocus
|
|
||||||
onChangeText={(text) => {
|
|
||||||
setTitle(text);
|
|
||||||
}}
|
|
||||||
placeholderTextColor="#2d2d30"
|
|
||||||
style={{ fontFamily: "Manrope_500Medium", fontSize: 22 }}
|
|
||||||
paddingT-15
|
|
||||||
paddingL-30
|
|
||||||
returnKeyType="next"
|
|
||||||
/>
|
|
||||||
<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" />
|
|
||||||
<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={(date) => setStartTime(date)}
|
|
||||||
maximumDate={endTime}
|
|
||||||
minuteInterval={5}
|
|
||||||
dateTimeFormatter={(date, mode) =>
|
|
||||||
date.toLocaleTimeString("en-us", {
|
|
||||||
hour: "numeric",
|
|
||||||
minute: "numeric",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
mode="time"
|
|
||||||
style={{
|
style={{
|
||||||
fontFamily: "PlusJakartaSans_500Medium",
|
flex: 1,
|
||||||
fontSize: 16,
|
backgroundColor: "#fff",
|
||||||
|
paddingTop: insets.top, // Safe area inset for top
|
||||||
|
paddingBottom: insets.bottom, // Safe area inset for bottom
|
||||||
|
paddingLeft: insets.left, // Safe area inset for left
|
||||||
|
paddingRight: insets.right, // Safe area inset for right
|
||||||
}}
|
}}
|
||||||
marginR-30
|
>
|
||||||
/>
|
<View
|
||||||
</View>
|
|
||||||
|
|
||||||
{!isAllDay && (
|
|
||||||
<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={{
|
style={{
|
||||||
fontFamily: "PlusJakartaSans_500Medium",
|
flexDirection: "row",
|
||||||
fontSize: 16,
|
justifyContent: "space-between",
|
||||||
|
padding: 16,
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
|
<TouchableOpacity onPress={close}>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
color: "#05a8b6",
|
||||||
|
fontFamily: "PlusJakartaSans_400Regular",
|
||||||
|
fontSize: 16,
|
||||||
|
}}
|
||||||
|
text70
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<DropModalIcon onPress={close}/>
|
||||||
|
<TouchableOpacity onPress={handleSave}>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
color: "#05a8b6",
|
||||||
|
fontFamily: "PlusJakartaSans_400Regular",
|
||||||
|
fontSize: 16,
|
||||||
|
}}
|
||||||
|
text70
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
<DateTimePicker
|
<ScrollView>
|
||||||
value={endTime}
|
<TextField
|
||||||
onChange={(date) => setEndTime(date)}
|
placeholder="Add event title"
|
||||||
minimumDate={startTime}
|
value={title}
|
||||||
minuteInterval={5}
|
autoFocus
|
||||||
dateTimeFormatter={(date, mode) =>
|
onChangeText={(text) => {
|
||||||
date.toLocaleTimeString("en-us", {
|
setTitle(text);
|
||||||
hour: "numeric",
|
}}
|
||||||
minute: "numeric",
|
placeholderTextColor="#2d2d30"
|
||||||
})
|
style={{fontFamily: "Manrope_500Medium", fontSize: 22}}
|
||||||
}
|
paddingT-15
|
||||||
mode="time"
|
paddingL-30
|
||||||
style={{
|
returnKeyType="next"
|
||||||
fontFamily: "PlusJakartaSans_500Medium",
|
/>
|
||||||
fontSize: 16,
|
<View style={styles.divider} marginT-8/>
|
||||||
}}
|
<View marginL-30 centerV>
|
||||||
marginR-30
|
<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={(date) => setStartTime(date)}
|
||||||
|
maximumDate={endTime}
|
||||||
|
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 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={(date) => setEndTime(date)}
|
||||||
|
minimumDate={startTime}
|
||||||
|
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>
|
||||||
|
<Ionicons name="person-circle-outline" size={28} color="#919191"/>
|
||||||
|
<Text
|
||||||
|
style={{fontFamily: "Manrope_600SemiBold", fontSize: 18}}
|
||||||
|
marginL-10
|
||||||
|
>
|
||||||
|
Attendees
|
||||||
|
</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="Add"
|
||||||
|
labelStyle={{fontFamily: "Manrope_600SemiBold", fontSize: 14}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View marginL-35>
|
||||||
|
<AssigneesDisplay/>
|
||||||
|
</View>
|
||||||
|
<View style={styles.divider}/>
|
||||||
|
<View marginH-30 marginB-0 row spread centerV>
|
||||||
|
<View row centerV>
|
||||||
|
<ClockIcon/>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
fontFamily: "Manrope_600SemiBold",
|
||||||
|
fontSize: 18,
|
||||||
|
}}
|
||||||
|
marginL-10
|
||||||
|
>
|
||||||
|
Reminders
|
||||||
|
</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,
|
||||||
|
}}
|
||||||
|
labelStyle={{fontFamily: "Manrope_600SemiBold", fontSize: 14}}
|
||||||
|
color="#ea156c"
|
||||||
|
label="Set Reminder"
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View style={styles.divider}/>
|
||||||
|
<View marginH-30 marginB-0 row spread centerV>
|
||||||
|
<View row>
|
||||||
|
<LockIcon/>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
fontFamily: "PlusJakartaSans_500Medium",
|
||||||
|
fontSize: 16,
|
||||||
|
}}
|
||||||
|
marginL-10
|
||||||
|
>
|
||||||
|
Mark as Private
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<View>
|
||||||
|
<Switch
|
||||||
|
onColor={"#ea156c"}
|
||||||
|
offColor={"#e1e1e2"}
|
||||||
|
marginL-10
|
||||||
|
value={isPrivate}
|
||||||
|
onValueChange={(value) => setIsPrivate(value)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View style={styles.divider}/>
|
||||||
|
<View marginH-30 marginB-0 row spread centerV>
|
||||||
|
<View row centerV>
|
||||||
|
<MenuIcon/>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
fontFamily: "PlusJakartaSans_500Medium",
|
||||||
|
fontSize: 16,
|
||||||
|
}}
|
||||||
|
marginL-10
|
||||||
|
>
|
||||||
|
Add Details
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<View></View>
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
<Button
|
||||||
|
marginH-30
|
||||||
|
marginB-15
|
||||||
|
label="Create event from image"
|
||||||
|
text70
|
||||||
|
style={{height: 47}}
|
||||||
|
labelStyle={{fontFamily: "PlusJakartaSans_500Medium", fontSize: 15}}
|
||||||
|
backgroundColor="#05a8b6"
|
||||||
|
iconSource={() => (
|
||||||
|
<View marginR-5>
|
||||||
|
<CameraIcon color="white"/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View style={styles.divider} />
|
|
||||||
|
|
||||||
<View marginH-30 marginB-10 row centerV>
|
|
||||||
<Ionicons name="person-circle-outline" size={28} color="#919191" />
|
|
||||||
<Text
|
|
||||||
style={{ fontFamily: "Manrope_600SemiBold", fontSize: 18 }}
|
|
||||||
marginL-10
|
|
||||||
>
|
|
||||||
Attendees
|
|
||||||
</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="Add"
|
|
||||||
labelStyle={{ fontFamily: "Manrope_600SemiBold", fontSize: 14 }}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<View marginL-35>
|
|
||||||
<AssigneesDisplay />
|
|
||||||
</View>
|
|
||||||
<View style={styles.divider} />
|
|
||||||
<View marginH-30 marginB-0 row spread centerV>
|
|
||||||
<View row centerV>
|
|
||||||
<ClockIcon />
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
fontFamily: "Manrope_600SemiBold",
|
|
||||||
fontSize: 18,
|
|
||||||
}}
|
|
||||||
marginL-10
|
|
||||||
>
|
|
||||||
Reminders
|
|
||||||
</Text>
|
|
||||||
</View>
|
</View>
|
||||||
<View>
|
</Modal>
|
||||||
<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,
|
|
||||||
}}
|
|
||||||
labelStyle={{ fontFamily: "Manrope_600SemiBold", fontSize: 14 }}
|
|
||||||
color="#ea156c"
|
|
||||||
label="Set Reminder"
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
<View style={styles.divider} />
|
|
||||||
<View marginH-30 marginB-0 row spread centerV>
|
|
||||||
<View row>
|
|
||||||
<LockIcon />
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
fontFamily: "PlusJakartaSans_500Medium",
|
|
||||||
fontSize: 16,
|
|
||||||
}}
|
|
||||||
marginL-10
|
|
||||||
>
|
|
||||||
Mark as Private
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View>
|
|
||||||
<Switch
|
|
||||||
onColor={"#ea156c"}
|
|
||||||
offColor={"#e1e1e2"}
|
|
||||||
marginL-10
|
|
||||||
value={isPrivate}
|
|
||||||
onValueChange={(value) => setIsPrivate(value)}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
<View style={styles.divider} />
|
|
||||||
<View marginH-30 marginB-0 row spread centerV>
|
|
||||||
<View row centerV>
|
|
||||||
<MenuIcon />
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
fontFamily: "PlusJakartaSans_500Medium",
|
|
||||||
fontSize: 16,
|
|
||||||
}}
|
|
||||||
marginL-10
|
|
||||||
>
|
|
||||||
Add Details
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View></View>
|
|
||||||
</View>
|
|
||||||
</ScrollView>
|
|
||||||
<Button
|
|
||||||
marginH-30
|
|
||||||
marginB-15
|
|
||||||
label="Create event from image"
|
|
||||||
text70
|
|
||||||
style={{ height: 47 }}
|
|
||||||
labelStyle={{ fontFamily: "PlusJakartaSans_500Medium", fontSize: 15 }}
|
|
||||||
backgroundColor="#05a8b6"
|
|
||||||
iconSource={() => (
|
|
||||||
<View marginR-5>
|
|
||||||
<CameraIcon color="white" />
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</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",
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
width: "100%",
|
width: "100%",
|
||||||
},
|
},
|
||||||
buttonContainer: {
|
buttonContainer: {
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
bottom: 25,
|
bottom: 25,
|
||||||
width: "100%",
|
width: "100%",
|
||||||
},
|
},
|
||||||
button: {
|
button: {
|
||||||
backgroundColor: "rgb(253, 23, 117)",
|
backgroundColor: "rgb(253, 23, 117)",
|
||||||
paddingVertical: 20,
|
paddingVertical: 20,
|
||||||
},
|
},
|
||||||
topBtn: {
|
topBtn: {
|
||||||
backgroundColor: "white",
|
backgroundColor: "white",
|
||||||
color: "#05a8b6",
|
color: "#05a8b6",
|
||||||
},
|
},
|
||||||
rotateSwitch: {
|
rotateSwitch: {
|
||||||
marginLeft: 35,
|
marginLeft: 35,
|
||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
marginTop: 25,
|
marginTop: 25,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
9
components/pages/calendar/atoms.ts
Normal file
9
components/pages/calendar/atoms.ts
Normal 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);
|
||||||
20
components/pages/calendar/constants.ts
Normal file
20
components/pages/calendar/constants.ts
Normal 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",
|
||||||
|
];
|
||||||
13
components/pages/calendar/interfaces.ts
Normal file
13
components/pages/calendar/interfaces.ts
Normal 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;
|
||||||
|
}
|
||||||
@ -1,17 +1,17 @@
|
|||||||
import {Button, ButtonSize, Dialog, Text, TextField, View} from "react-native-ui-lib";
|
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 {useSignIn} from "@/hooks/firebase/useSignIn";
|
||||||
import {StyleSheet} from "react-native";
|
import {StyleSheet} from "react-native";
|
||||||
import Toast from 'react-native-toast-message';
|
import Toast from 'react-native-toast-message';
|
||||||
import {useLoginWithQrCode} from "@/hooks/firebase/useLoginWithQrCode";
|
import {useLoginWithQrCode} from "@/hooks/firebase/useLoginWithQrCode";
|
||||||
import {Camera, CameraView} from 'expo-camera';
|
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 [email, setEmail] = useState<string>("");
|
||||||
const [password, setPassword] = useState<string>("");
|
const [password, setPassword] = useState<string>("");
|
||||||
const [hasPermission, setHasPermission] = useState<boolean | null>(null);
|
const [hasPermission, setHasPermission] = useState<boolean | null>(null);
|
||||||
const [scanned, setScanned] = useState<boolean>(false);
|
|
||||||
const [showCameraDialog, setShowCameraDialog] = useState<boolean>(false);
|
const [showCameraDialog, setShowCameraDialog] = useState<boolean>(false);
|
||||||
|
|
||||||
const {mutateAsync: signIn, error, isError} = useSignIn();
|
const {mutateAsync: signIn, error, isError} = useSignIn();
|
||||||
@ -19,7 +19,7 @@ const SignInPage = ({setTab}: { setTab: React.Dispatch<React.SetStateAction<"re
|
|||||||
|
|
||||||
const handleSignIn = async () => {
|
const handleSignIn = async () => {
|
||||||
await signIn({email, password});
|
await signIn({email, password});
|
||||||
if(!isError) {
|
if (!isError) {
|
||||||
Toast.show({
|
Toast.show({
|
||||||
type: "success",
|
type: "success",
|
||||||
text1: "Login successful!"
|
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);
|
setShowCameraDialog(false);
|
||||||
try {
|
try {
|
||||||
await signInWithQrCode({ userId: data });
|
await signInWithQrCode({userId: data});
|
||||||
Toast.show({
|
Toast.show({
|
||||||
type: "success",
|
type: "success",
|
||||||
text1: "Login successful with QR code!"
|
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 getCameraPermissions = async (callback: () => void) => {
|
||||||
const { status } = await Camera.requestCameraPermissionsAsync();
|
const {status} = await Camera.requestCameraPermissionsAsync();
|
||||||
setHasPermission(status === 'granted');
|
setHasPermission(status === 'granted');
|
||||||
if(status === 'granted') {
|
if (status === 'granted') {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -83,12 +83,12 @@ const SignInPage = ({setTab}: { setTab: React.Dispatch<React.SetStateAction<"re
|
|||||||
label="Login with a QR Code"
|
label="Login with a QR Code"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
getCameraPermissions(() => setShowCameraDialog(true));
|
getCameraPermissions(() => setShowCameraDialog(true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
style={{marginBottom: 20}}
|
style={{marginBottom: 20}}
|
||||||
backgroundColor="#fd1775"
|
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>
|
<View row centerH marginB-5 gap-5>
|
||||||
<Text text70>
|
<Text text70>
|
||||||
@ -131,7 +131,7 @@ const SignInPage = ({setTab}: { setTab: React.Dispatch<React.SetStateAction<"re
|
|||||||
bottom
|
bottom
|
||||||
width="100%"
|
width="100%"
|
||||||
height="70%"
|
height="70%"
|
||||||
containerStyle={{ padding: 0 }}
|
containerStyle={{padding: 0}}
|
||||||
>
|
>
|
||||||
{hasPermission === null ? (
|
{hasPermission === null ? (
|
||||||
<Text>Requesting camera permissions...</Text>
|
<Text>Requesting camera permissions...</Text>
|
||||||
@ -139,7 +139,7 @@ const SignInPage = ({setTab}: { setTab: React.Dispatch<React.SetStateAction<"re
|
|||||||
<Text>No access to camera</Text>
|
<Text>No access to camera</Text>
|
||||||
) : (
|
) : (
|
||||||
<CameraView
|
<CameraView
|
||||||
style={{ flex: 1 }}
|
style={{flex: 1}}
|
||||||
onBarcodeScanned={handleQrCodeScanned}
|
onBarcodeScanned={handleQrCodeScanned}
|
||||||
barcodeScannerSettings={{
|
barcodeScannerSettings={{
|
||||||
barcodeTypes: ["qr"],
|
barcodeTypes: ["qr"],
|
||||||
@ -150,7 +150,7 @@ const SignInPage = ({setTab}: { setTab: React.Dispatch<React.SetStateAction<"re
|
|||||||
label="Cancel"
|
label="Cancel"
|
||||||
onPress={() => setShowCameraDialog(false)}
|
onPress={() => setShowCameraDialog(false)}
|
||||||
backgroundColor="#fd1775"
|
backgroundColor="#fd1775"
|
||||||
style={{ margin: 10 }}
|
style={{margin: 10}}
|
||||||
/>
|
/>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@ -1,118 +1,118 @@
|
|||||||
import { View, Text, Button } from "react-native-ui-lib";
|
import {Button, Text, View} from "react-native-ui-lib";
|
||||||
import React, { useState } from "react";
|
import React, {useState} from "react";
|
||||||
import { StyleSheet } from "react-native";
|
import {StyleSheet} from "react-native";
|
||||||
import { Entypo, Ionicons, Octicons } from "@expo/vector-icons";
|
import {Octicons} from "@expo/vector-icons";
|
||||||
import CalendarSettingsPage from "./CalendarSettingsPage";
|
import CalendarSettingsPage from "./CalendarSettingsPage";
|
||||||
import ChoreRewardSettings from "./ChoreRewardSettings";
|
import ChoreRewardSettings from "./ChoreRewardSettings";
|
||||||
import UserSettings from "./UserSettings";
|
import UserSettings from "./UserSettings";
|
||||||
import { AuthContextProvider } from "@/contexts/AuthContext";
|
|
||||||
import ProfileIcon from "@/assets/svgs/ProfileIcon";
|
import ProfileIcon from "@/assets/svgs/ProfileIcon";
|
||||||
import CalendarIcon from "@/assets/svgs/CalendarIcon";
|
import CalendarIcon from "@/assets/svgs/CalendarIcon";
|
||||||
import PrivacyPolicyIcon from "@/assets/svgs/PrivacyPolicyIcon";
|
import PrivacyPolicyIcon from "@/assets/svgs/PrivacyPolicyIcon";
|
||||||
import ArrowRightIcon from "@/assets/svgs/ArrowRightIcon";
|
import ArrowRightIcon from "@/assets/svgs/ArrowRightIcon";
|
||||||
|
|
||||||
const pageIndex = {
|
const pageIndex = {
|
||||||
main: 0,
|
main: 0,
|
||||||
user: 1,
|
user: 1,
|
||||||
calendar: 2,
|
calendar: 2,
|
||||||
chore: 3,
|
chore: 3,
|
||||||
policy: 4,
|
policy: 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
const SettingsPage = () => {
|
const SettingsPage = () => {
|
||||||
const [selectedPage, setSelectedPage] = useState<number>(0);
|
const [selectedPage, setSelectedPage] = useState<number>(0);
|
||||||
return (
|
return (
|
||||||
<View flexG>
|
<View flexG>
|
||||||
{selectedPage == 0 && (
|
{selectedPage == 0 && (
|
||||||
<View flexG centerH marginH-30 marginT-30>
|
<View flexG centerH marginH-30 marginT-30>
|
||||||
<Button
|
<Button
|
||||||
backgroundColor="white"
|
backgroundColor="white"
|
||||||
style={styles.mainBtn}
|
style={styles.mainBtn}
|
||||||
children={
|
children={
|
||||||
<View row centerV width={"100%"}>
|
<View row centerV width={"100%"}>
|
||||||
<ProfileIcon style={{ marginRight: 10 }} color="#07b9c8" />
|
<ProfileIcon style={{marginRight: 10}} color="#07b9c8"/>
|
||||||
<Text style={styles.label} color="#07b8c7">
|
<Text style={styles.label} color="#07b8c7">
|
||||||
Manage My Profile
|
Manage My Profile
|
||||||
</Text>
|
</Text>
|
||||||
<ArrowRightIcon style={{ marginLeft: "auto" }} />
|
<ArrowRightIcon style={{marginLeft: "auto"}}/>
|
||||||
</View>
|
</View>
|
||||||
}
|
}
|
||||||
onPress={() => setSelectedPage(pageIndex.user)}
|
onPress={() => setSelectedPage(pageIndex.user)}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
backgroundColor="white"
|
backgroundColor="white"
|
||||||
style={styles.mainBtn}
|
style={styles.mainBtn}
|
||||||
children={
|
children={
|
||||||
<View row centerV width={"100%"}>
|
<View row centerV width={"100%"}>
|
||||||
<CalendarIcon style={{ marginRight: 10 }} />
|
<CalendarIcon style={{marginRight: 10}}/>
|
||||||
<Text style={styles.label} color="#fd1775">
|
<Text style={styles.label} color="#fd1775">
|
||||||
Calendar Settings
|
Calendar Settings
|
||||||
</Text>
|
</Text>
|
||||||
<ArrowRightIcon style={{ marginLeft: "auto" }} />
|
<ArrowRightIcon style={{marginLeft: "auto"}}/>
|
||||||
</View>
|
</View>
|
||||||
}
|
}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
setSelectedPage(pageIndex.calendar);
|
setSelectedPage(pageIndex.calendar);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
backgroundColor="white"
|
backgroundColor="white"
|
||||||
style={styles.mainBtn}
|
style={styles.mainBtn}
|
||||||
children={
|
children={
|
||||||
<View row centerV width={"100%"}>
|
<View row centerV width={"100%"}>
|
||||||
<Octicons
|
<Octicons
|
||||||
name="gear"
|
name="gear"
|
||||||
size={24}
|
size={24}
|
||||||
color="#ff9900"
|
color="#ff9900"
|
||||||
style={{ marginRight: 10 }}
|
style={{marginRight: 10}}
|
||||||
/>
|
/>
|
||||||
<Text style={styles.label} color="#ff9900">
|
<Text style={styles.label} color="#ff9900">
|
||||||
To-Do Reward Settings
|
To-Do Reward Settings
|
||||||
</Text>
|
</Text>
|
||||||
<ArrowRightIcon style={{ marginLeft: "auto" }} />
|
<ArrowRightIcon style={{marginLeft: "auto"}}/>
|
||||||
</View>
|
</View>
|
||||||
}
|
}
|
||||||
onPress={() => setSelectedPage(pageIndex.chore)}
|
onPress={() => setSelectedPage(pageIndex.chore)}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
backgroundColor="white"
|
backgroundColor="white"
|
||||||
style={styles.mainBtn}
|
style={styles.mainBtn}
|
||||||
children={
|
children={
|
||||||
<View row centerV width={"100%"}>
|
<View row centerV width={"100%"}>
|
||||||
<PrivacyPolicyIcon style={{ marginRight: 10 }} />
|
<PrivacyPolicyIcon style={{marginRight: 10}}/>
|
||||||
<Text style={styles.label} color="#6c645b">
|
<Text style={styles.label} color="#6c645b">
|
||||||
Cally Privacy Policy
|
Cally Privacy Policy
|
||||||
</Text>
|
</Text>
|
||||||
<ArrowRightIcon style={{ marginLeft: "auto" }} />
|
<ArrowRightIcon style={{marginLeft: "auto"}}/>
|
||||||
</View>
|
</View>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
{selectedPage == pageIndex.calendar && (
|
||||||
|
<CalendarSettingsPage setSelectedPage={setSelectedPage}/>
|
||||||
|
)}
|
||||||
|
{selectedPage == pageIndex.chore && (
|
||||||
|
<ChoreRewardSettings setSelectedPage={setSelectedPage}/>
|
||||||
|
)}
|
||||||
|
{selectedPage == pageIndex.user && (
|
||||||
|
<UserSettings setSelectedPage={setSelectedPage}/>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
)}
|
);
|
||||||
{selectedPage == pageIndex.calendar && (
|
|
||||||
<CalendarSettingsPage setSelectedPage={setSelectedPage} />
|
|
||||||
)}
|
|
||||||
{selectedPage == pageIndex.chore && (
|
|
||||||
<ChoreRewardSettings setSelectedPage={setSelectedPage} />
|
|
||||||
)}
|
|
||||||
{selectedPage == pageIndex.user && (
|
|
||||||
<UserSettings setSelectedPage={setSelectedPage} />
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SettingsPage;
|
export default SettingsPage;
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
mainBtn: {
|
mainBtn: {
|
||||||
width: 311,
|
width: 311,
|
||||||
justifyContent: "flex-start",
|
justifyContent: "flex-start",
|
||||||
marginBottom: 20,
|
marginBottom: 20,
|
||||||
height: 57.61,
|
height: 57.61,
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
fontFamily: "Poppins_400Regular",
|
fontFamily: "Poppins_400Regular",
|
||||||
fontSize: 14.71,
|
fontSize: 14.71,
|
||||||
textAlignVertical: "center",
|
textAlignVertical: "center",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -99,6 +99,7 @@ export const AuthContextProvider: FC<{ children: ReactNode }> = ({children}) =>
|
|||||||
const onAuthStateChangedHandler = async (authUser: FirebaseAuthTypes.User | null) => {
|
const onAuthStateChangedHandler = async (authUser: FirebaseAuthTypes.User | null) => {
|
||||||
setUser(authUser);
|
setUser(authUser);
|
||||||
|
|
||||||
|
|
||||||
if (authUser) {
|
if (authUser) {
|
||||||
await refreshProfileData(authUser);
|
await refreshProfileData(authUser);
|
||||||
const pushToken = await registerForPushNotificationsAsync();
|
const pushToken = await registerForPushNotificationsAsync();
|
||||||
|
|||||||
@ -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;
|
|
||||||
};
|
|
||||||
@ -10,5 +10,5 @@ export interface EventData {
|
|||||||
surpriseEvent?: boolean,
|
surpriseEvent?: boolean,
|
||||||
notes?: string,
|
notes?: string,
|
||||||
reminders?: string[]
|
reminders?: string[]
|
||||||
id?: string,
|
id?: string | number,
|
||||||
}
|
}
|
||||||
@ -2,9 +2,12 @@ import {useQuery} from "react-query";
|
|||||||
import firestore from "@react-native-firebase/firestore";
|
import firestore from "@react-native-firebase/firestore";
|
||||||
import {useAuthContext} from "@/contexts/AuthContext";
|
import {useAuthContext} from "@/contexts/AuthContext";
|
||||||
import {colorMap} from "@/contexts/SettingsContext";
|
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 { user, profileData } = useAuthContext();
|
||||||
|
const isFamilyView = useAtomValue(isFamilyViewAtom)
|
||||||
|
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["events", user?.uid, isFamilyView],
|
queryKey: ["events", user?.uid, isFamilyView],
|
||||||
|
|||||||
@ -1,10 +1,8 @@
|
|||||||
import {useAuthContext} from "@/contexts/AuthContext";
|
|
||||||
import {useMutation, useQueryClient} from "react-query";
|
import {useMutation, useQueryClient} from "react-query";
|
||||||
import firestore from "@react-native-firebase/firestore";
|
import firestore from "@react-native-firebase/firestore";
|
||||||
import {EventData} from "@/hooks/firebase/types/eventData";
|
import {EventData} from "@/hooks/firebase/types/eventData";
|
||||||
|
|
||||||
export const useUpdateEvent = () => {
|
export const useUpdateEvent = () => {
|
||||||
const {user: currentUser} = useAuthContext()
|
|
||||||
const queryClients = useQueryClient()
|
const queryClients = useQueryClient()
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
@ -13,7 +11,7 @@ export const useUpdateEvent = () => {
|
|||||||
try {
|
try {
|
||||||
await firestore()
|
await firestore()
|
||||||
.collection("Events")
|
.collection("Events")
|
||||||
.doc(eventData.id)
|
.doc(`${eventData.id}`)
|
||||||
.update(eventData);
|
.update(eventData);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
|
|||||||
@ -441,7 +441,7 @@
|
|||||||
);
|
);
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
|
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.cally.app;
|
PRODUCT_BUNDLE_IDENTIFIER = com.cally.app;
|
||||||
PRODUCT_NAME = "CallyFamilyPlanner";
|
PRODUCT_NAME = CallyFamilyPlanner;
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "cally/cally-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "cally/cally-Bridging-Header.h";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
@ -472,7 +472,7 @@
|
|||||||
);
|
);
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
|
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.cally.app;
|
PRODUCT_BUNDLE_IDENTIFIER = com.cally.app;
|
||||||
PRODUCT_NAME = "CallyFamilyPlanner";
|
PRODUCT_NAME = CallyFamilyPlanner;
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "cally/cally-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "cally/cally-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
|||||||
Reference in New Issue
Block a user