From 7d9d41acfcf098bdc036a14620aef63698506da5 Mon Sep 17 00:00:00 2001 From: Dejan Date: Fri, 11 Oct 2024 10:28:49 +0200 Subject: [PATCH 01/14] - Added proper oauth2 authentication with google and saving of the access token to the profile --- calendar-integration/google-calendar-utils.js | 2 + .../pages/settings/CalendarSettingsPage.tsx | 112 ++++++++---------- 2 files changed, 50 insertions(+), 64 deletions(-) diff --git a/calendar-integration/google-calendar-utils.js b/calendar-integration/google-calendar-utils.js index 428cb42..c7d332c 100644 --- a/calendar-integration/google-calendar-utils.js +++ b/calendar-integration/google-calendar-utils.js @@ -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; diff --git a/components/pages/settings/CalendarSettingsPage.tsx b/components/pages/settings/CalendarSettingsPage.tsx index 5ea3b52..c75128e 100644 --- a/components/pages/settings/CalendarSettingsPage.tsx +++ b/components/pages/settings/CalendarSettingsPage.tsx @@ -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: { diff --git a/components/pages/todos/ToDosList.tsx b/components/pages/todos/ToDosList.tsx index 1571c3f..5cb8d30 100644 --- a/components/pages/todos/ToDosList.tsx +++ b/components/pages/todos/ToDosList.tsx @@ -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 { 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(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 ( - - + {noDateToDos.length > 0 && ( - {noDateToDos - .sort((a, b) => Number(a.done) - Number(b.done)) - .map((item) => ( - - ))} + + + Unscheduled + + {!expandNoDate && ( + {setExpandNoDate(!expandNoDate)}}/> + )} + {expandNoDate && ( + {setExpandNoDate(!expandNoDate)}}/> + )} + + {expandNoDate && + noDateToDos + .sort((a, b) => Number(a.done) - Number(b.done)) + .map((item) => )} )} {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 ( - toggleExpand(dateKey)} style={{ - fontWeight: "bold", - marginVertical: 8, + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", paddingHorizontal: 20, + marginVertical: 8, }} > - {dateKey} - - {sortedToDos.map((item) => ( - - ))} + + {dateKey} + + {!isExpanded && ( + + )} + {isExpanded && ( + + )} + + + {isExpanded && + sortedToDos.map((item) => )} ); })} From 04a6103470da8d9658b353aea02a50dc235a8993 Mon Sep 17 00:00:00 2001 From: Milan Paunovic Date: Fri, 11 Oct 2024 23:53:08 +0200 Subject: [PATCH 10/14] Notifications --- android/app/src/main/AndroidManifest.xml | 3 + android/app/src/main/res/values/colors.xml | 1 + app.json | 7 + app/_layout.tsx | 20 +-- .../pages/calendar/ManuallyAddEventModal.tsx | 11 +- contexts/AuthContext.tsx | 97 ++++++++++++-- contexts/CalendarContext.tsx | 2 +- firebase/functions/index.js | 122 +++++++++++++++++- firebase/functions/package-lock.json | 53 +++++++- firebase/functions/package.json | 1 + hooks/firebase/useCreateEvent.ts | 1 + ios/Podfile.lock | 38 ++---- ios/cally.xcodeproj/project.pbxproj | 8 +- ios/cally/Info.plist | 3 + ios/cally/cally.entitlements | 5 +- package.json | 2 + yarn.lock | 102 ++++++++++++++- 17 files changed, 392 insertions(+), 84 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 6c2d8b6..1319cbe 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -14,6 +14,9 @@ + + + diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml index f387b90..ebe53de 100644 --- a/android/app/src/main/res/values/colors.xml +++ b/android/app/src/main/res/values/colors.xml @@ -3,4 +3,5 @@ #ffffff #023c69 #ffffff + #ffffff \ No newline at end of file diff --git a/app.json b/app.json index 99588e2..84c0c79 100644 --- a/app.json +++ b/app.json @@ -51,6 +51,13 @@ "microphonePermission": "Allow $(PRODUCT_NAME) to access your microphone", "recordAudioAndroid": true } + ], + [ + "expo-notifications", + { + "color": "#ffffff", + "defaultChannel": "default" + } ] ], "experiments": { diff --git a/app/_layout.tsx b/app/_layout.tsx index 47d1e50..1b0128e 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -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() { - + diff --git a/components/pages/calendar/ManuallyAddEventModal.tsx b/components/pages/calendar/ManuallyAddEventModal.tsx index d1cb54c..5c365f2 100644 --- a/components/pages/calendar/ManuallyAddEventModal.tsx +++ b/components/pages/calendar/ManuallyAddEventModal.tsx @@ -98,7 +98,7 @@ export const ManuallyAddEventModal = ({ return combined; }; - const handleSave = () => { + const handleSave = async () => { let finalStartDate: Date; let finalEndDate: Date; @@ -113,15 +113,14 @@ export const ManuallyAddEventModal = ({ finalEndDate = combineDateAndTime(endDate, endTime); } - const eventData: CalendarEvent = { + const eventData: Partial = { title: title, - start: finalStartDate, - end: finalEndDate, + startDate: finalStartDate, + endDate: finalEndDate, allDay: isAllDay, - private: isPrivate, }; - addEvent(eventData); + await createEvent(eventData); close(); }; diff --git a/contexts/AuthContext.tsx b/contexts/AuthContext.tsx index 9795114..6b01656 100644 --- a/contexts/AuthContext.tsx +++ b/contexts/AuthContext.tsx @@ -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,28 +24,89 @@ interface IAuthContext { refreshProfileData: () => Promise } +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(undefined!) export const AuthContextProvider: FC<{ children: ReactNode }> = ({children}) => { - const [user, setUser] = useState(null) + const [user, setUser] = useState(null); const [initializing, setInitializing] = useState(true); const [profileType, setProfileType] = useState(undefined); const [profileData, setProfileData] = useState(undefined); - - const {replace} = useRouter() - const ready = !initializing + const {replace} = useRouter(); + const ready = !initializing; const onAuthStateChangedHandler = async (authUser: FirebaseAuthTypes.User | null) => { setUser(authUser); if (authUser) { await refreshProfileData(authUser); + const pushToken = await registerForPushNotificationsAsync(); + if (pushToken) { + await savePushTokenToFirestore(authUser.uid, pushToken); + } } if (initializing) setInitializing(false); - } + }; - const refreshProfileData = async (authUser: FirebaseAuthTypes.User) => { + const refreshProfileData = async (user?: FirebaseAuthTypes.User) => { + const authUser = user ?? auth().currentUser if (authUser) { try { const documentSnapshot = await firestore() @@ -58,6 +124,16 @@ export const AuthContextProvider: FC<{ children: ReactNode }> = ({children}) => } }; + 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(onAuthStateChangedHandler); return subscriber; @@ -71,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]); @@ -85,7 +161,8 @@ export const AuthContextProvider: FC<{ children: ReactNode }> = ({children}) => {children} - ) -} + ); +}; + export const useAuthContext = () => useContext(AuthContext)!; diff --git a/contexts/CalendarContext.tsx b/contexts/CalendarContext.tsx index 4e5551b..b084caa 100644 --- a/contexts/CalendarContext.tsx +++ b/contexts/CalendarContext.tsx @@ -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 diff --git a/firebase/functions/index.js b/firebase/functions/index.js index a592e3d..43e13ee 100644 --- a/firebase/functions/index.js +++ b/firebase/functions/index.js @@ -1,14 +1,107 @@ 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 pushTokens = await getPushTokensForEvent(eventData); + + 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); + } + } + + 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 +190,20 @@ 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; +} + diff --git a/firebase/functions/package-lock.json b/firebase/functions/package-lock.json index 535835a..8f16530 100644 --- a/firebase/functions/package-lock.json +++ b/firebase/functions/package-lock.json @@ -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" diff --git a/firebase/functions/package.json b/firebase/functions/package.json index 379e7e2..7f49cb0 100644 --- a/firebase/functions/package.json +++ b/firebase/functions/package.json @@ -14,6 +14,7 @@ }, "main": "index.js", "dependencies": { + "expo-server-sdk": "^3.11.0", "firebase-admin": "^12.1.0", "firebase-functions": "^5.0.0" }, diff --git a/hooks/firebase/useCreateEvent.ts b/hooks/firebase/useCreateEvent.ts index 33bdaa6..c7efcfc 100644 --- a/hooks/firebase/useCreateEvent.ts +++ b/hooks/firebase/useCreateEvent.ts @@ -11,6 +11,7 @@ export const useCreateEvent = () => { mutationKey: ["createEvent"], mutationFn: async (eventData: Partial) => { try { + console.log("CALLLLL") await firestore() .collection("Events") .add({...eventData, creatorId: currentUser?.uid}) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 96c5cc1..3ac0afc 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -977,6 +977,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 +1188,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 +1196,8 @@ PODS: - ZXingObjC/PDF417 - ExpoCrypto (13.0.2): - ExpoModulesCore + - ExpoDevice (6.0.2): + - ExpoModulesCore - ExpoFileSystem (17.0.1): - ExpoModulesCore - ExpoFont (12.0.10): @@ -1351,10 +1351,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 +1454,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) @@ -2704,9 +2697,6 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNGoogleSignin (13.1.0): - - GoogleSignIn (~> 7.1) - - React-Core - RNReanimated (3.10.1): - DoubleConversion - glog @@ -2770,15 +2760,16 @@ DEPENDENCIES: - 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`) @@ -2853,7 +2844,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,11 +2871,9 @@ SPEC REPOS: - FirebaseSessions - FirebaseSharedSwift - GoogleDataTransport - - GoogleSignIn - GoogleUtilities - "gRPC-C++" - gRPC-Core - - GTMAppAuth - GTMSessionFetcher - leveldb-library - nanopb @@ -2914,6 +2902,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 +2914,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: @@ -3077,8 +3067,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: @@ -3101,15 +3089,16 @@ SPEC CHECKSUMS: 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 @@ -3140,11 +3129,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 @@ -3210,7 +3197,6 @@ SPEC CHECKSUMS: RNFBFirestore: e47cdde04ea3d9e73e58e037e1aa1d0b1141c316 RNFBFunctions: 738cc9e2177d060d29b5d143ef2f9ed0eda4bb1f RNGestureHandler: 20a4307fd21cbff339abfcfa68192f3f0a6a518b - RNGoogleSignin: 9e68b9bcc3888219357924e32ee563624745647d RNReanimated: d51431fd3597a8f8320319dce8e42cee82a5445f RNScreens: 30249f9331c3b00ae7cb7922e11f58b3ed369c07 RNSVG: 4590aa95758149fa27c5c83e54a6a466349a1688 diff --git a/ios/cally.xcodeproj/project.pbxproj b/ios/cally.xcodeproj/project.pbxproj index f1636ec..58e4b09 100644 --- a/ios/cally.xcodeproj/project.pbxproj +++ b/ios/cally.xcodeproj/project.pbxproj @@ -298,6 +298,8 @@ "${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}/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,10 +310,8 @@ "${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", @@ -331,6 +331,8 @@ "${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}/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,10 +343,8 @@ "${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", diff --git a/ios/cally/Info.plist b/ios/cally/Info.plist index 27ad89a..f808154 100644 --- a/ios/cally/Info.plist +++ b/ios/cally/Info.plist @@ -82,6 +82,9 @@ $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route + $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route + $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route + $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route UILaunchStoryboardName SplashScreen diff --git a/ios/cally/cally.entitlements b/ios/cally/cally.entitlements index f683276..018a6e2 100644 --- a/ios/cally/cally.entitlements +++ b/ios/cally/cally.entitlements @@ -1,5 +1,8 @@ - + + aps-environment + development + \ No newline at end of file diff --git a/package.json b/package.json index f71b956..b195298 100644 --- a/package.json +++ b/package.json @@ -46,9 +46,11 @@ "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", diff --git a/yarn.lock b/yarn.lock index 490277e..76c74a5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1679,6 +1679,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" @@ -3289,6 +3294,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" @@ -3468,6 +3484,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" @@ -3697,7 +3718,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== @@ -4366,7 +4387,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== @@ -4917,6 +4938,13 @@ 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-file-system@~17.0.1: version "17.0.1" resolved "https://registry.npmjs.org/expo-file-system/-/expo-file-system-17.0.1.tgz" @@ -4987,6 +5015,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" @@ -6164,6 +6206,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" @@ -7906,12 +7956,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== @@ -9482,7 +9540,16 @@ string-length@^5.0.1: char-regex "^2.0.0" strip-ansi "^7.0.1" -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -9542,7 +9609,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -9556,6 +9623,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz" @@ -10014,6 +10088,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" @@ -10178,7 +10257,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== @@ -10418,7 +10497,7 @@ wonka@^4.0.14: resolved "https://registry.npmjs.org/wonka/-/wonka-4.0.15.tgz" integrity sha512-U0IUQHKXXn6PFo9nqsHphVCE5m3IntqZNB9Jjn7EB1lrR7YTDY3YWgFvEvwniTzXSvOH/XMzAZaIfJF/LvHYXg== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -10436,6 +10515,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz" From a8ab69b69f2ddd291bfa76d484e629b0834dfcc9 Mon Sep 17 00:00:00 2001 From: ivic00 <102467664+ivic00@users.noreply.github.com> Date: Sat, 12 Oct 2024 00:08:15 +0200 Subject: [PATCH 11/14] new icons --- app/(auth)/_layout.tsx | 29 ++++++----------- assets/svgs/CalendarIcon.tsx | 19 ++++++++++++ assets/svgs/NavBrainDumpIcon.tsx | 36 ++++++++++++++++++++++ assets/svgs/NavCalendarIcon.tsx | 19 ++++++++++++ assets/svgs/NavGroceryIcon.tsx | 20 ++++++++++++ assets/svgs/NavSettingsIcon.tsx | 28 +++++++++++++++++ assets/svgs/NavToDosIcon.tsx | 18 +++++++++++ assets/svgs/PrivacyPolicyIcon.tsx | 23 ++++++++++++++ assets/svgs/ProfileIcon.tsx | 26 ++++++++++++++++ components/pages/settings/SettingsPage.tsx | 24 ++++----------- components/pages/todos/AddChoreDialog.tsx | 19 +++++++++--- 11 files changed, 219 insertions(+), 42 deletions(-) create mode 100644 assets/svgs/CalendarIcon.tsx create mode 100644 assets/svgs/NavBrainDumpIcon.tsx create mode 100644 assets/svgs/NavCalendarIcon.tsx create mode 100644 assets/svgs/NavGroceryIcon.tsx create mode 100644 assets/svgs/NavSettingsIcon.tsx create mode 100644 assets/svgs/NavToDosIcon.tsx create mode 100644 assets/svgs/PrivacyPolicyIcon.tsx create mode 100644 assets/svgs/ProfileIcon.tsx diff --git a/app/(auth)/_layout.tsx b/app/(auth)/_layout.tsx index 42a9438..d823e99 100644 --- a/app/(auth)/_layout.tsx +++ b/app/(auth)/_layout.tsx @@ -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(); @@ -52,26 +57,14 @@ export default function TabLayout() { color="rgb(7, 184, 199)" bgColor={"rgb(231, 248, 250)"} pressFunc={() => props.navigation.navigate("calendar")} - icon={ - - } + icon={} /> props.navigation.navigate("grocery")} - icon={ - - } + icon={} /> @@ -93,16 +86,14 @@ export default function TabLayout() { title={"To Do's"} bgColor={"#f3e6fd"} pressFunc={() => props.navigation.navigate("todos")} - icon={ - - } + icon={} /> props.navigation.navigate("brain_dump")} - icon={} + icon={} /> {/* signOut()} />*/} @@ -120,7 +111,7 @@ export default function TabLayout() { centerV centerH > - + )} backgroundColor="white" diff --git a/assets/svgs/CalendarIcon.tsx b/assets/svgs/CalendarIcon.tsx new file mode 100644 index 0000000..a688729 --- /dev/null +++ b/assets/svgs/CalendarIcon.tsx @@ -0,0 +1,19 @@ +import * as React from "react" +import Svg, { Path, SvgProps } from "react-native-svg" +const CalendarIcon: React.FC = (props) => ( + + + +) +export default CalendarIcon diff --git a/assets/svgs/NavBrainDumpIcon.tsx b/assets/svgs/NavBrainDumpIcon.tsx new file mode 100644 index 0000000..ea4aa72 --- /dev/null +++ b/assets/svgs/NavBrainDumpIcon.tsx @@ -0,0 +1,36 @@ +import * as React from "react" +import Svg, { Path, SvgProps } from "react-native-svg" +const NavBrainDumpIcon: React.FC = (props) => ( + + + + + +) +export default NavBrainDumpIcon diff --git a/assets/svgs/NavCalendarIcon.tsx b/assets/svgs/NavCalendarIcon.tsx new file mode 100644 index 0000000..19ea7d8 --- /dev/null +++ b/assets/svgs/NavCalendarIcon.tsx @@ -0,0 +1,19 @@ +import * as React from "react" +import Svg, { Path, SvgProps } from "react-native-svg" +const NavCalendarIcon: React.FC = (props) => ( + + + +) +export default NavCalendarIcon diff --git a/assets/svgs/NavGroceryIcon.tsx b/assets/svgs/NavGroceryIcon.tsx new file mode 100644 index 0000000..cb219b8 --- /dev/null +++ b/assets/svgs/NavGroceryIcon.tsx @@ -0,0 +1,20 @@ +import * as React from "react"; +import Svg, { Path, SvgProps } from "react-native-svg"; +const NavGroceryIcon: React.FC = (props) => ( + + + + +); +export default NavGroceryIcon; diff --git a/assets/svgs/NavSettingsIcon.tsx b/assets/svgs/NavSettingsIcon.tsx new file mode 100644 index 0000000..8027111 --- /dev/null +++ b/assets/svgs/NavSettingsIcon.tsx @@ -0,0 +1,28 @@ +import * as React from "react"; +import Svg, { Path, SvgProps } from "react-native-svg"; +const NavSettingsIcon: React.FC = (props) => ( + + + + +); +export default NavSettingsIcon; diff --git a/assets/svgs/NavToDosIcon.tsx b/assets/svgs/NavToDosIcon.tsx new file mode 100644 index 0000000..2cbb70a --- /dev/null +++ b/assets/svgs/NavToDosIcon.tsx @@ -0,0 +1,18 @@ +import * as React from "react" +import Svg, { Path, SvgProps } from "react-native-svg" +const NavToDosIcon: React.FC = (props) => ( + + + +) +export default NavToDosIcon diff --git a/assets/svgs/PrivacyPolicyIcon.tsx b/assets/svgs/PrivacyPolicyIcon.tsx new file mode 100644 index 0000000..f0ed5db --- /dev/null +++ b/assets/svgs/PrivacyPolicyIcon.tsx @@ -0,0 +1,23 @@ +import * as React from "react" +import Svg, { Path, SvgProps } from "react-native-svg" +const PrivacyPolicyIcon: React.FC = (props) => ( + + + + +) +export default PrivacyPolicyIcon diff --git a/assets/svgs/ProfileIcon.tsx b/assets/svgs/ProfileIcon.tsx new file mode 100644 index 0000000..7ba0c76 --- /dev/null +++ b/assets/svgs/ProfileIcon.tsx @@ -0,0 +1,26 @@ +import * as React from "react" +import Svg, { Path, SvgProps } from "react-native-svg" +const ProfileIcon: React.FC = (props) => ( + + + + +) +export default ProfileIcon diff --git a/components/pages/settings/SettingsPage.tsx b/components/pages/settings/SettingsPage.tsx index 6a3d07c..0c799c7 100644 --- a/components/pages/settings/SettingsPage.tsx +++ b/components/pages/settings/SettingsPage.tsx @@ -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={() => ( - + )} onPress={() => setSelectedPage(pageIndex.user)} /> @@ -49,12 +47,7 @@ const SettingsPage = () => { label="Calendar settings" color="#fd1775" iconSource={() => ( - + )} onPress={() => { setSelectedPage(pageIndex.calendar); @@ -80,12 +73,7 @@ const SettingsPage = () => { style={styles.mainBtn} label="Kaly privacy policy" iconSource={() => ( - + )} color="#6c645b" /> diff --git a/components/pages/todos/AddChoreDialog.tsx b/components/pages/todos/AddChoreDialog.tsx index 7b34109..e38180d 100644 --- a/components/pages/todos/AddChoreDialog.tsx +++ b/components/pages/todos/AddChoreDialog.tsx @@ -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(false); const [repeatType, setRepeatType] = useState("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(); }} /> { - addChoreDialogProps.setIsVisible(false); + handleCLose() }} /> @@ -84,7 +93,7 @@ const AddChoreDialog = (addChoreDialogProps: IAddChoreDialog) => { rotate: rotate, repeatType: repeatType, }); - addChoreDialogProps.setIsVisible(false); + handleCLose(); console.log(toDos); } catch (error) { console.error(error); From 3d4795c25d4318229d01433ac70d7f5bee3a2196 Mon Sep 17 00:00:00 2001 From: Milan Paunovic Date: Sat, 12 Oct 2024 07:01:56 +0200 Subject: [PATCH 12/14] New build --- app.json | 2 +- ios/cally/Info.plist | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app.json b/app.json index 84c0c79..f1836a4 100644 --- a/app.json +++ b/app.json @@ -16,7 +16,7 @@ "supportsTablet": true, "bundleIdentifier": "com.cally.app", "googleServicesFile": "./ios/GoogleService-Info.plist", - "buildNumber": "19" + "buildNumber": "20" }, "android": { "adaptiveIcon": { diff --git a/ios/cally/Info.plist b/ios/cally/Info.plist index f808154..553278d 100644 --- a/ios/cally/Info.plist +++ b/ios/cally/Info.plist @@ -45,7 +45,7 @@ CFBundleVersion - 19 + 20 LSRequiresIPhoneOS NSAppTransportSecurity @@ -85,6 +85,7 @@ $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route + $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route UILaunchStoryboardName SplashScreen From 6e1e665b931cebdf821bfc0a8d5a4c06d9363b27 Mon Sep 17 00:00:00 2001 From: Milan Paunovic Date: Sat, 12 Oct 2024 09:15:30 +0200 Subject: [PATCH 13/14] NOtification update --- android/app/src/main/AndroidManifest.xml | 4 +- android/app/src/main/res/values/strings.xml | 1 + app.json | 10 +++- eas.json | 10 ++-- firebase/functions/index.js | 31 +++++++++--- hooks/firebase/useCreateEvent.ts | 4 +- ios/cally/Info.plist | 3 ++ ios/cally/Supporting/Expo.plist | 6 ++- package.json | 3 +- yarn.lock | 52 ++++++++++++++++++++- 10 files changed, 107 insertions(+), 17 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 1319cbe..3a5ebac 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -17,9 +17,11 @@ - + + + diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 5c7a9f1..4713052 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -3,4 +3,5 @@ contain false light + 1.0.0 \ No newline at end of file diff --git a/app.json b/app.json index f1836a4..a73d188 100644 --- a/app.json +++ b/app.json @@ -24,7 +24,11 @@ "backgroundColor": "#ffffff" }, "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": { "bundler": "metro", @@ -70,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" } } } diff --git a/eas.json b/eas.json index a45666d..e022e28 100644 --- a/eas.json +++ b/eas.json @@ -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": { diff --git a/firebase/functions/index.js b/firebase/functions/index.js index 43e13ee..37e0c53 100644 --- a/firebase/functions/index.js +++ b/firebase/functions/index.js @@ -16,8 +16,14 @@ exports.sendNotificationOnEventCreation = functions.firestore .document('Events/{eventId}') .onCreate(async (snapshot, context) => { const eventData = snapshot.data(); + const { familyId, creatorId } = eventData; - const pushTokens = await getPushTokensForEvent(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.'); @@ -36,7 +42,7 @@ exports.sendNotificationOnEventCreation = functions.firestore sound: 'default', title: 'New Event Added!', body: `An event "${eventData.title}" has been added. Check it out!`, - data: {eventId: context.params.eventId}, + data: { eventId: context.params.eventId }, }); } @@ -45,7 +51,6 @@ exports.sendNotificationOnEventCreation = functions.firestore for (let chunk of chunks) { try { let ticketChunk = await expo.sendPushNotificationsAsync(chunk); - tickets.push(...ticketChunk); for (let ticket of ticketChunk) { @@ -53,7 +58,6 @@ exports.sendNotificationOnEventCreation = functions.firestore 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') { @@ -63,12 +67,12 @@ exports.sendNotificationOnEventCreation = functions.firestore } } } - } catch (error) { console.error('Error sending notification:', error); } } + // Retrieve and handle notification receipts let receiptIds = []; for (let ticket of tickets) { if (ticket.id) { @@ -84,7 +88,6 @@ exports.sendNotificationOnEventCreation = functions.firestore 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') { @@ -99,7 +102,6 @@ exports.sendNotificationOnEventCreation = functions.firestore } } - return null; }); @@ -207,3 +209,18 @@ async function getPushTokensForEvent() { 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; +} diff --git a/hooks/firebase/useCreateEvent.ts b/hooks/firebase/useCreateEvent.ts index c7efcfc..09eceaf 100644 --- a/hooks/firebase/useCreateEvent.ts +++ b/hooks/firebase/useCreateEvent.ts @@ -4,7 +4,7 @@ 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({ @@ -14,7 +14,7 @@ export const useCreateEvent = () => { 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) } diff --git a/ios/cally/Info.plist b/ios/cally/Info.plist index 553278d..0bab191 100644 --- a/ios/cally/Info.plist +++ b/ios/cally/Info.plist @@ -86,6 +86,9 @@ $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route + $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route + $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route + $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route UILaunchStoryboardName SplashScreen diff --git a/ios/cally/Supporting/Expo.plist b/ios/cally/Supporting/Expo.plist index 750be02..a0a9a33 100644 --- a/ios/cally/Supporting/Expo.plist +++ b/ios/cally/Supporting/Expo.plist @@ -5,8 +5,12 @@ EXUpdatesCheckOnLaunch ALWAYS EXUpdatesEnabled - + EXUpdatesLaunchWaitMs 0 + EXUpdatesRuntimeVersion + 1.0.0 + EXUpdatesURL + https://u.expo.dev/bdb8c57b-25bb-4d36-b3b8-5b09c5092f52 \ No newline at end of file diff --git a/package.json b/package.json index b195298..e69425e 100644 --- a/package.json +++ b/package.json @@ -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" }, @@ -55,6 +55,7 @@ "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", diff --git a/yarn.lock b/yarn.lock index 76c74a5..52158be 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1018,6 +1018,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" @@ -3228,6 +3242,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" @@ -4945,6 +4964,11 @@ expo-device@~6.0.2: 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" @@ -5056,6 +5080,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" @@ -5069,6 +5098,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" @@ -5963,7 +6013,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== From b3e40ad90935a27968c7eaff20c0d79f75a35ef4 Mon Sep 17 00:00:00 2001 From: Milan Paunovic Date: Sat, 12 Oct 2024 10:25:36 +0200 Subject: [PATCH 14/14] Use cally everywhere --- app/(auth)/_layout.tsx | 4 +- .../pages/calendar/ManuallyAddEventModal.tsx | 5 +- components/pages/main/SignUpPage.tsx | 2 +- .../pages/onboarding/OnboardingFlow.tsx | 13 +++-- ios/Podfile.lock | 51 +++++++++++++++++++ ios/cally.xcodeproj/project.pbxproj | 8 ++- 6 files changed, 73 insertions(+), 10 deletions(-) diff --git a/app/(auth)/_layout.tsx b/app/(auth)/_layout.tsx index d823e99..2edbd97 100644 --- a/app/(auth)/_layout.tsx +++ b/app/(auth)/_layout.tsx @@ -43,7 +43,7 @@ export default function TabLayout() { return ( - Welcome to Kali + Welcome to Cally signOut()} /> diff --git a/components/pages/calendar/ManuallyAddEventModal.tsx b/components/pages/calendar/ManuallyAddEventModal.tsx index 5c365f2..b0db46b 100644 --- a/components/pages/calendar/ManuallyAddEventModal.tsx +++ b/components/pages/calendar/ManuallyAddEventModal.tsx @@ -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", diff --git a/components/pages/main/SignUpPage.tsx b/components/pages/main/SignUpPage.tsx index 390cd97..5c843f9 100644 --- a/components/pages/main/SignUpPage.tsx +++ b/components/pages/main/SignUpPage.tsx @@ -31,7 +31,7 @@ const SignUpPage = ({setTab}: { setTab: React.Dispatch - Get started with Kali + Get started with Cally Please enter your details. { width={10} /> ), - title: Welcome to Kali, + title: Welcome to Cally, subtitle: ( Lightening Mental Loads, One Family at a Time