diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 4eb7d413..1970cc99 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ + 12.3.0) - - Firebase/Messaging (12.3.0): + - Firebase/CoreOnly (12.4.0): + - FirebaseCore (~> 12.4.0) + - Firebase/Messaging (12.4.0): - Firebase/CoreOnly - - FirebaseMessaging (~> 12.3.0) - - FirebaseCore (12.3.0): - - FirebaseCoreInternal (~> 12.3.0) + - FirebaseMessaging (~> 12.4.0) + - FirebaseCore (12.4.0): + - FirebaseCoreInternal (~> 12.4.0) - GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/Logger (~> 8.1) - - FirebaseCoreExtension (12.3.0): - - FirebaseCore (~> 12.3.0) - - FirebaseCoreInternal (12.3.0): + - FirebaseCoreExtension (12.4.0): + - FirebaseCore (~> 12.4.0) + - FirebaseCoreInternal (12.4.0): - "GoogleUtilities/NSData+zlib (~> 8.1)" - - FirebaseInstallations (12.3.0): - - FirebaseCore (~> 12.3.0) + - FirebaseInstallations (12.4.0): + - FirebaseCore (~> 12.4.0) - GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/UserDefaults (~> 8.1) - PromisesObjC (~> 2.4) - - FirebaseMessaging (12.3.0): - - FirebaseCore (~> 12.3.0) - - FirebaseInstallations (~> 12.3.0) + - FirebaseMessaging (12.4.0): + - FirebaseCore (~> 12.4.0) + - FirebaseInstallations (~> 12.4.0) - GoogleDataTransport (~> 10.1) - GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/Environment (~> 8.1) @@ -62,7 +63,7 @@ PODS: - hermes-engine (0.81.4): - hermes-engine/Pre-built (= 0.81.4) - hermes-engine/Pre-built (0.81.4) - - HotUpdater (0.20.0): + - HotUpdater (0.23.0): - boost - DoubleConversion - fast_float @@ -89,7 +90,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - SocketRocket - - SSZipArchive (~> 2.2.2) + - SWCompression (~> 4.8.0) - Yoga - libwebp (1.5.0): - libwebp/demux (= 1.5.0) @@ -103,11 +104,72 @@ PODS: - libwebp/sharpyuv (1.5.0) - libwebp/webp (1.5.0): - libwebp/sharpyuv + - MMKVCore (2.2.4) - nanopb (3.30910.0): - nanopb/decode (= 3.30910.0) - nanopb/encode (= 3.30910.0) - nanopb/decode (3.30910.0) - nanopb/encode (3.30910.0) + - NitroMmkv (4.0.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - MMKVCore (= 2.2.4) + - NitroModules + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-callinvoker + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga + - NitroModules (0.31.10): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-callinvoker + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga - OpenSSL-Universal (3.3.3001) - PromisesObjC (2.4.0) - RCT-Folly (2024.11.18.00): @@ -1851,37 +1913,11 @@ PODS: - React-RCTFBReactNativeSpec - ReactCommon/turbomodule/core - SocketRocket - - react-native-mmkv (3.3.3): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - react-native-netinfo (11.4.1): - React-Core - - react-native-pager-view (6.9.1): + - react-native-nfc-manager (3.17.2): + - React-Core + - react-native-pager-view (7.0.1): - boost - DoubleConversion - fast_float @@ -1967,7 +2003,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - react-native-safe-area-context (5.6.1): + - react-native-safe-area-context (5.6.2): - boost - DoubleConversion - fast_float @@ -1985,8 +2021,8 @@ PODS: - React-graphics - React-ImageManager - React-jsi - - react-native-safe-area-context/common (= 5.6.1) - - react-native-safe-area-context/fabric (= 5.6.1) + - react-native-safe-area-context/common (= 5.6.2) + - react-native-safe-area-context/fabric (= 5.6.2) - React-NativeModulesApple - React-RCTFabric - React-renderercss @@ -1997,7 +2033,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - react-native-safe-area-context/common (5.6.1): + - react-native-safe-area-context/common (5.6.2): - boost - DoubleConversion - fast_float @@ -2025,7 +2061,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - react-native-safe-area-context/fabric (5.6.1): + - react-native-safe-area-context/fabric (5.6.2): - boost - DoubleConversion - fast_float @@ -2627,15 +2663,15 @@ PODS: - React-Core - SDWebImage (~> 5.11.1) - SDWebImageWebPCoder (~> 0.8.4) - - RNFBApp (23.4.0): - - Firebase/CoreOnly (= 12.3.0) + - RNFBApp (23.5.0): + - Firebase/CoreOnly (= 12.4.0) - React-Core - - RNFBMessaging (23.4.0): - - Firebase/Messaging (= 12.3.0) + - RNFBMessaging (23.5.0): + - Firebase/Messaging (= 12.4.0) - FirebaseCoreExtension - React-Core - RNFBApp - - RNGestureHandler (2.28.0): + - RNGestureHandler (2.29.1): - boost - DoubleConversion - fast_float @@ -2692,7 +2728,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - RNLocalize (3.5.2): + - RNLocalize (3.6.0): - boost - DoubleConversion - fast_float @@ -2725,7 +2761,7 @@ PODS: - RNNotifee/NotifeeCore (= 9.1.8) - RNNotifee/NotifeeCore (9.1.8): - React-Core - - RNReanimated (4.1.2): + - RNReanimated (4.1.5): - boost - DoubleConversion - fast_float @@ -2752,11 +2788,11 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNReanimated/reanimated (= 4.1.2) + - RNReanimated/reanimated (= 4.1.5) - RNWorklets - SocketRocket - Yoga - - RNReanimated/reanimated (4.1.2): + - RNReanimated/reanimated (4.1.5): - boost - DoubleConversion - fast_float @@ -2783,11 +2819,11 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNReanimated/reanimated/apple (= 4.1.2) + - RNReanimated/reanimated/apple (= 4.1.5) - RNWorklets - SocketRocket - Yoga - - RNReanimated/reanimated/apple (4.1.2): + - RNReanimated/reanimated/apple (4.1.5): - boost - DoubleConversion - fast_float @@ -2817,7 +2853,7 @@ PODS: - RNWorklets - SocketRocket - Yoga - - RNScreens (4.16.0): + - RNScreens (4.18.0): - boost - DoubleConversion - fast_float @@ -2844,10 +2880,10 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNScreens/common (= 4.16.0) + - RNScreens/common (= 4.18.0) - SocketRocket - Yoga - - RNScreens/common (4.16.0): + - RNScreens/common (4.18.0): - boost - DoubleConversion - fast_float @@ -2876,7 +2912,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - RNSentry (6.22.0): + - RNSentry (7.7.0): - boost - DoubleConversion - fast_float @@ -2903,10 +2939,10 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - Sentry/HybridSDK (= 8.56.2) + - Sentry/HybridSDK (= 8.57.3) - SocketRocket - Yoga - - RNSVG (15.13.0): + - RNSVG (15.15.0): - boost - DoubleConversion - fast_float @@ -2932,10 +2968,10 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNSVG/common (= 15.13.0) + - RNSVG/common (= 15.15.0) - SocketRocket - Yoga - - RNSVG/common (15.13.0): + - RNSVG/common (15.15.0): - boost - DoubleConversion - fast_float @@ -2963,7 +2999,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - RNWorklets (0.6.0): + - RNWorklets (0.6.1): - boost - DoubleConversion - fast_float @@ -2990,10 +3026,10 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNWorklets/worklets (= 0.6.0) + - RNWorklets/worklets (= 0.6.1) - SocketRocket - Yoga - - RNWorklets/worklets (0.6.0): + - RNWorklets/worklets (0.6.1): - boost - DoubleConversion - fast_float @@ -3020,10 +3056,10 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNWorklets/worklets/apple (= 0.6.0) + - RNWorklets/worklets/apple (= 0.6.1) - SocketRocket - Yoga - - RNWorklets/worklets/apple (0.6.0): + - RNWorklets/worklets/apple (0.6.1): - boost - DoubleConversion - fast_float @@ -3058,9 +3094,49 @@ PODS: - SDWebImageWebPCoder (0.8.5): - libwebp (~> 1.0) - SDWebImage/Core (~> 5.10) - - Sentry/HybridSDK (8.56.2) + - Sentry/HybridSDK (8.57.3) - SocketRocket (0.7.1) - - SSZipArchive (2.2.3) + - SWCompression (4.8.6): + - BitByteData (~> 2.0) + - SWCompression/BZip2 (= 4.8.6) + - SWCompression/Deflate (= 4.8.6) + - SWCompression/GZip (= 4.8.6) + - SWCompression/LZ4 (= 4.8.6) + - SWCompression/LZMA (= 4.8.6) + - SWCompression/LZMA2 (= 4.8.6) + - SWCompression/SevenZip (= 4.8.6) + - SWCompression/TAR (= 4.8.6) + - SWCompression/XZ (= 4.8.6) + - SWCompression/ZIP (= 4.8.6) + - SWCompression/Zlib (= 4.8.6) + - SWCompression/BZip2 (4.8.6): + - BitByteData (~> 2.0) + - SWCompression/Deflate (4.8.6): + - BitByteData (~> 2.0) + - SWCompression/GZip (4.8.6): + - BitByteData (~> 2.0) + - SWCompression/Deflate + - SWCompression/LZ4 (4.8.6): + - BitByteData (~> 2.0) + - SWCompression/LZMA (4.8.6): + - BitByteData (~> 2.0) + - SWCompression/LZMA2 (4.8.6): + - BitByteData (~> 2.0) + - SWCompression/LZMA + - SWCompression/SevenZip (4.8.6): + - BitByteData (~> 2.0) + - SWCompression/LZMA2 + - SWCompression/TAR (4.8.6): + - BitByteData (~> 2.0) + - SWCompression/XZ (4.8.6): + - BitByteData (~> 2.0) + - SWCompression/LZMA2 + - SWCompression/ZIP (4.8.6): + - BitByteData (~> 2.0) + - SWCompression/Deflate + - SWCompression/Zlib (4.8.6): + - BitByteData (~> 2.0) + - SWCompression/Deflate - Yoga (0.0.0) DEPENDENCIES: @@ -3072,6 +3148,8 @@ DEPENDENCIES: - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) - "HotUpdater (from `../node_modules/@hot-updater/react-native`)" + - NitroMmkv (from `../node_modules/react-native-mmkv`) + - NitroModules (from `../node_modules/react-native-nitro-modules`) - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - RCTDeprecation (from `../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`) - RCTRequired (from `../node_modules/react-native/Libraries/Required`) @@ -3106,8 +3184,8 @@ DEPENDENCIES: - React-logger (from `../node_modules/react-native/ReactCommon/logger`) - React-Mapbuffer (from `../node_modules/react-native/ReactCommon`) - React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`) - - react-native-mmkv (from `../node_modules/react-native-mmkv`) - "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)" + - react-native-nfc-manager (from `../node_modules/react-native-nfc-manager`) - react-native-pager-view (from `../node_modules/react-native-pager-view`) - react-native-quick-crypto (from `../node_modules/react-native-quick-crypto`) - react-native-quick-sqlite (from `../node_modules/react-native-quick-sqlite`) @@ -3162,6 +3240,7 @@ DEPENDENCIES: SPEC REPOS: trunk: + - BitByteData - Firebase - FirebaseCore - FirebaseCoreExtension @@ -3171,6 +3250,7 @@ SPEC REPOS: - GoogleDataTransport - GoogleUtilities - libwebp + - MMKVCore - nanopb - OpenSSL-Universal - PromisesObjC @@ -3179,7 +3259,7 @@ SPEC REPOS: - SDWebImageWebPCoder - Sentry - SocketRocket - - SSZipArchive + - SWCompression EXTERNAL SOURCES: boost: @@ -3199,6 +3279,10 @@ EXTERNAL SOURCES: :tag: hermes-2025-07-07-RNv0.81.0-e0fc67142ec0763c6b6153ca2bf96df815539782 HotUpdater: :path: "../node_modules/@hot-updater/react-native" + NitroMmkv: + :path: "../node_modules/react-native-mmkv" + NitroModules: + :path: "../node_modules/react-native-nitro-modules" RCT-Folly: :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" RCTDeprecation: @@ -3265,10 +3349,10 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon" React-microtasksnativemodule: :path: "../node_modules/react-native/ReactCommon/react/nativemodule/microtasks" - react-native-mmkv: - :path: "../node_modules/react-native-mmkv" react-native-netinfo: :path: "../node_modules/@react-native-community/netinfo" + react-native-nfc-manager: + :path: "../node_modules/react-native-nfc-manager" react-native-pager-view: :path: "../node_modules/react-native-pager-view" react-native-quick-crypto: @@ -3371,24 +3455,28 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/yoga" SPEC CHECKSUMS: + BitByteData: 2422ed269fcddc44976d97f171a183d25ef535ce boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90 DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb fast_float: b32c788ed9c6a8c584d114d0047beda9664e7cc6 FBLazyVector: 941bef1c8eeabd9fe1f501e30a5220beee913886 - Firebase: f5439b235721ceeef14ca1f327c0da8e4e8556b5 - FirebaseCore: ff47fe1ad3ab9ef66edd3e8bc4647b493d2067f8 - FirebaseCoreExtension: 898ca55c7cb126f83747b32cb0daa95394b10853 - FirebaseCoreInternal: a9e1ff270f217489d9258563b693d11a312903bf - FirebaseInstallations: ca48ec60ea51b66b9f214a91847ea3720cde97f5 - FirebaseMessaging: 919ce76cb353f0c36d463f5461d8ab584e6ed765 + Firebase: f07b15ae5a6ec0f93713e30b923d9970d144af3e + FirebaseCore: bb595f3114953664e3c1dc032f008a244147cfd3 + FirebaseCoreExtension: 7e1f7118ee970e001a8013719fb90950ee5e0018 + FirebaseCoreInternal: d7f5a043c2cd01a08103ab586587c1468047bca6 + FirebaseInstallations: ae9f4902cb5bf1d0c5eaa31ec1f4e5495a0714e2 + FirebaseMessaging: d33971b7bb252745ea6cd31ab190d1a1df4b8ed5 fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd glog: 5683914934d5b6e4240e497e0f4a3b42d1854183 GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 hermes-engine: 35c763d57c9832d0eef764316ca1c4d043581394 - HotUpdater: af56906e6be7aa821d10036f8f7d6a63c8e4ad22 + HotUpdater: ecbc87212adaf29fbd04eab80f105cf9a972f12f libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8 + MMKVCore: f2dd4c9befea04277a55e84e7812f930537993df nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 + NitroMmkv: bdd120d9e6b234ebd637e60d066fbedf73004d11 + NitroModules: cf8804dafa637b6535c73fd113c3202b11307bfd OpenSSL-Universal: 6082b0bf950e5636fe0d78def171184e2b3899c2 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669 @@ -3425,12 +3513,12 @@ SPEC CHECKSUMS: React-logger: a3cb5b29c32b8e447b5a96919340e89334062b48 React-Mapbuffer: e4a65db5f4df53369f39558c0cf2f480f6d3d6c7 React-microtasksnativemodule: 86334c5c06315e0bccb7b6e6f2c905e92f98b615 - react-native-mmkv: 7b9c7469fa0a7e463f9411ad3e4fe273bd5ff030 react-native-netinfo: cec9c4e86083cb5b6aba0e0711f563e2fbbff187 - react-native-pager-view: 0e228ec2dfdd87807d125c7bbfb83299edede19c + react-native-nfc-manager: c8891e460b4943b695d63f7f4effc6345bbefc83 + react-native-pager-view: d91a84dd23f9fba7e63c4b9d68f32f52c9ec62ca react-native-quick-crypto: 2c53f72c27485924b444dda124639ee363cf1d69 react-native-quick-sqlite: 7d9f9fda3aa1fea00387454f5d912c2271dc35fb - react-native-safe-area-context: ee1e8e2a7abf737a8d4d9d1a5686a7f2e7466236 + react-native-safe-area-context: 0a3b034bb63a5b684dd2f5fffd3c90ef6ed41ee8 React-NativeModulesApple: 8c7eb6057b00c191a11ad5ced41826ec5a0e4d78 React-oscompat: 93b5535ea7f7dff46aaee4f78309a70979bdde9d React-perflogger: 5536d2df3d18fe0920263466f7b46a56351c0510 @@ -3465,22 +3553,22 @@ SPEC CHECKSUMS: RNCClipboard: e560338bf6cc4656a09ff90610b62ddc0dbdad65 RNExitApp: a2fd7923ab37b9166704fdc488622a48dcd372a2 RNFastImage: 462a183c4b0b6b26fdfd639e1ed6ba37536c3b87 - RNFBApp: 0211ae65fadcb017cc787b2bd539847330687b93 - RNFBMessaging: 97dc90cb79d1d015250d9d10032c920f1d49c354 - RNGestureHandler: b8d2e75c2e88fc2a1f6be3b3beeeed80b88fa37d + RNFBApp: 78d866e12b98c79b7cb141fcdcdf9813eae09b15 + RNFBMessaging: e16ebeae1f64f57603a193f1be6f3be7ab55c5e4 + RNGestureHandler: 9462ba64b3fdb2cb04cbc1b572d020c8d0a096e8 RNKeychain: 76d042fc1dfba47f3945920bc82e0bce9ad1ff55 - RNLocalize: d8ccb568b40c72c767714db3808ce66a1ecf444a + RNLocalize: a0bf70cf27f116db4e32be09a6f549715db0686d RNNotifee: 5e3b271e8ea7456a36eec994085543c9adca9168 - RNReanimated: 44af5b24b999a70c737df8e407582c32d12b3a87 - RNScreens: 2e9c41cd099b1ca50136af8d57c3594214d0086a - RNSentry: 9584c841b049b0b751befefc324d2edb769f546e - RNSVG: 08e4ffbf2e7e5b9f46dcc4b46b1326d1025cad52 - RNWorklets: 564218881b4c63ab941bd5b3be37ae585a30a1c6 + RNReanimated: c3de6d69dba4054d4f35562b14c84061699188e5 + RNScreens: 98771ad898d1c0528fc8139606bbacf5a2e9d237 + RNSentry: 145033a3996d33faa023a496c8338275b10269a3 + RNSVG: f2979aa72b14050b9220415c19de398f9cec9aaf + RNWorklets: ab618bf7d1c7fd2cb793b9f0f39c3e29274b3ebf SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d - Sentry: b53951377b78e21a734f5dc8318e333dbfc682d7 + Sentry: c643eb180df401dd8c734c5036ddd9dd9218daa6 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - SSZipArchive: 62d4947b08730e4cda640473b0066d209ff033c9 + SWCompression: 9379873ad5e9f25b31db88c441649b59a4c2bab7 Yoga: 9b30b783a17681321b52ac507a37219d7d795ace PODFILE CHECKSUM: b335b509a9bfd90d0626f23590a7a32278b465d8 diff --git a/ios/minibits_wallet.xcodeproj/project.pbxproj b/ios/minibits_wallet.xcodeproj/project.pbxproj index 54cb0f6e..1fef30f6 100644 --- a/ios/minibits_wallet.xcodeproj/project.pbxproj +++ b/ios/minibits_wallet.xcodeproj/project.pbxproj @@ -261,7 +261,7 @@ name = "[CP-User] [RNFB] Core Configuration"; runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##########################################################################\n##########################################################################\n#\n# NOTE THAT IF YOU CHANGE THIS FILE YOU MUST RUN pod install AFTERWARDS\n#\n# This file is installed as an Xcode build script in the project file\n# by cocoapods, and you will not see your changes until you pod install\n#\n##########################################################################\n##########################################################################\n\nset -e\n\n_MAX_LOOKUPS=2;\n_SEARCH_RESULT=''\n_RN_ROOT_EXISTS=''\n_CURRENT_LOOKUPS=1\n_JSON_ROOT=\"'react-native'\"\n_JSON_FILE_NAME='firebase.json'\n_JSON_OUTPUT_BASE64='e30=' # { }\n_CURRENT_SEARCH_DIR=${PROJECT_DIR}\n_PLIST_BUDDY=/usr/libexec/PlistBuddy\n_TARGET_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n_DSYM_PLIST=\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"\n\n# plist arrays\n_PLIST_ENTRY_KEYS=()\n_PLIST_ENTRY_TYPES=()\n_PLIST_ENTRY_VALUES=()\n\nfunction setPlistValue {\n echo \"info: setting plist entry '$1' of type '$2' in file '$4'\"\n ${_PLIST_BUDDY} -c \"Add :$1 $2 '$3'\" $4 || echo \"info: '$1' already exists\"\n}\n\nfunction getFirebaseJsonKeyValue () {\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n ruby -Ku -e \"require 'rubygems';require 'json'; output=JSON.parse('$1'); puts output[$_JSON_ROOT]['$2']\"\n else\n echo \"\"\n fi;\n}\n\nfunction jsonBoolToYesNo () {\n if [[ $1 == \"false\" ]]; then\n echo \"NO\"\n elif [[ $1 == \"true\" ]]; then\n echo \"YES\"\n else echo \"NO\"\n fi\n}\n\necho \"info: -> RNFB build script started\"\necho \"info: 1) Locating ${_JSON_FILE_NAME} file:\"\n\nif [[ -z ${_CURRENT_SEARCH_DIR} ]]; then\n _CURRENT_SEARCH_DIR=$(pwd)\nfi;\n\nwhile true; do\n _CURRENT_SEARCH_DIR=$(dirname \"$_CURRENT_SEARCH_DIR\")\n if [[ \"$_CURRENT_SEARCH_DIR\" == \"/\" ]] || [[ ${_CURRENT_LOOKUPS} -gt ${_MAX_LOOKUPS} ]]; then break; fi;\n echo \"info: ($_CURRENT_LOOKUPS of $_MAX_LOOKUPS) Searching in '$_CURRENT_SEARCH_DIR' for a ${_JSON_FILE_NAME} file.\"\n _SEARCH_RESULT=$(find \"$_CURRENT_SEARCH_DIR\" -maxdepth 2 -name ${_JSON_FILE_NAME} -print | /usr/bin/head -n 1)\n if [[ ${_SEARCH_RESULT} ]]; then\n echo \"info: ${_JSON_FILE_NAME} found at $_SEARCH_RESULT\"\n break;\n fi;\n _CURRENT_LOOKUPS=$((_CURRENT_LOOKUPS+1))\ndone\n\nif [[ ${_SEARCH_RESULT} ]]; then\n _JSON_OUTPUT_RAW=$(cat \"${_SEARCH_RESULT}\")\n if ! _RN_ROOT_EXISTS=$(ruby -Ku -e \"require 'json'; output=JSON.parse('$_JSON_OUTPUT_RAW'); puts output[$_JSON_ROOT]\"); then\n echo \"error: Failed to parse firebase.json, check for syntax errors.\"\n exit 1\n fi\n\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n if ! python3 --version >/dev/null 2>&1; then echo \"python3 not found, firebase.json file processing error.\" && exit 1; fi\n _JSON_OUTPUT_BASE64=$(python3 -c 'import json,sys,base64;print(base64.b64encode(bytes(json.dumps(json.loads(open('\"'${_SEARCH_RESULT}'\"', '\"'rb'\"').read())['${_JSON_ROOT}']), '\"'utf-8'\"')).decode())' || echo \"e30=\")\n fi\n\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n\n # config.app_data_collection_default_enabled\n _APP_DATA_COLLECTION_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_data_collection_default_enabled\")\n if [[ $_APP_DATA_COLLECTION_ENABLED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseDataCollectionDefaultEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_DATA_COLLECTION_ENABLED\")\")\n fi\n\n # config.analytics_auto_collection_enabled\n _ANALYTICS_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_auto_collection_enabled\")\n if [[ $_ANALYTICS_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_COLLECTION\")\")\n fi\n\n # config.analytics_collection_deactivated\n _ANALYTICS_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_collection_deactivated\")\n if [[ $_ANALYTICS_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_DEACTIVATED\")\")\n fi\n\n # config.analytics_idfv_collection_enabled\n _ANALYTICS_IDFV_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_idfv_collection_enabled\")\n if [[ $_ANALYTICS_IDFV_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_IDFV_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_IDFV_COLLECTION\")\")\n fi\n\n # config.analytics_default_allow_analytics_storage\n _ANALYTICS_STORAGE=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_analytics_storage\")\n if [[ $_ANALYTICS_STORAGE ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_ANALYTICS_STORAGE\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_STORAGE\")\")\n fi\n\n # config.analytics_default_allow_ad_storage\n _ANALYTICS_AD_STORAGE=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_storage\")\n if [[ $_ANALYTICS_AD_STORAGE ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_STORAGE\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AD_STORAGE\")\")\n fi\n\n # config.analytics_default_allow_ad_user_data\n _ANALYTICS_AD_USER_DATA=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_user_data\")\n if [[ $_ANALYTICS_AD_USER_DATA ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_USER_DATA\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AD_USER_DATA\")\")\n fi\n\n # config.analytics_default_allow_ad_personalization_signals\n _ANALYTICS_PERSONALIZATION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_personalization_signals\")\n if [[ $_ANALYTICS_PERSONALIZATION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_PERSONALIZATION_SIGNALS\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_PERSONALIZATION\")\")\n fi\n\n # config.analytics_registration_with_ad_network_enabled\n _ANALYTICS_REGISTRATION_WITH_AD_NETWORK=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"google_analytics_registration_with_ad_network_enabled\")\n if [[ $_ANALYTICS_REGISTRATION_WITH_AD_NETWORK ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_REGISTRATION_WITH_AD_NETWORK_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_REGISTRATION_WITH_AD_NETWORK\")\")\n fi\n\n # config.google_analytics_automatic_screen_reporting_enabled\n _ANALYTICS_AUTO_SCREEN_REPORTING=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"google_analytics_automatic_screen_reporting_enabled\")\n if [[ $_ANALYTICS_AUTO_SCREEN_REPORTING ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAutomaticScreenReportingEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_SCREEN_REPORTING\")\")\n fi\n\n # config.perf_auto_collection_enabled\n _PERF_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_auto_collection_enabled\")\n if [[ $_PERF_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_enabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_AUTO_COLLECTION\")\")\n fi\n\n # config.perf_collection_deactivated\n _PERF_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_collection_deactivated\")\n if [[ $_PERF_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_deactivated\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_DEACTIVATED\")\")\n fi\n\n # config.messaging_auto_init_enabled\n _MESSAGING_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"messaging_auto_init_enabled\")\n if [[ $_MESSAGING_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseMessagingAutoInitEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_MESSAGING_AUTO_INIT\")\")\n fi\n\n # config.in_app_messaging_auto_colllection_enabled\n _FIAM_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"in_app_messaging_auto_collection_enabled\")\n if [[ $_FIAM_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseInAppMessagingAutomaticDataCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_FIAM_AUTO_INIT\")\")\n fi\n\n # config.app_check_token_auto_refresh\n _APP_CHECK_TOKEN_AUTO_REFRESH=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_check_token_auto_refresh\")\n if [[ $_APP_CHECK_TOKEN_AUTO_REFRESH ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAppCheckTokenAutoRefreshEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_CHECK_TOKEN_AUTO_REFRESH\")\")\n fi\n\n # config.crashlytics_disable_auto_disabler - undocumented for now - mainly for debugging, document if becomes useful\n _CRASHLYTICS_AUTO_DISABLE_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"crashlytics_disable_auto_disabler\")\n if [[ $_CRASHLYTICS_AUTO_DISABLE_ENABLED == \"true\" ]]; then\n echo \"Disabled Crashlytics auto disabler.\" # do nothing\n else\n _PLIST_ENTRY_KEYS+=(\"FirebaseCrashlyticsCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"NO\")\n fi\nelse\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n echo \"warning: A firebase.json file was not found, whilst this file is optional it is recommended to include it to configure firebase services in React Native Firebase.\"\nfi;\n\necho \"info: 2) Injecting Info.plist entries: \"\n\n# Log out the keys we're adding\nfor i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n echo \" -> $i) ${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\"\ndone\n\nfor plist in \"${_TARGET_PLIST}\" \"${_DSYM_PLIST}\" ; do\n if [[ -f \"${plist}\" ]]; then\n\n # paths with spaces break the call to setPlistValue. temporarily modify\n # the shell internal field separator variable (IFS), which normally\n # includes spaces, to consist only of line breaks\n oldifs=$IFS\n IFS=\"\n\"\n\n for i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n setPlistValue \"${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\" \"${plist}\"\n done\n\n # restore the original internal field separator value\n IFS=$oldifs\n else\n echo \"warning: A Info.plist build output file was not found (${plist})\"\n fi\ndone\n\necho \"info: <- RNFB build script finished\"\n"; + shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##########################################################################\n##########################################################################\n#\n# NOTE THAT IF YOU CHANGE THIS FILE YOU MUST RUN pod install AFTERWARDS\n#\n# This file is installed as an Xcode build script in the project file\n# by cocoapods, and you will not see your changes until you pod install\n#\n##########################################################################\n##########################################################################\n\nset -e\n\n_MAX_LOOKUPS=2;\n_SEARCH_RESULT=''\n_RN_ROOT_EXISTS=''\n_CURRENT_LOOKUPS=1\n_JSON_ROOT=\"'react-native'\"\n_JSON_FILE_NAME='firebase.json'\n_JSON_OUTPUT_BASE64='e30=' # { }\n_CURRENT_SEARCH_DIR=${PROJECT_DIR}\n_PLIST_BUDDY=/usr/libexec/PlistBuddy\n_TARGET_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n_DSYM_PLIST=\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"\n\n# plist arrays\n_PLIST_ENTRY_KEYS=()\n_PLIST_ENTRY_TYPES=()\n_PLIST_ENTRY_VALUES=()\n\nfunction setPlistValue {\n echo \"note: setting plist entry '$1' of type '$2' in file '$4'\"\n ${_PLIST_BUDDY} -c \"Add :$1 $2 '$3'\" $4 || echo \"note: '$1' already exists\"\n}\n\nfunction getFirebaseJsonKeyValue () {\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n ruby -Ku -e \"require 'rubygems';require 'json'; output=JSON.parse('$1'); puts output[$_JSON_ROOT]['$2']\"\n else\n echo \"\"\n fi;\n}\n\nfunction jsonBoolToYesNo () {\n if [[ $1 == \"false\" ]]; then\n echo \"NO\"\n elif [[ $1 == \"true\" ]]; then\n echo \"YES\"\n else echo \"NO\"\n fi\n}\n\necho \"note: -> RNFB build script started\"\necho \"note: 1) Locating ${_JSON_FILE_NAME} file:\"\n\nif [[ -z ${_CURRENT_SEARCH_DIR} ]]; then\n _CURRENT_SEARCH_DIR=$(pwd)\nfi;\n\nwhile true; do\n _CURRENT_SEARCH_DIR=$(dirname \"$_CURRENT_SEARCH_DIR\")\n if [[ \"$_CURRENT_SEARCH_DIR\" == \"/\" ]] || [[ ${_CURRENT_LOOKUPS} -gt ${_MAX_LOOKUPS} ]]; then break; fi;\n echo \"note: ($_CURRENT_LOOKUPS of $_MAX_LOOKUPS) Searching in '$_CURRENT_SEARCH_DIR' for a ${_JSON_FILE_NAME} file.\"\n _SEARCH_RESULT=$(find \"$_CURRENT_SEARCH_DIR\" -maxdepth 2 -name ${_JSON_FILE_NAME} -print | /usr/bin/head -n 1)\n if [[ ${_SEARCH_RESULT} ]]; then\n echo \"note: ${_JSON_FILE_NAME} found at $_SEARCH_RESULT\"\n break;\n fi;\n _CURRENT_LOOKUPS=$((_CURRENT_LOOKUPS+1))\ndone\n\nif [[ ${_SEARCH_RESULT} ]]; then\n _JSON_OUTPUT_RAW=$(cat \"${_SEARCH_RESULT}\")\n if ! _RN_ROOT_EXISTS=$(ruby -Ku -e \"require 'json'; output=JSON.parse('$_JSON_OUTPUT_RAW'); puts output[$_JSON_ROOT]\"); then\n echo \"error: Failed to parse firebase.json, check for syntax errors.\"\n exit 1\n fi\n\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n if ! python3 --version >/dev/null 2>&1; then echo \"error: python3 not found, firebase.json file processing error.\" && exit 1; fi\n _JSON_OUTPUT_BASE64=$(python3 -c 'import json,sys,base64;print(base64.b64encode(bytes(json.dumps(json.loads(open('\"'${_SEARCH_RESULT}'\"', '\"'rb'\"').read())['${_JSON_ROOT}']), '\"'utf-8'\"')).decode())' || echo \"e30=\")\n fi\n\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n\n # config.app_data_collection_default_enabled\n _APP_DATA_COLLECTION_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_data_collection_default_enabled\")\n if [[ $_APP_DATA_COLLECTION_ENABLED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseDataCollectionDefaultEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_DATA_COLLECTION_ENABLED\")\")\n fi\n\n # config.analytics_auto_collection_enabled\n _ANALYTICS_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_auto_collection_enabled\")\n if [[ $_ANALYTICS_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_COLLECTION\")\")\n fi\n\n # config.analytics_collection_deactivated\n _ANALYTICS_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_collection_deactivated\")\n if [[ $_ANALYTICS_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_DEACTIVATED\")\")\n fi\n\n # config.analytics_idfv_collection_enabled\n _ANALYTICS_IDFV_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_idfv_collection_enabled\")\n if [[ $_ANALYTICS_IDFV_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_IDFV_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_IDFV_COLLECTION\")\")\n fi\n\n # config.analytics_default_allow_analytics_storage\n _ANALYTICS_STORAGE=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_analytics_storage\")\n if [[ $_ANALYTICS_STORAGE ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_ANALYTICS_STORAGE\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_STORAGE\")\")\n fi\n\n # config.analytics_default_allow_ad_storage\n _ANALYTICS_AD_STORAGE=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_storage\")\n if [[ $_ANALYTICS_AD_STORAGE ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_STORAGE\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AD_STORAGE\")\")\n fi\n\n # config.analytics_default_allow_ad_user_data\n _ANALYTICS_AD_USER_DATA=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_user_data\")\n if [[ $_ANALYTICS_AD_USER_DATA ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_USER_DATA\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AD_USER_DATA\")\")\n fi\n\n # config.analytics_default_allow_ad_personalization_signals\n _ANALYTICS_PERSONALIZATION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_personalization_signals\")\n if [[ $_ANALYTICS_PERSONALIZATION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_PERSONALIZATION_SIGNALS\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_PERSONALIZATION\")\")\n fi\n\n # config.analytics_registration_with_ad_network_enabled\n _ANALYTICS_REGISTRATION_WITH_AD_NETWORK=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"google_analytics_registration_with_ad_network_enabled\")\n if [[ $_ANALYTICS_REGISTRATION_WITH_AD_NETWORK ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_REGISTRATION_WITH_AD_NETWORK_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_REGISTRATION_WITH_AD_NETWORK\")\")\n fi\n\n # config.google_analytics_automatic_screen_reporting_enabled\n _ANALYTICS_AUTO_SCREEN_REPORTING=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"google_analytics_automatic_screen_reporting_enabled\")\n if [[ $_ANALYTICS_AUTO_SCREEN_REPORTING ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAutomaticScreenReportingEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_SCREEN_REPORTING\")\")\n fi\n\n # config.perf_auto_collection_enabled\n _PERF_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_auto_collection_enabled\")\n if [[ $_PERF_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_enabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_AUTO_COLLECTION\")\")\n fi\n\n # config.perf_collection_deactivated\n _PERF_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_collection_deactivated\")\n if [[ $_PERF_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_deactivated\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_DEACTIVATED\")\")\n fi\n\n # config.messaging_auto_init_enabled\n _MESSAGING_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"messaging_auto_init_enabled\")\n if [[ $_MESSAGING_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseMessagingAutoInitEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_MESSAGING_AUTO_INIT\")\")\n fi\n\n # config.in_app_messaging_auto_colllection_enabled\n _FIAM_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"in_app_messaging_auto_collection_enabled\")\n if [[ $_FIAM_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseInAppMessagingAutomaticDataCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_FIAM_AUTO_INIT\")\")\n fi\n\n # config.app_check_token_auto_refresh\n _APP_CHECK_TOKEN_AUTO_REFRESH=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_check_token_auto_refresh\")\n if [[ $_APP_CHECK_TOKEN_AUTO_REFRESH ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAppCheckTokenAutoRefreshEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_CHECK_TOKEN_AUTO_REFRESH\")\")\n fi\n\n # config.crashlytics_disable_auto_disabler - undocumented for now - mainly for debugging, document if becomes useful\n _CRASHLYTICS_AUTO_DISABLE_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"crashlytics_disable_auto_disabler\")\n if [[ $_CRASHLYTICS_AUTO_DISABLE_ENABLED == \"true\" ]]; then\n echo \"Disabled Crashlytics auto disabler.\" # do nothing\n else\n _PLIST_ENTRY_KEYS+=(\"FirebaseCrashlyticsCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"NO\")\n fi\nelse\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n echo \"warning: A firebase.json file was not found, whilst this file is optional it is recommended to include it to configure firebase services in React Native Firebase.\"\nfi;\n\necho \"note: 2) Injecting Info.plist entries: \"\n\n# Log out the keys we're adding\nfor i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n echo \" -> $i) ${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\"\ndone\n\nfor plist in \"${_TARGET_PLIST}\" \"${_DSYM_PLIST}\" ; do\n if [[ -f \"${plist}\" ]]; then\n\n # paths with spaces break the call to setPlistValue. temporarily modify\n # the shell internal field separator variable (IFS), which normally\n # includes spaces, to consist only of line breaks\n oldifs=$IFS\n IFS=\"\n\"\n\n for i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n setPlistValue \"${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\" \"${plist}\"\n done\n\n # restore the original internal field separator value\n IFS=$oldifs\n else\n echo \"warning: A Info.plist build output file was not found (${plist})\"\n fi\ndone\n\necho \"note: <- RNFB build script finished\"\n"; }; E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; @@ -447,7 +447,10 @@ "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", ); - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; @@ -530,7 +533,10 @@ "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", ); - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; diff --git a/package.json b/package.json index b6fcba12..ace47c48 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "machine-translate": "npx @inlang/cli machine translate --project minibits.inlang" }, "dependencies": { - "@cashu/cashu-ts": "minibits-cash/cashu-ts#b3ba36757d84ec03f1064029db02308eb3a09cc4", + "@cashu/cashu-ts": "minibits-cash/cashu-ts#c85d4638c007ac8126bc83a7287a6d848fcc8153", "@fortawesome/fontawesome-svg-core": "^7.1.0", "@fortawesome/free-brands-svg-icons": "^7.1.0", "@fortawesome/free-regular-svg-icons": "^7.1.0", @@ -30,30 +30,30 @@ "@gandlaf21/bc-ur": "^1.1.12", "@gandlaf21/bolt11-decode": "^3.1.1", "@gocodingnow/rn-size-matters": "^0.0.4", - "@hot-updater/react-native": "0.20.11", + "@hot-updater/react-native": "0.23.1", "@notifee/react-native": "^9.1.8", "@react-native-clipboard/clipboard": "^1.16.3", "@react-native-community/netinfo": "^11.4.1", - "@react-native-firebase/app": "^23.4.0", - "@react-native-firebase/messaging": "^23.4.0", - "@react-navigation/bottom-tabs": "^7.4.8", - "@react-navigation/native": "^7.1.18", - "@react-navigation/native-stack": "^7.3.27", - "@react-navigation/stack": "^7.4.9", + "@react-native-firebase/app": "^23.5.0", + "@react-native-firebase/messaging": "^23.5.0", + "@react-navigation/bottom-tabs": "^7.8.8", + "@react-navigation/native": "^7.1.22", + "@react-navigation/native-stack": "^7.8.2", + "@react-navigation/stack": "^7.6.8", "@scure/bip39": "1.6.0", - "@sentry/react-native": "^6.22.0", + "@sentry/react-native": "^7.7.0", "date-fns": "^4.1.0", "i18n-js": "^4.5.1", "js-lnurl": "^0.6.0", "lodash.clonedeep": "^4.5.0", "lodash.debounce": "4.0.8", "message-port-polyfill": "^0.2.0", - "mobx": "^6.13.7", - "mobx-react-lite": "^4.1.0", + "mobx": "^6.15.0", + "mobx-react-lite": "^4.1.1", "mobx-state-tree": "^7.0.2", "nostr-tools": "minibits-cash/nostr-tools#2991f59d0150a0cb2c71e2f9a4cb6c8aca3b19d7", "numbro": "^2.5.0", - "patch-package": "^8.0.0", + "patch-package": "^8.0.1", "react": "19.1.0", "react-native": "0.81.4", "react-native-animated-pagination-dots": "^0.1.73", @@ -61,24 +61,26 @@ "react-native-exit-app": "^2.0.0", "react-native-fast-image": "^8.6.3", "react-native-flash-message": "^0.4.2", - "react-native-gesture-handler": "^2.28.0", + "react-native-gesture-handler": "^2.29.1", "react-native-json-tree": "^1.5.0", "react-native-keychain": "^10.0.0", - "react-native-localize": "^3.5.2", + "react-native-localize": "^3.6.0", "react-native-logs": "^5.5.0", - "react-native-mmkv": "^3.3.3", + "react-native-mmkv": "^4.0.1", "react-native-modal": "^14.0.0-rc.1", - "react-native-pager-view": "^6.9.1", - "react-native-qrcode-svg": "^6.3.15", + "react-native-nfc-manager": "^3.17.2", + "react-native-nitro-modules": "^0.31.10", + "react-native-pager-view": "^7.0.1", + "react-native-qrcode-svg": "^6.3.20", "react-native-quick-crypto": "^0.7.17", "react-native-quick-sqlite": "8.2.7", - "react-native-reanimated": "4.1.2", - "react-native-safe-area-context": "^5.6.1", - "react-native-screens": "^4.16.0", - "react-native-svg": "^15.13.0", - "react-native-tab-view": "^4.1.3", + "react-native-reanimated": "4.1.5", + "react-native-safe-area-context": "^5.6.2", + "react-native-screens": "^4.18.0", + "react-native-svg": "^15.15.0", + "react-native-tab-view": "^4.2.0", "react-native-url-polyfill": "^3.0.0", - "react-native-worklets": "^0.6.0", + "react-native-worklets": "^0.6.1", "taskon": "^0.3.1", "text-encoding-polyfill": "^0.6.7", "util": "^0.12.5" @@ -88,9 +90,9 @@ "@babel/plugin-proposal-export-namespace-from": "^7.18.9", "@babel/preset-env": "^7.25.3", "@babel/runtime": "^7.25.0", - "@hot-updater/bare": "0.20.11", - "@hot-updater/firebase": "0.20.11", - "@hot-updater/sentry-plugin": "^0.20.11", + "@hot-updater/bare": "0.23.1", + "@hot-updater/firebase": "0.23.1", + "@hot-updater/sentry-plugin": "^0.23.1", "@react-native-community/cli": "20.0.0", "@react-native-community/cli-platform-android": "20.0.0", "@react-native-community/cli-platform-ios": "20.0.0", @@ -98,7 +100,7 @@ "@react-native/eslint-config": "0.81.4", "@react-native/metro-config": "0.81.4", "@react-native/typescript-config": "0.81.4", - "@sentry/cli": "^2.56.0", + "@sentry/cli": "^2.58.2", "@tsconfig/react-native": "^3.0.7", "@types/jest": "^29.5.13", "@types/lodash.clonedeep": "^4.5.9", @@ -108,9 +110,9 @@ "dotenv": "^17.2.3", "eslint": "^8.56.0", "eslint-plugin-unused-imports": "^2.0.0", - "firebase-admin": "^13.5.0", - "firebase-tools": "^14.19.0", - "hot-updater": "^0.19.9", + "firebase-admin": "^13.6.0", + "firebase-tools": "^14.26.0", + "hot-updater": "^0.23.1", "jest": "^29.6.3", "prettier": "2.8.8", "react-native-clean-project": "^4.0.3", diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index 75d2aebb..3eaacf06 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -23,6 +23,7 @@ import { faTelegramPlane } from "@fortawesome/free-brands-svg-icons/faTelegramPl import { faDiscord } from "@fortawesome/free-brands-svg-icons/faDiscord" import { faGithub } from "@fortawesome/free-brands-svg-icons/faGithub" import { faReddit } from "@fortawesome/free-brands-svg-icons/faReddit" +import { faNfcSymbol } from '@fortawesome/free-brands-svg-icons/faNfcSymbol' import { faWallet } from '@fortawesome/free-solid-svg-icons/faWallet' import { faAddressCard } from '@fortawesome/free-solid-svg-icons/faAddressCard' @@ -99,13 +100,15 @@ import { faHeartPulse } from "@fortawesome/free-solid-svg-icons/faHeartPulse" import { faSeedling } from "@fortawesome/free-solid-svg-icons/faSeedling" import { faChevronLeft } from '@fortawesome/free-solid-svg-icons/faChevronLeft' import { faArrowRightArrowLeft } from "@fortawesome/free-solid-svg-icons" +import { faWifi } from "@fortawesome/free-solid-svg-icons" + export type IconTypes = keyof typeof iconRegistry // TODO remove need for manual iconregistry? // would be best to just import all of them, i guess, or figure out something smart -export const iconRegistry = { faAddressCard, faAddressBook, faWallet, faQrcode, faClipboard, faSliders, faCoins, faEllipsisVertical, faEllipsis, faArrowUp, faArrowDown, faArrowLeft, faXmark, faInfoCircle, faBug, faCheckCircle, faArrowTurnUp, faArrowTurnDown, faPencil, faTags, faShareFromSquare, faRotate, faCode, faBan, faCircle, faPaperPlane, faBolt, faArrowUpFromBracket, faArrowRightToBracket, faPlus, faShieldHalved, faCloudArrowUp, faPaintbrush, faCopy, faBurst, faUserShield, faLock, faLockOpen, faTriangleExclamation, faDownload, faUpload, faRecycle, faListUl, faExpand, faFingerprint, faWandMagicSparkles, faCircleUser, faComment, faKey, faCircleNodes, faBullseye, faEyeSlash, faUpRightFromSquare, faShareNodes, faPaste, faKeyboard, faMoneyBill1, faGears, faTag, faBank, faChevronDown, faChevronUp, faCircleExclamation, faCircleQuestion, faEnvelope, faTwitter, faTelegramPlane, faDiscord, faGithub, faReddit, faCircleArrowUp, faCircleArrowDown, faGlobe, faCubes, faClock, faArrowRotateLeft, faHeartPulse, faSeedling, faChevronLeft, faArrowRightArrowLeft } +export const iconRegistry = { faWifi, faNfcSymbol, faAddressCard, faAddressBook, faWallet, faQrcode, faClipboard, faSliders, faCoins, faEllipsisVertical, faEllipsis, faArrowUp, faArrowDown, faArrowLeft, faXmark, faInfoCircle, faBug, faCheckCircle, faArrowTurnUp, faArrowTurnDown, faPencil, faTags, faShareFromSquare, faRotate, faCode, faBan, faCircle, faPaperPlane, faBolt, faArrowUpFromBracket, faArrowRightToBracket, faPlus, faShieldHalved, faCloudArrowUp, faPaintbrush, faCopy, faBurst, faUserShield, faLock, faLockOpen, faTriangleExclamation, faDownload, faUpload, faRecycle, faListUl, faExpand, faFingerprint, faWandMagicSparkles, faCircleUser, faComment, faKey, faCircleNodes, faBullseye, faEyeSlash, faUpRightFromSquare, faShareNodes, faPaste, faKeyboard, faMoneyBill1, faGears, faTag, faBank, faChevronDown, faChevronUp, faCircleExclamation, faCircleQuestion, faEnvelope, faTwitter, faTelegramPlane, faDiscord, faGithub, faReddit, faCircleArrowUp, faCircleArrowDown, faGlobe, faCubes, faClock, faArrowRotateLeft, faHeartPulse, faSeedling, faChevronLeft, faArrowRightArrowLeft } interface IconProps extends TouchableOpacityProps { diff --git a/src/navigation/TabsNavigator.tsx b/src/navigation/TabsNavigator.tsx index d6253edb..f5d86293 100644 --- a/src/navigation/TabsNavigator.tsx +++ b/src/navigation/TabsNavigator.tsx @@ -12,6 +12,7 @@ import { SendScreen, ScanScreen, LightningPayScreen, + NfcPayScreen, ContactsScreen, PictureScreen, ContactDetailScreen, @@ -68,7 +69,8 @@ const WalletStack = createNativeStackNavigator({ Receive: ReceiveScreen, Send: SendScreen, Scan: ScanScreen, - LightningPay: LightningPayScreen, + LightningPay: LightningPayScreen, + NfcPay: NfcPayScreen, Transfer: TransferScreen, Topup: TopupScreen, CashuPaymentRequest: CashuPaymentRequestScreen, diff --git a/src/screens/LightningPayScreen.tsx b/src/screens/LightningPayScreen.tsx index 51af01f8..f75e0823 100644 --- a/src/screens/LightningPayScreen.tsx +++ b/src/screens/LightningPayScreen.tsx @@ -165,7 +165,7 @@ export const LightningPayScreen = function LightningPayScreen({ route }: Props) const scanIcon = useThemeColor('text') const inputBg = useThemeColor('background') const inputText = useThemeColor('text') - const contactIcon = useThemeColor('headerTitle') + const contactIcon = useThemeColor('textDim') const headerBg = useThemeColor('header') const headerTitle = useThemeColor('headerTitle') diff --git a/src/screens/NfcPayScreen.tsx b/src/screens/NfcPayScreen.tsx new file mode 100644 index 00000000..115a6bc2 --- /dev/null +++ b/src/screens/NfcPayScreen.tsx @@ -0,0 +1,428 @@ +import React, { FC, useState, useEffect, useRef } from 'react' +import { + ViewStyle, + View, + TextStyle, + Alert, + Platform, + DeviceEventEmitter, +} from 'react-native' +import { PaymentRequest as CashuPaymentRequest, PaymentRequestTransport, PaymentRequestTransportType, decodePaymentRequest, getDecodedToken } from '@cashu/cashu-ts' +import NfcManager, { NfcTech, Ndef, NfcEvents } from 'react-native-nfc-manager' +import { colors, spacing, typography, useThemeColor } from '../theme' +import EventEmitter from '../utils/eventEmitter' +import { log } from '../services/logService' +import { IncomingDataType, IncomingParser } from '../services/incomingParser' +import AppError, { Err } from '../utils/AppError' +import { Button, Card, ErrorModal, Icon, InfoModal, ListItem, ScanIcon, Screen, Text } from '../components' +import { infoMessage } from '../utils/utils' +import { SvgXml } from 'react-native-svg' +import { MintUnit, MintUnits } from '../services/wallet/currency' +import { useStores } from '../models' +import { MintHeader } from './Mints/MintHeader' +import { verticalScale } from '@gocodingnow/rn-size-matters' +import useIsInternetReachable from '../utils/useIsInternetReachable' +import { translate } from '../i18n' +import { StaticScreenProps, useNavigation } from '@react-navigation/native' +import { observer } from 'mobx-react-lite' +import { Mint, MintBalance } from '../models/Mint' +import { TransactionTaskResult } from '../services' +import { SendOption } from './SendScreen' +import { TransferOption } from './TransferScreen' +import { Transaction, TransactionStatus } from '../models/Transaction' +import { Proof } from '../models/Proof' +import { SEND_TASK } from '../services/wallet/sendTask' + +type Props = StaticScreenProps<{ + unit: MintUnit +}> + +export const NfcPayScreen = observer(function NfcPayScreen({ route }: Props) { + const navigation = useNavigation() + const { mintsStore, walletStore, proofsStore } = useStores() + const unitRef = useRef('sat') + const isInternetReachable = useIsInternetReachable() + + const [paymentOption, setPaymentOption] = useState(TransferOption.PASTE_OR_SCAN_INVOICE) + const [encodedTokenToSend, setEncodedTokenToSend] = useState() + const [encodedCashuPaymentRequest, setEncodedCashuPaymentRequest] = useState() + const [decodedCashuPaymentRequest, setDecodedCashuPaymentRequest] = useState() + const [memo, setMemo] = useState('') + const [availableMintBalances, setAvailableMintBalances] = useState([]) + const [mintBalanceToSendFrom, setMintBalanceToSendFrom] = useState() + const [selectedProofs, setSelectedProofs] = useState([]) + const [transactionStatus, setTransactionStatus] = useState() + const [transaction, setTransaction] = useState() + const [transactionId, setTransactionId] = useState() + const [resultModalInfo, setResultModalInfo] = useState<{status: TransactionStatus, title?: string, message: string} | undefined>() + const [isLoading, setIsLoading] = useState(false) + + const [isMintSelectorVisible, setIsMintSelectorVisible] = useState(false) + const [isOfflineSend, setIsOfflineSend] = useState(false) + const [isSendTaskSentToQueue, setIsSendTaskSentToQueue] = useState(false) + const [isResultModalVisible, setIsResultModalVisible] = useState(false) + // NFC state + const [isNfcEnabled, setIsNfcEnabled] = useState(false) + const [isNfcSupported, setIsNfcSupported] = useState(false) + const [isProcessing, setIsProcessing] = useState(false) + const [nfcInfo, setNfcInfo] = useState() + const [readNfcData, setReadNfcData] = useState() + const [mint, setMint] = useState() + + const [error, setError] = useState() + const [info, setInfo] = useState('') + + // Start NFC only when this screen is visible + useEffect(() => { + const initNfc = async () => { + try { + const supported = await NfcManager.isSupported() + setIsNfcSupported(supported) + if (!supported) { + setNfcInfo('NFC is not supported on this device') + return + } + + await NfcManager.start() + + const enabled = await NfcManager.isEnabled() + if (!enabled) { + setNfcInfo('Please enable NFC in your device settings') + return + } + + setIsNfcEnabled(true) + + // Start listening for tags immediately + startNfcSession() + } catch (e: any) { + log.error('Failed to initialize NFC', e.message) + setNfcInfo('Failed to initialize NFC: ' + e.message) + // setError(new AppError(Err.NFC_ERROR, 'Failed to initialize NFC', e.message)) + } + } + + initNfc() + + return () => { + NfcManager.cancelTechnologyRequest().catch(() => {}) + NfcManager.setEventListener(NfcEvents.DiscoverTag, null) + } + }, []) + + // Auto-restart session when screen comes into focus + useEffect(() => { + const unsubscribe = navigation.addListener('focus', () => { + if (isNfcEnabled && !isProcessing) { + startNfcSession() + } + }) + return unsubscribe + }, [navigation, isNfcEnabled, isProcessing]) + + + useEffect(() => { + const handleSendTaskResult = async (result: TransactionTaskResult) => { + log.trace('handleSendTaskResult event handler triggered') + + const {transaction} = result + + setTransactionStatus(transaction?.status) + setTransaction(transaction) + setTransactionId(transaction?.id) + + if (result.encodedTokenToSend) { + setEncodedTokenToSend(result.encodedTokenToSend) + } + + if(paymentOption === SendOption.PAY_CASHU_PAYMENT_REQUEST) { + if(decodedCashuPaymentRequest && decodedCashuPaymentRequest.id && transaction) { + + transaction.update({ + paymentId: decodedCashuPaymentRequest.id, + paymentRequest: encodedCashuPaymentRequest, + }) + } + } + + if (result.error) { + setResultModalInfo({ + status: result.transaction?.status as TransactionStatus, + title: result.error.params?.message ? result.error.message : 'Payment failed', + message: result.error.params?.message || result.error.message, + }) + setIsResultModalVisible(true) + return + } + } + + // Subscribe to the 'sendCompleted' event + if(isSendTaskSentToQueue) { + EventEmitter.on(`ev_${SEND_TASK}_result`, handleSendTaskResult) + } + + // Unsubscribe from the 'sendCompleted' event on component unmount + return () => { + EventEmitter.off(`ev_${SEND_TASK}_result`, handleSendTaskResult) + } + }, [isSendTaskSentToQueue]) + + + const startNfcSession = async () => { + try { + await NfcManager.requestTechnology(NfcTech.Ndef, { + alertMessage: 'Hold phone near the device...', + //invalidateAfterFirstRead: false, + }) + + // Listen for tag discovery + NfcManager.setEventListener(NfcEvents.DiscoverTag, async (tag: any) => { + if (isProcessing) return + setIsProcessing(true) + setNfcInfo('Reading payment request...') + + try { + const ndefMessage = tag.ndefMessage?.[0] + if (!ndefMessage) { + throw new AppError(Err.VALIDATION_ERROR, 'No NDEF message found') + } + + let payload: string + + // Handle both Text and URI records + if (ndefMessage.type === Ndef.RTD_TEXT) { + const decoded = Ndef.text.decodePayload(ndefMessage.payload) + payload = decoded + } else if (ndefMessage.type === Ndef.RTD_URI) { + const decoded = Ndef.uri.decodePayload(ndefMessage.payload) + payload = decoded + } else { + payload = new TextDecoder().decode(ndefMessage.payload) + } + + log.trace('NFC tag read', { payload }) + setReadNfcData(payload) + await handlePaymentRequest(payload) + + } catch (e: any) { + handleError(e) + } finally { + setIsProcessing(false) + } + }) + } catch (ex: any) { + if (ex.message !== 'cancelled') { + setInfo('Tap terminal to pay') + } + } + } + + const handlePaymentRequest = async (data: string) => { + try { + Alert.alert(data) + const result = IncomingParser.findAndExtract(data) + + if (result.type === IncomingDataType.CASHU_PAYMENT_REQUEST) { + const encodedCashuPaymentRequest = result.encoded + const pr: CashuPaymentRequest = decodePaymentRequest(encodedCashuPaymentRequest) + + log.trace('[handlePaymentRequest] decoded payment request', {pr}) + + //setDecodedCashuPaymentRequest(pr) + //setEncodedCashuPaymentRequest(encodedCashuPaymentRequest) + + if(pr.unit && !MintUnits.includes(pr.unit as MintUnit)) { + throw new AppError(Err.VALIDATION_ERROR, `Wallet does not support ${pr.unit} unit.`) + } + + if(pr.unit) { + unitRef.current = pr.unit as MintUnit + } + + /*if (pr.description && pr.description.length > 0) { + setMemo(pr.description) + setIsCashuPrWithDesc(true) + }*/ + + if (!pr.amount || pr.amount <= 0) { + throw new AppError(Err.VALIDATION_ERROR, 'Cashu payment request does not specify an amount to pay.') + } + + /* + setAmountToSend(`${numbro(pr.amount / getCurrency(unitRef.current).precision) + .format({ + thousandSeparated: true, + mantissa: getCurrency(unitRef.current).mantissa + })}`) + + */ + + let availableBalances: MintBalance[] = [] + + if (pr.mints && pr.mints.length > 0) { + + for (const mint of pr.mints) { + if (mintsStore.mintExists(mint)) { + const mintBalance = proofsStore.getMintBalance(mint) + availableBalances.push(mintBalance!) + } + } + + if (availableBalances.length === 0) { + throw new AppError(Err.NOTFOUND_ERROR, 'Wallet does not have any of the mints accepted by Cashu payment request.', {mints: pr.mints}) + } + + const withEnoughBalance = availableBalances.filter(balance => { + const unitBalance = balance.balances[unitRef.current] + + if(unitBalance && unitBalance >= pr.amount!) { + return balance + } + + return null + }) + + if(withEnoughBalance.length === 0) { + throw new AppError(Err.NOTFOUND_ERROR, `Wallet does not have enough balance to fulfill Cashu payment request of ${pr.amount} ${unitRef.current}.`, {availableBalances, amount: pr.amount, unit: unitRef.current}) + } + + availableBalances = withEnoughBalance + + // setAvailableMintBalances(withEnoughBalance) + // setMintBalanceToSendFrom(withEnoughBalance[0]) + + } else { + availableBalances = proofsStore.getMintBalancesWithEnoughBalance(pr.amount, unitRef.current) + } + + + + //setIsMintSelectorVisible(true) + } + + if (result.type === IncomingDataType.INVOICE) { + Alert.alert(result.toString()) + } + + } catch (e: any) { + handleError(e) + } + } + + const writeTokenToTag = async (token: string) => { + try { + // Re-request tech to write back + await NfcManager.requestTechnology(NfcTech.Ndef) + + const textRecord = Ndef.textRecord('en', token) + const bytes = Ndef.encodeMessage([textRecord]) + + await NfcManager.ndefHandler.writeNdefMessage(bytes) + setInfo('Payment sent!') + } catch (e: any) { + throw new AppError(Err.NFC_ERROR, 'Failed to send token', e.message) + } finally { + NfcManager.cancelTechnologyRequest().catch(() => {}) + } + } + + const handleError = (e: AppError) => { + setError(e) + setInfo('') + setIsProcessing(false) + } + + const gotoScan = () => { + navigation.navigate('Scan', { unit: unitRef.current }) + } + + // Theme + const hintText = useThemeColor('textDim') + const headerBg = useThemeColor('header') + const headerTitle = useThemeColor('headerTitle') + const scanIcon = useThemeColor('text') + const scanButtonColor = useThemeColor('card') + + return ( + + + + + + + + + + + + } + /> + + + +