From 690d576d54180afe6c2314e855bf63af33759a11 Mon Sep 17 00:00:00 2001 From: Justin Hernandez Date: Wed, 8 Apr 2026 09:42:12 -0700 Subject: [PATCH 1/6] Upgrade Wave 2 mobile app dependencies (React Navigation, Tamagui, WalletConnect, Lottie, XState) (#1941) * Upgrade wave 2 mobile app dependencies * fix pipelines --- app/ios/Podfile.lock | 46 +- app/package.json | 26 +- package.json | 4 +- yarn.lock | 2973 ++++++++++++++++++++---------------------- 4 files changed, 1485 insertions(+), 1564 deletions(-) diff --git a/app/ios/Podfile.lock b/app/ios/Podfile.lock index 061672330..5768dfd30 100644 --- a/app/ios/Podfile.lock +++ b/app/ios/Podfile.lock @@ -142,12 +142,12 @@ PODS: - hermes-engine (0.77.0): - hermes-engine/Pre-built (= 0.77.0) - hermes-engine/Pre-built (0.77.0) - - lottie-ios (4.5.0) - - lottie-react-native (7.2.2): + - lottie-ios (4.6.0) + - lottie-react-native (7.3.6): - DoubleConversion - glog - hermes-engine - - lottie-ios (= 4.5.0) + - lottie-ios (= 4.6.0) - RCT-Folly (= 2024.11.18.00) - RCTRequired - RCTTypeSafety @@ -1428,7 +1428,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - react-native-compat (2.23.1): + - react-native-compat (2.23.9): - DoubleConversion - glog - hermes-engine @@ -1449,13 +1449,14 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga + - YttriumWrapper (= 0.10.50) - react-native-get-random-values (1.11.0): - React-Core - react-native-netinfo (11.4.1): - React-Core - react-native-nfc-manager (3.17.2): - React-Core - - react-native-passkey (3.3.2): + - react-native-passkey (3.3.3): - DoubleConversion - glog - hermes-engine @@ -1476,7 +1477,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - react-native-safe-area-context (5.6.2): + - react-native-safe-area-context (5.7.0): - DoubleConversion - glog - hermes-engine @@ -1489,8 +1490,8 @@ PODS: - React-featureflags - React-graphics - React-ImageManager - - react-native-safe-area-context/common (= 5.6.2) - - react-native-safe-area-context/fabric (= 5.6.2) + - react-native-safe-area-context/common (= 5.7.0) + - react-native-safe-area-context/fabric (= 5.7.0) - React-NativeModulesApple - React-RCTFabric - React-rendererdebug @@ -1499,7 +1500,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - react-native-safe-area-context/common (5.6.2): + - react-native-safe-area-context/common (5.7.0): - DoubleConversion - glog - hermes-engine @@ -1520,7 +1521,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - react-native-safe-area-context/fabric (5.6.2): + - react-native-safe-area-context/fabric (5.7.0): - DoubleConversion - glog - hermes-engine @@ -1955,7 +1956,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNInAppBrowser (3.7.0): + - RNInAppBrowser (3.7.1): - React-Core - RNKeychain (10.0.0): - DoubleConversion @@ -2131,7 +2132,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - SdkReactNative (3.2.7): + - SdkReactNative (3.2.8): - DiditSDK (~> 3.2) - DoubleConversion - glog @@ -2153,7 +2154,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - segment-analytics-react-native (2.21.4): + - segment-analytics-react-native (2.22.0): - React-Core - sovran-react-native - SelfNFCPassportReader (2.1.1): @@ -2166,6 +2167,7 @@ PODS: - SwiftQRScanner (1.1.6) - SwiftyTesseract (3.1.3) - Yoga (0.0.0) + - YttriumWrapper (0.10.50) DEPENDENCIES: - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`) @@ -2315,6 +2317,7 @@ SPEC REPOS: - Sentry - SocketRocket - SwiftyTesseract + - YttriumWrapper EXTERNAL SOURCES: boost: @@ -2586,8 +2589,8 @@ SPEC CHECKSUMS: GTMAppAuth: 217a876b249c3c585a54fd6f73e6b58c4f5c4238 GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 hermes-engine: 1f783c3d53940aed0d2c84586f0b7a85ab7827ef - lottie-ios: a881093fab623c467d3bce374367755c272bdd59 - lottie-react-native: 6cb05b7b4ea463afe657e3b46784f067858e1a5d + lottie-ios: 8f959969761e9c45d70353667d00af0e5b9cadb3 + lottie-react-native: d73a798e26348851f0ef349df3d40f2e27fd239b Mixpanel-swift: e9bef28a9648faff384d5ba6f48ecc2787eb24c0 nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 OpenSSL-Universal: 84efb8a29841f2764ac5403e0c4119a28b713346 @@ -2627,12 +2630,12 @@ SPEC CHECKSUMS: react-native-biometrics: 43ed5b828646a7862dbc7945556446be00798e7d react-native-blur: 745703f35133ed6a1210d4bbff358a631911f002 react-native-cloud-storage: 796c793dc354bb49f9df27ca25eed0f79a15549e - react-native-compat: 10b5f906b469268eaceca83ea2393c177f1ce18a + react-native-compat: ad6a412f03632d1c4d97d47e56b22d0597116085 react-native-get-random-values: d16467cf726c618e9c7a8c3c39c31faa2244bbba react-native-netinfo: cec9c4e86083cb5b6aba0e0711f563e2fbbff187 react-native-nfc-manager: c8891e460b4943b695d63f7f4effc6345bbefc83 - react-native-passkey: 8818f842d1b80e45c06e906a5c85964719782bf5 - react-native-safe-area-context: 5b5d3eb6ec9ef848f16c064a4eab4a92c7d7895e + react-native-passkey: 3c07a93dc2608929d794b7298c0d29d01b379f01 + react-native-safe-area-context: bf457bf5b3a617e9a3930d1ecd954a3335303cc7 react-native-sqlite-storage: 0c84826214baaa498796c7e46a5ccc9a82e114ed react-native-webview: 05734d99f1e422c5ddfeefbd083d53abd78fccb1 React-nativeconfig: 334c9961d74ddd3bc203afb92ee574ed01c7c755 @@ -2673,15 +2676,15 @@ SPEC CHECKSUMS: RNFBRemoteConfig: 8d3675f18c052483ce294bb97b857428467fb41e RNGestureHandler: 36aca36e4ef19f55dbf97239199d00fd58494e34 RNGoogleSignin: 60c3f470558dbff0ae54f2f164ef82a89d3eb561 - RNInAppBrowser: 6d3eb68d471b9834335c664704719b8be1bfdb20 + RNInAppBrowser: 904d24dc75e8e6c6c98a3160329192608946f9df RNKeychain: 35beaa17938f7d8e4990d8a38fad5f8a748fc47c RNLocalize: aa57bee9fcd545b98ce773a8e2404f9a36115b4a RNReactNativeHapticFeedback: eb5395b503c7a8f10de5e6722ef8afd3c61bc4f5 RNScreens: b0811b109e1a0b8b579f3348018e177bee374840 RNSentry: 98ab9f6a16c9596e36565ccf1a5871323f334766 RNSVG: d926926b169d8b81eb06aeb69734076e1dd566a3 - SdkReactNative: 7f65ca10a978bf9440730a537d54511e68ed50b4 - segment-analytics-react-native: 0eae155b0e9fa560fa6b17d78941df64537c35b7 + SdkReactNative: 34ba85b3f3060892c548b7415b06f0bd66fcba1c + segment-analytics-react-native: 8ab9c49df1859bbd6be93cf90a91ade17f20a0aa SelfNFCPassportReader: 8b53f9d483e0dd1f1a275953e3dc6dfc733694c5 Sentry: 59993bffde4a1ac297ba6d268dc4bbce068d7c1b SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 @@ -2689,6 +2692,7 @@ SPEC CHECKSUMS: SwiftQRScanner: e85a25f9b843e9231dab89a96e441472fe54a724 SwiftyTesseract: 1f3d96668ae92dc2208d9842c8a59bea9fad2cbb Yoga: c34725819ab0a5962e85455b9e56679b306910ee + YttriumWrapper: d7f63336830536f1da41b745ed8bacedb04228c4 PODFILE CHECKSUM: 0b99fae2ec87b0be3e6d9b3fd87360fe0a84b25f diff --git a/app/package.json b/app/package.json index 381e562e0..93ca5ffa5 100644 --- a/app/package.json +++ b/app/package.json @@ -101,8 +101,8 @@ "@react-native-firebase/messaging": "^21.14.0", "@react-native-firebase/remote-config": "^21.14.0", "@react-native-google-signin/google-signin": "^16.1.2", - "@react-navigation/native": "^7.0.14", - "@react-navigation/native-stack": "^7.2.0", + "@react-navigation/native": "^7.2.2", + "@react-navigation/native-stack": "^7.14.10", "@robinbobin/react-native-google-drive-api-wrapper": "^2.2.6", "@segment/analytics-react-native": "^2.22.0", "@segment/sovran-react-native": "^1.1.3", @@ -112,16 +112,16 @@ "@sentry/react": "^9.32.0", "@sentry/react-native": "7.0.0", "@stablelib/cbor": "^2.0.4", - "@tamagui/animations-css": "1.126.14", - "@tamagui/animations-react-native": "1.126.14", - "@tamagui/config": "1.126.14", - "@tamagui/lucide-icons": "1.126.14", - "@tamagui/toast": "1.126.14", + "@tamagui/animations-css": "1.144.4", + "@tamagui/animations-react-native": "1.144.4", + "@tamagui/config": "1.144.4", + "@tamagui/lucide-icons": "1.144.4", + "@tamagui/toast": "1.144.4", "@turnkey/api-key-stamper": "^0.5.0", "@turnkey/core": "1.7.0", "@turnkey/encoding": "^0.6.0", "@turnkey/react-native-wallet-kit": "1.1.5", - "@walletconnect/react-native-compat": "^2.23.0", + "@walletconnect/react-native-compat": "^2.23.9", "@xstate/react": "^5.0.3", "asn1js": "^3.0.7", "axios": "^1.14.0", @@ -136,7 +136,7 @@ "js-sha256": "^0.11.1", "js-sha512": "^0.9.0", "lottie-react": "^2.4.1", - "lottie-react-native": "7.2.2", + "lottie-react-native": "7.3.6", "node-forge": "^1.4.0", "pkijs": "^3.4.0", "poseidon-lite": "^0.2.0", @@ -172,9 +172,9 @@ "react-native-webview": "13.16.0", "react-qr-barcode-scanner": "^2.1.25", "socket.io-client": "^4.8.3", - "tamagui": "1.126.14", + "tamagui": "1.144.4", "uuid": "^11.1.0", - "xstate": "^5.20.2", + "xstate": "^5.30.0", "zustand": "^4.5.2" }, "devDependencies": { @@ -192,8 +192,8 @@ "@react-native/gradle-plugin": "0.77.0", "@react-native/metro-config": "0.77.0", "@react-native/typescript-config": "0.77.0", - "@tamagui/types": "1.126.14", - "@tamagui/vite-plugin": "1.126.14", + "@tamagui/types": "1.144.4", + "@tamagui/vite-plugin": "1.144.4", "@testing-library/react-native": "^13.3.3", "@tsconfig/react-native": "^3.0.9", "@types/dompurify": "^3.2.0", diff --git a/package.json b/package.json index bbe7c1b98..f373a4a77 100644 --- a/package.json +++ b/package.json @@ -58,8 +58,8 @@ "@noble/curves": "1.9.7", "@noble/hashes": "1.8.0", "@swc/core": "1.7.36", - "@tamagui/animations-react-native": "1.126.14", - "@tamagui/toast": "1.126.14", + "@tamagui/animations-react-native": "1.144.4", + "@tamagui/toast": "1.144.4", "@types/node": "^22.18.3", "@types/react": "^18.3.4", "@types/react-dom": "^18.3.0", diff --git a/yarn.lock b/yarn.lock index 940e98f25..e8a117ef0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6179,6 +6179,15 @@ __metadata: languageName: node linkType: hard +"@floating-ui/core@npm:^1.7.5": + version: 1.7.5 + resolution: "@floating-ui/core@npm:1.7.5" + dependencies: + "@floating-ui/utils": "npm:^0.2.11" + checksum: 10c0/f9c52205e198b231d63a387b09c659aab08c46a1899e0b0bbe147b8b4f048b546f15ba17cb5d2a471da9534f1883d979425e13e5c4ceee67be63e4b0abd4db5d + languageName: node + linkType: hard + "@floating-ui/dom@npm:^1.7.4": version: 1.7.4 resolution: "@floating-ui/dom@npm:1.7.4" @@ -6189,7 +6198,17 @@ __metadata: languageName: node linkType: hard -"@floating-ui/react-dom@npm:^2.1.2, @floating-ui/react-dom@npm:^2.1.6": +"@floating-ui/dom@npm:^1.7.6": + version: 1.7.6 + resolution: "@floating-ui/dom@npm:1.7.6" + dependencies: + "@floating-ui/core": "npm:^1.7.5" + "@floating-ui/utils": "npm:^0.2.11" + checksum: 10c0/5c098e0d7b58c9bc769f276cca1766994c2c9c70c92d091a61bba8b3e9be53c011e0a79a8457fc2fb2f3d91697a26eb52e0a4962ef936dc963b45f58613c212f + languageName: node + linkType: hard + +"@floating-ui/react-dom@npm:^2.1.6": version: 2.1.6 resolution: "@floating-ui/react-dom@npm:2.1.6" dependencies: @@ -6201,6 +6220,18 @@ __metadata: languageName: node linkType: hard +"@floating-ui/react-dom@npm:^2.1.8": + version: 2.1.8 + resolution: "@floating-ui/react-dom@npm:2.1.8" + dependencies: + "@floating-ui/dom": "npm:^1.7.6" + peerDependencies: + react: ">=16.8.0" + react-dom: ">=16.8.0" + checksum: 10c0/26260ca4bb23b57c73b824062505abf977a008ce6e0463bdacca74f7e49853c4cd1d2bbf1a77c6caa17fa37dfffda2c6c4cd07a8737ebd7474aaff7818401d75 + languageName: node + linkType: hard + "@floating-ui/react-native@npm:^0.10.7": version: 0.10.7 resolution: "@floating-ui/react-native@npm:0.10.7" @@ -6213,17 +6244,17 @@ __metadata: languageName: node linkType: hard -"@floating-ui/react@npm:^0.27.4": - version: 0.27.16 - resolution: "@floating-ui/react@npm:0.27.16" +"@floating-ui/react@npm:^0.27.16": + version: 0.27.19 + resolution: "@floating-ui/react@npm:0.27.19" dependencies: - "@floating-ui/react-dom": "npm:^2.1.6" - "@floating-ui/utils": "npm:^0.2.10" + "@floating-ui/react-dom": "npm:^2.1.8" + "@floating-ui/utils": "npm:^0.2.11" tabbable: "npm:^6.0.0" peerDependencies: react: ">=17.0.0" react-dom: ">=17.0.0" - checksum: 10c0/a026266d8875e69de1ac1e1a00588660c8ee299c1e7d067c5c5fd1d69a46fd10acff5dd6cb66c3fe40a3347b443234309ba95f5b33d49059d0cda121f558f566 + checksum: 10c0/2a2cdfd3e67e0606833b63f922ad2a9037974f22b944e1cb8c0991b4c40450f8413d69745c0bbf4646e5ba283747f60d2fdc9a8d289b68b24448e59d81a3a96d languageName: node linkType: hard @@ -6234,6 +6265,13 @@ __metadata: languageName: node linkType: hard +"@floating-ui/utils@npm:^0.2.11": + version: 0.2.11 + resolution: "@floating-ui/utils@npm:0.2.11" + checksum: 10c0/f4bcea1559bdbb721ecc8e8ead423ac58d6a5b6e70b602cf0810ba6ad4ed1c77211b207faa88b278a9042f0c743133de08a203ed6741c1b6443423332884d5b3 + languageName: node + linkType: hard + "@grpc/grpc-js@npm:~1.9.0": version: 1.9.15 resolution: "@grpc/grpc-js@npm:1.9.15" @@ -7758,7 +7796,7 @@ __metadata: languageName: node linkType: hard -"@napi-rs/nice@npm:^1.0.4": +"@napi-rs/nice@npm:^1.0.1, @napi-rs/nice@npm:^1.0.4": version: 1.1.1 resolution: "@napi-rs/nice@npm:1.1.1" dependencies: @@ -8679,62 +8717,6 @@ __metadata: languageName: node linkType: hard -"@oxc-transform/binding-darwin-arm64@npm:0.47.1": - version: 0.47.1 - resolution: "@oxc-transform/binding-darwin-arm64@npm:0.47.1" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"@oxc-transform/binding-darwin-x64@npm:0.47.1": - version: 0.47.1 - resolution: "@oxc-transform/binding-darwin-x64@npm:0.47.1" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"@oxc-transform/binding-linux-arm64-gnu@npm:0.47.1": - version: 0.47.1 - resolution: "@oxc-transform/binding-linux-arm64-gnu@npm:0.47.1" - conditions: os=linux & cpu=arm64 & libc=glibc - languageName: node - linkType: hard - -"@oxc-transform/binding-linux-arm64-musl@npm:0.47.1": - version: 0.47.1 - resolution: "@oxc-transform/binding-linux-arm64-musl@npm:0.47.1" - conditions: os=linux & cpu=arm64 & libc=musl - languageName: node - linkType: hard - -"@oxc-transform/binding-linux-x64-gnu@npm:0.47.1": - version: 0.47.1 - resolution: "@oxc-transform/binding-linux-x64-gnu@npm:0.47.1" - conditions: os=linux & cpu=x64 & libc=glibc - languageName: node - linkType: hard - -"@oxc-transform/binding-linux-x64-musl@npm:0.47.1": - version: 0.47.1 - resolution: "@oxc-transform/binding-linux-x64-musl@npm:0.47.1" - conditions: os=linux & cpu=x64 & libc=musl - languageName: node - linkType: hard - -"@oxc-transform/binding-win32-arm64-msvc@npm:0.47.1": - version: 0.47.1 - resolution: "@oxc-transform/binding-win32-arm64-msvc@npm:0.47.1" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - -"@oxc-transform/binding-win32-x64-msvc@npm:0.47.1": - version: 0.47.1 - resolution: "@oxc-transform/binding-win32-x64-msvc@npm:0.47.1" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - "@parcel/watcher-android-arm64@npm:2.5.1": version: 2.5.1 resolution: "@parcel/watcher-android-arm64@npm:2.5.1" @@ -10037,9 +10019,9 @@ __metadata: languageName: node linkType: hard -"@react-navigation/core@npm:^7.13.7": - version: 7.13.7 - resolution: "@react-navigation/core@npm:7.13.7" +"@react-navigation/core@npm:^7.17.2": + version: 7.17.2 + resolution: "@react-navigation/core@npm:7.17.2" dependencies: "@react-navigation/routers": "npm:^7.5.3" escape-string-regexp: "npm:^4.0.0" @@ -10051,53 +10033,53 @@ __metadata: use-sync-external-store: "npm:^1.5.0" peerDependencies: react: ">= 18.2.0" - checksum: 10c0/6d33e97a4fb72ffbb5fd135afb418b8d3411abbdbe7ce0fde026fcb0bc0ce7c96e8995a24c77314d6e5e4b689ba3b0311805cb619eedbafc2011aca5e26591fa + checksum: 10c0/df1889769e90f85b71605070818b22bd9967f5fd0572187d31e6a17386339336c8890b9c7ba505854382426cb3b5be8d03ea4684da4ad5be2cdae828302b98a0 languageName: node linkType: hard -"@react-navigation/elements@npm:^2.9.3": - version: 2.9.3 - resolution: "@react-navigation/elements@npm:2.9.3" +"@react-navigation/elements@npm:^2.9.14": + version: 2.9.14 + resolution: "@react-navigation/elements@npm:2.9.14" dependencies: color: "npm:^4.2.3" use-latest-callback: "npm:^0.2.4" use-sync-external-store: "npm:^1.5.0" peerDependencies: "@react-native-masked-view/masked-view": ">= 0.2.0" - "@react-navigation/native": ^7.1.26 + "@react-navigation/native": ^7.2.2 react: ">= 18.2.0" react-native: "*" react-native-safe-area-context: ">= 4.0.0" peerDependenciesMeta: "@react-native-masked-view/masked-view": optional: true - checksum: 10c0/50c30366507ca420e65b1a232edca8d64fbac1420d90d0a9aa16ec84d8c63719b9c626a24095943fd63283b4116c0ec888f6ccc6cb2317a4cba4d1b1c2c5021a + checksum: 10c0/c2602f5be41caaad8ea81a9bdfde654d4a89d83c627a910f5d166bd8cac2700399fce805f6bf15faaf06c2bbc776325269bc0ec1fbbe917b615e17a0a5d371e1 languageName: node linkType: hard -"@react-navigation/native-stack@npm:^7.2.0": - version: 7.9.0 - resolution: "@react-navigation/native-stack@npm:7.9.0" +"@react-navigation/native-stack@npm:^7.14.10": + version: 7.14.10 + resolution: "@react-navigation/native-stack@npm:7.14.10" dependencies: - "@react-navigation/elements": "npm:^2.9.3" + "@react-navigation/elements": "npm:^2.9.14" color: "npm:^4.2.3" sf-symbols-typescript: "npm:^2.1.0" warn-once: "npm:^0.1.1" peerDependencies: - "@react-navigation/native": ^7.1.26 + "@react-navigation/native": ^7.2.2 react: ">= 18.2.0" react-native: "*" react-native-safe-area-context: ">= 4.0.0" react-native-screens: ">= 4.0.0" - checksum: 10c0/45e039181e96a1bd41eee1946af00c729b7781611afde977cd28cd00fdcbf6577458728ef1984b9838cb79cad60b482c6bb227dc1bfe41d789065709354a63d0 + checksum: 10c0/0ec90836475a9d3988d642504868589d0e00bc9a4955e81806d942a0614fe5081f684c83e4b493c07bdf6a61b8676cfca41a5a374cf84bf76289fdb24cc8cd7b languageName: node linkType: hard -"@react-navigation/native@npm:^7.0.14": - version: 7.1.26 - resolution: "@react-navigation/native@npm:7.1.26" +"@react-navigation/native@npm:^7.2.2": + version: 7.2.2 + resolution: "@react-navigation/native@npm:7.2.2" dependencies: - "@react-navigation/core": "npm:^7.13.7" + "@react-navigation/core": "npm:^7.17.2" escape-string-regexp: "npm:^4.0.0" fast-deep-equal: "npm:^3.1.3" nanoid: "npm:^3.3.11" @@ -10105,7 +10087,7 @@ __metadata: peerDependencies: react: ">= 18.2.0" react-native: "*" - checksum: 10c0/7aa1e3ff40b36fb13f615412465d48eaa115f95c9b4278c67c616f8aa3d86bab68656b2528827307dbdba1cd2f30b3614837a042cc19b4316baf78ec0ff3f4c8 + checksum: 10c0/3ca6e742da2ed4110b81fc008536ca62f07cdf49b368e9b7f73cbc25ad86603f87f14d08492bf1de2647ca6f4cf7141bec4fa3cd76961fa2fa22f83d0f805e83 languageName: node linkType: hard @@ -11189,8 +11171,8 @@ __metadata: "@react-native/gradle-plugin": "npm:0.77.0" "@react-native/metro-config": "npm:0.77.0" "@react-native/typescript-config": "npm:0.77.0" - "@react-navigation/native": "npm:^7.0.14" - "@react-navigation/native-stack": "npm:^7.2.0" + "@react-navigation/native": "npm:^7.2.2" + "@react-navigation/native-stack": "npm:^7.14.10" "@robinbobin/react-native-google-drive-api-wrapper": "npm:^2.2.6" "@segment/analytics-react-native": "npm:^2.22.0" "@segment/sovran-react-native": "npm:^1.1.3" @@ -11200,13 +11182,13 @@ __metadata: "@sentry/react": "npm:^9.32.0" "@sentry/react-native": "npm:7.0.0" "@stablelib/cbor": "npm:^2.0.4" - "@tamagui/animations-css": "npm:1.126.14" - "@tamagui/animations-react-native": "npm:1.126.14" - "@tamagui/config": "npm:1.126.14" - "@tamagui/lucide-icons": "npm:1.126.14" - "@tamagui/toast": "npm:1.126.14" - "@tamagui/types": "npm:1.126.14" - "@tamagui/vite-plugin": "npm:1.126.14" + "@tamagui/animations-css": "npm:1.144.4" + "@tamagui/animations-react-native": "npm:1.144.4" + "@tamagui/config": "npm:1.144.4" + "@tamagui/lucide-icons": "npm:1.144.4" + "@tamagui/toast": "npm:1.144.4" + "@tamagui/types": "npm:1.144.4" + "@tamagui/vite-plugin": "npm:1.144.4" "@testing-library/react-native": "npm:^13.3.3" "@tsconfig/react-native": "npm:^3.0.9" "@turnkey/api-key-stamper": "npm:^0.5.0" @@ -11226,7 +11208,7 @@ __metadata: "@typescript-eslint/eslint-plugin": "npm:^8.39.0" "@typescript-eslint/parser": "npm:^8.39.0" "@vitejs/plugin-react-swc": "npm:^4.3.0" - "@walletconnect/react-native-compat": "npm:^2.23.0" + "@walletconnect/react-native-compat": "npm:^2.23.9" "@xstate/react": "npm:^5.0.3" asn1js: "npm:^3.0.7" axios: "npm:^1.14.0" @@ -11257,7 +11239,7 @@ __metadata: js-sha256: "npm:^0.11.1" js-sha512: "npm:^0.9.0" lottie-react: "npm:^2.4.1" - lottie-react-native: "npm:7.2.2" + lottie-react-native: "npm:7.3.6" node-forge: "npm:^1.4.0" pkijs: "npm:^3.4.0" poseidon-lite: "npm:^0.2.0" @@ -11299,13 +11281,13 @@ __metadata: rollup-plugin-visualizer: "npm:^6.0.5" socket.io-client: "npm:^4.8.3" stream-browserify: "npm:^3.0.0" - tamagui: "npm:1.126.14" + tamagui: "npm:1.144.4" ts-morph: "npm:^22.0.0" typescript: "npm:^5.9.3" uuid: "npm:^11.1.0" vite: "npm:^7.3.1" vite-plugin-svgr: "npm:^4.5.0" - xstate: "npm:^5.20.2" + xstate: "npm:^5.30.0" zustand: "npm:^4.5.2" languageName: unknown linkType: soft @@ -14331,15 +14313,6 @@ __metadata: languageName: node linkType: hard -"@swc/helpers@npm:^0.5.11": - version: 0.5.18 - resolution: "@swc/helpers@npm:0.5.18" - dependencies: - tslib: "npm:^2.8.0" - checksum: 10c0/cb32d72e32f775c30287bffbcf61c89ea3a963608cb3a4a675a3f9af545b8b3ab0bc9930432a5520a7307daaa87538158e253584ae1cf39f3e7e6e83408a2d51 - languageName: node - linkType: hard - "@swc/types@npm:^0.1.13": version: 0.1.25 resolution: "@swc/types@npm:0.1.25" @@ -14358,1114 +14331,1156 @@ __metadata: languageName: node linkType: hard -"@tamagui/accordion@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/accordion@npm:1.126.14" +"@tamagui/accordion@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/accordion@npm:1.144.4" dependencies: - "@tamagui/collapsible": "npm:1.126.14" - "@tamagui/collection": "npm:1.126.14" - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/constants": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - "@tamagui/create-context": "npm:1.126.14" - "@tamagui/polyfill-dev": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/text": "npm:1.126.14" - "@tamagui/use-controllable-state": "npm:1.126.14" + "@tamagui/collapsible": "npm:1.144.4" + "@tamagui/collection": "npm:1.144.4" + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/polyfill-dev": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/text": "npm:1.144.4" + "@tamagui/use-controllable-state": "npm:1.144.4" + "@tamagui/use-direction": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/08bf2ab2ca5ff5420991ad47aa2db956ff86112159aa36c671757be9c6597994de03f82bdea569e8fd02e6449e2b03fdf5ddb4dc44a1955d557dca949ba7c6be + checksum: 10c0/f73ce27634a43b34ed3c19e97e60d990a0e9746768a4d97ff13cdf5ccdd9122e65de91f2cb2e5bf8faadf1049a1c887f497e1e64191b24a324f5eedd8d224772 languageName: node linkType: hard -"@tamagui/adapt@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/adapt@npm:1.126.14" +"@tamagui/adapt@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/adapt@npm:1.144.4" dependencies: - "@tamagui/constants": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/portal": "npm:1.126.14" - "@tamagui/z-index-stack": "npm:1.126.14" - checksum: 10c0/d4fc8e7ea6e7c42d5498c4611d3990e50cecf234a285a8207e950433f0b3f583265552263fa42a3a9f0a120e85c413bfe9b4f7c69050d3b54a59516c423f6705 - languageName: node - linkType: hard - -"@tamagui/alert-dialog@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/alert-dialog@npm:1.126.14" - dependencies: - "@tamagui/animate-presence": "npm:1.126.14" - "@tamagui/aria-hidden": "npm:1.126.14" - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/constants": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - "@tamagui/create-context": "npm:1.126.14" - "@tamagui/dialog": "npm:1.126.14" - "@tamagui/dismissable": "npm:1.126.14" - "@tamagui/focus-scope": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/polyfill-dev": "npm:1.126.14" - "@tamagui/popper": "npm:1.126.14" - "@tamagui/portal": "npm:1.126.14" - "@tamagui/remove-scroll": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/text": "npm:1.126.14" - "@tamagui/use-controllable-state": "npm:1.126.14" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/portal": "npm:1.144.4" + "@tamagui/z-index-stack": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/05a0927ab23d438ec5f4b5076b04d6dda5deef53de653d9eefff02b16fbf2cc576a5effa2b57e86e2f5b6bf456106bff65766ad9342601f939791c0b37820d2e + checksum: 10c0/124fb7a88dca3493ca08681b9f33898fc3e838db9a62d184136723a244de59d1d4ca99e413cab8cb70faf5b9a3c721662964be81684252332aefd166faf4ec80 languageName: node linkType: hard -"@tamagui/animate-presence@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/animate-presence@npm:1.126.14" +"@tamagui/alert-dialog@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/alert-dialog@npm:1.144.4" dependencies: - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/use-constant": "npm:1.126.14" - "@tamagui/use-force-update": "npm:1.126.14" - "@tamagui/use-presence": "npm:1.126.14" - "@tamagui/web": "npm:1.126.14" - checksum: 10c0/b5f2d7d8bdbfdf552db0c9558fc7c45667b55f4acb3d73e1d69767f39576a2b022a318909a75d1a7a2326d272da0ab2ce185fb2e8d538b60832f3421d4e0b8f9 + "@tamagui/animate-presence": "npm:1.144.4" + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/dialog": "npm:1.144.4" + "@tamagui/dismissable": "npm:1.144.4" + "@tamagui/focus-scope": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/polyfill-dev": "npm:1.144.4" + "@tamagui/popper": "npm:1.144.4" + "@tamagui/portal": "npm:1.144.4" + "@tamagui/remove-scroll": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/text": "npm:1.144.4" + "@tamagui/use-controllable-state": "npm:1.144.4" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10c0/e50e6a122e366235eb3a75ca959a17fe20f23e496d48c82761c33ee1cef0c1103595d477feec9128162c64f1c8b949428a6de3f67393a0cc079a1b05e8001dbd languageName: node linkType: hard -"@tamagui/animate@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/animate@npm:1.126.14" +"@tamagui/animate-presence@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/animate-presence@npm:1.144.4" dependencies: - "@tamagui/animate-presence": "npm:1.126.14" - checksum: 10c0/bbcedc44087fa04512fda8de67153c2f20fa0a696df106e78a6ecc4ffa4f8e75ceb6e1e4882a55b2547321b47d3c161d628c524c1d4412fa50db5a425a15e220 + "@tamagui/constants": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/use-constant": "npm:1.144.4" + "@tamagui/use-force-update": "npm:1.144.4" + "@tamagui/use-presence": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/761a87ac041b37cacc35d5a18ff1c60988fb19476d27ecf7a19d86713858035bb6a55682f934c003eb94a4c4f973a292c264f2bf74d558eb422ce578d429752b languageName: node linkType: hard -"@tamagui/animations-css@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/animations-css@npm:1.126.14" +"@tamagui/animate@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/animate@npm:1.144.4" dependencies: - "@tamagui/constants": "npm:1.126.14" - "@tamagui/cubic-bezier-animator": "npm:1.126.14" - "@tamagui/use-presence": "npm:1.126.14" - "@tamagui/web": "npm:1.126.14" + "@tamagui/animate-presence": "npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/049ad017332cd73053ae4f31d96fd4b4e874aff6b90cbd8ff1eb27326a505e9af72017f6fd70a507475d4f68bf56b14f6483418cf130afb613955864ea3a689d + languageName: node + linkType: hard + +"@tamagui/animations-css@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/animations-css@npm:1.144.4" + dependencies: + "@tamagui/constants": "npm:1.144.4" + "@tamagui/cubic-bezier-animator": "npm:1.144.4" + "@tamagui/use-presence": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" peerDependencies: react: "*" react-dom: "*" - checksum: 10c0/7e6825f24ef9539ffb539e779d68659cce7fa699ed3f9ee0153b61c0edfb70f7d140b6b0931577da416bbe9634ca3ef14dd695db7c2f9bb6543df6c910af155b + checksum: 10c0/30ff57aa4eb9c0a98dc19db4141a08ab3c80411b4efd14a44fdea1c0cd1087a59016d4f6c1936ffd1093dcdfd7c4215d1ea18e75688dd873620c1d49c857e41b languageName: node linkType: hard -"@tamagui/animations-moti@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/animations-moti@npm:1.126.14" +"@tamagui/animations-moti@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/animations-moti@npm:1.144.4" dependencies: - "@tamagui/use-presence": "npm:1.126.14" - "@tamagui/web": "npm:1.126.14" - moti: "npm:^0.29.0" - peerDependencies: - react: "*" - checksum: 10c0/2b8b8efc2bd4a74e13088cb650ed0a1e60bf93b6c8402838e523e78abfd93380aeccd195db76012fcebd62107c1cdfef97fdf0ea8f0365577bd2dcc347d2e1ad - languageName: node - linkType: hard - -"@tamagui/animations-react-native@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/animations-react-native@npm:1.126.14" - dependencies: - "@tamagui/constants": "npm:1.126.14" - "@tamagui/use-presence": "npm:1.126.14" - "@tamagui/web": "npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/9b0c040fa8d58a984064bb07d29c8de16cb621a234951e9ccc6c15cc0bed8e4c5810f1d3ee062e8b10764bcb0e8a383e84544550c530e8b74d965767a93b0952 - languageName: node - linkType: hard - -"@tamagui/aria-hidden@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/aria-hidden@npm:1.126.14" - dependencies: - aria-hidden: "npm:^1.1.3" - peerDependencies: - react: "*" - checksum: 10c0/2bf069bd5cf3d586d1d05a618925e986b1aedce9ab58a46f2f765b16b3bb394846c4795929722d484bb26611bd70497f28c5333c94c02cc0cfc061add4cb3c72 - languageName: node - linkType: hard - -"@tamagui/avatar@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/avatar@npm:1.126.14" - dependencies: - "@tamagui/core": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/image": "npm:1.126.14" - "@tamagui/shapes": "npm:1.126.14" - "@tamagui/text": "npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/cb5aeed82883f45a6a8ddc3be119a1da6242437732268ba16c783b65045b0848075bf5b40cca806d3008e160a6c34dac8d7bbd5009d3d6722aaa08ca29335769 - languageName: node - linkType: hard - -"@tamagui/babel-plugin-fully-specified@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/babel-plugin-fully-specified@npm:1.126.14" - dependencies: - "@babel/core": "npm:^7.25.2" - checksum: 10c0/1f653e59ee0debcbfde5873a2efaf0470a8bc0c23d2225e2bb1b6ab1a90d0b4681563b27b187eb2d27f3a9ee23b14417f8c332b82282fef4d42fba94e6b56441 - languageName: node - linkType: hard - -"@tamagui/build@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/build@npm:1.126.14" - dependencies: - "@babel/core": "npm:^7.25.2" - "@swc/core": "npm:^1.7.21" - "@tamagui/babel-plugin-fully-specified": "npm:1.126.14" - "@types/fs-extra": "npm:^9.0.13" - chokidar: "npm:^3.5.2" - esbuild: "npm:^0.25.0" - esbuild-plugin-es5: "npm:^2.1.1" - esbuild-register: "npm:^3.6.0" - execa: "npm:^5.0.0" - fast-glob: "npm:^3.2.11" - fs-extra: "npm:^11.2.0" - lodash.debounce: "npm:^4.0.8" - oxc-transform: "npm:^0.47.1" - typescript: "npm:^5.8.2" - bin: - tamagui-build: tamagui-build.js - teesx: teesx.sh - checksum: 10c0/55533154a6b0bb7c466fb680df600c4806954842bd9f788c00ad7f84115cde640bc836a2a57e59f36d0721b66a990cfbf62aac6d8e06392a09095293886d0cb0 - languageName: node - linkType: hard - -"@tamagui/button@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/button@npm:1.126.14" - dependencies: - "@tamagui/font-size": "npm:1.126.14" - "@tamagui/get-button-sized": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/helpers-tamagui": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/text": "npm:1.126.14" - "@tamagui/web": "npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/173c644b1c85cda899810da95b5ff0481e5c6f65a9f41d36efd895e8c176b79570fe57408d02d453b88265cefd0509c203475d27220e926ee51b04897ac308c0 - languageName: node - linkType: hard - -"@tamagui/card@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/card@npm:1.126.14" - dependencies: - "@tamagui/create-context": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/web": "npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/31a6a25cfecb0b48381631ce84195a8603b3cadeea390733e7386e026b4a6d61fc98d7ce7554b7f87c330612ce782548c0f49df6ca512a3bead0de340d6ae7d8 - languageName: node - linkType: hard - -"@tamagui/checkbox-headless@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/checkbox-headless@npm:1.126.14" - dependencies: - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/constants": "npm:1.126.14" - "@tamagui/create-context": "npm:1.126.14" - "@tamagui/focusable": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/label": "npm:1.126.14" - "@tamagui/use-controllable-state": "npm:1.126.14" - "@tamagui/use-previous": "npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/8ae1169a8e101ed7fb8d2244720a5c56cf95a2de9bafc59088a8012a6157f4962aba221b9f583c8612beffc3ad7406f475c1dab6f406b4cb9575bb35a10570c2 - languageName: node - linkType: hard - -"@tamagui/checkbox@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/checkbox@npm:1.126.14" - dependencies: - "@tamagui/checkbox-headless": "npm:1.126.14" - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/constants": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - "@tamagui/create-context": "npm:1.126.14" - "@tamagui/focusable": "npm:1.126.14" - "@tamagui/font-size": "npm:1.126.14" - "@tamagui/get-token": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/helpers-tamagui": "npm:1.126.14" - "@tamagui/label": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/use-controllable-state": "npm:1.126.14" - "@tamagui/use-previous": "npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/48c965f52e1be7cf2728fa85f34a0792461f1cd89449918672e60fd8d8b7db5982ee5f044e55b0d03d85f08c350f7c3cadb01cb85bcca50ed712156113f6ada2 - languageName: node - linkType: hard - -"@tamagui/cli-color@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/cli-color@npm:1.126.14" - checksum: 10c0/e82454fda153d34f10ef9b3683280e209e59e2683ddd723223cf08684c2f332b70fca0719c92039f0553bdd75dae415511556bffc10e1fda3f9ad12e2b3d8f33 - languageName: node - linkType: hard - -"@tamagui/collapsible@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/collapsible@npm:1.126.14" - dependencies: - "@tamagui/animate-presence": "npm:1.126.14" - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - "@tamagui/create-context": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/polyfill-dev": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/use-controllable-state": "npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/439e779fabf608a64d2ff304f39ebfc972abd6ade495535bb3de6fb9f1e1bd18867424dba38c7b3003724c868a65afa04b874092b2173a66532a7a1e3189b5f2 - languageName: node - linkType: hard - -"@tamagui/collection@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/collection@npm:1.126.14" - dependencies: - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/constants": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - "@tamagui/create-context": "npm:1.126.14" - "@tamagui/polyfill-dev": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/use-controllable-state": "npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/fc23d916dedff3488d773aee0d79692362de65a242a1e43f632b3224d937fc62e9e57498bb2cd61364bcb72f5102a73390ca21b513b85641bad8933b4f927831 - languageName: node - linkType: hard - -"@tamagui/colors@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/colors@npm:1.126.14" - checksum: 10c0/0649110f52411149c35033bb56c75b85fcdbd6b42d0e940b88852ee996751eac151a578298d4fc305f6e04f88a0931a06430ffd836f949dff5542d632c918da4 - languageName: node - linkType: hard - -"@tamagui/compose-refs@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/compose-refs@npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/9007692bc579ba8493af901379c2aba19b18b5740ef5bf32f93e86da583f79eb262114010ec6fbb18987f9e4fabe227ed4a5376b9aa63b7d660cf57cbe14c904 - languageName: node - linkType: hard - -"@tamagui/config-default@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/config-default@npm:1.126.14" - dependencies: - "@tamagui/animations-css": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - "@tamagui/shorthands": "npm:1.126.14" - checksum: 10c0/ef7b1068209e3fb579a541db05e2900757c5f5377153a82d5e4cb52d5352804301d77472d83c7aed615ce712b397a0a8ca6d03d073729dbe04bfa0f1232b28d9 - languageName: node - linkType: hard - -"@tamagui/config@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/config@npm:1.126.14" - dependencies: - "@tamagui/animations-css": "npm:1.126.14" - "@tamagui/animations-moti": "npm:1.126.14" - "@tamagui/animations-react-native": "npm:1.126.14" - "@tamagui/colors": "npm:1.126.14" - "@tamagui/font-inter": "npm:1.126.14" - "@tamagui/font-silkscreen": "npm:1.126.14" - "@tamagui/react-native-media-driver": "npm:1.126.14" - "@tamagui/shorthands": "npm:1.126.14" - "@tamagui/themes": "npm:1.126.14" - "@tamagui/web": "npm:1.126.14" - checksum: 10c0/9a6303935e989d4978434de5b3dcdbef8dfe4dcffa3f9adb378b6b8f04305c356e760ff5b35c4b708f02c4e277de788ec39d32bee0613dd503793a23bd403df8 - languageName: node - linkType: hard - -"@tamagui/constants@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/constants@npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/a58de0c69750a8edf23cac59b89cdd20aafbf0726c6344f1059d76fa3b21aeca91ad349477d6f3b2ab94ad08159784e997785fffc48083d13a31473260fb4393 - languageName: node - linkType: hard - -"@tamagui/core@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/core@npm:1.126.14" - dependencies: - "@tamagui/react-native-media-driver": "npm:1.126.14" - "@tamagui/react-native-use-pressable": "npm:1.126.14" - "@tamagui/react-native-use-responder-events": "npm:1.126.14" - "@tamagui/use-event": "npm:1.126.14" - "@tamagui/web": "npm:1.126.14" - checksum: 10c0/225c3c367f26ca3edd041efdc6e6dcc2515aedee554961a983087640b022e4f4ccb487074ae2bbcdfa26990cef506d7ce2967de2cab37fe21e1d95570b4d4d87 - languageName: node - linkType: hard - -"@tamagui/create-context@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/create-context@npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/6a8bde76fde9af4cb1692af17f9f1f5cc867f2094a9c49ba8e353fce033798dbc88aaf0c98968443c2c0d7a3e8db5db4a6caa4715bb41307f07b52372ff2c392 - languageName: node - linkType: hard - -"@tamagui/create-theme@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/create-theme@npm:1.126.14" - dependencies: - "@tamagui/web": "npm:1.126.14" - checksum: 10c0/e6d1d83a6fe8bd708ec7c41ceedb2dcd9a14798eac22bf3ef6e86e696b7d0ae0f3f3b9b2719543505c13d7e40ecf1b2517388e7dd02e05b9a49276c4664dac4d - languageName: node - linkType: hard - -"@tamagui/cubic-bezier-animator@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/cubic-bezier-animator@npm:1.126.14" - checksum: 10c0/4e378b71e7800ea1260779603d346508e81fefe75bc6b615c348295041102fe75994f3edb51a487d07fc7e17631e85a230c39344cef7b4811f0f71ae63def2e3 - languageName: node - linkType: hard - -"@tamagui/dialog@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/dialog@npm:1.126.14" - dependencies: - "@tamagui/adapt": "npm:1.126.14" - "@tamagui/animate-presence": "npm:1.126.14" - "@tamagui/aria-hidden": "npm:1.126.14" - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/constants": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - "@tamagui/create-context": "npm:1.126.14" - "@tamagui/dismissable": "npm:1.126.14" - "@tamagui/focus-scope": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/polyfill-dev": "npm:1.126.14" - "@tamagui/popper": "npm:1.126.14" - "@tamagui/portal": "npm:1.126.14" - "@tamagui/remove-scroll": "npm:1.126.14" - "@tamagui/sheet": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/text": "npm:1.126.14" - "@tamagui/use-controllable-state": "npm:1.126.14" - "@tamagui/z-index-stack": "npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/05d4c91b092fd304a7b05c75ddc10d7a91b3e05e263507019f2fe16de46b28fcdfc80cea815a68162771c0b93fb711b6fcbae6f2dbd7b0995c67967858a83ff0 - languageName: node - linkType: hard - -"@tamagui/dismissable@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/dismissable@npm:1.126.14" - dependencies: - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/use-escape-keydown": "npm:1.126.14" - "@tamagui/use-event": "npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/3f85bb112ba1e1a738eca4db838b5ca352960d3f41ecf418089f10def57f0237aa84025dd4f21fc3749de8e3c499057e97fe2a145e7ad067c35c5b9327993342 - languageName: node - linkType: hard - -"@tamagui/elements@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/elements@npm:1.126.14" - dependencies: - "@tamagui/core": "npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/7c0dd1aa70dbe6f412cc813505925f482302ae741dd48d52aea2111c3dea676e0b54f6b22b925496104a54125eee531fdf71823fb598290018f5305ccffedbd2 - languageName: node - linkType: hard - -"@tamagui/fake-react-native@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/fake-react-native@npm:1.126.14" - checksum: 10c0/250768a3188e2500c712b355cff8e374ca277787dab410741005b6d909a2dd40f3fa246fbff0e937975b0e28ae0f351a1b1532baaaeeb1f4df8f94261ee99954 - languageName: node - linkType: hard - -"@tamagui/floating@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/floating@npm:1.126.14" - dependencies: - "@floating-ui/react-dom": "npm:^2.1.2" - "@floating-ui/react-native": "npm:^0.10.7" - peerDependencies: - react: "*" - checksum: 10c0/ef6fb5c86e97a380522cadbc694190c0147f6f5ee180228b71b3c60e5aa6aadd74a6b31911daafd22850654cb39bb6da13bf630ce23e26e740cf5c72fb209f84 - languageName: node - linkType: hard - -"@tamagui/focus-scope@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/focus-scope@npm:1.126.14" - dependencies: - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/start-transition": "npm:1.126.14" - "@tamagui/use-event": "npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/b13b3e3bd35fbdb0ea838eee6d72d81df428e63cd44b5f58bc27175db564652942fe1be948b552f667e9b6308b59bf872eb181f47c61cf90ef49e75c61fee5ec - languageName: node - linkType: hard - -"@tamagui/focusable@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/focusable@npm:1.126.14" - dependencies: - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/web": "npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/99b888af8bd374e3d8b8337b66d4d57725a37579b432ce75ed544eb68669dee11bbe0f9720d99eb7f6e7c6728743ee43507f46c33e61d423129afc940d3885e2 - languageName: node - linkType: hard - -"@tamagui/font-inter@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/font-inter@npm:1.126.14" - dependencies: - "@tamagui/core": "npm:1.126.14" - checksum: 10c0/cf435d9735d1a6da7b331c9e0caa658dbe8e138e66e969a78cb7e619d097b077bff6b17444761818abccdf0f41ddd3e27e4a5015aec3df80584eee0658a00e87 - languageName: node - linkType: hard - -"@tamagui/font-silkscreen@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/font-silkscreen@npm:1.126.14" - dependencies: - "@tamagui/core": "npm:1.126.14" - checksum: 10c0/c977ca3287e0205df30f6330ed1cb0c62082c6ce10726ab27c8be72f3b4ebe72d584055b0ca857cb928ef4506824077890f05292d725c01c0449f4e5607216da - languageName: node - linkType: hard - -"@tamagui/font-size@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/font-size@npm:1.126.14" - dependencies: - "@tamagui/core": "npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/ecde925368566ed9c8a39093cc939d7cb46621034668631c5a2219a07585b17a211acd271fd5008d68255b59566607b7440d7dcf56e4fa327c07158d61174f27 - languageName: node - linkType: hard - -"@tamagui/form@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/form@npm:1.126.14" - dependencies: - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - "@tamagui/create-context": "npm:1.126.14" - "@tamagui/focusable": "npm:1.126.14" - "@tamagui/get-button-sized": "npm:1.126.14" - "@tamagui/get-font-sized": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/text": "npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/8cc196fdeba84c279aa7b35e619e62c5242fa574004c2e389ff26348a964f53120126d56ae1f81f0e5fd3b1a5d56bab1c73a8abdadc51241e387964edd3fd13e - languageName: node - linkType: hard - -"@tamagui/generate-themes@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/generate-themes@npm:1.126.14" - dependencies: - "@tamagui/create-theme": "npm:1.126.14" - "@tamagui/theme-builder": "npm:1.126.14" - "@tamagui/types": "npm:1.126.14" - esbuild-register: "npm:^3.6.0" - fs-extra: "npm:^11.2.0" - checksum: 10c0/4c12eea2fb8d365ffbdd71a1a7232eb2b837a1f9ea8b0a71acfc59b12d494cebac86f0b97758abfeae45fe66f2cbf7c3396846a40dc7e90e1bc6b4b2b3dc3e7e - languageName: node - linkType: hard - -"@tamagui/get-button-sized@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/get-button-sized@npm:1.126.14" - dependencies: - "@tamagui/get-token": "npm:1.126.14" - "@tamagui/web": "npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/9d2b28b577b52960af413c760a6ceaad3926596d4bb0649c431b4ef202897553b7df918efd55f092cea349386a716359905f20a7d624e9ccd02b7664b706fad6 - languageName: node - linkType: hard - -"@tamagui/get-font-sized@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/get-font-sized@npm:1.126.14" - dependencies: - "@tamagui/constants": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/3409f70f1d5dfd82e1e2e725d6dda248bad14c3f297f88db203ed1b58c6282fcf3d166bce7eb6dda8089881cc299803d091b886ad8296b072f2b698ef599b645 - languageName: node - linkType: hard - -"@tamagui/get-token@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/get-token@npm:1.126.14" - dependencies: - "@tamagui/web": "npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/aecbd70c72ebc5a6d34c400e7d38d928f1b1ddc983c01e9bf8f4711b2067da7d43d0a4a11e4341a194cfefbb6b93dfa021bcafbd860393d52e6197123b2794ab - languageName: node - linkType: hard - -"@tamagui/group@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/group@npm:1.126.14" - dependencies: - "@tamagui/core": "npm:1.126.14" - "@tamagui/create-context": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/use-controllable-state": "npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/dd32aff09181259248996ccddc2615e8881d42ecc18ff3b1ed34c7fd88907fc7fca9466fb6dac9bf1b8d1123293762f67ac37be7ee72d351260be33255807938 - languageName: node - linkType: hard - -"@tamagui/helpers-icon@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/helpers-icon@npm:1.126.14" - dependencies: - "@tamagui/core": "npm:1.126.14" - peerDependencies: - react: "*" - react-native-svg: ">=12" - checksum: 10c0/6eeb765b7955b73eb3752eb8644f08dd5d4cd40d1a8b1b46fa4fe69bf8ac6e38291a6d66fa1a5180c637a1987ebd433b90b790dc5b454770dc50a5f72d46a7b9 - languageName: node - linkType: hard - -"@tamagui/helpers-node@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/helpers-node@npm:1.126.14" - dependencies: - "@tamagui/types": "npm:1.126.14" - checksum: 10c0/63a8f8bb941ea0305b0046f960533fe6bf48655b1dc3796516667679466b3bf7fed49f23a1a668073b5b196093e28c11e99c01254fa973a6e85d85246c22f1b7 - languageName: node - linkType: hard - -"@tamagui/helpers-tamagui@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/helpers-tamagui@npm:1.126.14" - dependencies: - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/web": "npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/60c67e95a9609c2b2481e95d539b66c8819cda6261e7a082399541599af1469aeea26be26351be78bfafcbd629d87aca6973d7550ae41ed6b1a829de9037d3b4 - languageName: node - linkType: hard - -"@tamagui/helpers@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/helpers@npm:1.126.14" - dependencies: - "@tamagui/constants": "npm:1.126.14" - "@tamagui/simple-hash": "npm:1.126.14" - checksum: 10c0/ce855143a3f8d4eb33593557e6fd4df31d702f490535bc11f2834ea045eaa490e10878276d51645fbb3b9ce54270f805fba21b36b73d463e1efcdd3bfcc8bec4 - languageName: node - linkType: hard - -"@tamagui/image@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/image@npm:1.126.14" - dependencies: - "@tamagui/constants": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/1034107ff574eb96b3a5d0e62428ec0f8b508ca94b3503ed122d36faf7004522ebfdb2ad69ba16dda0c54a80bd179ae8608621f5261a1a641c1eeccb5b4d1468 - languageName: node - linkType: hard - -"@tamagui/label@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/label@npm:1.126.14" - dependencies: - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/constants": "npm:1.126.14" - "@tamagui/create-context": "npm:1.126.14" - "@tamagui/focusable": "npm:1.126.14" - "@tamagui/get-button-sized": "npm:1.126.14" - "@tamagui/get-font-sized": "npm:1.126.14" - "@tamagui/text": "npm:1.126.14" - "@tamagui/web": "npm:1.126.14" + "@tamagui/core": "npm:1.144.4" + "@tamagui/use-presence": "npm:1.144.4" + moti: "npm:^0.30.0" peerDependencies: react: "*" react-native: "*" - checksum: 10c0/ae52461288b038da0e3a0f724a82c1ec86955b103a8a9cc436e58d08f4c0e508a19e953e0e1b96fe5691ed76be73bca67e0c24507685bc22c8e67cec1a55fc72 + checksum: 10c0/ac2a5946b1ceb9b627e64ad1effd78b03fec74cb9491c311dc1a2ec7e91a9264c8c839d0523e4d2599d2b6fc0106c8318e5c19ef22408a7b24eb1aafca1e0e93 languageName: node linkType: hard -"@tamagui/linear-gradient@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/linear-gradient@npm:1.126.14" +"@tamagui/animations-react-native@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/animations-react-native@npm:1.144.4" dependencies: - "@tamagui/core": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/use-presence": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/72e8912032a927466ffa31c887c61d9699b4577ec579771d31148f729001d2057094bc0d82b079c5567c5f2d0f79b48cdfc303981c533855c3f1f62f8e6845c8 + react-native: "*" + checksum: 10c0/ea0ad16946ff44903743fb799575d6a7e0c21e43fb5dbe912837739d2abf862f1a334c41853c5ddd675f0adb67234036c999aa1fe3909d3b26afc2617fd94d89 languageName: node linkType: hard -"@tamagui/list-item@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/list-item@npm:1.126.14" +"@tamagui/avatar@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/avatar@npm:1.144.4" dependencies: - "@tamagui/font-size": "npm:1.126.14" - "@tamagui/get-font-sized": "npm:1.126.14" - "@tamagui/get-token": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/helpers-tamagui": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/text": "npm:1.126.14" - "@tamagui/web": "npm:1.126.14" + "@tamagui/core": "npm:1.144.4" + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/image": "npm:1.144.4" + "@tamagui/shapes": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/text": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/66e36832df19593b38f02e4c74fd9499669ae5236f88f9837c10c366cc5599d48679f54d5c362e06835fe653ce6b97ef93fe65f83de8dea84958d40e081c2189 + react-native: "*" + checksum: 10c0/d8c9693fdfe30d3c888999b57a2df063dbf1e0bb868df455d5acaf00ea935c01fa2387cf5b96faca9322ade1f30a5572c735658f7d3dda204a47b73663214808 languageName: node linkType: hard -"@tamagui/lucide-icons@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/lucide-icons@npm:1.126.14" +"@tamagui/button@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/button@npm:1.144.4" dependencies: - "@tamagui/core": "npm:1.126.14" - "@tamagui/helpers-icon": "npm:1.126.14" + "@tamagui/config-default": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/font-size": "npm:1.144.4" + "@tamagui/get-button-sized": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/helpers-tamagui": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/text": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/d25b86a9f34f61ef98f619ce14e41d9d78ebee5b78f5a78ee624fa5fa8f77f068234d30d4cc3c381e195aa8fc6c58bc5e79d8baa5385944cf7134d704e04a4a1 + languageName: node + linkType: hard + +"@tamagui/card@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/card@npm:1.144.4" + dependencies: + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10c0/de9f84a96d4e18f6821f1b2874d59a1af86e8ad40e79909451ba044d31d6cbd7730d19f1f6b14aa82c7020a547a2d5e1457f4dbade0d5c9496a7145d4f3d4b18 + languageName: node + linkType: hard + +"@tamagui/checkbox-headless@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/checkbox-headless@npm:1.144.4" + dependencies: + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/focusable": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/label": "npm:1.144.4" + "@tamagui/use-controllable-state": "npm:1.144.4" + "@tamagui/use-previous": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10c0/4f87e9b74db361696a4e6bf91374696f2f1b1d98683e2306c86290448956fcff3fe376ff96eb755cffce806defd95cc02fb0c5c44209172003e1376b334c947b + languageName: node + linkType: hard + +"@tamagui/checkbox@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/checkbox@npm:1.144.4" + dependencies: + "@tamagui/checkbox-headless": "npm:1.144.4" + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/focusable": "npm:1.144.4" + "@tamagui/font-size": "npm:1.144.4" + "@tamagui/get-token": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/helpers-tamagui": "npm:1.144.4" + "@tamagui/label": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/use-controllable-state": "npm:1.144.4" + "@tamagui/use-previous": "npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/76fdc6304a060e3abe16910c14434db45c7c68bb3b8fe96adf636b51d3ac4d5f8d2a500a4f655d7b21b67c4821b8a584b614266e80912fb14a0f833d3b16735d + languageName: node + linkType: hard + +"@tamagui/cli-color@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/cli-color@npm:1.144.4" + checksum: 10c0/7b227e8129dfca30bbc328998cf978aa8cdfedeb71b9f06d9024a61f24c7c3040f27664cee64be1ea4d637600349ce89552ce2465eece7e3403be38ce9112d0c + languageName: node + linkType: hard + +"@tamagui/collapsible@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/collapsible@npm:1.144.4" + dependencies: + "@tamagui/animate-presence": "npm:1.144.4" + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/polyfill-dev": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/use-controllable-state": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/62b5a2f1dcedbcc7bd5cc96bab52d8b37e59b860e7ea4282ff2ce97183b62d9710dffa3075ca9833deca35e0610edc04e630e96215764b3b481bdca2b2e8ab42 + languageName: node + linkType: hard + +"@tamagui/collection@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/collection@npm:1.144.4" + dependencies: + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/polyfill-dev": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/use-controllable-state": "npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/cf52ef5abe35f013b532e2f1fa67bfa29bf6ed5f084fb00877efcc6ddbd1df70887bc1989ca2f645e7f6376cc3d32875a7d70af79c367e0809766679f0a6aef6 + languageName: node + linkType: hard + +"@tamagui/colors@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/colors@npm:1.144.4" + checksum: 10c0/82bd96734504f07afffc63c32f2a649ade34a4cde35272ed758e274767bd3535bda252e558300678c556bc4696d5f0f0bd750c22a81fe8dd43f12c84ef3619b0 + languageName: node + linkType: hard + +"@tamagui/compose-refs@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/compose-refs@npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/c4e7a6d01c24392bf4c78bb2abdc238c150f0a3c763eff0b67e5ddc5792ed52349b298bd45e9005df2c75faec7f82eeec77a97b663269faa9c487d939593c6f4 + languageName: node + linkType: hard + +"@tamagui/config-default@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/config-default@npm:1.144.4" + dependencies: + "@tamagui/animations-css": "npm:1.144.4" + "@tamagui/animations-react-native": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/shorthands": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" + checksum: 10c0/448bf6c15ebfdaaaa692ae6912cd47ed50896eb0e7e5ad98040071c173e1f5337400afd620edca995a2ffdf419cb502a40995fe220489bba57f2c938a16ade7b + languageName: node + linkType: hard + +"@tamagui/config@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/config@npm:1.144.4" + dependencies: + "@tamagui/animations-css": "npm:1.144.4" + "@tamagui/animations-moti": "npm:1.144.4" + "@tamagui/animations-react-native": "npm:1.144.4" + "@tamagui/colors": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/font-inter": "npm:1.144.4" + "@tamagui/font-silkscreen": "npm:1.144.4" + "@tamagui/react-native-media-driver": "npm:1.144.4" + "@tamagui/shorthands": "npm:1.144.4" + "@tamagui/theme-builder": "npm:1.144.4" + "@tamagui/themes": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" + checksum: 10c0/c54b0f0ba6655f16c6fcc848ca347bfe374491ef2787553c46af85564d6481e9798b919e2235da9cf18bf26bbe0b6976d88d88e31256c14b5d9c713f957f42da + languageName: node + linkType: hard + +"@tamagui/constants@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/constants@npm:1.144.4" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10c0/8f3e7ba70fce13cf5714e3551c15b520db07b179025d6def82e1b9aabcc0b9f4d9875ffefda4b074211eab3c9e5614a794a129ecd7db7bebf57b3fc0f891d015 + languageName: node + linkType: hard + +"@tamagui/core@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/core@npm:1.144.4" + dependencies: + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/react-native-media-driver": "npm:1.144.4" + "@tamagui/react-native-use-pressable": "npm:1.144.4" + "@tamagui/react-native-use-responder-events": "npm:1.144.4" + "@tamagui/use-element-layout": "npm:1.144.4" + "@tamagui/use-event": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10c0/f43089448a8d23885d32183f21be9516aea877b84b9cde9bb954ae32f7a4ac894610f8d7b481fef70679c7f08547bd7336201208e9cc63e6f947bb309b384fb0 + languageName: node + linkType: hard + +"@tamagui/create-context@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/create-context@npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/246e9f234f22b4df53f197c503a0c4e8ee38f968f9e48c86cd898f15bb88540bb93a6d51a45b9d98dea2ae2ed8f69372ad63c7a8b752d24c5c402afc17d38bb1 + languageName: node + linkType: hard + +"@tamagui/create-theme@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/create-theme@npm:1.144.4" + dependencies: + "@tamagui/web": "npm:1.144.4" + checksum: 10c0/2d91409f9a288d06efefcedcfbf4c962f9183a16de46f51cfff7a8c897aa569d41648126af68292ec1cde3b4ea90cc5f9df842734d8d9affec724ab7ff3195e4 + languageName: node + linkType: hard + +"@tamagui/cubic-bezier-animator@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/cubic-bezier-animator@npm:1.144.4" + checksum: 10c0/e79cc9b18253222824c8d136546b3e9f524c5d5c66500dccb353b600337e01aff93aa0835e1214fafcdf7dc8aa29b9c253b836c88ecd23d5959a2dbbf147a283 + languageName: node + linkType: hard + +"@tamagui/dialog@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/dialog@npm:1.144.4" + dependencies: + "@tamagui/adapt": "npm:1.144.4" + "@tamagui/animate-presence": "npm:1.144.4" + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/dismissable": "npm:1.144.4" + "@tamagui/focus-scope": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/polyfill-dev": "npm:1.144.4" + "@tamagui/popper": "npm:1.144.4" + "@tamagui/portal": "npm:1.144.4" + "@tamagui/remove-scroll": "npm:1.144.4" + "@tamagui/sheet": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/text": "npm:1.144.4" + "@tamagui/use-controllable-state": "npm:1.144.4" + "@tamagui/z-index-stack": "npm:1.144.4" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10c0/c2c68568bc1c96fdc531d1cf1bd58900a40e626808e40f467de51a3cea4ce7031548b850b14389fc6fb8f9409f208a8984a2e225628f55983a175ff812ae7292 + languageName: node + linkType: hard + +"@tamagui/dismissable@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/dismissable@npm:1.144.4" + dependencies: + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/use-escape-keydown": "npm:1.144.4" + "@tamagui/use-event": "npm:1.144.4" + peerDependencies: + react: "*" + react-dom: "*" + checksum: 10c0/b3ade384115bb9a7dda53cf7e2cb589e7167804d84a7b96abb5614bc462a859d8f460146c554b7b12b8e7008cd5aacfba0210234e1ec32a9afc7f2ef12c6d2d4 + languageName: node + linkType: hard + +"@tamagui/elements@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/elements@npm:1.144.4" + dependencies: + "@tamagui/core": "npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/db80c0729dfd93ad280e70591a51114c9fa8a868491e06f8c591fddd5ae0dbb85bb27f38ccf30dc870f4e140e9bfd73d19d3dbb287ebfd3d7cfc37a642ca34fd + languageName: node + linkType: hard + +"@tamagui/fake-react-native@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/fake-react-native@npm:1.144.4" + checksum: 10c0/f59e0ce3232911d075ed57191eecb0eaef96531c1bb080808baf06b1bc9dc5182a3f59dba23d58a06d079f2e9116c5945258faac453b845a8e32eeaff12b8e4b + languageName: node + linkType: hard + +"@tamagui/floating@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/floating@npm:1.144.4" + dependencies: + "@floating-ui/react-dom": "npm:^2.1.6" + "@floating-ui/react-native": "npm:^0.10.7" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10c0/102f72153d0cf4bce4764188547eb99fe855cdeb6fd2325ff68d0cc1a0b2a88410f748f13292f2d5bbfd948ffe87257fcfc028b282664fc1bf89a6def7096a3c + languageName: node + linkType: hard + +"@tamagui/focus-scope@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/focus-scope@npm:1.144.4" + dependencies: + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/start-transition": "npm:1.144.4" + "@tamagui/use-async": "npm:1.144.4" + "@tamagui/use-event": "npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/a62a292ced57ecde2d13ab4b9c026bd02b7b9b5d9aec463751447745193ff0f3f6cf91b0cc3fdc5ef87e4f7ef88c04880a651fdbe5a4683cdfc28b69c916d7ab + languageName: node + linkType: hard + +"@tamagui/focusable@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/focusable@npm:1.144.4" + dependencies: + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/73b9ed1cc7a567f88caf4fb24ee4609f53427586e02afbdfdc443b836b2152377138d071e89f279c18bf556425c80f0c6dc59614f99a9ba4870e7061f551211c + languageName: node + linkType: hard + +"@tamagui/font-inter@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/font-inter@npm:1.144.4" + dependencies: + "@tamagui/core": "npm:1.144.4" + checksum: 10c0/ea67f222d0e2075cb3a184207b8bc6f69d6008c3c646f1f31efa0cb1baeba015c6ed58e6055687df2cf996b201ce2f3e5269e6b8afa2cbf62e9b6a7dc9700b35 + languageName: node + linkType: hard + +"@tamagui/font-silkscreen@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/font-silkscreen@npm:1.144.4" + dependencies: + "@tamagui/core": "npm:1.144.4" + checksum: 10c0/b74a36124faa60dcfcdfeef28573b8f4d25d92a635419f6e9686368238a2084133c18cf530ad495992457ad72db20b5ca68f2cff8e0e460a8b555f3a34384229 + languageName: node + linkType: hard + +"@tamagui/font-size@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/font-size@npm:1.144.4" + dependencies: + "@tamagui/core": "npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/548c91444f92d98a7cbc6cb5109fc54dc4564b93abdb2cd7ebacdbb359c8b477cbfffd5bc294c27f3b01b797adaa798db9af92a89e5d240cc5a3e5c1686b084c + languageName: node + linkType: hard + +"@tamagui/form@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/form@npm:1.144.4" + dependencies: + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/focusable": "npm:1.144.4" + "@tamagui/get-button-sized": "npm:1.144.4" + "@tamagui/get-font-sized": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/text": "npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/ee05f0fefb29291a731fbda950f849d8dc6d4c6d0cb8c231fc64ecf57390c3e0bedd93ad58c9862032da8548a50390d200deca81627ffd0ae5075b72c66a5a24 + languageName: node + linkType: hard + +"@tamagui/generate-themes@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/generate-themes@npm:1.144.4" + dependencies: + "@tamagui/create-theme": "npm:1.144.4" + "@tamagui/theme-builder": "npm:1.144.4" + "@tamagui/types": "npm:1.144.4" + esbuild-register: "npm:^3.6.0" + fs-extra: "npm:^11.2.0" + checksum: 10c0/dcd4b00f78d7ce43c14163e2520fe45ebf5a225a72a018641cf5bfa4f68a06d8e5b9c4ed785a66298774b09ba6fea1d737849459882d72152ed2d743bcb7fb64 + languageName: node + linkType: hard + +"@tamagui/get-button-sized@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/get-button-sized@npm:1.144.4" + dependencies: + "@tamagui/get-token": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/9473c0342c7e3f85e2a513925737d6c59c5b4d715f2e97ee179356d955a002ef0638903ce172d8d55fd69833b39f47f24915eca9e6b800c8e444bb240b1d87c0 + languageName: node + linkType: hard + +"@tamagui/get-font-sized@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/get-font-sized@npm:1.144.4" + dependencies: + "@tamagui/constants": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/7fb3ca65287abfaa18d1f2329c716e9f2c9641628e7a35b272cc0f410678c4827d22a9742132ffaf472c81e05d775d0288e916f3a78d1f8d2516e8c12c6a01d0 + languageName: node + linkType: hard + +"@tamagui/get-token@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/get-token@npm:1.144.4" + dependencies: + "@tamagui/web": "npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/c91dd383f63bff577c5253d1106d6b50e2b4f8bee47c0d9976266b7962b18ab4f8cdb3fb598d5ddae87a94c2e88115236f07062e006f09ed83a66db3e031c6d8 + languageName: node + linkType: hard + +"@tamagui/group@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/group@npm:1.144.4" + dependencies: + "@tamagui/core": "npm:1.144.4" + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/use-controllable-state": "npm:1.144.4" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10c0/b59240c7d0e9b11cbee2d057c02156df624e032f3b12ef123fd43fb75b871c38be0094a54b53a09bdea342d9d4796c2b84e188824a1544b05e8064b6927510da + languageName: node + linkType: hard + +"@tamagui/helpers-icon@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/helpers-icon@npm:1.144.4" + dependencies: + "@tamagui/core": "npm:1.144.4" peerDependencies: react: "*" react-native-svg: ">=12" - checksum: 10c0/f23ddec5b45fa1905801e6b412f1b36d0cdadc7b31c27431c19ad3d2661a37d436acf5933d0becb4c001aa7a406fa4bfe11c1aba8693ea538897e0d510f51707 + checksum: 10c0/93082375f6208c6b3ff0cfd9adb8cd1133241a657cc70844b00aeb57fe1aa6d4b3a6314535642d171f1cfbe0a408724756c803c548fc1924ffa9b72873a65d5d languageName: node linkType: hard -"@tamagui/normalize-css-color@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/normalize-css-color@npm:1.126.14" +"@tamagui/helpers-node@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/helpers-node@npm:1.144.4" + dependencies: + "@tamagui/types": "npm:1.144.4" + checksum: 10c0/c71b5c855252b43c6141637fa0454ead6b64705ce024ae93f18e33299c38df9e46145ce9c0624e76f7a64c7b23398e29aa16619f0f1df228ed72f98433180c7a + languageName: node + linkType: hard + +"@tamagui/helpers-tamagui@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/helpers-tamagui@npm:1.144.4" + dependencies: + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10c0/d0ab8da9db3dea03dadec3c5ef5021dbc5444c388b6a985f2b84828f9c22ac68c805771997d797a3cda9d7f3a5d3ec0f24dc3b12d9f299534e2d2c03498db763 + languageName: node + linkType: hard + +"@tamagui/helpers@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/helpers@npm:1.144.4" + dependencies: + "@tamagui/constants": "npm:1.144.4" + "@tamagui/simple-hash": "npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/a32232d632f0b9f117cdce29738b4a80c9de097ad2d705fccef43ecfc5afa45b73187630212e4ee516ec88ca6091ac638a47335f3eab393202bef6d0d312059a + languageName: node + linkType: hard + +"@tamagui/image@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/image@npm:1.144.4" + dependencies: + "@tamagui/constants": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10c0/3fe083c14a6be8f1e6d59b581846b5ab76e28d8834b61b6f436cfceffb5316b0e351da130866d085e64cc229dceb1238f73b98004dfda86758bdc0a030016af8 + languageName: node + linkType: hard + +"@tamagui/is-equal-shallow@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/is-equal-shallow@npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/38510672979ad4c35ff7f2f2d41920174c527aec312c730dcb500d5de764e7997deac8a4bf2c1d7d8fad2dd57331fd86f35c5c93d4cb839b75f84a858e601c58 + languageName: node + linkType: hard + +"@tamagui/label@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/label@npm:1.144.4" + dependencies: + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/focusable": "npm:1.144.4" + "@tamagui/get-button-sized": "npm:1.144.4" + "@tamagui/get-font-sized": "npm:1.144.4" + "@tamagui/text": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10c0/5ab6d990027b7750c655ba9e6b77671ae57c768327761b7a2b867894e3397f80c89c91a204b1978216fdd75111cad9e19aae4fc010e2622cc8b3610f083293c5 + languageName: node + linkType: hard + +"@tamagui/linear-gradient@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/linear-gradient@npm:1.144.4" + dependencies: + "@tamagui/core": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10c0/d54edad4039f3627f75f958b6313ff05abae986a235b5e312af8c1aeb94ec50d5b6c762f361835f76ade6a93a99c5ade9bf51e69e4792ee142ae9aa2b5c63130 + languageName: node + linkType: hard + +"@tamagui/list-item@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/list-item@npm:1.144.4" + dependencies: + "@tamagui/font-size": "npm:1.144.4" + "@tamagui/get-font-sized": "npm:1.144.4" + "@tamagui/get-token": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/helpers-tamagui": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/text": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/1b4981e380f3c8933f3ec8a099dd2738abd10e461ec5d06bd3f2f4b68bc5c8f54de0c6c99e4e7252637538cc95e149076dd5467114233c3665415cb34541ab29 + languageName: node + linkType: hard + +"@tamagui/lucide-icons@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/lucide-icons@npm:1.144.4" + dependencies: + "@tamagui/core": "npm:1.144.4" + "@tamagui/helpers-icon": "npm:1.144.4" + peerDependencies: + react: "*" + react-native-svg: ">=12" + checksum: 10c0/85f6a3609cb09a1ce2afad3974b12b10cbafc0ee2d846faa2aad5f5d239bfcdfa2ddd0f4c3f4acb5b5c5faeabc362d56a9ebd62287132f5ab1914c9532f1c93e + languageName: node + linkType: hard + +"@tamagui/normalize-css-color@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/normalize-css-color@npm:1.144.4" dependencies: "@react-native/normalize-color": "npm:^2.1.0" - checksum: 10c0/7bc76af6b9320c972a1d05d57561049ceb4787cda7cf96457f53ad900726598177b3a4a97ca6ae0daf09a120c45f80cd6986603c2af656100d1e2a3459d30e82 + checksum: 10c0/b154d1734430ee5ca8e44a9d443284ee458c661796b5c667cad6719b8ef50df13bd93eb91b8e52f7120f626b7dc5eca6871a474bcb3bb78705f04df3a60e170d languageName: node linkType: hard -"@tamagui/polyfill-dev@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/polyfill-dev@npm:1.126.14" - checksum: 10c0/1a30bc399ff3954b228c14bef73cb09fd1b98d1e1260aeb5aac680012df47d653c31d1f4cc6242bd6801f55cce8723dec0269660e5a01f103f1a4b8d59c39ee1 +"@tamagui/polyfill-dev@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/polyfill-dev@npm:1.144.4" + checksum: 10c0/2cc7174c34c2b167ff8357ddfcf082978b16b9d39fbdd7aaf66334f077c9fda758ded11323247f240495dc2349605c59e113b84a23ecb060d6d3bba87d0eef17 languageName: node linkType: hard -"@tamagui/popover@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/popover@npm:1.126.14" +"@tamagui/popover@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/popover@npm:1.144.4" dependencies: - "@floating-ui/react": "npm:^0.27.4" - "@tamagui/adapt": "npm:1.126.14" - "@tamagui/animate": "npm:1.126.14" - "@tamagui/aria-hidden": "npm:1.126.14" - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/constants": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - "@tamagui/dismissable": "npm:1.126.14" - "@tamagui/floating": "npm:1.126.14" - "@tamagui/focus-scope": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/polyfill-dev": "npm:1.126.14" - "@tamagui/popper": "npm:1.126.14" - "@tamagui/portal": "npm:1.126.14" - "@tamagui/remove-scroll": "npm:1.126.14" - "@tamagui/scroll-view": "npm:1.126.14" - "@tamagui/sheet": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/use-controllable-state": "npm:1.126.14" + "@floating-ui/react": "npm:^0.27.16" + "@tamagui/adapt": "npm:1.144.4" + "@tamagui/animate": "npm:1.144.4" + "@tamagui/animate-presence": "npm:1.144.4" + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/dismissable": "npm:1.144.4" + "@tamagui/floating": "npm:1.144.4" + "@tamagui/focus-scope": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/polyfill-dev": "npm:1.144.4" + "@tamagui/popper": "npm:1.144.4" + "@tamagui/portal": "npm:1.144.4" + "@tamagui/remove-scroll": "npm:1.144.4" + "@tamagui/scroll-view": "npm:1.144.4" + "@tamagui/sheet": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/use-controllable-state": "npm:1.144.4" + "@tamagui/z-index-stack": "npm:1.144.4" react-freeze: "npm:^1.0.3" peerDependencies: react: "*" - checksum: 10c0/140f981aefd3efcb5fa8340da89eb9c034d072dcf4bcdc2532b4b5ed848d305b6cdb97ee1e46be568a6729d33727bab8897303a1c44534511cd63024f662cb7d + react-native: "*" + checksum: 10c0/77ee4d18134202b74b760ae9c8b16012a83b573a8a7c42297f2f118cc3cf507dcff518efc02de739768b9ba4efbe21ee8eded0468406b01b337b10c2c6d04620 languageName: node linkType: hard -"@tamagui/popper@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/popper@npm:1.126.14" +"@tamagui/popper@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/popper@npm:1.144.4" dependencies: - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/constants": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - "@tamagui/floating": "npm:1.126.14" - "@tamagui/get-token": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/start-transition": "npm:1.126.14" - "@tamagui/use-controllable-state": "npm:1.126.14" + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/floating": "npm:1.144.4" + "@tamagui/get-token": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/start-transition": "npm:1.144.4" + "@tamagui/use-controllable-state": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/02dfdf2aec939f588b42def7a08307a644257fc9a03c0d15569217679cd47b8ace9d37a5bcad669e37476349aed27885cdc16e8431f7c66ff752ed5642f0bfc8 + react-native: "*" + checksum: 10c0/e96de7d600d471b0a61bedae042c95238e80f1aa1d5e5a3c42171450336f98ca030143b69b437e2edb4ff535cb3e81cd921e897a420876a21f4371aa5502c262 languageName: node linkType: hard -"@tamagui/portal@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/portal@npm:1.126.14" +"@tamagui/portal@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/portal@npm:1.144.4" dependencies: - "@tamagui/constants": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/start-transition": "npm:1.126.14" - "@tamagui/use-did-finish-ssr": "npm:1.126.14" - "@tamagui/use-event": "npm:1.126.14" - "@tamagui/z-index-stack": "npm:1.126.14" - checksum: 10c0/e77b8d5f6e1f495b0692cf687738c75c88d77be20093a9fb38ab6aed54e2a7aa3b4032f9ea6dcf97e3aec2185999273eacac93d27eb0ed5eb204156f44252548 - languageName: node - linkType: hard - -"@tamagui/progress@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/progress@npm:1.126.14" - dependencies: - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - "@tamagui/create-context": "npm:1.126.14" - "@tamagui/get-token": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/start-transition": "npm:1.144.4" + "@tamagui/use-event": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" + "@tamagui/z-index-stack": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/1348600645d563de7b6f2fad3e607162940484cf3914dedbe4189b23cd03e338726f6f814b55eac06a745224c7fc32d1e686db3c49a389f182a97784f7fe7d1c + react-dom: "*" + react-native: "*" + checksum: 10c0/94b37b884a52000610b8f6f865d24f24cecae6e0aae2ca72d48f0e13f3275cbfc33647e38ad38331989c56b635500ba011aa2d5ee061da07605fbf078b80c1af languageName: node linkType: hard -"@tamagui/proxy-worm@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/proxy-worm@npm:1.126.14" - checksum: 10c0/8a36b725ff8535843651a101c76ecadd45b8c241fe9fe24105e98f94551b66877b3af867e4f4793328fd48490b59a08cc34d9788beadae7cffb8f4b489fdf2fd - languageName: node - linkType: hard - -"@tamagui/radio-group@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/radio-group@npm:1.126.14" +"@tamagui/progress@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/progress@npm:1.144.4" dependencies: - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/constants": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - "@tamagui/create-context": "npm:1.126.14" - "@tamagui/focusable": "npm:1.126.14" - "@tamagui/get-token": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/label": "npm:1.126.14" - "@tamagui/radio-headless": "npm:1.126.14" - "@tamagui/roving-focus": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/use-controllable-state": "npm:1.126.14" - "@tamagui/use-previous": "npm:1.126.14" + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/get-token": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/f757df1fecd0a1488574093945654ae5d7ef25be2f6741099ec4400fa2afcfef5eadfa97502561d1ad4c9ca58e0a97ac9ad05820d7cf126c1d8e69e08be8149f + react-native: "*" + checksum: 10c0/e0517bd915eabe60982e50cb7a060924232e94153d71fed424cfc383db4235e7e996241bac1008f90306ad4e3d67ce1818ffcb2355c229d53d8cae0c77cf0b1a languageName: node linkType: hard -"@tamagui/radio-headless@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/radio-headless@npm:1.126.14" +"@tamagui/proxy-worm@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/proxy-worm@npm:1.144.4" + checksum: 10c0/9d9d1999b866f8c4db9f9a39124856d50fc9fc569f5011197fbd3bba64d43208608ddfefba13493d10d3da163a3c1f49a923743f568d0f0ba32692f72d600eff + languageName: node + linkType: hard + +"@tamagui/radio-group@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/radio-group@npm:1.144.4" dependencies: - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/constants": "npm:1.126.14" - "@tamagui/create-context": "npm:1.126.14" - "@tamagui/focusable": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/label": "npm:1.126.14" - "@tamagui/use-controllable-state": "npm:1.126.14" - "@tamagui/use-previous": "npm:1.126.14" + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/focusable": "npm:1.144.4" + "@tamagui/get-token": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/label": "npm:1.144.4" + "@tamagui/radio-headless": "npm:1.144.4" + "@tamagui/roving-focus": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/use-controllable-state": "npm:1.144.4" + "@tamagui/use-previous": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/50eec474fbf6eb8674287c5ad430627f5c34115e7dddd146111e4367ed037e7800cf4e0724096a36443b70b7ef6d3264d67a46784261349a00aaf75eb3b7aaed + checksum: 10c0/4305e3ff837f137e8db2d6d73613fe3103f9cd24eb29f2a0b0a22a0235ccee00ea14067fc9202c56de44adf4c810de6089481e05addc7f5c957cff962f8abf32 languageName: node linkType: hard -"@tamagui/react-native-media-driver@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/react-native-media-driver@npm:1.126.14" +"@tamagui/radio-headless@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/radio-headless@npm:1.144.4" dependencies: - "@tamagui/web": "npm:1.126.14" + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/focusable": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/label": "npm:1.144.4" + "@tamagui/use-controllable-state": "npm:1.144.4" + "@tamagui/use-previous": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10c0/d7d79333b5a49290ddff548dcb31cefa06489a1322ea6dcd2400facea8e99307476c4f1844d042967babe2d111f6b1fe8957b61b5fc44ef2a575a45f782e20fc + languageName: node + linkType: hard + +"@tamagui/react-native-media-driver@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/react-native-media-driver@npm:1.144.4" + dependencies: + "@tamagui/web": "npm:1.144.4" peerDependencies: react-native: "*" - checksum: 10c0/fdb25375dde4a5289d3ad45cc3724c194a3f85c8ac200cb8682fafebb913cee58fcedb623d46a1a65bff720474c0d8a9bc919b66225a7f51cfba6938209c06a7 + checksum: 10c0/aeef291911cbd4982a0a5e5275739ba66a0c51cbb38d14ef692a4faaa4fa692316da5dba2a6098cc3c0f8fca71a1e55dea58a10ebbcaa94b1d4e7924ae61be50 languageName: node linkType: hard -"@tamagui/react-native-svg@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/react-native-svg@npm:1.126.14" - checksum: 10c0/bea990e3f1d9ec8416dfd81c987562b7ed2b539e66182a709e2600cf4b6989b843a3fd9bc1c2eaed56f9cdc931ebb7222834ae6f71b2136d4e9b27a488a48453 +"@tamagui/react-native-svg@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/react-native-svg@npm:1.144.4" + peerDependencies: + react-native-svg: "*" + peerDependenciesMeta: + react-native-svg: + optional: true + checksum: 10c0/c1df85aafd72b319742d472b7357994fc3473e31f2bcb9f00b1e02e85ab1af4f61d0f5ec05d8cbde4fe454c27bedeb119d42cdedc839f5c88d1ce1c374da64a1 languageName: node linkType: hard -"@tamagui/react-native-use-pressable@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/react-native-use-pressable@npm:1.126.14" +"@tamagui/react-native-use-pressable@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/react-native-use-pressable@npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/75cf6f7e503bd8b6c1228babc0a72b29482f016d884fdfccfd34c81e3c6c9d48cd50d1d3951e1388e4bf678faa5af2710642f041986cb44aae0f25e0eac3456e + checksum: 10c0/55583ee6e081faf1f42f8f3d6d9812e6e2347989ef51d5b3ecbe1996ec6f90397aff2454403dff8af901909c019462b9d7d756d07ce0ee0d3004502f6e22ffc0 languageName: node linkType: hard -"@tamagui/react-native-use-responder-events@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/react-native-use-responder-events@npm:1.126.14" +"@tamagui/react-native-use-responder-events@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/react-native-use-responder-events@npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/b487b79e61edd351c1a81d4ddbb3c49c591a8a4976d7186f51f5724b81e11fa4a0dc00b2e3a9bc03091151db734caa08376b66b34fdb9e215b085097817627a8 + checksum: 10c0/3da7eaec8190130f616d93368654b358ef17f600678b596e001fdcdec6b2a9c18e83333c1b38e78b11887a6c15bb6527f22c6f13ef96abe30c0efc07f72a35e0 languageName: node linkType: hard -"@tamagui/react-native-web-internals@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/react-native-web-internals@npm:1.126.14" +"@tamagui/react-native-web-internals@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/react-native-web-internals@npm:1.144.4" dependencies: - "@tamagui/normalize-css-color": "npm:1.126.14" - "@tamagui/react-native-use-pressable": "npm:1.126.14" - "@tamagui/react-native-use-responder-events": "npm:1.126.14" - "@tamagui/simple-hash": "npm:1.126.14" - "@tamagui/web": "npm:1.126.14" - react: "npm:*" - checksum: 10c0/4289cdfc881039e1fcaa5d7f3e55762cb05369512c53d5f5abe61447dd203148becc08fddd0e2955fa89969e191a2d041b84be0e0f80923329e69980066421a0 + "@tamagui/normalize-css-color": "npm:1.144.4" + "@tamagui/react-native-use-pressable": "npm:1.144.4" + "@tamagui/react-native-use-responder-events": "npm:1.144.4" + "@tamagui/simple-hash": "npm:1.144.4" + "@tamagui/use-element-layout": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" + peerDependencies: + react: "*" + react-dom: "*" + checksum: 10c0/c009890f405bc44749bfaf9236711d702bfb1f08fba1e74cbeee3ac01410a3a3b40fb3cd1dd2a9b162b592e0f2f984b70936b4337e377744c7e855d3213da019 languageName: node linkType: hard -"@tamagui/react-native-web-lite@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/react-native-web-lite@npm:1.126.14" +"@tamagui/react-native-web-lite@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/react-native-web-lite@npm:1.144.4" dependencies: - "@tamagui/normalize-css-color": "npm:1.126.14" - "@tamagui/react-native-use-pressable": "npm:1.126.14" - "@tamagui/react-native-use-responder-events": "npm:1.126.14" - "@tamagui/react-native-web-internals": "npm:1.126.14" + "@tamagui/normalize-css-color": "npm:1.144.4" + "@tamagui/react-native-use-pressable": "npm:1.144.4" + "@tamagui/react-native-use-responder-events": "npm:1.144.4" + "@tamagui/react-native-web-internals": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" invariant: "npm:^2.2.4" + memoize-one: "npm:^6.0.0" peerDependencies: react: "*" - checksum: 10c0/7c6954d44c0ef9fdc90fdde6b7dc30d2bae713b1e0d8fca725175033664297f9c10c26d30897354f612e26139fc96359b7d924f0078013fbaaafa8365b087b20 + react-dom: "*" + checksum: 10c0/da28e0fdf6d77d0ed3780c8f3a6f65f21464ac9b7d14bf928f6b5e13d13fa36d8872920c2be0f1f85580b531cfa3e64a594881da85449fbf02d78a60a4b30d72 languageName: node linkType: hard -"@tamagui/remove-scroll@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/remove-scroll@npm:1.126.14" - dependencies: - react-remove-scroll: "npm:^2.6.0" +"@tamagui/remove-scroll@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/remove-scroll@npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/88a7a8963a3f0ba79454aa1617a7033589374102069b5e4cdeb4fbf6b671d20188b9e7e79e192ebde26c6fae8e43d16644c85162766bbfaa8123d7bc1b07142e + checksum: 10c0/a75fbf9b72541b1b7de20e70364f23bc3b296c22eef0cc5a9f85900bf7d9259375ed8a11b3bf36c8f08bdd9d56ffaa59d6a73e17aefdc70606aad3e0b617d05e languageName: node linkType: hard -"@tamagui/roving-focus@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/roving-focus@npm:1.126.14" +"@tamagui/roving-focus@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/roving-focus@npm:1.144.4" dependencies: - "@tamagui/collection": "npm:1.126.14" - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/constants": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - "@tamagui/create-context": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/use-controllable-state": "npm:1.126.14" - "@tamagui/use-direction": "npm:1.126.14" - "@tamagui/use-event": "npm:1.126.14" + "@tamagui/collection": "npm:1.144.4" + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/use-controllable-state": "npm:1.144.4" + "@tamagui/use-direction": "npm:1.144.4" + "@tamagui/use-event": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/bde2017c62c53cf372a3fcc14b1298b111e40f0d9d19807c5071be9a73151a61786dab243015e93fbd2050c6b53e4c416042e89912b70ba1ee71a3d952fbc72a + checksum: 10c0/091cc703003f4bf658656626b7820ba9d5b300a21f01af8b2b06606e831f03fbbec5b6c758b46d362a0506caba26989f9744d688d440dc38735c65bdac67f820 languageName: node linkType: hard -"@tamagui/scroll-view@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/scroll-view@npm:1.126.14" +"@tamagui/scroll-view@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/scroll-view@npm:1.144.4" dependencies: - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/web": "npm:1.126.14" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/d5c3b2665b6cfc611a7f762091a4c0ad40d484f8bc987045fe092712a1287e9b15d22a0fac269fce4d36e5d1852da7bfbce1dfa009b915e3435c33e7ee74b3da + react-native: "*" + checksum: 10c0/21ed1b3944bc9cf9e4eeb30e951d8b44c810c2b560c5ebd3a16d740b448f5ba1734c10ec81ed6e1f61238be814c92c549db23d061cb38a64fa1243db264f985a languageName: node linkType: hard -"@tamagui/select@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/select@npm:1.126.14" +"@tamagui/select@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/select@npm:1.144.4" dependencies: - "@floating-ui/react": "npm:^0.27.4" - "@floating-ui/react-dom": "npm:^2.1.2" + "@floating-ui/react": "npm:^0.27.16" + "@floating-ui/react-dom": "npm:^2.1.6" "@floating-ui/react-native": "npm:^0.10.7" - "@tamagui/adapt": "npm:1.126.14" - "@tamagui/animate-presence": "npm:1.126.14" - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/constants": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - "@tamagui/create-context": "npm:1.126.14" - "@tamagui/dismissable": "npm:1.126.14" - "@tamagui/focus-scope": "npm:1.126.14" - "@tamagui/get-token": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/list-item": "npm:1.126.14" - "@tamagui/portal": "npm:1.126.14" - "@tamagui/remove-scroll": "npm:1.126.14" - "@tamagui/separator": "npm:1.126.14" - "@tamagui/sheet": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/text": "npm:1.126.14" - "@tamagui/use-controllable-state": "npm:1.126.14" - "@tamagui/use-debounce": "npm:1.126.14" - "@tamagui/use-event": "npm:1.126.14" - "@tamagui/use-previous": "npm:1.126.14" + "@tamagui/adapt": "npm:1.144.4" + "@tamagui/animate-presence": "npm:1.144.4" + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/dismissable": "npm:1.144.4" + "@tamagui/focus-scope": "npm:1.144.4" + "@tamagui/focusable": "npm:1.144.4" + "@tamagui/get-token": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/list-item": "npm:1.144.4" + "@tamagui/portal": "npm:1.144.4" + "@tamagui/remove-scroll": "npm:1.144.4" + "@tamagui/separator": "npm:1.144.4" + "@tamagui/sheet": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/text": "npm:1.144.4" + "@tamagui/use-controllable-state": "npm:1.144.4" + "@tamagui/use-debounce": "npm:1.144.4" + "@tamagui/use-event": "npm:1.144.4" + "@tamagui/use-previous": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/cdbc18f0af7fdd164d734ece327cb699bd1e2eeb39a874c8f9beb6da522730af660e869f92f528a81b094f6cc7952c22cc189e3a89e9e2723165716628f239c3 + react-dom: "*" + react-native: "*" + checksum: 10c0/cba876e787cc3a2d5b2a30f3e05fa1cee4ce7945b757053d314d883fc7b35fdfd82431fdc71d2c11ec46d012d669146d6caea674e1b4183a32db65b0f8ee3c67 languageName: node linkType: hard -"@tamagui/separator@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/separator@npm:1.126.14" +"@tamagui/separator@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/separator@npm:1.144.4" dependencies: - "@tamagui/constants": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/a40c56d99f0e813512ca71015e411ba12f5044ac721f2c52910a804a12f93dea6b785f7916d3a64d0a44c25d3b766508fb39efbeccb11c10063e83d39e96ff12 + checksum: 10c0/f65ddb4674e2e420ef48d679bdc34faf3c687d7f216cf7fc80d067e916fa67d37685b09c61cc9dca44a0f8398c43eda888a0ee8fda20a2ecfe4682b422d1c479 languageName: node linkType: hard -"@tamagui/shapes@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/shapes@npm:1.126.14" +"@tamagui/shapes@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/shapes@npm:1.144.4" dependencies: - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/web": "npm:1.126.14" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/75dff0d742e6dfb7259a07c73d5a2e6b5915b6387ddd176c72766c2f96df2a5a3ac2c0559ed2f917cb3b35f8ec9a17bbd09a7f42cbfe3653e31010f14434f93c + checksum: 10c0/0c641efa672bbd523d0030813179afbcd177760c863f0e122fddc6c0f09c7fe6184d335a3fd69273f4ec85c456cf181290ea8e1d1005ccfdc98d99a576918f03 languageName: node linkType: hard -"@tamagui/sheet@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/sheet@npm:1.126.14" +"@tamagui/sheet@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/sheet@npm:1.144.4" dependencies: - "@tamagui/adapt": "npm:1.126.14" - "@tamagui/animate-presence": "npm:1.126.14" - "@tamagui/animations-react-native": "npm:1.126.14" - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/constants": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - "@tamagui/create-context": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/portal": "npm:1.126.14" - "@tamagui/remove-scroll": "npm:1.126.14" - "@tamagui/scroll-view": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/use-constant": "npm:1.126.14" - "@tamagui/use-controllable-state": "npm:1.126.14" - "@tamagui/use-did-finish-ssr": "npm:1.126.14" - "@tamagui/use-keyboard-visible": "npm:1.126.14" - "@tamagui/z-index-stack": "npm:1.126.14" + "@tamagui/adapt": "npm:1.144.4" + "@tamagui/animate-presence": "npm:1.144.4" + "@tamagui/animations-react-native": "npm:1.144.4" + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/portal": "npm:1.144.4" + "@tamagui/remove-scroll": "npm:1.144.4" + "@tamagui/scroll-view": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/use-constant": "npm:1.144.4" + "@tamagui/use-controllable-state": "npm:1.144.4" + "@tamagui/use-did-finish-ssr": "npm:1.144.4" + "@tamagui/use-keyboard-visible": "npm:1.144.4" + "@tamagui/z-index-stack": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/9efd9a16fac428e9895369912f42168726a1fc8e30784f443cc48ba58260247f99d571c2c735a60051744b8acb2edd3cdf2c007a6be0c7019978a55fb647d15c + react-native: "*" + checksum: 10c0/ea63fb249f908b86e87d7163c726be21a868c03f30459464bdd019507c6938e911fb2a6b44fe2abf5b74d9e30eef4c952ce7d505357e85326db617b3d7beffe1 languageName: node linkType: hard -"@tamagui/shorthands@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/shorthands@npm:1.126.14" +"@tamagui/shorthands@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/shorthands@npm:1.144.4" dependencies: - "@tamagui/web": "npm:1.126.14" - checksum: 10c0/77476d3f84d0a98b1247b995371152e5ba1bda3fb8fe4d61390fd798b3a7b15b930266a8e7761194502571cb9f5d3bdc17610901cd5d5bbb0eec3f2b2331044e + "@tamagui/web": "npm:1.144.4" + checksum: 10c0/5760f240ca22b3e45d0444dc980f32c0ecb584d7d998ba1fb7207cc45bbe3af397f5e1b597082a2a97d08ac993d32f5481c104a01b7e9bfb4d78e3ed7779f453 languageName: node linkType: hard -"@tamagui/simple-hash@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/simple-hash@npm:1.126.14" - checksum: 10c0/5c8f9283b3533572ffa975b7248bf2105babed4b6315cc6f034692a12a69587ab2114ea3640ecfc2a7cfb44526beeb256182d547abbcdf782793a0e80c7e1dcc +"@tamagui/simple-hash@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/simple-hash@npm:1.144.4" + checksum: 10c0/d0ae10b8adbf7a39133b1346a945ae479e0e7c9872c13af2d7a9174a8af16d51108cadbc35e6f934a002fb019e3356ddbf59be7214683d9e5232fd1b2c1aedea languageName: node linkType: hard -"@tamagui/slider@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/slider@npm:1.126.14" +"@tamagui/slider@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/slider@npm:1.144.4" dependencies: - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/constants": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - "@tamagui/create-context": "npm:1.126.14" - "@tamagui/get-token": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/use-controllable-state": "npm:1.126.14" - "@tamagui/use-debounce": "npm:1.126.14" - "@tamagui/use-direction": "npm:1.126.14" + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/get-token": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/use-controllable-state": "npm:1.144.4" + "@tamagui/use-debounce": "npm:1.144.4" + "@tamagui/use-direction": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/d7bf4cdd3f697ad56dca58ba275e9e91cee38d885f10006d9942881b917d1a80597e4d62db4ff78645d940158442687ac437d51df504d6fc735403945ab07bca + react-native: "*" + checksum: 10c0/05a031d64a82d4def802dac03b276b77842bcadfbf6edc47b1bcf94a63c47e53d43ece4575446cd42f0865e25549926f13b3b44053a93bcd9e3858fbaf4433e7 languageName: node linkType: hard -"@tamagui/stacks@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/stacks@npm:1.126.14" +"@tamagui/stacks@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/stacks@npm:1.144.4" dependencies: - "@tamagui/core": "npm:1.126.14" + "@tamagui/core": "npm:1.144.4" + "@tamagui/get-button-sized": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/dc3187ac817c290336d3ffe331154bdebf50ffd0f0351d1afe693a123ce8102dc953f605882abe760b1cd37a821d3735495c1379ff2c1d20b36d8d686c27296f + checksum: 10c0/91bd3335b6b2ffe8f0c5525aca619e8279b2b85bf582079e6513f4787e4852b25833661faa89a770c6a47041287fe40963bb593e18bb4ecfb1722c57491a2903 languageName: node linkType: hard -"@tamagui/start-transition@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/start-transition@npm:1.126.14" - checksum: 10c0/4236e8b6a10557f094fa32d41ea3a4a5049ea3db367712ca20464a08acc2828cd19bde446aa86bf2c81a43e03f648e5330223c2f378a05dec54f6ed37106dd0d +"@tamagui/start-transition@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/start-transition@npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/35414c343752a49060ad674e46a6b557f3f5c86f9f239eb9f9f9ae4ecf6d7bf27e9e173993d84cf3cc19ce57be5f208a5096c9181b586378db66d4b9d96ea7c2 languageName: node linkType: hard -"@tamagui/static@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/static@npm:1.126.14" +"@tamagui/static-worker@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/static-worker@npm:1.144.4" + dependencies: + "@tamagui/static": "npm:1.144.4" + "@tamagui/types": "npm:1.144.4" + piscina: "npm:^4.7.0" + checksum: 10c0/06c552edc01d9dd669c1e76af8d170203d1d04af9db1fc51b113d8428a1d5578231ba6450d1101ac3278f4cb73770eee04243e4769a06aa6028f683fdbfdc919 + languageName: node + linkType: hard + +"@tamagui/static@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/static@npm:1.144.4" dependencies: "@babel/core": "npm:^7.25.2" "@babel/generator": "npm:^7.25.5" @@ -15473,25 +15488,26 @@ __metadata: "@babel/parser": "npm:^7.25.4" "@babel/plugin-transform-react-jsx": "npm:^7.25.2" "@babel/runtime": "npm:^7.25.4" + "@babel/template": "npm:^7.25.0" "@babel/traverse": "npm:^7.25.4" "@babel/types": "npm:^7.25.4" - "@tamagui/build": "npm:1.126.14" - "@tamagui/cli-color": "npm:1.126.14" - "@tamagui/config-default": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - "@tamagui/fake-react-native": "npm:1.126.14" - "@tamagui/generate-themes": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/helpers-node": "npm:1.126.14" - "@tamagui/proxy-worm": "npm:1.126.14" - "@tamagui/react-native-web-internals": "npm:1.126.14" - "@tamagui/react-native-web-lite": "npm:1.126.14" - "@tamagui/shorthands": "npm:1.126.14" - "@tamagui/types": "npm:1.126.14" + "@tamagui/cli-color": "npm:1.144.4" + "@tamagui/config-default": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/fake-react-native": "npm:1.144.4" + "@tamagui/generate-themes": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/helpers-node": "npm:1.144.4" + "@tamagui/proxy-worm": "npm:1.144.4" + "@tamagui/react-native-web-internals": "npm:1.144.4" + "@tamagui/react-native-web-lite": "npm:1.144.4" + "@tamagui/shorthands": "npm:1.144.4" + "@tamagui/types": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" babel-literal-to-ast: "npm:^2.1.0" browserslist: "npm:^4.22.2" check-dependency-version-consistency: "npm:^4.1.0" - esbuild: "npm:^0.25.0" + esbuild: "npm:^0.25.11" esbuild-register: "npm:^3.6.0" fast-glob: "npm:^3.2.11" find-cache-dir: "npm:^3.3.2" @@ -15499,379 +15515,422 @@ __metadata: fs-extra: "npm:^11.2.0" invariant: "npm:^2.2.4" js-yaml: "npm:^4.1.0" - lodash: "npm:^4.17.21" - react-native-web: "npm:^0.20.0" + react-native-web: "npm:^0.21.0" peerDependencies: react: "*" - checksum: 10c0/3f468e63a8f81e9974f9e1e52543c0973a6adba7f622d621e194df21a44a00011167b0a67de24e35b9d2f9b67ccb110d932552fe9b170dbaeb81ee8b438d9bad + react-native: "*" + checksum: 10c0/e4b18366fdf054f79906b8c0c0768a12da08b0ed7ae2fdf13fa266407e84c05e795780ab2562f17ad65c0ef5ed004a67c4cf5b28d76484fa8dd41c4be842f5b3 languageName: node linkType: hard -"@tamagui/switch-headless@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/switch-headless@npm:1.126.14" +"@tamagui/switch-headless@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/switch-headless@npm:1.144.4" dependencies: - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/constants": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/label": "npm:1.126.14" - "@tamagui/use-previous": "npm:1.126.14" + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/label": "npm:1.144.4" + "@tamagui/use-previous": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/291f6becfe7df943493b02e0db84bbe6f4b6ac2e9bfdfd2fa0e9b06a5593e1b0db4c40a6c78cb809aef02788a8f14d916f0ee0836ad05edc405ba1c85f3ee4f0 + react-native: "*" + checksum: 10c0/28de7a1a3dcbc8c4776718bf1fd61ea9a1756d76170c4f68afd23b3c3d55a97b4fabf48dc668f4411f5774eacdb2bbe8492467906f853e1ab05c9609c08f4974 languageName: node linkType: hard -"@tamagui/switch@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/switch@npm:1.126.14" +"@tamagui/switch@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/switch@npm:1.144.4" dependencies: - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/constants": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - "@tamagui/focusable": "npm:1.126.14" - "@tamagui/get-token": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/label": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/switch-headless": "npm:1.126.14" - "@tamagui/use-controllable-state": "npm:1.126.14" - "@tamagui/use-previous": "npm:1.126.14" + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/focusable": "npm:1.144.4" + "@tamagui/get-token": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/label": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/switch-headless": "npm:1.144.4" + "@tamagui/use-controllable-state": "npm:1.144.4" + "@tamagui/use-previous": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/78edd642e70c37229afd10c3a35fedf1ae013c6669c7b810a3753f18cfe3f8bec16d77849e5dc3fa564c1099ed47f5c86f5272af194a25fa9c5b4fa95ede23fd + react-native: "*" + checksum: 10c0/616b4fd4dd5f6d2b0ec29748d54c6e0a3a81b5a38775f85762bf1f7d7aee00beaecf62b488385189cdc380bc05a52ee2c0d564f29b1361a77db5b061435273f1 languageName: node linkType: hard -"@tamagui/tabs@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/tabs@npm:1.126.14" +"@tamagui/tabs@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/tabs@npm:1.144.4" dependencies: - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/constants": "npm:1.126.14" - "@tamagui/create-context": "npm:1.126.14" - "@tamagui/get-button-sized": "npm:1.126.14" - "@tamagui/group": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/roving-focus": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/use-controllable-state": "npm:1.126.14" - "@tamagui/use-direction": "npm:1.126.14" - "@tamagui/web": "npm:1.126.14" + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/get-button-sized": "npm:1.144.4" + "@tamagui/group": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/roving-focus": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/use-controllable-state": "npm:1.144.4" + "@tamagui/use-direction": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/846229ab206cf8f6a99a4093135fbd5dc9e743a89b41534b52b5e9fe46d2d5e81bf2ce5e7ffc0eea67bd34d05eaf7a15fae4b3117c82a144ab453dd653fc88ee + react-native: "*" + checksum: 10c0/bd3ad6cb6ec28536f10fa28f4e85e2c8f0a1ae9293a9b086513e065747d86b6e5a851bde06a3bafae7605fdd5272dcae48be3baccd11302db866dc064c78af2e languageName: node linkType: hard -"@tamagui/text@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/text@npm:1.126.14" +"@tamagui/text@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/text@npm:1.144.4" dependencies: - "@tamagui/get-font-sized": "npm:1.126.14" - "@tamagui/helpers-tamagui": "npm:1.126.14" - "@tamagui/web": "npm:1.126.14" + "@tamagui/get-font-sized": "npm:1.144.4" + "@tamagui/helpers-tamagui": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/ee0a679923ef637eb89d8c1ad3cbb7708acb2b8557897a0f2776a5c3d8871d789b5c006f50863cbb33e56a810db052f04db51cc6682c2606c08881d2a78d56a6 + checksum: 10c0/ba4fff35df6ed871daa87d758b70f754e48c3b8f2dddd4f8eb640246f5c8b6b62315de2ebff5798d58f4f94e85f11c09db21e371ee72e08145c0a0328d195599 languageName: node linkType: hard -"@tamagui/theme-builder@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/theme-builder@npm:1.126.14" +"@tamagui/theme-builder@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/theme-builder@npm:1.144.4" dependencies: - "@tamagui/create-theme": "npm:1.126.14" + "@tamagui/create-theme": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" color2k: "npm:^2.0.2" - checksum: 10c0/de195e4dcbbe53354a8ca0325c1153aa3e036101265fb0fae208de15e864eddc3b0eac415630693736db1e67ed3b7adab0ecb1790436a3d3ecec9eef6c79bef7 + checksum: 10c0/e43fae2a68e10fb2a9b156c91dd6f44b0c7c88c11b4404e648b0921046fbde21899a6157d2cbe0bf4a411bbf0e04f87f1bc5c7538f34b28e08725132e4ac3b3f languageName: node linkType: hard -"@tamagui/theme@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/theme@npm:1.126.14" +"@tamagui/theme@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/theme@npm:1.144.4" dependencies: - "@tamagui/constants": "npm:1.126.14" - "@tamagui/web": "npm:1.126.14" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/start-transition": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/497dd90835b1024cd74837ea31ff2717e604507c4252006e25ff9dfd4b8369f28a9227e73f9639f0584acf7180c3b958c42fc69902af3704f92c83e00aa64d3d + checksum: 10c0/8bfdda4967aa98c6e117207416a1cb9d5b2a964209abc53596a6e7f302034fdaac4432aa0e6f102eafdd36278a6d3f4dbf7b7acbbe3d42b06c03a29f2a69a0d6 languageName: node linkType: hard -"@tamagui/themes@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/themes@npm:1.126.14" +"@tamagui/themes@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/themes@npm:1.144.4" dependencies: - "@tamagui/colors": "npm:1.126.14" - "@tamagui/create-theme": "npm:1.126.14" - "@tamagui/theme-builder": "npm:1.126.14" - "@tamagui/web": "npm:1.126.14" + "@tamagui/colors": "npm:1.144.4" + "@tamagui/create-theme": "npm:1.144.4" + "@tamagui/theme-builder": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" color2k: "npm:^2.0.2" - checksum: 10c0/507cd73cf8451334cf48d73a720b2182e4f994a58aeb3339398b7a878a690e999d5dfd403907833a07823e2b84868b134e6962be43dbb3970188acafc7dfadb8 + checksum: 10c0/b568f975e1e78022f8d1214c2ae8e0c967e81e89a30024ad73f8bc8b3af542e965c76052b031cad6a3a1b08a1482f057daabf5dfa4b752e855178eee2d0193dc languageName: node linkType: hard -"@tamagui/timer@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/timer@npm:1.126.14" - checksum: 10c0/c154f3555cc9e6812d9a1f9f7c5d86d223b8549eb7def690003b86f15b62b09e93848e792aa1a052f73d1f6868e50892585bd55f367e827cf6dfd1d0f52fed61 +"@tamagui/timer@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/timer@npm:1.144.4" + checksum: 10c0/1340b92c3d9c72e4703a57ba2dccf76188ffbbce0e243980e1420f93a724342b134d27232b8bf5635669b2da45013e3e6685f26ea7897731a287e98259e2dca9 languageName: node linkType: hard -"@tamagui/toast@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/toast@npm:1.126.14" +"@tamagui/toast@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/toast@npm:1.144.4" dependencies: - "@tamagui/animate-presence": "npm:1.126.14" - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/constants": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - "@tamagui/create-context": "npm:1.126.14" - "@tamagui/dismissable": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/polyfill-dev": "npm:1.126.14" - "@tamagui/portal": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/start-transition": "npm:1.126.14" - "@tamagui/text": "npm:1.126.14" - "@tamagui/use-controllable-state": "npm:1.126.14" - "@tamagui/visually-hidden": "npm:1.126.14" + "@tamagui/animate-presence": "npm:1.144.4" + "@tamagui/collection": "npm:1.144.4" + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/dismissable": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/polyfill-dev": "npm:1.144.4" + "@tamagui/portal": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/start-transition": "npm:1.144.4" + "@tamagui/text": "npm:1.144.4" + "@tamagui/use-controllable-state": "npm:1.144.4" + "@tamagui/visually-hidden": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/21af07f398d6bc99d969702fb7b4a92fc697fc25b17797177db2a6535a6123dd1928044aeaea8a353a66961d9d4b17fd725bac5e543f00dd1bd27cc2f8d86c49 + react-native: "*" + checksum: 10c0/5335ab5275b41e88e8aa3a527d7fbc15f574bde4f1c031231fa390ff730502aba197bcb07ec125a87ea346114875ed21e5898fc99457a3bd410f11dd414f690a languageName: node linkType: hard -"@tamagui/toggle-group@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/toggle-group@npm:1.126.14" +"@tamagui/toggle-group@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/toggle-group@npm:1.144.4" dependencies: - "@tamagui/constants": "npm:1.126.14" - "@tamagui/create-context": "npm:1.126.14" - "@tamagui/focusable": "npm:1.126.14" - "@tamagui/font-size": "npm:1.126.14" - "@tamagui/get-token": "npm:1.126.14" - "@tamagui/group": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/helpers-tamagui": "npm:1.126.14" - "@tamagui/roving-focus": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/use-controllable-state": "npm:1.126.14" - "@tamagui/use-direction": "npm:1.126.14" - "@tamagui/web": "npm:1.126.14" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/focusable": "npm:1.144.4" + "@tamagui/font-size": "npm:1.144.4" + "@tamagui/get-token": "npm:1.144.4" + "@tamagui/group": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/helpers-tamagui": "npm:1.144.4" + "@tamagui/roving-focus": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/use-controllable-state": "npm:1.144.4" + "@tamagui/use-direction": "npm:1.144.4" + "@tamagui/web": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/39618ba922f9704d8188fefecafeb8f21034760b3dc118647312b39366450c7fe7c7e44ae1246155a1ccc279418aafedcdb7d19418fe795f56ec6c4849fded36 + checksum: 10c0/f75fefa7a3f6d4890901a7b380d663bee1f835925ec51e046774b5e70cd754df52d632ed0b03e20744df357a0f42fa544cc985987cd0de382ce20af185c42975 languageName: node linkType: hard -"@tamagui/tooltip@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/tooltip@npm:1.126.14" +"@tamagui/tooltip@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/tooltip@npm:1.144.4" dependencies: - "@floating-ui/react": "npm:^0.27.4" - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - "@tamagui/create-context": "npm:1.126.14" - "@tamagui/floating": "npm:1.126.14" - "@tamagui/get-token": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/polyfill-dev": "npm:1.126.14" - "@tamagui/popover": "npm:1.126.14" - "@tamagui/popper": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/text": "npm:1.126.14" - "@tamagui/use-controllable-state": "npm:1.126.14" + "@floating-ui/react": "npm:^0.27.16" + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/floating": "npm:1.144.4" + "@tamagui/get-token": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/polyfill-dev": "npm:1.144.4" + "@tamagui/popover": "npm:1.144.4" + "@tamagui/popper": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/text": "npm:1.144.4" + "@tamagui/use-controllable-state": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/dad803119ed8d1c4a66886cf6984721ade5747af46a85cb5755340a3d6bca87de09af78a9792a76ed573f1c5c8de261dab25b348711f93a2f15aedd508fd58b4 + react-native: "*" + checksum: 10c0/2b9b66ac7c1da83069f113efda9a4f19029b161201b764f7d6d34405231f65f1632ec5332e8371e255bb48c12d147712adc7f61e7c5809fb228246cb925a4969 languageName: node linkType: hard -"@tamagui/types@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/types@npm:1.126.14" - checksum: 10c0/3b2b67a0b9663fc2722e91efd8a44f66e0e4fdf25709bfb7a48a36abb683401245d7cbcd8b4a174ad2d1c416a8f02fcde6ab1e452ae6324c2df237d050c709b5 +"@tamagui/types@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/types@npm:1.144.4" + checksum: 10c0/cb48e4d30477b15ad01fbb1c95131f3b14afa04f36137344862e9e9801a9888c4a24db2884bf56013f24a8a6b6cee291a96fa9f9f2345ee20d71d9aca2ca1e07 languageName: node linkType: hard -"@tamagui/use-callback-ref@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/use-callback-ref@npm:1.126.14" - checksum: 10c0/b3d05b69a706c31b5072179e358c6955136c7cae00530873b3e41de40a057bdd6cd77539baf1867419a6f555b6591ba3dc01566c965838eaf6b7a523ac9c0ee2 - languageName: node - linkType: hard - -"@tamagui/use-constant@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/use-constant@npm:1.126.14" +"@tamagui/use-async@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/use-async@npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/8d94a31791edc375a18ff0ecc8b7e01423410a1cea8e00e06eeff103420155a87292bbd5ce439592a00d0bfea35103e239cce4a8191ff2dd07ad14513e7a09ff + checksum: 10c0/47153013829144c97c86b4331d90c976088f59f46e442a18aa285b0ba036b1f785a7b1edb4da1d12a4e5a5b50841d1b90e01ca767ad87a34d70ca1176fa86e3e languageName: node linkType: hard -"@tamagui/use-controllable-state@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/use-controllable-state@npm:1.126.14" +"@tamagui/use-callback-ref@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/use-callback-ref@npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/df6a0efa23a986ffbc2f205e9722753b62ff5b82615feae5cfaefbca2d61749be67ac0823f5d1d15cef1b24be1003c340d6425206bc607496f9f3066d38ba04f + languageName: node + linkType: hard + +"@tamagui/use-constant@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/use-constant@npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/a700bc361b4fb15c0141286be6b5c2c96a75716f186448d5a3e7ae2841bef405f18f85f1f5db6e5209356b5ac72e1140094e896486a9efe01abcfb47a6a7c947 + languageName: node + linkType: hard + +"@tamagui/use-controllable-state@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/use-controllable-state@npm:1.144.4" dependencies: - "@tamagui/start-transition": "npm:1.126.14" - "@tamagui/use-event": "npm:1.126.14" + "@tamagui/start-transition": "npm:1.144.4" + "@tamagui/use-event": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/37cf16c78a5b388e6fc82a529fbd1f4dec2a1b7c3503640820cbb0d0ec09b24f7058d08023d1a90a4be250b707ecdd36e4747f594b477e874fdf4918df6c5c99 + checksum: 10c0/b3f74c96e131d54c575b297c33e0a9e6cb625f84f29a3553664ae00dbc5c2941ea646b52fee50cd11b3ffdfac80091d58c5a357c5416e064a788331a06edeef2 languageName: node linkType: hard -"@tamagui/use-debounce@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/use-debounce@npm:1.126.14" +"@tamagui/use-debounce@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/use-debounce@npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/35d4a006d90b0e91bd527f762963cc2450035a11f84e8295221f1727b540ec2706b422ae8f83368e3de569f788945cb16fdbb7c7687771575825f9601d360d90 + checksum: 10c0/b83a0cb7607684daaf90ff2c4fc64fbc8ddf59cc4122882a10f38b3d5f46acf65d872579dd7f2d921ca1a6cb32e2aec3fa9ffa0900f665c7ee8bdb5b0542ad75 languageName: node linkType: hard -"@tamagui/use-did-finish-ssr@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/use-did-finish-ssr@npm:1.126.14" +"@tamagui/use-did-finish-ssr@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/use-did-finish-ssr@npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/baac8d1ff1de196deb624d5c18a3452540033660b8883598ec533f6ad587e5ae81301120df7a67ecadafed1b824e86fadbcc2a49e3e7abd57c04fa9253d2f242 + checksum: 10c0/3f45b20d3aec109e0c04b57dfc792611c541c18b062cadb635f431f42a13a5eedae365280c68f3a0b5c37455f2c14504817c1d25e8dd9d80decdd7053f4565fd languageName: node linkType: hard -"@tamagui/use-direction@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/use-direction@npm:1.126.14" +"@tamagui/use-direction@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/use-direction@npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/5c5bf2bd8f4e3a24f4450251a62a94412624aca9de3a2b2302e00bb48155dae5d21850667c70a3ef4c77353017962ce5ab022f02d10f16e84aedac2d8e0322b6 + checksum: 10c0/4cd36431364e043164ab649c332255b11faa2e49b4175179956f4ffb5221399b6ca7dfb2a44a8f5b4a32c7d7c59e508059aa7f3bd178f886f6961fb85f3acf08 languageName: node linkType: hard -"@tamagui/use-escape-keydown@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/use-escape-keydown@npm:1.126.14" +"@tamagui/use-element-layout@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/use-element-layout@npm:1.144.4" dependencies: - "@tamagui/use-callback-ref": "npm:1.126.14" - checksum: 10c0/9bd7e325fcbd21a13d184140d8bc087e3cddd90786fbc3b2068e0f8dc33d934658eae0405974b749c93a9b3643156d15e12f31a805c61580b9adb6f499013441 + "@tamagui/constants": "npm:1.144.4" + "@tamagui/is-equal-shallow": "npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/3f110245e7b875da91f997eddf5095d5673a451f4dfd19f89a23c918005186c1b13f4d4bcb6d0edc805b6fbd0a500df0f1e11cdab162889b258fb2c13c16d259 languageName: node linkType: hard -"@tamagui/use-event@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/use-event@npm:1.126.14" +"@tamagui/use-escape-keydown@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/use-escape-keydown@npm:1.144.4" dependencies: - "@tamagui/constants": "npm:1.126.14" + "@tamagui/use-callback-ref": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/f7ef4ae2f274fe763e4a38e89433a23d773a1cff4df1479e323b75ea3954606c2f6e242090859f40caded4b9e9594d03ee46917b315db00052accc8c44b6e709 + checksum: 10c0/89831788cf5d1047fe1e161ac1d78243d90c548acf068799c431e61f64f6d1cb4ee9e615aea7546a70fa508c9f725b94c045e3b0e4a310ecafd1c21ceeea050c languageName: node linkType: hard -"@tamagui/use-force-update@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/use-force-update@npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/f616f7b82645d819427718fc618e4348c7b23ed629038d13a4230e129e6db05b95f2d568e99cdae68ff2bd12566e6a98d5eb8bcf0c0fe4f503bdb16b71e1006f - languageName: node - linkType: hard - -"@tamagui/use-keyboard-visible@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/use-keyboard-visible@npm:1.126.14" - peerDependencies: - react: "*" - checksum: 10c0/ec332424f9a1620cb22bf1befa24be735fb6eda4310ee51f0433bd2e9c032c6a488bea582a6278e6daf1a75b5b76703d283d71d6bd7550a8cfd3cc5125702e4e - languageName: node - linkType: hard - -"@tamagui/use-presence@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/use-presence@npm:1.126.14" +"@tamagui/use-event@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/use-event@npm:1.144.4" dependencies: - "@tamagui/web": "npm:1.126.14" + "@tamagui/constants": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/e9c0b087af493d232e1a98fef78c47b6fc00356df33ea3451d6fcb2c1cfb18ad1076a115d1a45648f72b48b4c17bbb649a123c5a5325470b61ffa2680ea13679 + checksum: 10c0/410e4d0001eb4595f758bc2fab33c59a3d3fa40be4283b06af7a46857eb9d780f6dfea547ff52292092ade6b615ee7718bde30980700a371c4de1dd4106a8ef4 languageName: node linkType: hard -"@tamagui/use-previous@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/use-previous@npm:1.126.14" - checksum: 10c0/b810aeaa75d3c85322f33d8ed16f3b06bd807ed5c84134df9d2489abed5b05047cdcd9798f344ef444aa5c7cf6bd44ec5808533a89341610a0337996c212107c - languageName: node - linkType: hard - -"@tamagui/use-window-dimensions@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/use-window-dimensions@npm:1.126.14" - dependencies: - "@tamagui/constants": "npm:1.126.14" +"@tamagui/use-force-update@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/use-force-update@npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/ca7bfbf197cb057009d1495d4692a2ec41fbdc127f5c0935e2d4621493b01781905b9d5de1ed51784c04c923efb8ebb46a781d2ca953c673bc98e2d3e16453f7 + checksum: 10c0/b42e866462a3a5cdd5e23ccf8335c40316386a67afc80e4e9ea33e5bb97e56ae28c3f30aab83516b01ea74c6482fb76c2ca865a617bf59658d9d5e246a8d3809 languageName: node linkType: hard -"@tamagui/visually-hidden@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/visually-hidden@npm:1.126.14" - dependencies: - "@tamagui/web": "npm:1.126.14" +"@tamagui/use-keyboard-visible@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/use-keyboard-visible@npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/b7a24e98affaf7e89c61205d47026ef13870b503df71d739020b9f316c1722f627d708693a651ad01a705f7dbb181514ee3b52a3f9653e673093a7dbaf01cc91 + react-native: "*" + checksum: 10c0/c3a983a525b87d6c6ff7ce2aee1faa6c3f392982f4fa2b5ad289bbfbbc1c3a21f08a374a4785433c36edc57f02229760b978d11e549a1410d0b95ee0c79a8f84 languageName: node linkType: hard -"@tamagui/vite-plugin@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/vite-plugin@npm:1.126.14" +"@tamagui/use-presence@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/use-presence@npm:1.144.4" dependencies: - "@tamagui/fake-react-native": "npm:1.126.14" - "@tamagui/proxy-worm": "npm:1.126.14" - "@tamagui/react-native-svg": "npm:1.126.14" - "@tamagui/react-native-web-lite": "npm:1.126.14" - "@tamagui/static": "npm:1.126.14" + "@tamagui/web": "npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/fd00b3301fdd9af272a48909a20c0267fdbd43361e2c4d8110796640217e70987d54af21226641955f7ee6203f458fa89a1134a016ee66ae26fd8706244980f6 + languageName: node + linkType: hard + +"@tamagui/use-previous@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/use-previous@npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/bf75568dd45ec018dbf4247638fb54d659754526151ef05ebb5ee8074265c73f307a684b50095a6cf26711289f23fd08b34eb535f3b8b5d96f343d737b01ed03 + languageName: node + linkType: hard + +"@tamagui/use-window-dimensions@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/use-window-dimensions@npm:1.144.4" + dependencies: + "@tamagui/constants": "npm:1.144.4" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10c0/3ad93f5a5b94833d76d9d38cd6c570366c8f0228d4d0856d67ce0de74aa7e0c3d387ea76df819ee8f03a92faede604a4e9e17abe853f36649950b33b18a3cf54 + languageName: node + linkType: hard + +"@tamagui/visually-hidden@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/visually-hidden@npm:1.144.4" + dependencies: + "@tamagui/web": "npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/6d070a638c89032c75d559e5ee76150bfb7d3b39abcff3262fd1a0ce76a0cb09ccc701d7b381983957b7954da8469d6ea9ed12db1c1d06efadd6bb6c3e12f263 + languageName: node + linkType: hard + +"@tamagui/vite-plugin@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/vite-plugin@npm:1.144.4" + dependencies: + "@tamagui/fake-react-native": "npm:1.144.4" + "@tamagui/proxy-worm": "npm:1.144.4" + "@tamagui/react-native-svg": "npm:1.144.4" + "@tamagui/react-native-web-lite": "npm:1.144.4" + "@tamagui/static-worker": "npm:1.144.4" + "@tamagui/types": "npm:1.144.4" esm-resolve: "npm:^1.0.8" fs-extra: "npm:^11.2.0" outdent: "npm:^0.8.0" - react-native-web: "npm:^0.20.0" + react-native-web: "npm:^0.21.0" peerDependencies: vite: "*" - checksum: 10c0/51a19432692ef9fad5289259029ba9827e16c085a4f840b6033a177505ff24c354289f55d3921cc204d29b07b2efa242d96a9ac425d7124e736da0c18f4a6410 + checksum: 10c0/440adc63ef5f28a4eb345996695840c59018cc039d9a22a2178bcd79cf23489b36a3dc80ae65d0361333358a5cc0965b7923deec57a3f26dffad42d731267728 languageName: node linkType: hard -"@tamagui/web@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/web@npm:1.126.14" +"@tamagui/web@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/web@npm:1.144.4" dependencies: - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/constants": "npm:1.126.14" - "@tamagui/helpers": "npm:1.126.14" - "@tamagui/normalize-css-color": "npm:1.126.14" - "@tamagui/timer": "npm:1.126.14" - "@tamagui/types": "npm:1.126.14" - "@tamagui/use-did-finish-ssr": "npm:1.126.14" - "@tamagui/use-event": "npm:1.126.14" - "@tamagui/use-force-update": "npm:1.126.14" + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/helpers": "npm:1.144.4" + "@tamagui/is-equal-shallow": "npm:1.144.4" + "@tamagui/normalize-css-color": "npm:1.144.4" + "@tamagui/timer": "npm:1.144.4" + "@tamagui/types": "npm:1.144.4" + "@tamagui/use-did-finish-ssr": "npm:1.144.4" + "@tamagui/use-event": "npm:1.144.4" + "@tamagui/use-force-update": "npm:1.144.4" peerDependencies: react: "*" react-dom: "*" - checksum: 10c0/f71d319ef17a5c1f9d1b69300610524046a02f65e670ee58a51423b8c602cc9929dfe4e4097d94c6292ca6d7799d4e0b7f4cb5b8136b7b6d2f94d605e3b7f5d1 + react-native: "*" + checksum: 10c0/4a4f6cac9214b982b0a5f6297eff56da530656f0d5b798be6efce85f7f36727bd2b56b65f0e390ae4b6630aeb568ea9031dbc09781501aabaf9ee5a1cce227b8 languageName: node linkType: hard -"@tamagui/z-index-stack@npm:1.126.14": - version: 1.126.14 - resolution: "@tamagui/z-index-stack@npm:1.126.14" - checksum: 10c0/6e1a5c54cad3b9038bf76a76eebb4089d1dac038490f2b5b610bd8950703b5753c6fc5bfec7075310fe363129e5c578dcd46e19710a5dbad1d57e5052e622ed6 +"@tamagui/z-index-stack@npm:1.144.4": + version: 1.144.4 + resolution: "@tamagui/z-index-stack@npm:1.144.4" + peerDependencies: + react: "*" + checksum: 10c0/0d00ff98181b121e54f51945e2ef80465f8a521cedfabf69e649f93c3e5e011f06bc1a2fb5285c22c90aacc8eff04b15158f1ad795aec377e8a1f7c7c874e9e4 languageName: node linkType: hard @@ -16539,15 +16598,6 @@ __metadata: languageName: node linkType: hard -"@types/fs-extra@npm:^9.0.13": - version: 9.0.13 - resolution: "@types/fs-extra@npm:9.0.13" - dependencies: - "@types/node": "npm:*" - checksum: 10c0/576d4e9d382393316ed815c593f7f5c157408ec5e184521d077fcb15d514b5a985245f153ef52142b9b976cb9bd8f801850d51238153ebd0dc9e96b7a7548588 - languageName: node - linkType: hard - "@types/glob@npm:^7.1.1": version: 7.2.0 resolution: "@types/glob@npm:7.2.0" @@ -17746,9 +17796,9 @@ __metadata: languageName: node linkType: hard -"@walletconnect/react-native-compat@npm:^2.23.0": - version: 2.23.1 - resolution: "@walletconnect/react-native-compat@npm:2.23.1" +"@walletconnect/react-native-compat@npm:^2.23.9": + version: 2.23.9 + resolution: "@walletconnect/react-native-compat@npm:2.23.9" dependencies: events: "npm:3.3.0" fast-text-encoding: "npm:1.0.6" @@ -17762,7 +17812,7 @@ __metadata: peerDependenciesMeta: expo-application: optional: true - checksum: 10c0/f1687ba00f300c0e3a5e068089342c20af72bdf102979f85d7849593334a2c8b75cafb2078690c7157221ad4913b6b88dcf0c9850590f4b3eed923598a71c04e + checksum: 10c0/d957bab553ee56965a7cc7ae76f99b693d2e1c979a822d3d3b96d205b980b3d5c91e465772ecc2e1a6a6f6759f555c80eb0555b3b6b4986e6916393466814a9b languageName: node linkType: hard @@ -18956,15 +19006,6 @@ __metadata: languageName: node linkType: hard -"aria-hidden@npm:^1.1.3": - version: 1.2.6 - resolution: "aria-hidden@npm:1.2.6" - dependencies: - tslib: "npm:^2.0.0" - checksum: 10c0/7720cb539497a9f760f68f98a4b30f22c6767aa0e72fa7d58279f7c164e258fc38b2699828f8de881aab0fc8e9c56d1313a3f1a965046fc0381a554dbc72b54a - languageName: node - linkType: hard - "aria-query@npm:5.1.3": version: 5.1.3 resolution: "aria-query@npm:5.1.3" @@ -20736,7 +20777,7 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:^3.5.2, chokidar@npm:^3.5.3, chokidar@npm:^3.6.0": +"chokidar@npm:^3.5.3, chokidar@npm:^3.6.0": version: 3.6.0 resolution: "chokidar@npm:3.6.0" dependencies: @@ -22366,13 +22407,6 @@ __metadata: languageName: node linkType: hard -"detect-node-es@npm:^1.1.0": - version: 1.1.0 - resolution: "detect-node-es@npm:1.1.0" - checksum: 10c0/e562f00de23f10c27d7119e1af0e7388407eb4b06596a25f6d79a360094a109ff285de317f02b090faae093d314cf6e73ac3214f8a5bb3a0def5bece94557fbe - languageName: node - linkType: hard - "detect-node@npm:^2.0.4": version: 2.1.0 resolution: "detect-node@npm:2.1.0" @@ -23138,19 +23172,6 @@ __metadata: languageName: node linkType: hard -"esbuild-plugin-es5@npm:^2.1.1": - version: 2.1.1 - resolution: "esbuild-plugin-es5@npm:2.1.1" - dependencies: - "@swc/core": "npm:^1.5.25" - "@swc/helpers": "npm:^0.5.11" - deepmerge: "npm:^4.3.1" - peerDependencies: - esbuild: "*" - checksum: 10c0/6d00df632e9c31919c1fca6d33919c19940409899597d0eb44527a2fcf364d5185e7371c4f6f390640a5b042b5e3aa5caad5fb21dc2fd09d638f3b57f0db20b2 - languageName: node - linkType: hard - "esbuild-register@npm:^3.6.0": version: 3.6.0 resolution: "esbuild-register@npm:3.6.0" @@ -23340,7 +23361,7 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.25.0": +"esbuild@npm:^0.25.0, esbuild@npm:^0.25.11": version: 0.25.12 resolution: "esbuild@npm:0.25.12" dependencies: @@ -25692,13 +25713,6 @@ __metadata: languageName: node linkType: hard -"get-nonce@npm:^1.0.0": - version: 1.0.1 - resolution: "get-nonce@npm:1.0.1" - checksum: 10c0/2d7df55279060bf0568549e1ffc9b84bc32a32b7541675ca092dce56317cdd1a59a98dcc4072c9f6a980779440139a3221d7486f52c488e69dc0fd27b1efb162 - languageName: node - linkType: hard - "get-package-type@npm:^0.1.0": version: 0.1.0 resolution: "get-package-type@npm:0.1.0" @@ -30008,6 +30022,23 @@ __metadata: languageName: node linkType: hard +"lottie-react-native@npm:7.3.6": + version: 7.3.6 + resolution: "lottie-react-native@npm:7.3.6" + peerDependencies: + "@lottiefiles/dotlottie-react": ^0.13.5 + react: "*" + react-native: ">=0.46" + react-native-windows: ">=0.63.x" + peerDependenciesMeta: + "@lottiefiles/dotlottie-react": + optional: true + react-native-windows: + optional: true + checksum: 10c0/1e3c0d0e763f0021c14bc85f90a9fe45ad4fa446db8961c3153507b271b1029b9095656924a553dafdff6987120d220c054772ff5efaf002b2eb567fe74e3533 + languageName: node + linkType: hard + "lottie-react@npm:^2.4.0, lottie-react@npm:^2.4.1": version: 2.4.1 resolution: "lottie-react@npm:2.4.1" @@ -31415,14 +31446,14 @@ __metadata: languageName: node linkType: hard -"moti@npm:^0.29.0": - version: 0.29.0 - resolution: "moti@npm:0.29.0" +"moti@npm:^0.30.0": + version: 0.30.0 + resolution: "moti@npm:0.30.0" dependencies: framer-motion: "npm:^6.5.1" peerDependencies: react-native-reanimated: "*" - checksum: 10c0/f78454bc96965947e4370a9a3890e3344c1d037314016223520614d5b8d2c164f9d948b5c88f12e7180e51ee61b886c40579e8670803c782558e6f7759ce0ea8 + checksum: 10c0/9ef9f8c75cfd126a4d50e6a1cddaac2ef7dc6aefee532843903b34b381b8f5f96ef84458b1cd6d05739dd6032be4f07ebda17b4e33bf2818c860bad6cf524038 languageName: node linkType: hard @@ -32693,39 +32724,6 @@ __metadata: languageName: node linkType: hard -"oxc-transform@npm:^0.47.1": - version: 0.47.1 - resolution: "oxc-transform@npm:0.47.1" - dependencies: - "@oxc-transform/binding-darwin-arm64": "npm:0.47.1" - "@oxc-transform/binding-darwin-x64": "npm:0.47.1" - "@oxc-transform/binding-linux-arm64-gnu": "npm:0.47.1" - "@oxc-transform/binding-linux-arm64-musl": "npm:0.47.1" - "@oxc-transform/binding-linux-x64-gnu": "npm:0.47.1" - "@oxc-transform/binding-linux-x64-musl": "npm:0.47.1" - "@oxc-transform/binding-win32-arm64-msvc": "npm:0.47.1" - "@oxc-transform/binding-win32-x64-msvc": "npm:0.47.1" - dependenciesMeta: - "@oxc-transform/binding-darwin-arm64": - optional: true - "@oxc-transform/binding-darwin-x64": - optional: true - "@oxc-transform/binding-linux-arm64-gnu": - optional: true - "@oxc-transform/binding-linux-arm64-musl": - optional: true - "@oxc-transform/binding-linux-x64-gnu": - optional: true - "@oxc-transform/binding-linux-x64-musl": - optional: true - "@oxc-transform/binding-win32-arm64-msvc": - optional: true - "@oxc-transform/binding-win32-x64-msvc": - optional: true - checksum: 10c0/b33ce8c54d9deb8827c35eefd022eac2522ddd486e8f3b9e07ef8f448b063094246f329207f4b631622734e3025c3bd128a208d1e6b38ba08e98282ec7312855 - languageName: node - linkType: hard - "p-cancelable@npm:^2.0.0": version: 2.1.1 resolution: "p-cancelable@npm:2.1.1" @@ -33242,6 +33240,18 @@ __metadata: languageName: node linkType: hard +"piscina@npm:^4.7.0": + version: 4.9.2 + resolution: "piscina@npm:4.9.2" + dependencies: + "@napi-rs/nice": "npm:^1.0.1" + dependenciesMeta: + "@napi-rs/nice": + optional: true + checksum: 10c0/ab67830065ff41523cd901db41b11045cb00a0be43bf79323ff7b4ef2fbce5e3a56ad440d99d6c3944ce94451a0a69fd175500e3220b21efe54142e601322189 + languageName: node + linkType: hard + "piscina@npm:^5.0.0": version: 5.1.4 resolution: "piscina@npm:5.1.4" @@ -34615,26 +34625,7 @@ __metadata: languageName: node linkType: hard -"react-native-web@npm:^0.20.0": - version: 0.20.0 - resolution: "react-native-web@npm:0.20.0" - dependencies: - "@babel/runtime": "npm:^7.18.6" - "@react-native/normalize-colors": "npm:^0.74.1" - fbjs: "npm:^3.0.4" - inline-style-prefixer: "npm:^7.0.1" - memoize-one: "npm:^6.0.0" - nullthrows: "npm:^1.1.1" - postcss-value-parser: "npm:^4.2.0" - styleq: "npm:^0.1.3" - peerDependencies: - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - checksum: 10c0/266c16c67ccc4114864cf4facac14c3736412c937af8cf031eaaa618e801723f2c4aac5bf2d680536bbbe95602b97c13a819e775602884e900dd1362bbe2f3f5 - languageName: node - linkType: hard - -"react-native-web@npm:^0.21.2": +"react-native-web@npm:^0.21.0, react-native-web@npm:^0.21.2": version: 0.21.2 resolution: "react-native-web@npm:0.21.2" dependencies: @@ -34858,41 +34849,6 @@ __metadata: languageName: node linkType: hard -"react-remove-scroll-bar@npm:^2.3.7": - version: 2.3.8 - resolution: "react-remove-scroll-bar@npm:2.3.8" - dependencies: - react-style-singleton: "npm:^2.2.2" - tslib: "npm:^2.0.0" - peerDependencies: - "@types/react": "*" - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/9a0675c66cbb52c325bdbfaed80987a829c4504cefd8ff2dd3b6b3afc9a1500b8ec57b212e92c1fb654396d07bbe18830a8146fe77677d2a29ce40b5e1f78654 - languageName: node - linkType: hard - -"react-remove-scroll@npm:^2.6.0": - version: 2.7.2 - resolution: "react-remove-scroll@npm:2.7.2" - dependencies: - react-remove-scroll-bar: "npm:^2.3.7" - react-style-singleton: "npm:^2.2.3" - tslib: "npm:^2.1.0" - use-callback-ref: "npm:^1.3.3" - use-sidecar: "npm:^1.1.3" - peerDependencies: - "@types/react": "*" - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/b5f3315bead75e72853f492c0b51ba8fb4fa09a4534d7fc42d6fcd59ca3e047cf213279ffc1e35b337e314ef5a04cb2b12544c85e0078802271731c27c09e5aa - languageName: node - linkType: hard - "react-router-dom@npm:^6.28.0": version: 6.30.3 resolution: "react-router-dom@npm:6.30.3" @@ -34939,22 +34895,6 @@ __metadata: languageName: node linkType: hard -"react-style-singleton@npm:^2.2.2, react-style-singleton@npm:^2.2.3": - version: 2.2.3 - resolution: "react-style-singleton@npm:2.2.3" - dependencies: - get-nonce: "npm:^1.0.0" - tslib: "npm:^2.0.0" - peerDependencies: - "@types/react": "*" - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/841938ff16d16a6b76895f4cb2e1fea957e5fe3b30febbf03a54892dae1c9153f2383e231dea0b3ba41192ad2f2849448fa859caccd288943bce32639e971bee - languageName: node - linkType: hard - "react-test-renderer@npm:^18.3.1": version: 18.3.1 resolution: "react-test-renderer@npm:18.3.1" @@ -37808,66 +37748,67 @@ __metadata: languageName: node linkType: hard -"tamagui@npm:1.126.14": - version: 1.126.14 - resolution: "tamagui@npm:1.126.14" +"tamagui@npm:1.144.4": + version: 1.144.4 + resolution: "tamagui@npm:1.144.4" dependencies: - "@tamagui/accordion": "npm:1.126.14" - "@tamagui/adapt": "npm:1.126.14" - "@tamagui/alert-dialog": "npm:1.126.14" - "@tamagui/animate-presence": "npm:1.126.14" - "@tamagui/avatar": "npm:1.126.14" - "@tamagui/button": "npm:1.126.14" - "@tamagui/card": "npm:1.126.14" - "@tamagui/checkbox": "npm:1.126.14" - "@tamagui/compose-refs": "npm:1.126.14" - "@tamagui/constants": "npm:1.126.14" - "@tamagui/core": "npm:1.126.14" - "@tamagui/create-context": "npm:1.126.14" - "@tamagui/dialog": "npm:1.126.14" - "@tamagui/elements": "npm:1.126.14" - "@tamagui/fake-react-native": "npm:1.126.14" - "@tamagui/focusable": "npm:1.126.14" - "@tamagui/font-size": "npm:1.126.14" - "@tamagui/form": "npm:1.126.14" - "@tamagui/get-button-sized": "npm:1.126.14" - "@tamagui/get-font-sized": "npm:1.126.14" - "@tamagui/get-token": "npm:1.126.14" - "@tamagui/group": "npm:1.126.14" - "@tamagui/helpers-tamagui": "npm:1.126.14" - "@tamagui/image": "npm:1.126.14" - "@tamagui/label": "npm:1.126.14" - "@tamagui/linear-gradient": "npm:1.126.14" - "@tamagui/list-item": "npm:1.126.14" - "@tamagui/polyfill-dev": "npm:1.126.14" - "@tamagui/popover": "npm:1.126.14" - "@tamagui/popper": "npm:1.126.14" - "@tamagui/portal": "npm:1.126.14" - "@tamagui/progress": "npm:1.126.14" - "@tamagui/radio-group": "npm:1.126.14" - "@tamagui/react-native-media-driver": "npm:1.126.14" - "@tamagui/scroll-view": "npm:1.126.14" - "@tamagui/select": "npm:1.126.14" - "@tamagui/separator": "npm:1.126.14" - "@tamagui/shapes": "npm:1.126.14" - "@tamagui/sheet": "npm:1.126.14" - "@tamagui/slider": "npm:1.126.14" - "@tamagui/stacks": "npm:1.126.14" - "@tamagui/switch": "npm:1.126.14" - "@tamagui/tabs": "npm:1.126.14" - "@tamagui/text": "npm:1.126.14" - "@tamagui/theme": "npm:1.126.14" - "@tamagui/toggle-group": "npm:1.126.14" - "@tamagui/tooltip": "npm:1.126.14" - "@tamagui/use-controllable-state": "npm:1.126.14" - "@tamagui/use-debounce": "npm:1.126.14" - "@tamagui/use-force-update": "npm:1.126.14" - "@tamagui/use-window-dimensions": "npm:1.126.14" - "@tamagui/visually-hidden": "npm:1.126.14" - "@tamagui/z-index-stack": "npm:1.126.14" + "@tamagui/accordion": "npm:1.144.4" + "@tamagui/adapt": "npm:1.144.4" + "@tamagui/alert-dialog": "npm:1.144.4" + "@tamagui/animate-presence": "npm:1.144.4" + "@tamagui/avatar": "npm:1.144.4" + "@tamagui/button": "npm:1.144.4" + "@tamagui/card": "npm:1.144.4" + "@tamagui/checkbox": "npm:1.144.4" + "@tamagui/compose-refs": "npm:1.144.4" + "@tamagui/constants": "npm:1.144.4" + "@tamagui/core": "npm:1.144.4" + "@tamagui/create-context": "npm:1.144.4" + "@tamagui/dialog": "npm:1.144.4" + "@tamagui/elements": "npm:1.144.4" + "@tamagui/fake-react-native": "npm:1.144.4" + "@tamagui/focusable": "npm:1.144.4" + "@tamagui/font-size": "npm:1.144.4" + "@tamagui/form": "npm:1.144.4" + "@tamagui/get-button-sized": "npm:1.144.4" + "@tamagui/get-font-sized": "npm:1.144.4" + "@tamagui/get-token": "npm:1.144.4" + "@tamagui/group": "npm:1.144.4" + "@tamagui/helpers-tamagui": "npm:1.144.4" + "@tamagui/image": "npm:1.144.4" + "@tamagui/label": "npm:1.144.4" + "@tamagui/linear-gradient": "npm:1.144.4" + "@tamagui/list-item": "npm:1.144.4" + "@tamagui/polyfill-dev": "npm:1.144.4" + "@tamagui/popover": "npm:1.144.4" + "@tamagui/popper": "npm:1.144.4" + "@tamagui/portal": "npm:1.144.4" + "@tamagui/progress": "npm:1.144.4" + "@tamagui/radio-group": "npm:1.144.4" + "@tamagui/react-native-media-driver": "npm:1.144.4" + "@tamagui/scroll-view": "npm:1.144.4" + "@tamagui/select": "npm:1.144.4" + "@tamagui/separator": "npm:1.144.4" + "@tamagui/shapes": "npm:1.144.4" + "@tamagui/sheet": "npm:1.144.4" + "@tamagui/slider": "npm:1.144.4" + "@tamagui/stacks": "npm:1.144.4" + "@tamagui/switch": "npm:1.144.4" + "@tamagui/tabs": "npm:1.144.4" + "@tamagui/text": "npm:1.144.4" + "@tamagui/theme": "npm:1.144.4" + "@tamagui/toggle-group": "npm:1.144.4" + "@tamagui/tooltip": "npm:1.144.4" + "@tamagui/use-controllable-state": "npm:1.144.4" + "@tamagui/use-debounce": "npm:1.144.4" + "@tamagui/use-force-update": "npm:1.144.4" + "@tamagui/use-window-dimensions": "npm:1.144.4" + "@tamagui/visually-hidden": "npm:1.144.4" + "@tamagui/z-index-stack": "npm:1.144.4" peerDependencies: react: "*" - checksum: 10c0/8c1475d12c65d745e03b209684a0069d1c1812e61569e0b957a946775ff09e896a6e32b0326c47de75eacb215a22521bb83ab2c589b1a0f7f226056f3f270b0b + react-native: "*" + checksum: 10c0/d46b66329eacb539eef04c44942cfe5c317a626c3236012673b3eff596601cdcb2d5ed3e28577517296cdf57b8c4437cea5b83003e58f180c13ac31f2f232701 languageName: node linkType: hard @@ -38594,7 +38535,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:2.8.1, tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.6.0, tslib@npm:^2.6.2, tslib@npm:^2.7.0, tslib@npm:^2.8.0, tslib@npm:^2.8.1": +"tslib@npm:2.8.1, tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.6.0, tslib@npm:^2.6.2, tslib@npm:^2.7.0, tslib@npm:^2.8.1": version: 2.8.1 resolution: "tslib@npm:2.8.1" checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 @@ -38910,7 +38851,7 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.8.2, typescript@npm:^5.9.2, typescript@npm:^5.9.3, typescript@npm:~5.9.3": +"typescript@npm:^5.9.2, typescript@npm:^5.9.3, typescript@npm:~5.9.3": version: 5.9.3 resolution: "typescript@npm:5.9.3" bin: @@ -38930,7 +38871,7 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^5.8.2#optional!builtin, typescript@patch:typescript@npm%3A^5.9.2#optional!builtin, typescript@patch:typescript@npm%3A^5.9.3#optional!builtin, typescript@patch:typescript@npm%3A~5.9.3#optional!builtin": +"typescript@patch:typescript@npm%3A^5.9.2#optional!builtin, typescript@patch:typescript@npm%3A^5.9.3#optional!builtin, typescript@patch:typescript@npm%3A~5.9.3#optional!builtin": version: 5.9.3 resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin::version=5.9.3&hash=5786d5" bin: @@ -39361,21 +39302,6 @@ __metadata: languageName: node linkType: hard -"use-callback-ref@npm:^1.3.3": - version: 1.3.3 - resolution: "use-callback-ref@npm:1.3.3" - dependencies: - tslib: "npm:^2.0.0" - peerDependencies: - "@types/react": "*" - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/f887488c6e6075cdad4962979da1714b217bcb1ee009a9e57ce9a844bcfc4c3a99e93983dfc2e5af9e0913824d24e730090ff255e902c516dcb58d2d3837e01c - languageName: node - linkType: hard - "use-isomorphic-layout-effect@npm:^1.1.2": version: 1.2.1 resolution: "use-isomorphic-layout-effect@npm:1.2.1" @@ -39397,22 +39323,6 @@ __metadata: languageName: node linkType: hard -"use-sidecar@npm:^1.1.3": - version: 1.1.3 - resolution: "use-sidecar@npm:1.1.3" - dependencies: - detect-node-es: "npm:^1.1.0" - tslib: "npm:^2.0.0" - peerDependencies: - "@types/react": "*" - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/161599bf921cfaa41c85d2b01c871975ee99260f3e874c2d41c05890d41170297bdcf314bc5185e7a700de2034ac5b888e3efc8e9f35724f4918f53538d717c9 - languageName: node - linkType: hard - "use-sync-external-store@npm:^1.2.0, use-sync-external-store@npm:^1.2.2, use-sync-external-store@npm:^1.5.0": version: 1.6.0 resolution: "use-sync-external-store@npm:1.6.0" @@ -40816,6 +40726,13 @@ __metadata: languageName: node linkType: hard +"xstate@npm:^5.30.0": + version: 5.30.0 + resolution: "xstate@npm:5.30.0" + checksum: 10c0/4c41be815bea6c62492952f813f363d8c630364010fdfd3be73b43b4eecaa9da3548e0b30d058c28f861c386d705b024815692ae654555cd1048dbba0ca5184f + languageName: node + linkType: hard + "y18n@npm:^4.0.0": version: 4.0.3 resolution: "y18n@npm:4.0.3" From 77df53b99072df1a066d56cb625cc36651e0e1fd Mon Sep 17 00:00:00 2001 From: Justin Hernandez Date: Wed, 8 Apr 2026 10:43:50 -0700 Subject: [PATCH 2/6] Add Firebase Analytics and upgrade app dependencies (#1944) * updates * upgrade packages * fixes --- app/ios/Podfile | 4 + app/ios/Podfile.lock | 66 ++++++- app/package.json | 14 +- package.json | 3 +- packages/mobile-sdk-alpha/package.json | 8 +- yarn.lock | 239 +++++++++++++++++++++++-- 6 files changed, 303 insertions(+), 31 deletions(-) diff --git a/app/ios/Podfile b/app/ios/Podfile index a7baec718..fe16e519a 100755 --- a/app/ios/Podfile +++ b/app/ios/Podfile @@ -61,6 +61,10 @@ def simulator_arm64_blockers end end +# Build RNFirebase pods as static frameworks so vendored Firebase XCFrameworks +# (e.g. GoogleAppMeasurement, FirebaseAnalytics) link correctly with use_frameworks! +$RNFirebaseAsStaticFramework = true + linkage = ENV["USE_FRAMEWORKS"] if linkage != nil Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green diff --git a/app/ios/Podfile.lock b/app/ios/Podfile.lock index 5768dfd30..2a16a6ec6 100644 --- a/app/ios/Podfile.lock +++ b/app/ios/Podfile.lock @@ -59,6 +59,11 @@ PODS: - Yoga - fast_float (6.1.4) - FBLazyVector (0.77.0) + - Firebase/Analytics (11.11.0): + - Firebase/Core + - Firebase/Core (11.11.0): + - Firebase/CoreOnly + - FirebaseAnalytics (~> 11.11.0) - Firebase/CoreOnly (11.11.0): - FirebaseCore (~> 11.11.0) - Firebase/Messaging (11.11.0): @@ -69,6 +74,24 @@ PODS: - FirebaseRemoteConfig (~> 11.11.0) - FirebaseABTesting (11.11.0): - FirebaseCore (~> 11.11.0) + - FirebaseAnalytics (11.11.0): + - FirebaseAnalytics/AdIdSupport (= 11.11.0) + - FirebaseCore (~> 11.11.0) + - FirebaseInstallations (~> 11.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.0) + - GoogleUtilities/MethodSwizzler (~> 8.0) + - GoogleUtilities/Network (~> 8.0) + - "GoogleUtilities/NSData+zlib (~> 8.0)" + - nanopb (~> 3.30910.0) + - FirebaseAnalytics/AdIdSupport (11.11.0): + - FirebaseCore (~> 11.11.0) + - FirebaseInstallations (~> 11.0) + - GoogleAppMeasurement (= 11.11.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.0) + - GoogleUtilities/MethodSwizzler (~> 8.0) + - GoogleUtilities/Network (~> 8.0) + - "GoogleUtilities/NSData+zlib (~> 8.0)" + - nanopb (~> 3.30910.0) - FirebaseCore (11.11.0): - FirebaseCoreInternal (~> 11.11.0) - GoogleUtilities/Environment (~> 8.0) @@ -103,6 +126,26 @@ PODS: - FirebaseSharedSwift (11.15.0) - fmt (11.0.2) - glog (0.3.5) + - GoogleAppMeasurement (11.11.0): + - GoogleAppMeasurement/AdIdSupport (= 11.11.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.0) + - GoogleUtilities/MethodSwizzler (~> 8.0) + - GoogleUtilities/Network (~> 8.0) + - "GoogleUtilities/NSData+zlib (~> 8.0)" + - nanopb (~> 3.30910.0) + - GoogleAppMeasurement/AdIdSupport (11.11.0): + - GoogleAppMeasurement/WithoutAdIdSupport (= 11.11.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.0) + - GoogleUtilities/MethodSwizzler (~> 8.0) + - GoogleUtilities/Network (~> 8.0) + - "GoogleUtilities/NSData+zlib (~> 8.0)" + - nanopb (~> 3.30910.0) + - GoogleAppMeasurement/WithoutAdIdSupport (11.11.0): + - GoogleUtilities/AppDelegateSwizzler (~> 8.0) + - GoogleUtilities/MethodSwizzler (~> 8.0) + - GoogleUtilities/Network (~> 8.0) + - "GoogleUtilities/NSData+zlib (~> 8.0)" + - nanopb (~> 3.30910.0) - GoogleDataTransport (10.1.0): - nanopb (~> 3.30910.0) - PromisesObjC (~> 2.4) @@ -121,6 +164,9 @@ PODS: - GoogleUtilities/Logger (8.1.0): - GoogleUtilities/Environment - GoogleUtilities/Privacy + - GoogleUtilities/MethodSwizzler (8.1.0): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy - GoogleUtilities/Network (8.1.0): - GoogleUtilities/Logger - "GoogleUtilities/NSData+zlib" @@ -1901,6 +1947,10 @@ PODS: - Yoga - RNDeviceInfo (15.0.2): - React-Core + - RNFBAnalytics (21.14.0): + - Firebase/Analytics (= 11.11.0) + - React-Core + - RNFBApp - RNFBApp (21.14.0): - Firebase/CoreOnly (= 11.11.0) - React-Core @@ -2269,6 +2319,7 @@ DEPENDENCIES: - "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)" - "RNCClipboard (from `../node_modules/@react-native-clipboard/clipboard`)" - RNDeviceInfo (from `../node_modules/react-native-device-info`) + - "RNFBAnalytics (from `../node_modules/@react-native-firebase/analytics`)" - "RNFBApp (from `../node_modules/@react-native-firebase/app`)" - "RNFBMessaging (from `../node_modules/@react-native-firebase/messaging`)" - "RNFBRemoteConfig (from `../node_modules/@react-native-firebase/remote-config`)" @@ -2294,6 +2345,7 @@ SPEC REPOS: - AppCheckCore - Firebase - FirebaseABTesting + - FirebaseAnalytics - FirebaseCore - FirebaseCoreExtension - FirebaseCoreInternal @@ -2302,6 +2354,7 @@ SPEC REPOS: - FirebaseRemoteConfig - FirebaseRemoteConfigInterop - FirebaseSharedSwift + - GoogleAppMeasurement - GoogleDataTransport - GoogleSignIn - GoogleUtilities @@ -2507,6 +2560,8 @@ EXTERNAL SOURCES: :path: "../node_modules/@react-native-clipboard/clipboard" RNDeviceInfo: :path: "../node_modules/react-native-device-info" + RNFBAnalytics: + :path: "../node_modules/@react-native-firebase/analytics" RNFBApp: :path: "../node_modules/@react-native-firebase/app" RNFBMessaging: @@ -2573,6 +2628,7 @@ SPEC CHECKSUMS: FBLazyVector: 2bc03a5cf64e29c611bbc5d7eb9d9f7431f37ee6 Firebase: 6a8f201c61eda24e98f1ce2b44b1b9c2caf525cc FirebaseABTesting: 8551c24eb28e300ce697f8eb72c1a519bb96eb40 + FirebaseAnalytics: acfa848bf81e1a4dbf60ef1f0eddd7328fe6673e FirebaseCore: 2321536f9c423b1f857e047a82b8a42abc6d9e2c FirebaseCoreExtension: 3a64994969dd05f4bcb7e6896c654eded238e75b FirebaseCoreInternal: 31ee350d87b30a9349e907f84bf49ef8e6791e5a @@ -2583,6 +2639,7 @@ SPEC CHECKSUMS: FirebaseSharedSwift: e17c654ef1f1a616b0b33054e663ad1035c8fd40 fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd glog: eb93e2f488219332457c3c4eafd2738ddc7e80b8 + GoogleAppMeasurement: 8a82b93a6400c8e6551c0bcd66a9177f2e067aed GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleSignIn: fcee2257188d5eda57a5e2b6a715550ffff9206d GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 @@ -2671,9 +2728,10 @@ SPEC CHECKSUMS: RNCAsyncStorage: 6a8127b6987dc9fbce778669b252b14c8355c7ce RNCClipboard: 9f7b908de4bf4353871fb454c15fc03db4917b88 RNDeviceInfo: 4c852998208b60dc192ae3529e5867817719ad1e - RNFBApp: 4105e54d9ca4a1c10893a032268470f670181110 - RNFBMessaging: 6857871d9dff8f26b0c325fc7d97ba69cb77d213 - RNFBRemoteConfig: 8d3675f18c052483ce294bb97b857428467fb41e + RNFBAnalytics: 03c83ba4617a3754c99e66267983efcc908932a9 + RNFBApp: a448037d2df74af9d374a0b765be12ff1e844dc0 + RNFBMessaging: 0f0498a95c605e3afcf13ac5f349d0b201ea65f6 + RNFBRemoteConfig: 4eb5fc9f21dc324153c7d3f5b48c935ab9031876 RNGestureHandler: 36aca36e4ef19f55dbf97239199d00fd58494e34 RNGoogleSignin: 60c3f470558dbff0ae54f2f164ef82a89d3eb561 RNInAppBrowser: 904d24dc75e8e6c6c98a3160329192608946f9df @@ -2694,6 +2752,6 @@ SPEC CHECKSUMS: Yoga: c34725819ab0a5962e85455b9e56679b306910ee YttriumWrapper: d7f63336830536f1da41b745ed8bacedb04228c4 -PODFILE CHECKSUM: 0b99fae2ec87b0be3e6d9b3fd87360fe0a84b25f +PODFILE CHECKSUM: 83f631d1b6308502a035e656b2f9dfab30431ae3 COCOAPODS: 1.16.2 diff --git a/app/package.json b/app/package.json index 93ca5ffa5..dde9f9780 100644 --- a/app/package.json +++ b/app/package.json @@ -75,11 +75,6 @@ "web:build": "yarn build:deps && vite build", "web:preview": "vite preview" }, - "resolutions": { - "punycode": "npm:punycode.js@2.3.1", - "react-native-blur-effect": "1.1.3", - "react-native-webview": "13.16.0" - }, "overrides": { "punycode": "npm:punycode.js@2.3.1", "react-native-blur-effect": "1.1.3", @@ -97,6 +92,7 @@ "@react-native-clipboard/clipboard": "1.16.3", "@react-native-community/blur": "^4.4.1", "@react-native-community/netinfo": "^11.4.1", + "@react-native-firebase/analytics": "^21.14.0", "@react-native-firebase/app": "^21.14.0", "@react-native-firebase/messaging": "^21.14.0", "@react-native-firebase/remote-config": "^21.14.0", @@ -124,7 +120,7 @@ "@walletconnect/react-native-compat": "^2.23.9", "@xstate/react": "^5.0.3", "asn1js": "^3.0.7", - "axios": "^1.14.0", + "axios": "^1.15.0", "buffer": "^6.0.3", "country-emoji": "^1.5.6", "elliptic": "^6.6.1", @@ -169,7 +165,7 @@ "react-native-svg-web": "1.0.9", "react-native-url-polyfill": "^3.0.0", "react-native-web": "^0.21.2", - "react-native-webview": "13.16.0", + "react-native-webview": "13.16.1", "react-qr-barcode-scanner": "^2.1.25", "socket.io-client": "^4.8.3", "tamagui": "1.144.4", @@ -206,8 +202,8 @@ "@types/react-native-dotenv": "^0.2.2", "@types/react-native-sqlite-storage": "^6.0.5", "@types/react-native-web": "^0", - "@typescript-eslint/eslint-plugin": "^8.39.0", - "@typescript-eslint/parser": "^8.39.0", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", "@vitejs/plugin-react-swc": "^4.3.0", "babel-plugin-module-resolver": "^5.0.3", "babel-plugin-transform-remove-console": "^6.9.4", diff --git a/package.json b/package.json index f373a4a77..af39ff883 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-native-passkey": "^3.3.3", + "react-native-blur-effect": "1.1.3", "react-native-webview": "13.16.0" }, "dependencies": { @@ -88,7 +89,7 @@ "tsx": "^4.21.0", "typescript": "^5.9.3" }, - "packageManager": "yarn@4.12.0", + "packageManager": "yarn@4.13.0", "engines": { "node": ">=22 <23" } diff --git a/packages/mobile-sdk-alpha/package.json b/packages/mobile-sdk-alpha/package.json index 09c19dbec..3a55e7357 100644 --- a/packages/mobile-sdk-alpha/package.json +++ b/packages/mobile-sdk-alpha/package.json @@ -192,7 +192,7 @@ "eslint-plugin-sort-exports": "^0.9.1", "fake-indexeddb": "^6.2.5", "jsdom": "^25.0.1", - "lottie-react-native": "7.2.2", + "lottie-react-native": "7.3.6", "poseidon-lite": "^0.3.0", "prettier": "^3.5.3", "react": "^18.3.1", @@ -211,9 +211,10 @@ }, "peerDependencies": { "@react-native-async-storage/async-storage": ">=1.0.0", - "lottie-react-native": "7.2.2", + "lottie-react-native": "^7.2.2", "react": "^18.3.1", "react-native": ">=0.76.0 <0.78.0", + "react-native-blur-effect": "^1.1.3", "react-native-get-random-values": ">=1.0.0", "react-native-haptic-feedback": "*", "react-native-keychain": ">=8.0.0", @@ -225,6 +226,9 @@ "@react-native-async-storage/async-storage": { "optional": true }, + "react-native-blur-effect": { + "optional": true + }, "react-native-get-random-values": { "optional": true }, diff --git a/yarn.lock b/yarn.lock index e8a117ef0..d513cc3e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4713,7 +4713,18 @@ __metadata: languageName: node linkType: hard -"@eslint-community/regexpp@npm:^4.10.0, @eslint-community/regexpp@npm:^4.6.1": +"@eslint-community/eslint-utils@npm:^4.9.1": + version: 4.9.1 + resolution: "@eslint-community/eslint-utils@npm:4.9.1" + dependencies: + eslint-visitor-keys: "npm:^3.4.3" + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + checksum: 10c0/dc4ab5e3e364ef27e33666b11f4b86e1a6c1d7cbf16f0c6ff87b1619b3562335e9201a3d6ce806221887ff780ec9d828962a290bb910759fd40a674686503f02 + languageName: node + linkType: hard + +"@eslint-community/regexpp@npm:^4.10.0, @eslint-community/regexpp@npm:^4.12.2, @eslint-community/regexpp@npm:^4.6.1": version: 4.12.2 resolution: "@eslint-community/regexpp@npm:4.12.2" checksum: 10c0/fddcbc66851b308478d04e302a4d771d6917a0b3740dc351513c0da9ca2eab8a1adf99f5e0aa7ab8b13fa0df005c81adeee7e63a92f3effd7d367a163b721c2d @@ -9386,6 +9397,17 @@ __metadata: languageName: node linkType: hard +"@react-native-firebase/analytics@npm:^21.14.0": + version: 21.14.0 + resolution: "@react-native-firebase/analytics@npm:21.14.0" + dependencies: + superstruct: "npm:^2.0.2" + peerDependencies: + "@react-native-firebase/app": 21.14.0 + checksum: 10c0/1c5ea1f2fb3defb816209a33511db0196d258fdd332b5c89c468f2b5e57ab3300f04a7f3e893e5cfadc9021bae525029709e1efbec3968b7a8f7a93cba8435d7 + languageName: node + linkType: hard + "@react-native-firebase/app@npm:^21.14.0": version: 21.14.0 resolution: "@react-native-firebase/app@npm:21.14.0" @@ -11162,6 +11184,7 @@ __metadata: "@react-native-community/blur": "npm:^4.4.1" "@react-native-community/cli": "npm:^16.0.3" "@react-native-community/netinfo": "npm:^11.4.1" + "@react-native-firebase/analytics": "npm:^21.14.0" "@react-native-firebase/app": "npm:^21.14.0" "@react-native-firebase/messaging": "npm:^21.14.0" "@react-native-firebase/remote-config": "npm:^21.14.0" @@ -11205,13 +11228,13 @@ __metadata: "@types/react-native-dotenv": "npm:^0.2.2" "@types/react-native-sqlite-storage": "npm:^6.0.5" "@types/react-native-web": "npm:^0" - "@typescript-eslint/eslint-plugin": "npm:^8.39.0" - "@typescript-eslint/parser": "npm:^8.39.0" + "@typescript-eslint/eslint-plugin": "npm:^8.58.1" + "@typescript-eslint/parser": "npm:^8.58.1" "@vitejs/plugin-react-swc": "npm:^4.3.0" "@walletconnect/react-native-compat": "npm:^2.23.9" "@xstate/react": "npm:^5.0.3" asn1js: "npm:^3.0.7" - axios: "npm:^1.14.0" + axios: "npm:^1.15.0" babel-plugin-module-resolver: "npm:^5.0.3" babel-plugin-transform-remove-console: "npm:^6.9.4" buffer: "npm:^6.0.3" @@ -11275,7 +11298,7 @@ __metadata: react-native-svg-web: "npm:1.0.9" react-native-url-polyfill: "npm:^3.0.0" react-native-web: "npm:^0.21.2" - react-native-webview: "npm:13.16.0" + react-native-webview: "npm:13.16.1" react-qr-barcode-scanner: "npm:^2.1.25" react-test-renderer: "npm:^18.3.1" rollup-plugin-visualizer: "npm:^6.0.5" @@ -11318,7 +11341,7 @@ __metadata: eslint-plugin-sort-exports: "npm:^0.9.1" fake-indexeddb: "npm:^6.2.5" jsdom: "npm:^25.0.1" - lottie-react-native: "npm:7.2.2" + lottie-react-native: "npm:7.3.6" node-forge: "npm:^1.3.3" poseidon-lite: "npm:^0.3.0" prettier: "npm:^3.5.3" @@ -11343,9 +11366,10 @@ __metadata: zustand: "npm:^4.5.2" peerDependencies: "@react-native-async-storage/async-storage": ">=1.0.0" - lottie-react-native: 7.2.2 + lottie-react-native: ^7.2.2 react: ^18.3.1 react-native: ">=0.76.0 <0.78.0" + react-native-blur-effect: ^1.1.3 react-native-get-random-values: ">=1.0.0" react-native-haptic-feedback: "*" react-native-keychain: ">=8.0.0" @@ -11355,6 +11379,8 @@ __metadata: peerDependenciesMeta: "@react-native-async-storage/async-storage": optional: true + react-native-blur-effect: + optional: true react-native-get-random-values: optional: true react-native-keychain: @@ -17056,7 +17082,7 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^8.0.0, @typescript-eslint/eslint-plugin@npm:^8.39.0, @typescript-eslint/eslint-plugin@npm:^8.44.0": +"@typescript-eslint/eslint-plugin@npm:^8.0.0, @typescript-eslint/eslint-plugin@npm:^8.44.0": version: 8.50.1 resolution: "@typescript-eslint/eslint-plugin@npm:8.50.1" dependencies: @@ -17076,6 +17102,26 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/eslint-plugin@npm:^8.58.1": + version: 8.58.1 + resolution: "@typescript-eslint/eslint-plugin@npm:8.58.1" + dependencies: + "@eslint-community/regexpp": "npm:^4.12.2" + "@typescript-eslint/scope-manager": "npm:8.58.1" + "@typescript-eslint/type-utils": "npm:8.58.1" + "@typescript-eslint/utils": "npm:8.58.1" + "@typescript-eslint/visitor-keys": "npm:8.58.1" + ignore: "npm:^7.0.5" + natural-compare: "npm:^1.4.0" + ts-api-utils: "npm:^2.5.0" + peerDependencies: + "@typescript-eslint/parser": ^8.58.1 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/694bdcb2b775a7d8b99e39701cd4b56ad0645063333b3bf3eb3f2802ba01122c442753677efedd65485c89af82cd7397ce14b50a54834e61bda4feae67ca1c8c + languageName: node + linkType: hard + "@typescript-eslint/parser@npm:^7.1.1": version: 7.18.0 resolution: "@typescript-eslint/parser@npm:7.18.0" @@ -17094,7 +17140,7 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/parser@npm:^8.0.0, @typescript-eslint/parser@npm:^8.39.0, @typescript-eslint/parser@npm:^8.44.0": +"@typescript-eslint/parser@npm:^8.0.0, @typescript-eslint/parser@npm:^8.44.0": version: 8.50.1 resolution: "@typescript-eslint/parser@npm:8.50.1" dependencies: @@ -17110,6 +17156,22 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/parser@npm:^8.58.1": + version: 8.58.1 + resolution: "@typescript-eslint/parser@npm:8.58.1" + dependencies: + "@typescript-eslint/scope-manager": "npm:8.58.1" + "@typescript-eslint/types": "npm:8.58.1" + "@typescript-eslint/typescript-estree": "npm:8.58.1" + "@typescript-eslint/visitor-keys": "npm:8.58.1" + debug: "npm:^4.4.3" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/f1a1907079c2c2611011125218b0975d99547ac834ac434d7ff4e99fee4e938aedd6b8530ecdc5efc7bcc1a3b9d546252e318690d3e670c394b891ba75e66925 + languageName: node + linkType: hard + "@typescript-eslint/project-service@npm:8.50.1": version: 8.50.1 resolution: "@typescript-eslint/project-service@npm:8.50.1" @@ -17123,6 +17185,19 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/project-service@npm:8.58.1": + version: 8.58.1 + resolution: "@typescript-eslint/project-service@npm:8.58.1" + dependencies: + "@typescript-eslint/tsconfig-utils": "npm:^8.58.1" + "@typescript-eslint/types": "npm:^8.58.1" + debug: "npm:^4.4.3" + peerDependencies: + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/c48541a1350f12817b1ab54ab0e4d2a853811449fdc6d02a0d9b617520262fd286d1e3c4adf38b677e807df84cdbf32033e898e71ec7649299ce92e820f8e85d + languageName: node + linkType: hard + "@typescript-eslint/scope-manager@npm:5.62.0": version: 5.62.0 resolution: "@typescript-eslint/scope-manager@npm:5.62.0" @@ -17153,6 +17228,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/scope-manager@npm:8.58.1": + version: 8.58.1 + resolution: "@typescript-eslint/scope-manager@npm:8.58.1" + dependencies: + "@typescript-eslint/types": "npm:8.58.1" + "@typescript-eslint/visitor-keys": "npm:8.58.1" + checksum: 10c0/c7c67d249a9d1dd348ec29878e588422f2fe15531dfe83ff6fa35b8a0bffc2db9ee8a4e8fcc086742a32bc0c5da6c8ff3f4d4b007a62019b3f1da4381947ea7e + languageName: node + linkType: hard + "@typescript-eslint/tsconfig-utils@npm:8.50.1, @typescript-eslint/tsconfig-utils@npm:^8.50.1": version: 8.50.1 resolution: "@typescript-eslint/tsconfig-utils@npm:8.50.1" @@ -17162,6 +17247,15 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/tsconfig-utils@npm:8.58.1, @typescript-eslint/tsconfig-utils@npm:^8.58.1": + version: 8.58.1 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.58.1" + peerDependencies: + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/dcccf8c64e3806e3bcac750f9746f852cbf36abb816afb3e3a825f7d0268eb0bf3aa97c019082d0976508b93d2f09ff21cdfffcbffdc3204db3cb98cd0aa33cc + languageName: node + linkType: hard + "@typescript-eslint/type-utils@npm:7.18.0": version: 7.18.0 resolution: "@typescript-eslint/type-utils@npm:7.18.0" @@ -17195,6 +17289,22 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/type-utils@npm:8.58.1": + version: 8.58.1 + resolution: "@typescript-eslint/type-utils@npm:8.58.1" + dependencies: + "@typescript-eslint/types": "npm:8.58.1" + "@typescript-eslint/typescript-estree": "npm:8.58.1" + "@typescript-eslint/utils": "npm:8.58.1" + debug: "npm:^4.4.3" + ts-api-utils: "npm:^2.5.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/df3dd6f69edd8dd52c576882e8da0e810b47ad1608a3a57d82ff8a2ca12f134a715d0e1ec994bf877a7c6aecdeea349c305b3b8e4b39359c0c90417dc1cb9244 + languageName: node + linkType: hard + "@typescript-eslint/types@npm:5.62.0": version: 5.62.0 resolution: "@typescript-eslint/types@npm:5.62.0" @@ -17216,6 +17326,13 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/types@npm:8.58.1, @typescript-eslint/types@npm:^8.58.1": + version: 8.58.1 + resolution: "@typescript-eslint/types@npm:8.58.1" + checksum: 10c0/c468e2e3748d0d9a178b1e0f4a8dccb95085ba732ba9e462c21a3ac9be91ab63ce8147f3a181081f7a758f9c885ee6b2e0f5f890ee3f0f405e3caab515130b1a + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:5.62.0": version: 5.62.0 resolution: "@typescript-eslint/typescript-estree@npm:5.62.0" @@ -17272,6 +17389,25 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/typescript-estree@npm:8.58.1": + version: 8.58.1 + resolution: "@typescript-eslint/typescript-estree@npm:8.58.1" + dependencies: + "@typescript-eslint/project-service": "npm:8.58.1" + "@typescript-eslint/tsconfig-utils": "npm:8.58.1" + "@typescript-eslint/types": "npm:8.58.1" + "@typescript-eslint/visitor-keys": "npm:8.58.1" + debug: "npm:^4.4.3" + minimatch: "npm:^10.2.2" + semver: "npm:^7.7.3" + tinyglobby: "npm:^0.2.15" + ts-api-utils: "npm:^2.5.0" + peerDependencies: + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/06ad23dc71a7733c3f01019b7d426c2ebe1f4a845f3843d22f69c63aba8a3e8224a3e847996382da8ce253b3cff42f4f69a57b3db0bb2bc938291bf31d79ea4a + languageName: node + linkType: hard + "@typescript-eslint/utils@npm:7.18.0": version: 7.18.0 resolution: "@typescript-eslint/utils@npm:7.18.0" @@ -17301,6 +17437,21 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/utils@npm:8.58.1": + version: 8.58.1 + resolution: "@typescript-eslint/utils@npm:8.58.1" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.9.1" + "@typescript-eslint/scope-manager": "npm:8.58.1" + "@typescript-eslint/types": "npm:8.58.1" + "@typescript-eslint/typescript-estree": "npm:8.58.1" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/99538feaaa7e5a08c8cfeaaeff5775812bdaf9faba602d55341102761e84ffee8e1fbfbadc9dbd9b036feedc6b541550b300fe26b90ae92f92d1b687dc65ecda + languageName: node + linkType: hard + "@typescript-eslint/utils@npm:^5.10.0": version: 5.62.0 resolution: "@typescript-eslint/utils@npm:5.62.0" @@ -17349,6 +17500,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/visitor-keys@npm:8.58.1": + version: 8.58.1 + resolution: "@typescript-eslint/visitor-keys@npm:8.58.1" + dependencies: + "@typescript-eslint/types": "npm:8.58.1" + eslint-visitor-keys: "npm:^5.0.0" + checksum: 10c0/d2709bfb63bd86eb7b28bc86c15d9b29a8cceb5e25843418b039f497a1007fc92fa02eef8a2cbfd9cdec47f490205a00eab7fb204fd14472cf31b8db0e2db963 + languageName: node + linkType: hard + "@ungap/structured-clone@npm:^1.2.0, @ungap/structured-clone@npm:^1.3.0": version: 1.3.0 resolution: "@ungap/structured-clone@npm:1.3.0" @@ -19404,14 +19565,14 @@ __metadata: languageName: node linkType: hard -"axios@npm:^1.14.0": - version: 1.14.0 - resolution: "axios@npm:1.14.0" +"axios@npm:^1.15.0": + version: 1.15.0 + resolution: "axios@npm:1.15.0" dependencies: follow-redirects: "npm:^1.15.11" form-data: "npm:^4.0.5" proxy-from-env: "npm:^2.1.0" - checksum: 10c0/2541f4aa215a7d1842429dad006fc682d82bc0e74bd14500823f7d8cce3bbae0e0a8c328c8538946718f366ab8ce5a4c12e9ad40e5a0f3482ff8bff0cd115d45 + checksum: 10c0/47e0f860e98d4d7aa145e89ce0cae00e1fb0f1d2485f065c21fce955ddb1dba4103a46bd0e47acd18a27208a7f62c96249e620db575521b92a968619ab133409 languageName: node linkType: hard @@ -19814,6 +19975,13 @@ __metadata: languageName: node linkType: hard +"balanced-match@npm:^4.0.2": + version: 4.0.4 + resolution: "balanced-match@npm:4.0.4" + checksum: 10c0/07e86102a3eb2ee2a6a1a89164f29d0dbaebd28f2ca3f5ca786f36b8b23d9e417eb3be45a4acf754f837be5ac0a2317de90d3fcb7f4f4dc95720a1f36b26a17b + languageName: node + linkType: hard + "bare-events@npm:^2.5.4, bare-events@npm:^2.7.0": version: 2.8.2 resolution: "bare-events@npm:2.8.2" @@ -20227,6 +20395,15 @@ __metadata: languageName: node linkType: hard +"brace-expansion@npm:^5.0.5": + version: 5.0.5 + resolution: "brace-expansion@npm:5.0.5" + dependencies: + balanced-match: "npm:^4.0.2" + checksum: 10c0/4d238e14ed4f5cc9c07285550a41cef23121ca08ba99fa9eb5b55b580dcb6bf868b8210aa10526bdc9f8dc97f33ca2a7259039c4cc131a93042beddb424c48e3 + languageName: node + linkType: hard + "braces@npm:^3.0.3, braces@npm:~3.0.2": version: 3.0.3 resolution: "braces@npm:3.0.3" @@ -24030,6 +24207,13 @@ __metadata: languageName: node linkType: hard +"eslint-visitor-keys@npm:^5.0.0": + version: 5.0.1 + resolution: "eslint-visitor-keys@npm:5.0.1" + checksum: 10c0/16190bdf2cbae40a1109384c94450c526a79b0b9c3cb21e544256ed85ac48a4b84db66b74a6561d20fe6ab77447f150d711c2ad5ad74df4fcc133736bce99678 + languageName: node + linkType: hard + "eslint@npm:^8.57.0": version: 8.57.1 resolution: "eslint@npm:8.57.1" @@ -26797,7 +26981,7 @@ __metadata: languageName: node linkType: hard -"ignore@npm:7.0.5, ignore@npm:^7.0.0": +"ignore@npm:7.0.5, ignore@npm:^7.0.0, ignore@npm:^7.0.5": version: 7.0.5 resolution: "ignore@npm:7.0.5" checksum: 10c0/ae00db89fe873064a093b8999fe4cc284b13ef2a178636211842cceb650b9c3e390d3339191acb145d81ed5379d2074840cf0c33a20bdbd6f32821f79eb4ad5d @@ -31096,6 +31280,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^10.2.2": + version: 10.2.5 + resolution: "minimatch@npm:10.2.5" + dependencies: + brace-expansion: "npm:^5.0.5" + checksum: 10c0/6bb058bd6324104b9ec2f763476a35386d05079c1f5fe4fbf1f324a25237cd4534d6813ecd71f48208f4e635c1221899bef94c3c89f7df55698fe373aaae20fd + languageName: node + linkType: hard + "minimatch@npm:^5.0.1, minimatch@npm:^5.1.6": version: 5.1.6 resolution: "minimatch@npm:5.1.6" @@ -34239,7 +34432,7 @@ __metadata: languageName: node linkType: hard -"react-native-blur-effect@npm:^1.1.3": +"react-native-blur-effect@npm:1.1.3": version: 1.1.3 resolution: "react-native-blur-effect@npm:1.1.3" peerDependencies: @@ -37594,6 +37787,13 @@ __metadata: languageName: node linkType: hard +"superstruct@npm:^2.0.2": + version: 2.0.2 + resolution: "superstruct@npm:2.0.2" + checksum: 10c0/c6853db5240b4920f47b3c864dd1e23ede6819ea399ad29a65387d746374f6958c5f1c5b7e5bb152d9db117a74973e5005056d9bb83c24e26f18ec6bfae4a718 + languageName: node + linkType: hard + "supports-color@npm:^3.1.0": version: 3.2.3 resolution: "supports-color@npm:3.2.3" @@ -38355,6 +38555,15 @@ __metadata: languageName: node linkType: hard +"ts-api-utils@npm:^2.5.0": + version: 2.5.0 + resolution: "ts-api-utils@npm:2.5.0" + peerDependencies: + typescript: ">=4.8.4" + checksum: 10c0/767849383c114e7f1971fa976b20e73ac28fd0c70d8d65c0004790bf4d8f89888c7e4cf6d5949f9c1beae9bc3c64835bef77bbe27fddf45a3c7b60cebcf85c8c + languageName: node + linkType: hard + "ts-command-line-args@npm:^2.2.0": version: 2.5.1 resolution: "ts-command-line-args@npm:2.5.1" From 3e714facf543d5779818e03ed4618672ef06e0a2 Mon Sep 17 00:00:00 2001 From: Justin Hernandez Date: Wed, 8 Apr 2026 16:56:32 -0700 Subject: [PATCH 3/6] Rename KYC references to be platform agnostic (#1947) * first pass * clean up * format * pr feedback and format --- .claude/skills/spec-from-audit/SKILL.md | 2 +- CLAUDE.md | 2 +- app/android/app/src/debug/AndroidManifest.xml | 2 +- app/android/app/src/main/AndroidManifest.xml | 4 +- ...{useDiditLauncher.ts => useKycLauncher.ts} | 37 ++++++++------- ...seDiditWebSocket.ts => useKycWebSocket.ts} | 46 +++++++++++-------- app/src/hooks/usePendingKycRecovery.ts | 6 +-- app/src/integrations/{didit => kyc}/index.ts | 12 ++--- .../diditService.ts => kyc/kycService.ts} | 26 +++++------ app/src/integrations/{didit => kyc}/types.ts | 2 +- app/src/providers/selfClientProvider.tsx | 26 ++++++----- .../aadhaar/AadhaarUploadErrorScreen.tsx | 8 ++-- .../scanning/DocumentCameraTroubleScreen.tsx | 6 +-- .../scanning/DocumentNFCTroubleScreen.tsx | 6 +-- .../RegistrationFallbackMRZScreen.tsx | 8 ++-- .../RegistrationFallbackNFCScreen.tsx | 8 ++-- .../selection/LogoConfirmationScreen.tsx | 10 ++-- app/src/screens/kyc/KycSuccessScreen.tsx | 4 +- app/src/stores/errorInjectionStore.ts | 10 ++-- .../src/hooks/usePendingKycRecovery.test.ts | 8 ++-- app/tests/src/navigation.test.tsx | 2 +- .../src/screens/kyc/KycSuccessScreen.test.tsx | 4 +- common/src/utils/types.ts | 6 +-- .../contracts/constants/AttestationId.sol | 4 +- docs/maintenance/tech-debt-baseline.json | 2 +- .../onboarding/ProviderLaunchScreen.tsx | 14 +++--- ...{diditAttestation.ts => kycAttestation.ts} | 2 +- .../{diditProvider.ts => kycProvider.ts} | 20 ++++---- purple/global/authentication.md | 2 +- purple/global/external-services.md | 4 +- purple/temp/codebase-analysis.md | 2 +- .../sdk/workstreams/kmp-revival/SPEC.md | 12 ++--- .../sdk/workstreams/sdk-distribution/SPEC.md | 28 +++++------ 33 files changed, 172 insertions(+), 163 deletions(-) rename app/src/hooks/{useDiditLauncher.ts => useKycLauncher.ts} (78%) rename app/src/hooks/{useDiditWebSocket.ts => useKycWebSocket.ts} (79%) rename app/src/integrations/{didit => kyc}/index.ts (57%) rename app/src/integrations/{didit/diditService.ts => kyc/kycService.ts} (69%) rename app/src/integrations/{didit => kyc}/types.ts (94%) rename packages/webview-app/src/utils/{diditAttestation.ts => kycAttestation.ts} (95%) rename packages/webview-app/src/utils/{diditProvider.ts => kycProvider.ts} (85%) diff --git a/.claude/skills/spec-from-audit/SKILL.md b/.claude/skills/spec-from-audit/SKILL.md index 1d8ff769e..40f6cdec4 100644 --- a/.claude/skills/spec-from-audit/SKILL.md +++ b/.claude/skills/spec-from-audit/SKILL.md @@ -101,7 +101,7 @@ Follow these strictly: 1. **Decisions, not options** — "Use local wrappers" not "Consider adding to Euclid or using local wrappers." Every ambiguous implementation choice must be resolved in the spec. If you genuinely can't decide, flag it as a blocker and ask the user — don't embed it as an option. 2. **Second person** — "You are fixing...", "You will modify..." -3. **Exact file paths with line numbers** — `src/utils/sumsubProvider.ts:118`, not "the provider file" +3. **Exact file paths with line numbers** — `src/utils/kycProvider.ts:118`, not "the provider file" 4. **Current code, not stale references** — you read the files in Step 2, use what you actually saw 5. **Explicit constraints** — "You will NOT modify..." sections prevent scope creep 6. **Required vs optional** — mark every item. Don't let agents infer priority. diff --git a/CLAUDE.md b/CLAUDE.md index 66758b0f8..38dbb7f9f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -73,7 +73,7 @@ Specs are agent-executable prompts. A new Claude Code session with no prior cont - **Make decisions, not options.** "Use local wrappers" not "Consider adding to Euclid or using local wrappers." Agents can't choose between approaches — tell them which one. - **Use second person.** "You are fixing X" not "X should be fixed." - **Be explicit about constraints.** "You will NOT modify..." not just "Focus on..." -- **Provide exact file paths with line numbers.** `src/utils/sumsubProvider.ts:118` not "the provider file." +- **Provide exact file paths with line numbers.** `src/utils/kycProvider.ts:118` not "the provider file." - **State the validation command.** Agents will run it. If it's not there, they'll skip validation. - **One spec = one PR.** Target the PR size from Key Rules (1k–3k LOC). If a spec would exceed that, split it. - **Mark items as required vs optional.** Don't let agents infer priority. diff --git a/app/android/app/src/debug/AndroidManifest.xml b/app/android/app/src/debug/AndroidManifest.xml index c8db40b13..bc6dbce28 100644 --- a/app/android/app/src/debug/AndroidManifest.xml +++ b/app/android/app/src/debug/AndroidManifest.xml @@ -14,7 +14,7 @@ tools:ignore="GoogleAppIndexingWarning" tools:replace="android:usesCleartextTraffic"> - + - + - + void | Promise; /** @@ -41,42 +44,42 @@ export interface UseDiditLauncherOptions { */ onError?: ( error: unknown, - result?: DiditVerificationResult, + result?: KycVerificationResult, ) => void | Promise; } /** - * Custom hook for launching Didit verification with consistent error handling. + * Custom hook for launching KYC verification with consistent error handling. * * Abstracts the common pattern of: * 1. Creating a session - * 2. Launching Didit SDK + * 2. Launching the provider SDK * 3. Handling errors by navigating to fallback screen * 4. Managing loading state * * @example * ```tsx - * const { launchDiditVerification, isLoading } = useDiditLauncher({ + * const { launchKycVerification, isLoading } = useKycLauncher({ * countryCode: 'US', * errorSource: 'nfc_scan_failed', * }); * - * * ``` */ -export const useDiditLauncher = (options: UseDiditLauncherOptions) => { +export const useKycLauncher = (options: UseKycLauncherOptions) => { const { countryCode, errorSource, onSuccess, onCancel, onError } = options; const navigation = useNavigation>(); const [isLoading, setIsLoading] = useState(false); - const launchDiditVerification = useCallback(async () => { + const launchKycVerification = useCallback(async () => { setIsLoading(true); try { - const session = await createSession(); - const result = await launchDidit(session.sessionToken); + const session = await createKycSession(); + const result = await startKycVerification(session.sessionToken); // Handle user cancellation if (result.type === 'cancelled') { @@ -89,7 +92,7 @@ export const useDiditLauncher = (options: UseDiditLauncherOptions) => { const error = result.error?.message || result.error?.type || 'Unknown error'; const safeError = sanitizeErrorMessage(error); - console.error('Didit verification failed:', safeError); + console.error('KYC verification failed:', safeError); // Call custom error handler if provided, otherwise navigate to fallback screen if (onError) { @@ -134,7 +137,7 @@ export const useDiditLauncher = (options: UseDiditLauncherOptions) => { }, [navigation, countryCode, errorSource, onSuccess, onCancel, onError]); return { - launchDiditVerification, + launchKycVerification, isLoading, }; }; diff --git a/app/src/hooks/useDiditWebSocket.ts b/app/src/hooks/useKycWebSocket.ts similarity index 79% rename from app/src/hooks/useDiditWebSocket.ts rename to app/src/hooks/useKycWebSocket.ts index 323b55439..e62777b78 100644 --- a/app/src/hooks/useDiditWebSocket.ts +++ b/app/src/hooks/useKycWebSocket.ts @@ -9,12 +9,15 @@ import { DIDIT_TEE_URL } from '@env'; import { deserializeApplicantInfo } from '@selfxyz/common'; import type { DocumentType, KycData } from '@selfxyz/common/utils/types'; -import type { ApplicantInfoSerialized } from '@/integrations/didit/types'; +import type { ApplicantInfoSerialized } from '@/integrations/kyc/types'; import { navigationRef } from '@/navigation'; import { storeDocumentWithDeduplication } from '@/providers/passportDataProvider'; import { usePendingKycStore } from '@/stores/pendingKycStore'; -interface UseDiditWebSocketOptions { +const redactSessionId = (id: string) => + id.length > 8 ? `${id.slice(0, 4)}***${id.slice(-4)}` : '***'; + +interface UseKycWebSocketOptions { onSuccess?: () => void; onError?: (error: string) => void; onVerificationFailed?: (reason: string) => void; @@ -22,11 +25,11 @@ interface UseDiditWebSocketOptions { } /** - * Shared hook for Didit websocket subscription logic. + * Shared hook for KYC websocket subscription logic. * Handles connecting to the TEE service, subscribing to a sessionId, * and processing verification results. */ -export function useDiditWebSocket(options: UseDiditWebSocketOptions = {}) { +export function useKycWebSocket(options: UseKycWebSocketOptions = {}) { const { onSuccess, onError, @@ -51,8 +54,8 @@ export function useDiditWebSocket(options: UseDiditWebSocketOptions = {}) { (sessionId: string) => { if (subscribedSessionIdsRef.current.has(sessionId)) { console.log( - '[DiditWebSocket] Already subscribed to sessionId:', - sessionId, + '[KycWebSocket] Already subscribed to sessionId:', + redactSessionId(sessionId), ); return; } @@ -63,22 +66,22 @@ export function useDiditWebSocket(options: UseDiditWebSocketOptions = {}) { // Don't retry 'processing' verifications as the proving machine is reading to be triggered. if (isProcessing) { console.log( - '[DiditWebSocket] Verification in processing state, skipping for sessionId:', - sessionId, + '[KycWebSocket] Verification in processing state, skipping for sessionId:', + redactSessionId(sessionId), ); return; } if (!skipAddPending) { console.log( - '[DiditWebSocket] Adding pending verification for sessionId:', - sessionId, + '[KycWebSocket] Adding pending verification for sessionId:', + redactSessionId(sessionId), ); addPendingVerification(sessionId); } subscribedSessionIdsRef.current.add(sessionId); - console.log('[DiditWebSocket] Connecting to WebSocket:', DIDIT_TEE_URL); + console.log('[KycWebSocket] Connecting to WebSocket:', DIDIT_TEE_URL); const socket = io(DIDIT_TEE_URL, { transports: ['websocket', 'polling'], }); @@ -87,16 +90,16 @@ export function useDiditWebSocket(options: UseDiditWebSocketOptions = {}) { socket.on('connect', () => { console.log( - '[DiditWebSocket] Connected, subscribing to user:', - sessionId, + '[KycWebSocket] Connected, subscribing to user:', + redactSessionId(sessionId), ); socket.emit('subscribe', sessionId); }); socket.on('success', async (data: ApplicantInfoSerialized) => { console.log( - '[DiditWebSocket] Received applicant info for sessionId:', - sessionId, + '[KycWebSocket] Received applicant info for sessionId:', + redactSessionId(sessionId), ); try { @@ -113,7 +116,7 @@ export function useDiditWebSocket(options: UseDiditWebSocketOptions = {}) { }; const documentId = await storeDocumentWithDeduplication(kycData); console.log( - '[DiditWebSocket] KYC data stored successfully, documentId:', + '[KycWebSocket] KYC data stored successfully, documentId:', documentId, ); @@ -131,7 +134,7 @@ export function useDiditWebSocket(options: UseDiditWebSocketOptions = {}) { socket.emit('ack_success', sessionId); onSuccess?.(); } catch (err) { - console.error('[DiditWebSocket] Failed to store KYC data:', err); + console.error('[KycWebSocket] Failed to store KYC data:', err); updateVerificationStatus( sessionId, 'failed', @@ -146,7 +149,7 @@ export function useDiditWebSocket(options: UseDiditWebSocketOptions = {}) { }); socket.on('verification_failed', (reason: string) => { - console.log('[DiditWebSocket] Verification failed:', reason); + console.log('[KycWebSocket] Verification failed:', reason); updateVerificationStatus(sessionId, 'failed', reason); onVerificationFailed?.(reason); @@ -156,7 +159,7 @@ export function useDiditWebSocket(options: UseDiditWebSocketOptions = {}) { }); socket.on('error', (errorMessage: string) => { - console.error('[DiditWebSocket] Socket error:', errorMessage); + console.error('[KycWebSocket] Socket error:', errorMessage); updateVerificationStatus(sessionId, 'failed', errorMessage); onError?.(errorMessage); @@ -166,7 +169,10 @@ export function useDiditWebSocket(options: UseDiditWebSocketOptions = {}) { }); socket.on('disconnect', () => { - console.log('[DiditWebSocket] Disconnected for sessionId:', sessionId); + console.log( + '[KycWebSocket] Disconnected for sessionId:', + redactSessionId(sessionId), + ); }); }, [ diff --git a/app/src/hooks/usePendingKycRecovery.ts b/app/src/hooks/usePendingKycRecovery.ts index f308e2dc1..13a5b7222 100644 --- a/app/src/hooks/usePendingKycRecovery.ts +++ b/app/src/hooks/usePendingKycRecovery.ts @@ -4,7 +4,7 @@ import { useCallback, useEffect, useRef } from 'react'; -import { useDiditWebSocket } from '@/hooks/useDiditWebSocket'; +import { useKycWebSocket } from '@/hooks/useKycWebSocket'; import { navigationRef } from '@/navigation'; import { usePendingKycStore } from '@/stores/pendingKycStore'; @@ -28,7 +28,7 @@ function getRecoveryIdentifier(verification: RecoveryVerification) { * 2. For each non-expired pending/processing verification, reconnects to websocket * 3. Subscribes to the sessionId to receive any cached results * 4. Updates verification status based on server response - * 5. Initiates proving machine after document storage (handled in useDiditWebSocket) + * 5. Initiates proving machine after document storage (handled in useKycWebSocket) * * NOTE: This requires the TEE server to cache completed verification results * so they can be retrieved when the app reopens. @@ -51,7 +51,7 @@ export function usePendingKycRecovery() { console.log('[PendingKycRecovery] Verification failed:', reason); }, []); - const { subscribe, unsubscribeAll } = useDiditWebSocket({ + const { subscribe, unsubscribeAll } = useKycWebSocket({ skipAddPending: true, onSuccess: handleSuccess, onError: handleError, diff --git a/app/src/integrations/didit/index.ts b/app/src/integrations/kyc/index.ts similarity index 57% rename from app/src/integrations/didit/index.ts rename to app/src/integrations/kyc/index.ts index b9b2684a7..139398a0a 100644 --- a/app/src/integrations/didit/index.ts +++ b/app/src/integrations/kyc/index.ts @@ -4,11 +4,11 @@ export type { ApplicantInfoSerialized, - DiditVerificationResult, + KycVerificationResult, SessionResponse, -} from '@/integrations/didit/types'; +} from '@/integrations/kyc/types'; export { - type DiditConfig, - createSession, - launchDidit, -} from '@/integrations/didit/diditService'; + type KycLaunchConfig, + createKycSession, + launchKycVerification, +} from '@/integrations/kyc/kycService'; diff --git a/app/src/integrations/didit/diditService.ts b/app/src/integrations/kyc/kycService.ts similarity index 69% rename from app/src/integrations/didit/diditService.ts rename to app/src/integrations/kyc/kycService.ts index 2c0f19988..0e763aa55 100644 --- a/app/src/integrations/didit/diditService.ts +++ b/app/src/integrations/kyc/kycService.ts @@ -6,18 +6,18 @@ import { startVerification } from '@didit-protocol/sdk-react-native'; import { DIDIT_TEE_URL } from '@env'; import type { - DiditVerificationResult, + KycVerificationResult, SessionResponse, -} from '@/integrations/didit/types'; +} from '@/integrations/kyc/types'; -export interface DiditConfig { +export interface KycLaunchConfig { locale?: string; debug?: boolean; } const FETCH_TIMEOUT_MS = 30000; -export const createSession = async (): Promise => { +export const createKycSession = async (): Promise => { const apiUrl = DIDIT_TEE_URL; console.log('[Didit] createSession URL:', apiUrl); const controller = new AbortController(); @@ -36,9 +36,7 @@ export const createSession = async (): Promise => { clearTimeout(timeoutId); if (!response.ok) { - throw new Error( - `Failed to create Didit session (HTTP ${response.status})`, - ); + throw new Error(`Failed to create KYC session (HTTP ${response.status})`); } const body = await response.json(); @@ -54,24 +52,24 @@ export const createSession = async (): Promise => { if (err instanceof Error) { if (err.name === 'AbortError') { throw new Error( - `Request to Didit TEE timed out after ${FETCH_TIMEOUT_MS / 1000}s`, + `Request to KYC TEE timed out after ${FETCH_TIMEOUT_MS / 1000}s`, ); } - throw new Error(`Failed to create Didit session: ${err.message}`); + throw new Error(`Failed to create KYC session: ${err.message}`); } - throw new Error('Failed to create Didit session: Unknown error'); + throw new Error('Failed to create KYC session: Unknown error'); } }; -export const launchDidit = async ( +export const launchKycVerification = async ( sessionToken: string, - config?: DiditConfig, -): Promise => { + config?: KycLaunchConfig, +): Promise => { const result = await startVerification(sessionToken, { languageCode: config?.locale ?? 'en', loggingEnabled: config?.debug ?? __DEV__, }); - return result as DiditVerificationResult; + return result as KycVerificationResult; }; diff --git a/app/src/integrations/didit/types.ts b/app/src/integrations/kyc/types.ts similarity index 94% rename from app/src/integrations/didit/types.ts rename to app/src/integrations/kyc/types.ts index 8f0e51ccd..311066b5f 100644 --- a/app/src/integrations/didit/types.ts +++ b/app/src/integrations/kyc/types.ts @@ -8,7 +8,7 @@ export interface ApplicantInfoSerialized { pubkey: Array; } -export interface DiditVerificationResult { +export interface KycVerificationResult { type: 'completed' | 'cancelled' | 'failed'; session?: { status: string; diff --git a/app/src/providers/selfClientProvider.tsx b/app/src/providers/selfClientProvider.tsx index 3eb3f6d2e..859677ec4 100644 --- a/app/src/providers/selfClientProvider.tsx +++ b/app/src/providers/selfClientProvider.tsx @@ -23,7 +23,7 @@ import { } from '@selfxyz/mobile-sdk-alpha'; import { logNFCEvent, logProofEvent } from '@/config/sentry'; -import { createSession, launchDidit } from '@/integrations/didit'; +import { createKycSession, launchKycVerification } from '@/integrations/kyc'; import type { RootStackParamList } from '@/navigation'; import { navigationRef } from '@/navigation'; import { @@ -316,7 +316,7 @@ export const SelfClientProvider = ({ children }: PropsWithChildren) => { documentTypes: string[]; }) => { currentCountryCode = countryCode; - // Store country code early so it's available for Didit fallback flows + // Store country code early so it's available for KYC fallback flows useMRZStore.getState().update({ countryCode }); navigateIfReady('IDPicker', { countryCode, documentTypes }); }, @@ -346,23 +346,25 @@ export const SelfClientProvider = ({ children }: PropsWithChildren) => { if ( useErrorInjectionStore .getState() - .shouldTrigger('didit_initialization') + .shouldTrigger('kyc_initialization') ) { - console.log('[DEV] Injecting Didit initialization error'); + console.log('[DEV] Injecting KYC initialization error'); throw new Error( - 'Injected Didit initialization error for testing', + 'Injected KYC initialization error for testing', ); } - const session = await createSession(); - const result = await launchDidit(session.sessionToken); + const session = await createKycSession(); + const result = await launchKycVerification( + session.sessionToken, + ); - console.log('[Didit] Result:', JSON.stringify(result)); + console.log('[KYC] Result type:', result.type); // User cancelled/dismissed without completing verification if (result.type === 'cancelled') { console.log( - '[Didit] User cancelled or closed without completing', + '[KYC] User cancelled or closed without completing', ); return; } @@ -370,7 +372,7 @@ export const SelfClientProvider = ({ children }: PropsWithChildren) => { // Dev-only: Check for injected verification error const shouldInjectVerificationError = useErrorInjectionStore .getState() - .shouldTrigger('didit_verification'); + .shouldTrigger('kyc_verification'); // Actual error from provider if ( @@ -378,7 +380,7 @@ export const SelfClientProvider = ({ children }: PropsWithChildren) => { shouldInjectVerificationError ) { if (shouldInjectVerificationError) { - console.log('[DEV] Injecting Didit verification error'); + console.log('[DEV] Injecting KYC verification error'); } else { const safeError = sanitizeErrorMessage( result.error?.message || @@ -400,7 +402,7 @@ export const SelfClientProvider = ({ children }: PropsWithChildren) => { // User completed verification // Navigate to KYC success screen console.log( - '[Didit] Verification submitted, status:', + '[KYC] Verification submitted, status:', result.session?.status, ); if (navigationRef.isReady()) { diff --git a/app/src/screens/documents/aadhaar/AadhaarUploadErrorScreen.tsx b/app/src/screens/documents/aadhaar/AadhaarUploadErrorScreen.tsx index 7f0762a7c..89f5ddbaf 100644 --- a/app/src/screens/documents/aadhaar/AadhaarUploadErrorScreen.tsx +++ b/app/src/screens/documents/aadhaar/AadhaarUploadErrorScreen.tsx @@ -27,7 +27,7 @@ import { useSafeBottomPadding } from '@selfxyz/mobile-sdk-alpha/hooks'; import WarningIcon from '@/assets/images/warning.svg'; import { NavBar } from '@/components/navbar/BaseNavBar'; -import { useDiditLauncher } from '@/hooks/useDiditLauncher'; +import { useKycLauncher } from '@/hooks/useKycLauncher'; import { buttonTap } from '@/integrations/haptics'; import type { RootStackParamList } from '@/navigation'; import { extraYPadding } from '@/utils/styleUtils'; @@ -70,7 +70,7 @@ const AadhaarUploadErrorScreen: React.FC = () => { const errorType = route.params?.errorType || 'general'; const { title, description } = getErrorMessages(errorType); - const { launchDiditVerification, isLoading: isRetrying } = useDiditLauncher({ + const { launchKycVerification, isLoading: isRetrying } = useKycLauncher({ countryCode: 'IND', errorSource: 'mrz_scan_failed', // Use a compatible error source onCancel: () => { @@ -93,8 +93,8 @@ const AadhaarUploadErrorScreen: React.FC = () => { const handleTryAlternative = useCallback(async () => { trackEvent(AadhaarEvents.HELP_BUTTON_PRESSED, { errorType }); - await launchDiditVerification(); - }, [errorType, launchDiditVerification, trackEvent]); + await launchKycVerification(); + }, [errorType, launchKycVerification, trackEvent]); return ( diff --git a/app/src/screens/documents/scanning/DocumentCameraTroubleScreen.tsx b/app/src/screens/documents/scanning/DocumentCameraTroubleScreen.tsx index 8e1334285..2430e765a 100644 --- a/app/src/screens/documents/scanning/DocumentCameraTroubleScreen.tsx +++ b/app/src/screens/documents/scanning/DocumentCameraTroubleScreen.tsx @@ -16,8 +16,8 @@ import QrScan from '@/assets/icons/qr_scan.svg'; import Star from '@/assets/icons/star.svg'; import type { TipProps } from '@/components/Tips'; import Tips from '@/components/Tips'; -import { useDiditLauncher } from '@/hooks/useDiditLauncher'; import useHapticNavigation from '@/hooks/useHapticNavigation'; +import { useKycLauncher } from '@/hooks/useKycLauncher'; import SimpleScrolledTitleLayout from '@/layouts/SimpleScrolledTitleLayout'; import { flush as flushAnalytics } from '@/services/analytics'; @@ -54,7 +54,7 @@ const DocumentCameraTroubleScreen: React.FC = () => { const selfClient = useSelfClient(); const { useMRZStore } = selfClient; const { countryCode } = useMRZStore(); - const { launchDiditVerification, isLoading } = useDiditLauncher({ + const { launchKycVerification, isLoading } = useKycLauncher({ countryCode, errorSource: 'mrz_scan_failed', }); @@ -88,7 +88,7 @@ const DocumentCameraTroubleScreen: React.FC = () => { { const selfClient = useSelfClient(); const { useMRZStore } = selfClient; const { countryCode } = useMRZStore(); - const { launchDiditVerification, isLoading } = useDiditLauncher({ + const { launchKycVerification, isLoading } = useKycLauncher({ countryCode, errorSource: 'nfc_scan_failed', }); @@ -96,7 +96,7 @@ const DocumentNFCTroubleScreen: React.FC = () => { { const headerTitle = getHeaderTitle(documentType); - const { launchDiditVerification, isLoading: isRetrying } = useDiditLauncher({ + const { launchKycVerification, isLoading: isRetrying } = useKycLauncher({ countryCode, errorSource: 'mrz_scan_failed', onCancel: () => { @@ -87,8 +87,8 @@ const RegistrationFallbackMRZScreen: React.FC = () => { trackEvent('REGISTRATION_FALLBACK_TRY_ALTERNATIVE', { errorSource: 'mrz_scan_failed', }); - await launchDiditVerification(); - }, [launchDiditVerification, trackEvent]); + await launchKycVerification(); + }, [launchKycVerification, trackEvent]); const handleRetryOriginal = useCallback(() => { trackEvent('REGISTRATION_FALLBACK_RETRY_ORIGINAL', { diff --git a/app/src/screens/documents/scanning/RegistrationFallbackNFCScreen.tsx b/app/src/screens/documents/scanning/RegistrationFallbackNFCScreen.tsx index a02eda27c..1878ed4f9 100644 --- a/app/src/screens/documents/scanning/RegistrationFallbackNFCScreen.tsx +++ b/app/src/screens/documents/scanning/RegistrationFallbackNFCScreen.tsx @@ -26,7 +26,7 @@ import { useSafeBottomPadding } from '@selfxyz/mobile-sdk-alpha/hooks'; import WarningIcon from '@/assets/images/warning.svg'; import { NavBar } from '@/components/navbar/BaseNavBar'; -import { useDiditLauncher } from '@/hooks/useDiditLauncher'; +import { useKycLauncher } from '@/hooks/useKycLauncher'; import { buttonTap } from '@/integrations/haptics'; import type { RootStackParamList } from '@/navigation'; import { extraYPadding } from '@/utils/styleUtils'; @@ -67,7 +67,7 @@ const RegistrationFallbackNFCScreen: React.FC = () => { const headerTitle = getHeaderTitle(documentType); - const { launchDiditVerification, isLoading: isRetrying } = useDiditLauncher({ + const { launchKycVerification, isLoading: isRetrying } = useKycLauncher({ countryCode, errorSource: 'nfc_scan_failed', onCancel: () => { @@ -93,8 +93,8 @@ const RegistrationFallbackNFCScreen: React.FC = () => { trackEvent('REGISTRATION_FALLBACK_TRY_ALTERNATIVE', { errorSource: 'nfc_scan_failed', }); - await launchDiditVerification(); - }, [launchDiditVerification, trackEvent]); + await launchKycVerification(); + }, [launchKycVerification, trackEvent]); const handleRetryOriginal = useCallback(() => { trackEvent('REGISTRATION_FALLBACK_RETRY_ORIGINAL', { diff --git a/app/src/screens/documents/selection/LogoConfirmationScreen.tsx b/app/src/screens/documents/selection/LogoConfirmationScreen.tsx index e0c812294..dc961a52e 100644 --- a/app/src/screens/documents/selection/LogoConfirmationScreen.tsx +++ b/app/src/screens/documents/selection/LogoConfirmationScreen.tsx @@ -25,8 +25,8 @@ import { advercase, dinot } from '@selfxyz/mobile-sdk-alpha/constants/fonts'; import EPassportLogo from '@/assets/icons/epassport_logo.svg'; import { DocumentFlowNavBar } from '@/components/navbar/DocumentFlowNavBar'; import useHapticNavigation from '@/hooks/useHapticNavigation'; -import { createSession, launchDidit } from '@/integrations/didit'; import { buttonTap } from '@/integrations/haptics'; +import { createKycSession, launchKycVerification } from '@/integrations/kyc'; import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout'; import type { RootStackParamList } from '@/navigation'; import { useFeedback } from '@/providers/feedbackProvider'; @@ -58,8 +58,8 @@ const LogoConfirmationScreen: React.FC = () => { buttonText: 'Proceed with an external verifier', onButtonPress: async () => { try { - const session = await createSession(); - const result = await launchDidit(session.sessionToken); + const session = await createKycSession(); + const result = await launchKycVerification(session.sessionToken); // User cancelled/dismissed without completing verification if (result.type === 'cancelled') { @@ -69,7 +69,7 @@ const LogoConfirmationScreen: React.FC = () => { // Verification failed (provider error/rejection) if (result.type === 'failed') { console.error( - 'Didit verification failed:', + 'KYC verification failed:', result.error?.type ?? 'unknown', ); navigation.navigate('KycFailure', { @@ -82,7 +82,7 @@ const LogoConfirmationScreen: React.FC = () => { // Verification succeeded - navigate to KycSuccessScreen navigation.navigate('KycSuccess', { sessionId: session.sessionId }); } catch { - console.error('Error launching Didit verification'); + console.error('Error launching KYC verification'); showModal({ titleText: 'Error', bodyText: 'Unable to start verification. Please try again.', diff --git a/app/src/screens/kyc/KycSuccessScreen.tsx b/app/src/screens/kyc/KycSuccessScreen.tsx index 7466ba75a..c6c963122 100644 --- a/app/src/screens/kyc/KycSuccessScreen.tsx +++ b/app/src/screens/kyc/KycSuccessScreen.tsx @@ -21,7 +21,7 @@ import { import { ProofEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; import { black, white } from '@selfxyz/mobile-sdk-alpha/constants/colors'; -import { useDiditWebSocket } from '@/hooks/useDiditWebSocket'; +import { useKycWebSocket } from '@/hooks/useKycWebSocket'; import { buttonTap } from '@/integrations/haptics'; import type { RootStackParamList } from '@/navigation'; import { @@ -66,7 +66,7 @@ const KycSuccessScreen: React.FC = ({ console.log('[KycSuccessScreen] Verification failed:', reason); }, []); - const { subscribe, unsubscribeAll } = useDiditWebSocket({ + const { subscribe, unsubscribeAll } = useKycWebSocket({ onSuccess: handleWebSocketSuccess, onError: handleWebSocketError, onVerificationFailed: handleVerificationFailed, diff --git a/app/src/stores/errorInjectionStore.ts b/app/src/stores/errorInjectionStore.ts index dc20e42c7..7aa2f5e0e 100644 --- a/app/src/stores/errorInjectionStore.ts +++ b/app/src/stores/errorInjectionStore.ts @@ -16,8 +16,8 @@ export type InjectedErrorType = | 'nfc_parse_failure' | 'api_network_error' | 'api_timeout' - | 'didit_initialization' - | 'didit_verification'; + | 'kyc_initialization' + | 'kyc_verification'; export const ERROR_GROUPS = { MRZ: ['mrz_invalid_format', 'mrz_unknown_error'] as InjectedErrorType[], @@ -27,7 +27,7 @@ export const ERROR_GROUPS = { 'nfc_parse_failure', ] as InjectedErrorType[], API: ['api_network_error', 'api_timeout'] as InjectedErrorType[], - Didit: ['didit_initialization', 'didit_verification'] as InjectedErrorType[], + KYC: ['kyc_initialization', 'kyc_verification'] as InjectedErrorType[], }; export const ERROR_LABELS: Record = { @@ -38,8 +38,8 @@ export const ERROR_LABELS: Record = { nfc_parse_failure: 'NFC: Parse failure', api_network_error: 'API: Network error', api_timeout: 'API: Timeout', - didit_initialization: 'Didit: Initialization', - didit_verification: 'Didit: Verification', + kyc_initialization: 'KYC: Initialization', + kyc_verification: 'KYC: Verification', }; interface ErrorInjectionState { diff --git a/app/tests/src/hooks/usePendingKycRecovery.test.ts b/app/tests/src/hooks/usePendingKycRecovery.test.ts index c6c0a9a0f..7da1daef4 100644 --- a/app/tests/src/hooks/usePendingKycRecovery.test.ts +++ b/app/tests/src/hooks/usePendingKycRecovery.test.ts @@ -8,8 +8,8 @@ import { usePendingKycRecovery } from '@/hooks/usePendingKycRecovery'; import { navigationRef } from '@/navigation'; // Mock dependencies -jest.mock('@/hooks/useDiditWebSocket', () => ({ - useDiditWebSocket: jest.fn(() => ({ +jest.mock('@/hooks/useKycWebSocket', () => ({ + useKycWebSocket: jest.fn(() => ({ subscribe: jest.fn(), unsubscribeAll: jest.fn(), })), @@ -39,8 +39,8 @@ describe('usePendingKycRecovery', () => { jest.useFakeTimers(); // Setup default mocks - const { useDiditWebSocket } = jest.requireMock('@/hooks/useDiditWebSocket'); - useDiditWebSocket.mockReturnValue({ + const { useKycWebSocket } = jest.requireMock('@/hooks/useKycWebSocket'); + useKycWebSocket.mockReturnValue({ subscribe: mockSubscribe, unsubscribeAll: mockUnsubscribeAll, }); diff --git a/app/tests/src/navigation.test.tsx b/app/tests/src/navigation.test.tsx index 8f77e6069..c87132c13 100644 --- a/app/tests/src/navigation.test.tsx +++ b/app/tests/src/navigation.test.tsx @@ -24,7 +24,7 @@ jest.mock('@/services/analytics', () => ({ flush: jest.fn(), })); -// Mock Didit SDK to prevent ES module parsing errors in isolateModules +// Mock KYC SDK to prevent ES module parsing errors in isolateModules jest.mock('@didit-protocol/sdk-react-native', () => ({ __esModule: true, startVerification: jest.fn().mockResolvedValue({ diff --git a/app/tests/src/screens/kyc/KycSuccessScreen.test.tsx b/app/tests/src/screens/kyc/KycSuccessScreen.test.tsx index c6ff292b9..c2d6f00c6 100644 --- a/app/tests/src/screens/kyc/KycSuccessScreen.test.tsx +++ b/app/tests/src/screens/kyc/KycSuccessScreen.test.tsx @@ -44,8 +44,8 @@ jest.mock('react-native-edge-to-edge', () => ({ SystemBars: () => null, })); -jest.mock('@/hooks/useDiditWebSocket', () => ({ - useDiditWebSocket: jest.fn(() => ({ +jest.mock('@/hooks/useKycWebSocket', () => ({ + useKycWebSocket: jest.fn(() => ({ subscribe: jest.fn(), unsubscribe: jest.fn(), unsubscribeAll: jest.fn(), diff --git a/common/src/utils/types.ts b/common/src/utils/types.ts index 202af3c9d..2007f2491 100644 --- a/common/src/utils/types.ts +++ b/common/src/utils/types.ts @@ -90,9 +90,9 @@ export interface PassportData extends BaseIDData { passportMetadata?: PassportMetadata; } -// pending - pending didit verification -// processing - didit verification completed and pending onchain confirmation -// failed - didit verification failed +// pending - pending KYC verification +// processing - KYC verification completed and pending onchain confirmation +// failed - KYC verification failed export type PendingKycStatus = 'pending' | 'processing' | 'failed'; export interface PendingKycVerification { diff --git a/contracts/contracts/constants/AttestationId.sol b/contracts/contracts/constants/AttestationId.sol index f7add8b7e..f1b280b76 100644 --- a/contracts/contracts/constants/AttestationId.sol +++ b/contracts/contracts/constants/AttestationId.sol @@ -8,7 +8,7 @@ pragma solidity 0.8.28; * - E_PASSPORT (1): Electronic passports with NFC chip * - EU_ID_CARD (2): EU biometric ID cards with NFC chip * - AADHAAR (3): Indian Aadhaar identity documents - * - KYC (4): African identity documents via SumSub + * - KYC (4): KYC-backed identity documents */ library AttestationId { /// @notice Identifier for an E-PASSPORT attestation (electronic passports with NFC chip). @@ -20,6 +20,6 @@ library AttestationId { /// @notice Identifier for an AADHAAR attestation (Indian Aadhaar identity documents). bytes32 constant AADHAAR = bytes32(uint256(3)); - /// @notice Identifier for a KYC attestation (African identity documents via SumSub). + /// @notice Identifier for a KYC attestation (KYC-backed identity documents). bytes32 constant KYC = bytes32(uint256(4)); } diff --git a/docs/maintenance/tech-debt-baseline.json b/docs/maintenance/tech-debt-baseline.json index 152e64ffa..e4dc89fa8 100644 --- a/docs/maintenance/tech-debt-baseline.json +++ b/docs/maintenance/tech-debt-baseline.json @@ -40,7 +40,7 @@ "@sentry/react": "^9.32.0", "@sentry/react-native": "7.0.0", "@stablelib/cbor": "^2.0.1", - "@sumsub/react-native-mobilesdk-module": "1.40.2", + "@didit-protocol/sdk-react-native": "^3.2.8", "@tamagui/animations-css": "1.126.14", "@tamagui/animations-react-native": "1.126.14", "@tamagui/config": "1.126.14", diff --git a/packages/webview-app/src/screens/onboarding/ProviderLaunchScreen.tsx b/packages/webview-app/src/screens/onboarding/ProviderLaunchScreen.tsx index 061365e42..d7ce7cdef 100644 --- a/packages/webview-app/src/screens/onboarding/ProviderLaunchScreen.tsx +++ b/packages/webview-app/src/screens/onboarding/ProviderLaunchScreen.tsx @@ -13,11 +13,11 @@ import { useSelfClient } from '../../providers/SelfClientProvider'; import { useVerificationRequest } from '../../providers/VerificationRequestProvider'; import type { KycProviderResult } from '../../types/kycProvider'; import { buildKycDocument } from '../../utils/buildKycDocument'; -import { waitForAttestation } from '../../utils/diditAttestation'; -import { createDiditSession, launchDiditWebSdk } from '../../utils/diditProvider'; import { WEB_SAFE_AREA } from '../../utils/insets'; +import { waitForKycAttestation } from '../../utils/kycAttestation'; +import { createKycSession, launchKycWebSdk } from '../../utils/kycProvider'; -const CONTAINER_ID = 'didit-sdk-container'; +const CONTAINER_ID = 'kyc-sdk-container'; type Phase = 'loading' | 'active' | 'waiting' | 'error'; @@ -38,7 +38,7 @@ export const ProviderLaunchScreen: React.FC = () => { const defaultNextPath = nextPath ?? '/onboarding/provider-result'; const isTunnelFlow = defaultNextPath.startsWith('/tunnel/') || backPath?.startsWith('/tunnel/') === true; - const verificationId = ctxVerificationId ?? `didit-${Date.now()}`; + const verificationId = ctxVerificationId ?? `kyc-${Date.now()}`; const [phase, setPhase] = useState('loading'); const [errorMessage, setErrorMessage] = useState(''); @@ -57,7 +57,7 @@ export const ProviderLaunchScreen: React.FC = () => { if ((result.status === 'success' || result.status === 'partial') && sessionIdRef.current) { setPhase('waiting'); - const attestationResult = await waitForAttestation(sessionIdRef.current); + const attestationResult = await waitForKycAttestation(sessionIdRef.current); if (!mountedRef.current) return; @@ -163,12 +163,12 @@ export const ProviderLaunchScreen: React.FC = () => { (async () => { try { - const session = await createDiditSession(controller.signal); + const session = await createKycSession(controller.signal); if (cancelled) return; sessionIdRef.current = session.sessionId; - const destroy = await launchDiditWebSdk({ + const destroy = await launchKycWebSdk({ url: session.url, containerId: CONTAINER_ID, verificationId, diff --git a/packages/webview-app/src/utils/diditAttestation.ts b/packages/webview-app/src/utils/kycAttestation.ts similarity index 95% rename from packages/webview-app/src/utils/diditAttestation.ts rename to packages/webview-app/src/utils/kycAttestation.ts index 2ac300e64..d80eb7980 100644 --- a/packages/webview-app/src/utils/diditAttestation.ts +++ b/packages/webview-app/src/utils/kycAttestation.ts @@ -22,7 +22,7 @@ export interface AttestationResult { * * After receiving data, emits `ack_success` to trigger session deletion on the TEE. */ -export function waitForAttestation(sessionId: string, signal?: AbortSignal): Promise { +export function waitForKycAttestation(sessionId: string, signal?: AbortSignal): Promise { return new Promise(resolve => { const socket = io(DIDIT_TEE_URL, { transports: ['websocket', 'polling'], diff --git a/packages/webview-app/src/utils/diditProvider.ts b/packages/webview-app/src/utils/kycProvider.ts similarity index 85% rename from packages/webview-app/src/utils/diditProvider.ts rename to packages/webview-app/src/utils/kycProvider.ts index dba59de5d..7df77fca2 100644 --- a/packages/webview-app/src/utils/diditProvider.ts +++ b/packages/webview-app/src/utils/kycProvider.ts @@ -8,7 +8,7 @@ const FETCH_TIMEOUT_MS = 30_000; const DIDIT_TEE_URL = import.meta.env.VITE_DIDIT_TEE_URL ?? 'https://kyc.self.xyz'; -export interface DiditLaunchConfig { +export interface KycLaunchConfig { url: string; containerId: string; verificationId: string; @@ -17,7 +17,7 @@ export interface DiditLaunchConfig { onEvent?: (event: string, payload: unknown) => void; } -export interface DiditSession { +export interface KycSession { sessionId: string; sessionToken: string; url: string; @@ -33,7 +33,7 @@ function buildProviderResult(verificationId: string, overrides: Partial { +export async function createKycSession(signal?: AbortSignal): Promise { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS); @@ -50,27 +50,27 @@ export async function createDiditSession(signal?: AbortSignal): Promise void> { +export async function launchKycWebSdk(config: KycLaunchConfig): Promise<() => void> { // eslint-disable-next-line @typescript-eslint/no-explicit-any const { DiditSdk } = (await import('@didit-protocol/sdk-web')) as any; diff --git a/purple/global/authentication.md b/purple/global/authentication.md index 508d45da5..0937d8fa9 100644 --- a/purple/global/authentication.md +++ b/purple/global/authentication.md @@ -56,7 +56,7 @@ Platform adapters implement this — React Native uses keychain, web uses volati | Service | Method | Purpose | Token Persistence | |----------------|-------------|----------------------|-------------------| | Google Drive | OAuth 2.0 | Mnemonic backup | Per-session | -| Sumsub KYC | API token | Identity verification | Per-session | +| Didit KYC | API token | Identity verification | Per-session | | Turnkey | Google OAuth | Wallet backup | DISABLED | ## Keychain Security Levels (Adaptive) diff --git a/purple/global/external-services.md b/purple/global/external-services.md index 47df767f8..b22063aca 100644 --- a/purple/global/external-services.md +++ b/purple/global/external-services.md @@ -8,7 +8,7 @@ | Firebase Remote Config | `@react-native-firebase/remote-config` | Feature flags, remote config | `app/src/providers/remoteConfigProvider.tsx` | | Segment | `@segment/analytics-react-native` | Analytics / event tracking | `app/src/services/analytics.ts` | | Sentry | `@sentry/react-native` | Error tracking / crash reporting | `app/src/config/sentry.ts` | -| Sumsub | `@sumsub/react-native-mobilesdk-module` | KYC identity verification | `app/src/integrations/sumsub/` | +| Didit | `@didit-protocol/sdk-react-native` | KYC identity verification | `app/src/integrations/kyc/` | | Google Drive | `@robinbobin/react-native-google-drive-api-wrapper` | Cloud mnemonic backup | `app/src/services/cloud-backup/google.ts` | | Google OAuth | `react-native-app-auth` | OAuth 2.0 for Drive access | `app/src/services/cloud-backup/google.ts` | @@ -29,7 +29,7 @@ - Provides feature flags and runtime configuration - Fetched on app start, cached locally -### KYC (Sumsub) +### KYC (Didit) - Access token fetched per-session via TEE endpoint - 30-second timeout on token fetch - Token not persisted — fresh for each KYC session diff --git a/purple/temp/codebase-analysis.md b/purple/temp/codebase-analysis.md index d3a9f0738..029db3454 100644 --- a/purple/temp/codebase-analysis.md +++ b/purple/temp/codebase-analysis.md @@ -50,4 +50,4 @@ Identity verification wallet using NFC passport scanning + zero-knowledge proofs - No Express/Nest/etc server - Smart contracts serve as "backend" - SDK core package handles proving/verification -- External services: Firebase, Segment, Sentry, Sumsub KYC +- External services: Firebase, Segment, Sentry, Didit KYC diff --git a/specs/projects/sdk/workstreams/kmp-revival/SPEC.md b/specs/projects/sdk/workstreams/kmp-revival/SPEC.md index da33abbd7..3cd7e7bed 100644 --- a/specs/projects/sdk/workstreams/kmp-revival/SPEC.md +++ b/specs/projects/sdk/workstreams/kmp-revival/SPEC.md @@ -127,10 +127,10 @@ Any other domain request returns a `DOMAIN_NOT_FOUND` error response. ## Related Specs -| Spec | Relationship | -| ---------------------------------------------------------------- | --------------------------------------------------- | -| [SDK Overview](../../OVERVIEW.md) | Parent architecture | -| [Native Shells Lite](../native-shells-lite/SPEC.md) | Sibling — serves non-KMP consumers | -| [Paused Native Shells (KMP)](../../paused/native-shells/SPEC.md) | Historical KMP work — validated foundation | -| [Build Pipeline](../build-pipeline/SPEC.md) | Downstream — bundles webview-app into native assets | +| Spec | Relationship | +| ---------------------------------------------------------------- | -------------------------------------------------------------- | +| [SDK Overview](../../OVERVIEW.md) | Parent architecture | +| [Native Shells Lite](../native-shells-lite/SPEC.md) | Sibling — serves non-KMP consumers | +| [Paused Native Shells (KMP)](../../paused/native-shells/SPEC.md) | Historical KMP work — validated foundation | +| [Build Pipeline](../build-pipeline/SPEC.md) | Downstream — bundles webview-app into native assets | | [SDK Distribution — SD-06](../sdk-distribution/SPEC.md) | Downstream — remote publishing after KR-03 validates artifacts | diff --git a/specs/projects/sdk/workstreams/sdk-distribution/SPEC.md b/specs/projects/sdk/workstreams/sdk-distribution/SPEC.md index bcb255869..a5d40e6ff 100644 --- a/specs/projects/sdk/workstreams/sdk-distribution/SPEC.md +++ b/specs/projects/sdk/workstreams/sdk-distribution/SPEC.md @@ -58,13 +58,13 @@ ## Backlog -| ID | Title | Status | Priority | Depends On | Plan | PR | -| ----- | -------------------------------- | ------ | -------- | ---------- | ------------------------------------------------------------------------------------ | --- | -| SD-01 | Android hosted URL loading | Ready | High | NSL-01 | [plans/SD-01-android-hosted-url.md](./plans/SD-01-android-hosted-url.md) | — | -| SD-02 | iOS hosted URL loading | Ready | High | NSL-02 | [plans/SD-02-ios-hosted-url.md](./plans/SD-02-ios-hosted-url.md) | — | -| SD-03 | WebView app hosting setup | Ready | High | — | [plans/SD-03-hosting-setup.md](./plans/SD-03-hosting-setup.md) | — | -| SD-04 | Android Maven publishing | Ready | Medium | SD-01 | [plans/SD-04-android-maven-publishing.md](./plans/SD-04-android-maven-publishing.md) | — | -| SD-05 | iOS publishing (SPM + CocoaPods) | Ready | Medium | SD-02 | [plans/SD-05-ios-spm-publishing.md](./plans/SD-05-ios-spm-publishing.md) | — | +| ID | Title | Status | Priority | Depends On | Plan | PR | +| ----- | --------------------------------- | ------ | -------- | ---------- | ------------------------------------------------------------------------------------ | --- | +| SD-01 | Android hosted URL loading | Ready | High | NSL-01 | [plans/SD-01-android-hosted-url.md](./plans/SD-01-android-hosted-url.md) | — | +| SD-02 | iOS hosted URL loading | Ready | High | NSL-02 | [plans/SD-02-ios-hosted-url.md](./plans/SD-02-ios-hosted-url.md) | — | +| SD-03 | WebView app hosting setup | Ready | High | — | [plans/SD-03-hosting-setup.md](./plans/SD-03-hosting-setup.md) | — | +| SD-04 | Android Maven publishing | Ready | Medium | SD-01 | [plans/SD-04-android-maven-publishing.md](./plans/SD-04-android-maven-publishing.md) | — | +| SD-05 | iOS publishing (SPM + CocoaPods) | Ready | Medium | SD-02 | [plans/SD-05-ios-spm-publishing.md](./plans/SD-05-ios-spm-publishing.md) | — | | SD-06 | KMP remote publishing (Maven+SPM) | Ready | Medium | KR-03 | [plans/SD-06-kmp-remote-publishing.md](./plans/SD-06-kmp-remote-publishing.md) | — | Allowed statuses: `Ready`, `In Progress`, `Blocked`, `Deferred`, `Done` @@ -105,11 +105,11 @@ Allowed statuses: `Ready`, `In Progress`, `Blocked`, `Deferred`, `Done` ## Related Specs -| Spec | Relationship | -| --------------------------------------------------- | -------------------------------------------------------- | -| [SDK Overview](../../OVERVIEW.md) | Parent architecture | -| [Native Shells Lite](../native-shells-lite/SPEC.md) | Upstream — shells must exist before distribution changes | -| [Build Pipeline](../build-pipeline/SPEC.md) | Sibling — bundle script retained for local dev only | -| [WebView Spec](../webview/SPEC.md) | Upstream — produces the web app being hosted | -| [SDK Core Spec](../sdk-core/SPEC.md) | Sibling — engine consumed by hosted web app | +| Spec | Relationship | +| --------------------------------------------------- | ----------------------------------------------------------- | +| [SDK Overview](../../OVERVIEW.md) | Parent architecture | +| [Native Shells Lite](../native-shells-lite/SPEC.md) | Upstream — shells must exist before distribution changes | +| [Build Pipeline](../build-pipeline/SPEC.md) | Sibling — bundle script retained for local dev only | +| [WebView Spec](../webview/SPEC.md) | Upstream — produces the web app being hosted | +| [SDK Core Spec](../sdk-core/SPEC.md) | Sibling — engine consumed by hosted web app | | [KMP Revival](../kmp-revival/SPEC.md) | Upstream — KR-03 validates artifacts before SD-06 publishes | From b13ec9a05270108fd05092612ca3556c30df1050 Mon Sep 17 00:00:00 2001 From: Justin Hernandez Date: Thu, 9 Apr 2026 02:48:32 -0700 Subject: [PATCH 4/6] Remove Points and Self Apps screens, update navigation and copy (#1945) * commit * remove stale * create hook to open support form in webview; pr feedback * tests * ignore mcp * test * fix pipelines --- .gitignore | 1 + app/src/components/FeedbackModal.tsx | 4 +- app/src/components/PointHistoryList.tsx | 346 -------- app/src/components/navbar/Points.tsx | 600 -------------- app/src/components/navbar/PointsNavBar.tsx | 60 -- app/src/consts/links.ts | 1 - app/src/hooks/useEarnPointsFlow.ts | 203 ----- app/src/hooks/useOpenSupportForm.ts | 21 + app/src/hooks/usePoints.ts | 53 -- app/src/hooks/usePointsGuardrail.ts | 51 -- app/src/hooks/useTestReferralFlow.ts | 3 +- app/src/navigation/app.tsx | 11 - app/src/navigation/home.ts | 19 - app/src/navigation/types.ts | 11 - .../account/settings/CloudBackupScreen.tsx | 14 +- .../account/settings/SettingsScreen.tsx | 38 +- app/src/screens/app/GratificationScreen.tsx | 269 ------ app/src/screens/app/ReferralScreen.tsx | 4 +- app/src/screens/app/SplashScreen.tsx | 52 +- .../scanning/DocumentNFCScanScreen.tsx | 5 +- .../scanning/DocumentNFCTroubleScreen.tsx | 4 +- app/src/screens/home/HomeScreen.tsx | 145 +--- app/src/screens/home/PointsInfoScreen.tsx | 226 ----- app/src/screens/shared/ComingSoonScreen.tsx | 11 +- app/src/screens/shared/WebViewScreen.tsx | 2 +- .../verification/ProofRequestStatusScreen.tsx | 54 +- .../verification/QRCodeTroubleScreen.tsx | 3 +- app/src/services/support.ts | 37 +- app/src/utils/webview.ts | 1 + app/tests/src/consts/links.test.ts | 1 - app/tests/src/hooks/useEarnPointsFlow.test.ts | 777 ------------------ .../src/hooks/useOpenSupportForm.test.ts | 42 + app/tests/src/navigation.test.tsx | 3 - .../src/screens/GratificationScreen.test.tsx | 160 ---- .../screens/home/PointsInfoScreen.test.tsx | 339 -------- .../ProofRequestStatusScreen.test.tsx | 284 +++++++ 36 files changed, 484 insertions(+), 3371 deletions(-) delete mode 100644 app/src/components/PointHistoryList.tsx delete mode 100644 app/src/components/navbar/Points.tsx delete mode 100644 app/src/components/navbar/PointsNavBar.tsx delete mode 100644 app/src/hooks/useEarnPointsFlow.ts create mode 100644 app/src/hooks/useOpenSupportForm.ts delete mode 100644 app/src/hooks/usePoints.ts delete mode 100644 app/src/hooks/usePointsGuardrail.ts delete mode 100644 app/src/screens/app/GratificationScreen.tsx delete mode 100644 app/src/screens/home/PointsInfoScreen.tsx delete mode 100644 app/tests/src/hooks/useEarnPointsFlow.test.ts create mode 100644 app/tests/src/hooks/useOpenSupportForm.test.ts delete mode 100644 app/tests/src/screens/GratificationScreen.test.tsx delete mode 100644 app/tests/src/screens/home/PointsInfoScreen.test.tsx create mode 100644 app/tests/src/screens/verification/ProofRequestStatusScreen.test.tsx diff --git a/.gitignore b/.gitignore index 6f3e3d1a1..9749eec14 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ package-lock.json !.claude/skills/ **/.claude/settings.json **/.claude/settings.local.json +.mcp.json # CI-generated tarballs (don't commit these!) mobile-sdk-alpha-ci.tgz diff --git a/app/src/components/FeedbackModal.tsx b/app/src/components/FeedbackModal.tsx index 3be655be8..2a05a81fc 100644 --- a/app/src/components/FeedbackModal.tsx +++ b/app/src/components/FeedbackModal.tsx @@ -29,8 +29,8 @@ interface FeedbackModalProps { } const FeedbackModal: React.FC = ({ visible, onClose }) => { - const handleSupportForm = async () => { - await openSupportForm(); + const handleSupportForm = () => { + openSupportForm(); onClose(); }; diff --git a/app/src/components/PointHistoryList.tsx b/app/src/components/PointHistoryList.tsx deleted file mode 100644 index 0d18aaca7..000000000 --- a/app/src/components/PointHistoryList.tsx +++ /dev/null @@ -1,346 +0,0 @@ -// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. -// SPDX-License-Identifier: BUSL-1.1 -// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. - -import React, { useCallback, useMemo, useState } from 'react'; -import { - ActivityIndicator, - RefreshControl, - SectionList, - StyleSheet, -} from 'react-native'; -import { Card, Text, View, XStack, YStack } from 'tamagui'; - -import { useSelfClient } from '@selfxyz/mobile-sdk-alpha'; -import { PointEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; -import { - black, - blue600, - slate50, - slate200, - slate300, - slate400, - slate500, - white, -} from '@selfxyz/mobile-sdk-alpha/constants/colors'; -import { dinot, plexMono } from '@selfxyz/mobile-sdk-alpha/constants/fonts'; - -import HeartIcon from '@/assets/icons/heart.svg'; -import StarBlackIcon from '@/assets/icons/star_black.svg'; -import type { PointEvent } from '@/services/points'; -import { usePointEventStore } from '@/stores/pointEventStore'; - -type Section = { - title: string; - data: PointEvent[]; -}; - -export type PointHistoryListProps = { - ListHeaderComponent?: - | React.ComponentType> - | React.ReactElement - | null; - onLayout?: () => void; -}; - -const TIME_PERIODS = { - TODAY: 'TODAY', - THIS_WEEK: 'THIS WEEK', - THIS_MONTH: 'THIS MONTH', - MONTH_NAME: (date: Date): string => { - return date.toLocaleString('default', { month: 'long' }).toUpperCase(); - }, - OLDER: 'OLDER', -}; - -const getIconForEventType = (type: PointEvent['type']) => { - switch (type) { - case 'disclosure': - return ; - default: - return ; - } -}; - -export const PointHistoryList: React.FC = ({ - ListHeaderComponent, - onLayout, -}) => { - const selfClient = useSelfClient(); - const [refreshing, setRefreshing] = useState(false); - const pointEvents = usePointEventStore(state => state.getAllPointEvents()); - const isLoading = usePointEventStore(state => state.isLoading); - const refreshPoints = usePointEventStore(state => state.refreshPoints); - const refreshIncomingPoints = usePointEventStore( - state => state.refreshIncomingPoints, - ); - const loadDisclosureEvents = usePointEventStore( - state => state.loadDisclosureEvents, - ); - - const formatDate = (timestamp: number) => { - return new Date(timestamp).toLocaleTimeString([], { - hour: '2-digit', - minute: '2-digit', - }); - }; - - const formatDateFull = (timestamp: number) => { - return new Date(timestamp).toLocaleDateString([], { - month: 'short', - day: 'numeric', - }); - }; - - const getTimePeriod = useCallback((timestamp: number): string => { - const now = new Date(); - const eventDate = new Date(timestamp); - const startOfToday = new Date( - now.getFullYear(), - now.getMonth(), - now.getDate(), - ); - const startOfThisWeek = new Date(startOfToday); - startOfThisWeek.setDate(startOfToday.getDate() - startOfToday.getDay()); - const startOfThisMonth = new Date(now.getFullYear(), now.getMonth(), 1); - const startOfLastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1); - - if (eventDate >= startOfToday) { - return TIME_PERIODS.TODAY; - } else if (eventDate >= startOfThisWeek) { - return TIME_PERIODS.THIS_WEEK; - } else if (eventDate >= startOfThisMonth) { - return TIME_PERIODS.THIS_MONTH; - } else if (eventDate >= startOfLastMonth) { - return TIME_PERIODS.MONTH_NAME(eventDate); - } else { - return TIME_PERIODS.OLDER; - } - }, []); - - const groupedEvents = useMemo(() => { - const groups: Record = {}; - - [ - TIME_PERIODS.TODAY, - TIME_PERIODS.THIS_WEEK, - TIME_PERIODS.THIS_MONTH, - TIME_PERIODS.OLDER, - ].forEach(period => { - groups[period] = []; - }); - - const monthGroups = new Set(); - - pointEvents.forEach(event => { - const period = getTimePeriod(event.timestamp); - if ( - period !== TIME_PERIODS.TODAY && - period !== TIME_PERIODS.THIS_WEEK && - period !== TIME_PERIODS.THIS_MONTH && - period !== TIME_PERIODS.OLDER - ) { - monthGroups.add(period); - if (!groups[period]) { - groups[period] = []; - } - } - groups[period].push(event); - }); - - const sections: Section[] = []; - [ - TIME_PERIODS.TODAY, - TIME_PERIODS.THIS_WEEK, - TIME_PERIODS.THIS_MONTH, - ].forEach(period => { - if (groups[period] && groups[period].length > 0) { - sections.push({ title: period, data: groups[period] }); - } - }); - - Array.from(monthGroups) - .sort( - (a, b) => - new Date(groups[b][0].timestamp).getMonth() - - new Date(groups[a][0].timestamp).getMonth(), - ) - .forEach(month => { - sections.push({ title: month, data: groups[month] }); - }); - - if (groups[TIME_PERIODS.OLDER] && groups[TIME_PERIODS.OLDER].length > 0) { - sections.push({ - title: TIME_PERIODS.OLDER, - data: groups[TIME_PERIODS.OLDER], - }); - } - - return sections; - }, [pointEvents, getTimePeriod]); - - const renderItem = useCallback( - ({ - item, - index, - section, - }: { - item: PointEvent; - index: number; - section: Section; - }) => { - const borderRadiusSize = 16; - const isFirstItem = index === 0; - const isLastItem = index === section.data.length - 1; - - return ( - - - - - - {getIconForEventType(item.type)} - - - - {item.title} - - - {formatDateFull(item.timestamp)} •{' '} - {formatDate(item.timestamp)} - - - - +{item.points} - - - - - - ); - }, - [], - ); - - const renderSectionHeader = useCallback( - ({ section }: { section: Section }) => { - return ( - - - {section.title.toUpperCase()} - - - ); - }, - [], - ); - - const onRefresh = useCallback(() => { - selfClient.trackEvent(PointEvents.REFRESH_HISTORY); - setRefreshing(true); - Promise.all([ - refreshPoints(), - refreshIncomingPoints(), - loadDisclosureEvents(), - ]).finally(() => setRefreshing(false)); - }, [selfClient, refreshPoints, refreshIncomingPoints, loadDisclosureEvents]); - - const keyExtractor = useCallback((item: PointEvent) => item.id, []); - - const renderEmptyComponent = useCallback(() => { - if (isLoading) { - return ( - - - - Loading point history... - - - ); - } - return ( - - No point history available yet. - - Start earning points by completing actions! - - - ); - }, [isLoading]); - - return ( - - } - contentContainerStyle={[ - styles.listContent, - groupedEvents.length === 0 && styles.emptyList, - ]} - showsVerticalScrollIndicator={false} - stickySectionHeadersEnabled={false} - ListEmptyComponent={renderEmptyComponent} - ListHeaderComponent={ListHeaderComponent} - style={{ marginHorizontal: 15, marginBottom: 25 }} - onLayout={onLayout} - /> - ); -}; - -const styles = StyleSheet.create({ - listContent: { - paddingBottom: 100, - }, - emptyList: { - flexGrow: 1, - }, - emptyContainer: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - paddingHorizontal: 20, - paddingTop: 5, - }, -}); - -export default PointHistoryList; diff --git a/app/src/components/navbar/Points.tsx b/app/src/components/navbar/Points.tsx deleted file mode 100644 index 0085873c4..000000000 --- a/app/src/components/navbar/Points.tsx +++ /dev/null @@ -1,600 +0,0 @@ -// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. -// SPDX-License-Identifier: BUSL-1.1 -// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. - -import React, { useEffect, useState } from 'react'; -import { Pressable, StyleSheet } from 'react-native'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { Button, Image, Text, View, XStack, YStack, ZStack } from 'tamagui'; -import { useFocusEffect, useNavigation } from '@react-navigation/native'; -import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; -import { HelpCircle } from '@tamagui/lucide-icons'; - -import { useSelfClient } from '@selfxyz/mobile-sdk-alpha'; -import { PointEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; -import { - black, - blue600, - slate50, - slate200, - slate500, - white, -} from '@selfxyz/mobile-sdk-alpha/constants/colors'; -import { dinot } from '@selfxyz/mobile-sdk-alpha/constants/fonts'; - -import BellWhiteIcon from '@/assets/icons/bell_white.svg'; -import ClockIcon from '@/assets/icons/clock.svg'; -import LockWhiteIcon from '@/assets/icons/lock_white.svg'; -import StarBlackIcon from '@/assets/icons/star_black.svg'; -import LogoInversed from '@/assets/images/logo_inversed.svg'; -import MajongImage from '@/assets/images/majong.png'; -import { PointHistoryList } from '@/components/PointHistoryList'; -import { appsUrl } from '@/consts/links'; -import { useIncomingPoints, usePoints } from '@/hooks/usePoints'; -import { usePointsGuardrail } from '@/hooks/usePointsGuardrail'; -import type { RootStackParamList } from '@/navigation'; -import { trackScreenView } from '@/services/analytics'; -import { - isTopicSubscribed, - requestNotificationPermission, - subscribeToTopics, -} from '@/services/notifications/notificationService'; -import { - formatTimeUntilDate, - POINT_VALUES, - recordBackupPointEvent, - recordNotificationPointEvent, -} from '@/services/points'; -import { usePointEventStore } from '@/stores/pointEventStore'; -import { useSettingStore } from '@/stores/settingStore'; -import { registerModalCallbacks } from '@/utils/modalCallbackRegistry'; - -const Points: React.FC = () => { - const selfClient = useSelfClient(); - - const { bottom } = useSafeAreaInsets(); - const navigation = - useNavigation>(); - const [isGeneralSubscribed, setIsGeneralSubscribed] = useState(false); - const [isEnabling, setIsEnabling] = useState(false); - const incomingPoints = useIncomingPoints(); - const { amount: points } = usePoints(); - const loadEvents = usePointEventStore(state => state.loadEvents); - const { hasCompletedBackupForPoints, setBackupForPointsCompleted } = - useSettingStore(); - const [isBackingUp, setIsBackingUp] = useState(false); - - // Guard: Validate that user has registered a document and completed points disclosure - usePointsGuardrail(); - - // Track NavBar view analytics - useFocusEffect( - React.useCallback(() => { - trackScreenView('Points NavBar', { - screenName: 'Points NavBar', - }); - }, []), - ); - - const onHelpButtonPress = () => { - navigation.navigate('PointsInfo'); - }; - - //TODO - uncomment after merging - https://github.com/selfxyz/self/pull/1363/ - // useEffect(() => { - // const backupEvent = usePointEventStore - // .getState() - // .events.find( - // event => event.type === 'backup' && event.status === 'completed', - // ); - - // if (backupEvent && !hasCompletedBackupForPoints) { - // setBackupForPointsCompleted(); - // } - // }, [setBackupForPointsCompleted, hasCompletedBackupForPoints]); - - // Track if we should check for backup completion on next focus - const shouldCheckBackupRef = React.useRef(false); - - // Detect when returning from backup screen and record points if backup was completed - useFocusEffect( - React.useCallback(() => { - const { cloudBackupEnabled, turnkeyBackupEnabled } = - useSettingStore.getState(); - const currentHasCompletedBackup = - useSettingStore.getState().hasCompletedBackupForPoints; - - // Only check if we explicitly set the flag (when navigating to backup settings) - // This prevents false triggers when returning from other flows (like notification permissions) - if ( - shouldCheckBackupRef.current && - (cloudBackupEnabled || turnkeyBackupEnabled) && - !currentHasCompletedBackup - ) { - const recordPoints = async () => { - try { - const response = await recordBackupPointEvent(); - - if (response.success) { - useSettingStore.getState().setBackupForPointsCompleted(); - selfClient.trackEvent(PointEvents.EARN_BACKUP_SUCCESS); - - const callbackId = registerModalCallbacks({ - onButtonPress: () => {}, - onModalDismiss: () => {}, - }); - navigation.navigate('Modal', { - titleText: 'Success!', - bodyText: `Account backed up successfully! You earned ${POINT_VALUES.backup} points.\n\nPoints will be distributed to your wallet on the next Sunday at noon UTC.`, - buttonText: 'OK', - callbackId, - }); - } else { - console.error( - 'Error recording backup points after return:', - response.error, - ); - selfClient.trackEvent(PointEvents.EARN_BACKUP_FAILED); - } - } catch (error) { - selfClient.trackEvent(PointEvents.EARN_BACKUP_FAILED); - console.error('Error recording backup points after return:', error); - } - }; - - recordPoints(); - } - - // Reset the flag after checking - shouldCheckBackupRef.current = false; - }, [navigation, selfClient]), - ); - - // Mock function to check if user has backed up their account - const hasUserBackedUpAccount = (): boolean => { - return hasCompletedBackupForPoints; - }; - - useEffect(() => { - loadEvents(); - }, [loadEvents]); - - useEffect(() => { - const checkSubscription = async () => { - const subscribed = await isTopicSubscribed('general'); - setIsGeneralSubscribed(subscribed); - }; - checkSubscription(); - }, []); - - const handleEnableNotifications = async () => { - if (isEnabling) { - return; - } - selfClient.trackEvent(PointEvents.EARN_NOTIFICATION); - setIsEnabling(true); - try { - const granted = await requestNotificationPermission(); - if (granted) { - const result = await subscribeToTopics(['general']); - if (result.successes.length > 0) { - const response = await recordNotificationPointEvent(); - if (response.success) { - setIsGeneralSubscribed(true); - selfClient.trackEvent(PointEvents.EARN_NOTIFICATION_SUCCESS); - - navigation.navigate('Gratification', { - points: POINT_VALUES.notification, - }); - } else { - selfClient.trackEvent(PointEvents.EARN_NOTIFICATION_FAILED, { - reason: 'Failed to record points', - }); - - const callbackId = registerModalCallbacks({ - onButtonPress: () => {}, - onModalDismiss: () => {}, - }); - navigation.navigate('Modal', { - titleText: 'Verification Failed', - bodyText: - response.error || - 'Failed to register points. Please try again.', - buttonText: 'OK', - callbackId, - }); - } - } else { - selfClient.trackEvent(PointEvents.EARN_NOTIFICATION_FAILED, { - reason: 'Subscription failed', - }); - const callbackId = registerModalCallbacks({ - onButtonPress: () => {}, - onModalDismiss: () => {}, - }); - navigation.navigate('Modal', { - titleText: 'Error', - bodyText: `Failed to enable: ${result.failures.map(f => f.error).join(', ')}`, - buttonText: 'OK', - callbackId, - }); - } - } else { - selfClient.trackEvent(PointEvents.EARN_NOTIFICATION_FAILED, { - reason: 'Permission denied', - }); - const callbackId = registerModalCallbacks({ - onButtonPress: () => {}, - onModalDismiss: () => {}, - }); - navigation.navigate('Modal', { - titleText: 'Permission Required', - bodyText: - 'Could not enable notifications. Please enable them in your device Settings.', - buttonText: 'OK', - callbackId, - }); - } - } catch (error) { - selfClient.trackEvent(PointEvents.EARN_NOTIFICATION_FAILED, { - reason: 'Exception occurred', - }); - const callbackId = registerModalCallbacks({ - onButtonPress: () => {}, - onModalDismiss: () => {}, - }); - navigation.navigate('Modal', { - titleText: 'Error', - bodyText: - error instanceof Error - ? error.message - : 'Failed to enable notifications', - buttonText: 'OK', - callbackId, - }); - } finally { - setIsEnabling(false); - } - }; - - const handleBackupSecret = async () => { - if (isBackingUp) { - return; - } - selfClient.trackEvent(PointEvents.EARN_BACKUP); - - const { cloudBackupEnabled, turnkeyBackupEnabled } = - useSettingStore.getState(); - - // If either backup method is already enabled, just record points - if (cloudBackupEnabled || turnkeyBackupEnabled) { - setIsBackingUp(true); - try { - // this will add event to store and the new event will then trigger useIncomingPoints hook to refetch incoming points - const response = await recordBackupPointEvent(); - - if (response.success) { - setBackupForPointsCompleted(); - selfClient.trackEvent(PointEvents.EARN_BACKUP_SUCCESS); - - navigation.navigate('Gratification', { - points: POINT_VALUES.backup, - }); - } else { - selfClient.trackEvent(PointEvents.EARN_BACKUP_FAILED); - const callbackId = registerModalCallbacks({ - onButtonPress: () => {}, - onModalDismiss: () => {}, - }); - navigation.navigate('Modal', { - titleText: 'Verification Failed', - bodyText: - response.error || 'Failed to register points. Please try again.', - buttonText: 'OK', - callbackId, - }); - } - } catch (error) { - selfClient.trackEvent(PointEvents.EARN_BACKUP_FAILED); - const callbackId = registerModalCallbacks({ - onButtonPress: () => {}, - onModalDismiss: () => {}, - }); - navigation.navigate('Modal', { - titleText: 'Error', - bodyText: - error instanceof Error ? error.message : 'Failed to backup account', - buttonText: 'OK', - callbackId, - }); - } finally { - setIsBackingUp(false); - } - } else { - // Navigate to backup screen and return to Points after backup completes - // Set flag to check for backup completion when we return - shouldCheckBackupRef.current = true; - navigation.navigate('CloudBackupSettings', { returnToScreen: 'Points' }); - } - }; - - const ListHeader = ( - - - - - - - - - - - - {`${points} Self points`} - - - Earn points by referring friends, disclosing proof requests, and - more. - - - - {incomingPoints && ( - - - - {`${incomingPoints.amount} incoming points`} - - - {`Expected in ${formatTimeUntilDate(incomingPoints.expectedDate)}`} - - - )} - - {!isGeneralSubscribed && ( - - - - - - - - {isEnabling - ? 'Enabling notifications...' - : 'Turn on push notifications'} - - - Earn {POINT_VALUES.notification} points - - - - - )} - {!hasUserBackedUpAccount() && ( - - - - - - - - {isBackingUp ? 'Processing backup...' : 'Backup your account'} - - - Earn {POINT_VALUES.backup} points - - - - - )} - { - selfClient.trackEvent(PointEvents.EARN_REFERRAL); - navigation.navigate('Referral'); - }} - > - - - - - - - - Refer friends and earn rewards - - Refer now - - - - - ); - - return ( - - - - - - - - - ); -}; - -const styles = StyleSheet.create({ - pointsCard: { - backgroundColor: white, - borderRadius: 10, - borderWidth: 1, - borderColor: slate200, - overflow: 'hidden', - }, - pointsCardContent: { - paddingVertical: 30, - paddingHorizontal: 40, - alignItems: 'center', - gap: 20, - }, - logoContainer: { - width: 68, - height: 68, - borderRadius: 12, - borderWidth: 1, - borderColor: slate200, - alignItems: 'center', - justifyContent: 'center', - backgroundColor: white, - }, - pointsTitle: { - color: black, - textAlign: 'center', - fontFamily: dinot, - fontWeight: '500', - fontSize: 32, - lineHeight: 32, - letterSpacing: -1, - }, - pointsDescription: { - color: black, - fontFamily: dinot, - fontSize: 18, - fontWeight: '500', - textAlign: 'center', - paddingHorizontal: 20, - }, - incomingPointsBar: { - backgroundColor: slate50, - borderTopWidth: 1, - borderTopColor: slate200, - paddingVertical: 10, - paddingHorizontal: 10, - alignItems: 'center', - gap: 4, - }, - incomingPointsAmount: { - flex: 1, - fontFamily: dinot, - fontWeight: '500', - fontSize: 14, - color: black, - }, - incomingPointsTime: { - fontFamily: dinot, - fontWeight: '500', - fontSize: 14, - color: blue600, - }, - actionCard: { - gap: 22, - backgroundColor: white, - padding: 16, - borderRadius: 17, - borderWidth: 1, - borderColor: slate200, - }, - actionIconContainer: { - width: 60, - height: 60, - borderRadius: 16, - alignItems: 'center', - justifyContent: 'center', - backgroundColor: black, - }, - actionTitle: { - color: black, - fontFamily: dinot, - fontWeight: '500', - fontSize: 16, - }, - actionSubtitle: { - color: slate500, - fontFamily: dinot, - fontSize: 14, - }, - referralCard: { - height: 270, - backgroundColor: white, - borderRadius: 16, - borderWidth: 1, - borderColor: slate200, - }, - referralImageContainer: { - borderBottomWidth: 1, - borderBottomColor: slate200, - height: 170, - }, - referralImage: { - width: '80%', - height: '100%', - position: 'absolute', - right: 0, - top: 0, - }, - referralStarIcon: { - marginLeft: 16, - marginTop: 16, - }, - referralTitle: { - fontFamily: dinot, - fontSize: 16, - color: black, - }, - referralLink: { - fontFamily: dinot, - fontSize: 16, - color: blue600, - }, - blurView: { - position: 'absolute', - bottom: 0, - left: 0, - right: 0, - height: 100, - }, - exploreButtonContainer: { - position: 'absolute', - left: 20, - right: 20, - }, - exploreButton: { - backgroundColor: black, - paddingHorizontal: 20, - paddingVertical: 14, - borderRadius: 5, - height: 52, - }, - exploreButtonText: { - fontFamily: dinot, - fontSize: 16, - color: white, - textAlign: 'center', - }, - helpButton: { - position: 'absolute', - top: 0, - right: 0, - padding: 12, - }, -}); - -export default Points; diff --git a/app/src/components/navbar/PointsNavBar.tsx b/app/src/components/navbar/PointsNavBar.tsx deleted file mode 100644 index 1e2f3a621..000000000 --- a/app/src/components/navbar/PointsNavBar.tsx +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. -// SPDX-License-Identifier: BUSL-1.1 -// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. - -import React from 'react'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import type { NativeStackHeaderProps } from '@react-navigation/native-stack'; - -import { Text, View } from '@selfxyz/mobile-sdk-alpha/components'; -import { black, slate50 } from '@selfxyz/mobile-sdk-alpha/constants/colors'; - -import { NavBar } from '@/components/navbar/BaseNavBar'; -import { buttonTap } from '@/integrations/haptics'; -import { extraYPadding } from '@/utils/styleUtils'; - -export const PointsNavBar = (props: NativeStackHeaderProps) => { - const insets = useSafeAreaInsets(); - const closeButtonWidth = 50; - - return ( - - { - buttonTap(); - props.navigation.navigate('Home'); - }} - /> - - - Self Points - - - - } - /> - - ); -}; diff --git a/app/src/consts/links.ts b/app/src/consts/links.ts index a2747fabd..921a6fb52 100644 --- a/app/src/consts/links.ts +++ b/app/src/consts/links.ts @@ -14,7 +14,6 @@ export const apiBaseUrl = 'https://api.self.xyz'; export const apiPingUrl = 'https://api.self.xyz/ping'; export const appStoreUrl = 'https://apps.apple.com/app/self-zk/id6478563710'; export const appleICloudDocsUrl = 'https://support.apple.com/en-us/102651'; -export const appsUrl = 'https://apps.self.xyz'; export const discordUrl = 'https://discord.gg/selfxyz'; export const gitHubUrl = 'https://github.com/selfxyz/self'; export const googleDriveAppDataScope = diff --git a/app/src/hooks/useEarnPointsFlow.ts b/app/src/hooks/useEarnPointsFlow.ts deleted file mode 100644 index a7881780b..000000000 --- a/app/src/hooks/useEarnPointsFlow.ts +++ /dev/null @@ -1,203 +0,0 @@ -// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. -// SPDX-License-Identifier: BUSL-1.1 -// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. - -import { useCallback } from 'react'; -import { useNavigation } from '@react-navigation/native'; -import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; - -import { useSelfClient } from '@selfxyz/mobile-sdk-alpha'; - -import { useRegisterReferral } from '@/hooks/useRegisterReferral'; -import type { RootStackParamList } from '@/navigation'; -import { - hasUserAnIdentityDocumentRegistered, - hasUserDoneThePointsDisclosure, - POINT_VALUES, - pointsSelfApp, -} from '@/services/points'; -import useUserStore from '@/stores/userStore'; -import { registerModalCallbacks } from '@/utils/modalCallbackRegistry'; - -type UseEarnPointsFlowParams = { - hasReferrer: boolean; - isReferralConfirmed: boolean | undefined; -}; - -export const useEarnPointsFlow = ({ - hasReferrer, - isReferralConfirmed, -}: UseEarnPointsFlowParams) => { - const selfClient = useSelfClient(); - const navigation = - useNavigation>(); - const { registerReferral } = useRegisterReferral(); - const referrer = useUserStore(state => state.deepLinkReferrer); - - const navigateToPointsProof = useCallback(async () => { - const selfApp = await pointsSelfApp(); - selfClient.getSelfAppState().setSelfApp(selfApp); - - // Use setTimeout to ensure modal dismisses before navigating - setTimeout(() => { - navigation.navigate('ProvingScreenRouter'); - }, 100); - }, [selfClient, navigation]); - - const showIdentityVerificationModal = useCallback(() => { - const callbackId = registerModalCallbacks({ - onButtonPress: () => { - // Use setTimeout to ensure modal dismisses before navigating - setTimeout(() => { - navigation.navigate('CountryPicker'); - }, 100); - }, - onModalDismiss: () => { - if (hasReferrer) { - useUserStore.getState().clearDeepLinkReferrer(); - } - }, - }); - - navigation.navigate('Modal', { - titleText: 'Identity Verification Required', - bodyText: - 'To access Self Points, you need to register an identity document with Self first. This helps us verify your identity and keep your points secure.', - buttonText: 'Verify Identity', - secondaryButtonText: 'Not Now', - callbackId, - }); - }, [hasReferrer, navigation]); - - const showPointsDisclosureModal = useCallback(() => { - const callbackId = registerModalCallbacks({ - onButtonPress: () => { - navigateToPointsProof(); - }, - onModalDismiss: () => { - if (hasReferrer) { - useUserStore.getState().clearDeepLinkReferrer(); - } - }, - }); - - navigation.navigate('Modal', { - titleText: 'Points Disclosure Required', - bodyText: - 'To access Self Points, you need to complete the points disclosure first. This helps us verify your identity and keep your points secure.', - buttonText: 'Complete Points Disclosure', - secondaryButtonText: 'Not Now', - callbackId, - }); - }, [hasReferrer, navigation, navigateToPointsProof]); - - const showPointsInfoScreen = useCallback(() => { - const callbackId = registerModalCallbacks({ - onButtonPress: () => { - showPointsDisclosureModal(); - }, - onModalDismiss: () => { - if (hasReferrer) { - useUserStore.getState().clearDeepLinkReferrer(); - } - }, - }); - - navigation.navigate('PointsInfo', { - showNextButton: true, - callbackId, - }); - }, [hasReferrer, navigation, showPointsDisclosureModal]); - - const handleReferralFlow = useCallback(async () => { - if (!referrer) { - return; - } - - const showReferralErrorModal = (errorMessage: string) => { - const callbackId = registerModalCallbacks({ - onButtonPress: async () => { - await handleReferralFlow(); - }, - onModalDismiss: () => { - // Clear referrer when user dismisses to prevent retry loop - useUserStore.getState().clearDeepLinkReferrer(); - }, - }); - - navigation.navigate('Modal', { - titleText: 'Referral Registration Failed', - bodyText: `We couldn't register your referral at this time. ${errorMessage}. You can try again or dismiss this message.`, - buttonText: 'Try Again', - secondaryButtonText: 'Dismiss', - callbackId, - }); - }; - - const store = useUserStore.getState(); - // Check if already registered to avoid duplicate calls - if (!store.isReferrerRegistered(referrer)) { - const result = await registerReferral(referrer); - if (result.success) { - store.markReferrerAsRegistered(referrer); - - // Only navigate to GratificationScreen on success - store.clearDeepLinkReferrer(); - navigation.navigate('Gratification', { - points: POINT_VALUES.referee, - }); - } else { - // Registration failed - show error and preserve referrer - const errorMessage = result.error || 'Unknown error occurred'; - console.error('Referral registration failed:', errorMessage); - - // Show error modal with retry option, don't clear referrer - showReferralErrorModal(errorMessage); - } - } else { - // Already registered, navigate to gratification - store.clearDeepLinkReferrer(); - navigation.navigate('Gratification', { - points: POINT_VALUES.referee, - }); - } - }, [referrer, registerReferral, navigation]); - - const onEarnPointsPress = useCallback( - async (skipReferralFlow = true) => { - const hasUserAnIdentityDocumentRegistered_result = - await hasUserAnIdentityDocumentRegistered(); - if (!hasUserAnIdentityDocumentRegistered_result) { - showIdentityVerificationModal(); - return; - } - - const hasUserDoneThePointsDisclosure_result = - await hasUserDoneThePointsDisclosure(); - if (!hasUserDoneThePointsDisclosure_result) { - showPointsInfoScreen(); - return; - } - - // User has completed both checks - if (!skipReferralFlow && hasReferrer && isReferralConfirmed === true) { - await handleReferralFlow(); - } else { - // Just go to points upon pressing "Earn Points" button - if (!hasReferrer) { - navigation.navigate('Points'); - } - } - }, - [ - hasReferrer, - isReferralConfirmed, - navigation, - showIdentityVerificationModal, - showPointsInfoScreen, - handleReferralFlow, - ], - ); - - return { onEarnPointsPress }; -}; diff --git a/app/src/hooks/useOpenSupportForm.ts b/app/src/hooks/useOpenSupportForm.ts new file mode 100644 index 000000000..ed295a4cc --- /dev/null +++ b/app/src/hooks/useOpenSupportForm.ts @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import { useCallback } from 'react'; + +import { impactLight } from '@/integrations/haptics'; +import { openSupportForm } from '@/services/support'; + +/** + * Hook wrapper around openSupportForm that adds haptic feedback. + * Use this inside screen components. For code outside the navigation tree + * (providers, modals rendered at root), call openSupportForm() directly. + */ +const useOpenSupportForm = () => + useCallback(() => { + impactLight(); + openSupportForm(); + }, []); + +export default useOpenSupportForm; diff --git a/app/src/hooks/usePoints.ts b/app/src/hooks/usePoints.ts deleted file mode 100644 index 8330bd361..000000000 --- a/app/src/hooks/usePoints.ts +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. -// SPDX-License-Identifier: BUSL-1.1 -// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. - -import { useEffect } from 'react'; - -import { getNextSundayNoonUTC, type IncomingPoints } from '@/services/points'; -import { usePointEventStore } from '@/stores/pointEventStore'; - -/* - * Hook to get incoming points for the user. It shows the optimistic incoming points. - * Refreshes incoming points once on mount. - */ -export const useIncomingPoints = (): IncomingPoints => { - const incomingPoints = usePointEventStore(state => state.incomingPoints); - const totalOptimisticIncomingPoints = usePointEventStore(state => - state.totalOptimisticIncomingPoints(), - ); - const refreshIncomingPoints = usePointEventStore( - state => state.refreshIncomingPoints, - ); - - useEffect(() => { - // Only refresh once on mount - the store handles promise caching for concurrent calls - refreshIncomingPoints(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); // Empty deps: only run once on mount - - return { - amount: totalOptimisticIncomingPoints, - expectedDate: incomingPoints.expectedDate, - }; -}; - -/* - * Hook to fetch total points for the user. It refetches the total points when the next points update time is reached (each Sunday noon UTC). - */ -export const usePoints = () => { - const points = usePointEventStore(state => state.points); - const nextPointsUpdate = getNextSundayNoonUTC().getTime(); - const refreshPoints = usePointEventStore(state => state.refreshPoints); - - useEffect(() => { - refreshPoints(); - // refresh when points update time changes as its the only time points can change - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [nextPointsUpdate]); - - return { - amount: points, - refetch: refreshPoints, - }; -}; diff --git a/app/src/hooks/usePointsGuardrail.ts b/app/src/hooks/usePointsGuardrail.ts deleted file mode 100644 index e22bf8f7b..000000000 --- a/app/src/hooks/usePointsGuardrail.ts +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. -// SPDX-License-Identifier: BUSL-1.1 -// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. - -import { useCallback } from 'react'; -import { useFocusEffect, useNavigation } from '@react-navigation/native'; -import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; - -import type { RootStackParamList } from '@/navigation'; -import { - hasUserAnIdentityDocumentRegistered, - hasUserDoneThePointsDisclosure, -} from '@/services/points'; - -/** - * Guard hook that validates points screen access requirements. - * Redirects to Home if user hasn't: - * 1. Registered an identity document - * 2. Completed the points disclosure - * - * This prevents users from accessing the Points screen through: - * - GratificationScreen's "Explore rewards" button - * - CloudBackupSettings return paths - * - Any other navigation bypass - */ -export const usePointsGuardrail = () => { - const navigation = - useNavigation>(); - - useFocusEffect( - useCallback(() => { - let isActive = true; - - const checkRequirements = async () => { - const hasDocument = await hasUserAnIdentityDocumentRegistered(); - const hasDisclosed = await hasUserDoneThePointsDisclosure(); - - // Only navigate if the screen is still focused - if (isActive && (!hasDocument || !hasDisclosed)) { - // User hasn't met requirements, redirect to Home - navigation.navigate('Home', {}); - } - }; - checkRequirements(); - - return () => { - isActive = false; - }; - }, [navigation]), - ); -}; diff --git a/app/src/hooks/useTestReferralFlow.ts b/app/src/hooks/useTestReferralFlow.ts index 7438f5cc7..e655fe517 100644 --- a/app/src/hooks/useTestReferralFlow.ts +++ b/app/src/hooks/useTestReferralFlow.ts @@ -13,8 +13,7 @@ const TEST_REFERRER = '0x1234567890123456789012345678901234567890'; * Hook for testing referral flow in DEV mode. * Provides automatic timeout trigger (3 seconds) and manual trigger function. * - * Flow: Sets referrer → shows confirmation modal → on confirm, checks prerequisites - * → if identity doc & points disclosure done → registers referral → navigates to Gratification + * Flow: Sets referrer → shows confirmation modal → on confirm → registers referral * * @param shouldAutoTrigger - Whether to automatically trigger the flow after 3 seconds (default: false) */ diff --git a/app/src/navigation/app.tsx b/app/src/navigation/app.tsx index 766bed085..0b3b2d889 100644 --- a/app/src/navigation/app.tsx +++ b/app/src/navigation/app.tsx @@ -9,7 +9,6 @@ import type { DocumentCategory } from '@selfxyz/common/utils/types'; import { SystemBars } from '@/components/SystemBars'; import DeferredLinkingInfoScreen from '@/screens/app/DeferredLinkingInfoScreen'; -import GratificationScreen from '@/screens/app/GratificationScreen'; import LoadingScreen from '@/screens/app/LoadingScreen'; import type { ModalNavigationParams } from '@/screens/app/ModalScreen'; import ModalScreen from '@/screens/app/ModalScreen'; @@ -50,16 +49,6 @@ const appScreens = { header: () => , }, }, - Gratification: { - screen: GratificationScreen, - options: { - headerShown: false, - contentStyle: { backgroundColor: '#000000' }, - } as NativeStackNavigationOptions, - params: {} as { - points?: number; - }, - }, }; export default appScreens; diff --git a/app/src/navigation/home.ts b/app/src/navigation/home.ts index 73a22babb..9e64d9f30 100644 --- a/app/src/navigation/home.ts +++ b/app/src/navigation/home.ts @@ -5,11 +5,8 @@ import type { NativeStackNavigationOptions } from '@react-navigation/native-stack'; import { HomeNavBar } from '@/components/navbar'; -import PointsScreen from '@/components/navbar/Points'; -import { PointsNavBar } from '@/components/navbar/PointsNavBar'; import ReferralScreen from '@/screens/app/ReferralScreen'; import HomeScreen from '@/screens/home/HomeScreen'; -import PointsInfoScreen from '@/screens/home/PointsInfoScreen'; import ProofHistoryDetailScreen from '@/screens/home/ProofHistoryDetailScreen'; import ProofHistoryScreen from '@/screens/home/ProofHistoryScreen'; @@ -22,14 +19,6 @@ const homeScreens = { presentation: 'card', } as NativeStackNavigationOptions, }, - Points: { - screen: PointsScreen, - options: { - title: 'Self Points', - header: PointsNavBar, - presentation: 'card', - } as NativeStackNavigationOptions, - }, Referral: { screen: ReferralScreen, options: { @@ -49,14 +38,6 @@ const homeScreens = { title: 'Approval', }, }, - PointsInfo: { - screen: PointsInfoScreen, - options: { - headerBackTitle: 'close', - title: 'Self Points', - animation: 'slide_from_bottom', - } as NativeStackNavigationOptions, - }, }; export default homeScreens; diff --git a/app/src/navigation/types.ts b/app/src/navigation/types.ts index ee0f317f8..368004191 100644 --- a/app/src/navigation/types.ts +++ b/app/src/navigation/types.ts @@ -40,7 +40,6 @@ export type AccountRoutesParamList = { CloudBackupSettings: | { nextScreen?: 'SaveRecoveryPhrase'; - returnToScreen?: 'Points'; } | undefined; ProofSettings: undefined; @@ -58,9 +57,6 @@ export type AppRoutesParamList = { curveOrExponent?: string; }; Modal: ModalNavigationParams; - Gratification: { - points?: number; - }; StarfallPushCode: undefined; }; @@ -131,13 +127,6 @@ export type HomeRoutesParamList = { Home: { testReferralFlow?: boolean; }; - Points: undefined; - PointsInfo: - | { - showNextButton?: boolean; - callbackId?: number; - } - | undefined; }; /** diff --git a/app/src/screens/account/settings/CloudBackupScreen.tsx b/app/src/screens/account/settings/CloudBackupScreen.tsx index ef95c8529..892582531 100644 --- a/app/src/screens/account/settings/CloudBackupScreen.tsx +++ b/app/src/screens/account/settings/CloudBackupScreen.tsx @@ -40,7 +40,6 @@ type NextScreen = keyof Pick; type CloudBackupScreenProps = StaticScreenProps< | { nextScreen?: NextScreen; - returnToScreen?: 'Points'; } | undefined >; @@ -175,10 +174,6 @@ const CloudBackupScreen: React.FC = ({ await upload(storedMnemonic.data); toggleCloudBackupEnabled(); trackEvent(BackupEvents.CLOUD_BACKUP_ENABLED_DONE); - - if (params?.returnToScreen) { - navigation.navigate(params.returnToScreen); - } } catch (error) { console.error('iCloud backup error', error); } finally { @@ -191,8 +186,6 @@ const CloudBackupScreen: React.FC = ({ upload, toggleCloudBackupEnabled, trackEvent, - navigation, - params, selfClient, showNoRegisteredAccountModal, ]); @@ -226,9 +219,6 @@ const CloudBackupScreen: React.FC = ({ // await backupAccount(mnemonics.data.phrase); // setTurnkeyPending(false); - // if (params?.returnToScreen) { - // navigation.navigate(params.returnToScreen); - // } // } catch (error) { // if (error instanceof Error && error.message === 'already_exists') { // console.log('Already signed in with Turnkey'); @@ -238,9 +228,7 @@ const CloudBackupScreen: React.FC = ({ // error.message === 'already_backed_up' // ) { // console.log('Already backed up with Turnkey'); - // if (params?.returnToScreen) { - // navigation.navigate(params.returnToScreen); - // } else if (params?.nextScreen) { + // if (params?.nextScreen) { // navigation.navigate(params.nextScreen); // } else { // showAlreadyBackedUpModal(); diff --git a/app/src/screens/account/settings/SettingsScreen.tsx b/app/src/screens/account/settings/SettingsScreen.tsx index 5bfa545a2..1003860ce 100644 --- a/app/src/screens/account/settings/SettingsScreen.tsx +++ b/app/src/screens/account/settings/SettingsScreen.tsx @@ -42,9 +42,9 @@ import { telegramUrl, xUrl, } from '@/consts/links'; +import useOpenSupportForm from '@/hooks/useOpenSupportForm'; import { impactLight } from '@/integrations/haptics'; import { usePassport } from '@/providers/passportDataProvider'; -import { openSupportForm } from '@/services/support'; import { useSettingStore } from '@/stores/settingStore'; import { extraYPadding } from '@/utils/styleUtils'; @@ -58,6 +58,7 @@ interface MenuButtonProps extends PropsWithChildren { interface SocialButtonProps { Icon: React.FC; href: string; + onPress?: () => void; } // Avoid importing RootStackParamList; we only need string route names plus a few literals @@ -139,8 +140,12 @@ const MenuButton: React.FC = ({ children, Icon, onPress }) => ( ); -const SocialButton: React.FC = ({ Icon, href }) => { - const onPress = useCallback(() => { +const SocialButton: React.FC = ({ + Icon, + href, + onPress: customOnPress, +}) => { + const defaultOnPress = useCallback(() => { impactLight(); Linking.openURL(href); }, [href]); @@ -149,7 +154,7 @@ const SocialButton: React.FC = ({ Icon, href }) => { {social.map(([Icon, href], i) => ( - + ))} diff --git a/app/src/screens/app/GratificationScreen.tsx b/app/src/screens/app/GratificationScreen.tsx deleted file mode 100644 index aaac909cc..000000000 --- a/app/src/screens/app/GratificationScreen.tsx +++ /dev/null @@ -1,269 +0,0 @@ -// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. -// SPDX-License-Identifier: BUSL-1.1 -// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. - -import React, { useCallback, useState } from 'react'; -import { - Dimensions, - Pressable, - StyleSheet, - Text as RNText, -} from 'react-native'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { Text, View, YStack } from 'tamagui'; -import { useNavigation, useRoute } from '@react-navigation/native'; -import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; -import { X } from '@tamagui/lucide-icons'; - -import { DelayedLottieView } from '@selfxyz/mobile-sdk-alpha'; -import youWinAnimation from '@selfxyz/mobile-sdk-alpha/animations/loading/youWin.json'; -import { PrimaryButton } from '@selfxyz/mobile-sdk-alpha/components'; -import { - black, - slate700, - white, -} from '@selfxyz/mobile-sdk-alpha/constants/colors'; -import { dinot, dinotBold } from '@selfxyz/mobile-sdk-alpha/constants/fonts'; - -import GratificationBg from '@/assets/images/gratification_bg.svg'; -import SelfLogo from '@/assets/logos/self.svg'; -import { SystemBars } from '@/components/SystemBars'; -import type { RootStackParamList } from '@/navigation'; - -const GratificationScreen: React.FC = () => { - const { top, bottom } = useSafeAreaInsets(); - const navigation = - useNavigation>(); - const route = useRoute(); - const params = route.params as { points?: number } | undefined; - const pointsEarned = params?.points ?? 0; - const [isAnimationFinished, setIsAnimationFinished] = useState(false); - const { width: screenWidth, height: screenHeight } = Dimensions.get('window'); - - const handleExploreRewards = () => { - // Navigate to Points screen - navigation.navigate('Points' as never); - }; - - const handleInviteFriend = () => { - navigation.navigate('Referral' as never); - }; - - const handleBackPress = () => { - navigation.navigate('Points' as never); - }; - - const handleAnimationFinish = useCallback(() => { - setIsAnimationFinished(true); - }, []); - - // Show animation first, then content after it finishes - if (!isAnimationFinished) { - return ( - - - - ); - } - - return ( - - - {/* Full screen background */} - - - - - {/* Black overlay for top safe area (status bar) */} - - - {/* Black overlay for bottom safe area */} - - - {/* Back button */} - - - - - - - - - {/* Main content container */} - - {/* Dialogue container */} - - {/* Logo icon */} - - - - - {/* Points display */} - - - {pointsEarned} - - - points earned - - - - {/* Description text */} - - Earn more points by proving your identity and referring friends - - - - {/* Bottom button container */} - - - Explore rewards - - [ - styles.secondaryButton, - pressed && styles.secondaryButtonPressed, - ]} - > - Invite friends - - - - - ); -}; - -export default GratificationScreen; - -const styles = StyleSheet.create({ - primaryButton: { - borderRadius: 60, - borderWidth: 1, - borderColor: slate700, - padding: 14, - }, - secondaryButton: { - width: '100%', - backgroundColor: white, - borderWidth: 1, - borderColor: white, - padding: 14, - borderRadius: 60, - alignItems: 'center', - justifyContent: 'center', - }, - secondaryButtonPressed: { - opacity: 0.8, - }, - secondaryButtonText: { - fontFamily: dinot, - fontSize: 18, - color: black, - textAlign: 'center', - }, - logoContainer: { - paddingBottom: 24, - }, - animation: { - width: '100%', - height: '100%', - }, -}); diff --git a/app/src/screens/app/ReferralScreen.tsx b/app/src/screens/app/ReferralScreen.tsx index 0bcf889d3..42658293e 100644 --- a/app/src/screens/app/ReferralScreen.tsx +++ b/app/src/screens/app/ReferralScreen.tsx @@ -76,8 +76,8 @@ const ReferralScreen: React.FC = () => { gap={42} > diff --git a/app/src/screens/app/SplashScreen.tsx b/app/src/screens/app/SplashScreen.tsx index 803111e63..41de7f179 100644 --- a/app/src/screens/app/SplashScreen.tsx +++ b/app/src/screens/app/SplashScreen.tsx @@ -32,6 +32,8 @@ import { import { useSettingStore } from '@/stores/settingStore'; import { IS_DEV_MODE } from '@/utils/devUtils'; +const INIT_TIMEOUT_MS = 30_000; + const SplashScreen: React.FC = ({}) => { const selfClient = useSelfClient(); const navigation = @@ -44,6 +46,7 @@ const SplashScreen: React.FC = ({}) => { ); const [queuedDeepLink, setQueuedDeepLink] = useState(null); const dataLoadInitiatedRef = useRef(false); + const settledRef = useRef(false); useEffect(() => { if (!dataLoadInitiatedRef.current) { @@ -56,9 +59,14 @@ const SplashScreen: React.FC = ({}) => { }); const loadDataAndDetermineNextScreen = async () => { + const startTime = Date.now(); + const elapsed = () => `${Date.now() - startTime}ms`; + try { - // Initialize native modules first, before any data operations const modulesReady = await initializeNativeModules(); + console.log( + `SplashScreen: initializeNativeModules complete (${elapsed()})`, + ); if (!modulesReady) { console.warn( 'Native modules not ready, proceeding with limited functionality', @@ -66,22 +74,39 @@ const SplashScreen: React.FC = ({}) => { } await migrateFromLegacyStorage(); + console.log( + `SplashScreen: migrateFromLegacyStorage complete (${elapsed()})`, + ); const needsMigration = await checkIfAnyDocumentsNeedMigration(); + console.log( + `SplashScreen: checkIfAnyDocumentsNeedMigration complete (${elapsed()})`, + ); if (needsMigration) { await checkAndUpdateRegistrationStates(selfClient); + console.log( + `SplashScreen: checkAndUpdateRegistrationStates complete (${elapsed()})`, + ); } await hasAnyValidRegisteredDocument(selfClient); + console.log( + `SplashScreen: hasAnyValidRegisteredDocument complete (${elapsed()})`, + ); const parentScreen = 'Home'; - // Migrate keychain to secure storage with biometric protection try { await migrateToSecureKeychain(); + console.log( + `SplashScreen: migrateToSecureKeychain complete (${elapsed()})`, + ); } catch (error) { console.warn('Keychain migration failed, continuing:', error); } + if (settledRef.current) return; + settledRef.current = true; + setDeeplinkParentScreen(parentScreen); const queuedUrl = getAndClearQueuedUrl(); @@ -94,13 +119,32 @@ const SplashScreen: React.FC = ({}) => { setNextScreen(parentScreen); } } catch (error) { - console.error(`Error in SplashScreen data loading: ${error}`); + if (settledRef.current) return; + settledRef.current = true; + + console.error( + `SplashScreen: initialization failed (${elapsed()})`, + error, + ); setDeeplinkParentScreen('Home'); setNextScreen('Home'); } }; - loadDataAndDetermineNextScreen(); + const timeoutId = setTimeout(() => { + if (settledRef.current) return; + settledRef.current = true; + + console.error( + `SplashScreen: initialization timed out after ${INIT_TIMEOUT_MS}ms`, + ); + setDeeplinkParentScreen('Home'); + setNextScreen('Home'); + }, INIT_TIMEOUT_MS); + + loadDataAndDetermineNextScreen().finally(() => { + clearTimeout(timeoutId); + }); } }, [checkBiometricsAvailable, setBiometricsAvailable, selfClient]); diff --git a/app/src/screens/documents/scanning/DocumentNFCScanScreen.tsx b/app/src/screens/documents/scanning/DocumentNFCScanScreen.tsx index 42f06d033..609a9eb50 100644 --- a/app/src/screens/documents/scanning/DocumentNFCScanScreen.tsx +++ b/app/src/screens/documents/scanning/DocumentNFCScanScreen.tsx @@ -57,6 +57,7 @@ import { logNFCEvent } from '@/config/sentry'; import { useErrorInjection } from '@/hooks/useErrorInjection'; import { useFeedbackAutoHide } from '@/hooks/useFeedbackAutoHide'; import useHapticNavigation from '@/hooks/useHapticNavigation'; +import useOpenSupportForm from '@/hooks/useOpenSupportForm'; import { buttonTap, feedbackSuccess, @@ -75,7 +76,6 @@ import { trackNfcEvent, } from '@/services/analytics'; import { - openSupportForm, SUPPORT_FORM_BUTTON_TEXT, SUPPORT_FORM_MESSAGE, } from '@/services/support'; @@ -103,6 +103,7 @@ type DocumentNFCScanRoute = RouteProp< const DocumentNFCScanScreen: React.FC = () => { const selfClient = useSelfClient(); const { trackEvent, useMRZStore } = selfClient; + const openSupportForm = useOpenSupportForm(); const navigation = useNavigation>(); @@ -181,7 +182,7 @@ const DocumentNFCScanScreen: React.FC = () => { const onReportIssue = useCallback(() => { openSupportForm(); - }, []); + }, [openSupportForm]); const openErrorModal = useCallback( (message: string) => { diff --git a/app/src/screens/documents/scanning/DocumentNFCTroubleScreen.tsx b/app/src/screens/documents/scanning/DocumentNFCTroubleScreen.tsx index f50f24f55..f53debb11 100644 --- a/app/src/screens/documents/scanning/DocumentNFCTroubleScreen.tsx +++ b/app/src/screens/documents/scanning/DocumentNFCTroubleScreen.tsx @@ -17,10 +17,11 @@ import Tips from '@/components/Tips'; import { useFeedbackAutoHide } from '@/hooks/useFeedbackAutoHide'; import useHapticNavigation from '@/hooks/useHapticNavigation'; import { useKycLauncher } from '@/hooks/useKycLauncher'; +import useOpenSupportForm from '@/hooks/useOpenSupportForm'; import { selectionChange } from '@/integrations/haptics'; import SimpleScrolledTitleLayout from '@/layouts/SimpleScrolledTitleLayout'; import { flushAllAnalytics } from '@/services/analytics'; -import { openSupportForm, SUPPORT_FORM_BUTTON_TEXT } from '@/services/support'; +import { SUPPORT_FORM_BUTTON_TEXT } from '@/services/support'; const tips: TipProps[] = [ { @@ -50,6 +51,7 @@ const tips: TipProps[] = [ ]; const DocumentNFCTroubleScreen: React.FC = () => { + const openSupportForm = useOpenSupportForm(); const navigation = useNavigation(); const handleDismiss = useCallback(() => { selectionChange(); diff --git a/app/src/screens/home/HomeScreen.tsx b/app/src/screens/home/HomeScreen.tsx index 85ac717d3..5f14a464a 100644 --- a/app/src/screens/home/HomeScreen.tsx +++ b/app/src/screens/home/HomeScreen.tsx @@ -4,15 +4,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; import { Pressable } from 'react-native'; -import { - Button, - ScrollView, - Spinner, - Text, - View, - XStack, - YStack, -} from 'tamagui'; +import { ScrollView, Spinner, YStack } from 'tamagui'; import { useFocusEffect, useIsFocused, @@ -25,20 +17,10 @@ import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; import type { DocumentCatalog, IDDocument } from '@selfxyz/common/utils/types'; import type { DocumentMetadata } from '@selfxyz/mobile-sdk-alpha'; import { useSelfClient } from '@selfxyz/mobile-sdk-alpha'; -import { - DocumentEvents, - PointEvents, -} from '@selfxyz/mobile-sdk-alpha/constants/analytics'; -import { - black, - blue600, - slate50, - slate300, -} from '@selfxyz/mobile-sdk-alpha/constants/colors'; -import { dinot } from '@selfxyz/mobile-sdk-alpha/constants/fonts'; +import { DocumentEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics'; +import { black, slate50 } from '@selfxyz/mobile-sdk-alpha/constants/colors'; import { useSafeBottomPadding } from '@selfxyz/mobile-sdk-alpha/hooks'; -import LogoInversed from '@/assets/images/logo_inversed.svg'; import EmptyIdCard from '@/components/homescreen/EmptyIdCard'; import ExpiredIdCard from '@/components/homescreen/ExpiredIdCard'; import IdCardLayout from '@/components/homescreen/IdCard'; @@ -46,9 +28,8 @@ import PendingIdCard from '@/components/homescreen/PendingIdCard'; import UnregisteredIdCard from '@/components/homescreen/UnregisteredIdCard'; import { useAppUpdates } from '@/hooks/useAppUpdates'; import useConnectionModal from '@/hooks/useConnectionModal'; -import { useEarnPointsFlow } from '@/hooks/useEarnPointsFlow'; -import { usePoints } from '@/hooks/usePoints'; import { useReferralConfirmation } from '@/hooks/useReferralConfirmation'; +import { useRegisterReferral } from '@/hooks/useRegisterReferral'; import { useTestReferralFlow } from '@/hooks/useTestReferralFlow'; import type { RootStackParamList } from '@/navigation'; import { usePassport } from '@/providers/passportDataProvider'; @@ -96,8 +77,6 @@ const HomeScreen: React.FC = () => { v => v.status === 'pending' || v.status === 'processing', ); - const { amount: selfPoints } = usePoints(); - // DEV MODE: Test referral flow hook (only show alert when screen is focused) const isFocused = useIsFocused(); const route = useRoute(); @@ -191,28 +170,28 @@ const HomeScreen: React.FC = () => { // Calculate bottom padding to prevent button bleeding into system navigation const bottomPadding = useSafeBottomPadding(20); - // Create a stable reference to avoid hook dependency issues - const onEarnPointsPressRef = useRef< - ((skipReferralFlow?: boolean) => Promise) | null - >(null); + const { registerReferral } = useRegisterReferral(); - const { isReferralConfirmed } = useReferralConfirmation({ + const handleReferralConfirmed = useCallback(async () => { + if (!referrer) { + return; + } + const store = useUserStore.getState(); + if (!store.isReferrerRegistered(referrer)) { + const result = await registerReferral(referrer); + if (!result.success) { + return; + } + store.markReferrerAsRegistered(referrer); + } + store.clearDeepLinkReferrer(); + }, [referrer, registerReferral]); + + useReferralConfirmation({ hasReferrer, - onConfirmed: () => { - onEarnPointsPressRef.current?.(false); - }, + onConfirmed: handleReferralConfirmed, }); - const { onEarnPointsPress } = useEarnPointsFlow({ - hasReferrer, - isReferralConfirmed, - }); - - // Update the ref whenever onEarnPointsPress changes - useEffect(() => { - onEarnPointsPressRef.current = onEarnPointsPress; - }, [onEarnPointsPress]); - const handleDocumentPress = useCallback( (metadata: DocumentMetadata, documentData: IDDocument) => { selfClient.trackEvent(DocumentEvents.DOCUMENT_SELECTED, { @@ -344,86 +323,6 @@ const HomeScreen: React.FC = () => { ); })} - - - - - - - - {`${selfPoints} SELF POINTS`} - - - Earn points by referring friends, disclosing proof requests, and - more. - - - - - ); }; diff --git a/app/src/screens/home/PointsInfoScreen.tsx b/app/src/screens/home/PointsInfoScreen.tsx deleted file mode 100644 index 6e915d826..000000000 --- a/app/src/screens/home/PointsInfoScreen.tsx +++ /dev/null @@ -1,226 +0,0 @@ -// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. -// SPDX-License-Identifier: BUSL-1.1 -// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. - -import React, { useCallback, useEffect, useMemo, useRef } from 'react'; -import { Image, StyleSheet } from 'react-native'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { ScrollView, Text, View, XStack, YStack } from 'tamagui'; -import type { StaticScreenProps } from '@react-navigation/native'; - -import { PrimaryButton, Title } from '@selfxyz/mobile-sdk-alpha/components'; -import { - black, - slate50, - slate500, - white, -} from '@selfxyz/mobile-sdk-alpha/constants/colors'; -import { dinot } from '@selfxyz/mobile-sdk-alpha/constants/fonts'; - -import CheckmarkSquareIcon from '@/assets/icons/checkmark_square.svg'; -import CloudBackupIcon from '@/assets/icons/cloud_backup.svg'; -import PushNotificationsIcon from '@/assets/icons/push_notifications.svg'; -import StarIcon from '@/assets/icons/star.svg'; -import Referral from '@/assets/images/referral.png'; -import { - getModalCallbacks, - unregisterModalCallbacks, -} from '@/utils/modalCallbackRegistry'; - -type PointsInfoScreenProps = StaticScreenProps< - | { - showNextButton?: boolean; - callbackId?: number; - } - | undefined ->; - -interface EarnPointsItemProps { - title: string; - description: string; - icon: React.ReactNode; -} - -const EarnPointsItem = ({ title, description, icon }: EarnPointsItemProps) => { - return ( - - - {icon} - - - {title} - {description} - - - ); -}; - -const EARN_POINTS_ITEMS = [ - { - title: 'Inviting friends to Self', - description: - "You'll both receive Self Points after your friend signs their first proof.", - icon: , - }, - { - title: 'Signing proof requests', - description: - 'Every successful proof that you sign will reward you with Self Points.', - icon: , - }, - { - title: 'Enabling push notifications', - description: 'Instantly earn Self Points by activating push notifications.', - icon: , - }, - { - title: 'Activate cloud back up', - description: - 'Securely back up your account in settings to earn Self Points instantly.', - icon: , - }, -]; - -const PointsInfoScreen: React.FC = ({ - route: { params }, -}) => { - const { showNextButton, callbackId } = params || {}; - const { left, right, bottom } = useSafeAreaInsets(); - const callbacks = useMemo( - () => (callbackId ? getModalCallbacks(callbackId) : undefined), - [callbackId], - ); - const buttonPressedRef = useRef(false); - - // Handle button press: mark as pressed and call the callback - const handleNextPress = useCallback(() => { - if (callbackId !== undefined) { - buttonPressedRef.current = true; - } - callbacks?.onButtonPress(); - }, [callbackId, callbacks]); - - // Cleanup: Call onModalDismiss and unregister callbacks when component unmounts - // Only call onModalDismiss if user navigated back (didn't press the button) - useEffect(() => { - return () => { - if (callbackId !== undefined) { - // Always unregister on unmount to prevent memory leaks - if (!buttonPressedRef.current) { - // User navigated back without pressing "Next" - call onModalDismiss to clear referrer - callbacks?.onModalDismiss(); - } - unregisterModalCallbacks(callbackId); - } - }; - }, [callbackId, callbacks]); - - return ( - - - - - - How it works - - Self Points are rewards you earn for engaging with the Self - platform. You can earn Points by: - - - - {EARN_POINTS_ITEMS.map(item => ( - - ))} - - - Points are deposited at noon UTC every Sunday - - To ensure privacy and security on-chain, points are deposited into - your wallet every Sunday at noon UTC. - - - - - Any points that you earn during the week will be added to your - account on the following Sunday. - - - You can track your incoming points in the Self app along with the - countdown to Self Sunday every week. - - - - - {showNextButton && ( - - Next - - )} - - ); -}; - -export default PointsInfoScreen; - -const styles = StyleSheet.create({ - description: { - fontFamily: dinot, - fontSize: 18, - fontWeight: '500', - color: black, - }, - instructionsContainer: { - fontFamily: dinot, - fontSize: 16, - fontWeight: '500', - color: slate500, - backgroundColor: slate50, - paddingVertical: 20, - paddingHorizontal: 10, - borderRadius: 10, - }, - instructionsText: { - fontFamily: dinot, - fontSize: 16, - fontWeight: '500', - color: slate500, - }, - nextButton: { - textTransform: 'uppercase', - }, - iconContainer: { - width: 40, - height: 40, - alignItems: 'center', - justifyContent: 'center', - }, - pointsItemTitle: { - fontFamily: dinot, - fontSize: 18, - fontWeight: '500', - color: black, - }, - pointsItemDescription: { - fontFamily: dinot, - fontSize: 16, - fontWeight: '500', - color: slate500, - }, -}); diff --git a/app/src/screens/shared/ComingSoonScreen.tsx b/app/src/screens/shared/ComingSoonScreen.tsx index 9229935c3..ee56ff1ab 100644 --- a/app/src/screens/shared/ComingSoonScreen.tsx +++ b/app/src/screens/shared/ComingSoonScreen.tsx @@ -22,12 +22,12 @@ import { } from '@selfxyz/mobile-sdk-alpha/constants/colors'; import useHapticNavigation from '@/hooks/useHapticNavigation'; +import useOpenSupportForm from '@/hooks/useOpenSupportForm'; import { notificationError } from '@/integrations/haptics'; import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout'; import type { SharedRoutesParamList } from '@/navigation/types'; import { flush as flushAnalytics } from '@/services/analytics'; import { - openSupportForm, SUPPORT_FORM_COMING_SOON_BUTTON_TEXT, SUPPORT_FORM_COMING_SOON_MESSAGE, } from '@/services/support'; @@ -39,6 +39,7 @@ type ComingSoonScreenProps = NativeStackScreenProps< const ComingSoonScreen: React.FC = ({ route }) => { const navigateToHome = useHapticNavigation('Home'); + const openSupportForm = useOpenSupportForm(); const { countryName, countryCode, documentTypeText } = useMemo(() => { try { @@ -85,12 +86,8 @@ const ComingSoonScreen: React.FC = ({ route }) => { navigateToHome(); }; - const onNotifyMe = async () => { - try { - await openSupportForm(); - } catch (error) { - console.error('Failed to open support form:', error); - } + const onNotifyMe = () => { + openSupportForm(); }; useEffect(() => { diff --git a/app/src/screens/shared/WebViewScreen.tsx b/app/src/screens/shared/WebViewScreen.tsx index 6bf00df38..74775b195 100644 --- a/app/src/screens/shared/WebViewScreen.tsx +++ b/app/src/screens/shared/WebViewScreen.tsx @@ -51,7 +51,7 @@ type WebViewScreenProps = NativeStackScreenProps< >; const defaultUrl = selfUrl; -const fallbackUrl = 'https://apps.self.xyz'; +const fallbackUrl = 'https://self.xyz'; const styles = StyleSheet.create({ webViewContainer: { diff --git a/app/src/screens/verification/ProofRequestStatusScreen.tsx b/app/src/screens/verification/ProofRequestStatusScreen.tsx index c21092fa6..7d2b3f7e6 100644 --- a/app/src/screens/verification/ProofRequestStatusScreen.tsx +++ b/app/src/screens/verification/ProofRequestStatusScreen.tsx @@ -6,8 +6,7 @@ import type { LottieViewProps } from 'lottie-react-native'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import { Linking, StyleSheet, View } from 'react-native'; import { ScrollView, Spinner } from 'tamagui'; -import { useIsFocused, useNavigation } from '@react-navigation/native'; -import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; +import { useIsFocused } from '@react-navigation/native'; import { DelayedLottieView, useSelfClient } from '@selfxyz/mobile-sdk-alpha'; import loadingAnimation from '@selfxyz/mobile-sdk-alpha/animations/loading/misc.json'; @@ -30,8 +29,6 @@ import { notificationSuccess, } from '@/integrations/haptics'; import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout'; -import type { RootStackParamList } from '@/navigation'; -import { getWhiteListedDisclosureAddresses } from '@/services/points/utils'; import { useProofHistoryStore } from '@/stores/proofHistoryStore'; import { ProofStatus } from '@/stores/proofTypes'; @@ -42,8 +39,6 @@ const SuccessScreen: React.FC = () => { const selfApp = useSelfAppStore(state => state.selfApp); const appName = selfApp?.appName; const goHome = useHapticNavigation('Home'); - const navigation = - useNavigation>(); const { updateProofStatus } = useProofHistoryStore(); @@ -58,28 +53,18 @@ const SuccessScreen: React.FC = () => { useState(loadingAnimation); const [countdown, setCountdown] = useState(null); const [countdownStarted, setCountdownStarted] = useState(false); - const [whitelistedPoints, setWhitelistedPoints] = useState( - null, - ); const timerRef = useRef(null); const onOkPress = useCallback(async () => { buttonTap(); - - if (whitelistedPoints !== null) { - navigation.navigate('Gratification', { - points: whitelistedPoints, - }); - setTimeout(() => { + goHome(); + const completedSessionId = sessionId; + setTimeout(() => { + if (useProvingStore.getState().uuid === completedSessionId) { selfClient.getSelfAppState().cleanSelfApp(); - }, 2000); - } else { - goHome(); - setTimeout(() => { - selfClient.getSelfAppState().cleanSelfApp(); - }, 2000); - } - }, [whitelistedPoints, navigation, goHome, selfClient]); + } + }, 2000); + }, [goHome, selfClient, sessionId, useProvingStore]); function cancelDeeplinkCallbackRedirect() { setCountdown(null); @@ -105,27 +90,6 @@ const SuccessScreen: React.FC = () => { appName, }); - if (selfApp?.endpoint && whitelistedPoints === null) { - const checkWhitelist = async () => { - try { - const whitelistedContracts = - await getWhiteListedDisclosureAddresses(); - const endpoint = selfApp.endpoint.toLowerCase(); - const whitelistedContract = whitelistedContracts.find( - c => c.contract_address.toLowerCase() === endpoint, - ); - - if (whitelistedContract) { - setWhitelistedPoints(whitelistedContract.points_per_disclosure); - } - } catch (error) { - console.error('Error checking whitelist:', error); - } - }; - - checkWhitelist(); - } - if (isFocused && !countdownStarted && selfApp?.deeplinkCallback) { if (selfApp?.deeplinkCallback) { try { @@ -170,9 +134,7 @@ const SuccessScreen: React.FC = () => { reason, updateProofStatus, selfApp?.deeplinkCallback, - selfApp?.endpoint, countdownStarted, - whitelistedPoints, ]); useEffect(() => { diff --git a/app/src/screens/verification/QRCodeTroubleScreen.tsx b/app/src/screens/verification/QRCodeTroubleScreen.tsx index d345561e9..a76e8edb5 100644 --- a/app/src/screens/verification/QRCodeTroubleScreen.tsx +++ b/app/src/screens/verification/QRCodeTroubleScreen.tsx @@ -11,10 +11,10 @@ import { slate500 } from '@selfxyz/mobile-sdk-alpha/constants/colors'; import type { TipProps } from '@/components/Tips'; import Tips from '@/components/Tips'; import useHapticNavigation from '@/hooks/useHapticNavigation'; +import useOpenSupportForm from '@/hooks/useOpenSupportForm'; import SimpleScrolledTitleLayout from '@/layouts/SimpleScrolledTitleLayout'; import { flushAllAnalytics } from '@/services/analytics'; import { - openSupportForm, SUPPORT_FORM_BUTTON_TEXT, SUPPORT_FORM_TIP_MESSAGE, } from '@/services/support'; @@ -50,6 +50,7 @@ const tipsDeeplink: TipProps[] = [ ]; const QRCodeTrouble: React.FC = () => { + const openSupportForm = useOpenSupportForm(); const go = useHapticNavigation('Home', { action: 'cancel' }); // error screen, flush analytics diff --git a/app/src/services/support.ts b/app/src/services/support.ts index 62bec8cdf..730a0d422 100644 --- a/app/src/services/support.ts +++ b/app/src/services/support.ts @@ -2,9 +2,10 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import { Alert, Linking } from 'react-native'; +import { Linking } from 'react-native'; import { supportFormUrl } from '@/consts/links'; +import { navigationRef } from '@/navigation'; export const SUPPORT_FORM_BUTTON_TEXT = 'Send feedback'; @@ -17,26 +18,20 @@ export const SUPPORT_FORM_MESSAGE = 'Have feedback? Please fill out our form.'; export const SUPPORT_FORM_TIP_MESSAGE = 'Have feedback? Let us know.'; -export const openSupportForm = async (): Promise => { - try { - const canOpen = await Linking.canOpenURL(supportFormUrl); - if (canOpen) { - await Linking.openURL(supportFormUrl); - } else { - console.warn('Cannot open support form URL - no handler available'); - Alert.alert( - 'Unable to Open Link', - 'No app is available to open the support form. Please try again using a web browser.', - ); - } - } catch (error) { - console.error( - 'Failed to open support form:', - error instanceof Error ? error.message : String(error), - ); - Alert.alert( - 'Error', - 'Unable to open support form. Please try again later or contact support through another method.', +/** + * Imperatively open the support form using navigationRef. + * Safe to call from anywhere — inside or outside the React Navigation tree. + * Falls back to opening the URL in the system browser if navigation is not ready. + */ +export const openSupportForm = (): void => { + if (navigationRef.isReady()) { + navigationRef.navigate('WebView', { + url: supportFormUrl, + title: 'Get Support', + }); + } else { + Linking.openURL(supportFormUrl).catch(err => + console.warn('Failed to open support form URL:', err), ); } }; diff --git a/app/src/utils/webview.ts b/app/src/utils/webview.ts index ef4c0e78a..9eeb4f39a 100644 --- a/app/src/utils/webview.ts +++ b/app/src/utils/webview.ts @@ -64,6 +64,7 @@ export const TRUSTED_DOMAINS = Object.freeze([ 'coinbase.com', // Coinbase - Main domain 'karmahq.xyz', // Karma - Launch & fund projects 'lemonade.social', // Lemonade - Events and communities + 'notion.site', // Notion - Support/feedback forms 'self.xyz', // Base domain and all subdomains (*.self.xyz) - includes espresso.self.xyz 'talent.app', // Talent Protocol - Main app 'talentprotocol.com', // Talent Protocol - Marketing/info site diff --git a/app/tests/src/consts/links.test.ts b/app/tests/src/consts/links.test.ts index 2d5639e5e..4ef24d291 100644 --- a/app/tests/src/consts/links.test.ts +++ b/app/tests/src/consts/links.test.ts @@ -62,7 +62,6 @@ describe('links', () => { expect(links.selfUrl).toContain('self.xyz'); expect(links.privacyUrl).toContain('self.xyz'); expect(links.termsUrl).toContain('self.xyz'); - expect(links.appsUrl).toContain('self.xyz'); expect(links.referralBaseUrl).toContain('self.xyz'); expect(links.apiBaseUrl).toContain('self.xyz'); expect(links.pointsApiBaseUrl).toContain('self.xyz'); diff --git a/app/tests/src/hooks/useEarnPointsFlow.test.ts b/app/tests/src/hooks/useEarnPointsFlow.test.ts deleted file mode 100644 index f26452c4d..000000000 --- a/app/tests/src/hooks/useEarnPointsFlow.test.ts +++ /dev/null @@ -1,777 +0,0 @@ -// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. -// SPDX-License-Identifier: BUSL-1.1 -// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. - -import { useNavigation } from '@react-navigation/native'; -import { act, renderHook } from '@testing-library/react-native'; - -import { useSelfClient } from '@selfxyz/mobile-sdk-alpha'; - -import { useEarnPointsFlow } from '@/hooks/useEarnPointsFlow'; -import { useRegisterReferral } from '@/hooks/useRegisterReferral'; -import { - hasUserAnIdentityDocumentRegistered, - hasUserDoneThePointsDisclosure, - POINT_VALUES, - pointsSelfApp, -} from '@/services/points'; -import useUserStore from '@/stores/userStore'; -import { getModalCallbacks } from '@/utils/modalCallbackRegistry'; - -jest.mock('@react-navigation/native', () => ({ - useNavigation: jest.fn(), -})); - -jest.mock('@selfxyz/mobile-sdk-alpha', () => ({ - useSelfClient: jest.fn(), -})); - -jest.mock('@/hooks/useRegisterReferral', () => ({ - useRegisterReferral: jest.fn(), -})); - -jest.mock('@/services/points', () => ({ - hasUserAnIdentityDocumentRegistered: jest.fn(), - hasUserDoneThePointsDisclosure: jest.fn(), - pointsSelfApp: jest.fn(), - POINT_VALUES: { - referee: 24, - }, -})); - -// userStore is used as-is, no mock needed - -const mockNavigate = jest.fn(); -const mockUseNavigation = useNavigation as jest.MockedFunction< - typeof useNavigation ->; -const mockUseSelfClient = useSelfClient as jest.MockedFunction< - typeof useSelfClient ->; -const mockUseRegisterReferral = useRegisterReferral as jest.MockedFunction< - typeof useRegisterReferral ->; -const mockHasUserAnIdentityDocumentRegistered = - hasUserAnIdentityDocumentRegistered as jest.MockedFunction< - typeof hasUserAnIdentityDocumentRegistered - >; -const mockHasUserDoneThePointsDisclosure = - hasUserDoneThePointsDisclosure as jest.MockedFunction< - typeof hasUserDoneThePointsDisclosure - >; -const mockPointsSelfApp = pointsSelfApp as jest.MockedFunction< - typeof pointsSelfApp ->; - -describe('useEarnPointsFlow', () => { - const mockSetSelfApp = jest.fn(); - const mockSelfClient = { - getSelfAppState: jest.fn(() => ({ - setSelfApp: mockSetSelfApp, - })), - }; - const mockRegisterReferral = jest.fn(); - const mockSelfApp = { - appName: '✨ Self Points', - endpoint: '0x829d183faaa675f8f80e8bb25fb1476cd4f7c1f0', - sessionId: 'test-session-id', - }; - - beforeEach(() => { - jest.clearAllMocks(); - mockSetSelfApp.mockClear(); - jest.useFakeTimers(); - - mockUseNavigation.mockReturnValue({ - navigate: mockNavigate, - } as any); - - mockUseSelfClient.mockReturnValue(mockSelfClient as any); - - mockUseRegisterReferral.mockReturnValue({ - registerReferral: mockRegisterReferral, - isLoading: false, - error: null, - }); - - // Reset user store state - useUserStore.getState().clearDeepLinkReferrer(); - useUserStore.getState().registeredReferrers.clear(); - }); - - afterEach(() => { - jest.useRealTimers(); - }); - - describe('Identity verification flow', () => { - it('should show identity verification modal when user has no identity document', async () => { - mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(false); - - const { result } = renderHook(() => - useEarnPointsFlow({ - hasReferrer: false, - isReferralConfirmed: undefined, - }), - ); - - await act(async () => { - await result.current.onEarnPointsPress(); - }); - - expect(mockHasUserAnIdentityDocumentRegistered).toHaveBeenCalled(); - expect(mockNavigate).toHaveBeenCalledWith('Modal', { - titleText: 'Identity Verification Required', - bodyText: - 'To access Self Points, you need to register an identity document with Self first. This helps us verify your identity and keep your points secure.', - buttonText: 'Verify Identity', - secondaryButtonText: 'Not Now', - callbackId: expect.any(Number), - }); - }); - - it('should navigate to CountryPicker when identity verification modal button is pressed', async () => { - mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(false); - - const { result } = renderHook(() => - useEarnPointsFlow({ - hasReferrer: false, - isReferralConfirmed: undefined, - }), - ); - - await act(async () => { - await result.current.onEarnPointsPress(); - }); - - const callbackId = mockNavigate.mock.calls[0][1].callbackId; - const callbacks = getModalCallbacks(callbackId); - - expect(callbacks).toBeDefined(); - - act(() => { - callbacks!.onButtonPress(); - }); - - act(() => { - jest.advanceTimersByTime(100); - }); - - expect(mockNavigate).toHaveBeenCalledWith('CountryPicker'); - }); - - it('should clear referrer when identity verification modal is dismissed with referrer', async () => { - const referrer = '0x1234567890123456789012345678901234567890'; - useUserStore.getState().setDeepLinkReferrer(referrer); - mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(false); - - const { result } = renderHook(() => - useEarnPointsFlow({ - hasReferrer: true, - isReferralConfirmed: undefined, - }), - ); - - await act(async () => { - await result.current.onEarnPointsPress(); - }); - - const callbackId = mockNavigate.mock.calls[0][1].callbackId; - const callbacks = getModalCallbacks(callbackId); - - act(() => { - callbacks!.onModalDismiss(); - }); - - expect(useUserStore.getState().deepLinkReferrer).toBeUndefined(); - }); - }); - - describe('Points disclosure flow', () => { - it('should show points disclosure modal when user has not done disclosure', async () => { - mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(true); - mockHasUserDoneThePointsDisclosure.mockResolvedValue(false); - - const { result } = renderHook(() => - useEarnPointsFlow({ - hasReferrer: false, - isReferralConfirmed: undefined, - }), - ); - - await act(async () => { - await result.current.onEarnPointsPress(); - }); - - expect(mockHasUserAnIdentityDocumentRegistered).toHaveBeenCalled(); - expect(mockHasUserDoneThePointsDisclosure).toHaveBeenCalled(); - - expect(mockNavigate).toHaveBeenCalledWith('PointsInfo', { - showNextButton: true, - callbackId: expect.any(Number), - }); - - // We pass callbackId to retrieve and invoke the callback that displays the points disclosure modal - const callbackId = mockNavigate.mock.calls[0][1].callbackId; - const callbacks = getModalCallbacks(callbackId); - - await act(async () => { - await callbacks!.onButtonPress(); - }); - - expect(mockNavigate).toHaveBeenCalledWith('Modal', { - titleText: 'Points Disclosure Required', - bodyText: - 'To access Self Points, you need to complete the points disclosure first. This helps us verify your identity and keep your points secure.', - buttonText: 'Complete Points Disclosure', - secondaryButtonText: 'Not Now', - callbackId: expect.any(Number), - }); - }); - - it('should navigate to Prove screen when points disclosure modal button is pressed', async () => { - mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(true); - mockHasUserDoneThePointsDisclosure.mockResolvedValue(false); - mockPointsSelfApp.mockResolvedValue(mockSelfApp as any); - - const { result } = renderHook(() => - useEarnPointsFlow({ - hasReferrer: false, - isReferralConfirmed: undefined, - }), - ); - - await act(async () => { - await result.current.onEarnPointsPress(); - }); - - expect(mockNavigate).toHaveBeenCalledWith('PointsInfo', { - showNextButton: true, - callbackId: expect.any(Number), - }); - - const pointsInfoCallbackId = mockNavigate.mock.calls[0][1].callbackId; - const pointsInfoCallbacks = getModalCallbacks(pointsInfoCallbackId); - - await act(async () => { - await pointsInfoCallbacks!.onButtonPress(); - }); - - const callbackId = mockNavigate.mock.calls[1][1].callbackId; - const callbacks = getModalCallbacks(callbackId); - - expect(callbacks).toBeDefined(); - - await act(async () => { - await callbacks!.onButtonPress(); - }); - - expect(mockPointsSelfApp).toHaveBeenCalled(); - - // setSelfApp is called synchronously after pointsSelfApp resolves - expect(mockSetSelfApp).toHaveBeenCalledWith(mockSelfApp); - - act(() => { - jest.advanceTimersByTime(100); - }); - - expect(mockNavigate).toHaveBeenCalledWith('ProvingScreenRouter'); - }); - - it('should clear referrer when points disclosure modal is dismissed with referrer', async () => { - const referrer = '0x1234567890123456789012345678901234567890'; - useUserStore.getState().setDeepLinkReferrer(referrer); - mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(true); - mockHasUserDoneThePointsDisclosure.mockResolvedValue(false); - - const { result } = renderHook(() => - useEarnPointsFlow({ - hasReferrer: true, - isReferralConfirmed: undefined, - }), - ); - - await act(async () => { - await result.current.onEarnPointsPress(); - }); - - expect(mockNavigate).toHaveBeenCalledWith('PointsInfo', { - showNextButton: true, - callbackId: expect.any(Number), - }); - - const pointsInfoCallbackId = mockNavigate.mock.calls[0][1].callbackId; - const pointsInfoCallbacks = getModalCallbacks(pointsInfoCallbackId); - - await act(async () => { - await pointsInfoCallbacks!.onButtonPress(); - }); - - const callbackId = mockNavigate.mock.calls[1][1].callbackId; - const callbacks = getModalCallbacks(callbackId); - - act(() => { - callbacks!.onModalDismiss(); - }); - - expect(useUserStore.getState().deepLinkReferrer).toBeUndefined(); - }); - }); - - describe('Direct navigation flow', () => { - it('should navigate to Points screen when user has completed all checks and no referrer', async () => { - mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(true); - mockHasUserDoneThePointsDisclosure.mockResolvedValue(true); - - const { result } = renderHook(() => - useEarnPointsFlow({ - hasReferrer: false, - isReferralConfirmed: undefined, - }), - ); - - await act(async () => { - await result.current.onEarnPointsPress(); - }); - - expect(mockNavigate).toHaveBeenCalledWith('Points'); - }); - - it('should not navigate when user has completed all checks, has referrer, but skipReferralFlow is true', async () => { - const referrer = '0x1234567890123456789012345678901234567890'; - useUserStore.getState().setDeepLinkReferrer(referrer); - mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(true); - mockHasUserDoneThePointsDisclosure.mockResolvedValue(true); - - const { result } = renderHook(() => - useEarnPointsFlow({ - hasReferrer: true, - isReferralConfirmed: true, - }), - ); - - await act(async () => { - await result.current.onEarnPointsPress(true); - }); - - // Should not navigate to Points or Gratification - expect(mockNavigate).not.toHaveBeenCalledWith('Points'); - expect(mockNavigate).not.toHaveBeenCalledWith('Gratification'); - }); - }); - - describe('Referral flow', () => { - const referrer = '0x1234567890123456789012345678901234567890'; - - beforeEach(() => { - useUserStore.getState().setDeepLinkReferrer(referrer); - mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(true); - mockHasUserDoneThePointsDisclosure.mockResolvedValue(true); - }); - - it('should handle referral flow when referrer is confirmed and not skipped', async () => { - mockRegisterReferral.mockResolvedValue({ success: true }); - - const { result } = renderHook(() => - useEarnPointsFlow({ - hasReferrer: true, - isReferralConfirmed: true, - }), - ); - - await act(async () => { - await result.current.onEarnPointsPress(false); - }); - - expect(mockRegisterReferral).toHaveBeenCalledWith(referrer); - expect(useUserStore.getState().isReferrerRegistered(referrer)).toBe(true); - expect(useUserStore.getState().deepLinkReferrer).toBeUndefined(); - expect(mockNavigate).toHaveBeenCalledWith('Gratification', { - points: POINT_VALUES.referee, - }); - }); - - it('should not register referral if already registered', async () => { - useUserStore.getState().markReferrerAsRegistered(referrer); - - const { result } = renderHook(() => - useEarnPointsFlow({ - hasReferrer: true, - isReferralConfirmed: true, - }), - ); - - await act(async () => { - await result.current.onEarnPointsPress(false); - }); - - expect(mockRegisterReferral).not.toHaveBeenCalled(); - expect(mockNavigate).toHaveBeenCalledWith('Gratification', { - points: POINT_VALUES.referee, - }); - }); - - it('should show error modal and preserve referrer if referral registration fails', async () => { - mockRegisterReferral.mockResolvedValue({ - success: false, - error: 'Network error occurred', - }); - - const originalConsoleError = console.error; - console.error = jest.fn(); - - const { result } = renderHook(() => - useEarnPointsFlow({ - hasReferrer: true, - isReferralConfirmed: true, - }), - ); - - await act(async () => { - await result.current.onEarnPointsPress(false); - }); - - expect(mockRegisterReferral).toHaveBeenCalledWith(referrer); - - // Should NOT navigate to Gratification on failure - expect(mockNavigate).not.toHaveBeenCalledWith('Gratification', { - points: POINT_VALUES.referee, - }); - - // Should show error modal instead - expect(mockNavigate).toHaveBeenCalledWith('Modal', { - titleText: 'Referral Registration Failed', - bodyText: expect.stringContaining('Network error occurred'), - buttonText: 'Try Again', - secondaryButtonText: 'Dismiss', - callbackId: expect.any(Number), - }); - - // Should preserve the referrer for retry - expect(useUserStore.getState().deepLinkReferrer).toBe(referrer); - - // Should log the error - expect(console.error).toHaveBeenCalledWith( - 'Referral registration failed:', - 'Network error occurred', - ); - - console.error = originalConsoleError; - }); - - it('should retry referral registration when error modal retry button is pressed', async () => { - // First call fails, second call succeeds - mockRegisterReferral - .mockResolvedValueOnce({ - success: false, - error: 'Network error', - }) - .mockResolvedValueOnce({ - success: true, - }); - - const originalConsoleError = console.error; - console.error = jest.fn(); - - const { result } = renderHook(() => - useEarnPointsFlow({ - hasReferrer: true, - isReferralConfirmed: true, - }), - ); - - // First attempt - should fail - await act(async () => { - await result.current.onEarnPointsPress(false); - }); - - expect(mockRegisterReferral).toHaveBeenCalledTimes(1); - expect(mockNavigate).toHaveBeenCalledWith('Modal', { - titleText: 'Referral Registration Failed', - bodyText: expect.stringContaining('Network error'), - buttonText: 'Try Again', - secondaryButtonText: 'Dismiss', - callbackId: expect.any(Number), - }); - - // Referrer should still be in store - expect(useUserStore.getState().deepLinkReferrer).toBe(referrer); - - // Get the callback from the error modal and trigger retry - const callbackId = mockNavigate.mock.calls[0][1].callbackId; - const callbacks = getModalCallbacks(callbackId); - - mockNavigate.mockClear(); - - // Retry - should succeed - await act(async () => { - await callbacks!.onButtonPress(); - }); - - expect(mockRegisterReferral).toHaveBeenCalledTimes(2); - expect(mockRegisterReferral).toHaveBeenCalledWith(referrer); - - // Should now navigate to Gratification - expect(mockNavigate).toHaveBeenCalledWith('Gratification', { - points: POINT_VALUES.referee, - }); - - // Should mark referrer as registered and clear it - expect(useUserStore.getState().isReferrerRegistered(referrer)).toBe(true); - expect(useUserStore.getState().deepLinkReferrer).toBeUndefined(); - - console.error = originalConsoleError; - }); - - it('should clear referrer when error modal is dismissed', async () => { - mockRegisterReferral.mockResolvedValue({ - success: false, - error: 'API error', - }); - - const originalConsoleError = console.error; - console.error = jest.fn(); - - const { result } = renderHook(() => - useEarnPointsFlow({ - hasReferrer: true, - isReferralConfirmed: true, - }), - ); - - await act(async () => { - await result.current.onEarnPointsPress(false); - }); - - const callbackId = mockNavigate.mock.calls[0][1].callbackId; - const callbacks = getModalCallbacks(callbackId); - - // Dismiss the error modal - act(() => { - callbacks!.onModalDismiss(); - }); - - // Referrer should be cleared to prevent retry loop - expect(useUserStore.getState().deepLinkReferrer).toBeUndefined(); - - console.error = originalConsoleError; - }); - - it('should not handle referral flow when isReferralConfirmed is false', async () => { - const { result } = renderHook(() => - useEarnPointsFlow({ - hasReferrer: true, - isReferralConfirmed: false, - }), - ); - - await act(async () => { - await result.current.onEarnPointsPress(false); - }); - - expect(mockRegisterReferral).not.toHaveBeenCalled(); - expect(mockNavigate).not.toHaveBeenCalledWith('Gratification'); - }); - - it('should not handle referral flow when isReferralConfirmed is undefined', async () => { - const { result } = renderHook(() => - useEarnPointsFlow({ - hasReferrer: true, - isReferralConfirmed: undefined, - }), - ); - - await act(async () => { - await result.current.onEarnPointsPress(false); - }); - - expect(mockRegisterReferral).not.toHaveBeenCalled(); - expect(mockNavigate).not.toHaveBeenCalledWith('Gratification'); - }); - - it('should not handle referral flow when hasReferrer is false', async () => { - useUserStore.getState().clearDeepLinkReferrer(); - - const { result } = renderHook(() => - useEarnPointsFlow({ - hasReferrer: false, - isReferralConfirmed: true, - }), - ); - - await act(async () => { - await result.current.onEarnPointsPress(false); - }); - - expect(mockRegisterReferral).not.toHaveBeenCalled(); - expect(mockNavigate).not.toHaveBeenCalledWith('Gratification'); - }); - - it('should handle referral flow when referrer is not in store but hasReferrer is true', async () => { - useUserStore.getState().clearDeepLinkReferrer(); - mockRegisterReferral.mockResolvedValue({ success: true }); - - const { result } = renderHook(() => - useEarnPointsFlow({ - hasReferrer: true, - isReferralConfirmed: true, - }), - ); - - await act(async () => { - await result.current.onEarnPointsPress(false); - }); - - // Should not call registerReferral if referrer is not in store - expect(mockRegisterReferral).not.toHaveBeenCalled(); - expect(mockNavigate).not.toHaveBeenCalledWith('Gratification'); - }); - }); - - describe('Edge cases', () => { - it('should handle errors in hasUserAnIdentityDocumentRegistered gracefully', async () => { - // Mock to return false on error (as the actual function catches errors and returns false) - mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(false); - - const { result } = renderHook(() => - useEarnPointsFlow({ - hasReferrer: false, - isReferralConfirmed: undefined, - }), - ); - - await act(async () => { - await result.current.onEarnPointsPress(); - }); - - // The function catches errors and returns false, so it should show identity verification modal - expect(mockNavigate).toHaveBeenCalledWith( - 'Modal', - expect.objectContaining({ - titleText: 'Identity Verification Required', - callbackId: expect.any(Number), - }), - ); - }); - - it('should handle errors in hasUserDoneThePointsDisclosure gracefully', async () => { - mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(true); - // Mock to return false on error (as the actual function catches errors and returns false) - mockHasUserDoneThePointsDisclosure.mockResolvedValue(false); - - const { result } = renderHook(() => - useEarnPointsFlow({ - hasReferrer: false, - isReferralConfirmed: undefined, - }), - ); - - await act(async () => { - await result.current.onEarnPointsPress(); - }); - - expect(mockNavigate).toHaveBeenCalledWith('PointsInfo', { - showNextButton: true, - callbackId: expect.any(Number), - }); - - const pointsInfoCallbackId = mockNavigate.mock.calls[0][1].callbackId; - const pointsInfoCallbacks = getModalCallbacks(pointsInfoCallbackId); - - await act(async () => { - await pointsInfoCallbacks!.onButtonPress(); - }); - - // The function catches errors and returns false, so it should show points disclosure modal - expect(mockNavigate).toHaveBeenCalledWith( - 'Modal', - expect.objectContaining({ - titleText: 'Points Disclosure Required', - callbackId: expect.any(Number), - }), - ); - }); - - it('should call pointsSelfApp when navigating to points proof', async () => { - mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(true); - mockHasUserDoneThePointsDisclosure.mockResolvedValue(false); - mockPointsSelfApp.mockResolvedValue(mockSelfApp as any); - - const { result } = renderHook(() => - useEarnPointsFlow({ - hasReferrer: false, - isReferralConfirmed: undefined, - }), - ); - - await act(async () => { - await result.current.onEarnPointsPress(); - }); - - expect(mockNavigate).toHaveBeenCalledWith('PointsInfo', { - showNextButton: true, - callbackId: expect.any(Number), - }); - - const pointsInfoCallbackId = mockNavigate.mock.calls[0][1].callbackId; - const pointsInfoCallbacks = getModalCallbacks(pointsInfoCallbackId); - - await act(async () => { - await pointsInfoCallbacks!.onButtonPress(); - }); - - const callbackId = mockNavigate.mock.calls[1][1].callbackId; - const callbacks = getModalCallbacks(callbackId); - - await act(async () => { - await callbacks!.onButtonPress(); - }); - - // Verify pointsSelfApp was called - expect(mockPointsSelfApp).toHaveBeenCalled(); - - // setSelfApp should be called when pointsSelfApp succeeds - expect(mockSetSelfApp).toHaveBeenCalledWith(mockSelfApp); - }); - }); - - describe('Callback dependencies', () => { - it('should update callbacks when dependencies change', async () => { - mockHasUserAnIdentityDocumentRegistered.mockResolvedValue(true); - mockHasUserDoneThePointsDisclosure.mockResolvedValue(true); - - const referrer = '0x1234567890123456789012345678901234567890'; - useUserStore.getState().setDeepLinkReferrer(referrer); - mockRegisterReferral.mockResolvedValue({ success: true }); - - const { result, rerender } = renderHook( - ({ hasReferrer, isReferralConfirmed }) => - useEarnPointsFlow({ hasReferrer, isReferralConfirmed }), - { - initialProps: { - hasReferrer: false, - isReferralConfirmed: undefined, - }, - }, - ); - - await act(async () => { - await result.current.onEarnPointsPress(); - }); - - expect(mockNavigate).toHaveBeenCalledWith('Points'); - - mockNavigate.mockClear(); - - rerender({ - hasReferrer: true, - isReferralConfirmed: true, - }); - - await act(async () => { - await result.current.onEarnPointsPress(false); - }); - - expect(mockRegisterReferral).toHaveBeenCalledWith(referrer); - }); - }); -}); diff --git a/app/tests/src/hooks/useOpenSupportForm.test.ts b/app/tests/src/hooks/useOpenSupportForm.test.ts new file mode 100644 index 000000000..c2e33e484 --- /dev/null +++ b/app/tests/src/hooks/useOpenSupportForm.test.ts @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import { act, renderHook } from '@testing-library/react-native'; + +import { supportFormUrl } from '@/consts/links'; +import useOpenSupportForm from '@/hooks/useOpenSupportForm'; +import { impactLight } from '@/integrations/haptics'; +import { navigationRef } from '@/navigation'; + +jest.mock('@/integrations/haptics', () => ({ + impactLight: jest.fn(), +})); + +jest.mock('@/navigation', () => ({ + navigationRef: { + isReady: jest.fn(), + navigate: jest.fn(), + }, +})); + +describe('useOpenSupportForm', () => { + beforeEach(() => { + jest.clearAllMocks(); + (navigationRef.isReady as jest.Mock).mockReturnValue(true); + }); + + it('triggers haptic feedback and navigates to the support form WebView', () => { + const { result } = renderHook(() => useOpenSupportForm()); + + act(() => { + result.current(); + }); + + expect(impactLight).toHaveBeenCalledTimes(1); + expect(navigationRef.navigate).toHaveBeenCalledWith('WebView', { + url: supportFormUrl, + title: 'Get Support', + }); + }); +}); diff --git a/app/tests/src/navigation.test.tsx b/app/tests/src/navigation.test.tsx index c87132c13..e50d5d8c9 100644 --- a/app/tests/src/navigation.test.tsx +++ b/app/tests/src/navigation.test.tsx @@ -76,7 +76,6 @@ describe('navigation', () => { 'DocumentNFCTrouble', 'DocumentOnboarding', 'DocumentSelectorForProving', - 'Gratification', 'Home', 'IDPicker', 'IdDetails', @@ -89,8 +88,6 @@ describe('navigation', () => { 'ManageDocuments', 'MockDataDeepLink', 'Modal', - 'Points', - 'PointsInfo', 'ProofHistory', 'ProofHistoryDetail', 'ProofRequestStatus', diff --git a/app/tests/src/screens/GratificationScreen.test.tsx b/app/tests/src/screens/GratificationScreen.test.tsx deleted file mode 100644 index 41ab91d39..000000000 --- a/app/tests/src/screens/GratificationScreen.test.tsx +++ /dev/null @@ -1,160 +0,0 @@ -// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. -// SPDX-License-Identifier: BUSL-1.1 -// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. - -import { useNavigation, useRoute } from '@react-navigation/native'; -import { render, waitFor } from '@testing-library/react-native'; - -import GratificationScreen from '@/screens/app/GratificationScreen'; - -jest.mock('react-native', () => { - const MockView = ({ children, ...props }: any) => ( - {children} - ); - const MockText = ({ children, ...props }: any) => ( - {children} - ); - const mockDimensions = { - get: jest.fn(() => ({ width: 320, height: 640 })), - addEventListener: jest.fn(), - removeEventListener: jest.fn(), - }; - - return { - __esModule: true, - Dimensions: mockDimensions, - Platform: { OS: 'ios', select: jest.fn() }, - Pressable: ({ onPress, children }: any) => ( - - ), - StyleSheet: { - create: (styles: any) => styles, - flatten: (style: any) => style, - }, - Text: MockText, - View: MockView, - }; -}); - -jest.mock('react-native-edge-to-edge', () => ({ - SystemBars: () => null, -})); - -jest.mock('react-native-safe-area-context', () => ({ - useSafeAreaInsets: jest.fn(() => ({ top: 0, bottom: 0 })), -})); - -jest.mock('@react-navigation/native', () => ({ - useNavigation: jest.fn(), - useRoute: jest.fn(), -})); - -// Mock Tamagui components to avoid theme provider requirement -jest.mock('tamagui', () => { - const View: any = 'View'; - const Text: any = 'Text'; - const createViewComponent = (displayName: string) => { - const MockComponent = ({ children, ...props }: any) => ( - - {children} - - ); - MockComponent.displayName = displayName; - return MockComponent; - }; - - const MockYStack = createViewComponent('YStack'); - const MockView = createViewComponent('View'); - - const MockText = ({ children, ...props }: any) => ( - {children} - ); - MockText.displayName = 'Text'; - - return { - __esModule: true, - YStack: MockYStack, - View: MockView, - Text: MockText, - }; -}); - -jest.mock('@selfxyz/mobile-sdk-alpha', () => ({ - DelayedLottieView: ({ onAnimationFinish }: any) => { - // Simulate animation finishing immediately - setTimeout(() => { - onAnimationFinish?.(); - }, 0); - return null; - }, -})); - -jest.mock('@selfxyz/mobile-sdk-alpha/components', () => ({ - PrimaryButton: ({ children, onPress }: any) => ( - - ), -})); - -jest.mock('@/assets/icons/arrow_left.svg', () => 'ArrowLeft'); -jest.mock('@/assets/logos/self.svg', () => 'SelfLogo'); - -const mockUseNavigation = useNavigation as jest.MockedFunction< - typeof useNavigation ->; -const mockUseRoute = useRoute as jest.MockedFunction; - -describe('GratificationScreen', () => { - const mockNavigate = jest.fn(); - const mockGoBack = jest.fn(); - - beforeEach(() => { - jest.clearAllMocks(); - - mockUseNavigation.mockReturnValue({ - navigate: mockNavigate, - goBack: mockGoBack, - } as any); - - mockUseRoute.mockReturnValue({ - params: {}, - } as any); - }); - - it('should use default points value when not provided', async () => { - mockUseRoute.mockReturnValue({ - params: {}, - } as any); - - const { getByText } = render(); - - await waitFor(() => { - expect(getByText('0')).toBeTruthy(); - }); - }); - - it('should use custom points value when provided', async () => { - mockUseRoute.mockReturnValue({ - params: { points: 50 }, - } as any); - - const { getByText } = render(); - - await waitFor(() => { - expect(getByText('50')).toBeTruthy(); - }); - }); - - it('should display referral points value (24) when passed', async () => { - mockUseRoute.mockReturnValue({ - params: { points: 24 }, - } as any); - - const { getByText } = render(); - - await waitFor(() => { - expect(getByText('24')).toBeTruthy(); - }); - }); -}); diff --git a/app/tests/src/screens/home/PointsInfoScreen.test.tsx b/app/tests/src/screens/home/PointsInfoScreen.test.tsx deleted file mode 100644 index fba2451bf..000000000 --- a/app/tests/src/screens/home/PointsInfoScreen.test.tsx +++ /dev/null @@ -1,339 +0,0 @@ -// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. -// SPDX-License-Identifier: BUSL-1.1 -// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. - -import React from 'react'; -import { act, render } from '@testing-library/react-native'; - -import PointsInfoScreen from '@/screens/home/PointsInfoScreen'; -import { unregisterModalCallbacks } from '@/utils/modalCallbackRegistry'; - -jest.mock('react-native', () => { - const MockView = ({ children, ...props }: any) => ( - {children} - ); - const MockText = ({ children, ...props }: any) => ( - {children} - ); - const MockImage = ({ ...props }: any) => ; - - return { - __esModule: true, - Image: MockImage, - Platform: { OS: 'ios', select: jest.fn() }, - StyleSheet: { - create: (styles: any) => styles, - flatten: (style: any) => style, - }, - Text: MockText, - View: MockView, - }; -}); - -jest.mock('react-native-safe-area-context', () => ({ - useSafeAreaInsets: jest.fn(() => ({ - top: 0, - bottom: 0, - left: 0, - right: 0, - })), -})); - -// Mock Tamagui components -jest.mock('tamagui', () => { - const View: any = 'View'; - const Text: any = 'Text'; - const createViewComponent = (displayName: string) => { - const MockComponent = ({ children, ...props }: any) => ( - - {children} - - ); - MockComponent.displayName = displayName; - return MockComponent; - }; - - const MockYStack = createViewComponent('YStack'); - const MockXStack = createViewComponent('XStack'); - const MockView = createViewComponent('View'); - const MockScrollView = createViewComponent('ScrollView'); - - const MockText = ({ children, ...props }: any) => ( - {children} - ); - MockText.displayName = 'Text'; - - return { - __esModule: true, - YStack: MockYStack, - XStack: MockXStack, - View: MockView, - Text: MockText, - ScrollView: MockScrollView, - }; -}); - -// Mock mobile SDK components -jest.mock('@selfxyz/mobile-sdk-alpha/components', () => ({ - PrimaryButton: ({ children, onPress, ...props }: any) => ( - - {children} - - ), - Title: ({ children }: any) =>
{children}
, -})); - -// Mock SVG icons -jest.mock('@/assets/icons/checkmark_square.svg', () => 'CheckmarkSquareIcon'); -jest.mock('@/assets/icons/cloud_backup.svg', () => 'CloudBackupIcon'); -jest.mock( - '@/assets/icons/push_notifications.svg', - () => 'PushNotificationsIcon', -); -jest.mock('@/assets/icons/star.svg', () => 'StarIcon'); - -// Mock images -jest.mock('@/assets/images/referral.png', () => 'ReferralImage'); - -jest.mock('@/utils/modalCallbackRegistry', () => ({ - getModalCallbacks: jest.fn(), - registerModalCallbacks: jest.fn(), - unregisterModalCallbacks: jest.fn(), -})); - -const mockUnregisterModalCallbacks = - unregisterModalCallbacks as jest.MockedFunction< - typeof unregisterModalCallbacks - >; - -// Mock getModalCallbacks at module level -const { getModalCallbacks } = jest.requireMock('@/utils/modalCallbackRegistry'); - -describe('PointsInfoScreen', () => { - const mockOnButtonPress = jest.fn(); - const mockOnModalDismiss = jest.fn(); - - beforeEach(() => { - jest.clearAllMocks(); - - // Setup getModalCallbacks to return our mock callbacks - getModalCallbacks.mockImplementation((id: number) => { - if (id === 1) { - return { - onButtonPress: mockOnButtonPress, - onModalDismiss: mockOnModalDismiss, - }; - } - return undefined; - }); - }); - - it('should render without crashing', () => { - expect(() => { - render(); - }).not.toThrow(); - }); - - it('should not show Next button when showNextButton is false', () => { - const { queryByTestId } = render( - , - ); - - // Verify button is not rendered - const nextButton = queryByTestId('primary-button'); - expect(nextButton).toBeNull(); - }); - - it('should show Next button when showNextButton is true', () => { - const { getByTestId } = render( - , - ); - - // Verify button is rendered - const nextButton = getByTestId('primary-button'); - expect(nextButton).toBeTruthy(); - }); - - describe('Callback handling', () => { - it('should call onModalDismiss and unregister callbacks when component unmounts without button press', () => { - const { unmount } = render( - , - ); - - // Initially, no callbacks should be called - expect(mockOnModalDismiss).not.toHaveBeenCalled(); - expect(mockUnregisterModalCallbacks).not.toHaveBeenCalled(); - expect(mockOnButtonPress).not.toHaveBeenCalled(); - - // Unmount the component (simulating user navigating back) - act(() => { - unmount(); - }); - - // onModalDismiss should be called to clear referrer - expect(mockOnModalDismiss).toHaveBeenCalledTimes(1); - // Callbacks should be unregistered to prevent memory leak - expect(mockUnregisterModalCallbacks).toHaveBeenCalledWith(1); - // onButtonPress should not be called (user didn't press the button) - expect(mockOnButtonPress).not.toHaveBeenCalled(); - }); - - it('should call onModalDismiss on unmount even when showNextButton is false', () => { - const { unmount } = render( - , - ); - - act(() => { - unmount(); - }); - - // Callbacks should be called even if button is not shown (callbackId is present) - expect(mockOnModalDismiss).toHaveBeenCalledTimes(1); - expect(mockUnregisterModalCallbacks).toHaveBeenCalledWith(1); - }); - - it('should handle missing callbacks gracefully', () => { - // Mock getModalCallbacks to return undefined - getModalCallbacks.mockReturnValue(undefined); - - const { unmount } = render( - , - ); - - // Should not throw when unmounting with missing callbacks - expect(() => { - act(() => { - unmount(); - }); - }).not.toThrow(); - - // Should still attempt to unregister - expect(mockUnregisterModalCallbacks).toHaveBeenCalledWith(999); - }); - - it('should handle missing callbackId gracefully', () => { - const { unmount } = render( - , - ); - - // Should not throw when unmounting without callbackId - expect(() => { - act(() => { - unmount(); - }); - }).not.toThrow(); - - // Should not attempt to unregister if no callbackId - expect(mockUnregisterModalCallbacks).not.toHaveBeenCalled(); - }); - }); - - describe('Button press handling', () => { - it('should call onButtonPress and unregister callbacks when Next button is pressed, then not call onModalDismiss on unmount', () => { - const { getByTestId, unmount } = render( - , - ); - - const primaryButton = getByTestId('primary-button'); - - // Press the button - act(() => { - primaryButton.props.onPress(); - }); - - // onButtonPress should be called - expect(mockOnButtonPress).toHaveBeenCalledTimes(1); - // Callbacks should NOT be unregistered yet (component still mounted) - expect(mockUnregisterModalCallbacks).not.toHaveBeenCalled(); - // onModalDismiss should NOT be called (button was pressed) - expect(mockOnModalDismiss).not.toHaveBeenCalled(); - - // Clear mock calls from button press - jest.clearAllMocks(); - - // Unmount the component - act(() => { - unmount(); - }); - - // onModalDismiss should NOT be called (button was pressed, not navigated back) - expect(mockOnModalDismiss).not.toHaveBeenCalled(); - // Callbacks should be unregistered to prevent memory leak - expect(mockUnregisterModalCallbacks).toHaveBeenCalledWith(1); - }); - - it('should allow multiple button presses without unregistering callbacks (regression test)', () => { - const { getByTestId } = render( - , - ); - - const primaryButton = getByTestId('primary-button'); - - // Press the button first time - act(() => { - primaryButton.props.onPress(); - }); - - expect(mockOnButtonPress).toHaveBeenCalledTimes(1); - expect(mockUnregisterModalCallbacks).not.toHaveBeenCalled(); - - // Press the button again (simulating returning to this screen after modal dismissal) - act(() => { - primaryButton.props.onPress(); - }); - - // onButtonPress should be called again - expect(mockOnButtonPress).toHaveBeenCalledTimes(2); - // Callbacks should still NOT be unregistered (component still mounted) - expect(mockUnregisterModalCallbacks).not.toHaveBeenCalled(); - }); - }); - - describe('Referrer cleanup integration', () => { - it('should ensure cleanup is called in correct order for referrer clearing', () => { - const callOrder: string[] = []; - - const onModalDismissWithTracking = jest.fn(() => { - callOrder.push('onModalDismiss'); - }); - - const unregisterWithTracking = jest.fn(() => { - callOrder.push('unregister'); - }); - - getModalCallbacks.mockReturnValue({ - onButtonPress: mockOnButtonPress, - onModalDismiss: onModalDismissWithTracking, - }); - - mockUnregisterModalCallbacks.mockImplementation(unregisterWithTracking); - - const { unmount } = render( - , - ); - - act(() => { - unmount(); - }); - - // Verify onModalDismiss is called before unregister - expect(callOrder).toEqual(['onModalDismiss', 'unregister']); - }); - }); -}); diff --git a/app/tests/src/screens/verification/ProofRequestStatusScreen.test.tsx b/app/tests/src/screens/verification/ProofRequestStatusScreen.test.tsx new file mode 100644 index 000000000..529950fbf --- /dev/null +++ b/app/tests/src/screens/verification/ProofRequestStatusScreen.test.tsx @@ -0,0 +1,284 @@ +// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import React from 'react'; +import { useIsFocused } from '@react-navigation/native'; +import { + act, + fireEvent, + render, + screen, + waitFor, +} from '@testing-library/react-native'; + +import ProofRequestStatusScreen from '@/screens/verification/ProofRequestStatusScreen'; +import { ProofStatus } from '@/stores/proofTypes'; + +declare global { + namespace JSX { + interface IntrinsicElements { + 'mock-view': any; + 'mock-text': any; + 'mock-button': any; + 'mock-spinner': any; + 'mock-lottie': any; + 'mock-layout': any; + 'mock-top': any; + 'mock-bottom': any; + 'mock-scroll': any; + } + } +} + +jest.mock('react-native', () => ({ + __esModule: true, + Linking: { + openURL: jest.fn(), + }, + StyleSheet: { + create: (styles: unknown) => styles, + flatten: (style: unknown) => style, + }, + View: ({ children, ...props }: any) => ( + {children} + ), +})); + +jest.mock('@react-navigation/native', () => ({ + useIsFocused: jest.fn(), +})); + +jest.mock('tamagui', () => ({ + __esModule: true, + ScrollView: ({ children, ...props }: any) => ( + {children} + ), + Spinner: (props: any) => , +})); + +jest.mock('@selfxyz/mobile-sdk-alpha/constants/colors', () => ({ + black: '#000000', + white: '#ffffff', +})); + +jest.mock('@selfxyz/mobile-sdk-alpha/constants/analytics', () => ({ + ProofEvents: { + PROOF_COMPLETED: 'PROOF_COMPLETED', + PROOF_FAILED: 'PROOF_FAILED', + PROOF_RESULT_ACKNOWLEDGED: 'PROOF_RESULT_ACKNOWLEDGED', + }, +})); + +jest.mock('@selfxyz/mobile-sdk-alpha/animations/loading/misc.json', () => ({})); +jest.mock('@/assets/animations/proof_failed.json', () => ({})); +jest.mock('@/assets/animations/proof_success.json', () => ({})); + +jest.mock('@selfxyz/mobile-sdk-alpha/components', () => ({ + BodyText: ({ children, ...props }: any) => ( + {children} + ), + Description: ({ children, ...props }: any) => ( + {children} + ), + PrimaryButton: ({ children, onPress, disabled, ...props }: any) => ( + + {children} + + ), + Title: ({ children, ...props }: any) => ( + {children} + ), + typography: { + strong: {}, + }, +})); + +jest.mock('@selfxyz/mobile-sdk-alpha', () => ({ + DelayedLottieView: (props: any) => , + useSelfClient: jest.fn(), +})); + +jest.mock('@/hooks/useHapticNavigation', () => jest.fn()); + +jest.mock('@/integrations/haptics', () => ({ + buttonTap: jest.fn(), + notificationError: jest.fn(), + notificationSuccess: jest.fn(), +})); + +jest.mock('@/layouts/ExpandableBottomLayout', () => ({ + ExpandableBottomLayout: { + Layout: ({ children, ...props }: any) => ( + {children} + ), + TopSection: ({ children, ...props }: any) => ( + {children} + ), + BottomSection: ({ children, ...props }: any) => ( + {children} + ), + }, +})); + +jest.mock('@/stores/proofHistoryStore', () => ({ + useProofHistoryStore: jest.fn(), +})); + +const { Linking } = jest.requireMock('react-native') as { + Linking: { + openURL: jest.Mock; + }; +}; +const { useSelfClient } = jest.requireMock('@selfxyz/mobile-sdk-alpha') as { + useSelfClient: jest.Mock; +}; +const useHapticNavigation = jest.requireMock( + '@/hooks/useHapticNavigation', +) as jest.Mock; +const { buttonTap, notificationSuccess } = jest.requireMock( + '@/integrations/haptics', +) as { + buttonTap: jest.Mock; + notificationSuccess: jest.Mock; +}; +const { useProofHistoryStore } = jest.requireMock( + '@/stores/proofHistoryStore', +) as { + useProofHistoryStore: jest.Mock; +}; + +describe('ProofRequestStatusScreen', () => { + const mockGoHome = jest.fn(); + const mockTrackEvent = jest.fn(); + const mockCleanSelfApp = jest.fn(); + const mockUpdateProofStatus = jest.fn(); + + let provingState: { + currentState: string; + reason: string | null; + uuid: string; + error_code: string | null; + }; + let selfAppState: { + selfApp: { + appName: string; + deeplinkCallback: string | null; + }; + }; + + beforeEach(() => { + jest.clearAllMocks(); + jest.useFakeTimers(); + + provingState = { + currentState: 'completed', + reason: null, + uuid: 'session-1', + error_code: null, + }; + selfAppState = { + selfApp: { + appName: 'Verifier', + deeplinkCallback: null, + }, + }; + + (useIsFocused as jest.Mock).mockReturnValue(true); + useHapticNavigation.mockReturnValue(mockGoHome); + useProofHistoryStore.mockReturnValue({ + updateProofStatus: mockUpdateProofStatus, + }); + + const useProvingStore = Object.assign( + (selector: (state: typeof provingState) => unknown) => + selector(provingState), + { + getState: () => provingState, + }, + ); + const useSelfAppStore = ( + selector: (state: typeof selfAppState) => unknown, + ) => selector(selfAppState); + + useSelfClient.mockReturnValue({ + trackEvent: mockTrackEvent, + getSelfAppState: () => ({ cleanSelfApp: mockCleanSelfApp }), + useProvingStore, + useSelfAppStore, + }); + }); + + afterEach(() => { + act(() => { + jest.runOnlyPendingTimers(); + }); + jest.useRealTimers(); + }); + + it('goes home and clears the completed session after acknowledgement', async () => { + render(); + + await waitFor(() => { + expect(mockUpdateProofStatus).toHaveBeenCalledWith( + 'session-1', + ProofStatus.SUCCESS, + ); + }); + + fireEvent.press(screen.getByTestId('primary-button')); + + expect(buttonTap).toHaveBeenCalledTimes(1); + expect(mockGoHome).toHaveBeenCalledTimes(1); + + act(() => { + jest.advanceTimersByTime(2000); + }); + + expect(mockCleanSelfApp).toHaveBeenCalledTimes(1); + expect(notificationSuccess).toHaveBeenCalledTimes(1); + expect(mockTrackEvent).toHaveBeenCalledWith('PROOF_COMPLETED', { + sessionId: 'session-1', + appName: 'Verifier', + }); + }); + + it('does not clear self app state if a newer session replaces the completed one', async () => { + render(); + + fireEvent.press(screen.getByTestId('primary-button')); + provingState.uuid = 'session-2'; + + act(() => { + jest.advanceTimersByTime(2000); + }); + + expect(mockCleanSelfApp).not.toHaveBeenCalled(); + }); + + it('cancels deeplink redirect before it opens the external URL', async () => { + selfAppState.selfApp.deeplinkCallback = + 'https://callback.self.xyz/complete'; + + render(); + + await waitFor(() => { + expect(screen.getByTestId('primary-button').props.children).toBe( + 'Cancel', + ); + }); + + fireEvent.press(screen.getByTestId('primary-button')); + act(() => { + jest.advanceTimersByTime(6000); + }); + + expect(Linking.openURL).not.toHaveBeenCalled(); + expect(mockGoHome).not.toHaveBeenCalled(); + }); +}); From c3a805c6c891286b856dbad030ef0b627dd00638 Mon Sep 17 00:00:00 2001 From: Justin Hernandez Date: Thu, 9 Apr 2026 04:01:40 -0700 Subject: [PATCH 5/6] Rename KYC_TEE_URL and fix nav header regressions (#1948) * rename kyc tee env var * fix header text and close button * format --- .github/workflows/mobile-deploy.yml | 4 ++-- app/env.sample | 2 +- app/env.ts | 7 +++---- app/src/components/navbar/DefaultNavBar.tsx | 12 +++++++++--- app/src/hooks/useKycWebSocket.ts | 6 +++--- app/src/integrations/kyc/kycService.ts | 4 ++-- app/src/navigation/account.ts | 5 +++++ app/src/navigation/documents.ts | 2 ++ app/src/navigation/home.ts | 8 ++++++-- packages/webview-app/src/utils/kycAttestation.ts | 4 ++-- packages/webview-app/src/utils/kycProvider.ts | 4 ++-- 11 files changed, 37 insertions(+), 21 deletions(-) diff --git a/.github/workflows/mobile-deploy.yml b/.github/workflows/mobile-deploy.yml index e17f4c2cf..6b17ae02b 100644 --- a/.github/workflows/mobile-deploy.yml +++ b/.github/workflows/mobile-deploy.yml @@ -715,7 +715,7 @@ jobs: SEGMENT_KEY: ${{ secrets.SEGMENT_KEY }} SELFXYZ_APP_TOKEN: ${{ steps.github-token.outputs.token }} SENTRY_DSN: ${{ secrets.SENTRY_DSN }} - DIDIT_TEE_URL: ${{ secrets.DIDIT_TEE_URL }} + KYC_TEE_URL: ${{ secrets.KYC_TEE_URL }} TURNKEY_AUTH_PROXY_CONFIG_ID: ${{ secrets.TURNKEY_AUTH_PROXY_CONFIG_ID }} TURNKEY_GOOGLE_CLIENT_ID: ${{ secrets.TURNKEY_GOOGLE_CLIENT_ID }} TURNKEY_ORGANIZATION_ID: ${{ secrets.TURNKEY_ORGANIZATION_ID }} @@ -1176,7 +1176,7 @@ jobs: NODE_OPTIONS: "--max-old-space-size=6144" SEGMENT_KEY: ${{ secrets.SEGMENT_KEY }} SENTRY_DSN: ${{ secrets.SENTRY_DSN }} - DIDIT_TEE_URL: ${{ secrets.DIDIT_TEE_URL }} + KYC_TEE_URL: ${{ secrets.KYC_TEE_URL }} TURNKEY_AUTH_PROXY_CONFIG_ID: ${{ secrets.TURNKEY_AUTH_PROXY_CONFIG_ID }} TURNKEY_GOOGLE_CLIENT_ID: ${{ secrets.TURNKEY_GOOGLE_CLIENT_ID }} TURNKEY_ORGANIZATION_ID: ${{ secrets.TURNKEY_ORGANIZATION_ID }} diff --git a/app/env.sample b/app/env.sample index 794afd503..539a9e211 100644 --- a/app/env.sample +++ b/app/env.sample @@ -10,4 +10,4 @@ IS_TEST_BUILD= MIXPANEL_NFC_PROJECT_TOKEN= SEGMENT_KEY= SENTRY_DSN= -DIDIT_TEE_URL= +KYC_TEE_URL= diff --git a/app/env.ts b/app/env.ts index 48aeff52f..175f9922a 100644 --- a/app/env.ts +++ b/app/env.ts @@ -8,9 +8,6 @@ export const DEFAULT_DOE = undefined; export const DEFAULT_PNUMBER = undefined; -export const DIDIT_TEE_URL = - process.env.DIDIT_TEE_URL || 'http://localhost:8080'; - export const ENABLE_DEBUG_LOGS = process.env.ENABLE_DEBUG_LOGS === 'true'; export const GOOGLE_SIGNIN_ANDROID_CLIENT_ID = @@ -21,15 +18,17 @@ export const GOOGLE_SIGNIN_IOS_CLIENT_ID = export const GOOGLE_SIGNIN_WEB_CLIENT_ID = process.env.GOOGLE_SIGNIN_WEB_CLIENT_ID; + export const GRAFANA_LOKI_PASSWORD = process.env.GRAFANA_LOKI_PASSWORD; export const GRAFANA_LOKI_URL = process.env.GRAFANA_LOKI_URL; - export const GRAFANA_LOKI_USERNAME = process.env.GRAFANA_LOKI_USERNAME; /* This file provides compatiblity between how web expects env variables to be and how native does. * on web it is aliased to @env on native it is not used */ export const IS_TEST_BUILD = process.env.IS_TEST_BUILD === 'true'; + +export const KYC_TEE_URL = process.env.KYC_TEE_URL || 'http://localhost:8080'; export const MIXPANEL_NFC_PROJECT_TOKEN = undefined; export const SEGMENT_KEY = process.env.SEGMENT_KEY; export const SENTRY_DSN = process.env.SENTRY_DSN; diff --git a/app/src/components/navbar/DefaultNavBar.tsx b/app/src/components/navbar/DefaultNavBar.tsx index fdb7f89fe..b0fc0482c 100644 --- a/app/src/components/navbar/DefaultNavBar.tsx +++ b/app/src/components/navbar/DefaultNavBar.tsx @@ -47,9 +47,15 @@ export const DefaultNavBar = (props: NativeStackHeaderProps) => { {props.options.title} diff --git a/app/src/hooks/useKycWebSocket.ts b/app/src/hooks/useKycWebSocket.ts index e62777b78..808a0026d 100644 --- a/app/src/hooks/useKycWebSocket.ts +++ b/app/src/hooks/useKycWebSocket.ts @@ -4,7 +4,7 @@ import { useCallback, useRef } from 'react'; import { io, type Socket } from 'socket.io-client'; -import { DIDIT_TEE_URL } from '@env'; +import { KYC_TEE_URL } from '@env'; import { deserializeApplicantInfo } from '@selfxyz/common'; import type { DocumentType, KycData } from '@selfxyz/common/utils/types'; @@ -81,8 +81,8 @@ export function useKycWebSocket(options: UseKycWebSocketOptions = {}) { } subscribedSessionIdsRef.current.add(sessionId); - console.log('[KycWebSocket] Connecting to WebSocket:', DIDIT_TEE_URL); - const socket = io(DIDIT_TEE_URL, { + console.log('[KycWebSocket] Connecting to WebSocket:', KYC_TEE_URL); + const socket = io(KYC_TEE_URL, { transports: ['websocket', 'polling'], }); diff --git a/app/src/integrations/kyc/kycService.ts b/app/src/integrations/kyc/kycService.ts index 0e763aa55..954452a00 100644 --- a/app/src/integrations/kyc/kycService.ts +++ b/app/src/integrations/kyc/kycService.ts @@ -3,7 +3,7 @@ // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. import { startVerification } from '@didit-protocol/sdk-react-native'; -import { DIDIT_TEE_URL } from '@env'; +import { KYC_TEE_URL } from '@env'; import type { KycVerificationResult, @@ -18,7 +18,7 @@ export interface KycLaunchConfig { const FETCH_TIMEOUT_MS = 30000; export const createKycSession = async (): Promise => { - const apiUrl = DIDIT_TEE_URL; + const apiUrl = KYC_TEE_URL; console.log('[Didit] createSession URL:', apiUrl); const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS); diff --git a/app/src/navigation/account.ts b/app/src/navigation/account.ts index 6f4c9dc1d..151e168ef 100644 --- a/app/src/navigation/account.ts +++ b/app/src/navigation/account.ts @@ -58,6 +58,7 @@ const accountScreens = { screen: CloudBackupScreen, options: { title: 'Account Backup', + headerTintColor: black, headerStyle: { backgroundColor: white, }, @@ -70,6 +71,7 @@ const accountScreens = { screen: ProofSettingsScreen, options: { title: 'Proof Settings', + headerTintColor: black, headerStyle: { backgroundColor: white, }, @@ -83,6 +85,8 @@ const accountScreens = { options: { animation: 'slide_from_bottom', title: 'Settings', + headerBackTitle: 'close', + headerTintColor: black, headerStyle: { backgroundColor: white, }, @@ -106,6 +110,7 @@ const accountScreens = { } as NativeStackNavigationOptions) : ({ title: 'Recovery Phrase', + headerTintColor: black, headerStyle: { backgroundColor: white, }, diff --git a/app/src/navigation/documents.ts b/app/src/navigation/documents.ts index e28d353f4..142208400 100644 --- a/app/src/navigation/documents.ts +++ b/app/src/navigation/documents.ts @@ -126,6 +126,7 @@ const documentsScreens = { screen: ManageDocumentsScreen, options: { title: 'Manage Documents', + headerTintColor: black, headerStyle: { backgroundColor: white, }, @@ -138,6 +139,7 @@ const documentsScreens = { screen: DocumentDataInfoScreen, options: { title: 'Document Data Info', + headerTintColor: black, headerStyle: { backgroundColor: white, }, diff --git a/app/src/navigation/home.ts b/app/src/navigation/home.ts index 9e64d9f30..3675ae455 100644 --- a/app/src/navigation/home.ts +++ b/app/src/navigation/home.ts @@ -4,6 +4,8 @@ import type { NativeStackNavigationOptions } from '@react-navigation/native-stack'; +import { black } from '@selfxyz/mobile-sdk-alpha/constants/colors'; + import { HomeNavBar } from '@/components/navbar'; import ReferralScreen from '@/screens/app/ReferralScreen'; import HomeScreen from '@/screens/home/HomeScreen'; @@ -30,13 +32,15 @@ const homeScreens = { options: { title: 'Approved Requests', headerBackTitle: 'close', - }, + headerTintColor: black, + } as NativeStackNavigationOptions, }, ProofHistoryDetail: { screen: ProofHistoryDetailScreen, options: { title: 'Approval', - }, + headerTintColor: black, + } as NativeStackNavigationOptions, }, }; diff --git a/packages/webview-app/src/utils/kycAttestation.ts b/packages/webview-app/src/utils/kycAttestation.ts index d80eb7980..abe697070 100644 --- a/packages/webview-app/src/utils/kycAttestation.ts +++ b/packages/webview-app/src/utils/kycAttestation.ts @@ -6,7 +6,7 @@ import { io } from 'socket.io-client'; import type { KycProviderAttestation } from '../types/kycProvider'; -const DIDIT_TEE_URL = import.meta.env.VITE_DIDIT_TEE_URL ?? 'https://kyc.self.xyz'; +const KYC_TEE_URL = import.meta.env.VITE_KYC_TEE_URL ?? 'https://kyc.self.xyz'; const ATTESTATION_TIMEOUT_MS = 120_000; // 2 minutes @@ -24,7 +24,7 @@ export interface AttestationResult { */ export function waitForKycAttestation(sessionId: string, signal?: AbortSignal): Promise { return new Promise(resolve => { - const socket = io(DIDIT_TEE_URL, { + const socket = io(KYC_TEE_URL, { transports: ['websocket', 'polling'], }); diff --git a/packages/webview-app/src/utils/kycProvider.ts b/packages/webview-app/src/utils/kycProvider.ts index 7df77fca2..06e45e83f 100644 --- a/packages/webview-app/src/utils/kycProvider.ts +++ b/packages/webview-app/src/utils/kycProvider.ts @@ -6,7 +6,7 @@ import type { KycProviderResult } from '../types/kycProvider'; const FETCH_TIMEOUT_MS = 30_000; -const DIDIT_TEE_URL = import.meta.env.VITE_DIDIT_TEE_URL ?? 'https://kyc.self.xyz'; +const KYC_TEE_URL = import.meta.env.VITE_KYC_TEE_URL ?? 'https://kyc.self.xyz'; export interface KycLaunchConfig { url: string; @@ -40,7 +40,7 @@ export async function createKycSession(signal?: AbortSignal): Promise Date: Thu, 9 Apr 2026 12:32:03 -0700 Subject: [PATCH 6/6] SELF-2540: Fix account recovery flow after phone restore (#1942) * bug fix * fix pipelines * fix race condition * fixes * try again * fix maestro * update copy and abstract * fixes...i hope * fix ios pipeline? * pipeline fix * fix pipeline * fix e2e test * fix pipelines? * formatting --- .github/workflows/mobile-e2e.yml | 6 ++ app/src/navigation/deeplinks.ts | 48 ++++----- app/src/providers/authProvider.tsx | 9 +- .../recovery/AccountRecoveryChoiceScreen.tsx | 45 +++++---- .../recovery/AccountRecoveryScreen.tsx | 12 +-- .../recovery/DocumentDataNotFoundScreen.tsx | 7 +- .../recovery/RecoverWithPhraseScreen.tsx | 24 ++--- .../screens/account/recovery/recoveryCopy.ts | 41 ++++++++ app/src/screens/app/SplashScreen.tsx | 57 +++++++++-- app/src/screens/app/startupRouting.ts | 61 ++++++++++++ .../screens/onboarding/DisclaimerScreen.tsx | 84 ++++++++-------- app/src/stores/settingStore.ts | 20 ++++ app/tests/e2e/launch.android.flow.yaml | 8 +- app/tests/e2e/launch.ios.flow.yaml | 8 +- .../src/screens/app/startupRouting.test.ts | 97 +++++++++++++++++++ 15 files changed, 403 insertions(+), 124 deletions(-) create mode 100644 app/src/screens/account/recovery/recoveryCopy.ts create mode 100644 app/src/screens/app/startupRouting.ts create mode 100644 app/tests/src/screens/app/startupRouting.test.ts diff --git a/.github/workflows/mobile-e2e.yml b/.github/workflows/mobile-e2e.yml index a6b9640fd..05b17a4a4 100644 --- a/.github/workflows/mobile-e2e.yml +++ b/.github/workflows/mobile-e2e.yml @@ -824,6 +824,12 @@ jobs: SIMULATOR_ID="${IOS_SIMULATOR_ID:-iPhone SE (3rd generation)}" echo "Installing on simulator: $SIMULATOR_ID" + echo "Erasing simulator to ensure clean state..." + xcrun simctl shutdown "$SIMULATOR_ID" 2>/dev/null || true + xcrun simctl erase "$SIMULATOR_ID" + xcrun simctl boot "$SIMULATOR_ID" || true + xcrun simctl bootstatus "$SIMULATOR_ID" -b + echo "Removing any existing app installation..." xcrun simctl uninstall "$SIMULATOR_ID" "$IOS_BUNDLE_ID" 2>/dev/null || true diff --git a/app/src/navigation/deeplinks.ts b/app/src/navigation/deeplinks.ts index 108d12d03..67daa7b48 100644 --- a/app/src/navigation/deeplinks.ts +++ b/app/src/navigation/deeplinks.ts @@ -102,35 +102,12 @@ const createDeeplinkNavigationState = ( // Store the correct parent screen determined by splash screen let correctParentScreen: string = 'Home'; -// Function for splash screen to get and clear the queued initial URL export const getAndClearQueuedUrl = (): string | null => { const url = queuedInitialUrl; queuedInitialUrl = null; return url; }; -const safeNavigate = ( - navigationState: ReturnType, -): void => { - const targetScreen = navigationState.routes[1]?.name as - | keyof RootStackParamList - | undefined; - - const currentRoute = navigationRef.getCurrentRoute(); - const isColdLaunch = currentRoute?.name === 'Splash'; - - if (!isColdLaunch && targetScreen) { - // Use object syntax to satisfy TypeScript's strict typing for navigate - // The params will be undefined for screens that don't require them - navigationRef.navigate({ - name: targetScreen, - params: undefined, - } as Parameters[0]); - } else { - navigationRef.reset(navigationState); - } -}; - export const handleUrl = (selfClient: SelfClient, uri: string) => { const validatedParams = parseAndValidateUrlParams(uri); const { @@ -241,6 +218,28 @@ export const handleUrl = (selfClient: SelfClient, uri: string) => { } }; +const safeNavigate = ( + navigationState: ReturnType, +): void => { + const targetScreen = navigationState.routes[1]?.name as + | keyof RootStackParamList + | undefined; + + const currentRoute = navigationRef.getCurrentRoute(); + const isColdLaunch = currentRoute?.name === 'Splash'; + + if (!isColdLaunch && targetScreen) { + // Use object syntax to satisfy TypeScript's strict typing for navigate + // The params will be undefined for screens that don't require them + navigationRef.navigate({ + name: targetScreen, + params: undefined, + } as Parameters[0]); + } else { + navigationRef.reset(navigationState); + } +}; + /** * Parses and validates query parameters from a URL * @param uri - The URL to parse @@ -285,6 +284,9 @@ export const parseAndValidateUrlParams = (uri: string): ValidatedParams => { return validatedParams; }; +// Function for splash screen to get and clear the queued initial URL +export const peekQueuedUrl = (): string | null => queuedInitialUrl; + // Store the initial URL for splash screen to handle after initialization let queuedInitialUrl: string | null = null; diff --git a/app/src/providers/authProvider.tsx b/app/src/providers/authProvider.tsx index 64827fa2e..7f0faa769 100644 --- a/app/src/providers/authProvider.tsx +++ b/app/src/providers/authProvider.tsx @@ -419,8 +419,13 @@ export function getPrivateKeyFromMnemonic(mnemonic: string) { } export async function hasSecretStored() { - const seed = await Keychain.getGenericPassword({ service: SERVICE_NAME }); - return !!seed; + try { + const seed = await Keychain.getGenericPassword({ service: SERVICE_NAME }); + return !!seed; + } catch (error) { + console.warn('Error checking for stored secret:', error); + return false; + } } // Migrates existing mnemonic to use new security settings with accessControl. diff --git a/app/src/screens/account/recovery/AccountRecoveryChoiceScreen.tsx b/app/src/screens/account/recovery/AccountRecoveryChoiceScreen.tsx index fb83e318f..bef5e49fd 100644 --- a/app/src/screens/account/recovery/AccountRecoveryChoiceScreen.tsx +++ b/app/src/screens/account/recovery/AccountRecoveryChoiceScreen.tsx @@ -37,7 +37,8 @@ import { loadPassportData, reStorePassportDataWithRightCSCA, } from '@/providers/passportDataProvider'; -import { STORAGE_NAME, useBackupMnemonic } from '@/services/cloud-backup'; +import { recoveryCopy } from '@/screens/account/recovery/recoveryCopy'; +import { useBackupMnemonic } from '@/services/cloud-backup'; import { useSettingStore } from '@/stores/settingStore'; import type { Mnemonic } from '@/types/mnemonic'; @@ -82,25 +83,29 @@ const AccountRecoveryChoiceScreen: React.FC = () => { if (!result) { console.warn('Failed to restore account'); trackEvent(BackupEvents.CLOUD_RESTORE_FAILED_UNKNOWN); - navigation.navigate({ name: 'Home', params: {} }); setRestoring(false); return false; } const passportData = await loadPassportData(); - const secret = getPrivateKeyFromMnemonic(mnemonic.phrase); - if (!passportData || !secret) { - console.warn('Failed to load passport data or secret'); - trackEvent(BackupEvents.CLOUD_RESTORE_FAILED_AUTH, { - reason: 'no_passport_data_or_secret', + if (!passportData) { + console.warn( + 'Recovered secret but no local document data was found. Prompting the user to import their document again.', + ); + if (isCloudRestore && !cloudBackupEnabled) { + toggleCloudBackupEnabled(); + } + trackEvent(BackupEvents.CLOUD_RESTORE_SUCCESS, { + documentImportRequired: true, }); - navigation.navigate({ name: 'Home', params: {} }); + navigation.navigate('CountryPicker'); setRestoring(false); - return false; + return true; } const passportDataParsed = JSON.parse(passportData); + const secret = getPrivateKeyFromMnemonic(mnemonic.phrase); const { isRegistered, csca } = await isUserRegisteredWithAlternativeCSCA( @@ -140,7 +145,6 @@ const AccountRecoveryChoiceScreen: React.FC = () => { hasCSCA: !!csca, }, ); - navigation.navigate({ name: 'Home', params: {} }); setRestoring(false); return false; } @@ -241,16 +245,10 @@ const AccountRecoveryChoiceScreen: React.FC = () => { - Restore your Self account + {recoveryCopy.choice.title} - By continuing, you certify that this passport belongs to you and is - not stolen or forged.{' '} - {!biometricsAvailable && ( - <> - Your device doesn't support biometrics or is disabled for apps - and is required for cloud storage. - - )} + {recoveryCopy.choice.description}{' '} + {!biometricsAvailable && recoveryCopy.choice.noBiometrics} @@ -275,12 +273,11 @@ const AccountRecoveryChoiceScreen: React.FC = () => { testID="button-from-teststorage" disabled={restoringFromCloud || !biometricsAvailable} > - {restoringFromCloud ? 'Restoring' : 'Restore'} from {STORAGE_NAME} - {restoringFromCloud ? '…' : ''} + {recoveryCopy.choice.actions.cloud(restoringFromCloud)} - OR + {recoveryCopy.choice.actions.or} { - Enter recovery phrase + + {recoveryCopy.choice.actions.phrase} + diff --git a/app/src/screens/account/recovery/AccountRecoveryScreen.tsx b/app/src/screens/account/recovery/AccountRecoveryScreen.tsx index af7ef333a..c7b0cee5f 100644 --- a/app/src/screens/account/recovery/AccountRecoveryScreen.tsx +++ b/app/src/screens/account/recovery/AccountRecoveryScreen.tsx @@ -21,6 +21,7 @@ import { import RestoreAccountSvg from '@/assets/icons/restore_account.svg'; import useHapticNavigation from '@/hooks/useHapticNavigation'; import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout'; +import { recoveryCopy } from '@/screens/account/recovery/recoveryCopy'; const AccountRecoveryScreen: React.FC = () => { const onRestoreAccountPress = useHapticNavigation('AccountRecoveryChoice'); @@ -44,24 +45,21 @@ const AccountRecoveryScreen: React.FC = () => { - Restore your Self account - - By continuing, you certify that this passport belongs to you and is - not stolen or forged. - + {recoveryCopy.entry.title} + {recoveryCopy.entry.description} - Restore my account + {recoveryCopy.entry.actions.recover} - Create new account + {recoveryCopy.entry.actions.register} diff --git a/app/src/screens/account/recovery/DocumentDataNotFoundScreen.tsx b/app/src/screens/account/recovery/DocumentDataNotFoundScreen.tsx index 84baf5046..3291f3a66 100644 --- a/app/src/screens/account/recovery/DocumentDataNotFoundScreen.tsx +++ b/app/src/screens/account/recovery/DocumentDataNotFoundScreen.tsx @@ -46,7 +46,7 @@ const DocumentDataNotFoundScreen: React.FC = () => { - ✨ Are you new here? + No document found { color: slate200, }} > - It seems like you need to go through the registration flow first. + We couldn't find a registered document on this device. Register your + ID to continue. { height={150} backgroundColor={white} > - Go to Registration + Continue ); diff --git a/app/src/screens/account/recovery/RecoverWithPhraseScreen.tsx b/app/src/screens/account/recovery/RecoverWithPhraseScreen.tsx index a1a4e8f02..45760c2e1 100644 --- a/app/src/screens/account/recovery/RecoverWithPhraseScreen.tsx +++ b/app/src/screens/account/recovery/RecoverWithPhraseScreen.tsx @@ -36,6 +36,7 @@ import { loadPassportData, reStorePassportDataWithRightCSCA, } from '@/providers/passportDataProvider'; +import { recoveryCopy } from '@/screens/account/recovery/recoveryCopy'; const RecoverWithPhraseScreen: React.FC = () => { const navigation = @@ -70,26 +71,22 @@ const RecoverWithPhraseScreen: React.FC = () => { trackEvent(BackupEvents.CLOUD_RESTORE_FAILED_AUTH, { mnemonicLength: slimMnemonic.split(' ').length, }); - navigation.navigate({ name: 'Home', params: {} }); setRestoring(false); return; } const passportData = await loadPassportData(); - const secret = getPrivateKeyFromMnemonic(slimMnemonic); - if (!passportData || !secret) { + if (!passportData) { console.warn( - 'No passport data found on device. Please scan or import your document.', + 'Recovered secret but no local document data was found. Prompting the user to import their document again.', ); - trackEvent(BackupEvents.CLOUD_RESTORE_FAILED_AUTH, { - reason: 'no_passport_data', - }); - navigation.navigate({ name: 'Home', params: {} }); + navigation.navigate('CountryPicker'); setRestoring(false); return; } const passportDataParsed = JSON.parse(passportData); + const secret = getPrivateKeyFromMnemonic(slimMnemonic); const { isRegistered, csca } = await isUserRegisteredWithAlternativeCSCA( passportDataParsed, @@ -124,7 +121,6 @@ const RecoverWithPhraseScreen: React.FC = () => { reason: 'document_not_registered', hasCSCA: !!csca, }); - navigation.navigate({ name: 'Home', params: {} }); setRestoring(false); return; } @@ -143,7 +139,6 @@ const RecoverWithPhraseScreen: React.FC = () => { error: error instanceof Error ? error.message : 'unknown', }); setRestoring(false); - navigation.navigate({ name: 'Home', params: {} }); } }, [ mnemonic, @@ -162,8 +157,7 @@ const RecoverWithPhraseScreen: React.FC = () => { style={styles.layout} > - Your recovery phrase has 24 words. Enter the words in the correct order, - separated by spaces. + {recoveryCopy.phrase.instructions}