notifications

This commit is contained in:
2025-10-07 16:18:05 +02:00
parent b508857000
commit a4dca1d4f0
10 changed files with 499 additions and 4 deletions
+1
View File
@@ -50,6 +50,7 @@ SplashScreen.preventAutoHideAsync();
const App = () => { const App = () => {
const [currentUID, setCurrentUID] = useGlobal("currentUID"); const [currentUID, setCurrentUID] = useGlobal("currentUID");
const [pendingData, setPendingData] = useGlobal("pendingData");
const [, setCurrentUserRoles] = useGlobal("currentUserRoles"); const [, setCurrentUserRoles] = useGlobal("currentUserRoles");
const [appIsReady, setAppIsReady] = useState(false); const [appIsReady, setAppIsReady] = useState(false);
+8
View File
@@ -8,6 +8,13 @@ exports.refList = {
projects: admin.firestore().collection("projects"), projects: admin.firestore().collection("projects"),
playlists: admin.firestore().collection("playlists"), playlists: admin.firestore().collection("playlists"),
tasks: admin.firestore().collection("tasks"), tasks: admin.firestore().collection("tasks"),
notifications: admin.firestore().collection("notifications"),
};
exports.ALERT_TYPE = {
NEW_LIKE: "NEW_LIKE",
NEW_COMMENT: "NEW_COMMENT",
NEW_FOLLOWER: "NEW_FOLLOWER",
}; };
// Exporter toutes les fonctions // Exporter toutes les fonctions
@@ -19,3 +26,4 @@ exports.projects = require("./src/project");
exports.thumbnail = require("./src/thumbnail"); exports.thumbnail = require("./src/thumbnail");
exports.upload = require("./src/upload"); exports.upload = require("./src/upload");
exports.algolia = require("./src/algolia"); exports.algolia = require("./src/algolia");
exports.notifications = require("./src/notifications");
+69
View File
@@ -10,6 +10,7 @@
"@genkit-ai/googleai": "^1.18.0", "@genkit-ai/googleai": "^1.18.0",
"@google-cloud/storage": "^7.17.1", "@google-cloud/storage": "^7.17.1",
"axios": "^1.11.0", "axios": "^1.11.0",
"expo-server-sdk": "^4.0.0",
"ffmpeg-static": "^5.2.0", "ffmpeg-static": "^5.2.0",
"firebase-admin": "^12.7.0", "firebase-admin": "^12.7.0",
"firebase-functions": "^6.4.0", "firebase-functions": "^6.4.0",
@@ -3416,6 +3417,12 @@
"node": ">=6" "node": ">=6"
} }
}, },
"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": { "node_modules/error": {
"version": "7.0.2", "version": "7.0.2",
"resolved": "https://registry.npmjs.org/error/-/error-7.0.2.tgz", "resolved": "https://registry.npmjs.org/error/-/error-7.0.2.tgz",
@@ -3703,6 +3710,40 @@
"uuid": "dist/bin/uuid" "uuid": "dist/bin/uuid"
} }
}, },
"node_modules/expo-server-sdk": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/expo-server-sdk/-/expo-server-sdk-4.0.0.tgz",
"integrity": "sha512-zi83XtG2pqyP3gyn1JIRYkydo2i6HU3CYaWo/VvhZG/F29U+QIDv6LBEUsWf4ddZlVE7c9WN1N8Be49rHgO8OQ==",
"license": "MIT",
"dependencies": {
"node-fetch": "^2.6.0",
"promise-limit": "^2.7.0",
"promise-retry": "^2.0.1"
},
"engines": {
"node": ">=20"
}
},
"node_modules/expo-server-sdk/node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"license": "MIT",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/express": { "node_modules/express": {
"version": "4.21.2", "version": "4.21.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
@@ -5707,6 +5748,34 @@
"node": ">=0.4.0" "node": ">=0.4.0"
} }
}, },
"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/proto3-json-serializer": { "node_modules/proto3-json-serializer": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz",
+1
View File
@@ -18,6 +18,7 @@
"@genkit-ai/googleai": "^1.18.0", "@genkit-ai/googleai": "^1.18.0",
"@google-cloud/storage": "^7.17.1", "@google-cloud/storage": "^7.17.1",
"axios": "^1.11.0", "axios": "^1.11.0",
"expo-server-sdk": "^4.0.0",
"ffmpeg-static": "^5.2.0", "ffmpeg-static": "^5.2.0",
"firebase-admin": "^12.7.0", "firebase-admin": "^12.7.0",
"firebase-functions": "^6.4.0", "firebase-functions": "^6.4.0",
+213
View File
@@ -0,0 +1,213 @@
const { onDocumentCreated } = require("firebase-functions/v2/firestore");
const admin = require("firebase-admin");
const { refList, ALERT_TYPE } = require("../index");
const { Expo } = require("expo-server-sdk");
// Initialisation de Expo SDK
let expo = new Expo();
const db = admin.firestore();
exports.sendNotificationWhenDocIsCreated = onDocumentCreated(
{ region: "europe-west1", document: "notifications/{notificationId}" },
async (event) => {
try {
const {
receiver = null,
title = "MusicLand",
receiverCollection = "users",
message = "",
data: notifData = {},
} = event.data.data();
const { pushToken = null } =
(await db.collection(receiverCollection).doc(receiver).get())?.data() ||
{};
if (!receiver || !message) {
throw new Error("Receiver and message are required");
}
if (pushToken) {
await sendExpoNotification(pushToken, {
title: title || "MusicLand",
message: message,
data: notifData || {},
});
} else {
console.warn("User push token not found");
}
} catch (e) {
console.log(e);
return e;
}
}
);
// Fonction pour envoyer la notification via Expo SDK
async function sendExpoNotification(
pushToken,
{ title = "", message = "", data = {} }
) {
try {
if (!Expo.isExpoPushToken(pushToken)) {
throw new Error(`Invalid push token: ${pushToken}`);
} else {
console.log("Valid push token:", pushToken);
}
const receipts = await expo.sendPushNotificationsAsync([
{
to: pushToken,
sound: "default",
title: title,
body: message,
data: data || {},
priority: "high",
badge: 1,
channelId: "default",
},
]);
console.log("Sent push notifications:", receipts);
return { sent: true };
} catch (e) {
console.log("Error sending notification:", e);
throw e;
}
}
// Fonction pour ajouter une notification à la base de données
const sendNotification = async ({
sender = "SYSTEM",
receiver = null,
receiverCollection = "users",
title = "",
message = null,
data = {},
}) => {
try {
if (!receiver || !message) {
throw new Error("Receiver and message are required");
}
const { id } = await refList.notifications.add({
sender,
receiver,
receiverCollection,
title,
message,
time: new Date(),
data,
});
console.log(`Notification ${id} created`);
return id;
} catch (e) {
console.log(e);
}
};
exports.sendNotification = sendNotification;
exports.createProjectCommentNotification = onDocumentCreated(
{
region: "europe-west1",
document: "projects/{projectId}/comments/{commentId}",
},
async (event) => {
try {
console.log(
"[createProjectCommentNotification] Trigger received",
JSON.stringify(event.params || {}, null, 2)
);
const { data: snap } = event;
const { projectId, commentId } = event.params || {};
const comment = snap?.data();
if (!projectId || !comment) {
console.log(
"[createProjectCommentNotification] Missing project/comment data",
{ hasProjectId: !!projectId, hasComment: !!comment }
);
return null;
}
console.log(
"[createProjectCommentNotification] Comment payload",
JSON.stringify(comment, null, 2)
);
const projectSnap = await refList.projects.doc(projectId).get();
if (!projectSnap.exists) {
console.log(
"[createProjectCommentNotification] Project not found",
projectId
);
return null;
}
const project = projectSnap.data() || {};
const receiver = project.userId || null;
if (!receiver || receiver === comment.userId) {
console.log(
"[createProjectCommentNotification] Invalid receiver",
JSON.stringify({ receiver, commentUserId: comment.userId })
);
return null;
}
const commenterName =
typeof comment?.userName === "string" && comment.userName.trim()
? comment.userName.trim()
: "Un utilisateur";
const projectTitle =
typeof project.title === "string" && project.title.trim()
? project.title.trim()
: "ton projet";
const message = `${commenterName} a commenté ton projet "${projectTitle}"`;
console.log(
"[createProjectCommentNotification] Creating notification",
JSON.stringify(
{
receiver,
message,
commentId,
projectId,
},
null,
2
)
);
await sendNotification({
sender: comment.userId || "SYSTEM",
receiver,
receiverCollection: "users",
title: "Nouveau commentaire",
message,
data: {
type: ALERT_TYPE?.NEW_COMMENT,
projectId,
commentId,
commenterId: comment.userId || null,
commenterName: commenterName,
commenterProfilePicture: comment?.profilePicture || "",
text:
typeof comment?.text === "string" && comment.text.trim()
? comment.text.trim()
: "",
},
});
console.log(
"[createProjectCommentNotification] Notification creation complete"
);
return null;
} catch (error) {
console.log("createProjectCommentNotification error:", error);
return error;
}
}
);
+1
View File
@@ -54,6 +54,7 @@
"expo-linear-gradient": "~14.0.2", "expo-linear-gradient": "~14.0.2",
"expo-linking": "~7.0.5", "expo-linking": "~7.0.5",
"expo-location": "~18.0.10", "expo-location": "~18.0.10",
"expo-notifications": "~0.29.14",
"expo-speech": "~13.0.1", "expo-speech": "~13.0.1",
"expo-splash-screen": "~0.29.24", "expo-splash-screen": "~0.29.24",
"expo-status-bar": "~2.0.1", "expo-status-bar": "~2.0.1",
+103
View File
@@ -0,0 +1,103 @@
import Constants from "expo-constants";
import * as Device from "expo-device";
import * as Notifications from "expo-notifications";
import React, { createContext, useEffect, useRef, useState } from "react";
import { useGlobal } from "reactn";
import { usersRef } from "../config/firebase";
export const NotificationContext = createContext(null);
export default function NotificationProvider({ children }) {
const [user] = useGlobal("currentUserData");
const [uid] = useGlobal("currentUID");
const [pendingData, setPendingData] = useGlobal("pendingData");
const [notifInit, setNotifInit] = useState(false);
const [allowNotifications, setAllowNotifications] = useState(false);
const notificationListener = useRef();
const responseListener = useRef();
useEffect(() => {
if (!!uid && !!user && !notifInit) {
registerForPushNotificationsAsync();
} else if (notifInit && allowNotifications) {
notificationListener.current =
Notifications.addNotificationReceivedListener(async (notification) => {
console.log("Notification received", notification);
await Notifications.scheduleNotificationAsync(notification?.request);
});
responseListener.current =
Notifications.addNotificationResponseReceivedListener(
async (response) => {
console.log("Notification response", response);
if (
response?.actionIdentifier ===
"expo.modules.notifications.actions.DEFAULT"
) {
const { data } = response.notification.request.content;
if (data) {
console.log("Notification data", data);
await setPendingData(data);
}
}
}
);
}
return () => {
if (notifInit && allowNotifications) {
Notifications.removeNotificationSubscription(
notificationListener.current
);
Notifications.removeNotificationSubscription(responseListener.current);
}
};
}, [user, uid, notifInit]);
async function registerForPushNotificationsAsync() {
try {
if (Device.isDevice) {
const { status } = await Notifications.requestPermissionsAsync();
if (status !== "granted") {
console.log("Notification permissions denied");
return;
}
setAllowNotifications(true);
setNotifInit(true);
// Get the push token
const pushToken = (
await Notifications.getExpoPushTokenAsync({
projectId: Constants.expoConfig?.extra?.eas?.projectId || "",
})
).data;
if (user?.pushToken !== pushToken && !!user) {
await usersRef.doc(uid).update({ pushToken });
// if (!user?.alerts) {
// await usersRef.doc(uid).update({
// alerts: Object.values(ALERT_TYPE),
// });
// }
}
} else {
console.log("Must use physical device for Push Notifications");
}
} catch (e) {
console.log(e);
}
}
return (
<NotificationContext.Provider
value={{
allowNotifications,
pendingData,
setPendingData,
}}
>
{children}
</NotificationContext.Provider>
);
}
+2
View File
@@ -4,6 +4,7 @@ import React from "react";
import { SafeAreaProvider } from "react-native-safe-area-context"; import { SafeAreaProvider } from "react-native-safe-area-context";
import { SheetProvider } from "react-native-actions-sheet"; import { SheetProvider } from "react-native-actions-sheet";
import NotificationProvider from "./NotificationProvider";
import PlayerProvider from "./PlayerProvider"; import PlayerProvider from "./PlayerProvider";
import SplashAnimationProvider from "./SplashAnimationProvider"; import SplashAnimationProvider from "./SplashAnimationProvider";
import StripeEmbeddedProvider from "./StripeEmbeddedProvider"; import StripeEmbeddedProvider from "./StripeEmbeddedProvider";
@@ -23,6 +24,7 @@ const SharedProviders = ({ children }) => {
[BottomSheetModalProvider, {}], [BottomSheetModalProvider, {}],
[SheetProvider, {}], [SheetProvider, {}],
[PlayerProvider, {}], [PlayerProvider, {}],
[NotificationProvider, {}],
]; ];
// Dynamically nest the providers using reduce // Dynamically nest the providers using reduce
+12
View File
@@ -100,6 +100,7 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
// Follow state derived from owner.followedBy // Follow state derived from owner.followedBy
const [isFollowing, setIsFollowing] = useState(false); const [isFollowing, setIsFollowing] = useState(false);
const [isFollowActionPending, setIsFollowActionPending] = useState(false);
useEffect(() => { useEffect(() => {
const list = Array.isArray(owner?.followedBy) ? owner.followedBy : []; const list = Array.isArray(owner?.followedBy) ? owner.followedBy : [];
setIsFollowing(currentUID ? list.includes(currentUID) : false); setIsFollowing(currentUID ? list.includes(currentUID) : false);
@@ -278,9 +279,14 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
</Pressable> </Pressable>
{owner?.id && currentUID && owner.id !== currentUID && ( {owner?.id && currentUID && owner.id !== currentUID && (
<Pressable <Pressable
disabled={isFollowActionPending}
onPress={async () => { onPress={async () => {
if (isFollowActionPending) return;
try { try {
setIsFollowActionPending(true);
const next = !isFollowing; const next = !isFollowing;
console.log("follwin");
setIsFollowing(next); setIsFollowing(next);
// Optimistic update of local owner.followedBy // Optimistic update of local owner.followedBy
setOwner((prev) => { setOwner((prev) => {
@@ -294,9 +300,15 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
}); });
if (next) await followUser?.(owner.id); if (next) await followUser?.(owner.id);
else await unfollowUser?.(owner.id); else await unfollowUser?.(owner.id);
// Timeout de 2 secondes avant de pouvoir recliquer
global.setTimeout(() => {
setIsFollowActionPending(false);
}, 1000);
} catch (e) { } catch (e) {
// rollback on failure // rollback on failure
setIsFollowing((v) => !v); setIsFollowing((v) => !v);
setIsFollowActionPending(false);
} }
}} }}
> >
+89 -4
View File
@@ -1898,6 +1898,11 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba" resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba"
integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==
"@ide/backoff@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@ide/backoff/-/backoff-1.0.0.tgz#466842c25bd4a4833e0642fab41ccff064010176"
integrity sha512-F0YfUDjvT+Mtt/R4xdl2X0EYCHMMiJqNLdxHD++jDT5ydEFIyqbCHh51Qx2E211dgZprPKhV7sHmnXKpLuvc5g==
"@isaacs/cliui@^8.0.2": "@isaacs/cliui@^8.0.2":
version "8.0.2" version "8.0.2"
resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550"
@@ -3155,6 +3160,17 @@ asap@~2.0.3, asap@~2.0.6:
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==
assert@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/assert/-/assert-2.1.0.tgz#6d92a238d05dc02e7427c881fb8be81c8448b2dd"
integrity sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==
dependencies:
call-bind "^1.0.2"
is-nan "^1.3.2"
object-is "^1.1.5"
object.assign "^4.1.4"
util "^0.12.5"
ast-types@0.15.2: ast-types@0.15.2:
version "0.15.2" version "0.15.2"
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.15.2.tgz#39ae4809393c4b16df751ee563411423e85fb49d" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.15.2.tgz#39ae4809393c4b16df751ee563411423e85fb49d"
@@ -3322,6 +3338,11 @@ babel-preset-jest@^29.6.3:
babel-plugin-jest-hoist "^29.6.3" babel-plugin-jest-hoist "^29.6.3"
babel-preset-current-node-syntax "^1.0.0" babel-preset-current-node-syntax "^1.0.0"
badgin@^1.1.5:
version "1.2.3"
resolved "https://registry.yarnpkg.com/badgin/-/badgin-1.2.3.tgz#994b5f519827d7d5422224825b2c8faea2bc43ad"
integrity sha512-NQGA7LcfCpSzIbGRbkgjgdWkjy7HI+Th5VLxTJfW5EeaAf3fnS+xWQaQOCYiny+q6QSvxqoSO04vCx+4u++EJw==
balanced-match@^1.0.0: balanced-match@^1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
@@ -3557,7 +3578,7 @@ call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-
es-errors "^1.3.0" es-errors "^1.3.0"
function-bind "^1.1.2" function-bind "^1.1.2"
call-bind@^1.0.7, call-bind@^1.0.8: call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.7, call-bind@^1.0.8:
version "1.0.8" version "1.0.8"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c"
integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww== integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==
@@ -5233,6 +5254,19 @@ expo-modules-core@2.2.3:
dependencies: dependencies:
invariant "^2.2.4" invariant "^2.2.4"
expo-notifications@~0.29.14:
version "0.29.14"
resolved "https://registry.yarnpkg.com/expo-notifications/-/expo-notifications-0.29.14.tgz#77beb6bc74b1b1abfa3adcab77fb6c9ea5d7d1b0"
integrity sha512-AVduNx9mKOgcAqBfrXS1OHC9VAQZrDQLbVbcorMjPDGXW7m0Q5Q+BG6FYM/saVviF2eO8fhQRsTT40yYv5/bhQ==
dependencies:
"@expo/image-utils" "^0.6.5"
"@ide/backoff" "^1.0.0"
abort-controller "^3.0.0"
assert "^2.0.0"
badgin "^1.1.5"
expo-application "~6.0.2"
expo-constants "~17.0.8"
expo-speech@~13.0.1: expo-speech@~13.0.1:
version "13.0.1" version "13.0.1"
resolved "https://registry.yarnpkg.com/expo-speech/-/expo-speech-13.0.1.tgz#f49fc2c8a73f9bada030e52fc86a235cd3963f4a" resolved "https://registry.yarnpkg.com/expo-speech/-/expo-speech-13.0.1.tgz#f49fc2c8a73f9bada030e52fc86a235cd3963f4a"
@@ -5791,6 +5825,11 @@ functions-have-names@^1.2.3:
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
generator-function@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/generator-function/-/generator-function-2.0.1.tgz#0e75dd410d1243687a0ba2e951b94eedb8f737a2"
integrity sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==
gensync@^1.0.0-beta.2: gensync@^1.0.0-beta.2:
version "1.0.0-beta.2" version "1.0.0-beta.2"
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
@@ -6332,7 +6371,7 @@ inflight@^1.0.4:
once "^1.3.0" once "^1.3.0"
wrappy "1" wrappy "1"
inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@~2.0.3: inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3:
version "2.0.4" version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -6392,6 +6431,14 @@ ipaddr.js@^1.9.0:
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
is-arguments@^1.0.4:
version "1.2.0"
resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.2.0.tgz#ad58c6aecf563b78ef2bf04df540da8f5d7d8e1b"
integrity sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==
dependencies:
call-bound "^1.0.2"
has-tostringtag "^1.0.2"
is-array-buffer@^3.0.4, is-array-buffer@^3.0.5: is-array-buffer@^3.0.4, is-array-buffer@^3.0.5:
version "3.0.5" version "3.0.5"
resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz#65742e1e687bd2cc666253068fd8707fe4d44280" resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz#65742e1e687bd2cc666253068fd8707fe4d44280"
@@ -6520,6 +6567,17 @@ is-generator-function@^1.0.10:
has-tostringtag "^1.0.2" has-tostringtag "^1.0.2"
safe-regex-test "^1.1.0" safe-regex-test "^1.1.0"
is-generator-function@^1.0.7:
version "1.1.2"
resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.1.2.tgz#ae3b61e3d5ea4e4839b90bad22b02335051a17d5"
integrity sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==
dependencies:
call-bound "^1.0.4"
generator-function "^2.0.0"
get-proto "^1.0.1"
has-tostringtag "^1.0.2"
safe-regex-test "^1.1.0"
is-gif@^3.0.0: is-gif@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/is-gif/-/is-gif-3.0.0.tgz#c4be60b26a301d695bb833b20d9b5d66c6cf83b1" resolved "https://registry.yarnpkg.com/is-gif/-/is-gif-3.0.0.tgz#c4be60b26a301d695bb833b20d9b5d66c6cf83b1"
@@ -6552,6 +6610,14 @@ is-map@^2.0.3:
resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e"
integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==
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-natural-number@^4.0.1: is-natural-number@^4.0.1:
version "4.0.1" version "4.0.1"
resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8" resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8"
@@ -6678,7 +6744,7 @@ is-symbol@^1.0.4, is-symbol@^1.1.1:
has-symbols "^1.1.0" has-symbols "^1.1.0"
safe-regex-test "^1.1.0" safe-regex-test "^1.1.0"
is-typed-array@^1.1.13, is-typed-array@^1.1.14, is-typed-array@^1.1.15: is-typed-array@^1.1.13, is-typed-array@^1.1.14, is-typed-array@^1.1.15, is-typed-array@^1.1.3:
version "1.1.15" version "1.1.15"
resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.15.tgz#4bfb4a45b61cee83a5a46fba778e4e8d59c0ce0b" resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.15.tgz#4bfb4a45b61cee83a5a46fba778e4e8d59c0ce0b"
integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ== integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==
@@ -7945,6 +8011,14 @@ object-inspect@^1.13.3, object-inspect@^1.13.4:
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213"
integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==
object-is@^1.1.5:
version "1.1.6"
resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07"
integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==
dependencies:
call-bind "^1.0.7"
define-properties "^1.2.1"
object-keys@^1.1.1: object-keys@^1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
@@ -10497,6 +10571,17 @@ util-deprecate@~1.0.1:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
util@^0.12.5:
version "0.12.5"
resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc"
integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==
dependencies:
inherits "^2.0.3"
is-arguments "^1.0.4"
is-generator-function "^1.0.7"
is-typed-array "^1.1.3"
which-typed-array "^1.1.2"
utils-merge@1.0.1: utils-merge@1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
@@ -10650,7 +10735,7 @@ which-collection@^1.0.2:
is-weakmap "^2.0.2" is-weakmap "^2.0.2"
is-weakset "^2.0.3" is-weakset "^2.0.3"
which-typed-array@^1.1.16, which-typed-array@^1.1.19: which-typed-array@^1.1.16, which-typed-array@^1.1.19, which-typed-array@^1.1.2:
version "1.1.19" version "1.1.19"
resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956"
integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw== integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==