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>
</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">
<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_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">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>

View File

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

View File

@ -3,4 +3,5 @@
<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_system_ui_user_interface_style" translatable="false">light</string>
<string name="expo_runtime_version">1.0.0</string>
</resources>

View File

@ -25,6 +25,10 @@
},
"package": "com.cally.app",
"googleServicesFile": "./android/app/google-services.json",
"permissions": [
"android.permission.CAMERA",
"android.permission.RECORD_AUDIO"
]
},
"web": {
"bundler": "metro",
@ -51,6 +55,13 @@
"microphonePermission": "Allow $(PRODUCT_NAME) to access your microphone",
"recordAudioAndroid": true
}
],
[
"expo-notifications",
{
"color": "#ffffff",
"defaultChannel": "default"
}
]
],
"experiments": {
@ -63,6 +74,10 @@
"eas": {
"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";
import MenuIcon from "@/assets/svgs/MenuIcon";
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() {
const { mutateAsync: signOut } = useSignOut();
@ -38,7 +43,7 @@ export default function TabLayout() {
return (
<DrawerContentScrollView {...props} style={{ height: "100%" }}>
<View centerH centerV margin-30>
<Text text50>Welcome to Kali</Text>
<Text text50>Welcome to Cally</Text>
</View>
<View
style={{
@ -52,26 +57,14 @@ export default function TabLayout() {
color="rgb(7, 184, 199)"
bgColor={"rgb(231, 248, 250)"}
pressFunc={() => props.navigation.navigate("calendar")}
icon={
<Feather
name="calendar"
size={30}
color="rgb(7, 184, 199)"
/>
}
icon={<NavCalendarIcon />}
/>
<DrawerButton
color="#50be0c"
title={"Groceries"}
bgColor={"#eef9e7"}
pressFunc={() => props.navigation.navigate("grocery")}
icon={
<MaterialCommunityIcons
name="food-apple-outline"
size={30}
color="#50be0c"
/>
}
icon={<NavGroceryIcon />}
/>
</View>
<View style={{ flex: 1 }}>
@ -90,19 +83,17 @@ export default function TabLayout() {
/>*/}
<DrawerButton
color="#8005eb"
title={"To Dos"}
title={"To Do's"}
bgColor={"#f3e6fd"}
pressFunc={() => props.navigation.navigate("todos")}
icon={
<AntDesign name="checkcircleo" size={30} color="#8005eb" />
}
icon={<NavToDosIcon />}
/>
<DrawerButton
color="#e0ca03"
title={"Brain Dump"}
bgColor={"#fffacb"}
pressFunc={() => props.navigation.navigate("brain_dump")}
icon={<AntDesign name="book" size={30} color="#e0ca03" />}
icon={<NavBrainDumpIcon />}
/>
{/*<DrawerItem label="Logout" onPress={() => signOut()} />*/}
</View>
@ -120,7 +111,7 @@ export default function TabLayout() {
centerV
centerH
>
<Octicons name="gear" size={30} color="#6c645b" />
<NavSettingsIcon />
</View>
)}
backgroundColor="white"
@ -142,7 +133,7 @@ export default function TabLayout() {
borderWidth: 2,
borderColor: "#fd1775",
}}
label="Sign out of Kali"
label="Sign out of Cally"
color="#fd1775"
onPress={() => signOut()}
/>
@ -197,7 +188,7 @@ export default function TabLayout() {
name="todos"
options={{
drawerLabel: "To-Do",
title: "To-Do",
title: "To-Do's",
}}
/>
</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 {Stack} from 'expo-router';
import * as SplashScreen from 'expo-splash-screen';
import {useEffect} from 'react';
import 'react-native-reanimated';
import {useColorScheme} from '@/hooks/useColorScheme';
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 {Toast} from "react-native-ui-lib";
// Prevent the splash screen from auto-hiding before asset loading is complete.
SplashScreen.preventAutoHideAsync();
const queryClient = new QueryClient()
if (__DEV__) {
functions().useEmulator('localhost', 5001);
firestore().useEmulator("localhost", 5471);
auth().useEmulator("http://localhost:9099");
}
const queryClient = new QueryClient();
export default function RootLayout() {
const [loaded] = useFonts({
@ -50,7 +36,7 @@ export default function RootLayout() {
<Stack.Screen name="(unauth)" options={{headerShown: false}}/>
<Stack.Screen name="+not-found"/>
</Stack>
<Toast />
<Toast/>
</ThemeProvider>
</AuthContextProvider>
</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) {
console.log(token);
const response = await fetch(
`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();
console.log(data);
const googleEvents = [];
data.items?.forEach((item) => {
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 [mode, setMode] = useState<"week" | "month" | "day">("week");
const [selectedDate, setSelectedDate] = useState<Date>(new Date());

View File

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

View File

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

View File

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

View File

@ -1,53 +1,58 @@
import {
View,
Text,
Button,
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 {Checkbox, Text, TouchableOpacity, View,} from "react-native-ui-lib";
import React, {useEffect, useState} from "react";
import {AntDesign} from "@expo/vector-icons";
import {GroceryCategory, useGroceryContext,} from "@/contexts/GroceryContext";
import EditGroceryFrequency from "./EditGroceryFrequency";
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 = ({
item,
handleItemApproved,
}: {
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 [isEditingTitle, setIsEditingTitle] = useState<boolean>(false);
const [newTitle, setNewTitle] = useState<string>("");
const [category, setCategory] = useState<GroceryCategory>(
GroceryCategory.None
);
const [itemCreator, setItemCreator] = useState<UserProfile>(null);
const handleTitleChange = (newTitle: string) => {
updateGroceryItem(item.id, { title: newTitle });
updateGroceryItem({id: item?.id, title: newTitle});
};
const handleCategoryChange = (newCategory: GroceryCategory) => {
updateGroceryItem(item.id, { category: newCategory });
updateGroceryItem({id: item?.id, category: newCategory});
};
useEffect(() => {
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 (
<View
key={item.id}
@ -114,7 +119,7 @@ const GroceryItem = ({
containerStyle={styles.checkbox}
hitSlop={20}
onValueChange={() =>
updateGroceryItem(item.id, { bought: !item.bought })
updateGroceryItem({ id: item.id, bought: !item.bought })
}
/>
)}
@ -135,7 +140,7 @@ const GroceryItem = ({
}}
></View>
<Text color="#858585" text70>
Requested by Austin
Requested by {itemCreator?.firstName}
</Text>
</View>
</View>

View File

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

View File

@ -28,21 +28,28 @@ const OnboardingFlow = () => {
width={10}
/>
),
title: <Text text30>Welcome to Kali</Text>,
title: <Text text30>Welcome to Cally</Text>,
subtitle: (
<View paddingB-250 marginH-20 spread>
<Text text50R>Lightening Mental Loads, One Family at a Time</Text>
<Button
label="Continue"
style={{ backgroundColor: "#fd1775" }}
onPress={() => onboardingRef.current.goToPage(1, true)}
onPress={() => onboardingRef?.current?.goToPage(1, true)}
/>
</View>
),
},
{
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: (
<View
style={{

View File

@ -1,5 +1,5 @@
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 { ScrollView, StyleSheet } from "react-native";
import { colorMap } from "@/contexts/SettingsContext";
@ -9,21 +9,19 @@ import { fetchMicrosoftCalendarEvents } from "@/calendar-integration/microsoft-c
import { useCreateEventFromProvider } from "@/hooks/firebase/useCreateEvent";
import { useAuthContext } from "@/contexts/AuthContext";
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 AppleIcon from "@/assets/svgs/AppleIcon";
import GoogleIcon from "@/assets/svgs/GoogleIcon";
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({
webClientId:
"406146460310-hjadmfa1gg4ptaouira5rkhu0djlo5ut.apps.googleusercontent.com",
scopes: ["profile", "email"], // Note: add calendar scope
});
const GoogleLogin = async () => {
return await GoogleSignin.signIn();
const googleConfig = {
androidClientId: "406146460310-2u67ab2nbhu23trp8auho1fq4om29fc0.apps.googleusercontent.com",
iosClientId: "406146460310-2u67ab2nbhu23trp8auho1fq4om29fc0.apps.googleusercontent.com",
webClientId: "406146460310-2u67ab2nbhu23trp8auho1fq4om29fc0.apps.googleusercontent.com",
scopes: ["email", "profile", "https://www.googleapis.com/auth/calendar.events.owned"]
};
const microsoftConfig = {
@ -57,7 +55,15 @@ const CalendarSettingsPage = (props: {
const { mutateAsync: createEventFromProvider } = useCreateEventFromProvider();
const { mutateAsync: updateUserData } = useUpdateUserData();
WebBrowser.maybeCompleteAuthSession()
const [request, response, promptAsync] = Google.useAuthRequest(googleConfig);
useEffect(() => {
signInWithGoogle();
}, [response]);
const fetchAndSaveGoogleEvents = () => {
console.log("fetch");
const timeMin = new Date(new Date().setHours(0, 0, 0, 0));
const timeMax = new Date(
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 {
const response = await GoogleLogin();
if (response) {
const googleUserData = response.data;
let idToken = googleUserData?.idToken;
if (idToken) {
await updateUserData({ newUserData: { googleToken: idToken } });
// Attempt to retrieve user information from AsyncStorage
if (response?.type === 'success') {
console.log(response.authentication)
await updateUserData({newUserData: {googleToken: response.authentication?.accessToken}})
}
}
} catch (apiError) {
console.log(apiError || "Something went wrong");
} catch (error) {
// 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>
<Button
onPress={handleGoogleLogin}
onPress={() => promptAsync()}
label="Connect Google"
iconSource={() => (
<View marginR-15>
@ -329,50 +332,31 @@ const CalendarSettingsPage = (props: {
Connected Calendars
</Text>
<View style={styles.card}>
<Text text70>Calendars</Text>
<View style={{ marginTop: 20 }}>
<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}
/>
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 File

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

View File

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

View File

@ -1,5 +1,5 @@
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 { repeatOptions, useToDosContext } from "@/contexts/ToDosContext";
import { Feather, AntDesign, Ionicons } from "@expo/vector-icons";
@ -27,6 +27,15 @@ const AddChoreDialog = (addChoreDialogProps: IAddChoreDialog) => {
const [rotate, setRotate] = useState<boolean>(false);
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 numericValue = parseInt(text, 10);
@ -41,7 +50,7 @@ const AddChoreDialog = (addChoreDialogProps: IAddChoreDialog) => {
bottom={true}
height={"90%"}
panDirection={PanningDirectionsEnum.DOWN}
onDismiss={() => addChoreDialogProps.setIsVisible(false)}
onDismiss={() => handleCLose}
containerStyle={{
borderRadius: 10,
backgroundColor: "white",
@ -59,13 +68,13 @@ const AddChoreDialog = (addChoreDialogProps: IAddChoreDialog) => {
style={styles.topBtn}
label="Cancel"
onPress={() => {
addChoreDialogProps.setIsVisible(false);
handleCLose();
}}
/>
<View marginT-12>
<DropModalIcon
onPress={() => {
addChoreDialogProps.setIsVisible(false);
handleCLose()
}}
/>
</View>
@ -84,7 +93,7 @@ const AddChoreDialog = (addChoreDialogProps: IAddChoreDialog) => {
rotate: rotate,
repeatType: repeatType,
});
addChoreDialogProps.setIsVisible(false);
handleCLose();
console.log(toDos);
} catch (error) {
console.error(error);

View File

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

@ -5,6 +5,11 @@ import {useRouter} from "expo-router";
import firestore from "@react-native-firebase/firestore";
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 {
"PARENT" = "parent",
"CHILD" = "child",
@ -19,48 +24,118 @@ interface IAuthContext {
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!)
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 [profileType, setProfileType] = useState<ProfileType | undefined>(undefined);
const [profileData, setProfileData] = useState<UserProfile | undefined>(undefined);
const {replace} = useRouter();
const ready = !initializing;
const {replace} = useRouter()
const ready = !initializing
const onAuthStateChangedHandler = async (authUser: FirebaseAuthTypes.User | null) => {
setUser(authUser);
const onAuthStateChanged = async (user: FirebaseAuthTypes.User | null) => {
setUser(user);
if (user) {
await refreshProfileData();
if (authUser) {
await refreshProfileData(authUser);
const pushToken = await registerForPushNotificationsAsync();
if (pushToken) {
await savePushTokenToFirestore(authUser.uid, pushToken);
}
}
if (initializing) setInitializing(false);
}
};
const refreshProfileData = async () => {
if (user) {
const refreshProfileData = async (user?: FirebaseAuthTypes.User) => {
const authUser = user ?? auth().currentUser
if (authUser) {
try {
const documentSnapshot = await firestore()
.collection("Profiles")
.doc(user.uid)
.doc(authUser.uid)
.get();
if (documentSnapshot.exists) {
setProfileType(documentSnapshot.data()?.userType);
setProfileData(documentSnapshot.data() as UserProfile);
}
} catch (error) {
console.error("Error fetching user profile data:", error);
setProfileType(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(() => {
const subscriber = auth().onAuthStateChanged(onAuthStateChanged);
const subscriber = auth().onAuthStateChanged(onAuthStateChangedHandler);
return subscriber;
}, []);
@ -72,9 +147,9 @@ export const AuthContextProvider: FC<{ children: ReactNode }> = ({children}) =>
useEffect(() => {
if (ready && user) {
replace({pathname: "/(auth)/calendar"})
replace({pathname: "/(auth)/calendar"});
} else if (ready && !user) {
replace({pathname: "/(unauth)"})
replace({pathname: "/(unauth)"});
}
}, [user, ready]);
@ -86,7 +161,8 @@ export const AuthContextProvider: FC<{ children: ReactNode }> = ({children}) =>
<AuthContext.Provider value={{user, profileType, profileData, setProfileData, refreshProfileData}}>
{children}
</AuthContext.Provider>
)
}
);
};
export const useAuthContext = () => useContext(AuthContext)!;

View File

@ -3,7 +3,7 @@ import React, { createContext, useContext, useState, ReactNode } from "react";
// Define the CalendarEvent interface
export interface CalendarEvent {
id?: number; // Unique identifier for the event
id?: number | string; // Unique identifier for the event
user?: string;
title: string; // Event title or name
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 {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 {
Never = "Never",
@ -11,16 +14,6 @@ export enum GroceryFrequency {
Quarterly = "Quarterly",
}
export interface IGrocery {
id: number;
title: string;
category: GroceryCategory;
approved: boolean;
recurring: boolean;
frequency: GroceryFrequency;
bought: boolean;
}
export enum GroceryCategory {
Fruit = "Fruit",
Dairy = "Dairy",
@ -52,23 +45,7 @@ const iconMapping: { [key in GroceryCategory]: MaterialIconNames } = {
[GroceryCategory.Frozen]: "snowflake",
};*/
interface IGroceryContext {
groceries: IGrocery[];
//iconMapping: { [key in GroceryCategory]: MaterialIconNames };
updateGroceryItem: (id: number, changes: Partial<IGrocery>) => void;
isAddingGrocery: boolean;
setIsAddingGrocery: (value: boolean) => void;
addGrocery: (grocery: IGrocery) => void;
fuzzyMatchGroceryCategory: (groceryTitle: string) => GroceryCategory;
}
const GroceryContext = createContext<IGroceryContext | undefined>(undefined);
export const GroceryProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const [isAddingGrocery, setIsAddingGrocery] = useState<boolean>(false);
const [groceries, setGroceries] = useState<IGrocery[]>([
const initialGroceryList = [
{
id: 0,
title: "Carrots",
@ -105,21 +82,9 @@ export const GroceryProvider: React.FC<{ children: React.ReactNode }> = ({
recurring: false,
frequency: GroceryFrequency.Never,
},
]);
];
const addGrocery = (grocery: IGrocery) => {
setGroceries((prevGroceries) => [
...prevGroceries,
{
...grocery,
id: prevGroceries.length
? prevGroceries[prevGroceries.length - 1].id + 1
: 0,
},
]);
};
const groceryExamples = {
const groceryExamples = {
[GroceryCategory.Fruit]: [
'apple', 'apples', 'banana', 'bananas', 'orange', 'oranges', 'grape', 'grapes',
'pear', 'pears', 'pineapple', 'pineapples', 'kiwi', 'kiwis', 'strawberry',
@ -204,6 +169,32 @@ export const GroceryProvider: React.FC<{ children: React.ReactNode }> = ({
'frozen pie', 'frozen pies', 'frozen lasagna', 'frozen burrito', 'frozen burritos',
'frozen nuggets', 'frozen pastry', 'frozen pastries', 'frozen meals'
],
};
interface IGroceryContext {
groceries: IGrocery[];
//iconMapping: { [key in GroceryCategory]: MaterialIconNames };
updateGroceryItem: (changes: Partial<IGrocery>) => void;
isAddingGrocery: boolean;
setIsAddingGrocery: (value: boolean) => void;
addGrocery: (grocery: IGrocery) => void;
fuzzyMatchGroceryCategory: (groceryTitle: string) => GroceryCategory;
}
const GroceryContext = createContext<IGroceryContext | undefined>(undefined);
export const GroceryProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const [isAddingGrocery, setIsAddingGrocery] = useState<boolean>(false);
const { mutateAsync: createGrocery } = useCreateGrocery();
const { mutateAsync: updateGrocery } = useUpdateGrocery();
const { data: groceries } = useGetGroceries();
const addGrocery = (grocery: IGrocery) => {
createGrocery(grocery);
};
const fuzzyMatchGroceryCategory = (groceryTitle: string): GroceryCategory => {
@ -221,12 +212,8 @@ export const GroceryProvider: React.FC<{ children: React.ReactNode }> = ({
return bestMatchCategory;
};
const updateGroceryItem = (id: number, changes: Partial<IGrocery>) => {
setGroceries((prevGroceries) =>
prevGroceries.map((grocery) =>
grocery.id === id ? { ...grocery, ...changes } : grocery
)
);
const updateGroceryItem = (changes: Partial<IGrocery>) => {
updateGrocery(changes);
};
return (

View File

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

View File

@ -1,14 +1,109 @@
const {onRequest} = require("firebase-functions/v2/https");
const {getAuth} = require("firebase-admin/auth");
const {getFirestore} = require("firebase-admin/firestore");
const admin = require("firebase-admin");
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();
} catch (error) {
console.error(error)
}
admin.initializeApp();
const db = admin.firestore();
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) => {
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"});
}
});
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",
"dependencies": {
"expo-server-sdk": "^3.11.0",
"firebase-admin": "^12.1.0",
"firebase-functions": "^5.0.0"
},
@ -3115,6 +3116,12 @@
"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": {
"version": "1.3.2",
"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_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": {
"version": "4.19.2",
"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",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"license": "MIT",
"optional": true,
"dependencies": {
"whatwg-url": "^5.0.0"
},
@ -6183,6 +6199,34 @@
"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": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
@ -7048,8 +7092,7 @@
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"license": "MIT",
"optional": true
"license": "MIT"
},
"node_modules/ts-api-utils": {
"version": "1.3.0",
@ -7268,8 +7311,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
"license": "BSD-2-Clause",
"optional": true
"license": "BSD-2-Clause"
},
"node_modules/websocket-driver": {
"version": "0.7.4",
@ -7299,7 +7341,6 @@
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"license": "MIT",
"optional": true,
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"

View File

@ -14,6 +14,7 @@
},
"main": "index.js",
"dependencies": {
"expo-server-sdk": "^3.11.0",
"firebase-admin": "^12.1.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";
export const useCreateEvent = () => {
const {user: currentUser} = useAuthContext()
const {user: currentUser, profileData} = useAuthContext()
const queryClients = useQueryClient()
return useMutation({
mutationKey: ["createEvent"],
mutationFn: async (eventData: Partial<EventData>) => {
try {
console.log("CALLLLL")
await firestore()
.collection("Events")
.add({...eventData, creatorId: currentUser?.uid})
.add({...eventData, creatorId: currentUser?.uid, familyId: profileData?.familyId})
} catch (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
return {
id: doc.id,
title: data.title,
start: new Date(data.startDate.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):
- React-Core
- DoubleConversion (1.1.6)
- EASClient (0.12.0):
- ExpoModulesCore
- EXApplication (5.9.1):
- ExpoModulesCore
- EXBarCodeScanner (13.0.1):
@ -977,6 +979,8 @@ PODS:
- EXJSONUtils (0.13.1)
- EXManifests (0.14.3):
- ExpoModulesCore
- EXNotifications (0.28.18):
- ExpoModulesCore
- Expo (51.0.34):
- ExpoModulesCore
- expo-dev-client (4.0.27):
@ -1186,10 +1190,6 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- ExpoAdapterGoogleSignIn (13.1.0):
- ExpoModulesCore
- GoogleSignIn (~> 7.1)
- React-Core
- ExpoAsset (10.0.10):
- ExpoModulesCore
- ExpoCamera (15.0.16):
@ -1198,6 +1198,8 @@ PODS:
- ZXingObjC/PDF417
- ExpoCrypto (13.0.2):
- ExpoModulesCore
- ExpoDevice (6.0.2):
- ExpoModulesCore
- ExpoFileSystem (17.0.1):
- ExpoModulesCore
- ExpoFont (12.0.10):
@ -1257,6 +1259,35 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- 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):
- ExpoModulesCore
- FBLazyVector (0.74.3)
@ -1351,10 +1382,6 @@ PODS:
- GoogleUtilities/Environment (~> 7.7)
- nanopb (< 2.30911.0, >= 2.30908.0)
- 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/Environment
- GoogleUtilities/Logger
@ -1458,9 +1485,6 @@ PODS:
- gRPC-Core/Privacy (= 1.62.5)
- gRPC-Core/Interface (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)
- hermes-engine (0.74.3):
- hermes-engine/Pre-built (= 0.74.3)
@ -1496,6 +1520,7 @@ PODS:
- FBLazyVector (= 0.74.3)
- RCTRequired (= 0.74.3)
- React-Core (= 0.74.3)
- ReachabilitySwift (5.2.3)
- React (0.74.3):
- React-Core (= 0.74.3)
- React-Core/DevSupport (= 0.74.3)
@ -2704,9 +2729,6 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- RNGoogleSignin (13.1.0):
- GoogleSignIn (~> 7.1)
- React-Core
- RNReanimated (3.10.1):
- DoubleConversion
- glog
@ -2753,6 +2775,9 @@ PODS:
- RNSVG (15.7.1):
- React-Core
- 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)
- ZXingObjC/Core (3.6.9)
- ZXingObjC/OneD (3.6.9):
@ -2764,21 +2789,23 @@ DEPENDENCIES:
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
- BVLinearGradient (from `../node_modules/react-native-linear-gradient`)
- 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`)
- EXBarCodeScanner (from `../node_modules/expo-barcode-scanner/ios`)
- EXConstants (from `../node_modules/expo-constants/ios`)
- EXImageLoader (from `../node_modules/expo-image-loader/ios`)
- EXJSONUtils (from `../node_modules/expo-json-utils/ios`)
- EXManifests (from `../node_modules/expo-manifests/ios`)
- EXNotifications (from `../node_modules/expo-notifications/ios`)
- Expo (from `../node_modules/expo`)
- expo-dev-client (from `../node_modules/expo-dev-client/ios`)
- expo-dev-launcher (from `../node_modules/expo-dev-launcher`)
- expo-dev-menu (from `../node_modules/expo-dev-menu`)
- 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`)
- ExpoCamera (from `../node_modules/expo-camera/ios`)
- ExpoCrypto (from `../node_modules/expo-crypto/ios`)
- ExpoDevice (from `../node_modules/expo-device/ios`)
- ExpoFileSystem (from `../node_modules/expo-file-system/ios`)
- ExpoFont (from `../node_modules/expo-font/ios`)
- ExpoHead (from `../node_modules/expo-router/ios`)
@ -2788,6 +2815,8 @@ DEPENDENCIES:
- ExpoSystemUI (from `../node_modules/expo-system-ui/ios`)
- ExpoWebBrowser (from `../node_modules/expo-web-browser/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`)
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
- fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`)
@ -2853,7 +2882,6 @@ DEPENDENCIES:
- "RNFBFirestore (from `../node_modules/@react-native-firebase/firestore`)"
- "RNFBFunctions (from `../node_modules/@react-native-firebase/functions`)"
- 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`)
- RNScreens (from `../node_modules/react-native-screens`)
- RNSVG (from `../node_modules/react-native-svg`)
@ -2881,18 +2909,18 @@ SPEC REPOS:
- FirebaseSessions
- FirebaseSharedSwift
- GoogleDataTransport
- GoogleSignIn
- GoogleUtilities
- "gRPC-C++"
- gRPC-Core
- GTMAppAuth
- GTMSessionFetcher
- leveldb-library
- nanopb
- PromisesObjC
- PromisesSwift
- ReachabilitySwift
- RecaptchaInterop
- SocketRocket
- sqlite3
- ZXingObjC
EXTERNAL SOURCES:
@ -2902,6 +2930,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-linear-gradient"
DoubleConversion:
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
EASClient:
:path: "../node_modules/expo-eas-client/ios"
EXApplication:
:path: "../node_modules/expo-application/ios"
EXBarCodeScanner:
@ -2914,6 +2944,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/expo-json-utils/ios"
EXManifests:
:path: "../node_modules/expo-manifests/ios"
EXNotifications:
:path: "../node_modules/expo-notifications/ios"
Expo:
:path: "../node_modules/expo"
expo-dev-client:
@ -2924,14 +2956,14 @@ EXTERNAL SOURCES:
:path: "../node_modules/expo-dev-menu"
expo-dev-menu-interface:
:path: "../node_modules/expo-dev-menu-interface/ios"
ExpoAdapterGoogleSignIn:
:path: "../node_modules/@react-native-google-signin/google-signin/expo/ios"
ExpoAsset:
:path: "../node_modules/expo-asset/ios"
ExpoCamera:
:path: "../node_modules/expo-camera/ios"
ExpoCrypto:
:path: "../node_modules/expo-crypto/ios"
ExpoDevice:
:path: "../node_modules/expo-device/ios"
ExpoFileSystem:
:path: "../node_modules/expo-file-system/ios"
ExpoFont:
@ -2950,6 +2982,10 @@ EXTERNAL SOURCES:
:path: "../node_modules/expo-web-browser/ios"
EXSplashScreen:
:path: "../node_modules/expo-splash-screen/ios"
EXStructuredHeaders:
:path: "../node_modules/expo-structured-headers/ios"
EXUpdates:
:path: "../node_modules/expo-updates/ios"
EXUpdatesInterface:
:path: "../node_modules/expo-updates-interface/ios"
FBLazyVector:
@ -3077,8 +3113,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/@react-native-firebase/functions"
RNGestureHandler:
:path: "../node_modules/react-native-gesture-handler"
RNGoogleSignin:
:path: "../node_modules/@react-native-google-signin/google-signin"
RNReanimated:
:path: "../node_modules/react-native-reanimated"
RNScreens:
@ -3095,21 +3129,23 @@ SPEC CHECKSUMS:
BoringSSL-GRPC: 1e2348957acdbcad360b80a264a90799984b2ba6
BVLinearGradient: 880f91a7854faff2df62518f0281afb1c60d49a3
DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5
EASClient: 1509a9a6b48b932ec61667644634daf2562983b8
EXApplication: c08200c34daca7af7fd76ac4b9d606077410e8ad
EXBarCodeScanner: e2dd9b42c1b522a2adc9202b1dfbc64cb34456d1
EXConstants: 409690fbfd5afea964e5e9d6c4eb2c2b59222c59
EXImageLoader: ab589d67d6c5f2c33572afea9917304418566334
EXJSONUtils: 30c17fd9cc364d722c0946a550dfbf1be92ef6a4
EXManifests: c1fab4c3237675e7b0299ea8df0bcb14baca4f42
EXNotifications: dd289340c26bc5388e440fc90d0b2c661cbd0285
Expo: 963ef4ae102e4f4ac114a8510d70127c44adffbf
expo-dev-client: 85deba11af998ea86e62093b17fce56aa2c6f26b
expo-dev-launcher: fe4f2c0a0aa627449eeaec5f9f11e04090f97c40
expo-dev-menu: 5b14897ecce3a8cf9e9cf9109344c2c192a3766a
expo-dev-menu-interface: be32c09f1e03833050f0ee290dcc86b3ad0e73e4
ExpoAdapterGoogleSignIn: da10ae7e7c1d73a10c2facebcdfe5ebea8e073ce
ExpoAsset: 323700f291684f110fb55f0d4022a3362ea9f875
ExpoCamera: 929be541d1c1319fcf32f9f5d9df8b97804346b5
ExpoCrypto: 156078f266bf28f80ecf5e2a9c3a0d6ffce07a1c
ExpoDevice: fc94f0e42ecdfd897e7590f2874fc64dfa7e9b1c
ExpoFileSystem: 80bfe850b1f9922c16905822ecbf97acd711dc51
ExpoFont: 00756e6c796d8f7ee8d211e29c8b619e75cbf238
ExpoHead: fcb28a68ed4ba28f177394d2dfb8a0a8824cd103
@ -3119,6 +3155,8 @@ SPEC CHECKSUMS:
ExpoSystemUI: d4f065a016cae6721b324eb659cdee4d4cf0cb26
ExpoWebBrowser: 7595ccac6938eb65b076385fd23d035db9ecdc8e
EXSplashScreen: d8b3c547b9b18a41d80c6f6b274c4c26664febd4
EXStructuredHeaders: cb8d1f698e144f4c5547b4c4963e1552f5d2b457
EXUpdates: fe42e1cdace89a8db450351fbc38577b237af76b
EXUpdatesInterface: 996527fd7d1a5d271eb523258d603f8f92038f24
FBLazyVector: 7e977dd099937dc5458851233141583abba49ff2
Firebase: cec914dab6fd7b1bd8ab56ea07ce4e03dd251c2d
@ -3140,11 +3178,9 @@ SPEC CHECKSUMS:
fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120
glog: fdfdfe5479092de0c4bdbebedd9056951f092c4f
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
GoogleSignIn: d4281ab6cf21542b1cfaff85c191f230b399d2db
GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15
"gRPC-C++": e725ef63c4475d7cdb7e2cf16eb0fde84bd9ee51
gRPC-Core: eee4be35df218649fe66d721a05a7f27a28f069b
GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
hermes-engine: 1f547997900dd0752dc0cc0ae6dd16173c49e09b
leveldb-library: e8eadf9008a61f9e1dde3978c086d2b6d9b9dc28
@ -3155,6 +3191,7 @@ SPEC CHECKSUMS:
RCTDeprecation: 4c7eeb42be0b2e95195563c49be08d0b839d22b4
RCTRequired: d530a0f489699c8500e944fde963102c42dcd0c2
RCTTypeSafety: b20878506b094fa3d9007d7b9e4be0faa3562499
ReachabilitySwift: 7f151ff156cea1481a8411701195ac6a984f4979
React: 2f9da0177233f60fa3462d83fcccde245759f81a
React-callinvoker: d0205f0dcebf72ec27263ab41e3a5ad827ed503f
React-Codegen: 27212e14727ad7d6d9fd1b1967fbf21929e4ce29
@ -3210,11 +3247,11 @@ SPEC CHECKSUMS:
RNFBFirestore: e47cdde04ea3d9e73e58e037e1aa1d0b1141c316
RNFBFunctions: 738cc9e2177d060d29b5d143ef2f9ed0eda4bb1f
RNGestureHandler: 20a4307fd21cbff339abfcfa68192f3f0a6a518b
RNGoogleSignin: 9e68b9bcc3888219357924e32ee563624745647d
RNReanimated: d51431fd3597a8f8320319dce8e42cee82a5445f
RNScreens: 30249f9331c3b00ae7cb7922e11f58b3ed369c07
RNSVG: 4590aa95758149fa27c5c83e54a6a466349a1688
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
sqlite3: 02d1f07eaaa01f80a1c16b4b31dfcbb3345ee01a
Yoga: bd92064a0d558be92786820514d74fc4dddd1233
ZXingObjC: 8898711ab495761b2dbbdec76d90164a6d7e14c5

View File

@ -298,6 +298,9 @@
"${PODS_CONFIGURATION_BUILD_DIR}/EXApplication/ExpoApplication_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.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}/ExpoSystemUI/ExpoSystemUI_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}/FirebaseFirestoreInternal/FirebaseFirestoreInternal_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}/GoogleDataTransport/GoogleDataTransport_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/GoogleSignIn/GoogleSignIn.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises_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}/abseil/xcprivacy.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}/EXConstants.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}/ExpoSystemUI_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}/FirebaseFirestoreInternal_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}/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}/FBLPromises_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}/xcprivacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevLauncher.bundle",
@ -437,7 +441,7 @@
);
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = com.cally.app;
PRODUCT_NAME = "CallyFamilyPlanner";
PRODUCT_NAME = CallyFamilyPlanner;
SWIFT_OBJC_BRIDGING_HEADER = "cally/cally-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@ -468,7 +472,7 @@
);
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.cally.app;
PRODUCT_NAME = "CallyFamilyPlanner";
PRODUCT_NAME = CallyFamilyPlanner;
SWIFT_OBJC_BRIDGING_HEADER = "cally/cally-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";

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>
</array>
<key>UILaunchStoryboardName</key>
<string>SplashScreen</string>

View File

@ -5,8 +5,12 @@
<key>EXUpdatesCheckOnLaunch</key>
<string>ALWAYS</string>
<key>EXUpdatesEnabled</key>
<false/>
<true/>
<key>EXUpdatesLaunchWaitMs</key>
<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>
</plist>

View File

@ -1,5 +1,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">
<plist version="1.0">
<dict/>
<dict>
<key>aps-environment</key>
<string>development</string>
</dict>
</plist>

View File

@ -19,7 +19,7 @@
"lint": "expo lint",
"submit": "npx eas-cli submit -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-cicd": "yarn build-cicd"
},
@ -35,7 +35,6 @@
"@react-native-firebase/crashlytics": "^20.3.0",
"@react-native-firebase/firestore": "^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/native": "^6.0.2",
"date-fns": "^3.6.0",
@ -47,13 +46,16 @@
"expo-camera": "~15.0.16",
"expo-constants": "~16.0.2",
"expo-dev-client": "~4.0.27",
"expo-device": "~6.0.2",
"expo-font": "~12.0.9",
"expo-image-picker": "~15.0.7",
"expo-linking": "~6.3.1",
"expo-notifications": "~0.28.18",
"expo-router": "~3.5.20",
"expo-splash-screen": "~0.27.5",
"expo-status-bar": "~1.12.1",
"expo-system-ui": "~3.0.7",
"expo-updates": "~0.25.27",
"expo-web-browser": "~13.0.3",
"firebase-admin": "^12.3.1",
"firebase-functions": "^5.1.0",

128
yarn.lock
View File

@ -1025,6 +1025,20 @@
dotenv-expand "~11.0.6"
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":
version "0.5.1"
resolved "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.5.1.tgz"
@ -1686,6 +1700,11 @@
dependencies:
"@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":
version "8.0.2"
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"
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":
version "0.74.85"
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"
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:
version "5.0.2"
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"
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:
version "0.15.2"
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-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:
version "1.0.2"
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"
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"
resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz"
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"
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"
resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz"
integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==
@ -4964,6 +4999,18 @@ expo-dev-menu@5.0.21:
expo-dev-menu-interface "1.8.3"
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:
version "17.0.1"
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:
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:
version "3.5.23"
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"
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:
version "3.0.7"
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"
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:
version "13.0.3"
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"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
ignore@^5.2.0:
ignore@^5.2.0, ignore@^5.3.1:
version "5.3.2"
resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz"
integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
@ -6240,6 +6327,14 @@ is-invalid-path@^0.1.0:
dependencies:
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:
version "2.0.3"
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"
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:
version "1.1.1"
resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz"
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
object.assign@^4.1.5:
object.assign@^4.1.4, object.assign@^4.1.5:
version "4.1.5"
resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz"
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"
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:
version "1.0.38"
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"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
util@^0.12.3:
util@^0.12.3, util@^0.12.5:
version "0.12.5"
resolved "https://registry.npmjs.org/util/-/util-0.12.5.tgz"
integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==