diff --git a/App.js b/App.js
index d661ab6..cf00f86 100644
--- a/App.js
+++ b/App.js
@@ -50,6 +50,7 @@ SplashScreen.preventAutoHideAsync();
const App = () => {
const [currentUID, setCurrentUID] = useGlobal("currentUID");
+ const [pendingData, setPendingData] = useGlobal("pendingData");
const [, setCurrentUserRoles] = useGlobal("currentUserRoles");
const [appIsReady, setAppIsReady] = useState(false);
diff --git a/functions/index.js b/functions/index.js
index 134d496..b75108c 100644
--- a/functions/index.js
+++ b/functions/index.js
@@ -8,6 +8,13 @@ exports.refList = {
projects: admin.firestore().collection("projects"),
playlists: admin.firestore().collection("playlists"),
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
@@ -19,3 +26,4 @@ exports.projects = require("./src/project");
exports.thumbnail = require("./src/thumbnail");
exports.upload = require("./src/upload");
exports.algolia = require("./src/algolia");
+exports.notifications = require("./src/notifications");
diff --git a/functions/package-lock.json b/functions/package-lock.json
index 41f986c..dcc95c5 100644
--- a/functions/package-lock.json
+++ b/functions/package-lock.json
@@ -10,6 +10,7 @@
"@genkit-ai/googleai": "^1.18.0",
"@google-cloud/storage": "^7.17.1",
"axios": "^1.11.0",
+ "expo-server-sdk": "^4.0.0",
"ffmpeg-static": "^5.2.0",
"firebase-admin": "^12.7.0",
"firebase-functions": "^6.4.0",
@@ -3416,6 +3417,12 @@
"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": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/error/-/error-7.0.2.tgz",
@@ -3703,6 +3710,40 @@
"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": {
"version": "4.21.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
@@ -5707,6 +5748,34 @@
"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": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz",
diff --git a/functions/package.json b/functions/package.json
index b490321..03fe9fb 100644
--- a/functions/package.json
+++ b/functions/package.json
@@ -18,6 +18,7 @@
"@genkit-ai/googleai": "^1.18.0",
"@google-cloud/storage": "^7.17.1",
"axios": "^1.11.0",
+ "expo-server-sdk": "^4.0.0",
"ffmpeg-static": "^5.2.0",
"firebase-admin": "^12.7.0",
"firebase-functions": "^6.4.0",
diff --git a/functions/src/notifications.js b/functions/src/notifications.js
new file mode 100644
index 0000000..5e80aff
--- /dev/null
+++ b/functions/src/notifications.js
@@ -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;
+ }
+ }
+);
diff --git a/package.json b/package.json
index 13d5735..481f120 100644
--- a/package.json
+++ b/package.json
@@ -54,6 +54,7 @@
"expo-linear-gradient": "~14.0.2",
"expo-linking": "~7.0.5",
"expo-location": "~18.0.10",
+ "expo-notifications": "~0.29.14",
"expo-speech": "~13.0.1",
"expo-splash-screen": "~0.29.24",
"expo-status-bar": "~2.0.1",
diff --git a/src/providers/NotificationProvider.js b/src/providers/NotificationProvider.js
new file mode 100644
index 0000000..a91ecf5
--- /dev/null
+++ b/src/providers/NotificationProvider.js
@@ -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 (
+
+ {children}
+
+ );
+}
diff --git a/src/providers/SharedProviders.js b/src/providers/SharedProviders.js
index dad50dd..e6c57e3 100644
--- a/src/providers/SharedProviders.js
+++ b/src/providers/SharedProviders.js
@@ -4,6 +4,7 @@ import React from "react";
import { SafeAreaProvider } from "react-native-safe-area-context";
import { SheetProvider } from "react-native-actions-sheet";
+import NotificationProvider from "./NotificationProvider";
import PlayerProvider from "./PlayerProvider";
import SplashAnimationProvider from "./SplashAnimationProvider";
import StripeEmbeddedProvider from "./StripeEmbeddedProvider";
@@ -23,6 +24,7 @@ const SharedProviders = ({ children }) => {
[BottomSheetModalProvider, {}],
[SheetProvider, {}],
[PlayerProvider, {}],
+ [NotificationProvider, {}],
];
// Dynamically nest the providers using reduce
diff --git a/src/screens/Playbacks/Playbacks.js b/src/screens/Playbacks/Playbacks.js
index 18f7500..33d7b57 100644
--- a/src/screens/Playbacks/Playbacks.js
+++ b/src/screens/Playbacks/Playbacks.js
@@ -100,6 +100,7 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
// Follow state derived from owner.followedBy
const [isFollowing, setIsFollowing] = useState(false);
+ const [isFollowActionPending, setIsFollowActionPending] = useState(false);
useEffect(() => {
const list = Array.isArray(owner?.followedBy) ? owner.followedBy : [];
setIsFollowing(currentUID ? list.includes(currentUID) : false);
@@ -278,9 +279,14 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
{owner?.id && currentUID && owner.id !== currentUID && (
{
+ if (isFollowActionPending) return;
+
try {
+ setIsFollowActionPending(true);
const next = !isFollowing;
+ console.log("follwin");
setIsFollowing(next);
// Optimistic update of local owner.followedBy
setOwner((prev) => {
@@ -294,9 +300,15 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
});
if (next) await followUser?.(owner.id);
else await unfollowUser?.(owner.id);
+
+ // Timeout de 2 secondes avant de pouvoir recliquer
+ global.setTimeout(() => {
+ setIsFollowActionPending(false);
+ }, 1000);
} catch (e) {
// rollback on failure
setIsFollowing((v) => !v);
+ setIsFollowActionPending(false);
}
}}
>
diff --git a/yarn.lock b/yarn.lock
index a277b00..5241160 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1898,6 +1898,11 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba"
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":
version "8.0.2"
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"
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.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-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.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"
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"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c"
integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==
@@ -5233,6 +5254,19 @@ expo-modules-core@2.2.3:
dependencies:
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:
version "13.0.1"
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"
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:
version "1.0.0-beta.2"
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"
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"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
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"
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:
version "3.0.5"
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"
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:
version "3.0.0"
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"
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:
version "4.0.1"
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"
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"
resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.15.tgz#4bfb4a45b61cee83a5a46fba778e4e8d59c0ce0b"
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"
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:
version "1.1.1"
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"
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:
version "1.0.1"
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-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"
resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956"
integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==