diff --git a/.gitignore b/.gitignore index b37a5e87..9a5e0cd0 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,5 @@ yalc.lock out.log out.txt /.yarn/ + +.env \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100755 index 1dd11286..00000000 --- a/.husky/pre-commit +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - -echo "Running prettier..." -yarn run fix:prettier . && git add -A . - -exit $? diff --git a/App.tsx b/App.tsx index 056f01a1..47d4c29e 100644 --- a/App.tsx +++ b/App.tsx @@ -24,6 +24,8 @@ import {getUsers} from './src/store/actions/user.actions'; import {PlatformsEnum} from './src/types'; import {GestureHandlerRootView} from 'react-native-gesture-handler'; import './src/agent/index'; +import {ChatProvider} from './src/providers/chat/chatProvider'; +import {AssistantProvider} from './src/providers/chat/AssistantProvider'; LogBox.ignoreLogs([ // Ignore require cycles for the app in dev mode. They do show up in Metro! @@ -118,9 +120,13 @@ export default function App() { setNavigationIsReady(true)} ref={navigationRef}> - - - + + + + + + + diff --git a/android/app/build.gradle b/android/app/build.gradle index c334c1d2..dfc55ea7 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -92,8 +92,8 @@ android { applicationId "com.sphereon.ssi.wallet" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 38 - versionName "0.4.9" + versionCode 39 + versionName "0.4.10" missingDimensionStrategy 'react-native-camera', 'general' manifestPlaceholders = [ appAuthRedirectScheme: "com.sphereon.ssi.wallet" diff --git a/ios/Podfile b/ios/Podfile index ce6c5a9a..25dc750f 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -41,7 +41,7 @@ setup_permissions([ # 'LocationAlways', # 'LocationWhenInUse', # 'MediaLibrary', - # 'Microphone', + 'Microphone', # 'Motion', 'Notifications', # 'PhotoLibrary', diff --git a/ios/Podfile.lock b/ios/Podfile.lock index e8cdaabf..aaad5086 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1086,6 +1086,8 @@ PODS: - Yoga - react-native-fingerprint-scanner (6.0.0): - React + - react-native-get-random-values (1.11.0): + - React-Core - react-native-linear-gradient-text (1.2.8): - DoubleConversion - glog @@ -1412,12 +1414,18 @@ PODS: - React-logger (= 0.74.3) - React-perflogger (= 0.74.3) - React-utils (= 0.74.3) + - RNAudioRecord (0.2.2): + - React + - RNAudioRecorderPlayer (3.6.12): + - React-Core - RNCMaskedView (0.3.1): - React-Core - RNFastImage (8.6.3): - React-Core - SDWebImage (~> 5.11.1) - SDWebImageWebPCoder (~> 0.8.4) + - RNFS (2.20.0): + - React-Core - RNGestureHandler (2.16.2): - DoubleConversion - glog @@ -1492,6 +1500,11 @@ PODS: - React-Core - RNShareMenu (6.0.0): - React + - RNSound (0.11.2): + - React-Core + - RNSound/Core (= 0.11.2) + - RNSound/Core (0.11.2): + - React-Core - RNSVG (15.2.0): - React-Core - SDWebImage (5.11.1): @@ -1563,6 +1576,7 @@ DEPENDENCIES: - react-native-app-auth (from `../node_modules/react-native-app-auth`) - "react-native-blur (from `../node_modules/@react-native-community/blur`)" - react-native-fingerprint-scanner (from `../node_modules/react-native-fingerprint-scanner`) + - react-native-get-random-values (from `../node_modules/react-native-get-random-values`) - react-native-linear-gradient-text (from `../node_modules/react-native-linear-gradient-text`) - react-native-mmkv-storage (from `../node_modules/react-native-mmkv-storage`) - react-native-pager-view (from `../node_modules/react-native-pager-view`) @@ -1594,8 +1608,11 @@ DEPENDENCIES: - React-runtimescheduler (from `../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`) - React-utils (from `../node_modules/react-native/ReactCommon/react/utils`) - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) + - RNAudioRecord (from `../node_modules/react-native-audio-record`) + - RNAudioRecorderPlayer (from `../node_modules/react-native-audio-recorder-player`) - "RNCMaskedView (from `../node_modules/@react-native-masked-view/masked-view`)" - RNFastImage (from `../node_modules/react-native-fast-image`) + - RNFS (from `../node_modules/react-native-fs`) - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - RNOS (from `../node_modules/react-native-os`) - RNPermissions (from `../node_modules/react-native-permissions`) @@ -1604,6 +1621,7 @@ DEPENDENCIES: - RNSecureRandom (from `../node_modules/react-native-securerandom`) - RNShare (from `../node_modules/react-native-share`) - RNShareMenu (from `../node_modules/react-native-share-menu`) + - RNSound (from `../node_modules/react-native-sound`) - RNSVG (from `../node_modules/react-native-svg`) - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) - YubiKit @@ -1730,6 +1748,8 @@ EXTERNAL SOURCES: :path: "../node_modules/@react-native-community/blur" react-native-fingerprint-scanner: :path: "../node_modules/react-native-fingerprint-scanner" + react-native-get-random-values: + :path: "../node_modules/react-native-get-random-values" react-native-linear-gradient-text: :path: "../node_modules/react-native-linear-gradient-text" react-native-mmkv-storage: @@ -1792,10 +1812,16 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/react/utils" ReactCommon: :path: "../node_modules/react-native/ReactCommon" + RNAudioRecord: + :path: "../node_modules/react-native-audio-record" + RNAudioRecorderPlayer: + :path: "../node_modules/react-native-audio-recorder-player" RNCMaskedView: :path: "../node_modules/@react-native-masked-view/masked-view" RNFastImage: :path: "../node_modules/react-native-fast-image" + RNFS: + :path: "../node_modules/react-native-fs" RNGestureHandler: :path: "../node_modules/react-native-gesture-handler" RNOS: @@ -1812,6 +1838,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-share" RNShareMenu: :path: "../node_modules/react-native-share-menu" + RNSound: + :path: "../node_modules/react-native-sound" RNSVG: :path: "../node_modules/react-native-svg" Yoga: @@ -1877,6 +1905,7 @@ SPEC CHECKSUMS: react-native-app-auth: fd1eaa667c0bc014199456d14a6440cb74de814e react-native-blur: 30d91a67da86a4d4d924b0c7c36f6e01479a246b react-native-fingerprint-scanner: ac6656f18c8e45a7459302b84da41a44ad96dbbe + react-native-get-random-values: 21325b2244dfa6b58878f51f9aa42821e7ba3d06 react-native-linear-gradient-text: e5c4b82093a7c9858dc3ffc059a10be0ff8abf2d react-native-mmkv-storage: 36fcafe92a2c4cf19bce96be0b5d587f7a62a2c6 react-native-pager-view: c1e29e1a6105a02807392ba822ad322447a72f55 @@ -1908,21 +1937,25 @@ SPEC CHECKSUMS: React-runtimescheduler: 0c80752bceb80924cb8a4babc2a8e3ed70d41e87 React-utils: a06061b3887c702235d2dac92dacbd93e1ea079e ReactCommon: f00e436b3925a7ae44dfa294b43ef360fbd8ccc4 + RNAudioRecord: 162fee3b8fb628773ec704bf0333823125be2ed7 + RNAudioRecorderPlayer: 224c7de87722938aedce04000d09baa633148f5b RNCMaskedView: 090213d32d8b3bb83a4dcb7d12c18f0152591906 RNFastImage: 5c9c9fed9c076e521b3f509fe79e790418a544e8 + RNFS: 4ac0f0ea233904cb798630b3c077808c06931688 RNGestureHandler: 2282cfbcf86c360d29f44ace393203afd5c6cff7 RNOS: 6f2f9a70895bbbfbdad7196abd952e7b01d45027 - RNPermissions: 2d80aab8cf15a2dcf5be697552bfdd6409c3e3d3 + RNPermissions: bab3c7941037bbcda872e336870fa65f98d040ab RNReanimated: 35f9ac9c3ac42d0497ebd1cce5c39d7687a8493e RNScreens: b32a9ff15bea7fcdbe5dff6477bc503f792b1208 RNSecureRandom: 07efbdf2cd99efe13497433668e54acd7df49fef RNShare: 0fad69ae2d71de9d1f7b9a43acf876886a6cb99c RNShareMenu: cb9dac548c8bf147d06f0bf07296ad51ea9f5fc3 + RNSound: 6c156f925295bdc83e8e422e7d8b38d33bc71852 RNSVG: 43b64ed39c14ce830d840903774154ca0c1f27ec SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d - Yoga: 88480008ccacea6301ff7bf58726e27a72931c8d + Yoga: 04f1db30bb810187397fa4c37dd1868a27af229c YubiKit: 390e7733942f9518f9b3f5890c60914f7b8d74c8 ZXingObjC: 8898711ab495761b2dbbdec76d90164a6d7e14c5 diff --git a/ios/SphereonWallet.xcodeproj/project.pbxproj b/ios/SphereonWallet.xcodeproj/project.pbxproj index 625d1a19..68623f20 100644 --- a/ios/SphereonWallet.xcodeproj/project.pbxproj +++ b/ios/SphereonWallet.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ 4EE59167281F4208A3BC1FAF /* Poppins-ExtraBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = FD4AE482C461467BB0F43D71 /* Poppins-ExtraBold.ttf */; }; 551AA2A5387640F2A4CCC787 /* Poppins-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D6BDFE0FD163426C8636F341 /* Poppins-Medium.ttf */; }; 64B361F617414351BF16EF44 /* Poppins-Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 761410D9D96C4DE3922872B1 /* Poppins-Italic.ttf */; }; + 73F38FBF253F87BF90CAD713 /* libPods-SphereonWallet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 772BDAED61E678305A8EECC5 /* libPods-SphereonWallet.a */; }; 914258F1530BA52391F703D0 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 46F0421A11A5981FF2784C6B /* PrivacyInfo.xcprivacy */; }; 96DC2693AA5B4C7F864F6F88 /* Poppins-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1CC149FA7F02428A9036F996 /* Poppins-Bold.ttf */; }; 9AC3A6B54D3542B6A0144797 /* Poppins-BlackItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0D7C476C2ABA459E86A44736 /* Poppins-BlackItalic.ttf */; }; @@ -30,7 +31,6 @@ CE59D921DD7844F4AA9943E3 /* Poppins-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = FDC004113EFB4F998C415868 /* Poppins-SemiBold.ttf */; }; D2987F3EE8944F0C928F3EDD /* Poppins-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 46404C6F56FA466B9D06B883 /* Poppins-Light.ttf */; }; D2F63BEDFEFD473D9C3A98D3 /* Poppins-MediumItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 87B1C56729164D2389F11C59 /* Poppins-MediumItalic.ttf */; }; - D4C40AD6D506B7DCF7155247 /* libPods-SphereonWallet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7DC93FAB7BDA7BF1EBBAFB5A /* libPods-SphereonWallet.a */; }; E365D3F86ECD4DA8836A484E /* Poppins-ExtraLight.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 21458FEC64BF492DB6D67A51 /* Poppins-ExtraLight.ttf */; }; EEEF7E5E8E854D3CA6A0280C /* Poppins-LightItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 4686824566EC48CDB402A94F /* Poppins-LightItalic.ttf */; }; F932577EF9A841469AFF3D14 /* Poppins-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0D6D6E89FD324595B1F599A8 /* Poppins-Regular.ttf */; }; @@ -39,7 +39,6 @@ /* Begin PBXFileReference section */ 0D6D6E89FD324595B1F599A8 /* Poppins-Regular.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Poppins-Regular.ttf"; path = "../src/assets/fonts/Poppins-Regular.ttf"; sourceTree = ""; }; 0D7C476C2ABA459E86A44736 /* Poppins-BlackItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Poppins-BlackItalic.ttf"; path = "../src/assets/fonts/Poppins-BlackItalic.ttf"; sourceTree = ""; }; - 0F902BB8D8D2EB9E7C415C7E /* Pods-SphereonWallet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SphereonWallet.debug.xcconfig"; path = "Target Support Files/Pods-SphereonWallet/Pods-SphereonWallet.debug.xcconfig"; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* SphereonWallet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SphereonWallet.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = SphereonWallet/AppDelegate.h; sourceTree = ""; }; 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = SphereonWallet/AppDelegate.mm; sourceTree = ""; }; @@ -47,6 +46,7 @@ 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = SphereonWallet/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = SphereonWallet/main.m; sourceTree = ""; }; 14E6050A725749199DD4E393 /* Poppins-ThinItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Poppins-ThinItalic.ttf"; path = "../src/assets/fonts/Poppins-ThinItalic.ttf"; sourceTree = ""; }; + 176B704FB23D8B3740C3DD96 /* Pods-SphereonWallet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SphereonWallet.debug.xcconfig"; path = "Target Support Files/Pods-SphereonWallet/Pods-SphereonWallet.debug.xcconfig"; sourceTree = ""; }; 1CC149FA7F02428A9036F996 /* Poppins-Bold.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Poppins-Bold.ttf"; path = "../src/assets/fonts/Poppins-Bold.ttf"; sourceTree = ""; }; 1F6EB0CA2A576BE90077011A /* node_modules */ = {isa = PBXFileReference; lastKnownFileType = folder; name = node_modules; path = ../node_modules; sourceTree = ""; }; 1F6EB0CC2A576C670077011A /* libReact-RCTLinking.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libReact-RCTLinking.a"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -59,16 +59,16 @@ 46404C6F56FA466B9D06B883 /* Poppins-Light.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Poppins-Light.ttf"; path = "../src/assets/fonts/Poppins-Light.ttf"; sourceTree = ""; }; 4686824566EC48CDB402A94F /* Poppins-LightItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Poppins-LightItalic.ttf"; path = "../src/assets/fonts/Poppins-LightItalic.ttf"; sourceTree = ""; }; 46F0421A11A5981FF2784C6B /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = SphereonWallet/PrivacyInfo.xcprivacy; sourceTree = ""; }; - 497B8C8555EA4F08425EE1FE /* Pods-SphereonWallet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SphereonWallet.release.xcconfig"; path = "Target Support Files/Pods-SphereonWallet/Pods-SphereonWallet.release.xcconfig"; sourceTree = ""; }; 5674539E296F49AB8BD1A1DA /* Poppins-SemiBoldItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Poppins-SemiBoldItalic.ttf"; path = "../src/assets/fonts/Poppins-SemiBoldItalic.ttf"; sourceTree = ""; }; 56CBB080799F4E3EB87DEC65 /* noop-file.swift */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.swift; name = "noop-file.swift"; path = "SphereonWallet/noop-file.swift"; sourceTree = ""; }; 5C51657A0F7547228C3E57BC /* Poppins-BoldItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Poppins-BoldItalic.ttf"; path = "../src/assets/fonts/Poppins-BoldItalic.ttf"; sourceTree = ""; }; 761410D9D96C4DE3922872B1 /* Poppins-Italic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Poppins-Italic.ttf"; path = "../src/assets/fonts/Poppins-Italic.ttf"; sourceTree = ""; }; - 7DC93FAB7BDA7BF1EBBAFB5A /* libPods-SphereonWallet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SphereonWallet.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 772BDAED61E678305A8EECC5 /* libPods-SphereonWallet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SphereonWallet.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 87B1C56729164D2389F11C59 /* Poppins-MediumItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Poppins-MediumItalic.ttf"; path = "../src/assets/fonts/Poppins-MediumItalic.ttf"; sourceTree = ""; }; 8FD2B11C8F05446499AEB7E0 /* Poppins-ExtraLightItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Poppins-ExtraLightItalic.ttf"; path = "../src/assets/fonts/Poppins-ExtraLightItalic.ttf"; sourceTree = ""; }; AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = SphereonWallet/SplashScreen.storyboard; sourceTree = ""; }; BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = ""; }; + BB8941CFDB966E298859B990 /* Pods-SphereonWallet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SphereonWallet.release.xcconfig"; path = "Target Support Files/Pods-SphereonWallet/Pods-SphereonWallet.release.xcconfig"; sourceTree = ""; }; D6BDFE0FD163426C8636F341 /* Poppins-Medium.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Poppins-Medium.ttf"; path = "../src/assets/fonts/Poppins-Medium.ttf"; sourceTree = ""; }; E563FB7DD4D041CD99944C2B /* Poppins-Black.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Poppins-Black.ttf"; path = "../src/assets/fonts/Poppins-Black.ttf"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; @@ -82,7 +82,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D4C40AD6D506B7DCF7155247 /* libPods-SphereonWallet.a in Frameworks */, + 73F38FBF253F87BF90CAD713 /* libPods-SphereonWallet.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -114,7 +114,7 @@ 1F6EB0CC2A576C670077011A /* libReact-RCTLinking.a */, 1F6EB0CA2A576BE90077011A /* node_modules */, ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - 7DC93FAB7BDA7BF1EBBAFB5A /* libPods-SphereonWallet.a */, + 772BDAED61E678305A8EECC5 /* libPods-SphereonWallet.a */, ); name = Frameworks; sourceTree = ""; @@ -170,8 +170,8 @@ D65327D7A22EEC0BE12398D9 /* Pods */ = { isa = PBXGroup; children = ( - 0F902BB8D8D2EB9E7C415C7E /* Pods-SphereonWallet.debug.xcconfig */, - 497B8C8555EA4F08425EE1FE /* Pods-SphereonWallet.release.xcconfig */, + 176B704FB23D8B3740C3DD96 /* Pods-SphereonWallet.debug.xcconfig */, + BB8941CFDB966E298859B990 /* Pods-SphereonWallet.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -216,14 +216,14 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "SphereonWallet" */; buildPhases = ( - C0D0F7B249112B7A5D4A0754 /* [CP] Check Pods Manifest.lock */, + 96401FC55382C060F43DF09D /* [CP] Check Pods Manifest.lock */, 0E87B95D25E799A23DB4C374 /* [Expo] Configure project */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - 8026BD442BD02408DCDDD6F8 /* [CP] Embed Pods Frameworks */, - 9CAD27000EEC80F960BD416B /* [CP] Copy Pods Resources */, + DCF90591B0773973A06C5FCB /* [CP] Embed Pods Frameworks */, + 832EE0C992C5091A8D4CC963 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -332,24 +332,7 @@ shellPath = /bin/sh; shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-SphereonWallet/expo-configure-project.sh\"\n"; }; - 8026BD442BD02408DCDDD6F8 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-SphereonWallet/Pods-SphereonWallet-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-SphereonWallet/Pods-SphereonWallet-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SphereonWallet/Pods-SphereonWallet-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 9CAD27000EEC80F960BD416B /* [CP] Copy Pods Resources */ = { + 832EE0C992C5091A8D4CC963 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -366,7 +349,7 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SphereonWallet/Pods-SphereonWallet-resources.sh\"\n"; showEnvVarsInLog = 0; }; - C0D0F7B249112B7A5D4A0754 /* [CP] Check Pods Manifest.lock */ = { + 96401FC55382C060F43DF09D /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -388,6 +371,23 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + DCF90591B0773973A06C5FCB /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-SphereonWallet/Pods-SphereonWallet-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-SphereonWallet/Pods-SphereonWallet-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SphereonWallet/Pods-SphereonWallet-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -407,7 +407,7 @@ /* Begin XCBuildConfiguration section */ 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0F902BB8D8D2EB9E7C415C7E /* Pods-SphereonWallet.debug.xcconfig */; + baseConfigurationReference = 176B704FB23D8B3740C3DD96 /* Pods-SphereonWallet.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; @@ -539,7 +539,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 497B8C8555EA4F08425EE1FE /* Pods-SphereonWallet.release.xcconfig */; + baseConfigurationReference = BB8941CFDB966E298859B990 /* Pods-SphereonWallet.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; diff --git a/package.json b/package.json index bef8dea3..7129ece6 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "build:android:production": "eas build -p android --profile production", "bundle:ios": "react-native bundle --entry-file index.js --platform ios --dev false --bundle-output ios/main.jsbundle --assets-dest ios", "postinstall": "patch-package && rn-nodeify --install fs,os,path,assert,process,vm --hack", - "prepare": "husky install", "android": "expo run:android" }, "dependencies": { @@ -32,6 +31,7 @@ "@craftzdog/react-native-buffer": "^6.0.5", "@digitalbazaar/security-context": "^1.0.1", "@ethersproject/shims": "^5.7.0", + "@openai/realtime-api-beta": "github:openai/openai-realtime-api-beta", "@pakenfit/react-native-pin-input": "^1.4.0", "@react-native-community/blur": "^4.4.0", "@react-native-community/hooks": "^3.0.0", @@ -93,6 +93,7 @@ "assert": "^1.1.1", "buffer": "^4.9.1", "countries-list": "^3.1.1", + "base-64": "^1.0.0", "debug": "^4.3.5", "did-jwt": "6.11.6", "did-resolver": "^4.1.0", @@ -124,9 +125,14 @@ "react-is": "^18.2.0", "react-native": "0.74.3", "react-native-app-auth": "^6.4.3", + "react-native-audio-record": "^0.2.2", + "react-native-audio-recorder-player": "^3.6.12", "react-native-fast-image": "^8.6.3", "react-native-fingerprint-scanner": "git+https://github.com/hieuvp/react-native-fingerprint-scanner.git", + "react-native-fs": "^2.20.0", "react-native-gesture-handler": "~2.16.1", + "react-native-get-random-values": "^1.11.0", + "react-native-gifted-chat": "^2.6.4", "react-native-gradle-plugin": "^0.71.19", "react-native-json-tree": "^1.3.0", "react-native-level-fs": "^3.0.1", @@ -139,7 +145,7 @@ "react-native-pager-view": "6.3.0", "react-native-permissions": "^4.1.5", "react-native-pure-jwt": "^3.0.2", - "react-native-quick-sqlite": "github:margelo/react-native-quick-sqlite#99f34ebefa91698945f3ed26622e002bd79489e0", + "react-native-quick-sqlite": "github:margelo/react-native-nitro-sqlite#99f34ebefa91698945f3ed26622e002bd79489e0", "react-native-randombytes": "^3.6.1", "react-native-reanimated": "~3.10.1", "react-native-safe-area-context": "4.10.5", @@ -148,6 +154,7 @@ "react-native-share": "^10.2.1", "react-native-share-menu": "^6.0.0", "react-native-size-matters": "^0.4.2", + "react-native-sound": "^0.11.2", "react-native-svg": "15.2.0", "react-native-svg-transformer": "^1.5.0", "react-native-swipe-list-view": "^3.2.9", @@ -166,6 +173,7 @@ "uint8arrays": "^3.1.1", "vm-browserify": "^1.1.2", "web-did-resolver": "^2.0.23", + "ws": "^8.18.0", "xstate": "^4.38.3" }, "devDependencies": { @@ -189,10 +197,13 @@ "@types/jest": "^29.2.1", "@types/lodash.memoize": "^4.1.6", "@types/react": "~18.2.79", + "@types/react-native-dotenv": "^0.2.2", "@types/react-native-share-menu": "^5.0.2", "@types/react-redux": "^7.1.18", "@types/react-test-renderer": "^18.0.0", "@types/uuid": "^9.0.6", + "@types/ws": "^8.5.12", + "@types/base-64": "^1.0.2", "@typescript-eslint/eslint-plugin": "^5.12.0", "@typescript-eslint/parser": "^5.12.0", "babel-jest": "^29.7.0", @@ -203,7 +214,6 @@ "eslint-config-prettier": "^8.3.0", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-import": "^2.25.4", - "husky": "^8.0.3", "jest": "^29.2.1", "jest-expo": "~51.0.3", "metro": "~0.80.8", diff --git a/src/@types/react-native/index.d.ts b/src/@types/react-native/index.d.ts index 26646e55..7e87b57e 100644 --- a/src/@types/react-native/index.d.ts +++ b/src/@types/react-native/index.d.ts @@ -2,3 +2,8 @@ declare module 'react-native/Libraries/Utilities/__mocks__/BackHandler' { const anyValue: any; export default anyValue; } + +declare module 'react-native-dotenv' { + export const DEBUG: string; + export const OPENAI_API_KEY: string; +} diff --git a/src/components/chat/Chat/index.tsx b/src/components/chat/Chat/index.tsx new file mode 100644 index 00000000..0c8514e2 --- /dev/null +++ b/src/components/chat/Chat/index.tsx @@ -0,0 +1,126 @@ +import {useChat} from '../../../providers/chat/chatProvider'; +import {useAssistant} from '../../../providers/chat/AssistantProvider'; +import ChatButton, {ChatButtonPosition} from '../ChatButton'; +import ChatModal from '../ChatModal'; +import {useEffect, useMemo} from 'react'; +import {ToolDefinitionType} from '@openai/realtime-api-beta/dist/lib/client'; +import {IMessage} from 'react-native-gifted-chat'; +import {reopenChatPrompt} from '../../../instructions'; + +export type ChatTools = {tool: ToolDefinitionType; callback: (args: unknown) => void}[]; + +type Props = { + buttonPosition?: ChatButtonPosition; + screenContext?: string; + tools?: ChatTools; +}; + +export const Chat = ({buttonPosition, screenContext, tools}: Props) => { + const {openModal, setMessages} = useChat(); + const { + isVoiceRecording, + startVoiceRecording, + endVoiceRecording, + enableVoiceMode, + enableTextMode, + chatMode, + handleChatOpened, + sendPrompt, + items, + addTool, + removeTool, + speakMessage, + } = useAssistant(); + + useEffect(() => { + tools?.forEach(({tool, callback}) => { + addTool(tool, callback); + }); + return () => { + tools?.forEach(({tool}) => { + removeTool(tool.name); + }); + }; + }, [tools]); + + useEffect(() => { + const updatedMessages = items + .map(item => { + let messageText = ''; + + if (item.type === 'function_call_output') return; + + // Check if it's a tool message + if (item.formatted.tool) { + messageText = `${item.formatted.tool.name} (${item.formatted.tool.arguments})`; + } + // Check if it's a user message without a tool + else if (item.role === 'user') { + if (item.formatted.transcript) { + messageText = item.formatted.transcript; + } else if (item.formatted.audio?.length) { + messageText = '(awaiting transcript)'; + } else { + messageText = item.formatted.text || '(item sent)'; + } + if (messageText === reopenChatPrompt) { + // hacky way of hiding the reopen chat prompt + return null; + } + } else if (item.role === 'assistant') { + if (item.formatted.transcript) { + messageText = item.formatted.transcript; + } else { + messageText = item.formatted.text || '(truncated)'; + } + } + + return { + _id: item.id, + text: messageText, + createdAt: new Date(), + user: {_id: item.role === 'user' ? 1 : 2}, // _id 1 for user and 2 for assistant + }; + }) + .filter(item => !!item); + setMessages(updatedMessages); + }, [items]); + + const handleSendMessage = (message: string) => { + sendPrompt(message, screenContext); + }; + + const handleTextPress = () => { + enableTextMode(); + openModal(); + handleChatOpened(screenContext); + }; + + const handleVoicePress = () => { + // if (chatMode === 'voice') { + // if (isVoiceRecording) { + // endVoiceRecording(); + // } else { + // startVoiceRecording(); + // } + // } else { + // enableVoiceMode(); + // } + }; + + const handleAudioPress = (message: IMessage) => { + console.log('handleAudioPress', message); + const item = items.find(({id}) => id === message._id); + if (item?.formatted.audio) { + console.log('Playing audio', item.formatted.audio.length); + speakMessage(item?.formatted.audio); + } + }; + + return ( + <> + + + + ); +}; diff --git a/src/components/chat/ChatBubble/index.tsx b/src/components/chat/ChatBubble/index.tsx new file mode 100644 index 00000000..2249ea22 --- /dev/null +++ b/src/components/chat/ChatBubble/index.tsx @@ -0,0 +1,133 @@ +import {Ionicons} from '@expo/vector-icons'; +import {backgroundColors, fontColors} from '@sphereon/ui-components.core'; +import {LinearGradient} from 'expo-linear-gradient'; +import {StyleSheet, TouchableOpacity, View, ViewProps} from 'react-native'; +import {BubbleProps, IMessage, isSameDay, isSameUser, MessageText} from 'react-native-gifted-chat'; + +export const ChatBubble = (props: BubbleProps & {handleAudioPress?: () => void}) => { + const {currentMessage, position, nextMessage, containerToNextStyle, previousMessage, containerToPreviousStyle, handleAudioPress} = props; + const styles = { + left: StyleSheet.create({ + container: { + flex: 1, + alignItems: 'flex-start', + marginBottom: 10, + }, + wrapper: { + borderRadius: 0, + + marginRight: 0, + minHeight: 20, + justifyContent: 'flex-end', + padding: 0, + }, + containerToNext: { + borderBottomLeftRadius: 3, + }, + containerToPrevious: { + borderTopLeftRadius: 3, + }, + bottom: { + flexDirection: 'row', + justifyContent: 'flex-start', + }, + }), + right: StyleSheet.create({ + container: { + flex: 1, + alignItems: 'flex-end', + marginBottom: 10, + }, + wrapper: { + borderRadius: 8, + borderTopRightRadius: 2, + marginLeft: 0, + minHeight: 20, + justifyContent: 'flex-end', + backgroundColor: backgroundColors.primaryDark, + }, + containerToNext: { + borderBottomRightRadius: 3, + }, + containerToPrevious: { + borderTopRightRadius: 3, + }, + bottom: { + flexDirection: 'row', + justifyContent: 'flex-end', + }, + }), + content: StyleSheet.create({ + tick: { + fontSize: 10, + + color: 'pink', + }, + tickView: { + flexDirection: 'row', + marginRight: 10, + }, + username: { + top: -3, + left: 0, + fontSize: 12, + color: '#aaa', + }, + usernameView: { + flexDirection: 'row', + marginHorizontal: 10, + }, + }), + text: StyleSheet.create({ + left: { + fontSize: 16, + lineHeight: 20, + marginTop: 0, + marginBottom: 0, + marginLeft: 0, + marginRight: 0, + color: fontColors.dark, + }, + right: { + fontSize: 16, + lineHeight: 20, + marginTop: 8, + marginBottom: 8, + marginLeft: 4, + marginRight: 4, + color: fontColors.light, + }, + }), + }; + + function styledBubbleToNext() { + if (currentMessage && nextMessage && position && isSameUser(currentMessage, nextMessage) && isSameDay(currentMessage, nextMessage)) + return [styles[position].containerToNext, containerToNextStyle?.[position]]; + + return null; + } + + function styledBubbleToPrevious() { + if (currentMessage && previousMessage && position && isSameUser(currentMessage, previousMessage) && isSameDay(currentMessage, previousMessage)) + return [styles[position].containerToPrevious, containerToPreviousStyle && containerToPreviousStyle[position]]; + + return null; + } + + const Gradient = (props: ViewProps) => ; + + const WrapperComponent = position === 'left' ? View : Gradient; + + return ( + + + + {position === 'left' && ( + handleAudioPress && handleAudioPress()}> + + + )} + + + ); +}; diff --git a/src/components/chat/ChatButton/index.tsx b/src/components/chat/ChatButton/index.tsx new file mode 100644 index 00000000..b8300b45 --- /dev/null +++ b/src/components/chat/ChatButton/index.tsx @@ -0,0 +1,78 @@ +import {Ionicons} from '@expo/vector-icons'; +import {fontColors} from '@sphereon/ui-components.core'; +import React, {useEffect} from 'react'; +import {TouchableOpacity, View, ViewStyle} from 'react-native'; +import {useChat} from '../../../providers/chat/chatProvider'; +import {useAssistant} from '../../../providers/chat/AssistantProvider'; +import {LinearGradient} from 'expo-linear-gradient'; + +export type ChatButtonPosition = { + bottom: number; + right: number; +}; + +type Props = { + style?: ViewStyle; + position?: ChatButtonPosition; + onVoicePress: () => void; + onTextPress: () => void; +}; + +const ChatButton = ({style, position, onVoicePress, onTextPress}: Props) => { + const {isVoiceRecording, chatMode} = useAssistant(); + + return ( + + { + onTextPress(); + }}> + + + { + onVoicePress(); + }}> + {isVoiceRecording && ( + + )} + + + + ); +}; + +export default ChatButton; diff --git a/src/components/chat/ChatInputToolbar/index.tsx b/src/components/chat/ChatInputToolbar/index.tsx new file mode 100644 index 00000000..cee1f10e --- /dev/null +++ b/src/components/chat/ChatInputToolbar/index.tsx @@ -0,0 +1,86 @@ +import {Ionicons} from '@expo/vector-icons'; +import {useNavigation} from '@react-navigation/native'; +import {fontColors} from '@sphereon/ui-components.core'; +import React, {useEffect, useState} from 'react'; +import {Keyboard, TextInput, TouchableOpacity, View} from 'react-native'; +import {verticalScale} from 'react-native-size-matters'; +import {useChat} from '../../../providers/chat/chatProvider'; +import {useAssistant} from '../../../providers/chat/AssistantProvider'; + +const ChatInputToolbar = (props: any) => { + const {closeModal} = useChat(); + const {enableVoiceMode} = useAssistant(); + const navigation = useNavigation(); + + const [keyboardVisible, setKeyboardVisible] = useState(false); + const [inputValue, setInputValue] = useState(''); + + useEffect(() => { + const keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', () => { + setKeyboardVisible(true); + }); + const keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', () => { + setKeyboardVisible(false); + }); + + return () => { + keyboardDidShowListener.remove(); + keyboardDidHideListener.remove(); + }; + }, []); + + const handleVoicePress = () => { + console.log('Voice Pressed'); + enableVoiceMode(); + closeModal(); + }; + + return ( + + navigation.goBack()} /> + + setInputValue(text)} + /> + {!keyboardVisible && ( + { + handleVoicePress(); + }}> + + + )} + {keyboardVisible && ( + { + // Logic to send the message + props.onSend([{text: inputValue}]); + setInputValue(''); // Clear the input field after sending + }} + disabled={!inputValue.trim()}> + + + )} + + ); +}; + +export default ChatInputToolbar; diff --git a/src/components/chat/ChatModal/index.tsx b/src/components/chat/ChatModal/index.tsx new file mode 100644 index 00000000..3b0448b5 --- /dev/null +++ b/src/components/chat/ChatModal/index.tsx @@ -0,0 +1,83 @@ +import React, {useEffect} from 'react'; +import {Modal, View, Keyboard, Text, TouchableOpacity} from 'react-native'; +import {GiftedChat, IMessage} from 'react-native-gifted-chat'; +import {Ionicons} from '@expo/vector-icons'; +import {ChatBubble} from '../ChatBubble'; +import ChatInputToolbar from '../ChatInputToolbar'; +import {useChat} from '../../../providers/chat/chatProvider'; +import {useAssistant} from '../../../providers/chat/AssistantProvider'; +import {GestureHandlerRootView} from 'react-native-gesture-handler'; +import {LinearGradient} from 'expo-linear-gradient'; +import {fontColors} from '@sphereon/ui-components.core'; + +type Props = { + onSendMessage: (message: string) => void; + onAudioPress: (currentMessage: IMessage) => void; +}; + +const ChatModal = ({onSendMessage, onAudioPress}: Props) => { + const {messages, isModalVisible, closeModal} = useChat(); + + const handleSendMessage = (newMessages: IMessage[]) => { + onSendMessage(newMessages[0].text); + }; + + return ( + // + + + + + Sphereon Digital Assistant + + + + + ): void => { + Keyboard.dismiss(); + handleSendMessage(messages); + }} + user={{ + _id: 1, + }} + messagesContainerStyle={{ + marginHorizontal: 16, + }} + showUserAvatar={false} + renderAvatar={null} + renderInputToolbar={props => } + renderBubble={props => onAudioPress(props.currentMessage)} />} + // parsePatterns={parsePatterns} + /> + + + + // + ); +}; + +export default ChatModal; diff --git a/src/hooks/useAIAssistant.ts b/src/hooks/useAIAssistant.ts new file mode 100644 index 00000000..8af04b3c --- /dev/null +++ b/src/hooks/useAIAssistant.ts @@ -0,0 +1,260 @@ +import {RealtimeClient, RealtimeUtils} from '@openai/realtime-api-beta'; +import {ItemType} from '@openai/realtime-api-beta/dist/lib/client.js'; +import {useCallback, useEffect, useRef, useState} from 'react'; +import {OPENAI_API_KEY} from 'react-native-dotenv'; +import {useSelector} from 'react-redux'; +import {RootState} from 'src/types'; +import {basicInstructions, reopenChatPrompt} from '../instructions'; +import {navigationRef} from '../navigation/rootNavigation'; +import WavRecorder from '../utils/wavtools/WavRecorder'; +import WavStreamPlayer from '../utils/wavtools/WavStreamPlayer'; +import {stringifyState} from '../utils/stringifyState'; + +export type ChatMode = 'text' | 'voice'; + +const useAIAssistant = () => { + const [isConnected, setIsConnected] = useState(false); + const state = useSelector((state: RootState) => state); + const [items, setItems] = useState([]); + const [isVoiceRecording, setIsVoiceRecording] = useState(false); + const [chatMode, setChatMode] = useState('text'); + const [ignoreTools, setIgnoreTools] = useState(false); + const wavStreamPlayerRef = useRef(new WavStreamPlayer()); + const clientRef = useRef( + new RealtimeClient({ + apiKey: OPENAI_API_KEY, + dangerouslyAllowAPIKeyInBrowser: true, + }), + ); + const startTimeRef = useRef(new Date().toISOString()); + + const base64ToArrayBuffer = (base64: string) => { + const buffer = Buffer.from(base64, 'base64'); // Decode base64 + const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength); + return arrayBuffer; + }; + + const appendInputAudio = (base64: string) => { + console.log('appendInputAudio', base64.length); + if (base64.length > 0) { + clientRef.current?.realtime.send('input_audio_buffer.append', { + audio: base64, + }); + clientRef.current.inputAudioBuffer = RealtimeUtils.mergeInt16Arrays(clientRef.current.inputAudioBuffer, base64ToArrayBuffer(base64)); + } + return true; + }; + + const chunkCallback = useCallback( + (base64: string) => { + appendInputAudio(base64); + }, + [clientRef.current], + ); + const wavRecorderRef = useRef(new WavRecorder(chunkCallback)); + + const connect = useCallback(async () => { + const client = clientRef.current; + + // Set state variables + startTimeRef.current = new Date().toISOString(); + setIsConnected(true); + setItems(client.conversation.getItems().reverse()); + await client.connect(); + }, []); + + const connectConversation = useCallback( + async (shouldCreateResponse = true, screenContext?: string) => { + await connect(); + + const client = clientRef.current; + const wavStreamPlayer = wavStreamPlayerRef.current; + + updateSession({ + screenContext, + instructions: + 'user has just connected to the conversation. Introduce yourself. Tell the user what you can help them with. Then give information about the current screen. If you have relevant information from the app state, provide it.', + }); + + client.on('error', (event: any) => console.error(event)); + + client.on('conversation.interrupted', async () => { + const trackSampleOffset = await wavStreamPlayer.interrupt(); + if (trackSampleOffset?.trackId) { + const {trackId, offset} = trackSampleOffset; + client.cancelResponse(trackId, offset); + } + }); + + client.on('conversation.updated', async ({item, delta}: any) => { + const items = client.conversation.getItems(); + if (delta?.audio && chatMode === 'voice') { + wavStreamPlayer.add16BitPCM(delta.audio, item.id); + } + setItems(items.reverse().filter(item => item.type !== 'function_call')); + }); + + client.on('response.created', async ({response}: any) => { + console.log('response created', '\n', JSON.stringify(response, null, 2), 'response.created'); + }); + + setItems(client.conversation.getItems().reverse()); + + if (shouldCreateResponse) { + client.createResponse(); + } + }, + [state], + ); + + const connectVoice = useCallback(async () => { + console.log('connecting voice'); + await connect(); + const wavRecorder = wavRecorderRef.current; + wavRecorder.begin(); + updateSession({ + instructions: + 'user has just enabled voice mode, which can be toggled with the microphone button in the bottom right corner of the screen. Instruct user if necessary.', + }); + clientRef.current.createResponse(); + }, []); + + const disconnectConversation = useCallback(async () => { + setIsConnected(false); + setItems([]); + + const client = clientRef.current; + client.disconnect(); + + const wavStreamPlayer = wavStreamPlayerRef.current; + wavStreamPlayer.interrupt(); + }, []); + + const deleteConversationItem = useCallback(async (id: string) => { + const client = clientRef.current; + client.deleteItem(id); + }, []); + + useEffect(() => { + return () => { + console.log('resetting convo'); + // cleanup; resets to defaults + const client = clientRef.current; + client.reset(); + }; + }, []); + + const updateSession = ({screenContext, instructions}: {screenContext?: string; instructions?: string}) => { + const client = clientRef.current; + + const route = stringifyState(navigationRef?.current?.getCurrentRoute() || {}); + + const appState = stringifyState(state); + + console.log('appState', appState); + console.log('route', route); + + client.updateSession({ + instructions: ` + # general instructions: + ${basicInstructions} + + # current app state: + ${appState} + + # current route: + ${route} + + # current onscreen context: + ${screenContext || 'unknown'} + + # specific instructions for current context: + ${instructions || ''} + `, + }); + }; + + const sendPrompt = async (prompt: string, screenContext?: string): Promise => { + if (!isConnected) { + await connectConversation(false); + } + + updateSession({screenContext}); + clientRef.current.sendUserMessageContent([{type: 'input_text', text: prompt}]); + }; + const startVoiceRecording = async () => { + const wavRecorder = wavRecorderRef.current; + await wavRecorder.startRecording(); + setIsVoiceRecording(true); + }; + + const endVoiceRecording = async () => { + setIsVoiceRecording(false); + const wavRecorder = wavRecorderRef.current; + const client = clientRef.current; + const filePath = await wavRecorder.stopRecording(); + // if (!base64) return; + // const int16Array = RealtimeUtils.base64ToArrayBuffer(base64); + // // console.log('int16Array', int16Array); + // // if (!int16Array) return; + + // // wavStreamPlayerRef.current.add16BitPCM(int16Array, 'user-audio'); + + // // make chunks of 2400 samples + // const chunkSize = 2400; + // const chunks = []; + // for (let i = 0; i < int16Array.byteLength; i += chunkSize) { + // chunks.push(int16Array.slice(i, i + chunkSize)); + // } + // chunks.forEach(chunk => { + // client.appendInputAudio(chunk); + // }); + + client.createResponse(); + }; + const enableVoiceMode = () => { + setChatMode('voice'); + connectVoice(); + }; + + const enableTextMode = () => { + setChatMode('text'); + }; + + const handleChatOpened = (screenContext?: string) => { + if (!isConnected) { + connectConversation(true, screenContext); + } else { + // workaround for re-opening chat. Using createResponse would create a new response based on the previous prompt. + // This message will be hidden based on the content. Slightly ugly, but it works. + sendPrompt(reopenChatPrompt, screenContext); + } + }; + + const speakMessage = async (int16Array: Int16Array) => { + wavStreamPlayerRef.current.add16BitPCM(int16Array, 'assistant-audio'); + }; + + return { + isConnected, + sendPrompt, + updateSession, + connectConversation, + disconnectConversation, + chatMode, + enableVoiceMode, + enableTextMode, + isVoiceRecording, + startVoiceRecording, + endVoiceRecording, + items, + wavStreamPlayer: wavStreamPlayerRef.current, + addTool: clientRef.current.addTool.bind(clientRef.current), + removeTool: clientRef.current.removeTool.bind(clientRef.current), + handleChatOpened, + speakMessage, + ignoreTools, + }; +}; + +export default useAIAssistant; diff --git a/src/instructions/index.ts b/src/instructions/index.ts new file mode 100644 index 00000000..59004c7c --- /dev/null +++ b/src/instructions/index.ts @@ -0,0 +1,126 @@ +export const basicInstructions = ` +You are an assistant that supports users inside the Sphereon Waller app, guiding them through adding a digital credential using specific, context-aware prompts that match the user's current position within the app. +Each step-by-step instruction is concise and highlights a key term or action to make instructions easy to follow. + +**App Description** +The Sphereon Wallet is a new breed of open standards, open-source, privacy-preserving applications, that gives you full and sole control over your own information. It enables you to manage your own data. +Your data is stored nowhere else but on your phone. Nobody else will have access unless you decide to share it with them. Only you decide if you want to share your data with someone else. +The Sphereon Wallet is build around W3C Decentralized Identifiers and can receive W3C Verifiable Credentials from Issuers and present them to Verifiers. +The wallet is build using our Apache2 open-source licensed SSI-SDK and its key/DID extensions, which you can use to create Issuer and Verifier agents as well as mobile and web wallets. + +Receiving Credentials from an Issuer +You can receive Verifiable Credential from so called issuers. The wallet has support for multiple open standards to get these Credentials. Currently on the OpenID for Verifiable Credential Issuance standard is enabled. + +OpenID for Verifiable Credential Issuance (OID4VCI) process +The current wallet only supports the new OID4VCI specification for receipt of credentials. To get a credential issued to the wallet, using OpenID for Verifiable Credential Issuance (OpenID4VCI) the following steps can be followed. The below issuer systems were part of the JFF/W3C-EDU plugfest 2 to show interop for OpenID4VCI. Please note that the Verifiable Credentials issued by the below list are just for demo/testing purposes. +Launch the wallet +Navigate to the QR reader at the bottom left. +Scan a QR code supplied by an issuer +The first time you encounter an Issuer or Verifier system a Contact needs to be created. The Wallet will pre-fill a suggested name +Depending on whether the issuer supports issuing multiple credentials or not, you will have to make a selection. Note that the current wallet can only accept one credential at a time! +Depending on whether the issuer is requiring a Pincode you will have to enter a pincode. Note this is not the pincode of your wallet!: +You now will go to the Credential Offer screen, which is showing you the offered Credential: +Review the Credential Offer and decide to either accept or decline the credential. +If you accept the offer you will go to the Verifiable Credenital Overview screen and you will see the following message: + +**OID4VCI helpdesk description** +you can use this helpdesk description to answer in-depth questions about the OID4VCI process. + + What is OID4VCI? OpenID for Verifiable Credential Issuance (OID4VCI) is a standard used by our app to issue secure digital credentials—like a virtual ID or certificate—directly to a user's wallet. It works within a federation, which is a trusted network of systems and organizations that follow the same rules for security and authentication. + + How It Works (Simplified for Users): + Requesting a Credential: + + The user (or their wallet app) sends a request to a credential issuer within the federation. + Example: You request a credential that proves your membership, identity, or qualification. + Verifying Trust Through the Federation: + + The app checks that both the user (you) and the credential issuer belong to the same trusted federation. + The issuer verifies your identity and confirms you have permission to receive the credential. This process is secured using OAuth 2.0, which ensures that only trusted parties can participate. + Issuing the Credential: + + After verifying everything, the issuer sends the credential to your wallet. + Because it's part of the federation, the credential is trusted by other services in the network. It's digitally signed to ensure it hasn't been tampered with. + Storing and Using the Credential: + + Your wallet securely stores the credential. When you need to prove something (e.g., your membership), the app can help you present the credential to verifiers, who also belong to the federation. + + How We Use OID4VCI and Federation in Our App: + Federation: The app ensures all parties—wallets, issuers, and verifiers—are part of a trusted network (the federation). This makes it easier to trust and use your credentials across services. + SSI-SDK: The app uses the SSI-SDK to handle the technical bits, like securely managing credentials, verifying federation rules, and integrating wallets. + Interoperability: Because of federation and OpenID standards, your credential works in other apps or systems that follow the same protocols. + + Common Questions Users Might Have: + + What's a federation? + It's a trusted network of organizations that all agree to use the same rules for security and authentication. This ensures your credentials can be trusted across systems. + + How does the app check trust? + The app verifies that the issuer and wallet are part of the federation and follows the agreed security protocols (e.g., using OAuth 2.0 and digital signatures). + + Is my credential secure? + Yes. Credentials are digitally signed and stored securely in your wallet. Only you can share them. + + Can I use my credential in other apps? + Yes, as long as those apps are part of the same or compatible federation. + + +**Adding a credential** +This is the happy flow of adding a credential to the wallet: To get a VC from an issuer by scanning a QR code. +1. The user scans the provided QR code from the issuer within the app on the QR Reader screen. +2. If the credential requires a verification code, the user enters the PIN provided by the issuer. +3. If the credential comes from an unknown contact, The user can review the contact information. +4. User gets to see the credential that can be added +5. Credential is added. + +For each screen the users interaction looks as following: +1. **QR Reader**: + - route name: QR_READER + - The assistant prompts users to scan the QR code displayed by the credential issuer to start the process. + - The assistant supports users to find find the QR code and instructions on how to scan it if needed. +2. **Contact Review**: + - Users are prompted to carefully review contact information provided by the issuer + - A button offers the option to change the alias name of the contact if desired. + - A button "continue" means that the user acknowledged the displayed information and leads the user to the "Enter Verification Code" Screen. + - Once the user clicks 'Continue,' the assistant is allowed continuing with assisting in the next step. +3. **Enter Verification Code**: + - If the QR code requires a verification code, the assistant prompts users to enter the PIN provided by the issuer, with guidance on locating the code if necessary. + - After entering the PIN correctly, the assistant moves forward. +4. **Credential Details**: + - Users are encouraged to read through credential details with an option to review associated claims or credential statuses like 'Pending,' 'Active,' or 'Expired.' + - A button offers the option to change the alias name of the credential if desired. + - A button 'Add to wallet' means that the users want the displayed information in its wallet and leads the user to the credential overview screen/ Add to wallet confirmation screen + - Once the user clicks 'Add to wallet,' the assistant is allowed continuing with assisting in the next step. +5. **Add to Wallet Confirmation**: + - The assistant displays a success message upon completion and offers further guidance as needed. + +**Displaying Information** +- If relevant information is available from the app state, provide it in your answer. +- Whenever you say that certain information is available, also provide the information in the answer. +- Always provide specific information whenever you can. +- information about the user is available from app state currentUser. Use that information to make answers more personal. + +**Guidelines**: +- Avoid technical details unless explicitly requested; keep instructions clear, simple, and relevant. +- Limit your answers to the context of the Digital Wallet App. +- When asked for explanations about the conceptual and technical workings of the app, you can provide an explanation. +- Security and privacy are paramount; encourage users to verify issuers and avoid sharing credentials. +- Do not provide multiple steps in an answer. +- Use a friendly, helpful tone that matches the user's activity in-app, offering brief, sequential guidance. +- Encourage issuer verification and best practices for secure credential sharing. +- Assume that the user has no technical knowledge +- If asked for information that you can get from the state, provide it. +- Provide specific information whenever you can. +- write in short paragraphs and use bullet points for lists. +- Don't use terms like 'app state' or 'state' in your answers. Instead, refer to the information as 'current information' or 'available information'. +- always prioritize screenContext, state and route from these instructions over what the user tells you about it. E.g. the user can tell you that they are on the QR Reader screen, but you should always check the screen state to confirm this. +- in knowing where the user is in the process of getting a new credential, base your knowledge on screenContext, state and route from these instructions. Not on assumptions based on chat history. +- If you have the relevant tools at your disposal, instead of telling a user to perform a certain action, call a function that does this action. E.g. instead of telling a user to click a button, tell them to tap the button. +- If you have the relevant tools at your disposal, instead of instructing the user they can perform a certain action by tapping a button, tell them they can tell you to do this action. + +**Clarification**: +- Request additional details if questions are unclear, particularly on multi-step processes or specific credential functions. +`; + +export const reopenChatPrompt = + "I just re-opened the chat. Guide me. Respond as if you're initiating the thought process independently, without reference to this prompt."; diff --git a/src/navigation/navigation.tsx b/src/navigation/navigation.tsx index 04403098..9794d7b3 100644 --- a/src/navigation/navigation.tsx +++ b/src/navigation/navigation.tsx @@ -1,7 +1,7 @@ import {BottomTabBarProps, createBottomTabNavigator} from '@react-navigation/bottom-tabs'; import {NativeStackHeaderProps, createNativeStackNavigator} from '@react-navigation/native-stack'; import Debug, {Debugger} from 'debug'; -import React, {useEffect} from 'react'; +import React, {useCallback, useEffect} from 'react'; import Toast from 'react-native-toast-message'; import {useSelector} from 'react-redux'; import {APP_ID, EMERGENCY_ALERT_DELAY} from '../@config/constants'; @@ -220,7 +220,6 @@ const MainStackNavigator = (): JSX.Element => { const TabStackNavigator = (): JSX.Element => { const credentialState: ICredentialState = useSelector((state: RootState) => state.credential); - return ( | undefined>(undefined); + +export const useAssistant = () => { + const context = useContext(AssistantContext); + if (!context) { + throw new Error('useAssistant must be used within an AssistantProvider'); + } + return context; +}; + +export const AssistantProvider: React.FC<{children: React.ReactNode}> = ({children}) => { + const assistant = useAIAssistant(); + return {children}; +}; diff --git a/src/providers/chat/chatProvider.tsx b/src/providers/chat/chatProvider.tsx new file mode 100644 index 00000000..9a0334c4 --- /dev/null +++ b/src/providers/chat/chatProvider.tsx @@ -0,0 +1,56 @@ +import React, {createContext, useContext, useState, useCallback} from 'react'; +import {IMessage} from 'react-native-gifted-chat'; + +type ChatContextType = { + messages: IMessage[]; + setMessages: React.Dispatch>; + openModal: () => void; + closeModal: () => void; + isModalVisible: boolean; + showChatButton: (position?: ChatButtonPosition) => void; + hideChatButton: () => void; + isChatButtonVisible: boolean; + chatButtonPosition: ChatButtonPosition; +}; + +type ChatButtonPosition = { + bottom: number; + right: number; +}; + +const ChatContext = createContext(undefined); + +export const useChat = () => { + const context = useContext(ChatContext); + if (!context) { + throw new Error('useChat must be used within a ChatProvider'); + } + return context; +}; + +export const ChatProvider: React.FC<{children: React.ReactNode}> = ({children}) => { + const [isModalVisible, setIsModalVisible] = useState(false); + const [messages, setMessages] = useState([]); + const [isChatButtonVisible, setIsChatButtonVisible] = useState(true); + const [chatButtonPosition, setChatButtonPosition] = useState({bottom: 16, right: 16}); + + const openModal = useCallback(() => { + console.log('openModal'); + setIsModalVisible(true); + }, []); + const closeModal = useCallback(() => setIsModalVisible(false), []); + const showChatButton = useCallback((position?: ChatButtonPosition) => { + setIsChatButtonVisible(true); + if (position) { + setChatButtonPosition(position); + } + }, []); + const hideChatButton = useCallback(() => setIsChatButtonVisible(false), []); + + return ( + + {children} + + ); +}; diff --git a/src/screens/CredentialCatalogScreen/index.tsx b/src/screens/CredentialCatalogScreen/index.tsx index da11143f..40049219 100644 --- a/src/screens/CredentialCatalogScreen/index.tsx +++ b/src/screens/CredentialCatalogScreen/index.tsx @@ -1,4 +1,4 @@ -import React, {FC, ReactElement} from 'react'; +import React, {FC, ReactElement, useMemo} from 'react'; import {RefreshControl, ListRenderItemInfo, TouchableWithoutFeedback} from 'react-native'; import {SwipeListView} from 'react-native-swipe-list-view'; import {NativeStackScreenProps} from '@react-navigation/native-stack'; @@ -26,7 +26,10 @@ import { CredentialCatalogCredentialListContainerStyled as CredentialListContainer, CredentialCatalogScreenPreviewCredentialContentContainerStyled as PreviewCredentialContentContainer, } from '../../styles/components'; -import {MainRoutesEnum, ScreenRoutesEnum, StackParamList, ToastTypeEnum} from '../../types'; +import {MainRoutesEnum, NavigationBarRoutesEnum, ScreenRoutesEnum, StackParamList, ToastTypeEnum} from '../../types'; +import {Chat} from '../../components/chat/Chat'; +import RootNavigation from '../../navigation/rootNavigation'; +import {useChat} from '../../providers/chat/chatProvider'; type Props = NativeStackScreenProps; @@ -148,6 +151,8 @@ const CredentialCatalogScreen: FC = (props: Props): ReactElement => { }, ]; + const {closeModal} = useChat(); + const onClose = async (): Promise => { props.navigation.goBack(); }; @@ -194,6 +199,25 @@ const CredentialCatalogScreen: FC = (props: Props): ReactElement => { ); }; + const tools = useMemo( + () => [ + { + tool: { + name: 'navigateToQRScanner', + description: 'navigate to QR Scanner Screen', + parameters: {}, + }, + callback: () => { + setTimeout(() => { + RootNavigation.navigate(NavigationBarRoutesEnum.QR); + closeModal(); + }, 2000); + }, + }, + ], + [], + ); + return ( @@ -241,6 +265,10 @@ const CredentialCatalogScreen: FC = (props: Props): ReactElement => { /> + ); }; diff --git a/src/screens/CredentialDetailsScreen/index.tsx b/src/screens/CredentialDetailsScreen/index.tsx index b656e200..0ad70829 100644 --- a/src/screens/CredentialDetailsScreen/index.tsx +++ b/src/screens/CredentialDetailsScreen/index.tsx @@ -3,7 +3,7 @@ import {NativeStackScreenProps} from '@react-navigation/native-stack'; import {ImageAttributes, backgroundColors, fontColors} from '@sphereon/ui-components.core'; import {CredentialDetailsRow, CredentialSummary, getCredentialStatus, getIssuerLogo} from '@sphereon/ui-components.credential-branding'; import {PrimaryButton, SSICredentialCardView, SecondaryButton} from '@sphereon/ui-components.ssi-react-native'; -import React, {FC} from 'react'; +import React, {FC, useMemo} from 'react'; import {FlatList, ListRenderItemInfo, View} from 'react-native'; import {DETAILS_INITIAL_NUMBER_TO_RENDER} from '../../@config/constants'; import {NavigationButton} from '../../components/NavigationButton'; @@ -23,6 +23,8 @@ import { } from '../../styles/components'; import {Divider} from '../../styles/components/screens/SSIContactDetailsScreen'; import {ScreenRoutesEnum, StackParamList} from '../../types'; +import {Chat} from '../../components/chat/Chat'; +import {stringifyState} from '../../utils/stringifyState'; type Props = NativeStackScreenProps; @@ -96,6 +98,62 @@ const CredentialDetailsScreen: FC = (props: Props): JSX.Element => { return true; }); + const screenContext = useMemo(() => `this screen shows credential details. onscreen credential: ${stringifyState(credential)}`, [credential]); + + const tools = useMemo( + () => [ + { + tool: { + name: 'editAlias', + description: 'edit alias. Change the name of the contact', + parameters: {}, + }, + callback: () => { + console.log('edit alias'); + }, + }, + { + tool: { + name: 'editConsent', + description: 'edit consent', + parameters: {}, + }, + callback: () => { + console.log('edit consent'); + }, + }, + ...(primaryAction + ? [ + { + tool: { + name: 'accept', + description: 'accept contact', + parameters: {}, + }, + callback: () => { + primaryAction && primaryAction.onPress(); + }, + }, + ] + : []), + ...(secondaryAction + ? [ + { + tool: { + name: 'decline', + description: 'decline contact', + parameters: {}, + }, + callback: () => { + secondaryAction && secondaryAction.onPress(); + }, + }, + ] + : []), + ], + [primaryAction, secondaryAction], + ); + return ( @@ -160,6 +218,7 @@ const CredentialDetailsScreen: FC = (props: Props): JSX.Element => { )} + ); }; diff --git a/src/screens/CredentialsOverviewScreen/index.tsx b/src/screens/CredentialsOverviewScreen/index.tsx index 8a7d32b8..66b1f271 100644 --- a/src/screens/CredentialsOverviewScreen/index.tsx +++ b/src/screens/CredentialsOverviewScreen/index.tsx @@ -1,13 +1,16 @@ -import React from 'react'; +import React, {useMemo} from 'react'; import {Image, View} from 'react-native'; import {connect} from 'react-redux'; import {createTopBarNavigator} from '../../components/navigators/TopBarNavigator'; import {SSIBasicContainerStyled as Container, SSIStatusBarDarkModeStyled as StatusBar} from '../../styles/components'; -import {CreditOverviewStackParamsList, IUser, RootState} from '../../types'; +import {CreditOverviewStackParamsList, IUser, NavigationBarRoutesEnum, RootState} from '../../types'; import {ConfigurableViewKey, ViewPreference} from '../../types/preferences'; import CredentialsOverviewCardList from './CredentialsOverviewCardList'; import CredentialsOverviewList from './CredentialsOverviewList'; import {CredentialsOverviewImages} from './constants'; +import {Chat} from '../../components/chat/Chat'; +import RootNavigation from '../../navigation/rootNavigation'; +import {useChat} from '../../providers/chat/chatProvider'; const CredentialViewTypeNav = createTopBarNavigator(); @@ -21,6 +24,23 @@ type Props = {activeUser: IUser}; const CredentialsOverviewScreen = ({activeUser}: Props) => { const viewPreference = activeUser.preferences.views[ConfigurableViewKey.CREDENTIAL_OVERVIEW]; const initialRouteName = viewPreference === ViewPreference.CARD ? 'Card' : 'List'; + const {closeModal} = useChat(); + const tools = useMemo( + () => [ + { + tool: { + name: 'navigateToQRScanner', + description: 'navigate to QR Scanner Screen', + parameters: {}, + }, + callback: () => { + RootNavigation.navigate(NavigationBarRoutesEnum.QR); + closeModal(); + }, + }, + ], + [], // Only re-create if dependencies change (none in this case) + ); return ( @@ -48,6 +68,13 @@ const CredentialsOverviewScreen = ({activeUser}: Props) => { + ); }; diff --git a/src/screens/NewContactAddScreen/index.tsx b/src/screens/NewContactAddScreen/index.tsx index 96b913e1..f1a6b529 100644 --- a/src/screens/NewContactAddScreen/index.tsx +++ b/src/screens/NewContactAddScreen/index.tsx @@ -1,4 +1,4 @@ -import React, {FC, ReactElement, useCallback, useEffect, useRef, useState} from 'react'; +import React, {FC, ReactElement, useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {BackHandler} from 'react-native'; import {NativeStackScreenProps} from '@react-navigation/native-stack'; import {SSIBasicContainerStyled as Container} from '../../styles/components'; @@ -13,7 +13,9 @@ import {useDispatch, useSelector} from 'react-redux'; import {CONTACT_ALIAS_MAX_LENGTH} from '../../@config/constants'; import {createContact, fetchBrandingForContact, updateContact} from '../../store/actions/contact.actions'; import {useFocusEffect} from '@react-navigation/native'; - +import {Chat} from '../../components/chat/Chat'; +import {stringifyState} from '../../utils/stringifyState'; +import {useChat} from '../../providers/chat/chatProvider'; type Props = NativeStackScreenProps; const NewContactAddScreen: FC = (props: Props): ReactElement => { @@ -41,6 +43,8 @@ const NewContactAddScreen: FC = (props: Props): ReactElement => { const [brandedFederations, setBrandedFederations] = useState>([]); const contactAliasRef = useRef(name); + const {closeModal} = useChat(); + const onBackPress = (): boolean => { if (onBack) { void onBack(); @@ -208,6 +212,62 @@ const NewContactAddScreen: FC = (props: Props): ReactElement => { }); }; + const tools = useMemo( + () => [ + { + tool: { + name: 'accept', + description: 'accept contact', + parameters: {}, + }, + callback: () => { + onContinuePressed(); + closeModal(); + }, + }, + { + tool: { + name: 'decline', + description: 'decline contact', + parameters: {}, + }, + callback: () => { + onDeclinePressed(); + closeModal(); + }, + }, + { + tool: { + name: 'editAlias', + description: 'edit alias. Change the name of the contact', + parameters: {}, + }, + callback: () => { + console.log('edit alias'); + }, + }, + { + tool: { + name: 'editConsent', + description: 'edit consent', + parameters: {}, + }, + callback: () => { + console.log('edit consent'); + }, + }, + ], + [], + ); + + const screenContext = useMemo( + () => + `you are currently on the Contact Review screen. Here you can see information about the contact related to the credential you are adding. Communicate contact details, trust level and possible actions. contact: ${contactState} screen props: ${stringifyState( + {name, uri, roles, logo, description, clientUri, tosUri, policyUri, identities, federations}, + )}`, + [contactState, name, uri, roles, logo, description, clientUri, tosUri, policyUri, identities, federations], + ); + return ( {federations !== undefined && ( @@ -285,6 +345,7 @@ const NewContactAddScreen: FC = (props: Props): ReactElement => { }} logo={logo} /> + ); }; diff --git a/src/screens/Onboarding/WelcomeScreen/index.tsx b/src/screens/Onboarding/WelcomeScreen/index.tsx index 436ec972..3895258d 100644 --- a/src/screens/Onboarding/WelcomeScreen/index.tsx +++ b/src/screens/Onboarding/WelcomeScreen/index.tsx @@ -9,6 +9,7 @@ import ScreenTitleAndDescription from '../../../components/containers/ScreenTitl import {translate} from '../../../localization/Localization'; import {OnboardingContext} from '../../../navigation/machines/onboardingStateNavigation'; import {OnboardingMachineEvents} from '../../../types/machines/onboarding'; +import ChatButton from '../../../components/chat/ChatButton'; // Size of the assets/images/fitted.svg file const SVG_ASSET_WIDTH = 375; diff --git a/src/screens/SSIContactAddScreen/index.tsx b/src/screens/SSIContactAddScreen/index.tsx index 14a47dcd..062598e0 100644 --- a/src/screens/SSIContactAddScreen/index.tsx +++ b/src/screens/SSIContactAddScreen/index.tsx @@ -42,6 +42,10 @@ class SSIContactAddScreen extends PureComponent { hasConsent: this.props.route.params.hasConsent ?? true, }; + constructor(props: IProps) { + super(props); + } + componentDidMount(): void { const {onAliasChange} = this.props.route.params; this.hardwareBackPressListener = BackHandler.addEventListener('hardwareBackPress', this.onBack); diff --git a/src/screens/SSIQRReaderScreen/index.tsx b/src/screens/SSIQRReaderScreen/index.tsx index 801e7843..6a1d0ee4 100644 --- a/src/screens/SSIQRReaderScreen/index.tsx +++ b/src/screens/SSIQRReaderScreen/index.tsx @@ -7,6 +7,7 @@ import {translate} from '../../localization/Localization'; import {onQRScanned} from '../../services/qrService'; import {SSIBasicContainerStyled, SSIQRReaderScreenScannerStyled as QRScanner} from '../../styles/components'; import {PlatformsEnum, ScreenRoutesEnum, StackParamList} from '../../types'; +import {Chat} from '../../components/chat/Chat'; type Props = NativeStackScreenProps; @@ -53,6 +54,8 @@ const SSIQRReaderScreen: FC = (props: Props): JSX.Element => { )} + + ); }; diff --git a/src/utils/stringifyState.ts b/src/utils/stringifyState.ts new file mode 100644 index 00000000..7c192da1 --- /dev/null +++ b/src/utils/stringifyState.ts @@ -0,0 +1,27 @@ +export const stringifyState = (state: {}): string => { + let newState = {...state}; + const clean = (obj: any) => { + const newObj: Record = {}; + for (const key in obj) { + if (Array.isArray(obj[key])) { + newObj[key] = obj[key].map((item: any) => { + if (typeof item === 'object') { + return clean(item); + } + return item; + }); + } else if (typeof obj[key] === 'object' && obj[key] !== null) { + newObj[key] = clean(obj[key]); + } else if (typeof obj[key] === 'string') { + if (!obj[key].startsWith('data:') && obj[key].length < 100) { + // only include strings that are less than 100 characters and do not start with 'data:' + newObj[key] = obj[key]; + } + } + } + return newObj; + }; + + const cleanState = clean(newState); + return JSON.stringify(cleanState); +}; diff --git a/src/utils/wavtools/WavPacker.ts b/src/utils/wavtools/WavPacker.ts new file mode 100644 index 00000000..ea2ee7c2 --- /dev/null +++ b/src/utils/wavtools/WavPacker.ts @@ -0,0 +1,195 @@ +import {decode} from 'base-64'; + +export default class WavPacker { + // Converts Float32Array audio data to WAV ArrayBuffer in Int16 format + static float32ToWav(float32Array: Float32Array, sampleRate = 24000, numChannels = 1) { + const bufferLength = float32Array.length * 2 + 44; // Header + PCM data + const buffer = new ArrayBuffer(bufferLength); + const view = new DataView(buffer); + + // Write WAV header + this.writeWavHeader(view, float32Array.length, sampleRate, numChannels); + + // Write PCM samples + let offset = 44; + for (let i = 0; i < float32Array.length; i++, offset += 2) { + const sample = Math.max(-1, Math.min(1, float32Array[i])); // clamp value + view.setInt16(offset, sample < 0 ? sample * 0x8000 : sample * 0x7fff, true); // little-endian + } + + return buffer; + } + + static int16ToWav(int16Array: Int16Array, sampleRate = 24000, numChannels = 1) { + const bufferLength = int16Array.length * 2 + 44; // Header + PCM data + const buffer = new ArrayBuffer(bufferLength); + const view = new DataView(buffer); + + // Write WAV header + this.writeWavHeader(view, int16Array.length, sampleRate, numChannels); + + // Write PCM samples + let offset = 44; + for (let i = 0; i < int16Array.length; i++, offset += 2) { + view.setInt16(offset, int16Array[i], true); // little-endian + } + + return buffer; + } + + // Helper function to write WAV file headers + static writeWavHeader(view: DataView, dataLength: number, sampleRate: number, numChannels: number) { + const blockAlign = numChannels * 2; + const byteRate = sampleRate * blockAlign; + const fileLength = dataLength * blockAlign + 44 - 8; + + // RIFF identifier + view.setUint32(0, 0x46464952, false); // "RIFF" + view.setUint32(4, fileLength, true); + view.setUint32(8, 0x45564157, false); // "WAVE" + view.setUint32(12, 0x20746d66, false); // "fmt " chunk + view.setUint32(16, 16, true); // PCM format chunk size + view.setUint16(20, 1, true); // Audio format (1 = PCM) + view.setUint16(22, numChannels, true); + view.setUint32(24, sampleRate, true); + view.setUint32(28, byteRate, true); + view.setUint16(32, blockAlign, true); + view.setUint16(34, 16, true); // Bits per sample + view.setUint32(36, 0x61746164, false); // "data" chunk + view.setUint32(40, dataLength * blockAlign, true); + } + + static createWavBlob( + data: Float32Array, // PCM audio data as Float32Array + sampleRate: number, + channels: Float32Array[], // Array representing audio channels + bitsPerSample: number = 32, // Set to 32 for Float32 PCM + ): Blob { + // Helper function to pack data into a buffer in little-endian format + function packData(value: number, bytes: number): Uint8Array { + const buffer = new ArrayBuffer(bytes); + const view = new DataView(buffer); + if (bytes === 4) { + view.setUint32(0, value, true); // 32-bit value in little-endian + } else if (bytes === 2) { + view.setUint16(0, value, true); // 16-bit value in little-endian + } + return new Uint8Array(buffer); + } + + // Update audio format code for Float32 PCM + const audioFormat = bitsPerSample === 32 ? 3 : 1; // 3 = IEEE float, 1 = PCM (int16) + + // Build the WAV header + const header = [ + // "RIFF" chunk descriptor + new TextEncoder().encode('RIFF'), + packData(36 + (data.length * channels.length * bitsPerSample) / 8, 4), // Chunk size + new TextEncoder().encode('WAVE'), + + // "fmt " sub-chunk + new TextEncoder().encode('fmt '), + packData(16, 4), // Subchunk1Size for PCM + packData(audioFormat, 2), // Audio format (3 for IEEE float, 1 for PCM int16) + packData(channels.length, 2), // Number of channels + packData(sampleRate, 4), // Sample rate + packData((sampleRate * channels.length * bitsPerSample) / 8, 4), // Byte rate + packData((channels.length * bitsPerSample) / 8, 2), // Block align + packData(bitsPerSample, 2), // Bits per sample + + // "data" sub-chunk + new TextEncoder().encode('data'), + packData((data.length * channels.length * bitsPerSample) / 8, 4), // Subchunk2Size + ]; + + // Concatenate header and PCM data into a single Blob + const blobParts = [...header, new Uint8Array(data.buffer)]; + const blob = new Blob(blobParts, {type: 'audio/wav'}); + + return blob; + } + + static base64ToInt16Array(base64: string): Int16Array { + // Decode the Base64 string into a Uint8Array + const binaryString = decode(base64); + const wavData = new Uint8Array(binaryString.length); + + for (let i = 0; i < binaryString.length; i++) { + wavData[i] = binaryString.charCodeAt(i); + } + + return new Int16Array(wavData.buffer, 0, wavData.length / 2); // 2 bytes per sample for Int16 + } + + static createWavBase64( + data: Int16Array, // PCM audio data as Int16Array + sampleRate: number, + channels: Int16Array[], // Array representing audio channels + bitsPerSample: number = 16, // Set to 16 for Int16 PCM data + ): string { + // Helper function to pack data into a buffer in little-endian format + function packData(value: number, bytes: number): Uint8Array { + const buffer = new ArrayBuffer(bytes); + const view = new DataView(buffer); + if (bytes === 4) { + view.setUint32(0, value, true); // 32-bit value in little-endian + } else if (bytes === 2) { + view.setUint16(0, value, true); // 16-bit value in little-endian + } + return new Uint8Array(buffer); + } + + // Set audio format for 16-bit PCM + const audioFormat = 1; // 1 = PCM (for Int16 data) + + // Calculate the WAV header + const headerParts = [ + // "RIFF" chunk descriptor + new TextEncoder().encode('RIFF'), + packData(36 + (data.length * channels.length * bitsPerSample) / 8, 4), // Chunk size + new TextEncoder().encode('WAVE'), + + // "fmt " sub-chunk + new TextEncoder().encode('fmt '), + packData(16, 4), // Subchunk1Size for PCM + packData(audioFormat, 2), // Audio format (1 for PCM Int16) + packData(channels.length, 2), // Number of channels + packData(sampleRate, 4), // Sample rate + packData((sampleRate * channels.length * bitsPerSample) / 8, 4), // Byte rate + packData((channels.length * bitsPerSample) / 8, 2), // Block align + packData(bitsPerSample, 2), // Bits per sample + + // "data" sub-chunk + new TextEncoder().encode('data'), + packData((data.length * channels.length * bitsPerSample) / 8, 4), // Subchunk2Size + ]; + + // Flatten the header arrays by calculating the total length first + const headerLength = headerParts.reduce((sum, part) => sum + part.length, 0); + const headerData = new Uint8Array(headerLength); + let offset = 0; + headerParts.forEach(part => { + headerData.set(part, offset); + offset += part.length; + }); + + // Convert PCM Int16Array data to Uint8Array + const pcmData = new Uint8Array(data.buffer); + + // Combine header and PCM data into a single array + const wavData = new Uint8Array(headerData.length + pcmData.length); + wavData.set(headerData, 0); + wavData.set(pcmData, headerData.length); + + // Convert Uint8Array to a Base64 string in chunks to avoid maximum call stack issues + let binary = ''; + const chunkSize = 8192; // Process data in chunks + for (let i = 0; i < wavData.length; i += chunkSize) { + binary += String.fromCharCode.apply(null, wavData.slice(i, i + chunkSize) as unknown as number[]); + } + + const base64String = btoa(binary); + + return base64String; + } +} diff --git a/src/utils/wavtools/WavRecorder.ts b/src/utils/wavtools/WavRecorder.ts new file mode 100644 index 00000000..dde8c36b --- /dev/null +++ b/src/utils/wavtools/WavRecorder.ts @@ -0,0 +1,209 @@ +import AudioRecorderPlayer, {AVEncodingOption} from 'react-native-audio-recorder-player'; +import AudioRecord from 'react-native-audio-record'; +import {Platform} from 'react-native'; +import * as FileSystem from 'expo-file-system'; +import WavPacker from './WavPacker'; +import {Buffer} from 'buffer'; +import {PERMISSIONS, request, check, RESULTS} from 'react-native-permissions'; + +global.Buffer = Buffer; + +const audioRecorderPlayer = new AudioRecorderPlayer(); + +class WavRecorder { + recorder: AudioRecorderPlayer; + recordPath: string; + isRecording: boolean; + chunkInterval: NodeJS.Timeout | undefined; + chunkCallback: (base64: string) => void; + + constructor(chunkCallback: (base64: string) => void) { + this.recorder = audioRecorderPlayer; + this.recordPath = `${FileSystem.documentDirectory}audio_temp.raw`; // Temporary raw recording + this.isRecording = false; + this.chunkInterval = undefined; + this.chunkCallback = chunkCallback; + } + + /** + * Begins a recording session and requests microphone permissions if not already granted. + * @returns {Promise} True if the recording session successfully begins. + */ + async begin(): Promise { + try { + // Request permissions using react-native-permissions + const permissionType = Platform.OS === 'android' ? PERMISSIONS.ANDROID.RECORD_AUDIO : PERMISSIONS.IOS.MICROPHONE; + + const permissionStatus = await check(permissionType); + + if (permissionStatus === RESULTS.DENIED || permissionStatus === RESULTS.BLOCKED || permissionStatus === RESULTS.UNAVAILABLE) { + const requestResult = await request(permissionType); + if (requestResult !== RESULTS.GRANTED) { + throw new Error('Microphone permission denied'); + } + } else if (permissionStatus !== RESULTS.GRANTED) { + throw new Error('Microphone permission not granted'); + } + + // Check if we are already recording + if (this.isRecording) { + throw new Error('Already connected: please call .end() to start a new session'); + } + + const options = { + sampleRate: 24000, // default 44100 + channels: 1, // 1 or 2, default 1 + bitsPerSample: 16, // 8 or 16, default 16 + audioSource: 6, // android only (see below) + wavFile: this.recordPath, + }; + + AudioRecord.init(options); + } catch (error) { + console.error('Error starting recording session:', error); + return false; + } + return true; + } + + async startRecording(): Promise { + try { + // Start recording in a raw PCM format + AudioRecord.start(); + AudioRecord.on('data', data => { + this.chunkCallback(data); + }); + + // console.log('Recording started...'); + this.isRecording = true; + + return true; + } catch (error) { + console.error('Error starting recording session:', error); + return false; + } + } + + async stopRecording() { + try { + if (!this.isRecording) { + throw new Error('Recording session has not started'); + } + + this.isRecording = false; + + if (this.chunkInterval) { + clearInterval(this.chunkInterval); + this.chunkInterval = undefined; + } + const audioFile = await AudioRecord.stop(); + return audioFile; + } catch (error) { + console.error('Error stopping recorder:', error); + } + } + + base64ToFloat32Array(base64Data: WithImplicitCoercion): Float32Array { + // Simple utility to convert from base64 string to Float32Array + const rawData: Buffer = Buffer.from(base64Data, 'base64'); + const float32Array: Float32Array = new Float32Array(rawData.buffer); + return float32Array; + } + + /** + * Records audio in chunks and invokes a callback with each audio chunk. + * @param chunkCallback - Callback function to handle the audio chunks. + * @returns {Promise} + */ + async record(chunkCallback: (data: {mono: Float32Array}) => void): Promise { + try { + // Start recording if not already started + if (this.isRecording) { + throw new Error('Recording already in progress'); + } + + await this.recorder.startRecorder(this.recordPath, { + AVFormatIDKeyIOS: AVEncodingOption.lpcm, // iOS format option + }); + + this.isRecording = true; + + // console.log('Recording started...'); + + // Set up a timer to read chunks periodically + this.chunkInterval = setInterval(async () => { + try { + // Read the current recorded file as Base64 + const audioData: string = await FileSystem.readAsStringAsync(this.recordPath, { + encoding: FileSystem.EncodingType.Base64, + }); + + // Convert to Float32Array + const rawArray: Float32Array = this.base64ToFloat32Array(audioData); + + // Send mono audio data via the callback + chunkCallback({mono: rawArray}); + + // console.log('Chunk processed.'); + } catch (error) { + console.error('Error processing chunk:', error); + } + }, 200); // Processing every 500ms - adjust this as necessary + } catch (error) { + console.error('Error starting recording with chunk processing:', error); + } + } + + /** + * Decodes audio data from multiple formats to a Blob, url, Float32Array and AudioBuffer + * @param {Blob|Float32Array|Int16Array|ArrayBuffer|number[]} audioData + * @param {number} sampleRate + * @param {number} fromSampleRate + * @returns {Promise} + */ + static async decode(audioData: Int16Array, sampleRate = 44100, fromSampleRate = -1) { + const channels: Int16Array[] = [audioData]; + const bitsPerSample = 16; + const wavBase64String = WavPacker.createWavBase64(audioData, sampleRate, channels, bitsPerSample); + + return wavBase64String; + } + + /** + * Helper function to create a WAV header. + * @param pcmDataLength - Length of the PCM data in bytes. + * @param sampleRate - Sample rate of the audio. + * @param numChannels - Number of audio channels. + * @param bitsPerSample - Number of bits per sample (e.g., 16). + * @returns {Buffer} - A buffer containing the WAV header. + */ + static createWavHeader(pcmDataLength: number, sampleRate: number, numChannels: number, bitsPerSample: number): Buffer { + const blockAlign = (numChannels * bitsPerSample) / 8; + const byteRate = sampleRate * blockAlign; + + const header = Buffer.alloc(44); + + // RIFF chunk descriptor + header.write('RIFF', 0); // ChunkID + header.writeUInt32LE(36 + pcmDataLength, 4); // ChunkSize + header.write('WAVE', 8); // Format + + // "fmt " sub-chunk + header.write('fmt ', 12); // Subchunk1ID + header.writeUInt32LE(16, 16); // Subchunk1Size (PCM = 16) + header.writeUInt16LE(1, 20); // AudioFormat (1 = PCM) + header.writeUInt16LE(numChannels, 22); // NumChannels + header.writeUInt32LE(sampleRate, 24); // SampleRate + header.writeUInt32LE(byteRate, 28); // ByteRate + header.writeUInt16LE(blockAlign, 32); // BlockAlign + header.writeUInt16LE(bitsPerSample, 34); // BitsPerSample + + // "data" sub-chunk + header.write('data', 36); // Subchunk2ID + header.writeUInt32LE(pcmDataLength, 40); // Subchunk2Size + + return header; + } +} + +export default WavRecorder; diff --git a/src/utils/wavtools/WavStreamPlayer.ts b/src/utils/wavtools/WavStreamPlayer.ts new file mode 100644 index 00000000..5d7f153d --- /dev/null +++ b/src/utils/wavtools/WavStreamPlayer.ts @@ -0,0 +1,118 @@ +import Sound from 'react-native-sound'; +import * as FileSystem from 'expo-file-system'; +import {Buffer} from 'buffer'; +import WavRecorder from './WavRecorder'; + +async function checkIfFileExists(filePath: string): Promise { + try { + const fileInfo = await FileSystem.getInfoAsync(filePath); + return fileInfo.exists; // This will be true if the file exists + } catch (error) { + console.error('Error checking if file exists:', error); + return false; + } +} + +class WavStreamPlayer { + currentSound: Sound | null; + trackId: string | null; + pcmBuffer: Buffer; + queue: {sound: Sound; filePath: string}[]; + playing: boolean; + + constructor() { + Sound.setCategory('Playback', true); // Setting playback category + this.currentSound = null; + this.trackId = null; + this.pcmBuffer = Buffer.alloc(0); // Initialize an empty buffer to store PCM data + this.queue = []; + } + + async playQueuedFile() { + console.log('Playing queued file', this.queue.length); + if (this.queue.length > 0) { + const queueItem = this.queue.shift(); + if (queueItem) { + try { + await this.play(queueItem.sound, queueItem.filePath, 'temp'); + } finally { + this.playQueuedFile(); + } + } + } + } + + play(sound: Sound, filePath: string, id: string) { + return new Promise((resolve, reject) => { + try { + this.currentSound = sound; + this.trackId = id; + sound.play(success => { + this.currentSound = null; + // FileSystem.deleteAsync(filePath).catch(console.error); + if (!success) { + console.error('Playback failed due to audio decoding errors'); + reject(new Error('Playback failed due to audio decoding errors')); + } else { + resolve(); + } + }); + } catch (error) { + console.error('Error playing sound:', error); + reject(error); + } + }); + } + + async add16BitPCM(pcmData: Int16Array, id: string) { + const base64String = await WavRecorder.decode(pcmData, 24000, 24000); + const filenamePrefix = `${FileSystem.documentDirectory}${id}`; + const timestamp = new Date().getTime(); + const filePath = `${filenamePrefix}_${timestamp}.wav`; + + await FileSystem.writeAsStringAsync(filePath, base64String, { + encoding: FileSystem.EncodingType.Base64, + }); + const sound = new Sound(filePath, '', error => { + if (error) { + console.error('Failed to load the sound', error); + return; + } + this.queue.push({sound, filePath}); + setTimeout(() => { + if (!this.currentSound) { + this.playQueuedFile(); + } + }, 300); // Delay playback to allow for more audio to be added + }); + } + + stopPlaying() { + if (this.currentSound) { + this.currentSound.stop(() => { + // console.log('Playback stopped.'); + this.currentSound = null; + }); + } else { + // console.log('No sound is currently playing.'); + } + } + + async interrupt() { + return new Promise<{trackId: string | null; offset: number} | undefined>((resolve, reject) => { + if (this.currentSound) { + this.currentSound.pause(); + this.currentSound.getCurrentTime(seconds => { + resolve({ + trackId: this.trackId, + offset: seconds, + }); + }); + } else { + // console.log('No sound is currently playing to interrupt.'); + } + }); + } +} + +export default WavStreamPlayer; diff --git a/yarn.lock b/yarn.lock index 19b4879a..bdd864ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -150,10 +150,10 @@ regexpu-core "^6.1.1" semver "^6.3.1" -"@babel/helper-define-polyfill-provider@^0.6.2": - version "0.6.2" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz#18594f789c3594acb24cfdb4a7f7b7d2e8bd912d" - integrity sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ== +"@babel/helper-define-polyfill-provider@^0.6.2", "@babel/helper-define-polyfill-provider@^0.6.3": + version "0.6.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz#f4f2792fae2ef382074bc2d713522cf24e6ddb21" + integrity sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg== dependencies: "@babel/helper-compilation-targets" "^7.22.6" "@babel/helper-plugin-utils" "^7.22.5" @@ -1797,10 +1797,10 @@ dependencies: uuid "^8.0.0" -"@expo/cli@0.18.30": - version "0.18.30" - resolved "https://registry.yarnpkg.com/@expo/cli/-/cli-0.18.30.tgz#0cb4829aa11e98ae350a5c15958b9816e9a1d2f0" - integrity sha512-V90TUJh9Ly8stYo8nwqIqNWCsYjE28GlVFWEhAFCUOp99foiQr8HSTpiiX5GIrprcPoWmlGoY+J5fQA29R4lFg== +"@expo/cli@0.18.31": + version "0.18.31" + resolved "https://registry.yarnpkg.com/@expo/cli/-/cli-0.18.31.tgz#d07b7f1b2d10d146ec8b732ce1353b90912c56bd" + integrity sha512-v9llw9fT3Uv+TCM6Xllo54t672CuYtinEQZ2LPJ2EJsCwuTc4Cd2gXQaouuIVD21VoeGQnr5JtJuWbF97sBKzQ== dependencies: "@babel/runtime" "^7.20.0" "@expo/code-signing-certificates" "0.0.5" @@ -1888,10 +1888,10 @@ node-forge "^1.2.1" nullthrows "^1.1.1" -"@expo/config-plugins@8.0.10", "@expo/config-plugins@^8.0.6", "@expo/config-plugins@~8.0.0", "@expo/config-plugins@~8.0.8": - version "8.0.10" - resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-8.0.10.tgz#5cda076f38bc04675cb42d8acdd23d6e460a62de" - integrity sha512-KG1fnSKRmsudPU9BWkl59PyE0byrE2HTnqbOrgwr2FAhqh7tfr9nRs6A9oLS/ntpGzmFxccTEcsV0L4apsuxxg== +"@expo/config-plugins@8.0.11", "@expo/config-plugins@^8.0.6", "@expo/config-plugins@~8.0.0", "@expo/config-plugins@~8.0.8": + version "8.0.11" + resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-8.0.11.tgz#b814395a910f4c8b7cc95d9719dccb6ca53ea4c5" + integrity sha512-oALE1HwnLFthrobAcC9ocnR9KXLzfWEjgIe4CPe+rDsfC6GDs8dGYCXfRFoCEzoLN4TGYs9RdZ8r0KoCcNrm2A== dependencies: "@expo/config-types" "^51.0.3" "@expo/json-file" "~8.3.0" @@ -1985,6 +1985,15 @@ json5 "^2.2.2" write-file-atomic "^2.3.0" +"@expo/json-file@^9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@expo/json-file/-/json-file-9.0.0.tgz#e3688c9b108cfd7e819f1354a9458ba6e93fc943" + integrity sha512-M+55xFVrFzDcgMDf+52lPDLjKB5xwRfStWlv/b/Vu2OLgxGZLWpxoPYjlRoHqxjPbCQIi2ZCbobK+0KuNhsELg== + dependencies: + "@babel/code-frame" "~7.10.4" + json5 "^2.2.3" + write-file-atomic "^2.3.0" + "@expo/metro-config@0.18.11": version "0.18.11" resolved "https://registry.yarnpkg.com/@expo/metro-config/-/metro-config-0.18.11.tgz#22e82d92fb9d94ac760cc8b3bff48e6f32b4f032" @@ -2010,28 +2019,28 @@ resolve-from "^5.0.0" "@expo/osascript@^2.0.31": - version "2.1.3" - resolved "https://registry.yarnpkg.com/@expo/osascript/-/osascript-2.1.3.tgz#912b74825cb83f3b958cad81034df9e19f1f2808" - integrity sha512-aOEkhPzDsaAfolSswObGiYW0Pf0ROfR9J2NBRLQACdQ6uJlyAMiPF45DVEVknAU9juKh0y8ZyvC9LXqLEJYohA== + version "2.1.4" + resolved "https://registry.yarnpkg.com/@expo/osascript/-/osascript-2.1.4.tgz#4918d16ba09d8b01cb393bc5997055e61d31246f" + integrity sha512-LcPjxJ5FOFpqPORm+5MRLV0CuYWMthJYV6eerF+lQVXKlvgSn3EOqaHC3Vf3H+vmB0f6G4kdvvFtg40vG4bIhA== dependencies: "@expo/spawn-async" "^1.7.2" exec-async "^2.2.0" "@expo/package-manager@^1.5.0": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@expo/package-manager/-/package-manager-1.5.2.tgz#6015963669977a188bbbac930aa0dc103162ee73" - integrity sha512-IuA9XtGBilce0q8cyxtWINqbzMB1Fia0Yrug/O53HNuRSwQguV/iqjV68bsa4z8mYerePhcFgtvISWLAlNEbUA== + version "1.6.1" + resolved "https://registry.yarnpkg.com/@expo/package-manager/-/package-manager-1.6.1.tgz#ab845238dec10bb48bca2b90e060dfe8c1525602" + integrity sha512-4rT46wP/94Ll+CWXtFKok1Lbo9XncSUtErFOo/9/3FVughGbIfdG4SKZOAWIpr9wxwEfkyhHfAP9q71ONlWODw== dependencies: - "@expo/json-file" "^8.3.0" + "@expo/json-file" "^9.0.0" "@expo/spawn-async" "^1.7.2" ansi-regex "^5.0.0" chalk "^4.0.0" find-up "^5.0.0" - find-yarn-workspace-root "~2.0.0" js-yaml "^3.13.1" - micromatch "^4.0.2" - npm-package-arg "^7.0.0" + micromatch "^4.0.8" + npm-package-arg "^11.0.0" ora "^3.4.0" + resolve-workspace-root "^2.0.0" split "^1.0.1" sudo-prompt "9.1.1" @@ -2044,7 +2053,7 @@ base64-js "^1.2.3" xmlbuilder "^14.0.0" -"@expo/prebuild-config@7.0.8", "@expo/prebuild-config@7.0.9", "@expo/prebuild-config@~7.0.0": +"@expo/prebuild-config@7.0.9", "@expo/prebuild-config@~7.0.0": version "7.0.9" resolved "https://registry.yarnpkg.com/@expo/prebuild-config/-/prebuild-config-7.0.9.tgz#7abd489e18ed6514a0c9cd214eb34c0d5efda799" integrity sha512-9i6Cg7jInpnGEHN0jxnW0P+0BexnePiBzmbUvzSbRXpdXihYUX2AKMu73jgzxn5P1hXOSkzNS7umaY+BZ+aBag== @@ -2061,6 +2070,14 @@ semver "^7.6.0" xml2js "0.6.0" +"@expo/react-native-action-sheet@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@expo/react-native-action-sheet/-/react-native-action-sheet-4.1.0.tgz#d10da29f487a4e0fa8c688b560cc354d5212d9d3" + integrity sha512-RILoWhREgjMdr1NUSmZa/cHg8onV2YPDAMOy0iIP1c3H7nT9QQZf5dQNHK8ehcLM82sarVxriBJyYSSHAx7j6w== + dependencies: + "@types/hoist-non-react-statics" "^3.3.1" + hoist-non-react-statics "^3.3.0" + "@expo/rudder-sdk-node@1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@expo/rudder-sdk-node/-/rudder-sdk-node-1.1.1.tgz#6aa575f346833eb6290282118766d4919c808c6a" @@ -2535,6 +2552,12 @@ dependencies: semver "^7.3.5" +"@openai/realtime-api-beta@github:openai/openai-realtime-api-beta": + version "0.0.0" + resolved "https://codeload.github.com/openai/openai-realtime-api-beta/tar.gz/a5cb94824f625423858ebacb9f769226ca98945f" + dependencies: + ws "^8.18.0" + "@pakenfit/react-native-pin-input@^1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@pakenfit/react-native-pin-input/-/react-native-pin-input-1.4.0.tgz#23d0d55de136d39dea95f1d6ff57bd8bae397ba6" @@ -4817,6 +4840,11 @@ dependencies: "@babel/types" "^7.20.7" +"@types/base-64@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/base-64/-/base-64-1.0.2.tgz#f7bc80d242306f20c57f076d79d1efe2d31032ca" + integrity sha512-uPgKMmM9fmn7I+Zi6YBqctOye4SlJsHKcisjHIMWpb2YKZRc36GpKyNuQ03JcT+oNXg1m7Uv4wU94EVltn8/cw== + "@types/base16@^1.0.2": version "1.0.5" resolved "https://registry.yarnpkg.com/@types/base16/-/base16-1.0.5.tgz#9a7df8eed525c6968d254dada2a2f653a28e73f6" @@ -4876,7 +4904,7 @@ resolved "https://registry.yarnpkg.com/@types/hammerjs/-/hammerjs-2.0.46.tgz#381daaca1360ff8a7c8dff63f32e69745b9fb1e1" integrity sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw== -"@types/hoist-non-react-statics@^3.3.0": +"@types/hoist-non-react-statics@^3.3.0", "@types/hoist-non-react-statics@^3.3.1": version "3.3.5" resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz#dab7867ef789d87e2b4b0003c9d65c49cc44a494" integrity sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg== @@ -4943,6 +4971,13 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== +"@types/lodash.isequal@^4.5.8": + version "4.5.8" + resolved "https://registry.yarnpkg.com/@types/lodash.isequal/-/lodash.isequal-4.5.8.tgz#b30bb6ff6a5f6c19b3daf389d649ac7f7a250499" + integrity sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA== + dependencies: + "@types/lodash" "*" + "@types/lodash.memoize@^4.1.6": version "4.1.9" resolved "https://registry.yarnpkg.com/@types/lodash.memoize/-/lodash.memoize-4.1.9.tgz#9f8912d39b6e450c0d342a2b74c99d331bf2016b" @@ -4968,9 +5003,9 @@ "@types/node" "*" "@types/node@*": - version "22.9.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.9.0.tgz#b7f16e5c3384788542c72dc3d561a7ceae2c0365" - integrity sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ== + version "22.9.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.9.1.tgz#bdf91c36e0e7ecfb7257b2d75bf1b206b308ca71" + integrity sha512-p8Yy/8sw1caA8CdRIQBG5tiLHmxtQKObCijiAa9Ez+d4+PRffM4054xbju0msf+cvhJpnFEeNjxmVT/0ipktrg== dependencies: undici-types "~6.19.8" @@ -4986,6 +5021,11 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.13.tgz#2af91918ee12d9d32914feb13f5326658461b451" integrity sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA== +"@types/react-native-dotenv@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@types/react-native-dotenv/-/react-native-dotenv-0.2.2.tgz#14af0d8fdddb46c01a26d7cbd562a6455002487e" + integrity sha512-YDgO2hdTK5PaxZrIFtVXrjeFOhJ+7A9a8VDUK4QmHCPGIB5i6DroLG9IpItX5qCshz7aPsQfgy9X3w82Otd4HA== + "@types/react-native-share-menu@^5.0.2": version "5.0.5" resolved "https://registry.yarnpkg.com/@types/react-native-share-menu/-/react-native-share-menu-5.0.5.tgz#790c635ed991fb1541abce078c3d326369f204d3" @@ -5046,6 +5086,13 @@ resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.12.2.tgz#760329e756e18a4aab82fc502b51ebdfebbe49f5" integrity sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA== +"@types/ws@^8.5.12": + version "8.5.13" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.13.tgz#6414c280875e2691d0d1e080b05addbf5cb91e20" + integrity sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA== + dependencies: + "@types/node" "*" + "@types/yargs-parser@*": version "21.0.3" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" @@ -6049,12 +6096,12 @@ babel-plugin-module-resolver@^4.1.0: resolve "^1.13.1" babel-plugin-polyfill-corejs2@^0.4.10: - version "0.4.11" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz#30320dfe3ffe1a336c15afdcdafd6fd615b25e33" - integrity sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q== + version "0.4.12" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz#ca55bbec8ab0edeeef3d7b8ffd75322e210879a9" + integrity sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og== dependencies: "@babel/compat-data" "^7.22.6" - "@babel/helper-define-polyfill-provider" "^0.6.2" + "@babel/helper-define-polyfill-provider" "^0.6.3" semver "^6.3.1" babel-plugin-polyfill-corejs3@^0.10.6: @@ -6066,11 +6113,11 @@ babel-plugin-polyfill-corejs3@^0.10.6: core-js-compat "^3.38.0" babel-plugin-polyfill-regenerator@^0.6.1: - version "0.6.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz#addc47e240edd1da1058ebda03021f382bba785e" - integrity sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg== + version "0.6.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz#abeb1f3f1c762eace37587f42548b08b57789bc8" + integrity sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q== dependencies: - "@babel/helper-define-polyfill-provider" "^0.6.2" + "@babel/helper-define-polyfill-provider" "^0.6.3" babel-plugin-react-compiler@0.0.0-experimental-592953e-20240517: version "0.0.0-experimental-592953e-20240517" @@ -6197,6 +6244,16 @@ base-58@^0.0.1: resolved "https://registry.yarnpkg.com/base-58/-/base-58-0.0.1.tgz#85d3e70251075661933388f831d1eb8b8f6314e3" integrity sha512-denlKTnozZTVWuh1QkbXf10kkFNc+0/eno29RR+6g5al0yGI+iAOFt/cIA2tvnKoADlUFLZHs50ZdWF+c9WBnw== +base-64@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb" + integrity sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA== + +base-64@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/base-64/-/base-64-1.0.0.tgz#09d0f2084e32a3fd08c2475b973788eee6ae8f4a" + integrity sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg== + base-x@^3.0.2: version "3.0.10" resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.10.tgz#62de58653f8762b5d6f8d9fe30fa75f7b2585a75" @@ -6287,9 +6344,9 @@ blakejs@^1.1.1, blakejs@^1.2.1: integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ== bn.js@^4.0.0, bn.js@^4.11.9: - version "4.12.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" - integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + version "4.12.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.1.tgz#215741fe3c9dba2d7e12c001d0cfdbae43975ba7" + integrity sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg== bn.js@^5.2.1, bn.js@~5.2.0: version "5.2.1" @@ -6532,9 +6589,9 @@ camelize@^1.0.0: integrity sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ== caniuse-lite@^1.0.30001669: - version "1.0.30001677" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001677.tgz#27c2e2c637e007cfa864a16f7dfe7cde66b38b5f" - integrity sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog== + version "1.0.30001683" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001683.tgz#7f026a2d5d319a9cf8915a1451173052caaadc81" + integrity sha512-iqmNnThZ0n70mNwvxpEC2nBJ037ZHZUoBI5Gorh1Mw6IlEAZujEoU1tXA628iZfzm7R9FvFzxbfdgml82a3k8Q== canonicalize@^1.0.1, canonicalize@^1.0.3, canonicalize@^1.0.8: version "1.0.8" @@ -6585,9 +6642,9 @@ char-regex@^1.0.2: integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== char-regex@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-2.0.1.tgz#6dafdb25f9d3349914079f010ba8d0e6ff9cd01e" - integrity sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw== + version "2.0.2" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-2.0.2.tgz#81385bb071af4df774bff8721d0ca15ef29ea0bb" + integrity sha512-cbGOjAptfM2LVmWhwRFHEKTPkLwNddVmuqYZQt895yXwAsWsXObCG+YN4DGQ/JBtT4GP1a1lPPdio2z413LmTg== charenc@0.0.2, charenc@~0.0.1: version "0.0.2" @@ -6968,9 +7025,9 @@ cross-fetch@^4.0.0: node-fetch "^2.6.12" cross-spawn@^6.0.0: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + version "6.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.6.tgz#30d0efa0712ddb7eb5a76e1e8721bffafa6b5d57" + integrity sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw== dependencies: nice-try "^1.0.4" path-key "^2.0.1" @@ -6979,9 +7036,9 @@ cross-spawn@^6.0.0: which "^1.2.9" cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" @@ -7157,7 +7214,7 @@ data-view-byte-offset@^1.0.0: es-errors "^1.3.0" is-data-view "^1.0.1" -dayjs@^1.11.9, dayjs@^1.8.15: +dayjs@^1.11.13, dayjs@^1.11.9, dayjs@^1.8.15: version "1.11.13" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c" integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== @@ -7468,13 +7525,13 @@ dot-case@^3.0.4: tslib "^2.0.3" dotenv-expand@~11.0.6: - version "11.0.6" - resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-11.0.6.tgz#f2c840fd924d7c77a94eff98f153331d876882d3" - integrity sha512-8NHi73otpWsZGBSZwwknTXS5pqMOrk9+Ssrna8xCaxkzEpU9OTf9R5ArQGVw03//Zmk9MOwLPng9WwndvpAJ5g== + version "11.0.7" + resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-11.0.7.tgz#af695aea007d6fdc84c86cd8d0ad7beb40a0bd08" + integrity sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA== dependencies: - dotenv "^16.4.4" + dotenv "^16.4.5" -dotenv@^16.0.3, dotenv@^16.3.1, dotenv@^16.4.4, dotenv@^16.4.5, dotenv@~16.4.5: +dotenv@^16.0.3, dotenv@^16.3.1, dotenv@^16.4.5, dotenv@~16.4.5: version "16.4.5" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== @@ -7507,9 +7564,9 @@ ejs@^3.1.10: jake "^10.8.5" electron-to-chromium@^1.5.41: - version "1.5.52" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.52.tgz#2bed832c95a56a195504f918150e548474687da8" - integrity sha512-xtoijJTZ+qeucLBDNztDOuQBE1ksqjvNjvqFoST3nGC7fSpqJ+X6BdTBaY5BHG+IhWWmpc6b/KfpeuEDupEPOQ== + version "1.5.64" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.64.tgz#ac8c4c89075d35a1514b620f47dfe48a71ec3697" + integrity sha512-IXEuxU+5ClW2IGEYFC2T7szbyVgehupCWQe5GNh+H065CD6U6IFN0s4KeAMFGNmQolRU4IV7zGBWSYMmZ8uuqQ== elliptic@6.5.4: version "6.5.4" @@ -7525,9 +7582,9 @@ elliptic@6.5.4: minimalistic-crypto-utils "^1.0.1" elliptic@^6.5.2, elliptic@^6.5.4, elliptic@^6.5.7: - version "6.6.0" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.6.0.tgz#5919ec723286c1edf28685aa89261d4761afa210" - integrity sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA== + version "6.6.1" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.6.1.tgz#3b8ffb02670bf69e382c7f65bf524c97c5405c06" + integrity sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g== dependencies: bn.js "^4.11.9" brorand "^1.1.0" @@ -7640,9 +7697,9 @@ errorhandler@^1.5.1: escape-html "~1.0.3" es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.1, es-abstract@^1.23.2, es-abstract@^1.23.3: - version "1.23.3" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" - integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== + version "1.23.5" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.5.tgz#f4599a4946d57ed467515ed10e4f157289cd52fb" + integrity sha512-vlmniQ0WNPwXqA0BnmwV3Ng7HxiGlh6r5U6JcTMNx8OilcAGqVJBHJcPjqOMaczU9fRuRK5Px2BdVyPRnKMMVQ== dependencies: array-buffer-byte-length "^1.0.1" arraybuffer.prototype.slice "^1.0.3" @@ -7659,7 +7716,7 @@ es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23 function.prototype.name "^1.1.6" get-intrinsic "^1.2.4" get-symbol-description "^1.0.2" - globalthis "^1.0.3" + globalthis "^1.0.4" gopd "^1.0.1" has-property-descriptors "^1.0.2" has-proto "^1.0.3" @@ -7675,10 +7732,10 @@ es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23 is-string "^1.0.7" is-typed-array "^1.1.13" is-weakref "^1.0.2" - object-inspect "^1.13.1" + object-inspect "^1.13.3" object-keys "^1.1.1" object.assign "^4.1.5" - regexp.prototype.flags "^1.5.2" + regexp.prototype.flags "^1.5.3" safe-array-concat "^1.1.2" safe-regex-test "^1.0.3" string.prototype.trim "^1.2.9" @@ -8218,11 +8275,11 @@ expo-random@~14.0.1: base64-js "^1.3.0" expo-splash-screen@~0.27.5: - version "0.27.6" - resolved "https://registry.yarnpkg.com/expo-splash-screen/-/expo-splash-screen-0.27.6.tgz#d57f3f80d22f4ada90fd2edf573bbc148f956f67" - integrity sha512-joUwZQS48k3VMnucQ0Y8Dle1t1FyIvluQA4kjuPx2x7l2dRrfctbo34ahTnC0p1o2go5oN2iEnSTOElY4wRQHw== + version "0.27.7" + resolved "https://registry.yarnpkg.com/expo-splash-screen/-/expo-splash-screen-0.27.7.tgz#52171be54d8c008880d928e802819d767fbd3c12" + integrity sha512-s+eGcG185878nixlrjhhLD6UDYrvoqBUaBkIEozBVWFg3pkdsKpONPiUAco4XR3h7I/9ODq4quN28RJLFO+s0Q== dependencies: - "@expo/prebuild-config" "7.0.8" + "@expo/prebuild-config" "7.0.9" expo-status-bar@~1.12.1: version "1.12.1" @@ -8230,14 +8287,14 @@ expo-status-bar@~1.12.1: integrity sha512-/t3xdbS8KB0prj5KG5w7z+wZPFlPtkgs95BsmrP/E7Q0xHXTcDcQ6Cu2FkFuRM+PKTb17cJDnLkawyS5vDLxMA== expo@~51.0.24: - version "51.0.38" - resolved "https://registry.yarnpkg.com/expo/-/expo-51.0.38.tgz#e4127b230454a34a507cfb9f1a2e4b3855cb0579" - integrity sha512-/B9npFkOPmv6WMIhdjQXEY0Z9k/67UZIVkodW8JxGIXwKUZAGHL+z1R5hTtWimpIrvVhyHUFU3f8uhfEKYhHNQ== + version "51.0.39" + resolved "https://registry.yarnpkg.com/expo/-/expo-51.0.39.tgz#d9efab081a91a0d3e925b0e4648722b13a8fceae" + integrity sha512-Cs/9xopyzJrpXWbyVUZnr37rprdFJorRgfSp6cdBfvbjxZeKnw2MEu7wJwV/s626i5lZTPGjZPHUF9uQvt51cg== dependencies: "@babel/runtime" "^7.20.0" - "@expo/cli" "0.18.30" + "@expo/cli" "0.18.31" "@expo/config" "9.0.4" - "@expo/config-plugins" "8.0.10" + "@expo/config-plugins" "8.0.11" "@expo/metro-config" "0.18.11" "@expo/vector-icons" "^14.0.3" babel-preset-expo "~11.0.15" @@ -8271,6 +8328,11 @@ factory.ts@^1.0.0: clone-deep "^4.0.1" source-map-support "^0.5.21" +fast-base64-decode@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz#b434a0dd7d92b12b43f26819300d2dafb83ee418" + integrity sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q== + fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -8478,9 +8540,9 @@ flat-cache@^3.0.4: rimraf "^3.0.2" flatted@^3.2.9: - version "3.3.1" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" - integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== + version "3.3.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.2.tgz#adba1448a9841bec72b42c532ea23dbbedef1a27" + integrity sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA== flow-enums-runtime@^0.0.6: version "0.0.6" @@ -8488,9 +8550,9 @@ flow-enums-runtime@^0.0.6: integrity sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw== flow-parser@0.*: - version "0.251.1" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.251.1.tgz#b561c765baff1a93d85c510360d2d9c78f81ed86" - integrity sha512-8ZuLqJPlL/T9K3zFdr1m88Lx8JOoJluTTdyvN4uH5NT9zoIIFqbCDoXVhkHh022k2lhuAyFF27cu0BYKh5SmDA== + version "0.254.1" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.254.1.tgz#914a3420506c0dbd7a3be73d94c51ca41f2a7727" + integrity sha512-dyUrQD6ZyI4PVQppj8PP5kj6BVThK8FprAcPCnJNLIZM7zcAL6/xGS1EE1haWv2HcO/dHy7GSwOAO59uaffjYw== fontfaceobserver@^2.1.0: version "2.3.0" @@ -8942,6 +9004,13 @@ hosted-git-info@^3.0.2: dependencies: lru-cache "^6.0.0" +hosted-git-info@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-7.0.2.tgz#9b751acac097757667f30114607ef7b661ff4f17" + integrity sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w== + dependencies: + lru-cache "^10.0.1" + html-encoding-sniffer@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" @@ -8987,10 +9056,10 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -husky@^8.0.3: - version "8.0.3" - resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184" - integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg== +hyochan-welcome@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hyochan-welcome/-/hyochan-welcome-1.0.1.tgz#a949de8bc3c1e18fe096016bc273aa191c844971" + integrity sha512-WRZNH5grESkOXP/r7xc7TMhO9cUqxaJIuZcQDAjzHWs6viGP+sWtVbiBigxc9YVRrw3hnkESQWwzqg+oOga65A== i18n-js@^3.8.0, i18n-js@^3.9.2: version "3.9.2" @@ -10555,9 +10624,9 @@ levn@~0.3.0: type-check "~0.3.2" libphonenumber-js@^1.10.53: - version "1.11.13" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.11.13.tgz#2d50697a3d6ec77ff10e7344864421ab4ba1f9ae" - integrity sha512-LIJmXxgs7o1njVZPcX5fkbtcFgDnXXPvJQQBH5Ho/8+r6BFlJaEbJ+bAiaUGaChWUhFtvawwdmXIOz4wZBANCg== + version "1.11.15" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.11.15.tgz#0947ba02208cf6c44fdf3b07e097a98b3ec945f4" + integrity sha512-M7+rtYi9l5RvMmHyjyoF3BHHUpXTYdJ0PezZGHNs0GyW1lO+K7jxlXpbdIb7a56h0nqLYdjIw+E+z0ciGaJP7g== lighthouse-logger@^1.0.0: version "1.4.2" @@ -11150,7 +11219,7 @@ metro@0.80.12, metro@^0.80.3, metro@~0.80.8: ws "^7.5.10" yargs "^17.6.2" -micromatch@^4.0.2, micromatch@^4.0.4: +micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== @@ -11464,9 +11533,9 @@ node-forge@^1, node-forge@^1.2.1, node-forge@^1.3.1: integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: - version "4.8.2" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.2.tgz#4f802b71c1ab2ca16af830e6c1ea7dd1ad9496fa" - integrity sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw== + version "4.8.4" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.4.tgz#8a70ee85464ae52327772a90d66c6077a900cfc8" + integrity sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ== node-int64@^0.4.0: version "0.4.0" @@ -11493,6 +11562,16 @@ normalize-path@^3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +npm-package-arg@^11.0.0: + version "11.0.3" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-11.0.3.tgz#dae0c21199a99feca39ee4bfb074df3adac87e2d" + integrity sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw== + dependencies: + hosted-git-info "^7.0.0" + proc-log "^4.0.0" + semver "^7.3.5" + validate-npm-package-name "^5.0.0" + npm-package-arg@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-7.0.0.tgz#52cdf08b491c0c59df687c4c925a89102ef794a5" @@ -11551,10 +11630,10 @@ object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-inspect@^1.13.1: - version "1.13.2" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" - integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== +object-inspect@^1.13.1, object-inspect@^1.13.3: + version "1.13.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.3.tgz#f14c183de51130243d6d18ae149375ff50ea488a" + integrity sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA== object-is@^1.1.5: version "1.1.6" @@ -12014,7 +12093,7 @@ pause@0.0.1: resolved "https://registry.yarnpkg.com/pause/-/pause-0.0.1.tgz#1d408b3fdb76923b9543d96fb4c9dfd535d9cb5d" integrity sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg== -picocolors@^1.0.0, picocolors@^1.1.0: +picocolors@^1.0.0, picocolors@^1.1.0, picocolors@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== @@ -12106,12 +12185,12 @@ postcss@8.4.38: source-map-js "^1.2.0" postcss@~8.4.32: - version "8.4.47" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.47.tgz#5bf6c9a010f3e724c503bf03ef7947dcb0fea365" - integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ== + version "8.4.49" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.49.tgz#4ea479048ab059ab3ae61d082190fabfd994fe19" + integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA== dependencies: nanoid "^3.3.7" - picocolors "^1.1.0" + picocolors "^1.1.1" source-map-js "^1.2.1" prelude-ls@^1.2.1: @@ -12170,6 +12249,11 @@ pretty-format@^29.0.0, pretty-format@^29.0.3, pretty-format@^29.7.0: ansi-styles "^5.0.0" react-is "^18.0.0" +proc-log@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-4.2.0.tgz#b6f461e4026e75fdfe228b265e9f7a00779d7034" + integrity sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA== + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -12207,7 +12291,7 @@ prompts@^2.0.1, prompts@^2.2.1, prompts@^2.3.2, prompts@^2.4.2: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.7.x, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -12227,9 +12311,11 @@ prr@~1.0.1: integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw== psl@^1.1.33: - version "1.9.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" - integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== + version "1.13.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.13.0.tgz#8b2357f13ef3cf546af3f52de00543a94da86cfa" + integrity sha512-BFwmFXiJoFqlUpZ5Qssolv15DMyc84gTBds1BjsV1BfXEo1UyyD7GsmN67n7J77uRhoSNW1AXtXKPLcBFQn9Aw== + dependencies: + punycode "^2.3.1" pump@^3.0.0: version "3.0.2" @@ -12239,7 +12325,7 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -punycode@^2.1.1: +punycode@^2.1.1, punycode@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== @@ -12272,9 +12358,9 @@ qrcode-terminal@0.11.0: integrity sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ== qs@^6.11.2: - version "6.13.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" - integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== + version "6.13.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.1.tgz#3ce5fc72bd3a8171b85c99b93c65dd20b7d1b16e" + integrity sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg== dependencies: side-channel "^1.0.6" @@ -12402,11 +12488,28 @@ react-native-app-auth@^6.4.3: invariant "2.2.4" react-native-base64 "0.0.2" +react-native-audio-record@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/react-native-audio-record/-/react-native-audio-record-0.2.2.tgz#9cbb0ddd3cbdd18f5edfabae812c0bc9c680e928" + integrity sha512-+JEY3AWz21xylePbXZ81DLHcqU5oJYztn3Uel3ay53P3ZrvJlwfmOmyOuoavA9boPV1O0dLLQIu9gC7xbKwNvQ== + +react-native-audio-recorder-player@^3.6.12: + version "3.6.12" + resolved "https://registry.yarnpkg.com/react-native-audio-recorder-player/-/react-native-audio-recorder-player-3.6.12.tgz#edc3aa271534a4ce6bc4b32ff2b0ece555b35498" + integrity sha512-/pnZxPA2jvQ9K7oDtrjcAjc/hn124swi451Q/i1Z6yesDVqupE38moNm12HgY1IVupdUCjeiZVqPoShcE0N8mw== + dependencies: + hyochan-welcome "^1.0.0" + react-native-base64@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/react-native-base64/-/react-native-base64-0.0.2.tgz#c28463c2c6779ac3ec5fdd12979ebc8c5f9b410a" integrity sha512-Fu/J1a2y0X22EJDWqJR2oEa1fpP4gTFjYxk8ElJdt1Yak3HOXmFJ7EohLVHU2DaQkgmKfw8qb7u/48gpzveRbg== +react-native-communications@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/react-native-communications/-/react-native-communications-2.2.1.tgz#7883b56b20a002eeb790c113f8616ea8692ca795" + integrity sha512-5+C0X9mopI0+qxyQHzOPEi5v5rxNBQjxydPPiKMQSlX1RBIcJ8uTcqUPssQ9Mo8p6c1IKIWJUSqCj4jAmD0qVQ== + react-native-dotenv@^3.4.11: version "3.4.11" resolved "https://registry.yarnpkg.com/react-native-dotenv/-/react-native-dotenv-3.4.11.tgz#2e6c4eabd55d5f1bf109b3dd9141dadf9c55cdd4" @@ -12423,6 +12526,14 @@ react-native-fast-image@^8.6.3: version "6.0.0" resolved "git+https://github.com/hieuvp/react-native-fingerprint-scanner.git#3898597b031ebf4b2f685958b7de0a755bb8fb16" +react-native-fs@^2.20.0: + version "2.20.0" + resolved "https://registry.yarnpkg.com/react-native-fs/-/react-native-fs-2.20.0.tgz#05a9362b473bfc0910772c0acbb73a78dbc810f6" + integrity sha512-VkTBzs7fIDUiy/XajOSNk0XazFE9l+QlMAce7lGuebZcag5CnjszB+u4BdqzwaQOdcYb5wsJIsqq4kxInIRpJQ== + dependencies: + base-64 "^0.1.0" + utf8 "^3.0.0" + react-native-gesture-handler@~2.16.1: version "2.16.2" resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-2.16.2.tgz#032bd2a07334292d7f6cff1dc9d1ec928f72e26d" @@ -12434,11 +12545,39 @@ react-native-gesture-handler@~2.16.1: lodash "^4.17.21" prop-types "^15.7.2" +react-native-get-random-values@^1.11.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/react-native-get-random-values/-/react-native-get-random-values-1.11.0.tgz#1ca70d1271f4b08af92958803b89dccbda78728d" + integrity sha512-4BTbDbRmS7iPdhYLRcz3PGFIpFJBwNZg9g42iwa2P6FOv9vZj/xJc678RZXnLNZzd0qd7Q3CCF6Yd+CU2eoXKQ== + dependencies: + fast-base64-decode "^1.0.0" + +react-native-gifted-chat@^2.6.4: + version "2.6.4" + resolved "https://registry.yarnpkg.com/react-native-gifted-chat/-/react-native-gifted-chat-2.6.4.tgz#66dea64439cd4e63dd25cb4f27f340d36d07eec6" + integrity sha512-Ut31I1w6g4hG/iMyC1mVNVPZCNAUfijbJaEbRmKQnTPG+nAvYFje57OHr5coe9w+IBWsJKKoNvaI4h8w9Il5aQ== + dependencies: + "@expo/react-native-action-sheet" "^4.1.0" + "@types/lodash.isequal" "^4.5.8" + dayjs "^1.11.13" + lodash.isequal "^4.5.0" + prop-types "^15.8.1" + react-native-communications "^2.2.1" + react-native-iphone-x-helper "^1.3.1" + react-native-lightbox-v2 "^0.9.0" + react-native-parsed-text "^0.0.22" + uuid "^10.0.0" + react-native-gradle-plugin@^0.71.19: version "0.71.19" resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.19.tgz#3379e28341fcd189bc1f4691cefc84c1a4d7d232" integrity sha512-1dVk9NwhoyKHCSxcrM6vY6cxmojeATsBobDicX0ZKr7DgUF2cBQRTKsimQFvzH8XhOVXyH8p4HyDSZNIFI8OlQ== +react-native-iphone-x-helper@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.3.1.tgz#20c603e9a0e765fd6f97396638bdeb0e5a60b010" + integrity sha512-HOf0jzRnq2/aFUcdCJ9w9JGzN3gdEg0zFE4FyYlp4jtidqU03D5X7ZegGKfT1EWteR0gPBGp9ye5T5FvSWi9Yg== + react-native-json-tree@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/react-native-json-tree/-/react-native-json-tree-1.3.0.tgz#fe3d2fba1b26620b72175ac5b97dd7866b9e1bbb" @@ -12455,6 +12594,11 @@ react-native-level-fs@^3.0.1: level-filesystem "^1.0.1" levelup "^0.18.2" +react-native-lightbox-v2@^0.9.0: + version "0.9.2" + resolved "https://registry.yarnpkg.com/react-native-lightbox-v2/-/react-native-lightbox-v2-0.9.2.tgz#58e256ee18bdd2e270f228a911a76831251229e5" + integrity sha512-+8LwINeSWvPP69YAyWhiJQyaw4Gtu8X4EMvT3PEN615NrOeDaz8jOcIw73pzYJst3z4Z0vxvFB73iH16wCPXtw== + react-native-linear-gradient-text@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/react-native-linear-gradient-text/-/react-native-linear-gradient-text-1.2.8.tgz#b58761129f1491116737d9902f89ef44f632c6dd" @@ -12493,6 +12637,13 @@ react-native-pager-view@6.3.0: resolved "https://registry.yarnpkg.com/react-native-pager-view/-/react-native-pager-view-6.3.0.tgz#7e734e84b1692f877591335373f6fd92f0d67aa6" integrity sha512-ufJOoVa9pFL1J/yb4hpsCqp8n1qTlcF5VvwqvCacHX//D7hSeRscsiIXg1u1pXNWwllvACb+mqxec/3Uj2mxrA== +react-native-parsed-text@^0.0.22: + version "0.0.22" + resolved "https://registry.yarnpkg.com/react-native-parsed-text/-/react-native-parsed-text-0.0.22.tgz#a23c756eaa5d6724296814755085127f9072e5f5" + integrity sha512-hfD83RDXZf9Fvth3DowR7j65fMnlqM9PpxZBGWkzVcUTFtqe6/yPcIoIAgrJbKn6YmtzkivmhWE2MCE4JKBXrQ== + dependencies: + prop-types "^15.7.x" + react-native-permissions@^4.1.5: version "4.1.5" resolved "https://registry.yarnpkg.com/react-native-permissions/-/react-native-permissions-4.1.5.tgz#db4d1ddbf076570043f4fd4168f54bb6020aec92" @@ -12510,9 +12661,9 @@ react-native-quick-base64@^2.0.5: dependencies: base64-js "^1.5.1" -"react-native-quick-sqlite@github:margelo/react-native-quick-sqlite#99f34ebefa91698945f3ed26622e002bd79489e0": +"react-native-quick-sqlite@github:margelo/react-native-nitro-sqlite#99f34ebefa91698945f3ed26622e002bd79489e0": version "8.1.0" - resolved "https://codeload.github.com/margelo/react-native-quick-sqlite/tar.gz/99f34ebefa91698945f3ed26622e002bd79489e0" + resolved "https://codeload.github.com/margelo/react-native-nitro-sqlite/tar.gz/99f34ebefa91698945f3ed26622e002bd79489e0" react-native-randombytes@^3.6.1: version "3.6.1" @@ -12571,6 +12722,11 @@ react-native-size-matters@^0.4.2: resolved "https://registry.yarnpkg.com/react-native-size-matters/-/react-native-size-matters-0.4.2.tgz#4348bdd6fc47383f60326d58ad69870c998a5f9a" integrity sha512-DKE3f/sdcozd24oASgkP1iGg+YU3HoajRa5k3a4wkRzpiqREq8SGX12Y5zBgAt/8IivLQoTMYkyQu1/Giuy+zQ== +react-native-sound@^0.11.2: + version "0.11.2" + resolved "https://registry.yarnpkg.com/react-native-sound/-/react-native-sound-0.11.2.tgz#e542dc5b9e16ab4b3ac7e6eaddb1fc8d98da9038" + integrity sha512-LmGc8lgOK3qecYMVQpyHvww/C+wgT6sWeMpVbOe4NCRGC2yKd4fo4U0KBUo9PO7AqKESO3I/2GZg1/C0+bwiiA== + react-native-svg-transformer@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/react-native-svg-transformer/-/react-native-svg-transformer-1.5.0.tgz#75ae4dbdc4b1a1508e75282d54d66900ee5fe3c2" @@ -12852,7 +13008,7 @@ regenerator-transform@^0.15.2: dependencies: "@babel/runtime" "^7.8.4" -regexp.prototype.flags@^1.5.1, regexp.prototype.flags@^1.5.2: +regexp.prototype.flags@^1.5.1, regexp.prototype.flags@^1.5.2, regexp.prototype.flags@^1.5.3: version "1.5.3" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz#b3ae40b1d2499b8350ab2c3fe6ef3845d3a96f42" integrity sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ== @@ -12863,14 +13019,14 @@ regexp.prototype.flags@^1.5.1, regexp.prototype.flags@^1.5.2: set-function-name "^2.0.2" regexpu-core@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-6.1.1.tgz#b469b245594cb2d088ceebc6369dceb8c00becac" - integrity sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw== + version "6.2.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-6.2.0.tgz#0e5190d79e542bf294955dccabae04d3c7d53826" + integrity sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA== dependencies: regenerate "^1.4.2" regenerate-unicode-properties "^10.2.0" regjsgen "^0.8.0" - regjsparser "^0.11.0" + regjsparser "^0.12.0" unicode-match-property-ecmascript "^2.0.0" unicode-match-property-value-ecmascript "^2.1.0" @@ -12879,10 +13035,10 @@ regjsgen@^0.8.0: resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.8.0.tgz#df23ff26e0c5b300a6470cad160a9d090c3a37ab" integrity sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q== -regjsparser@^0.11.0: - version "0.11.2" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.11.2.tgz#7404ad42be00226d72bcf1f003f1f441861913d8" - integrity sha512-3OGZZ4HoLJkkAZx/48mTXJNlmqTGOzc0o9OWQPuWpkOlXXPbyN6OafCcoXUnBqE2D3f/T5L+pWc1kdEmnfnRsA== +regjsparser@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.12.0.tgz#0e846df6c6530586429377de56e0475583b088dc" + integrity sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ== dependencies: jsesc "~3.0.2" @@ -12947,6 +13103,11 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== +resolve-workspace-root@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-workspace-root/-/resolve-workspace-root-2.0.0.tgz#a0098daa0067cd0efa6eb525c57c8fb4a61e78f8" + integrity sha512-IsaBUZETJD5WsI11Wt8PKHwaIe45or6pwNc8yflvLJ4DWtImK9kuLoH5kUva/2Mmx/RdIyr4aONNSa2v9LTJsw== + resolve.exports@^2.0.0, resolve.exports@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" @@ -13603,16 +13764,7 @@ string-range@~1.2, string-range@~1.2.1: resolved "https://registry.yarnpkg.com/string-range/-/string-range-1.2.2.tgz#a893ed347e72299bc83befbbf2a692a8d239d5dd" integrity sha512-tYft6IFi8SjplJpxCUxyqisD3b+R2CSkomrtJYCkvuf1KuCAWgz7YXt4O0jip7efpfCemwHEzTEAO8EuOYgh3w== -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - 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: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -13703,7 +13855,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -13717,13 +13869,6 @@ 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.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -14441,9 +14586,14 @@ use-latest-callback@^0.1.5: integrity sha512-8nhb73STSD/z3GTHklvNjL8F9wMOo0bj0AFnulpIYuFTm6aQlT3ZcNbXF2YurKImIY8+kpSFSDHZZyQmurGrhw== use-latest-callback@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/use-latest-callback/-/use-latest-callback-0.2.1.tgz#4d4e6a9e4817b13142834850dcfa8d24ca4569cf" - integrity sha512-QWlq8Is8BGWBf883QOEQP5HWYX/kMI+JTbJ5rdtvJLmXTIh9XoHIO3PQcmQl8BU44VKxow1kbQUHa6mQSMALDQ== + version "0.2.3" + resolved "https://registry.yarnpkg.com/use-latest-callback/-/use-latest-callback-0.2.3.tgz#2d644d3063040b9bc2d4c55bb525a13ae3de9e16" + integrity sha512-7vI3fBuyRcP91pazVboc4qu+6ZqM8izPWX9k7cRnT8hbD5svslcknsh3S9BUhaK11OmgTV4oWZZVSeQAiV53SQ== + +utf8@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" @@ -14473,6 +14623,11 @@ utils-merge@1.0.1, utils-merge@^1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== +uuid@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294" + integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ== + uuid@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" @@ -14519,6 +14674,11 @@ validate-npm-package-name@^3.0.0: dependencies: builtins "^1.0.3" +validate-npm-package-name@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz#a316573e9b49f3ccd90dbb6eb52b3f06c6d604e8" + integrity sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ== + validator@^13.7.0, validator@^13.9.0: version "13.12.0" resolved "https://registry.yarnpkg.com/validator/-/validator-13.12.0.tgz#7d78e76ba85504da3fee4fd1922b385914d4b35f" @@ -14786,7 +14946,7 @@ word-wrap@^1.2.5, word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -14804,15 +14964,6 @@ 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.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - 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.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" @@ -14861,7 +15012,7 @@ ws@^7, ws@^7.5.10: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== -ws@^8.11.0, ws@^8.12.1: +ws@^8.11.0, ws@^8.12.1, ws@^8.18.0: version "8.18.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== @@ -14961,9 +15112,9 @@ yallist@^4.0.0: integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yaml@^2.2.1, yaml@^2.2.2, yaml@^2.4.5: - version "2.6.0" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.6.0.tgz#14059ad9d0b1680d0f04d3a60fe00f3a857303c3" - integrity sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ== + version "2.6.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.6.1.tgz#42f2b1ba89203f374609572d5349fb8686500773" + integrity sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg== yargs-parser@^18.1.2: version "18.1.3"