Merge branch 'refs/heads/dev'

# Conflicts:
#	app.json
#	ios/cally/Info.plist
This commit is contained in:
Milan Paunovic
2024-10-12 10:26:30 +02:00
49 changed files with 1208 additions and 462 deletions

View File

@ -14,9 +14,14 @@
</intent> </intent>
</queries> </queries>
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme"> <application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme">
<meta-data android:name="expo.modules.updates.ENABLED" android:value="false"/> <meta-data android:name="com.google.firebase.messaging.default_notification_channel_id" android:value="default"/>
<meta-data android:name="com.google.firebase.messaging.default_notification_color" android:resource="@color/notification_icon_color"/>
<meta-data android:name="expo.modules.notifications.default_notification_color" android:resource="@color/notification_icon_color"/>
<meta-data android:name="expo.modules.updates.ENABLED" android:value="true"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/> <meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/> <meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATE_URL" android:value="https://u.expo.dev/bdb8c57b-25bb-4d36-b3b8-5b09c5092f52"/>
<meta-data android:name="expo.modules.updates.EXPO_RUNTIME_VERSION" android:value="@string/expo_runtime_version"/>
<activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true" android:screenOrientation="portrait"> <activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true" android:screenOrientation="portrait">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>

View File

@ -3,4 +3,5 @@
<color name="iconBackground">#ffffff</color> <color name="iconBackground">#ffffff</color>
<color name="colorPrimary">#023c69</color> <color name="colorPrimary">#023c69</color>
<color name="colorPrimaryDark">#ffffff</color> <color name="colorPrimaryDark">#ffffff</color>
<color name="notification_icon_color">#ffffff</color>
</resources> </resources>

View File

@ -3,4 +3,5 @@
<string name="expo_splash_screen_resize_mode" translatable="false">contain</string> <string name="expo_splash_screen_resize_mode" translatable="false">contain</string>
<string name="expo_splash_screen_status_bar_translucent" translatable="false">false</string> <string name="expo_splash_screen_status_bar_translucent" translatable="false">false</string>
<string name="expo_system_ui_user_interface_style" translatable="false">light</string> <string name="expo_system_ui_user_interface_style" translatable="false">light</string>
<string name="expo_runtime_version">1.0.0</string>
</resources> </resources>

View File

@ -25,6 +25,10 @@
}, },
"package": "com.cally.app", "package": "com.cally.app",
"googleServicesFile": "./android/app/google-services.json", "googleServicesFile": "./android/app/google-services.json",
"permissions": [
"android.permission.CAMERA",
"android.permission.RECORD_AUDIO"
]
}, },
"web": { "web": {
"bundler": "metro", "bundler": "metro",
@ -51,6 +55,13 @@
"microphonePermission": "Allow $(PRODUCT_NAME) to access your microphone", "microphonePermission": "Allow $(PRODUCT_NAME) to access your microphone",
"recordAudioAndroid": true "recordAudioAndroid": true
} }
],
[
"expo-notifications",
{
"color": "#ffffff",
"defaultChannel": "default"
}
] ]
], ],
"experiments": { "experiments": {
@ -63,6 +74,10 @@
"eas": { "eas": {
"projectId": "bdb8c57b-25bb-4d36-b3b8-5b09c5092f52" "projectId": "bdb8c57b-25bb-4d36-b3b8-5b09c5092f52"
} }
},
"runtimeVersion": "1.0.0",
"updates": {
"url": "https://u.expo.dev/bdb8c57b-25bb-4d36-b3b8-5b09c5092f52"
} }
} }
} }

View File

@ -18,6 +18,11 @@ import {
} from "@expo/vector-icons"; } from "@expo/vector-icons";
import MenuIcon from "@/assets/svgs/MenuIcon"; import MenuIcon from "@/assets/svgs/MenuIcon";
import { router } from "expo-router"; import { router } from "expo-router";
import NavGroceryIcon from "@/assets/svgs/NavGroceryIcon";
import NavToDosIcon from "@/assets/svgs/NavToDosIcon";
import NavBrainDumpIcon from "@/assets/svgs/NavBrainDumpIcon";
import NavCalendarIcon from "@/assets/svgs/NavCalendarIcon";
import NavSettingsIcon from "@/assets/svgs/NavSettingsIcon";
export default function TabLayout() { export default function TabLayout() {
const { mutateAsync: signOut } = useSignOut(); const { mutateAsync: signOut } = useSignOut();
@ -38,7 +43,7 @@ export default function TabLayout() {
return ( return (
<DrawerContentScrollView {...props} style={{ height: "100%" }}> <DrawerContentScrollView {...props} style={{ height: "100%" }}>
<View centerH centerV margin-30> <View centerH centerV margin-30>
<Text text50>Welcome to Kali</Text> <Text text50>Welcome to Cally</Text>
</View> </View>
<View <View
style={{ style={{
@ -52,26 +57,14 @@ export default function TabLayout() {
color="rgb(7, 184, 199)" color="rgb(7, 184, 199)"
bgColor={"rgb(231, 248, 250)"} bgColor={"rgb(231, 248, 250)"}
pressFunc={() => props.navigation.navigate("calendar")} pressFunc={() => props.navigation.navigate("calendar")}
icon={ icon={<NavCalendarIcon />}
<Feather
name="calendar"
size={30}
color="rgb(7, 184, 199)"
/>
}
/> />
<DrawerButton <DrawerButton
color="#50be0c" color="#50be0c"
title={"Groceries"} title={"Groceries"}
bgColor={"#eef9e7"} bgColor={"#eef9e7"}
pressFunc={() => props.navigation.navigate("grocery")} pressFunc={() => props.navigation.navigate("grocery")}
icon={ icon={<NavGroceryIcon />}
<MaterialCommunityIcons
name="food-apple-outline"
size={30}
color="#50be0c"
/>
}
/> />
</View> </View>
<View style={{ flex: 1 }}> <View style={{ flex: 1 }}>
@ -90,19 +83,17 @@ export default function TabLayout() {
/>*/} />*/}
<DrawerButton <DrawerButton
color="#8005eb" color="#8005eb"
title={"To Dos"} title={"To Do's"}
bgColor={"#f3e6fd"} bgColor={"#f3e6fd"}
pressFunc={() => props.navigation.navigate("todos")} pressFunc={() => props.navigation.navigate("todos")}
icon={ icon={<NavToDosIcon />}
<AntDesign name="checkcircleo" size={30} color="#8005eb" />
}
/> />
<DrawerButton <DrawerButton
color="#e0ca03" color="#e0ca03"
title={"Brain Dump"} title={"Brain Dump"}
bgColor={"#fffacb"} bgColor={"#fffacb"}
pressFunc={() => props.navigation.navigate("brain_dump")} pressFunc={() => props.navigation.navigate("brain_dump")}
icon={<AntDesign name="book" size={30} color="#e0ca03" />} icon={<NavBrainDumpIcon />}
/> />
{/*<DrawerItem label="Logout" onPress={() => signOut()} />*/} {/*<DrawerItem label="Logout" onPress={() => signOut()} />*/}
</View> </View>
@ -120,7 +111,7 @@ export default function TabLayout() {
centerV centerV
centerH centerH
> >
<Octicons name="gear" size={30} color="#6c645b" /> <NavSettingsIcon />
</View> </View>
)} )}
backgroundColor="white" backgroundColor="white"
@ -142,7 +133,7 @@ export default function TabLayout() {
borderWidth: 2, borderWidth: 2,
borderColor: "#fd1775", borderColor: "#fd1775",
}} }}
label="Sign out of Kali" label="Sign out of Cally"
color="#fd1775" color="#fd1775"
onPress={() => signOut()} onPress={() => signOut()}
/> />
@ -197,7 +188,7 @@ export default function TabLayout() {
name="todos" name="todos"
options={{ options={{
drawerLabel: "To-Do", drawerLabel: "To-Do",
title: "To-Do", title: "To-Do's",
}} }}
/> />
</Drawer> </Drawer>

View File

@ -1,30 +1,16 @@
import {DarkTheme, DefaultTheme, ThemeProvider} from '@react-navigation/native'; import {DefaultTheme, ThemeProvider} from '@react-navigation/native';
import {useFonts} from 'expo-font'; import {useFonts} from 'expo-font';
import {Stack} from 'expo-router'; import {Stack} from 'expo-router';
import * as SplashScreen from 'expo-splash-screen'; import * as SplashScreen from 'expo-splash-screen';
import {useEffect} from 'react'; import {useEffect} from 'react';
import 'react-native-reanimated'; import 'react-native-reanimated';
import {useColorScheme} from '@/hooks/useColorScheme';
import {AuthContextProvider} from "@/contexts/AuthContext"; import {AuthContextProvider} from "@/contexts/AuthContext";
import functions from "@react-native-firebase/functions";
import firestore from "@react-native-firebase/firestore";
import auth from "@react-native-firebase/auth";
import {QueryClient, QueryClientProvider} from "react-query"; import {QueryClient, QueryClientProvider} from "react-query";
import {Toast} from "react-native-ui-lib"; import {Toast} from "react-native-ui-lib";
// Prevent the splash screen from auto-hiding before asset loading is complete.
SplashScreen.preventAutoHideAsync(); SplashScreen.preventAutoHideAsync();
const queryClient = new QueryClient() const queryClient = new QueryClient();
if (__DEV__) {
functions().useEmulator('localhost', 5001);
firestore().useEmulator("localhost", 5471);
auth().useEmulator("http://localhost:9099");
}
export default function RootLayout() { export default function RootLayout() {
const [loaded] = useFonts({ const [loaded] = useFonts({
@ -50,7 +36,7 @@ export default function RootLayout() {
<Stack.Screen name="(unauth)" options={{headerShown: false}}/> <Stack.Screen name="(unauth)" options={{headerShown: false}}/>
<Stack.Screen name="+not-found"/> <Stack.Screen name="+not-found"/>
</Stack> </Stack>
<Toast /> <Toast/>
</ThemeProvider> </ThemeProvider>
</AuthContextProvider> </AuthContextProvider>
</QueryClientProvider> </QueryClientProvider>

View File

@ -0,0 +1,19 @@
import * as React from "react"
import Svg, { Path, SvgProps } from "react-native-svg"
const CalendarIcon: React.FC<SvgProps> = (props) => (
<Svg
width={props.width || 21}
height={props.height || 21}
fill="none"
{...props}
>
<Path
stroke={props.color || "#FD1775"}
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={1.455}
d="M1.413 7.359h18.294m-4.065 4.067-10.164-.002m3.388 4.066H5.478m0-14.23v2.033m10.164-2.032v2.032M4.665 19.555h11.79c1.138 0 1.708 0 2.142-.222.383-.194.694-.505.889-.888.221-.435.221-1.004.221-2.142V6.546c0-1.139 0-1.708-.221-2.143a2.032 2.032 0 0 0-.889-.888c-.434-.222-1.004-.222-2.142-.222H4.665c-1.138 0-1.707 0-2.142.222a2.033 2.033 0 0 0-.888.888c-.222.435-.222 1.004-.222 2.143v9.757c0 1.138 0 1.707.222 2.142.195.383.505.694.888.888.435.222 1.004.222 2.142.222Z"
/>
</Svg>
)
export default CalendarIcon

View File

@ -0,0 +1,36 @@
import * as React from "react"
import Svg, { Path, SvgProps } from "react-native-svg"
const NavBrainDumpIcon: React.FC<SvgProps> = (props) => (
<Svg
width={22}
height={28}
fill="none"
{...props}
>
<Path
stroke="#F90"
strokeLinecap="round"
strokeLinejoin="round"
strokeMiterlimit={10}
strokeWidth={1.509}
d="M21.1.859H3.994C2.284.859.976 2.167.976 3.877c0 1.71 1.308 3.019 3.018 3.019H21.1V27.02"
/>
<Path
stroke="#F90"
strokeLinecap="round"
strokeLinejoin="round"
strokeMiterlimit={10}
strokeWidth={1.509}
d="M21.1 27.021H3.994c-1.71 0-3.018-1.308-3.018-3.018V3.878M21.097 3.878H3.991"
/>
<Path
stroke="#F90"
strokeLinecap="round"
strokeLinejoin="round"
strokeMiterlimit={10}
strokeWidth={1.509}
d="M6.007 6.897v12.075l3.019-1.006 3.018 1.006V6.897"
/>
</Svg>
)
export default NavBrainDumpIcon

View File

@ -0,0 +1,19 @@
import * as React from "react"
import Svg, { Path, SvgProps } from "react-native-svg"
const NavCalendarIcon: React.FC<SvgProps> = (props) => (
<Svg
width={28}
height={28}
fill="none"
{...props}
>
<Path
stroke="#07B8C7"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2.124}
d="M1.825 10.075h25.043m-5.565 5.567L7.39 15.64m4.638 5.566H7.39m0-19.478V4.51m13.913-2.782V4.51M6.277 26.77h16.139c1.558 0 2.337 0 2.933-.303.523-.267.949-.692 1.216-1.216.303-.595.303-1.375.303-2.933V8.962c0-1.558 0-2.337-.303-2.933a2.782 2.782 0 0 0-1.216-1.216c-.596-.303-1.375-.303-2.933-.303H6.277c-1.558 0-2.337 0-2.933.303-.523.267-.949.693-1.216 1.216-.303.596-.303 1.375-.303 2.933v13.356c0 1.558 0 2.338.303 2.933.267.523.693.95 1.216 1.216.596.303 1.375.303 2.933.303Z"
/>
</Svg>
)
export default NavCalendarIcon

View File

@ -0,0 +1,20 @@
import * as React from "react";
import Svg, { Path, SvgProps } from "react-native-svg";
const NavGroceryIcon: React.FC<SvgProps> = (props) => (
<Svg
width={29}
height={32}
fill="none"
{...props}
>
<Path
fill="#50BE0C"
d="M27.8 12c-.888-2.27-2.662-4.14-4.983-4.956-2.518-.883-5.143-.382-7.636.307-.047-1.362.068-2.728.262-4.076.11-.761.634-2.335-.668-2.389-1.081-.045-1.084 1.402-1.19 2.14-.203 1.416-.323 2.85-.284 4.281-.599-.1-1.19-.322-1.787-.446a13.485 13.485 0 0 0-2.704-.3c-1.752 0-3.48.465-4.922 1.479C1.09 10.01 0 13.446.046 16.747c.052 3.575 1.263 6.99 3.3 9.905.899 1.288 1.932 2.522 3.21 3.451 1.241.903 2.628 1.325 4.133 1.54.64.093 1.29.096 1.927-.009.557-.09 1.078-.325 1.63-.419.417-.07 1.005.258 1.408.348a6.297 6.297 0 0 0 2.096.107c2.563-.292 4.594-1.449 6.28-3.387 3.71-4.264 5.921-10.791 3.77-16.284Zm-1.69 8.743c-.474 1.663-1.225 3.182-2.195 4.608-.86 1.263-1.855 2.514-3.131 3.377-1.172.79-2.76 1.223-4.173 1.093-.719-.065-1.373-.41-2.087-.478-.8-.076-1.538.352-2.318.459-3.064.416-5.485-1.665-7.146-3.972-2.04-2.831-3.249-6.214-3.134-9.732.106-3.223 1.643-6.55 4.998-7.432 1.678-.44 3.446-.188 5.099.238.715.183 1.454.48 2.196.52.572.066 1.2-.196 1.74-.344 3.233-.89 6.873-1.105 9.124 1.855 2.08 2.732 1.925 6.666 1.027 9.808Z"
/>
<Path
fill="#50BE0C"
d="M12 17.966c-.778.285-1.977.71-2.432 1.454.11-.183.221-.363-.002.002-.222.365-.113.186-.001.003-.668 1.099.272 2.53 1.547 2.347 1.258-.18 1.378-1.419 1.364-2.443a8.822 8.822 0 0 0-.095-1.282c-.043-.264-.157-.164-.382-.08ZM18.096 18.917c-.435-.304-.919-.542-1.405-.752-.118-.05-.698-.375-.832-.297.018-.01.036-.021 0 0-.036.023-.02.012-.001.001-.11.07-.096.609-.104.716a9.003 9.003 0 0 0-.01 1.534c.1 1.117 1.049 2.082 2.228 1.517 1.165-.558 1.095-2.04.124-2.719ZM12.223 3.815C12.343.28 8.347-.271 6.922.432c.371 4.075 3.48 4.164 5.301 3.383Z"
/>
</Svg>
);
export default NavGroceryIcon;

View File

@ -0,0 +1,28 @@
import * as React from "react";
import Svg, { Path, SvgProps } from "react-native-svg";
const NavSettingsIcon: React.FC<SvgProps> = (props) => (
<Svg
width={26}
height={28}
fill="none"
{...props}
>
<Path
stroke="#6C645B"
strokeLinecap="round"
strokeLinejoin="round"
strokeMiterlimit={10}
strokeWidth={1.69}
d="M12.972 17.893a3.887 3.887 0 1 0 0-7.774 3.887 3.887 0 0 0 0 7.774Z"
/>
<Path
stroke="#6C645B"
strokeLinecap="round"
strokeLinejoin="round"
strokeMiterlimit={10}
strokeWidth={1.69}
d="m24.398 8.531-.972-1.683a1.943 1.943 0 0 0-2.655-.711l-.511.295c-1.944 1.121-4.373-.28-4.373-2.525v-.59c0-1.074-.87-1.944-1.944-1.944H12c-1.073 0-1.943.87-1.943 1.943v.591c0 2.244-2.43 3.647-4.373 2.525l-.511-.295a1.943 1.943 0 0 0-2.655.711l-.972 1.683a1.943 1.943 0 0 0 .711 2.655l.511.296c1.944 1.122 1.944 3.927 0 5.049l-.51.295a1.943 1.943 0 0 0-.712 2.655l.972 1.684a1.943 1.943 0 0 0 2.655.71l.51-.294c1.944-1.123 4.374.28 4.374 2.524v.591c0 1.074.87 1.944 1.944 1.944h1.943c1.074 0 1.944-.87 1.944-1.944v-.59c0-2.245 2.43-3.648 4.373-2.526l.511.296c.93.536 2.119.219 2.655-.711l.972-1.684a1.943 1.943 0 0 0-.712-2.655l-.51-.295c-1.944-1.122-1.944-3.927 0-5.05l.51-.295a1.942 1.942 0 0 0 .712-2.655Z"
/>
</Svg>
);
export default NavSettingsIcon;

View File

@ -0,0 +1,18 @@
import * as React from "react"
import Svg, { Path, SvgProps } from "react-native-svg"
const NavToDosIcon: React.FC<SvgProps> = (props) => (
<Svg
width={30}
height={30}
fill="none"
{...props}
>
<Path
fill="#8005EB"
stroke="#8005EB"
strokeWidth={0.5}
d="M15.038 29.649c-7.953 0-14.4-6.447-14.4-14.4s6.447-14.4 14.4-14.4 14.4 6.447 14.4 14.4-6.447 14.4-14.4 14.4Zm0-1.44c7.157 0 12.96-5.802 12.96-12.96 0-7.158-5.803-12.96-12.96-12.96-7.158 0-12.96 5.802-12.96 12.96 0 7.158 5.802 12.96 12.96 12.96Zm4.53-17.07a.72.72 0 1 1 1.019 1.019l-7.2 7.2a.72.72 0 0 1-1.018 0l-2.88-2.88a.72.72 0 1 1 1.018-1.018l2.37 2.37 6.692-6.69Z"
/>
</Svg>
)
export default NavToDosIcon

View File

@ -0,0 +1,23 @@
import * as React from "react"
import Svg, { Path, SvgProps } from "react-native-svg"
const PrivacyPolicyIcon: React.FC<SvgProps> = (props) => (
<Svg
width={props.width ||20}
height={props.height ||22}
fill="none"
{...props}
>
<Path
stroke={props.color || "#6C645B"}
strokeWidth={1.2}
d="M1.413 8.656c0-3.7 0-5.55 1.15-6.7 1.15-1.15 3-1.15 6.7-1.15h1.962c3.7 0 5.55 0 6.7 1.15 1.149 1.15 1.149 3 1.149 6.7v3.925c0 3.7 0 5.55-1.15 6.7-1.15 1.149-3 1.149-6.7 1.149H9.263c-3.7 0-5.55 0-6.7-1.15-1.149-1.15-1.149-3-1.149-6.7V8.657Z"
/>
<Path
stroke={props.color || "#6C645B"}
strokeLinecap="round"
strokeWidth={1.2}
d="M6.324 10.617h7.85M6.324 6.694h7.85M6.324 14.541h4.906"
/>
</Svg>
)
export default PrivacyPolicyIcon

View File

@ -0,0 +1,26 @@
import * as React from "react"
import Svg, { Path, SvgProps } from "react-native-svg"
const ProfileIcon: React.FC<SvgProps> = (props) => (
<Svg
width={21}
height={21}
fill="none"
{...props}
>
<Path
stroke={props.color || "red"}
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={1.414}
d="M10.956 11.538a.91.91 0 0 0-.226 0 3.083 3.083 0 0 1-2.98-3.084 3.089 3.089 0 0 1 3.093-3.093 3.09 3.09 0 0 1 .113 6.176ZM17.199 17.762a9.368 9.368 0 0 1-6.356 2.47 9.368 9.368 0 0 1-6.356-2.47c.095-.886.66-1.754 1.67-2.433 2.583-1.716 6.808-1.716 9.373 0 1.009.68 1.575 1.547 1.669 2.433Z"
/>
<Path
stroke={props.color || "red"}
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={1.414}
d="M10.843 20.233a9.43 9.43 0 1 0 0-18.86 9.43 9.43 0 0 0 0 18.86Z"
/>
</Svg>
)
export default ProfileIcon

View File

@ -1,4 +1,5 @@
export async function fetchGoogleCalendarEvents(token, startDate, endDate) { export async function fetchGoogleCalendarEvents(token, startDate, endDate) {
console.log(token);
const response = await fetch( const response = await fetch(
`https://www.googleapis.com/calendar/v3/calendars/primary/events?single_events=true&time_min=${startDate}&time_max=${endDate}`, `https://www.googleapis.com/calendar/v3/calendars/primary/events?single_events=true&time_min=${startDate}&time_max=${endDate}`,
{ {
@ -9,6 +10,7 @@ export async function fetchGoogleCalendarEvents(token, startDate, endDate) {
); );
const data = await response.json(); const data = await response.json();
console.log(data);
const googleEvents = []; const googleEvents = [];
data.items?.forEach((item) => { data.items?.forEach((item) => {
let isAllDay = false; let isAllDay = false;

View File

@ -47,7 +47,7 @@ export default function CalendarPage() {
}, },
}); });
const [isFamilyView, setIsFamilyView] = useState<boolean>(true); const [isFamilyView, setIsFamilyView] = useState<boolean>(false);
const [calendarHeight, setCalendarHeight] = useState(0); const [calendarHeight, setCalendarHeight] = useState(0);
const [mode, setMode] = useState<"week" | "month" | "day">("week"); const [mode, setMode] = useState<"week" | "month" | "day">("week");
const [selectedDate, setSelectedDate] = useState<Date>(new Date()); const [selectedDate, setSelectedDate] = useState<Date>(new Date());

View File

@ -1,7 +1,5 @@
import { View, Text, Button, Switch } from "react-native-ui-lib"; import { View, Text, Button, Switch } from "react-native-ui-lib";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import PointsSlider from "@/components/shared/PointsSlider";
import { repeatOptions, useToDosContext } from "@/contexts/ToDosContext";
import { Feather, AntDesign, Ionicons } from "@expo/vector-icons"; import { Feather, AntDesign, Ionicons } from "@expo/vector-icons";
import { import {
Dialog, Dialog,
@ -13,11 +11,11 @@ import {
import { PanningDirectionsEnum } from "react-native-ui-lib/src/incubator/panView"; import { PanningDirectionsEnum } from "react-native-ui-lib/src/incubator/panView";
import { StyleSheet } from "react-native"; import { StyleSheet } from "react-native";
import DropModalIcon from "@/assets/svgs/DropModalIcon"; import DropModalIcon from "@/assets/svgs/DropModalIcon";
import { CalendarEvent, useCalendarContext } from "@/contexts/CalendarContext"; 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 { eventCellCss } from "react-native-big-calendar"; import { useUpdateEvent } from "@/hooks/firebase/useUpdateEvent";
interface IEditEventDialog { interface IEditEventDialog {
event: CalendarEvent; event: CalendarEvent;
@ -25,9 +23,10 @@ interface IEditEventDialog {
setIsVisible: (value: boolean) => void; setIsVisible: (value: boolean) => void;
} }
const EditEventDialog = (editEventProps: IEditEventDialog) => { const EditEventDialog = (editEventProps: IEditEventDialog) => {
const { updateEvent } = useCalendarContext();
const [event, setEvent] = useState<CalendarEvent>(editEventProps.event); const [event, setEvent] = useState<CalendarEvent>(editEventProps.event);
const { mutateAsync: updateEvent } = useUpdateEvent();
useEffect(() => { useEffect(() => {
setEvent(editEventProps.event); setEvent(editEventProps.event);
}, [editEventProps.isVisible]); }, [editEventProps.isVisible]);
@ -72,8 +71,7 @@ const EditEventDialog = (editEventProps: IEditEventDialog) => {
onPress={() => { onPress={() => {
try { try {
if (event.id) { if (event.id) {
updateEvent(event, event.id); updateEvent(event).then(() => editEventProps.setIsVisible(false));
editEventProps.setIsVisible(false);
} }
} catch (error) { } catch (error) {
console.error(error); console.error(error);

View File

@ -81,8 +81,9 @@ export const ManuallyAddEventModal = ({
const { mutateAsync: createEvent, isLoading, isError } = useCreateEvent(); const { mutateAsync: createEvent, isLoading, isError } = useCreateEvent();
const formatDateTime = (date: Date) => { const formatDateTime = (date?: Date | string) => {
return date.toLocaleDateString("en-US", { if(!date) return undefined
return new Date(date).toLocaleDateString("en-US", {
weekday: "long", weekday: "long",
month: "short", month: "short",
day: "numeric", day: "numeric",
@ -98,7 +99,7 @@ export const ManuallyAddEventModal = ({
return combined; return combined;
}; };
const handleSave = () => { const handleSave = async () => {
let finalStartDate: Date; let finalStartDate: Date;
let finalEndDate: Date; let finalEndDate: Date;
@ -113,15 +114,14 @@ export const ManuallyAddEventModal = ({
finalEndDate = combineDateAndTime(endDate, endTime); finalEndDate = combineDateAndTime(endDate, endTime);
} }
const eventData: CalendarEvent = { const eventData: Partial<EventData> = {
title: title, title: title,
start: finalStartDate, startDate: finalStartDate,
end: finalEndDate, endDate: finalEndDate,
allDay: isAllDay, allDay: isAllDay,
private: isPrivate,
}; };
addEvent(eventData); await createEvent(eventData);
close(); close();
}; };

View File

@ -1,5 +1,5 @@
import { StyleSheet } from "react-native"; import { StyleSheet } from "react-native";
import React, { useState } from "react"; import React from "react";
import { import {
Dialog, Dialog,
Text, Text,
@ -11,9 +11,9 @@ import {
} from "react-native-ui-lib"; } from "react-native-ui-lib";
import { import {
GroceryFrequency, GroceryFrequency,
IGrocery,
useGroceryContext, useGroceryContext,
} from "@/contexts/GroceryContext"; } from "@/contexts/GroceryContext";
import { IGrocery } from "@/hooks/firebase/types/groceryData";
interface EditGroceryFrequencyProps { interface EditGroceryFrequencyProps {
visible: boolean; visible: boolean;
onClose: () => void; onClose: () => void;
@ -42,7 +42,7 @@ const EditGroceryFrequency = (props: EditGroceryFrequencyProps) => {
<Switch <Switch
value={props.item.recurring} value={props.item.recurring}
onValueChange={(value) => onValueChange={(value) =>
updateGroceryItem(props.item.id, { recurring: value }) updateGroceryItem({id: props.item.id, recurring: value})
} }
onColor={"lime"} onColor={"lime"}
/> />
@ -56,8 +56,9 @@ const EditGroceryFrequency = (props: EditGroceryFrequencyProps) => {
const selectedFrequency = const selectedFrequency =
GroceryFrequency[item as keyof typeof GroceryFrequency]; GroceryFrequency[item as keyof typeof GroceryFrequency];
if (selectedFrequency) { if (selectedFrequency) {
updateGroceryItem(props.item.id, { updateGroceryItem({
frequency: selectedFrequency, id: props.item.id,
frequency: selectedFrequency,
}); });
} else { } else {
console.error("Invalid frequency selected"); console.error("Invalid frequency selected");

View File

@ -1,53 +1,58 @@
import { import {Checkbox, Text, TouchableOpacity, View,} from "react-native-ui-lib";
View, import React, {useEffect, useState} from "react";
Text, import {AntDesign} from "@expo/vector-icons";
Button, import {GroceryCategory, useGroceryContext,} from "@/contexts/GroceryContext";
TouchableOpacity,
Checkbox,
ButtonSize,
} from "react-native-ui-lib";
import React, { useEffect, useState } from "react";
import { ProfileType, useAuthContext } from "@/contexts/AuthContext";
import { MaterialCommunityIcons, AntDesign } from "@expo/vector-icons";
import { ListItem } from "react-native-ui-lib";
import {
GroceryCategory,
IGrocery,
useGroceryContext,
} from "@/contexts/GroceryContext";
import EditGroceryFrequency from "./EditGroceryFrequency"; import EditGroceryFrequency from "./EditGroceryFrequency";
import EditGroceryItem from "./EditGroceryItem"; import EditGroceryItem from "./EditGroceryItem";
import { StyleSheet } from "react-native"; import {StyleSheet} from "react-native";
import {IGrocery} from "@/hooks/firebase/types/groceryData";
import firestore from "@react-native-firebase/firestore";
import {UserProfile} from "@/hooks/firebase/types/profileTypes";
const GroceryItem = ({ const GroceryItem = ({
item, item,
handleItemApproved, handleItemApproved,
}: { }: {
item: IGrocery; item: IGrocery;
handleItemApproved: (id: number, changes: Partial<IGrocery>) => void; handleItemApproved: (id: string, changes: Partial<IGrocery>) => void;
}) => { }) => {
const { updateGroceryItem, groceries } = useGroceryContext(); const { updateGroceryItem } = useGroceryContext();
const { profileType } = useAuthContext();
const [openFreqEdit, setOpenFreqEdit] = useState<boolean>(false); const [openFreqEdit, setOpenFreqEdit] = useState<boolean>(false);
const [isEditingTitle, setIsEditingTitle] = useState<boolean>(false); const [isEditingTitle, setIsEditingTitle] = useState<boolean>(false);
const [newTitle, setNewTitle] = useState<string>(""); const [newTitle, setNewTitle] = useState<string>("");
const [category, setCategory] = useState<GroceryCategory>( const [category, setCategory] = useState<GroceryCategory>(
GroceryCategory.None GroceryCategory.None
); );
const [itemCreator, setItemCreator] = useState<UserProfile>(null);
const handleTitleChange = (newTitle: string) => { const handleTitleChange = (newTitle: string) => {
updateGroceryItem(item.id, { title: newTitle }); updateGroceryItem({id: item?.id, title: newTitle});
}; };
const handleCategoryChange = (newCategory: GroceryCategory) => { const handleCategoryChange = (newCategory: GroceryCategory) => {
updateGroceryItem(item.id, { category: newCategory }); updateGroceryItem({id: item?.id, category: newCategory});
}; };
useEffect(() => { useEffect(() => {
setNewTitle(item.title); setNewTitle(item.title);
getItemCreator(item?.creatorId);
}, []); }, []);
const getItemCreator = async (uid: string | undefined) => {
if (uid) {
const documentSnapshot = await firestore()
.collection("Profiles")
.doc(uid)
.get();
if (documentSnapshot.exists) {
setItemCreator(documentSnapshot.data() as UserProfile)
}
}
}
return ( return (
<View <View
key={item.id} key={item.id}
@ -114,7 +119,7 @@ const GroceryItem = ({
containerStyle={styles.checkbox} containerStyle={styles.checkbox}
hitSlop={20} hitSlop={20}
onValueChange={() => onValueChange={() =>
updateGroceryItem(item.id, { bought: !item.bought }) updateGroceryItem({ id: item.id, bought: !item.bought })
} }
/> />
)} )}
@ -135,7 +140,7 @@ const GroceryItem = ({
}} }}
></View> ></View>
<Text color="#858585" text70> <Text color="#858585" text70>
Requested by Austin Requested by {itemCreator?.firstName}
</Text> </Text>
</View> </View>
</View> </View>

View File

@ -1,18 +1,13 @@
import { FlatList, StyleSheet } from "react-native"; import {FlatList, StyleSheet} from "react-native";
import React, { RefObject, useEffect, useState } from "react"; import React, {useEffect, useState} from "react";
import { View, Text, ListItem, Button, TextField, TextFieldRef } from "react-native-ui-lib"; import {Button, Text, View} from "react-native-ui-lib";
import GroceryItem from "./GroceryItem"; import GroceryItem from "./GroceryItem";
import { import {GroceryCategory, GroceryFrequency, useGroceryContext,} from "@/contexts/GroceryContext";
IGrocery,
GroceryCategory,
GroceryFrequency,
useGroceryContext,
} from "@/contexts/GroceryContext";
import HeaderTemplate from "@/components/shared/HeaderTemplate"; import HeaderTemplate from "@/components/shared/HeaderTemplate";
import CategoryDropdown from "./CategoryDropdown"; import {AntDesign, MaterialIcons} from "@expo/vector-icons";
import { AntDesign, MaterialIcons } from "@expo/vector-icons";
import EditGroceryItem from "./EditGroceryItem"; import EditGroceryItem from "./EditGroceryItem";
import { ProfileType, useAuthContext } from "@/contexts/AuthContext"; import {ProfileType, useAuthContext} from "@/contexts/AuthContext";
import {IGrocery} from "@/hooks/firebase/types/groceryData";
const GroceryList = () => { const GroceryList = () => {
const { const {
@ -24,10 +19,10 @@ const GroceryList = () => {
} = useGroceryContext(); } = useGroceryContext();
const { profileData } = useAuthContext(); const { profileData } = useAuthContext();
const [approvedGroceries, setapprovedGroceries] = useState<IGrocery[]>( const [approvedGroceries, setapprovedGroceries] = useState<IGrocery[]>(
groceries.filter((item) => item.approved === true) groceries?.filter((item) => item.approved === true)
); );
const [pendingGroceries, setPendingGroceries] = useState<IGrocery[]>( const [pendingGroceries, setPendingGroceries] = useState<IGrocery[]>(
groceries.filter((item) => item.approved !== true) groceries?.filter((item) => item.approved !== true)
); );
const [category, setCategory] = useState<GroceryCategory>( const [category, setCategory] = useState<GroceryCategory>(
GroceryCategory.Bakery GroceryCategory.Bakery
@ -39,7 +34,7 @@ const GroceryList = () => {
const [approvedVisible, setApprovedVisible] = useState<boolean>(true); const [approvedVisible, setApprovedVisible] = useState<boolean>(true);
// Group approved groceries by category // Group approved groceries by category
const approvedGroceriesByCategory = approvedGroceries.reduce( const approvedGroceriesByCategory = approvedGroceries?.reduce(
(groups: any, item: IGrocery) => { (groups: any, item: IGrocery) => {
const category = item.category || "Uncategorized"; const category = item.category || "Uncategorized";
if (!groups[category]) { if (!groups[category]) {
@ -55,10 +50,10 @@ const GroceryList = () => {
if (submit) { if (submit) {
if (title?.length > 2 && title?.length <= 25) { if (title?.length > 2 && title?.length <= 25) {
addGrocery({ addGrocery({
id: 0, id: "",
title: title, title: title,
category: category, category: category,
approved: profileData?.userType === ProfileType.PARENT ? true : false, approved: profileData?.userType === ProfileType.PARENT,
recurring: false, recurring: false,
frequency: GroceryFrequency.Never, frequency: GroceryFrequency.Never,
bought: false, bought: false,
@ -76,8 +71,8 @@ const GroceryList = () => {
}, [category]); }, [category]);
useEffect(() => { useEffect(() => {
setapprovedGroceries(groceries.filter((item) => item.approved === true)); setapprovedGroceries(groceries?.filter((item) => item.approved === true));
setPendingGroceries(groceries.filter((item) => item.approved !== true)); setPendingGroceries(groceries?.filter((item) => item.approved !== true));
}, [groceries]); }, [groceries]);
return ( return (
@ -93,8 +88,8 @@ const GroceryList = () => {
style={{ borderRadius: 50 }} style={{ borderRadius: 50 }}
> >
<Text text70BL color="#46a80a"> <Text text70BL color="#46a80a">
{approvedGroceries.length} list{" "} {approvedGroceries?.length} list{" "}
{approvedGroceries.length === 1 ? ( {approvedGroceries?.length === 1 ? (
<Text text70BL color="#46a80a"> <Text text70BL color="#46a80a">
item item
</Text> </Text>
@ -111,7 +106,7 @@ const GroceryList = () => {
style={{ borderRadius: 50 }} style={{ borderRadius: 50 }}
> >
<Text text70BL color="#e28800"> <Text text70BL color="#e28800">
{pendingGroceries.length} pending {pendingGroceries?.length} pending
</Text> </Text>
</View> </View>
<Button <Button
@ -161,18 +156,18 @@ const GroceryList = () => {
}} }}
> >
<Text text70 center color="#e28800"> <Text text70 center color="#e28800">
{pendingGroceries.length.toString()} {pendingGroceries?.length.toString()}
</Text> </Text>
</View> </View>
</View> </View>
{pendingGroceries.length > 0 {pendingGroceries?.length > 0
? pendingVisible && ( ? pendingVisible && (
<FlatList <FlatList
data={pendingGroceries} data={pendingGroceries}
renderItem={({ item }) => ( renderItem={({ item }) => (
<GroceryItem <GroceryItem
item={item} item={item}
handleItemApproved={updateGroceryItem} handleItemApproved={(id, changes) => updateGroceryItem({...changes, id: id})}
/> />
)} )}
keyExtractor={(item) => item.id.toString()} keyExtractor={(item) => item.id.toString()}
@ -217,7 +212,7 @@ const GroceryList = () => {
}} }}
> >
<Text text70 center color="#46a80a"> <Text text70 center color="#46a80a">
{approvedGroceries.length.toString()} {approvedGroceries?.length.toString()}
</Text> </Text>
</View> </View>
</View> </View>
@ -234,7 +229,7 @@ const GroceryList = () => {
)} )}
{/* Render Approved Groceries Grouped by Category */} {/* Render Approved Groceries Grouped by Category */}
{approvedGroceries.length > 0 {approvedGroceries?.length > 0
? approvedVisible && ( ? approvedVisible && (
<FlatList <FlatList
data={Object.keys(approvedGroceriesByCategory)} data={Object.keys(approvedGroceriesByCategory)}
@ -250,7 +245,7 @@ const GroceryList = () => {
<GroceryItem <GroceryItem
key={grocery.id} key={grocery.id}
item={grocery} item={grocery}
handleItemApproved={updateGroceryItem} handleItemApproved={(id, changes) => updateGroceryItem({...changes, id: id})}
/> />
) )
)} )}

View File

@ -31,7 +31,7 @@ const SignUpPage = ({setTab}: { setTab: React.Dispatch<React.SetStateAction<"re
return ( return (
<View padding-10 height={"100%"} flexG> <View padding-10 height={"100%"} flexG>
<Text text30 center> <Text text30 center>
Get started with Kali Get started with Cally
</Text> </Text>
<Text center>Please enter your details.</Text> <Text center>Please enter your details.</Text>
<TextField <TextField

View File

@ -28,21 +28,28 @@ const OnboardingFlow = () => {
width={10} width={10}
/> />
), ),
title: <Text text30>Welcome to Kali</Text>, title: <Text text30>Welcome to Cally</Text>,
subtitle: ( subtitle: (
<View paddingB-250 marginH-20 spread> <View paddingB-250 marginH-20 spread>
<Text text50R>Lightening Mental Loads, One Family at a Time</Text> <Text text50R>Lightening Mental Loads, One Family at a Time</Text>
<Button <Button
label="Continue" label="Continue"
style={{ backgroundColor: "#fd1775" }} style={{ backgroundColor: "#fd1775" }}
onPress={() => onboardingRef.current.goToPage(1, true)} onPress={() => onboardingRef?.current?.goToPage(1, true)}
/> />
</View> </View>
), ),
}, },
{ {
backgroundColor: "#f9f8f7", backgroundColor: "#f9f8f7",
title: <Text>Get started with Kali</Text>, title: <Text>Get started with Cally</Text>,
image: (
<Image
source={require("../../../assets/images/splash-clock.png")}
height={10}
width={10}
/>
),
subtitle: ( subtitle: (
<View <View
style={{ style={{

View File

@ -1,5 +1,5 @@
import { AntDesign, Ionicons } from "@expo/vector-icons"; import { AntDesign, Ionicons } from "@expo/vector-icons";
import React, { useCallback, useState } from "react"; import React, {useCallback, useEffect, useState} from "react";
import { Button, Checkbox, Text, View } from "react-native-ui-lib"; import { Button, Checkbox, Text, View } from "react-native-ui-lib";
import { ScrollView, StyleSheet } from "react-native"; import { ScrollView, StyleSheet } from "react-native";
import { colorMap } from "@/contexts/SettingsContext"; import { colorMap } from "@/contexts/SettingsContext";
@ -9,21 +9,19 @@ import { fetchMicrosoftCalendarEvents } from "@/calendar-integration/microsoft-c
import { useCreateEventFromProvider } from "@/hooks/firebase/useCreateEvent"; import { useCreateEventFromProvider } from "@/hooks/firebase/useCreateEvent";
import { useAuthContext } from "@/contexts/AuthContext"; import { useAuthContext } from "@/contexts/AuthContext";
import { useUpdateUserData } from "@/hooks/firebase/useUpdateUserData"; import { useUpdateUserData } from "@/hooks/firebase/useUpdateUserData";
import { GoogleSignin } from "@react-native-google-signin/google-signin";
import * as AuthSession from "expo-auth-session";
import debounce from "debounce"; import debounce from "debounce";
import AppleIcon from "@/assets/svgs/AppleIcon"; import AppleIcon from "@/assets/svgs/AppleIcon";
import GoogleIcon from "@/assets/svgs/GoogleIcon"; import GoogleIcon from "@/assets/svgs/GoogleIcon";
import OutlookIcon from "@/assets/svgs/OutlookIcon"; import OutlookIcon from "@/assets/svgs/OutlookIcon";
import * as AuthSession from "expo-auth-session";
import * as Google from "expo-auth-session/providers/google";
import * as WebBrowser from "expo-web-browser";
GoogleSignin.configure({ const googleConfig = {
webClientId: androidClientId: "406146460310-2u67ab2nbhu23trp8auho1fq4om29fc0.apps.googleusercontent.com",
"406146460310-hjadmfa1gg4ptaouira5rkhu0djlo5ut.apps.googleusercontent.com", iosClientId: "406146460310-2u67ab2nbhu23trp8auho1fq4om29fc0.apps.googleusercontent.com",
scopes: ["profile", "email"], // Note: add calendar scope webClientId: "406146460310-2u67ab2nbhu23trp8auho1fq4om29fc0.apps.googleusercontent.com",
}); scopes: ["email", "profile", "https://www.googleapis.com/auth/calendar.events.owned"]
const GoogleLogin = async () => {
return await GoogleSignin.signIn();
}; };
const microsoftConfig = { const microsoftConfig = {
@ -57,7 +55,15 @@ const CalendarSettingsPage = (props: {
const { mutateAsync: createEventFromProvider } = useCreateEventFromProvider(); const { mutateAsync: createEventFromProvider } = useCreateEventFromProvider();
const { mutateAsync: updateUserData } = useUpdateUserData(); const { mutateAsync: updateUserData } = useUpdateUserData();
WebBrowser.maybeCompleteAuthSession()
const [request, response, promptAsync] = Google.useAuthRequest(googleConfig);
useEffect(() => {
signInWithGoogle();
}, [response]);
const fetchAndSaveGoogleEvents = () => { const fetchAndSaveGoogleEvents = () => {
console.log("fetch");
const timeMin = new Date(new Date().setHours(0, 0, 0, 0)); const timeMin = new Date(new Date().setHours(0, 0, 0, 0));
const timeMax = new Date( const timeMax = new Date(
new Date(new Date().setHours(0, 0, 0, 0)).setDate(timeMin.getDate() + 30) new Date(new Date().setHours(0, 0, 0, 0)).setDate(timeMin.getDate() + 30)
@ -94,19 +100,16 @@ const CalendarSettingsPage = (props: {
}); });
}; };
const handleGoogleLogin = async () => { const signInWithGoogle = async () => {
try { try {
const response = await GoogleLogin(); // Attempt to retrieve user information from AsyncStorage
if (response) { if (response?.type === 'success') {
const googleUserData = response.data; console.log(response.authentication)
let idToken = googleUserData?.idToken; await updateUserData({newUserData: {googleToken: response.authentication?.accessToken}})
if (idToken) {
await updateUserData({ newUserData: { googleToken: idToken } });
}
} }
} catch (apiError) { } catch (error) {
console.log(apiError || "Something went wrong"); // Handle any errors that occur during AsyncStorage retrieval or other operations
console.error("Error retrieving user data from AsyncStorage:", error);
} }
}; };
@ -290,7 +293,7 @@ const CalendarSettingsPage = (props: {
</Text> </Text>
<Button <Button
onPress={handleGoogleLogin} onPress={() => promptAsync()}
label="Connect Google" label="Connect Google"
iconSource={() => ( iconSource={() => (
<View marginR-15> <View marginR-15>
@ -329,50 +332,31 @@ const CalendarSettingsPage = (props: {
Connected Calendars Connected Calendars
</Text> </Text>
<View style={styles.card}> <View style={styles.card}>
<Text text70>Calendars</Text>
<View style={{ marginTop: 20 }}> <View style={{ marginTop: 20 }}>
<Button <Button
label={"Sync Outlook"}
iconSource={() => (
<View
backgroundColor="#ededed"
width={40}
height={40}
style={{ borderRadius: 50 }}
marginR-10
centerV
centerH
>
<Ionicons name="logo-microsoft" size={22} color="#979797" />
</View>
)}
backgroundColor="white"
color="#464039"
borderRadius={15}
onPress={fetchAndSaveMicrosoftEvents}
/>
{profileData?.googleToken !== undefined && (
<Button
label={"Sync Google"}
iconSource={() => (
<View
backgroundColor="#ededed"
width={40}
height={40}
style={{ borderRadius: 50 }}
marginR-10
centerV
centerH
>
<Ionicons name="logo-google" size={22} color="#979797" />
</View>
)}
backgroundColor="white"
color="#464039"
borderRadius={15}
onPress={fetchAndSaveGoogleEvents} onPress={fetchAndSaveGoogleEvents}
/> label="Sync Google"
)} iconSource={() => (
<View marginR-15>
<GoogleIcon />
</View>
)}
style={styles.addCalBtn}
color="black"
text70BL
/>
<Button
onPress={fetchAndSaveMicrosoftEvents}
label="Sync Outlook"
iconSource={() => (
<View marginR-15>
<OutlookIcon />
</View>
)}
style={styles.addCalBtn}
color="black"
text70BL
/>
</View> </View>
</View> </View>
</View> </View>

View File

@ -6,6 +6,9 @@ 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 { AuthContextProvider } from "@/contexts/AuthContext";
import ProfileIcon from "@/assets/svgs/ProfileIcon";
import CalendarIcon from "@/assets/svgs/CalendarIcon";
import PrivacyPolicyIcon from "@/assets/svgs/PrivacyPolicyIcon";
const styles = StyleSheet.create({ const styles = StyleSheet.create({
mainBtn: { mainBtn: {
@ -34,12 +37,7 @@ const SettingsPage = () => {
label="Manage my profile" label="Manage my profile"
color="#07b8c7" color="#07b8c7"
iconSource={() => ( iconSource={() => (
<Ionicons <ProfileIcon style={{marginRight: 10}} color="#07b9c8" />
name="person-circle-sharp"
size={24}
color="#07b8c7"
style={{ marginRight: 10 }}
/>
)} )}
onPress={() => setSelectedPage(pageIndex.user)} onPress={() => setSelectedPage(pageIndex.user)}
/> />
@ -49,12 +47,7 @@ const SettingsPage = () => {
label="Calendar settings" label="Calendar settings"
color="#fd1775" color="#fd1775"
iconSource={() => ( iconSource={() => (
<Ionicons <CalendarIcon style={{marginRight: 10}}/>
name="home-outline"
size={24}
color="#fd1775"
style={{ marginRight: 10 }}
/>
)} )}
onPress={() => { onPress={() => {
setSelectedPage(pageIndex.calendar); setSelectedPage(pageIndex.calendar);
@ -80,12 +73,7 @@ const SettingsPage = () => {
style={styles.mainBtn} style={styles.mainBtn}
label="Kaly privacy policy" label="Kaly privacy policy"
iconSource={() => ( iconSource={() => (
<Entypo <PrivacyPolicyIcon style={{marginRight: 10}}/>
name="text-document"
size={24}
color="#6c645b"
style={{ marginRight: 10 }}
/>
)} )}
color="#6c645b" color="#6c645b"
/> />

View File

@ -44,7 +44,7 @@ const AddChore = () => {
> >
<AntDesign name="plus" size={24} color="white" /> <AntDesign name="plus" size={24} color="white" />
<Text white text60R marginL-10> <Text white text60R marginL-10>
Add to do Add To Do
</Text> </Text>
</Button> </Button>
</View> </View>

View File

@ -1,5 +1,5 @@
import { View, Text, Button, Switch } from "react-native-ui-lib"; import { View, Text, Button, Switch } from "react-native-ui-lib";
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
import PointsSlider from "@/components/shared/PointsSlider"; import PointsSlider from "@/components/shared/PointsSlider";
import { repeatOptions, useToDosContext } from "@/contexts/ToDosContext"; import { repeatOptions, useToDosContext } from "@/contexts/ToDosContext";
import { Feather, AntDesign, Ionicons } from "@expo/vector-icons"; import { Feather, AntDesign, Ionicons } from "@expo/vector-icons";
@ -27,6 +27,15 @@ const AddChoreDialog = (addChoreDialogProps: IAddChoreDialog) => {
const [rotate, setRotate] = useState<boolean>(false); const [rotate, setRotate] = useState<boolean>(false);
const [repeatType, setRepeatType] = useState<string>("Every week"); const [repeatType, setRepeatType] = useState<string>("Every week");
const handleCLose = () => {
setNewTitle("");
setPoints(10);
setChoreDate(new Date());
setRotate(false);
setRepeatType("every week");
addChoreDialogProps.setIsVisible(false);
};
const handleChange = (text: string) => { const handleChange = (text: string) => {
const numericValue = parseInt(text, 10); const numericValue = parseInt(text, 10);
@ -41,7 +50,7 @@ const AddChoreDialog = (addChoreDialogProps: IAddChoreDialog) => {
bottom={true} bottom={true}
height={"90%"} height={"90%"}
panDirection={PanningDirectionsEnum.DOWN} panDirection={PanningDirectionsEnum.DOWN}
onDismiss={() => addChoreDialogProps.setIsVisible(false)} onDismiss={() => handleCLose}
containerStyle={{ containerStyle={{
borderRadius: 10, borderRadius: 10,
backgroundColor: "white", backgroundColor: "white",
@ -59,13 +68,13 @@ const AddChoreDialog = (addChoreDialogProps: IAddChoreDialog) => {
style={styles.topBtn} style={styles.topBtn}
label="Cancel" label="Cancel"
onPress={() => { onPress={() => {
addChoreDialogProps.setIsVisible(false); handleCLose();
}} }}
/> />
<View marginT-12> <View marginT-12>
<DropModalIcon <DropModalIcon
onPress={() => { onPress={() => {
addChoreDialogProps.setIsVisible(false); handleCLose()
}} }}
/> />
</View> </View>
@ -84,7 +93,7 @@ const AddChoreDialog = (addChoreDialogProps: IAddChoreDialog) => {
rotate: rotate, rotate: rotate,
repeatType: repeatType, repeatType: repeatType,
}); });
addChoreDialogProps.setIsVisible(false); handleCLose();
console.log(toDos); console.log(toDos);
} catch (error) { } catch (error) {
console.error(error); console.error(error);

View File

@ -1,8 +1,10 @@
import { View, Text } from "react-native-ui-lib"; import { View, Text, TouchableOpacity, Icon } from "react-native-ui-lib";
import React from "react"; import React, { useState } from "react";
import { IToDo, useToDosContext } from "@/contexts/ToDosContext"; import { IToDo, useToDosContext } from "@/contexts/ToDosContext";
import ToDoItem from "./ToDoItem"; import ToDoItem from "./ToDoItem";
import { format, isToday, isTomorrow } from "date-fns"; import { format, isToday, isTomorrow } from "date-fns";
import DropModalIcon from "@/assets/svgs/DropModalIcon";
import { AntDesign } from "@expo/vector-icons";
const groupToDosByDate = (toDos: IToDo[]) => { const groupToDosByDate = (toDos: IToDo[]) => {
return toDos.reduce((groups, toDo) => { return toDos.reduce((groups, toDo) => {
@ -31,23 +33,53 @@ const ToDosList = () => {
const { toDos } = useToDosContext(); const { toDos } = useToDosContext();
const groupedToDos = groupToDosByDate(toDos); const groupedToDos = groupToDosByDate(toDos);
const [expandedGroups, setExpandedGroups] = useState<{
[key: string]: boolean;
}>({});
const [expandNoDate, setExpandNoDate] = useState<boolean>(true);
const toggleExpand = (dateKey: string) => {
setExpandedGroups((prev) => ({
...prev,
[dateKey]: !prev[dateKey],
}));
};
const noDateToDos = groupedToDos["No Date"] || []; const noDateToDos = groupedToDos["No Date"] || [];
const datedToDos = Object.keys(groupedToDos).filter((key) => key !== "No Date"); const datedToDos = Object.keys(groupedToDos).filter(
(key) => key !== "No Date"
);
return ( return (
<View marginB-140> <View marginB-402>
{noDateToDos.length > 0 && ( {noDateToDos.length > 0 && (
<View key="No Date"> <View key="No Date">
{noDateToDos <View row spread paddingH-19 marginB-12>
.sort((a, b) => Number(a.done) - Number(b.done)) <Text
.map((item) => ( text70
<ToDoItem key={item.id} item={item} /> style={{
))} fontWeight: "bold",
}}
>
Unscheduled
</Text>
{!expandNoDate && (
<AntDesign name="caretright" size={24} color="#fd1575" onPress={() => {setExpandNoDate(!expandNoDate)}}/>
)}
{expandNoDate && (
<AntDesign name="caretdown" size={24} color="#fd1575" onPress={() => {setExpandNoDate(!expandNoDate)}}/>
)}
</View>
{expandNoDate &&
noDateToDos
.sort((a, b) => Number(a.done) - Number(b.done))
.map((item) => <ToDoItem key={item.id} item={item} />)}
</View> </View>
)} )}
{datedToDos.map((dateKey) => { {datedToDos.map((dateKey) => {
const isExpanded = expandedGroups[dateKey] || false;
const sortedToDos = [ const sortedToDos = [
...groupedToDos[dateKey].filter((toDo) => !toDo.done), ...groupedToDos[dateKey].filter((toDo) => !toDo.done),
...groupedToDos[dateKey].filter((toDo) => toDo.done), ...groupedToDos[dateKey].filter((toDo) => toDo.done),
@ -55,19 +87,34 @@ const ToDosList = () => {
return ( return (
<View key={dateKey}> <View key={dateKey}>
<Text <TouchableOpacity
text70 onPress={() => toggleExpand(dateKey)}
style={{ style={{
fontWeight: "bold", flexDirection: "row",
marginVertical: 8, justifyContent: "space-between",
alignItems: "center",
paddingHorizontal: 20, paddingHorizontal: 20,
marginVertical: 8,
}} }}
> >
{dateKey} <Text
</Text> text70
{sortedToDos.map((item) => ( style={{
<ToDoItem key={item.id} item={item} /> fontWeight: "bold",
))} }}
>
{dateKey}
</Text>
{!isExpanded && (
<AntDesign name="caretright" size={24} color="#fd1575" />
)}
{isExpanded && (
<AntDesign name="caretdown" size={24} color="#fd1575" />
)}
</TouchableOpacity>
{isExpanded &&
sortedToDos.map((item) => <ToDoItem key={item.id} item={item} />)}
</View> </View>
); );
})} })}

View File

@ -5,6 +5,11 @@ import {useRouter} from "expo-router";
import firestore from "@react-native-firebase/firestore"; import firestore from "@react-native-firebase/firestore";
import {UserProfile} from "@/hooks/firebase/types/profileTypes"; import {UserProfile} from "@/hooks/firebase/types/profileTypes";
import * as Notifications from 'expo-notifications';
import * as Device from 'expo-device';
import Constants from 'expo-constants';
import { Platform } from 'react-native';
export enum ProfileType { export enum ProfileType {
"PARENT" = "parent", "PARENT" = "parent",
"CHILD" = "child", "CHILD" = "child",
@ -19,48 +24,118 @@ interface IAuthContext {
refreshProfileData: () => Promise<void> refreshProfileData: () => Promise<void>
} }
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: true,
}),
});
Notifications.addNotificationReceivedListener(notification => {
console.log('Notification received:', notification);
});
async function registerForPushNotificationsAsync() {
if (Platform.OS === 'android') {
await Notifications.setNotificationChannelAsync('default', {
name: 'default',
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: '#FF231F7C',
});
}
if (Device.isDevice) {
const { status: existingStatus } = await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== 'granted') {
alert('Failed to get push token for push notification!');
return;
}
const projectId =
Constants?.expoConfig?.extra?.eas?.projectId ?? Constants?.easConfig?.projectId;
if (!projectId) {
alert('Project ID not found');
return;
}
try {
const token = (await Notifications.getExpoPushTokenAsync({ projectId })).data;
console.log('Push Token:', token);
return token;
} catch (error) {
alert(`Error getting push token: ${error}`);
throw error;
}
} else {
alert('Must use a physical device for push notifications');
}
}
const AuthContext = createContext<IAuthContext>(undefined!) const AuthContext = createContext<IAuthContext>(undefined!)
export const AuthContextProvider: FC<{ children: ReactNode }> = ({children}) => { export const AuthContextProvider: FC<{ children: ReactNode }> = ({children}) => {
const [user, setUser] = useState<FirebaseAuthTypes.User | null>(null) const [user, setUser] = useState<FirebaseAuthTypes.User | null>(null);
const [initializing, setInitializing] = useState(true); const [initializing, setInitializing] = useState(true);
const [profileType, setProfileType] = useState<ProfileType | undefined>(undefined); const [profileType, setProfileType] = useState<ProfileType | undefined>(undefined);
const [profileData, setProfileData] = useState<UserProfile | undefined>(undefined); const [profileData, setProfileData] = useState<UserProfile | undefined>(undefined);
const {replace} = useRouter();
const ready = !initializing;
const {replace} = useRouter() const onAuthStateChangedHandler = async (authUser: FirebaseAuthTypes.User | null) => {
const ready = !initializing setUser(authUser);
const onAuthStateChanged = async (user: FirebaseAuthTypes.User | null) => { if (authUser) {
setUser(user); await refreshProfileData(authUser);
const pushToken = await registerForPushNotificationsAsync();
if (user) { if (pushToken) {
await refreshProfileData(); await savePushTokenToFirestore(authUser.uid, pushToken);
}
} }
if (initializing) setInitializing(false); if (initializing) setInitializing(false);
} };
const refreshProfileData = async () => { const refreshProfileData = async (user?: FirebaseAuthTypes.User) => {
if (user) { const authUser = user ?? auth().currentUser
if (authUser) {
try { try {
const documentSnapshot = await firestore() const documentSnapshot = await firestore()
.collection("Profiles") .collection("Profiles")
.doc(user.uid) .doc(authUser.uid)
.get(); .get();
if (documentSnapshot.exists) { if (documentSnapshot.exists) {
setProfileType(documentSnapshot.data()?.userType); setProfileType(documentSnapshot.data()?.userType);
setProfileData(documentSnapshot.data() as UserProfile); setProfileData(documentSnapshot.data() as UserProfile);
} }
} catch (error) { } catch (error) {
console.error("Error fetching user profile data:", error);
setProfileType(undefined); setProfileType(undefined);
setProfileData(undefined); setProfileData(undefined);
} }
} }
}; };
const savePushTokenToFirestore = async (uid: string, token: string) => {
try {
await firestore().collection("Profiles").doc(uid).update({
pushToken: token,
});
} catch (error) {
console.error('Error saving push token to Firestore:', error);
}
};
useEffect(() => { useEffect(() => {
const subscriber = auth().onAuthStateChanged(onAuthStateChanged); const subscriber = auth().onAuthStateChanged(onAuthStateChangedHandler);
return subscriber; return subscriber;
}, []); }, []);
@ -72,9 +147,9 @@ export const AuthContextProvider: FC<{ children: ReactNode }> = ({children}) =>
useEffect(() => { useEffect(() => {
if (ready && user) { if (ready && user) {
replace({pathname: "/(auth)/calendar"}) replace({pathname: "/(auth)/calendar"});
} else if (ready && !user) { } else if (ready && !user) {
replace({pathname: "/(unauth)"}) replace({pathname: "/(unauth)"});
} }
}, [user, ready]); }, [user, ready]);
@ -86,7 +161,8 @@ export const AuthContextProvider: FC<{ children: ReactNode }> = ({children}) =>
<AuthContext.Provider value={{user, profileType, profileData, setProfileData, refreshProfileData}}> <AuthContext.Provider value={{user, profileType, profileData, setProfileData, refreshProfileData}}>
{children} {children}
</AuthContext.Provider> </AuthContext.Provider>
) );
} };
export const useAuthContext = () => useContext(AuthContext)!; export const useAuthContext = () => useContext(AuthContext)!;

View File

@ -3,7 +3,7 @@ import React, { createContext, useContext, useState, ReactNode } from "react";
// Define the CalendarEvent interface // Define the CalendarEvent interface
export interface CalendarEvent { export interface CalendarEvent {
id?: number; // Unique identifier for the event id?: number | string; // Unique identifier for the event
user?: string; user?: string;
title: string; // Event title or name title: string; // Event title or name
description?: string; // Optional description for the event description?: string; // Optional description for the event

View File

@ -1,6 +1,9 @@
import { MaterialCommunityIcons } from "@expo/vector-icons"; import {createContext, useContext, useState} from "react";
import { createContext, useContext, useState } from "react";
import fuzzysort from "fuzzysort"; import fuzzysort from "fuzzysort";
import {useCreateGrocery} from "@/hooks/firebase/useCreateGrocery";
import {IGrocery} from "@/hooks/firebase/types/groceryData";
import {useUpdateGrocery} from "@/hooks/firebase/useUpdateGrocery";
import {useGetGroceries} from "@/hooks/firebase/useGetGroceries";
export enum GroceryFrequency { export enum GroceryFrequency {
Never = "Never", Never = "Never",
@ -11,16 +14,6 @@ export enum GroceryFrequency {
Quarterly = "Quarterly", Quarterly = "Quarterly",
} }
export interface IGrocery {
id: number;
title: string;
category: GroceryCategory;
approved: boolean;
recurring: boolean;
frequency: GroceryFrequency;
bought: boolean;
}
export enum GroceryCategory { export enum GroceryCategory {
Fruit = "Fruit", Fruit = "Fruit",
Dairy = "Dairy", Dairy = "Dairy",
@ -52,10 +45,136 @@ const iconMapping: { [key in GroceryCategory]: MaterialIconNames } = {
[GroceryCategory.Frozen]: "snowflake", [GroceryCategory.Frozen]: "snowflake",
};*/ };*/
const initialGroceryList = [
{
id: 0,
title: "Carrots",
category: GroceryCategory.Vegetables,
approved: false,
bought: false,
recurring: false,
frequency: GroceryFrequency.Never,
},
{
id: 1,
title: "Steak",
category: GroceryCategory.Meat,
approved: true,
bought: false,
recurring: false,
frequency: GroceryFrequency.Never,
},
{
id: 2,
title: "Chicken Breast",
category: GroceryCategory.Poultry,
approved: true,
bought: false,
recurring: false,
frequency: GroceryFrequency.Never,
},
{
id: 3,
title: "Greek Yoghurt",
category: GroceryCategory.Dairy,
approved: false,
bought: false,
recurring: false,
frequency: GroceryFrequency.Never,
},
];
const groceryExamples = {
[GroceryCategory.Fruit]: [
'apple', 'apples', 'banana', 'bananas', 'orange', 'oranges', 'grape', 'grapes',
'pear', 'pears', 'pineapple', 'pineapples', 'kiwi', 'kiwis', 'strawberry',
'strawberries', 'blueberry', 'blueberries', 'mango', 'mangoes', 'watermelon',
'watermelons', 'peach', 'peaches', 'plum', 'plums', 'cherry', 'cherries',
'raspberry', 'raspberries', 'blackberry', 'blackberries', 'pomegranate',
'pomegranates', 'lemon', 'lemons', 'lime', 'limes', 'coconut', 'coconuts'
],
[GroceryCategory.Dairy]: [
'milk', 'whole milk', 'skim milk', 'almond milk', 'soy milk', 'cheese',
'cheeses', 'yoghurt', 'yogurt', 'greek yoghurt', 'greek yogurt', 'butter',
'margarine', 'cream', 'whipping cream', 'heavy cream', 'ice cream', 'ice creams',
'sour cream', 'whipped cream', 'cream cheese', 'cream cheeses', 'buttermilk',
'cottage cheese', 'ghee', 'kefir'
],
[GroceryCategory.Vegetables]: [
'carrot', 'carrots', 'broccoli', 'lettuce', 'lettuces', 'spinach', 'kale',
'cabbage', 'cabbages', 'cauliflower', 'zucchini', 'zucchinis', 'onion', 'onions',
'garlic', 'pepper', 'peppers', 'bell pepper', 'bell peppers', 'tomato', 'tomatoes',
'cucumber', 'cucumbers', 'potato', 'potatoes', 'sweet potato', 'sweet potatoes',
'beet', 'beets', 'eggplant', 'eggplants', 'celery', 'radish', 'radishes',
'asparagus', 'mushroom', 'mushrooms'
],
[GroceryCategory.Meat]: [
'steak', 'steaks', 'beef', 'pork', 'lamb', 'veal', 'ribeye', 'tenderloin',
'filet mignon', 'bacon', 'ham', 'sausage', 'sausages', 'salami', 'ground beef',
'beef mince', 'hamburger meat', 'prosciutto', 'brisket', 'pork chop', 'pork chops'
],
[GroceryCategory.Poultry]: [
'chicken', 'whole chicken', 'chickens', 'chicken breast', 'chicken breasts',
'breast of chicken', 'chicken thigh', 'chicken thighs', 'chicken leg', 'chicken legs',
'chicken wing', 'chicken wings', 'wing', 'wings', 'drumsticks', 'chicken drumstick',
'turkey', 'whole turkey', 'ground turkey', 'turkey breast', 'turkey breasts',
'turkey leg', 'duck', 'ducks', 'goose', 'quail', 'pheasant'
],
[GroceryCategory.Bakery]: [
'bread', 'breads', 'whole wheat bread', 'sourdough bread', 'bagel', 'bagels',
'croissant', 'croissants', 'muffin', 'muffins', 'buns', 'hamburger bun',
'hamburger buns', 'hotdog bun', 'hotdog buns', 'donut', 'donuts', 'roll', 'rolls',
'dinner roll', 'dinner rolls', 'scone', 'scones', 'toast', 'ciabatta',
'focaccia', 'brioche', 'pita', 'pitas', 'naan', 'baguette', 'baguettes',
'pastry', 'pastries', 'pretzel', 'pretzels'
],
[GroceryCategory.Beverages]: [
'coffee', 'decaf coffee', 'iced coffee', 'cold brew', 'tea', 'iced tea',
'black tea', 'green tea', 'herbal tea', 'juice', 'apple juice', 'orange juice',
'grape juice', 'water', 'sparkling water', 'flavored water', 'soda', 'cola',
'diet soda', 'beer', 'lager', 'ale', 'wine', 'red wine', 'white wine',
'whiskey', 'whisky', 'vodka', 'rum', 'gin', 'smoothie', 'smoothies', 'milkshake',
'milkshakes', 'energy drink', 'energy drinks', 'sports drink', 'sports drinks',
'lemonade', 'sparkling lemonade', 'iced lemonade', 'sparkling water', 'cider',
'hard cider', 'kombucha'
],
[GroceryCategory.Snacks]: [
'chips', 'potato chips', 'tortilla chips', 'corn chips', 'candy', 'candies',
'chocolate', 'chocolates', 'dark chocolate', 'milk chocolate', 'white chocolate',
'cookies', 'popcorn', 'pretzel', 'pretzels', 'nuts', 'mixed nuts', 'almonds',
'cashews', 'trail mix', 'granola bar', 'granola bars', 'protein bar', 'protein bars',
'crackers', 'gummies', 'gummy bears', 'fruit snacks', 'dried fruit',
'peanut butter', 'rice cakes', 'snack cakes'
],
[GroceryCategory.Household]: [
'detergent', 'laundry detergent', 'dish soap', 'dishwasher detergent',
'toilet paper', 'paper towels', 'trash bags', 'bin liners', 'broom', 'mop',
'cleaner', 'surface cleaner', 'multi-purpose cleaner', 'disinfectant', 'bleach',
'fabric softener', 'vacuum bags', 'aluminum foil', 'plastic wrap', 'cling wrap',
'light bulbs', 'batteries', 'laundry soap', 'sponges', 'scrubbers',
'dishwashing liquid'
],
[GroceryCategory.PersonalCare]: [
'shampoo', 'conditioner', 'hair shampoo', 'hair conditioner', 'soap', 'bar soap',
'liquid soap', 'toothpaste', 'toothbrush', 'toothbrushes', 'electric toothbrush',
'deodorant', 'antiperspirant', 'lotion', 'moisturizer', 'razor', 'shaving razor',
'shaving cream', 'body wash', 'face wash', 'sunscreen', 'hair gel', 'hair spray',
'nail polish', 'cotton swabs', 'lip balm', 'chapstick', 'hand cream', 'sanitizer'
],
[GroceryCategory.Frozen]: [
'ice cream', 'ice creams', 'frozen pizza', 'frozen pizzas', 'frozen vegetables',
'frozen veg', 'frozen peas', 'frozen fruit', 'frozen fish', 'frozen chicken',
'frozen wings', 'frozen fries', 'frozen french fries', 'frozen shrimp',
'frozen prawns', 'frozen dumplings', 'frozen waffles', 'frozen pancakes',
'frozen pie', 'frozen pies', 'frozen lasagna', 'frozen burrito', 'frozen burritos',
'frozen nuggets', 'frozen pastry', 'frozen pastries', 'frozen meals'
],
};
interface IGroceryContext { interface IGroceryContext {
groceries: IGrocery[]; groceries: IGrocery[];
//iconMapping: { [key in GroceryCategory]: MaterialIconNames }; //iconMapping: { [key in GroceryCategory]: MaterialIconNames };
updateGroceryItem: (id: number, changes: Partial<IGrocery>) => void; updateGroceryItem: (changes: Partial<IGrocery>) => void;
isAddingGrocery: boolean; isAddingGrocery: boolean;
setIsAddingGrocery: (value: boolean) => void; setIsAddingGrocery: (value: boolean) => void;
addGrocery: (grocery: IGrocery) => void; addGrocery: (grocery: IGrocery) => void;
@ -68,142 +187,14 @@ export const GroceryProvider: React.FC<{ children: React.ReactNode }> = ({
children, children,
}) => { }) => {
const [isAddingGrocery, setIsAddingGrocery] = useState<boolean>(false); const [isAddingGrocery, setIsAddingGrocery] = useState<boolean>(false);
const [groceries, setGroceries] = useState<IGrocery[]>([
{ const { mutateAsync: createGrocery } = useCreateGrocery();
id: 0, const { mutateAsync: updateGrocery } = useUpdateGrocery();
title: "Carrots", const { data: groceries } = useGetGroceries();
category: GroceryCategory.Vegetables,
approved: false,
bought: false,
recurring: false,
frequency: GroceryFrequency.Never,
},
{
id: 1,
title: "Steak",
category: GroceryCategory.Meat,
approved: true,
bought: false,
recurring: false,
frequency: GroceryFrequency.Never,
},
{
id: 2,
title: "Chicken Breast",
category: GroceryCategory.Poultry,
approved: true,
bought: false,
recurring: false,
frequency: GroceryFrequency.Never,
},
{
id: 3,
title: "Greek Yoghurt",
category: GroceryCategory.Dairy,
approved: false,
bought: false,
recurring: false,
frequency: GroceryFrequency.Never,
},
]);
const addGrocery = (grocery: IGrocery) => { const addGrocery = (grocery: IGrocery) => {
setGroceries((prevGroceries) => [ createGrocery(grocery);
...prevGroceries,
{
...grocery,
id: prevGroceries.length
? prevGroceries[prevGroceries.length - 1].id + 1
: 0,
},
]);
};
const groceryExamples = {
[GroceryCategory.Fruit]: [
'apple', 'apples', 'banana', 'bananas', 'orange', 'oranges', 'grape', 'grapes',
'pear', 'pears', 'pineapple', 'pineapples', 'kiwi', 'kiwis', 'strawberry',
'strawberries', 'blueberry', 'blueberries', 'mango', 'mangoes', 'watermelon',
'watermelons', 'peach', 'peaches', 'plum', 'plums', 'cherry', 'cherries',
'raspberry', 'raspberries', 'blackberry', 'blackberries', 'pomegranate',
'pomegranates', 'lemon', 'lemons', 'lime', 'limes', 'coconut', 'coconuts'
],
[GroceryCategory.Dairy]: [
'milk', 'whole milk', 'skim milk', 'almond milk', 'soy milk', 'cheese',
'cheeses', 'yoghurt', 'yogurt', 'greek yoghurt', 'greek yogurt', 'butter',
'margarine', 'cream', 'whipping cream', 'heavy cream', 'ice cream', 'ice creams',
'sour cream', 'whipped cream', 'cream cheese', 'cream cheeses', 'buttermilk',
'cottage cheese', 'ghee', 'kefir'
],
[GroceryCategory.Vegetables]: [
'carrot', 'carrots', 'broccoli', 'lettuce', 'lettuces', 'spinach', 'kale',
'cabbage', 'cabbages', 'cauliflower', 'zucchini', 'zucchinis', 'onion', 'onions',
'garlic', 'pepper', 'peppers', 'bell pepper', 'bell peppers', 'tomato', 'tomatoes',
'cucumber', 'cucumbers', 'potato', 'potatoes', 'sweet potato', 'sweet potatoes',
'beet', 'beets', 'eggplant', 'eggplants', 'celery', 'radish', 'radishes',
'asparagus', 'mushroom', 'mushrooms'
],
[GroceryCategory.Meat]: [
'steak', 'steaks', 'beef', 'pork', 'lamb', 'veal', 'ribeye', 'tenderloin',
'filet mignon', 'bacon', 'ham', 'sausage', 'sausages', 'salami', 'ground beef',
'beef mince', 'hamburger meat', 'prosciutto', 'brisket', 'pork chop', 'pork chops'
],
[GroceryCategory.Poultry]: [
'chicken', 'whole chicken', 'chickens', 'chicken breast', 'chicken breasts',
'breast of chicken', 'chicken thigh', 'chicken thighs', 'chicken leg', 'chicken legs',
'chicken wing', 'chicken wings', 'wing', 'wings', 'drumsticks', 'chicken drumstick',
'turkey', 'whole turkey', 'ground turkey', 'turkey breast', 'turkey breasts',
'turkey leg', 'duck', 'ducks', 'goose', 'quail', 'pheasant'
],
[GroceryCategory.Bakery]: [
'bread', 'breads', 'whole wheat bread', 'sourdough bread', 'bagel', 'bagels',
'croissant', 'croissants', 'muffin', 'muffins', 'buns', 'hamburger bun',
'hamburger buns', 'hotdog bun', 'hotdog buns', 'donut', 'donuts', 'roll', 'rolls',
'dinner roll', 'dinner rolls', 'scone', 'scones', 'toast', 'ciabatta',
'focaccia', 'brioche', 'pita', 'pitas', 'naan', 'baguette', 'baguettes',
'pastry', 'pastries', 'pretzel', 'pretzels'
],
[GroceryCategory.Beverages]: [
'coffee', 'decaf coffee', 'iced coffee', 'cold brew', 'tea', 'iced tea',
'black tea', 'green tea', 'herbal tea', 'juice', 'apple juice', 'orange juice',
'grape juice', 'water', 'sparkling water', 'flavored water', 'soda', 'cola',
'diet soda', 'beer', 'lager', 'ale', 'wine', 'red wine', 'white wine',
'whiskey', 'whisky', 'vodka', 'rum', 'gin', 'smoothie', 'smoothies', 'milkshake',
'milkshakes', 'energy drink', 'energy drinks', 'sports drink', 'sports drinks',
'lemonade', 'sparkling lemonade', 'iced lemonade', 'sparkling water', 'cider',
'hard cider', 'kombucha'
],
[GroceryCategory.Snacks]: [
'chips', 'potato chips', 'tortilla chips', 'corn chips', 'candy', 'candies',
'chocolate', 'chocolates', 'dark chocolate', 'milk chocolate', 'white chocolate',
'cookies', 'popcorn', 'pretzel', 'pretzels', 'nuts', 'mixed nuts', 'almonds',
'cashews', 'trail mix', 'granola bar', 'granola bars', 'protein bar', 'protein bars',
'crackers', 'gummies', 'gummy bears', 'fruit snacks', 'dried fruit',
'peanut butter', 'rice cakes', 'snack cakes'
],
[GroceryCategory.Household]: [
'detergent', 'laundry detergent', 'dish soap', 'dishwasher detergent',
'toilet paper', 'paper towels', 'trash bags', 'bin liners', 'broom', 'mop',
'cleaner', 'surface cleaner', 'multi-purpose cleaner', 'disinfectant', 'bleach',
'fabric softener', 'vacuum bags', 'aluminum foil', 'plastic wrap', 'cling wrap',
'light bulbs', 'batteries', 'laundry soap', 'sponges', 'scrubbers',
'dishwashing liquid'
],
[GroceryCategory.PersonalCare]: [
'shampoo', 'conditioner', 'hair shampoo', 'hair conditioner', 'soap', 'bar soap',
'liquid soap', 'toothpaste', 'toothbrush', 'toothbrushes', 'electric toothbrush',
'deodorant', 'antiperspirant', 'lotion', 'moisturizer', 'razor', 'shaving razor',
'shaving cream', 'body wash', 'face wash', 'sunscreen', 'hair gel', 'hair spray',
'nail polish', 'cotton swabs', 'lip balm', 'chapstick', 'hand cream', 'sanitizer'
],
[GroceryCategory.Frozen]: [
'ice cream', 'ice creams', 'frozen pizza', 'frozen pizzas', 'frozen vegetables',
'frozen veg', 'frozen peas', 'frozen fruit', 'frozen fish', 'frozen chicken',
'frozen wings', 'frozen fries', 'frozen french fries', 'frozen shrimp',
'frozen prawns', 'frozen dumplings', 'frozen waffles', 'frozen pancakes',
'frozen pie', 'frozen pies', 'frozen lasagna', 'frozen burrito', 'frozen burritos',
'frozen nuggets', 'frozen pastry', 'frozen pastries', 'frozen meals'
],
}; };
const fuzzyMatchGroceryCategory = (groceryTitle: string): GroceryCategory => { const fuzzyMatchGroceryCategory = (groceryTitle: string): GroceryCategory => {
@ -221,12 +212,8 @@ export const GroceryProvider: React.FC<{ children: React.ReactNode }> = ({
return bestMatchCategory; return bestMatchCategory;
}; };
const updateGroceryItem = (id: number, changes: Partial<IGrocery>) => { const updateGroceryItem = (changes: Partial<IGrocery>) => {
setGroceries((prevGroceries) => updateGrocery(changes);
prevGroceries.map((grocery) =>
grocery.id === id ? { ...grocery, ...changes } : grocery
)
);
}; };
return ( return (

View File

@ -5,12 +5,16 @@
"build": { "build": {
"development": { "development": {
"developmentClient": true, "developmentClient": true,
"distribution": "internal" "distribution": "internal",
"channel": "development"
}, },
"preview": { "preview": {
"distribution": "internal" "distribution": "internal",
"channel": "preview"
}, },
"production": {} "production": {
"channel": "production"
}
}, },
"submit": { "submit": {
"production": { "production": {

View File

@ -1,14 +1,109 @@
const {onRequest} = require("firebase-functions/v2/https"); const {onRequest} = require("firebase-functions/v2/https");
const {getAuth} = require("firebase-admin/auth"); const {getAuth} = require("firebase-admin/auth");
const {getFirestore} = require("firebase-admin/firestore"); const {getFirestore} = require("firebase-admin/firestore");
const admin = require("firebase-admin");
const logger = require("firebase-functions/logger"); const logger = require("firebase-functions/logger");
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const {Expo} = require('expo-server-sdk');
try { admin.initializeApp();
admin.initializeApp(); const db = admin.firestore();
} catch (error) {
console.error(error) let expo = new Expo({accessToken: process.env.EXPO_ACCESS_TOKEN});
}
// Firestore trigger that listens for new events in the 'Events' collection
exports.sendNotificationOnEventCreation = functions.firestore
.document('Events/{eventId}')
.onCreate(async (snapshot, context) => {
const eventData = snapshot.data();
const { familyId, creatorId } = eventData;
if (!familyId || !creatorId) {
console.error('Missing familyId or creatorId in event data');
return;
}
const pushTokens = await getPushTokensForFamilyExcludingCreator(familyId, creatorId);
if (!pushTokens.length) {
console.log('No push tokens available for the event.');
return;
}
let messages = [];
for (let pushToken of pushTokens) {
if (!Expo.isExpoPushToken(pushToken)) {
console.error(`Push token ${pushToken} is not a valid Expo push token`);
continue;
}
messages.push({
to: pushToken,
sound: 'default',
title: 'New Event Added!',
body: `An event "${eventData.title}" has been added. Check it out!`,
data: { eventId: context.params.eventId },
});
}
let chunks = expo.chunkPushNotifications(messages);
let tickets = [];
for (let chunk of chunks) {
try {
let ticketChunk = await expo.sendPushNotificationsAsync(chunk);
tickets.push(...ticketChunk);
for (let ticket of ticketChunk) {
if (ticket.status === 'ok') {
console.log('Notification successfully sent:', ticket.id);
} else if (ticket.status === 'error') {
console.error(`Notification error: ${ticket.message}`);
if (ticket.details && ticket.details.error) {
console.error('Error details:', ticket.details.error);
if (ticket.details.error === 'DeviceNotRegistered') {
console.log(`Removing invalid push token: ${ticket.to}`);
await removeInvalidPushToken(ticket.to);
}
}
}
}
} catch (error) {
console.error('Error sending notification:', error);
}
}
// Retrieve and handle notification receipts
let receiptIds = [];
for (let ticket of tickets) {
if (ticket.id) {
receiptIds.push(ticket.id);
}
}
let receiptIdChunks = expo.chunkPushNotificationReceiptIds(receiptIds);
for (let chunk of receiptIdChunks) {
try {
let receipts = await expo.getPushNotificationReceiptsAsync(chunk);
console.log('Receipts:', receipts);
for (let receiptId in receipts) {
let { status, message, details } = receipts[receiptId];
if (status === 'ok') {
console.log(`Notification with receipt ID ${receiptId} was delivered successfully`);
} else if (status === 'error') {
console.error(`Notification error: ${message}`);
if (details && details.error) {
console.error(`Error details: ${details.error}`);
}
}
}
} catch (error) {
console.error('Error retrieving receipts:', error);
}
}
return null;
});
exports.createSubUser = onRequest(async (request, response) => { exports.createSubUser = onRequest(async (request, response) => {
const authHeader = request.get('Authorization'); const authHeader = request.get('Authorization');
@ -97,3 +192,35 @@ exports.generateCustomToken = onRequest(async (request, response) => {
response.status(500).json({error: "Failed to generate custom token"}); response.status(500).json({error: "Failed to generate custom token"});
} }
}); });
async function getPushTokensForEvent() {
const usersRef = db.collection('Profiles');
const snapshot = await usersRef.get();
let pushTokens = [];
snapshot.forEach(doc => {
const data = doc.data();
if (data.pushToken) {
pushTokens.push(data.pushToken);
}
});
console.log('Push Tokens:', pushTokens);
return pushTokens;
}
async function getPushTokensForFamilyExcludingCreator(familyId, creatorId) {
const usersRef = db.collection('Profiles');
const snapshot = await usersRef.where('familyId', '==', familyId).get();
let pushTokens = [];
snapshot.forEach(doc => {
const data = doc.data();
// Exclude the creator
if (data.uid !== creatorId && data.pushToken) {
pushTokens.push(data.pushToken);
}
});
return pushTokens;
}

View File

@ -6,6 +6,7 @@
"": { "": {
"name": "functions", "name": "functions",
"dependencies": { "dependencies": {
"expo-server-sdk": "^3.11.0",
"firebase-admin": "^12.1.0", "firebase-admin": "^12.1.0",
"firebase-functions": "^5.0.0" "firebase-functions": "^5.0.0"
}, },
@ -3115,6 +3116,12 @@
"once": "^1.4.0" "once": "^1.4.0"
} }
}, },
"node_modules/err-code": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
"integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
"license": "MIT"
},
"node_modules/error-ex": { "node_modules/error-ex": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@ -3425,6 +3432,16 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0" "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
} }
}, },
"node_modules/expo-server-sdk": {
"version": "3.11.0",
"resolved": "https://registry.npmjs.org/expo-server-sdk/-/expo-server-sdk-3.11.0.tgz",
"integrity": "sha512-EGH82ZcdAFjKq+6daDE8Xj7BjaSeP1VDvZ3Hmtn/KzEQ3ffqHkauMsgXL2wLEPlvatLq3EsYNcejXRBV54WnFQ==",
"dependencies": {
"node-fetch": "^2.6.0",
"promise-limit": "^2.7.0",
"promise-retry": "^2.0.1"
}
},
"node_modules/express": { "node_modules/express": {
"version": "4.19.2", "version": "4.19.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
@ -5754,7 +5771,6 @@
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"whatwg-url": "^5.0.0" "whatwg-url": "^5.0.0"
}, },
@ -6183,6 +6199,34 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1" "url": "https://github.com/chalk/ansi-styles?sponsor=1"
} }
}, },
"node_modules/promise-limit": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/promise-limit/-/promise-limit-2.7.0.tgz",
"integrity": "sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==",
"license": "ISC"
},
"node_modules/promise-retry": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
"integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
"license": "MIT",
"dependencies": {
"err-code": "^2.0.2",
"retry": "^0.12.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/promise-retry/node_modules/retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
"integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/prompts": { "node_modules/prompts": {
"version": "2.4.2", "version": "2.4.2",
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
@ -7048,8 +7092,7 @@
"version": "0.0.3", "version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"license": "MIT", "license": "MIT"
"optional": true
}, },
"node_modules/ts-api-utils": { "node_modules/ts-api-utils": {
"version": "1.3.0", "version": "1.3.0",
@ -7268,8 +7311,7 @@
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
"license": "BSD-2-Clause", "license": "BSD-2-Clause"
"optional": true
}, },
"node_modules/websocket-driver": { "node_modules/websocket-driver": {
"version": "0.7.4", "version": "0.7.4",
@ -7299,7 +7341,6 @@
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"tr46": "~0.0.3", "tr46": "~0.0.3",
"webidl-conversions": "^3.0.0" "webidl-conversions": "^3.0.0"

View File

@ -14,6 +14,7 @@
}, },
"main": "index.js", "main": "index.js",
"dependencies": { "dependencies": {
"expo-server-sdk": "^3.11.0",
"firebase-admin": "^12.1.0", "firebase-admin": "^12.1.0",
"firebase-functions": "^5.0.0" "firebase-functions": "^5.0.0"
}, },

View File

@ -0,0 +1,13 @@
import {GroceryCategory, GroceryFrequency} from "@/contexts/GroceryContext";
export interface IGrocery {
id: string;
title: string;
category: GroceryCategory;
approved: boolean;
recurring: boolean;
frequency: GroceryFrequency;
bought: boolean;
familyId?: string,
creatorId?: string
}

View File

@ -4,16 +4,17 @@ import firestore from "@react-native-firebase/firestore";
import { EventData } from "@/hooks/firebase/types/eventData"; import { EventData } from "@/hooks/firebase/types/eventData";
export const useCreateEvent = () => { export const useCreateEvent = () => {
const {user: currentUser} = useAuthContext() const {user: currentUser, profileData} = useAuthContext()
const queryClients = useQueryClient() const queryClients = useQueryClient()
return useMutation({ return useMutation({
mutationKey: ["createEvent"], mutationKey: ["createEvent"],
mutationFn: async (eventData: Partial<EventData>) => { mutationFn: async (eventData: Partial<EventData>) => {
try { try {
console.log("CALLLLL")
await firestore() await firestore()
.collection("Events") .collection("Events")
.add({...eventData, creatorId: currentUser?.uid}) .add({...eventData, creatorId: currentUser?.uid, familyId: profileData?.familyId})
} catch (e) { } catch (e) {
console.error(e) console.error(e)
} }

View File

@ -0,0 +1,26 @@
import { useMutation, useQueryClient } from "react-query";
import firestore from "@react-native-firebase/firestore";
import { useAuthContext } from "@/contexts/AuthContext";
import {IGrocery} from "@/hooks/firebase/types/groceryData";
export const useCreateGrocery = () => {
const { user: currentUser, profileData } = useAuthContext();
const queryClients = useQueryClient();
return useMutation({
mutationKey: ["createGrocery"],
mutationFn: async (groceryData: Partial<IGrocery>) => {
try {
const newDoc = firestore().collection('Groceries').doc();
await firestore()
.collection("Groceries")
.add({...groceryData, id: newDoc.id, familyId: profileData?.familyId, creatorId: currentUser?.uid})
} catch (e) {
console.error(e)
}
},
onSuccess: () => {
queryClients.invalidateQueries("groceries")
}
})
}

View File

@ -31,6 +31,7 @@ export const useGetEvents = (isFamilyView: boolean) => {
const eventColor: string = profileData?.eventColor || colorMap.pink // Default color if not found const eventColor: string = profileData?.eventColor || colorMap.pink // Default color if not found
return { return {
id: doc.id,
title: data.title, title: data.title,
start: new Date(data.startDate.seconds * 1000), start: new Date(data.startDate.seconds * 1000),
end: new Date(data.endDate.seconds * 1000), end: new Date(data.endDate.seconds * 1000),

View File

@ -0,0 +1,32 @@
import {useQuery} from "react-query";
import firestore from "@react-native-firebase/firestore";
import {useAuthContext} from "@/contexts/AuthContext";
export const useGetGroceries = () => {
const { user, profileData } = useAuthContext();
return useQuery({
queryKey: ["groceries", user?.uid],
queryFn: async () => {
const snapshot = await firestore()
.collection("Groceries")
.where("familyId", "==", profileData?.familyId)
.get();
return snapshot.docs.map((doc) => {
const data = doc.data();
return {
id: doc.id,
title: data.title,
category: data.category,
approved: data.approved,
bought: data.bought,
recurring: data.recurring,
frequency: data.frequency,
creatorId: data.creatorId
};
});
}
})
};

View File

@ -0,0 +1,26 @@
import {useAuthContext} from "@/contexts/AuthContext";
import {useMutation, useQueryClient} from "react-query";
import firestore from "@react-native-firebase/firestore";
import {EventData} from "@/hooks/firebase/types/eventData";
export const useUpdateEvent = () => {
const {user: currentUser} = useAuthContext()
const queryClients = useQueryClient()
return useMutation({
mutationKey: ["updateEvent"],
mutationFn: async (eventData: Partial<EventData>) => {
try {
await firestore()
.collection("Events")
.doc(eventData.id)
.update(eventData);
} catch (e) {
console.error(e)
}
},
onSuccess: () => {
queryClients.invalidateQueries("events")
}
})
}

View File

@ -0,0 +1,24 @@
import {useMutation, useQueryClient} from "react-query";
import firestore from "@react-native-firebase/firestore";
import {IGrocery} from "@/hooks/firebase/types/groceryData";
export const useUpdateGrocery = () => {
const queryClients = useQueryClient()
return useMutation({
mutationKey: ["updateGrocery"],
mutationFn: async (groceryData: Partial<IGrocery>) => {
try {
await firestore()
.collection("Groceries")
.doc(groceryData.id)
.update(groceryData);
} catch (e) {
console.error(e)
}
},
onSuccess: () => {
queryClients.invalidateQueries("groceries")
}
})
}

View File

@ -962,6 +962,8 @@ PODS:
- BVLinearGradient (2.8.3): - BVLinearGradient (2.8.3):
- React-Core - React-Core
- DoubleConversion (1.1.6) - DoubleConversion (1.1.6)
- EASClient (0.12.0):
- ExpoModulesCore
- EXApplication (5.9.1): - EXApplication (5.9.1):
- ExpoModulesCore - ExpoModulesCore
- EXBarCodeScanner (13.0.1): - EXBarCodeScanner (13.0.1):
@ -977,6 +979,8 @@ PODS:
- EXJSONUtils (0.13.1) - EXJSONUtils (0.13.1)
- EXManifests (0.14.3): - EXManifests (0.14.3):
- ExpoModulesCore - ExpoModulesCore
- EXNotifications (0.28.18):
- ExpoModulesCore
- Expo (51.0.34): - Expo (51.0.34):
- ExpoModulesCore - ExpoModulesCore
- expo-dev-client (4.0.27): - expo-dev-client (4.0.27):
@ -1186,10 +1190,6 @@ PODS:
- ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- Yoga - Yoga
- ExpoAdapterGoogleSignIn (13.1.0):
- ExpoModulesCore
- GoogleSignIn (~> 7.1)
- React-Core
- ExpoAsset (10.0.10): - ExpoAsset (10.0.10):
- ExpoModulesCore - ExpoModulesCore
- ExpoCamera (15.0.16): - ExpoCamera (15.0.16):
@ -1198,6 +1198,8 @@ PODS:
- ZXingObjC/PDF417 - ZXingObjC/PDF417
- ExpoCrypto (13.0.2): - ExpoCrypto (13.0.2):
- ExpoModulesCore - ExpoModulesCore
- ExpoDevice (6.0.2):
- ExpoModulesCore
- ExpoFileSystem (17.0.1): - ExpoFileSystem (17.0.1):
- ExpoModulesCore - ExpoModulesCore
- ExpoFont (12.0.10): - ExpoFont (12.0.10):
@ -1257,6 +1259,35 @@ PODS:
- ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- Yoga - Yoga
- EXStructuredHeaders (3.8.0)
- EXUpdates (0.25.27):
- DoubleConversion
- EASClient
- EXManifests
- ExpoModulesCore
- EXStructuredHeaders
- EXUpdatesInterface
- glog
- hermes-engine
- RCT-Folly (= 2024.01.01.00)
- RCTRequired
- RCTTypeSafety
- ReachabilitySwift
- React-Codegen
- React-Core
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- React-NativeModulesApple
- React-RCTFabric
- React-rendererdebug
- React-utils
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- "sqlite3 (~> 3.45.3+1)"
- Yoga
- EXUpdatesInterface (0.16.2): - EXUpdatesInterface (0.16.2):
- ExpoModulesCore - ExpoModulesCore
- FBLazyVector (0.74.3) - FBLazyVector (0.74.3)
@ -1351,10 +1382,6 @@ PODS:
- GoogleUtilities/Environment (~> 7.7) - GoogleUtilities/Environment (~> 7.7)
- nanopb (< 2.30911.0, >= 2.30908.0) - nanopb (< 2.30911.0, >= 2.30908.0)
- PromisesObjC (< 3.0, >= 1.2) - PromisesObjC (< 3.0, >= 1.2)
- GoogleSignIn (7.1.0):
- AppAuth (< 2.0, >= 1.7.3)
- GTMAppAuth (< 5.0, >= 4.1.1)
- GTMSessionFetcher/Core (~> 3.3)
- GoogleUtilities/AppDelegateSwizzler (7.13.3): - GoogleUtilities/AppDelegateSwizzler (7.13.3):
- GoogleUtilities/Environment - GoogleUtilities/Environment
- GoogleUtilities/Logger - GoogleUtilities/Logger
@ -1458,9 +1485,6 @@ PODS:
- gRPC-Core/Privacy (= 1.62.5) - gRPC-Core/Privacy (= 1.62.5)
- gRPC-Core/Interface (1.62.5) - gRPC-Core/Interface (1.62.5)
- gRPC-Core/Privacy (1.62.5) - gRPC-Core/Privacy (1.62.5)
- GTMAppAuth (4.1.1):
- AppAuth/Core (~> 1.7)
- GTMSessionFetcher/Core (< 4.0, >= 3.3)
- GTMSessionFetcher/Core (3.5.0) - GTMSessionFetcher/Core (3.5.0)
- hermes-engine (0.74.3): - hermes-engine (0.74.3):
- hermes-engine/Pre-built (= 0.74.3) - hermes-engine/Pre-built (= 0.74.3)
@ -1496,6 +1520,7 @@ PODS:
- FBLazyVector (= 0.74.3) - FBLazyVector (= 0.74.3)
- RCTRequired (= 0.74.3) - RCTRequired (= 0.74.3)
- React-Core (= 0.74.3) - React-Core (= 0.74.3)
- ReachabilitySwift (5.2.3)
- React (0.74.3): - React (0.74.3):
- React-Core (= 0.74.3) - React-Core (= 0.74.3)
- React-Core/DevSupport (= 0.74.3) - React-Core/DevSupport (= 0.74.3)
@ -2704,9 +2729,6 @@ PODS:
- ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- Yoga - Yoga
- RNGoogleSignin (13.1.0):
- GoogleSignIn (~> 7.1)
- React-Core
- RNReanimated (3.10.1): - RNReanimated (3.10.1):
- DoubleConversion - DoubleConversion
- glog - glog
@ -2753,6 +2775,9 @@ PODS:
- RNSVG (15.7.1): - RNSVG (15.7.1):
- React-Core - React-Core
- SocketRocket (0.7.0) - SocketRocket (0.7.0)
- "sqlite3 (3.45.3+1)":
- "sqlite3/common (= 3.45.3+1)"
- "sqlite3/common (3.45.3+1)"
- Yoga (0.0.0) - Yoga (0.0.0)
- ZXingObjC/Core (3.6.9) - ZXingObjC/Core (3.6.9)
- ZXingObjC/OneD (3.6.9): - ZXingObjC/OneD (3.6.9):
@ -2764,21 +2789,23 @@ DEPENDENCIES:
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`) - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
- BVLinearGradient (from `../node_modules/react-native-linear-gradient`) - BVLinearGradient (from `../node_modules/react-native-linear-gradient`)
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- EASClient (from `../node_modules/expo-eas-client/ios`)
- EXApplication (from `../node_modules/expo-application/ios`) - EXApplication (from `../node_modules/expo-application/ios`)
- EXBarCodeScanner (from `../node_modules/expo-barcode-scanner/ios`) - EXBarCodeScanner (from `../node_modules/expo-barcode-scanner/ios`)
- EXConstants (from `../node_modules/expo-constants/ios`) - EXConstants (from `../node_modules/expo-constants/ios`)
- EXImageLoader (from `../node_modules/expo-image-loader/ios`) - EXImageLoader (from `../node_modules/expo-image-loader/ios`)
- EXJSONUtils (from `../node_modules/expo-json-utils/ios`) - EXJSONUtils (from `../node_modules/expo-json-utils/ios`)
- EXManifests (from `../node_modules/expo-manifests/ios`) - EXManifests (from `../node_modules/expo-manifests/ios`)
- EXNotifications (from `../node_modules/expo-notifications/ios`)
- Expo (from `../node_modules/expo`) - Expo (from `../node_modules/expo`)
- expo-dev-client (from `../node_modules/expo-dev-client/ios`) - expo-dev-client (from `../node_modules/expo-dev-client/ios`)
- expo-dev-launcher (from `../node_modules/expo-dev-launcher`) - expo-dev-launcher (from `../node_modules/expo-dev-launcher`)
- expo-dev-menu (from `../node_modules/expo-dev-menu`) - expo-dev-menu (from `../node_modules/expo-dev-menu`)
- expo-dev-menu-interface (from `../node_modules/expo-dev-menu-interface/ios`) - expo-dev-menu-interface (from `../node_modules/expo-dev-menu-interface/ios`)
- "ExpoAdapterGoogleSignIn (from `../node_modules/@react-native-google-signin/google-signin/expo/ios`)"
- ExpoAsset (from `../node_modules/expo-asset/ios`) - ExpoAsset (from `../node_modules/expo-asset/ios`)
- ExpoCamera (from `../node_modules/expo-camera/ios`) - ExpoCamera (from `../node_modules/expo-camera/ios`)
- ExpoCrypto (from `../node_modules/expo-crypto/ios`) - ExpoCrypto (from `../node_modules/expo-crypto/ios`)
- ExpoDevice (from `../node_modules/expo-device/ios`)
- ExpoFileSystem (from `../node_modules/expo-file-system/ios`) - ExpoFileSystem (from `../node_modules/expo-file-system/ios`)
- ExpoFont (from `../node_modules/expo-font/ios`) - ExpoFont (from `../node_modules/expo-font/ios`)
- ExpoHead (from `../node_modules/expo-router/ios`) - ExpoHead (from `../node_modules/expo-router/ios`)
@ -2788,6 +2815,8 @@ DEPENDENCIES:
- ExpoSystemUI (from `../node_modules/expo-system-ui/ios`) - ExpoSystemUI (from `../node_modules/expo-system-ui/ios`)
- ExpoWebBrowser (from `../node_modules/expo-web-browser/ios`) - ExpoWebBrowser (from `../node_modules/expo-web-browser/ios`)
- EXSplashScreen (from `../node_modules/expo-splash-screen/ios`) - EXSplashScreen (from `../node_modules/expo-splash-screen/ios`)
- EXStructuredHeaders (from `../node_modules/expo-structured-headers/ios`)
- EXUpdates (from `../node_modules/expo-updates/ios`)
- EXUpdatesInterface (from `../node_modules/expo-updates-interface/ios`) - EXUpdatesInterface (from `../node_modules/expo-updates-interface/ios`)
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
- fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`) - fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`)
@ -2853,7 +2882,6 @@ DEPENDENCIES:
- "RNFBFirestore (from `../node_modules/@react-native-firebase/firestore`)" - "RNFBFirestore (from `../node_modules/@react-native-firebase/firestore`)"
- "RNFBFunctions (from `../node_modules/@react-native-firebase/functions`)" - "RNFBFunctions (from `../node_modules/@react-native-firebase/functions`)"
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
- "RNGoogleSignin (from `../node_modules/@react-native-google-signin/google-signin`)"
- RNReanimated (from `../node_modules/react-native-reanimated`) - RNReanimated (from `../node_modules/react-native-reanimated`)
- RNScreens (from `../node_modules/react-native-screens`) - RNScreens (from `../node_modules/react-native-screens`)
- RNSVG (from `../node_modules/react-native-svg`) - RNSVG (from `../node_modules/react-native-svg`)
@ -2881,18 +2909,18 @@ SPEC REPOS:
- FirebaseSessions - FirebaseSessions
- FirebaseSharedSwift - FirebaseSharedSwift
- GoogleDataTransport - GoogleDataTransport
- GoogleSignIn
- GoogleUtilities - GoogleUtilities
- "gRPC-C++" - "gRPC-C++"
- gRPC-Core - gRPC-Core
- GTMAppAuth
- GTMSessionFetcher - GTMSessionFetcher
- leveldb-library - leveldb-library
- nanopb - nanopb
- PromisesObjC - PromisesObjC
- PromisesSwift - PromisesSwift
- ReachabilitySwift
- RecaptchaInterop - RecaptchaInterop
- SocketRocket - SocketRocket
- sqlite3
- ZXingObjC - ZXingObjC
EXTERNAL SOURCES: EXTERNAL SOURCES:
@ -2902,6 +2930,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-linear-gradient" :path: "../node_modules/react-native-linear-gradient"
DoubleConversion: DoubleConversion:
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
EASClient:
:path: "../node_modules/expo-eas-client/ios"
EXApplication: EXApplication:
:path: "../node_modules/expo-application/ios" :path: "../node_modules/expo-application/ios"
EXBarCodeScanner: EXBarCodeScanner:
@ -2914,6 +2944,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/expo-json-utils/ios" :path: "../node_modules/expo-json-utils/ios"
EXManifests: EXManifests:
:path: "../node_modules/expo-manifests/ios" :path: "../node_modules/expo-manifests/ios"
EXNotifications:
:path: "../node_modules/expo-notifications/ios"
Expo: Expo:
:path: "../node_modules/expo" :path: "../node_modules/expo"
expo-dev-client: expo-dev-client:
@ -2924,14 +2956,14 @@ EXTERNAL SOURCES:
:path: "../node_modules/expo-dev-menu" :path: "../node_modules/expo-dev-menu"
expo-dev-menu-interface: expo-dev-menu-interface:
:path: "../node_modules/expo-dev-menu-interface/ios" :path: "../node_modules/expo-dev-menu-interface/ios"
ExpoAdapterGoogleSignIn:
:path: "../node_modules/@react-native-google-signin/google-signin/expo/ios"
ExpoAsset: ExpoAsset:
:path: "../node_modules/expo-asset/ios" :path: "../node_modules/expo-asset/ios"
ExpoCamera: ExpoCamera:
:path: "../node_modules/expo-camera/ios" :path: "../node_modules/expo-camera/ios"
ExpoCrypto: ExpoCrypto:
:path: "../node_modules/expo-crypto/ios" :path: "../node_modules/expo-crypto/ios"
ExpoDevice:
:path: "../node_modules/expo-device/ios"
ExpoFileSystem: ExpoFileSystem:
:path: "../node_modules/expo-file-system/ios" :path: "../node_modules/expo-file-system/ios"
ExpoFont: ExpoFont:
@ -2950,6 +2982,10 @@ EXTERNAL SOURCES:
:path: "../node_modules/expo-web-browser/ios" :path: "../node_modules/expo-web-browser/ios"
EXSplashScreen: EXSplashScreen:
:path: "../node_modules/expo-splash-screen/ios" :path: "../node_modules/expo-splash-screen/ios"
EXStructuredHeaders:
:path: "../node_modules/expo-structured-headers/ios"
EXUpdates:
:path: "../node_modules/expo-updates/ios"
EXUpdatesInterface: EXUpdatesInterface:
:path: "../node_modules/expo-updates-interface/ios" :path: "../node_modules/expo-updates-interface/ios"
FBLazyVector: FBLazyVector:
@ -3077,8 +3113,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/@react-native-firebase/functions" :path: "../node_modules/@react-native-firebase/functions"
RNGestureHandler: RNGestureHandler:
:path: "../node_modules/react-native-gesture-handler" :path: "../node_modules/react-native-gesture-handler"
RNGoogleSignin:
:path: "../node_modules/@react-native-google-signin/google-signin"
RNReanimated: RNReanimated:
:path: "../node_modules/react-native-reanimated" :path: "../node_modules/react-native-reanimated"
RNScreens: RNScreens:
@ -3095,21 +3129,23 @@ SPEC CHECKSUMS:
BoringSSL-GRPC: 1e2348957acdbcad360b80a264a90799984b2ba6 BoringSSL-GRPC: 1e2348957acdbcad360b80a264a90799984b2ba6
BVLinearGradient: 880f91a7854faff2df62518f0281afb1c60d49a3 BVLinearGradient: 880f91a7854faff2df62518f0281afb1c60d49a3
DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5 DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5
EASClient: 1509a9a6b48b932ec61667644634daf2562983b8
EXApplication: c08200c34daca7af7fd76ac4b9d606077410e8ad EXApplication: c08200c34daca7af7fd76ac4b9d606077410e8ad
EXBarCodeScanner: e2dd9b42c1b522a2adc9202b1dfbc64cb34456d1 EXBarCodeScanner: e2dd9b42c1b522a2adc9202b1dfbc64cb34456d1
EXConstants: 409690fbfd5afea964e5e9d6c4eb2c2b59222c59 EXConstants: 409690fbfd5afea964e5e9d6c4eb2c2b59222c59
EXImageLoader: ab589d67d6c5f2c33572afea9917304418566334 EXImageLoader: ab589d67d6c5f2c33572afea9917304418566334
EXJSONUtils: 30c17fd9cc364d722c0946a550dfbf1be92ef6a4 EXJSONUtils: 30c17fd9cc364d722c0946a550dfbf1be92ef6a4
EXManifests: c1fab4c3237675e7b0299ea8df0bcb14baca4f42 EXManifests: c1fab4c3237675e7b0299ea8df0bcb14baca4f42
EXNotifications: dd289340c26bc5388e440fc90d0b2c661cbd0285
Expo: 963ef4ae102e4f4ac114a8510d70127c44adffbf Expo: 963ef4ae102e4f4ac114a8510d70127c44adffbf
expo-dev-client: 85deba11af998ea86e62093b17fce56aa2c6f26b expo-dev-client: 85deba11af998ea86e62093b17fce56aa2c6f26b
expo-dev-launcher: fe4f2c0a0aa627449eeaec5f9f11e04090f97c40 expo-dev-launcher: fe4f2c0a0aa627449eeaec5f9f11e04090f97c40
expo-dev-menu: 5b14897ecce3a8cf9e9cf9109344c2c192a3766a expo-dev-menu: 5b14897ecce3a8cf9e9cf9109344c2c192a3766a
expo-dev-menu-interface: be32c09f1e03833050f0ee290dcc86b3ad0e73e4 expo-dev-menu-interface: be32c09f1e03833050f0ee290dcc86b3ad0e73e4
ExpoAdapterGoogleSignIn: da10ae7e7c1d73a10c2facebcdfe5ebea8e073ce
ExpoAsset: 323700f291684f110fb55f0d4022a3362ea9f875 ExpoAsset: 323700f291684f110fb55f0d4022a3362ea9f875
ExpoCamera: 929be541d1c1319fcf32f9f5d9df8b97804346b5 ExpoCamera: 929be541d1c1319fcf32f9f5d9df8b97804346b5
ExpoCrypto: 156078f266bf28f80ecf5e2a9c3a0d6ffce07a1c ExpoCrypto: 156078f266bf28f80ecf5e2a9c3a0d6ffce07a1c
ExpoDevice: fc94f0e42ecdfd897e7590f2874fc64dfa7e9b1c
ExpoFileSystem: 80bfe850b1f9922c16905822ecbf97acd711dc51 ExpoFileSystem: 80bfe850b1f9922c16905822ecbf97acd711dc51
ExpoFont: 00756e6c796d8f7ee8d211e29c8b619e75cbf238 ExpoFont: 00756e6c796d8f7ee8d211e29c8b619e75cbf238
ExpoHead: fcb28a68ed4ba28f177394d2dfb8a0a8824cd103 ExpoHead: fcb28a68ed4ba28f177394d2dfb8a0a8824cd103
@ -3119,6 +3155,8 @@ SPEC CHECKSUMS:
ExpoSystemUI: d4f065a016cae6721b324eb659cdee4d4cf0cb26 ExpoSystemUI: d4f065a016cae6721b324eb659cdee4d4cf0cb26
ExpoWebBrowser: 7595ccac6938eb65b076385fd23d035db9ecdc8e ExpoWebBrowser: 7595ccac6938eb65b076385fd23d035db9ecdc8e
EXSplashScreen: d8b3c547b9b18a41d80c6f6b274c4c26664febd4 EXSplashScreen: d8b3c547b9b18a41d80c6f6b274c4c26664febd4
EXStructuredHeaders: cb8d1f698e144f4c5547b4c4963e1552f5d2b457
EXUpdates: fe42e1cdace89a8db450351fbc38577b237af76b
EXUpdatesInterface: 996527fd7d1a5d271eb523258d603f8f92038f24 EXUpdatesInterface: 996527fd7d1a5d271eb523258d603f8f92038f24
FBLazyVector: 7e977dd099937dc5458851233141583abba49ff2 FBLazyVector: 7e977dd099937dc5458851233141583abba49ff2
Firebase: cec914dab6fd7b1bd8ab56ea07ce4e03dd251c2d Firebase: cec914dab6fd7b1bd8ab56ea07ce4e03dd251c2d
@ -3140,11 +3178,9 @@ SPEC CHECKSUMS:
fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120 fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120
glog: fdfdfe5479092de0c4bdbebedd9056951f092c4f glog: fdfdfe5479092de0c4bdbebedd9056951f092c4f
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
GoogleSignIn: d4281ab6cf21542b1cfaff85c191f230b399d2db
GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15
"gRPC-C++": e725ef63c4475d7cdb7e2cf16eb0fde84bd9ee51 "gRPC-C++": e725ef63c4475d7cdb7e2cf16eb0fde84bd9ee51
gRPC-Core: eee4be35df218649fe66d721a05a7f27a28f069b gRPC-Core: eee4be35df218649fe66d721a05a7f27a28f069b
GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
hermes-engine: 1f547997900dd0752dc0cc0ae6dd16173c49e09b hermes-engine: 1f547997900dd0752dc0cc0ae6dd16173c49e09b
leveldb-library: e8eadf9008a61f9e1dde3978c086d2b6d9b9dc28 leveldb-library: e8eadf9008a61f9e1dde3978c086d2b6d9b9dc28
@ -3155,6 +3191,7 @@ SPEC CHECKSUMS:
RCTDeprecation: 4c7eeb42be0b2e95195563c49be08d0b839d22b4 RCTDeprecation: 4c7eeb42be0b2e95195563c49be08d0b839d22b4
RCTRequired: d530a0f489699c8500e944fde963102c42dcd0c2 RCTRequired: d530a0f489699c8500e944fde963102c42dcd0c2
RCTTypeSafety: b20878506b094fa3d9007d7b9e4be0faa3562499 RCTTypeSafety: b20878506b094fa3d9007d7b9e4be0faa3562499
ReachabilitySwift: 7f151ff156cea1481a8411701195ac6a984f4979
React: 2f9da0177233f60fa3462d83fcccde245759f81a React: 2f9da0177233f60fa3462d83fcccde245759f81a
React-callinvoker: d0205f0dcebf72ec27263ab41e3a5ad827ed503f React-callinvoker: d0205f0dcebf72ec27263ab41e3a5ad827ed503f
React-Codegen: 27212e14727ad7d6d9fd1b1967fbf21929e4ce29 React-Codegen: 27212e14727ad7d6d9fd1b1967fbf21929e4ce29
@ -3210,11 +3247,11 @@ SPEC CHECKSUMS:
RNFBFirestore: e47cdde04ea3d9e73e58e037e1aa1d0b1141c316 RNFBFirestore: e47cdde04ea3d9e73e58e037e1aa1d0b1141c316
RNFBFunctions: 738cc9e2177d060d29b5d143ef2f9ed0eda4bb1f RNFBFunctions: 738cc9e2177d060d29b5d143ef2f9ed0eda4bb1f
RNGestureHandler: 20a4307fd21cbff339abfcfa68192f3f0a6a518b RNGestureHandler: 20a4307fd21cbff339abfcfa68192f3f0a6a518b
RNGoogleSignin: 9e68b9bcc3888219357924e32ee563624745647d
RNReanimated: d51431fd3597a8f8320319dce8e42cee82a5445f RNReanimated: d51431fd3597a8f8320319dce8e42cee82a5445f
RNScreens: 30249f9331c3b00ae7cb7922e11f58b3ed369c07 RNScreens: 30249f9331c3b00ae7cb7922e11f58b3ed369c07
RNSVG: 4590aa95758149fa27c5c83e54a6a466349a1688 RNSVG: 4590aa95758149fa27c5c83e54a6a466349a1688
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
sqlite3: 02d1f07eaaa01f80a1c16b4b31dfcbb3345ee01a
Yoga: bd92064a0d558be92786820514d74fc4dddd1233 Yoga: bd92064a0d558be92786820514d74fc4dddd1233
ZXingObjC: 8898711ab495761b2dbbdec76d90164a6d7e14c5 ZXingObjC: 8898711ab495761b2dbbdec76d90164a6d7e14c5

View File

@ -298,6 +298,9 @@
"${PODS_CONFIGURATION_BUILD_DIR}/EXApplication/ExpoApplication_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/EXApplication/ExpoApplication_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXNotifications/ExpoNotifications_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXUpdates/EXUpdates.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoDevice/ExpoDevice_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseAuth/FirebaseAuth_Privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseAuth/FirebaseAuth_Privacy.bundle",
@ -308,13 +311,12 @@
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestore/FirebaseFirestore_Privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestore/FirebaseFirestore_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestoreInternal/FirebaseFirestoreInternal_Privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestoreInternal/FirebaseFirestoreInternal_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations/FirebaseInstallations_Privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations/FirebaseInstallations_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/GTMAppAuth/GTMAppAuth_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher/GTMSessionFetcher_Core_Privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher/GTMSessionFetcher_Core_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/GoogleDataTransport/GoogleDataTransport_Privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/GoogleDataTransport/GoogleDataTransport_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/GoogleSignIn/GoogleSignIn.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities_Privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises_Privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/PromisesSwift/Promises_Privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/PromisesSwift/Promises_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ReachabilitySwift/ReachabilitySwift.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/abseil/xcprivacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/abseil/xcprivacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-launcher/EXDevLauncher.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-launcher/EXDevLauncher.bundle",
@ -331,6 +333,9 @@
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoApplication_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoApplication_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoNotifications_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXUpdates.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoDevice_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseAuth_Privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseAuth_Privacy.bundle",
@ -341,13 +346,12 @@
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseFirestore_Privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseFirestore_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseFirestoreInternal_Privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseFirestoreInternal_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseInstallations_Privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseInstallations_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GTMAppAuth_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GTMSessionFetcher_Core_Privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GTMSessionFetcher_Core_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleDataTransport_Privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleDataTransport_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleSignIn.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleUtilities_Privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleUtilities_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FBLPromises_Privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FBLPromises_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Promises_Privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Promises_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ReachabilitySwift.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/xcprivacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/xcprivacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevLauncher.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevLauncher.bundle",
@ -437,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;
@ -468,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";

View File

@ -84,6 +84,11 @@
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
</array> </array>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string>SplashScreen</string> <string>SplashScreen</string>

View File

@ -5,8 +5,12 @@
<key>EXUpdatesCheckOnLaunch</key> <key>EXUpdatesCheckOnLaunch</key>
<string>ALWAYS</string> <string>ALWAYS</string>
<key>EXUpdatesEnabled</key> <key>EXUpdatesEnabled</key>
<false/> <true/>
<key>EXUpdatesLaunchWaitMs</key> <key>EXUpdatesLaunchWaitMs</key>
<integer>0</integer> <integer>0</integer>
<key>EXUpdatesRuntimeVersion</key>
<string>1.0.0</string>
<key>EXUpdatesURL</key>
<string>https://u.expo.dev/bdb8c57b-25bb-4d36-b3b8-5b09c5092f52</string>
</dict> </dict>
</plist> </plist>

View File

@ -1,5 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict/> <dict>
<key>aps-environment</key>
<string>development</string>
</dict>
</plist> </plist>

View File

@ -19,7 +19,7 @@
"lint": "expo lint", "lint": "expo lint",
"submit": "npx eas-cli submit -p ios", "submit": "npx eas-cli submit -p ios",
"prebuild": "npx expo prebuild -p ios", "prebuild": "npx expo prebuild -p ios",
"prebuild-build-submit-ios": "npm run prebuild && npm run build-ios && npm run submit", "prebuild-build-submit-ios": "yarn run prebuild && yarn run build-ios && yarn run submit",
"prebuild-build-submit-ios-cicd": "yarn build-ios-cicd", "prebuild-build-submit-ios-cicd": "yarn build-ios-cicd",
"prebuild-build-submit-cicd": "yarn build-cicd" "prebuild-build-submit-cicd": "yarn build-cicd"
}, },
@ -35,7 +35,6 @@
"@react-native-firebase/crashlytics": "^20.3.0", "@react-native-firebase/crashlytics": "^20.3.0",
"@react-native-firebase/firestore": "^20.4.0", "@react-native-firebase/firestore": "^20.4.0",
"@react-native-firebase/functions": "^20.4.0", "@react-native-firebase/functions": "^20.4.0",
"@react-native-google-signin/google-signin": "^13.1.0",
"@react-navigation/drawer": "^6.7.2", "@react-navigation/drawer": "^6.7.2",
"@react-navigation/native": "^6.0.2", "@react-navigation/native": "^6.0.2",
"date-fns": "^3.6.0", "date-fns": "^3.6.0",
@ -47,13 +46,16 @@
"expo-camera": "~15.0.16", "expo-camera": "~15.0.16",
"expo-constants": "~16.0.2", "expo-constants": "~16.0.2",
"expo-dev-client": "~4.0.27", "expo-dev-client": "~4.0.27",
"expo-device": "~6.0.2",
"expo-font": "~12.0.9", "expo-font": "~12.0.9",
"expo-image-picker": "~15.0.7", "expo-image-picker": "~15.0.7",
"expo-linking": "~6.3.1", "expo-linking": "~6.3.1",
"expo-notifications": "~0.28.18",
"expo-router": "~3.5.20", "expo-router": "~3.5.20",
"expo-splash-screen": "~0.27.5", "expo-splash-screen": "~0.27.5",
"expo-status-bar": "~1.12.1", "expo-status-bar": "~1.12.1",
"expo-system-ui": "~3.0.7", "expo-system-ui": "~3.0.7",
"expo-updates": "~0.25.27",
"expo-web-browser": "~13.0.3", "expo-web-browser": "~13.0.3",
"firebase-admin": "^12.3.1", "firebase-admin": "^12.3.1",
"firebase-functions": "^5.1.0", "firebase-functions": "^5.1.0",

128
yarn.lock
View File

@ -1025,6 +1025,20 @@
dotenv-expand "~11.0.6" dotenv-expand "~11.0.6"
getenv "^1.0.0" getenv "^1.0.0"
"@expo/fingerprint@^0.10.2":
version "0.10.3"
resolved "https://registry.yarnpkg.com/@expo/fingerprint/-/fingerprint-0.10.3.tgz#87c2811fe7773ec7d00cae86ab041d578f9041b5"
integrity sha512-h/BnnyloJyMSrzeXonKLE6HfiMpRg3e9m8CAv+eUaAozG9heKMG9ftHW4cfm2StDYj/rWjFc5YK6MSIX6qd+xg==
dependencies:
"@expo/spawn-async" "^1.7.2"
chalk "^4.1.2"
debug "^4.3.4"
find-up "^5.0.0"
minimatch "^3.0.4"
p-limit "^3.1.0"
resolve-from "^5.0.0"
semver "^7.6.0"
"@expo/image-utils@^0.5.0": "@expo/image-utils@^0.5.0":
version "0.5.1" version "0.5.1"
resolved "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.5.1.tgz" resolved "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.5.1.tgz"
@ -1686,6 +1700,11 @@
dependencies: dependencies:
"@hapi/hoek" "^9.0.0" "@hapi/hoek" "^9.0.0"
"@ide/backoff@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@ide/backoff/-/backoff-1.0.0.tgz#466842c25bd4a4833e0642fab41ccff064010176"
integrity sha512-F0YfUDjvT+Mtt/R4xdl2X0EYCHMMiJqNLdxHD++jDT5ydEFIyqbCHh51Qx2E211dgZprPKhV7sHmnXKpLuvc5g==
"@isaacs/cliui@^8.0.2": "@isaacs/cliui@^8.0.2":
version "8.0.2" version "8.0.2"
resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz" resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz"
@ -2283,11 +2302,6 @@
resolved "https://registry.npmjs.org/@react-native-firebase/functions/-/functions-20.4.0.tgz" resolved "https://registry.npmjs.org/@react-native-firebase/functions/-/functions-20.4.0.tgz"
integrity sha512-g4kAWZboTE9cTdT7KT6k1haHDmEBA36bPCvrh2MJ2RACo2JxotB2MIOEPZ5U/cT94eIAlgI5YtxQQGQfC+VcBQ== integrity sha512-g4kAWZboTE9cTdT7KT6k1haHDmEBA36bPCvrh2MJ2RACo2JxotB2MIOEPZ5U/cT94eIAlgI5YtxQQGQfC+VcBQ==
"@react-native-google-signin/google-signin@^13.1.0":
version "13.1.0"
resolved "https://registry.npmjs.org/@react-native-google-signin/google-signin/-/google-signin-13.1.0.tgz"
integrity sha512-C2/sqb0/s0c+Dwc/mykASZsRuHxGqn7SFrCxCY9D8p8IOQO05haInhCc7lzraJshRixGva5c/4usQZ71HMYSEQ==
"@react-native/assets-registry@0.74.85": "@react-native/assets-registry@0.74.85":
version "0.74.85" version "0.74.85"
resolved "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.74.85.tgz" resolved "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.74.85.tgz"
@ -3247,6 +3261,11 @@ application-config-path@^0.1.0:
resolved "https://registry.npmjs.org/application-config-path/-/application-config-path-0.1.1.tgz" resolved "https://registry.npmjs.org/application-config-path/-/application-config-path-0.1.1.tgz"
integrity sha512-zy9cHePtMP0YhwG+CfHm0bgwdnga2X3gZexpdCwEj//dpb+TKajtiC8REEUJUSq6Ab4f9cgNy2l8ObXzCXFkEw== integrity sha512-zy9cHePtMP0YhwG+CfHm0bgwdnga2X3gZexpdCwEj//dpb+TKajtiC8REEUJUSq6Ab4f9cgNy2l8ObXzCXFkEw==
arg@4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.0.tgz#583c518199419e0037abb74062c37f8519e575f0"
integrity sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg==
arg@5.0.2: arg@5.0.2:
version "5.0.2" version "5.0.2"
resolved "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz" resolved "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz"
@ -3313,6 +3332,17 @@ asap@~2.0.3, asap@~2.0.6:
resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz" resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz"
integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==
assert@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/assert/-/assert-2.1.0.tgz#6d92a238d05dc02e7427c881fb8be81c8448b2dd"
integrity sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==
dependencies:
call-bind "^1.0.2"
is-nan "^1.3.2"
object-is "^1.1.5"
object.assign "^4.1.4"
util "^0.12.5"
ast-types@0.15.2: ast-types@0.15.2:
version "0.15.2" version "0.15.2"
resolved "https://registry.npmjs.org/ast-types/-/ast-types-0.15.2.tgz" resolved "https://registry.npmjs.org/ast-types/-/ast-types-0.15.2.tgz"
@ -3492,6 +3522,11 @@ babel-preset-jest@^29.6.3:
babel-plugin-jest-hoist "^29.6.3" babel-plugin-jest-hoist "^29.6.3"
babel-preset-current-node-syntax "^1.0.0" babel-preset-current-node-syntax "^1.0.0"
badgin@^1.1.5:
version "1.2.3"
resolved "https://registry.yarnpkg.com/badgin/-/badgin-1.2.3.tgz#994b5f519827d7d5422224825b2c8faea2bc43ad"
integrity sha512-NQGA7LcfCpSzIbGRbkgjgdWkjy7HI+Th5VLxTJfW5EeaAf3fnS+xWQaQOCYiny+q6QSvxqoSO04vCx+4u++EJw==
balanced-match@^1.0.0: balanced-match@^1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
@ -3721,7 +3756,7 @@ calendarize@^1.1.1:
resolved "https://registry.npmjs.org/calendarize/-/calendarize-1.1.1.tgz" resolved "https://registry.npmjs.org/calendarize/-/calendarize-1.1.1.tgz"
integrity sha512-C2JyBAtNp2NG4DX4fA1EILggLt/5PlYzvQR0crHktoAPBc9TlIfdhzg7tWekCbe+pH6+9qoK+FhPbi+vYJJlqw== integrity sha512-C2JyBAtNp2NG4DX4fA1EILggLt/5PlYzvQR0crHktoAPBc9TlIfdhzg7tWekCbe+pH6+9qoK+FhPbi+vYJJlqw==
call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7:
version "1.0.7" version "1.0.7"
resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz" resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz"
integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==
@ -4413,7 +4448,7 @@ define-lazy-prop@^2.0.0:
resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz" resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz"
integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==
define-properties@^1.2.0, define-properties@^1.2.1: define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1:
version "1.2.1" version "1.2.1"
resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz" resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz"
integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==
@ -4964,6 +4999,18 @@ expo-dev-menu@5.0.21:
expo-dev-menu-interface "1.8.3" expo-dev-menu-interface "1.8.3"
semver "^7.5.4" semver "^7.5.4"
expo-device@~6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/expo-device/-/expo-device-6.0.2.tgz#9bc3eccd16509c2819c225cc2ca8f7c3e3bdd11e"
integrity sha512-sCt91CuTmAuMXX4SlFOn4lIos2UIr8vb0jDstDDZXys6kErcj0uynC7bQAMreU5uRUTKMAl4MAMpKt9ufCXPBw==
dependencies:
ua-parser-js "^0.7.33"
expo-eas-client@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/expo-eas-client/-/expo-eas-client-0.12.0.tgz#e8b6f7d33873e6f630f37f7bfc41646ae7b0b2a9"
integrity sha512-Jkww9Cwpv0z7DdLYiRX0r4fqBEcI9cKqTn7cHx63S09JaZ2rcwEE4zYHgrXwjahO+tU2VW8zqH+AJl6RhhW4zA==
expo-file-system@~17.0.1: expo-file-system@~17.0.1:
version "17.0.1" version "17.0.1"
resolved "https://registry.npmjs.org/expo-file-system/-/expo-file-system-17.0.1.tgz" resolved "https://registry.npmjs.org/expo-file-system/-/expo-file-system-17.0.1.tgz"
@ -5034,6 +5081,20 @@ expo-modules-core@1.12.24:
dependencies: dependencies:
invariant "^2.2.4" invariant "^2.2.4"
expo-notifications@~0.28.18:
version "0.28.18"
resolved "https://registry.yarnpkg.com/expo-notifications/-/expo-notifications-0.28.18.tgz#a3e5488429079d664885e975985dd2d6bdb52a5b"
integrity sha512-oRvr8rYhbbKNhVgcO+fj5g5g6vS0umGcElpeMSWa0KudUfOOgV6nNLvv5M89393z2Ahd7wPK4bnK8lygc0nCPQ==
dependencies:
"@expo/image-utils" "^0.5.0"
"@ide/backoff" "^1.0.0"
abort-controller "^3.0.0"
assert "^2.0.0"
badgin "^1.1.5"
expo-application "~5.9.0"
expo-constants "~16.0.0"
fs-extra "^9.1.0"
expo-router@~3.5.20: expo-router@~3.5.20:
version "3.5.23" version "3.5.23"
resolved "https://registry.npmjs.org/expo-router/-/expo-router-3.5.23.tgz" resolved "https://registry.npmjs.org/expo-router/-/expo-router-3.5.23.tgz"
@ -5061,6 +5122,11 @@ expo-status-bar@~1.12.1:
resolved "https://registry.npmjs.org/expo-status-bar/-/expo-status-bar-1.12.1.tgz" resolved "https://registry.npmjs.org/expo-status-bar/-/expo-status-bar-1.12.1.tgz"
integrity sha512-/t3xdbS8KB0prj5KG5w7z+wZPFlPtkgs95BsmrP/E7Q0xHXTcDcQ6Cu2FkFuRM+PKTb17cJDnLkawyS5vDLxMA== integrity sha512-/t3xdbS8KB0prj5KG5w7z+wZPFlPtkgs95BsmrP/E7Q0xHXTcDcQ6Cu2FkFuRM+PKTb17cJDnLkawyS5vDLxMA==
expo-structured-headers@~3.8.0:
version "3.8.0"
resolved "https://registry.yarnpkg.com/expo-structured-headers/-/expo-structured-headers-3.8.0.tgz#11797a4c3a7a6770b21126cecffcda148030e361"
integrity sha512-R+gFGn0x5CWl4OVlk2j1bJTJIz4KO8mPoCHpRHmfqMjmrMvrOM0qQSY3V5NHXwp1yT/L2v8aUmFQsBRIdvi1XA==
expo-system-ui@~3.0.7: expo-system-ui@~3.0.7:
version "3.0.7" version "3.0.7"
resolved "https://registry.npmjs.org/expo-system-ui/-/expo-system-ui-3.0.7.tgz" resolved "https://registry.npmjs.org/expo-system-ui/-/expo-system-ui-3.0.7.tgz"
@ -5074,6 +5140,27 @@ expo-updates-interface@~0.16.2:
resolved "https://registry.npmjs.org/expo-updates-interface/-/expo-updates-interface-0.16.2.tgz" resolved "https://registry.npmjs.org/expo-updates-interface/-/expo-updates-interface-0.16.2.tgz"
integrity sha512-929XBU70q5ELxkKADj1xL0UIm3HvhYhNAOZv5DSk7rrKvLo7QDdPyl+JVnwZm9LrkNbH4wuE2rLoKu1KMgZ+9A== integrity sha512-929XBU70q5ELxkKADj1xL0UIm3HvhYhNAOZv5DSk7rrKvLo7QDdPyl+JVnwZm9LrkNbH4wuE2rLoKu1KMgZ+9A==
expo-updates@~0.25.27:
version "0.25.27"
resolved "https://registry.yarnpkg.com/expo-updates/-/expo-updates-0.25.27.tgz#4aff889fea2aa221d8341a902646288f84c48b9e"
integrity sha512-1hyYZqBEXcAiEuSRPJ6dINTndGlWi6/bwlyYGjSnyoYfu/vzZQrJ+XA8JUP4EvJ3b0g8a0UOIjlDJ9ke9kMcfg==
dependencies:
"@expo/code-signing-certificates" "0.0.5"
"@expo/config" "~9.0.0-beta.0"
"@expo/config-plugins" "~8.0.8"
"@expo/fingerprint" "^0.10.2"
"@expo/spawn-async" "^1.7.2"
arg "4.1.0"
chalk "^4.1.2"
expo-eas-client "~0.12.0"
expo-manifests "~0.14.0"
expo-structured-headers "~3.8.0"
expo-updates-interface "~0.16.2"
fast-glob "^3.3.2"
fbemitter "^3.0.0"
ignore "^5.3.1"
resolve-from "^5.0.0"
expo-web-browser@~13.0.0, expo-web-browser@~13.0.3: expo-web-browser@~13.0.0, expo-web-browser@~13.0.3:
version "13.0.3" version "13.0.3"
resolved "https://registry.npmjs.org/expo-web-browser/-/expo-web-browser-13.0.3.tgz" resolved "https://registry.npmjs.org/expo-web-browser/-/expo-web-browser-13.0.3.tgz"
@ -5997,7 +6084,7 @@ ieee754@^1.1.13:
resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
ignore@^5.2.0: ignore@^5.2.0, ignore@^5.3.1:
version "5.3.2" version "5.3.2"
resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz" resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz"
integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
@ -6240,6 +6327,14 @@ is-invalid-path@^0.1.0:
dependencies: dependencies:
is-glob "^2.0.0" is-glob "^2.0.0"
is-nan@^1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d"
integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==
dependencies:
call-bind "^1.0.0"
define-properties "^1.1.3"
is-negative-zero@^2.0.3: is-negative-zero@^2.0.3:
version "2.0.3" version "2.0.3"
resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz" resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz"
@ -7959,12 +8054,20 @@ object-inspect@^1.13.1:
resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz" resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz"
integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==
object-is@^1.1.5:
version "1.1.6"
resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07"
integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==
dependencies:
call-bind "^1.0.7"
define-properties "^1.2.1"
object-keys@^1.1.1: object-keys@^1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz"
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
object.assign@^4.1.5: object.assign@^4.1.4, object.assign@^4.1.5:
version "4.1.5" version "4.1.5"
resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz" resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz"
integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==
@ -10149,6 +10252,11 @@ typical@^2.6.0:
resolved "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz" resolved "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz"
integrity sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg== integrity sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg==
ua-parser-js@^0.7.33:
version "0.7.39"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.39.tgz#c71efb46ebeabc461c4612d22d54f88880fabe7e"
integrity sha512-IZ6acm6RhQHNibSt7+c09hhvsKy9WUr4DVbeq9U8o71qxyYtJpQeDxQnMrVqnIFMLcQjHO0I9wgfO2vIahht4w==
ua-parser-js@^1.0.35: ua-parser-js@^1.0.35:
version "1.0.38" version "1.0.38"
resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.38.tgz" resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.38.tgz"
@ -10313,7 +10421,7 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1:
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
util@^0.12.3: util@^0.12.3, util@^0.12.5:
version "0.12.5" version "0.12.5"
resolved "https://registry.npmjs.org/util/-/util-0.12.5.tgz" resolved "https://registry.npmjs.org/util/-/util-0.12.5.tgz"
integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==