From 04a6103470da8d9658b353aea02a50dc235a8993 Mon Sep 17 00:00:00 2001 From: Milan Paunovic Date: Fri, 11 Oct 2024 23:53:08 +0200 Subject: [PATCH] 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"