From b80c37c99fcaf37a781e4f7beac19669199c70a3 Mon Sep 17 00:00:00 2001 From: Dylan Audius Date: Tue, 23 Jun 2026 18:38:19 -0700 Subject: [PATCH] feat(mobile): migrate push notifications from Wix to notifee + firebase Replace the abandoned react-native-notifications (Wix) with @notifee/react-native + @react-native-firebase/messaging + app. - Rewrite notifications.ts PushNotifications singleton: - remote token via messaging().getToken() / onTokenRefresh() - permissions via messaging().requestPermission() (Android 13+ still requests POST_NOTIFICATIONS via react-native-permissions) - opened-handling via onNotificationOpenedApp() + getInitialNotification() - badge counts via notifee.setBadgeCount() - local notification cancel via notifee.cancelNotification() - FCM data values arrive as strings on both platforms, so the Android string-parsing is now applied unconditionally - iOS AppDelegate.mm: drop Wix RNNotifications, add #import and [FIRApp configure] in didFinishLaunchingWithOptions - Android MainApplication.kt: remove dead Wix RNNotificationsPackage import - Podfile: opt GoogleUtilities/nanopb into modular headers so the Firebase Swift pods integrate as static libraries - pod install integrates Firebase 12.10 / RNFB 24.1 / RNNotifee 9.1 (GoogleService-Info.plist and google-services.json already present) Co-Authored-By: Claude Opus 4.8 (1M context) --- package-lock.json | 861 +++++++++++++++++- .../java/co/audius/app/MainApplication.kt | 1 - .../project.pbxproj | 14 + .../ios/AudiusReactNative/AppDelegate.mm | 16 +- packages/mobile/ios/Podfile | 8 + packages/mobile/ios/Podfile.lock | 180 +++- packages/mobile/ios/PrivacyInfo.xcprivacy | 14 +- packages/mobile/package.json | 4 +- packages/mobile/src/notifications.ts | 152 ++-- 9 files changed, 1141 insertions(+), 109 deletions(-) diff --git a/package-lock.json b/package-lock.json index 30e0fb6c557..5b71dd22583 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12519,6 +12519,635 @@ "node": ">=14" } }, + "node_modules/@firebase/ai": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@firebase/ai/-/ai-2.13.0.tgz", + "integrity": "sha512-nJJDQKqjAcbkZdZGT/5WTVLrGZ+pYhWbwKC90nNzmvtoRTtnOJaNS34fhKSHQeB9SALgD2kxuWT5I4AkytdZ/Q==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.4", + "@firebase/component": "0.7.3", + "@firebase/logger": "0.5.1", + "@firebase/util": "1.15.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/analytics": { + "version": "0.10.22", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.22.tgz", + "integrity": "sha512-8BSaq/QRGU1+xyi8L2PTLTJU7MH9aMA72RQdIxrbhWFauOZY9OXo8f2YDN/972xA8d588tlnNVEQ2Mo69pT9Ow==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.3", + "@firebase/installations": "0.6.22", + "@firebase/logger": "0.5.1", + "@firebase/util": "1.15.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/analytics-compat": { + "version": "0.2.28", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.28.tgz", + "integrity": "sha512-lIAlqUUbBu93FJMlQfslryQtBwwzdzvp23ePC6FNgymXk6Ook5v4Uvc0vdutvoIeqmyA3LfP0ZeRFK8+11kOOQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/analytics": "0.10.22", + "@firebase/analytics-types": "0.8.4", + "@firebase/component": "0.7.3", + "@firebase/util": "1.15.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/analytics-types": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.4.tgz", + "integrity": "sha512-zQ+XTgkwH6CY/eUSHJRP7e4LxM30RCxlCmob5sy2axs25GE3Ny0XdgpDscMTHHQIGqWkxPXad4w2Mw9sCgT8zQ==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/app": { + "version": "0.14.13", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.14.13.tgz", + "integrity": "sha512-H89Jeyp31+EZk9GPu6vaeL9mEmoXgM3nASB7UPBYYS/lqAks21mO1BU1dF8NbsVTL6tgGZkGUtiGJgxtDiwHkw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.3", + "@firebase/logger": "0.5.1", + "@firebase/util": "1.15.1", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@firebase/app-check": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.11.4.tgz", + "integrity": "sha512-G8EsbVJV9gSfoibx0dNoNOUrvr+PkL7J//+W/BST/oUassimkZeq9bjj3bKkB0pn4og5GMQ9qs7FefwP00kkgg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.3", + "@firebase/logger": "0.5.1", + "@firebase/util": "1.15.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/app-check-compat": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.4.4.tgz", + "integrity": "sha512-9iP0MvmaVagulNXmrca96U3tqNAI3j98wsC1z7rj62nnOTajlrHM//jjB9VoHqRw6/islMskp6RsKnM7vhLDqA==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check": "0.11.4", + "@firebase/app-check-types": "0.5.4", + "@firebase/component": "0.7.3", + "@firebase/logger": "0.5.1", + "@firebase/util": "1.15.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.4.tgz", + "integrity": "sha512-zz3i6e13B8BfWiLy8MABtTh8aGIACgKbf9UVnyHcWs+yQzJXgQcl8A46b0zfaiJHdQ+niF0ouAfcpuf+3LMPQg==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/app-check-types": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.4.tgz", + "integrity": "sha512-xV7JsIyzVr15aA7f3Pi0rB9gdBuVubs89FGA8VkRYA4g0l78poADgdfrScgf7NndSg9mm7cR7PJyY0+t22KaGw==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/app-compat": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.5.13.tgz", + "integrity": "sha512-pn3FvXwUR34kWPccDQfCKsNZcM2wD1OS+J1jeEgzM1ZNXoxR2NaF6e5DjDuRrnTwR6LN2XQQt0IqE6yKmgpCQg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app": "0.14.13", + "@firebase/component": "0.7.3", + "@firebase/logger": "0.5.1", + "@firebase/util": "1.15.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@firebase/app-types": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.5.tgz", + "integrity": "sha512-YevqTjvo7Iujsa9Dwowmd6dSoElhzmD63ZSrq6bzjvQ6POjYgNjOFHLmNIgJs48eNO093NCERibuFnxbfOvU7A==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/logger": "0.5.1" + } + }, + "node_modules/@firebase/app/node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", + "license": "ISC" + }, + "node_modules/@firebase/auth": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.13.2.tgz", + "integrity": "sha512-B4w0iS7MxRg28oIh2fJFTE6cM0lYdBrW19eHpc42jqEcloUjlYyVrpPqZvqA4+v9KFEVSKEs2SfWyta7hbzkJQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.3", + "@firebase/logger": "0.5.1", + "@firebase/util": "1.15.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@react-native-async-storage/async-storage": "^2.2.0 || ^3.0.0" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@firebase/auth-compat": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.6.7.tgz", + "integrity": "sha512-XgKnOgY1Siq7gylAmLkYtHAlRxNeWEAspH+nO3gJZJnfHqoTHbr9UjJ3nHNFALYXV5CfpQlyPROyB2ztySBHBQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/auth": "1.13.2", + "@firebase/auth-types": "0.13.1", + "@firebase/component": "0.7.3", + "@firebase/util": "1.15.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.5.tgz", + "integrity": "sha512-1Li/YuBDBAXcKv7BzY4U28gontUmAaw53sYiqbaVOMCFb2lFKK/c3CGMUWqtwe7+TXrl3poWnTCL5umYBg85Eg==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/auth-types": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.13.1.tgz", + "integrity": "sha512-0c1Mnid0uMDfGJHeUS4zfvBa4/CedJXotGy/n/NZJnBjwiJawt0ZYU+wH2VAVLiRCEfG2ncCkAX3yd1/2nrB7g==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/component": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.7.3.tgz", + "integrity": "sha512-wFofIaa2879ogD/WvkjYXJxRmfnL0scen6ORgaC3na1FNOR9ASIUANQdhqQcmWu/h77/pVHY7ch5flewa5Bcew==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/util": "1.15.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@firebase/data-connect": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.7.1.tgz", + "integrity": "sha512-2LbUU8mmSA63HknxQMmWHjpzuNLBKflvVwQc2tpoVKg0biWleNEJX031ELks0vzFs+dDjOUkCJR72RP6mQHFOg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/auth-interop-types": "0.2.5", + "@firebase/component": "0.7.3", + "@firebase/logger": "0.5.1", + "@firebase/util": "1.15.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/database": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.1.3.tgz", + "integrity": "sha512-XwWCa+E4TvNGpGwXrycLRNfdogADwFcvuhyow6wDWma9W54roaQIhe+4PM0KiLsIftBdSCGI7OKCXrdSRHbIhw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.4", + "@firebase/auth-interop-types": "0.2.5", + "@firebase/component": "0.7.3", + "@firebase/logger": "0.5.1", + "@firebase/util": "1.15.1", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.1.4.tgz", + "integrity": "sha512-3pK35F1MAgmqFJQlf2nhQl44vtAXQO1uaCaQOEUI9kCRtLFqi7N+QRKR7lFZPg+xIZIyubgxQaxY69YgfZRZWg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.3", + "@firebase/database": "1.1.3", + "@firebase/database-types": "1.0.20", + "@firebase/logger": "0.5.1", + "@firebase/util": "1.15.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.20.tgz", + "integrity": "sha512-kegbOk/w8iU64pr0q6k2ItyNGjnQBMHFhwS7ohdWI4W+pc0/zhhdGXTdFj6X1oxItRjPoYOsSQmERgBkn/ihxw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-types": "0.9.5", + "@firebase/util": "1.15.1" + } + }, + "node_modules/@firebase/firestore": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.15.0.tgz", + "integrity": "sha512-Fj9osqYkz2Rqr7kW3/A8BRd8CyJ7yA5K8YjhihRdyJWbL+FsELVcR6DpoCplrp1IyU+xeGgTubo1UOySXpY+EA==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.3", + "@firebase/logger": "0.5.1", + "@firebase/util": "1.15.1", + "@firebase/webchannel-wrapper": "1.0.6", + "@grpc/grpc-js": "~1.9.0", + "@grpc/proto-loader": "^0.7.8", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/firestore-compat": { + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.4.10.tgz", + "integrity": "sha512-yMP3FADDjikdrQv4YmvL4EkIny6Hw+N+a2O5T40rlHiniyMpRPxgYkKiFOvMZnsqKLqBVnKqCAElC0pa/IZtdw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.3", + "@firebase/firestore": "4.15.0", + "@firebase/firestore-types": "3.0.4", + "@firebase/util": "1.15.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/firestore-types": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.4.tgz", + "integrity": "sha512-jGn+JSS4X9zZsrfu7Yw66v5YRdOLD1oyQh4USR0xWl4CUqV/DA6bNIXRPpxH/cUl3iVTNiP6MN7g+EL42A4qfA==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/functions": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.13.5.tgz", + "integrity": "sha512-bWCx713f4kE/uFV7gdFOLBS7lDoiZj48MRkbAqe35gkXcCeWF4QjRNO07Jhmve7EJIoQOBczL29y2r8VRuN1kw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.4", + "@firebase/auth-interop-types": "0.2.5", + "@firebase/component": "0.7.3", + "@firebase/messaging-interop-types": "0.2.5", + "@firebase/util": "1.15.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/functions-compat": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.4.5.tgz", + "integrity": "sha512-10qlUXGY25G5/1g9UihqksPp2po+ZqSE7LEizsrdUP7vrTmkysXxGSZCDyojSEp6mQe/ecRDdDDI+z4XRdb4wQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.3", + "@firebase/functions": "0.13.5", + "@firebase/functions-types": "0.6.4", + "@firebase/util": "1.15.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/functions-types": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.4.tgz", + "integrity": "sha512-zV6kgqtduR4rUAdC/ilS7kmb93XD7bEZoJDlVBZqlOw2uGGGCNBQBuleww2rr0Ulr3L9o2TDjumEt68/l1f9DQ==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/installations": { + "version": "0.6.22", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.22.tgz", + "integrity": "sha512-ef6nn3GGQTdReCfotRMG77PJZu8CqEbiK5pEoBnM0gTu/Z9v0i/az2p3HABsa/1beQmmyh1OsOjf7P5+pgwdZw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.3", + "@firebase/util": "1.15.1", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/installations-compat": { + "version": "0.2.22", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.22.tgz", + "integrity": "sha512-C/zpAuTP5S9OgKSPvXRupw3hoY/JZSlA1wFjD/Sb7LIQE0FNbcMdO8Y4KXVEkjVzma/DDDDIAzxEXqKMAzc88w==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.3", + "@firebase/installations": "0.6.22", + "@firebase/installations-types": "0.5.4", + "@firebase/util": "1.15.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/installations-types": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.4.tgz", + "integrity": "sha512-U2eFapdHwjb43Vx9o+Pmj4dFfvcHEK1IirEFLqMtWrTHvmdrS3gBpBD1kmJk/9HjsOtoHZxJ2Paoe79e+L1ZPg==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/installations/node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", + "license": "ISC" + }, + "node_modules/@firebase/logger": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.5.1.tgz", + "integrity": "sha512-vZKLsqE1ABOy8OjQiE7cUTFn4gvaqlk88yp8N94Pk/sDpq61YqZGqmVFZTvOyflTwuYFcWirBdYGoJgbDaXKYQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@firebase/messaging": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.13.0.tgz", + "integrity": "sha512-GZoo0uGRvEbszo83xcgbjJp4FpkmBEr4l8Z4hi8gl+P1Spn/MTK3HapanMzSX4yUHuTEiF5hasWRxOaz+o5sxQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.3", + "@firebase/installations": "0.6.22", + "@firebase/messaging-interop-types": "0.2.5", + "@firebase/util": "1.15.1", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/messaging-compat": { + "version": "0.2.27", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.27.tgz", + "integrity": "sha512-JNOiu1PPgdHzEPEtoFiNxQuu0x9bm4bfETSQCpGfcTlgWkhlSK7uh7nlsjC10TQLUNgYetLmuutaYTh8aeYLVA==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.3", + "@firebase/messaging": "0.13.0", + "@firebase/util": "1.15.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/messaging-interop-types": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.5.tgz", + "integrity": "sha512-tUEKnaAP2Y/MNIqgnriPpV6e5l13Vs/+p2yrd6NGlncPJT9O3a8muYZtdnWe+IJ4fgKLHJVC79n/asxk/N5Msw==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/messaging/node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", + "license": "ISC" + }, + "node_modules/@firebase/performance": { + "version": "0.7.12", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.7.12.tgz", + "integrity": "sha512-fe7nV8teUU3OBHlMUZ9Lw4gLhCW2k4m5Uc3pfWGV+fl8uwJQBGp9Q3lqsJ+HSrFu3Q2pJyLAgrClPGSKyDeYgQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.3", + "@firebase/installations": "0.6.22", + "@firebase/logger": "0.5.1", + "@firebase/util": "1.15.1", + "tslib": "^2.1.0", + "web-vitals": "^4.2.4" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/performance-compat": { + "version": "0.2.25", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.25.tgz", + "integrity": "sha512-q6NjTXpIPoFuUmCmMN/maCdTgzT6aExs9xZo+PxfVLj6uLVGvpyAD6XWjmcrb7jChsFBYbq7E5dyNDF7Zhy9kA==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.3", + "@firebase/logger": "0.5.1", + "@firebase/performance": "0.7.12", + "@firebase/performance-types": "0.2.4", + "@firebase/util": "1.15.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/performance-types": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.4.tgz", + "integrity": "sha512-kJSEk7b0uhpcPRyL4SQ/GPujLqk52XNKcXlnsKDbWGAb9vugcLvOU3u6zfEdwd+d8hWJb5S5ZizV1JFFI0nkKg==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/remote-config": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.8.4.tgz", + "integrity": "sha512-lslywR5lGvHWTu4z/MPoYs3UwS3CKdeY+ELXY87087VsOpBpkD+9Orra23tA9GW683arPTDOM3CM6eKmtiOO3g==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.3", + "@firebase/installations": "0.6.22", + "@firebase/logger": "0.5.1", + "@firebase/util": "1.15.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/remote-config-compat": { + "version": "0.2.25", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.25.tgz", + "integrity": "sha512-FnA5S4IxFJAAFrCnYzWlO0FCaizlYdqhe42ygFMA+wE/mUP+w36iXzHyKj1OO1A+2gyMFjeRHyg8HhkJ6c5vRA==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.3", + "@firebase/logger": "0.5.1", + "@firebase/remote-config": "0.8.4", + "@firebase/remote-config-types": "0.5.1", + "@firebase/util": "1.15.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/remote-config-types": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.5.1.tgz", + "integrity": "sha512-cX/1LT6KQwkXzck2eSzeKnuvXZCyr8qaPpDcikoJs7jmI+oBOXixpDLeDtWj1U6GNMkIoXrEDNoyT2Ypcyp5/A==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/storage": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.14.3.tgz", + "integrity": "sha512-YX4/YL6P6/fufSSeGnVhjWddcIXbFq2cWIhMKFTZo1E/Rtcl2mJj/BYUQTwJfcE1Tl8un1FOya4L05jcSLN/Eg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.3", + "@firebase/util": "1.15.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/storage-compat": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.4.3.tgz", + "integrity": "sha512-gruVqjtUGX8tEoeNbaWXZm0Zfcfcb7fvmDmBxV8yPAbWvExRnZYLO2+qw9idxNE7BvPXt5csyjSYHy//dAizxw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.7.3", + "@firebase/storage": "0.14.3", + "@firebase/storage-types": "0.8.4", + "@firebase/util": "1.15.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/storage-types": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.4.tgz", + "integrity": "sha512-BT7cwxJOx8SWwlQfrlC+bD/Sk3Cw+1odCi8UZNFNWTVZoPsBnA5W+mqtZzVnvsdJpXCFGSGQ7R7vOR6dtM/BRA==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/util": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.15.1.tgz", + "integrity": "sha512-LUdM4Wg7YM9Pq/49nGYySJA0CSQEKnGffFzWV8+6gXN7mGxn+FL1IqvFbuZUtAQcfZgHYDwCE1wwlK7rB7gl2g==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@firebase/webchannel-wrapper": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.6.tgz", + "integrity": "sha512-Vr/Mqu79dMwGRAyGbJ4uN4+BtXB3/mRTdzetD1daWNeG8QaWuzhhbG77GltO5c0yYmYls8i250iX73624GJd7Q==", + "license": "Apache-2.0" + }, "node_modules/@floating-ui/core": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", @@ -12722,6 +13351,67 @@ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, + "node_modules/@grpc/grpc-js": { + "version": "1.9.16", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.16.tgz", + "integrity": "sha512-wE4Ut/olIzfKqp631XrG+wbF0v1vWFN4YL9FyXC2LJiG33DsV7PLzURjrCvY/6je2ntdRkeLpPDluzSRGaVltQ==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@grpc/proto-loader/node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/@grpc/proto-loader/node_modules/protobufjs": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.2.tgz", + "integrity": "sha512-N9EiLovGEQOJSPF26Ij7qUGvahfEnq0eeYZ02aigIedkmz1qZSwjnP9SBITHJuF/6MYbIW4HDN8zdYjsjqJKXQ==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.2", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", + "@types/node": ">=13.7.0", + "long": "^5.3.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/@hapi/hoek": { "version": "9.3.0", "dev": true, @@ -17362,6 +18052,15 @@ "node": ">= 8" } }, + "node_modules/@notifee/react-native": { + "version": "9.1.8", + "resolved": "https://registry.npmjs.org/@notifee/react-native/-/react-native-9.1.8.tgz", + "integrity": "sha512-Az/dueoPerJsbbjRxu8a558wKY+gONUrfoy3Hs++5OqbeMsR0dYe6P+4oN6twrLFyzAhEA1tEoZRvQTFDRmvQg==", + "license": "Apache-2.0", + "peerDependencies": { + "react-native": "*" + } + }, "node_modules/@npmcli/config": { "version": "6.4.0", "dev": true, @@ -19351,19 +20050,24 @@ "license": "BSD-3-Clause" }, "node_modules/@protobufjs/codegen": { - "version": "2.0.4", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/fetch": { - "version": "1.1.0", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", "license": "BSD-3-Clause", "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" + "@protobufjs/aspromise": "^1.1.1" } }, "node_modules/@protobufjs/float": { @@ -19371,7 +20075,9 @@ "license": "BSD-3-Clause" }, "node_modules/@protobufjs/inquire": { - "version": "1.1.0", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.2.tgz", + "integrity": "sha512-pa0vFRuws4wkvaXKK1uXZMAwAX4/t8ANaJo45iw/oQHNQ9q5xUzwgFmVJGXiga2BeN+zpX7Vf9vmsiIa2J+MUw==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/path": { @@ -19383,7 +20089,9 @@ "license": "BSD-3-Clause" }, "node_modules/@protobufjs/utf8": { - "version": "1.1.0", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", + "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", "license": "BSD-3-Clause" }, "node_modules/@radix-ui/colors": { @@ -23148,6 +23856,40 @@ "react-native": "*" } }, + "node_modules/@react-native-firebase/app": { + "version": "24.1.1", + "resolved": "https://registry.npmjs.org/@react-native-firebase/app/-/app-24.1.1.tgz", + "integrity": "sha512-NKCUgBeilQvchHQeKwB6RuE3Ne2AZMlClXjJBVySkCAF2FNO73ML2brqrAyegRkuVswtqHsB6wAgypmP82WnnA==", + "license": "Apache-2.0", + "dependencies": { + "firebase": "12.14.0" + }, + "peerDependencies": { + "expo": ">=47.0.0", + "react": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "expo": { + "optional": true + } + } + }, + "node_modules/@react-native-firebase/messaging": { + "version": "24.1.1", + "resolved": "https://registry.npmjs.org/@react-native-firebase/messaging/-/messaging-24.1.1.tgz", + "integrity": "sha512-tMb75+4ehbQshlH+PifbxKbP+pyDgK/dfBIvlFJeDVEAbdaM2nVpMkVyV6s5WJ3WRPUt6v+hyU1P3krQPYhkig==", + "license": "Apache-2.0", + "peerDependencies": { + "@react-native-firebase/app": "24.1.1", + "expo": ">=47.0.0" + }, + "peerDependenciesMeta": { + "expo": { + "optional": true + } + } + }, "node_modules/@react-native/babel-plugin-codegen": { "version": "0.79.5", "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.79.5.tgz", @@ -60748,6 +61490,18 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/fb-watchman": { "version": "2.0.2", "license": "Apache-2.0", @@ -61212,6 +61966,42 @@ "node": ">=8" } }, + "node_modules/firebase": { + "version": "12.14.0", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-12.14.0.tgz", + "integrity": "sha512-aEZ/lniDR1hOCYpx/x/V8Nrrqq9pepKDNkqP/4WGZFC69gTv6F59Z4/54W/SUP4L/hFlrRNmWj35aweQq+IHow==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/ai": "2.13.0", + "@firebase/analytics": "0.10.22", + "@firebase/analytics-compat": "0.2.28", + "@firebase/app": "0.14.13", + "@firebase/app-check": "0.11.4", + "@firebase/app-check-compat": "0.4.4", + "@firebase/app-compat": "0.5.13", + "@firebase/app-types": "0.9.5", + "@firebase/auth": "1.13.2", + "@firebase/auth-compat": "0.6.7", + "@firebase/data-connect": "0.7.1", + "@firebase/database": "1.1.3", + "@firebase/database-compat": "2.1.4", + "@firebase/firestore": "4.15.0", + "@firebase/firestore-compat": "0.4.10", + "@firebase/functions": "0.13.5", + "@firebase/functions-compat": "0.4.5", + "@firebase/installations": "0.6.22", + "@firebase/installations-compat": "0.2.22", + "@firebase/messaging": "0.13.0", + "@firebase/messaging-compat": "0.2.27", + "@firebase/performance": "0.7.12", + "@firebase/performance-compat": "0.2.25", + "@firebase/remote-config": "0.8.4", + "@firebase/remote-config-compat": "0.2.25", + "@firebase/storage": "0.14.3", + "@firebase/storage-compat": "0.4.3", + "@firebase/util": "1.15.1" + } + }, "node_modules/fix-hmr": { "version": "1.0.2", "license": "MIT", @@ -65760,6 +66550,12 @@ "version": "1.0.0", "license": "ISC" }, + "node_modules/http-parser-js": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", + "license": "MIT" + }, "node_modules/http-proxy-agent": { "version": "5.0.0", "dev": true, @@ -71015,7 +71811,6 @@ }, "node_modules/lodash.camelcase": { "version": "4.3.0", - "dev": true, "license": "MIT" }, "node_modules/lodash.clonedeep": { @@ -101891,6 +102686,12 @@ "node": ">= 8" } }, + "node_modules/web-vitals": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", + "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==", + "license": "Apache-2.0" + }, "node_modules/web-worker": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.5.0.tgz", @@ -104522,6 +105323,29 @@ "node": ">=4.0.0" } }, + "node_modules/websocket-driver": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.5.tgz", + "integrity": "sha512-ZL2+3c7kMBdIRCMz6l8jQMHyGVxj+UL+xVk74Ombiciboca8rHa15L86B19E5oh1pL9Ii/uj54gtsIrZGMo6zA==", + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/websocket/node_modules/debug": { "version": "2.6.9", "license": "MIT", @@ -117513,6 +118337,7 @@ "@hcaptcha/react-native-hcaptcha": "1.6.0", "@noble/hashes": "^1.1.5", "@noble/secp256k1": "1.7.0", + "@notifee/react-native": "^9.1.8", "@optimizely/optimizely-sdk": "4.9.2", "@peculiar/webcrypto": "1.4.3", "@react-native-async-storage/async-storage": "^2.0.0", @@ -117523,6 +118348,8 @@ "@react-native-community/netinfo": "^11.3.3", "@react-native-community/slider": "4.5.0", "@react-native-documents/picker": "^10.1.2", + "@react-native-firebase/app": "^24.1.1", + "@react-native-firebase/messaging": "^24.1.1", "@react-native-masked-view/masked-view": "^0.3.2", "@react-navigation/bottom-tabs": "6.5.11", "@react-navigation/core": "^7.13.3", @@ -117587,7 +118414,6 @@ "react-native-markdown-display": "7.0.2", "react-native-modal": "^13.0.0", "react-native-modal-datetime-picker": "18.0.0", - "react-native-notifications": "5.1.0", "react-native-pager-view": "6.7.1", "react-native-permissions": "5.4.4", "react-native-qrcode-svg": "6.2.0", @@ -120335,6 +121161,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "packages/mobile/node_modules/react": { + "version": "19.1.8", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.8.tgz", + "integrity": "sha512-XzY1Xytszq73B9T+4Q8VYfUX9qTz54+AoeZ+cSYNSVAzgz4gxFMZ8Ngk4RlmE9yVWpiZ3JAtQeHBLOK6ZfvOnw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "packages/mobile/node_modules/react-native": { "version": "0.81.6", "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.81.6.tgz", @@ -120532,14 +121367,6 @@ "react-native": ">=0.65.0" } }, - "packages/mobile/node_modules/react-native-notifications": { - "version": "5.1.0", - "license": "MIT", - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, "packages/mobile/node_modules/react-native-qrcode-svg": { "version": "6.2.0", "license": "MIT", diff --git a/packages/mobile/android/app/src/main/java/co/audius/app/MainApplication.kt b/packages/mobile/android/app/src/main/java/co/audius/app/MainApplication.kt index 125d8c32c23..e635ec74134 100644 --- a/packages/mobile/android/app/src/main/java/co/audius/app/MainApplication.kt +++ b/packages/mobile/android/app/src/main/java/co/audius/app/MainApplication.kt @@ -12,7 +12,6 @@ import com.facebook.react.defaults.DefaultReactNativeHost import com.facebook.react.soloader.OpenSourceMergedSoMapping import com.facebook.soloader.SoLoader import com.microsoft.codepush.react.CodePush -import com.wix.reactnativenotifications.RNNotificationsPackage class MainApplication : Application(), ReactApplication { diff --git a/packages/mobile/ios/AudiusReactNative.xcodeproj/project.pbxproj b/packages/mobile/ios/AudiusReactNative.xcodeproj/project.pbxproj index 2cb7d38b73c..bb7932d7e44 100644 --- a/packages/mobile/ios/AudiusReactNative.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/AudiusReactNative.xcodeproj/project.pbxproj @@ -256,6 +256,7 @@ CBD3B687421F14165031EAF0 /* [CP] Embed Pods Frameworks */, B73C21C532E1201FEFBAECDA /* [CP] Copy Pods Resources */, 23A43AB82E37E73000CDCDF2 /* Embed Foundation Extensions */, + CB6D10CC025B9D671A7354C5 /* [CP-User] [RNFB] Core Configuration */, ); buildRules = ( ); @@ -429,6 +430,19 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AudiusReactNative/Pods-AudiusReactNative-resources.sh\"\n"; showEnvVarsInLog = 0; }; + CB6D10CC025B9D671A7354C5 /* [CP-User] [RNFB] Core Configuration */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)", + ); + 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 \"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"; + }; CBD3B687421F14165031EAF0 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; diff --git a/packages/mobile/ios/AudiusReactNative/AppDelegate.mm b/packages/mobile/ios/AudiusReactNative/AppDelegate.mm index 35e9d17ec82..24d9cb55262 100644 --- a/packages/mobile/ios/AudiusReactNative/AppDelegate.mm +++ b/packages/mobile/ios/AudiusReactNative/AppDelegate.mm @@ -8,7 +8,7 @@ #import #import #import -#import "RNNotifications.h" +#import #import @implementation AppDelegate @@ -35,7 +35,7 @@ - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserAct - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [RNNotifications startMonitorNotifications]; + [FIRApp configure]; NSString *receiverAppID = @"222B31C8"; GCKDiscoveryCriteria *criteria = [[GCKDiscoveryCriteria alloc] initWithApplicationID:receiverAppID]; GCKCastOptions* options = [[GCKCastOptions alloc] initWithDiscoveryCriteria:criteria]; @@ -78,16 +78,4 @@ - (NSURL *)bundleURL #endif } -- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { - [RNNotifications didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; -} - -- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { - [RNNotifications didFailToRegisterForRemoteNotificationsWithError:error]; -} - -- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler { - [RNNotifications didReceiveBackgroundNotification:userInfo withCompletionHandler:completionHandler]; -} - @end diff --git a/packages/mobile/ios/Podfile b/packages/mobile/ios/Podfile index d213e93e2ed..296dbcbfb86 100644 --- a/packages/mobile/ios/Podfile +++ b/packages/mobile/ios/Podfile @@ -36,6 +36,14 @@ target 'AudiusReactNative' do pod 'nSure' + # Firebase (via @react-native-firebase) ships Swift pods (FirebaseCoreInternal, + # etc.) that depend on these C/Obj-C pods, which don't define module maps. The + # project links pods statically by default, and Swift static pods can't import + # non-modular dependencies — so opt these into modular headers. See the + # @react-native-firebase iOS "non-modular" troubleshooting note. + pod 'GoogleUtilities', :modular_headers => true + pod 'nanopb', :modular_headers => true + config = use_native_modules! use_react_native!( diff --git a/packages/mobile/ios/Podfile.lock b/packages/mobile/ios/Podfile.lock index 1e35219bca6..b43e1e94a7b 100644 --- a/packages/mobile/ios/Podfile.lock +++ b/packages/mobile/ios/Podfile.lock @@ -43,9 +43,79 @@ PODS: - ffmpeg-kit-react-native/full-gpl (6.0.2): - ffmpeg-kit-ios-full-gpl (= 6.0) - React-Core + - Firebase/CoreOnly (12.10.0): + - FirebaseCore (~> 12.10.0) + - Firebase/Messaging (12.10.0): + - Firebase/CoreOnly + - FirebaseMessaging (~> 12.10.0) + - FirebaseCore (12.10.0): + - FirebaseCoreInternal (~> 12.10.0) + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/Logger (~> 8.1) + - FirebaseCoreExtension (12.10.0): + - FirebaseCore (~> 12.10.0) + - FirebaseCoreInternal (12.10.0): + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - FirebaseInstallations (12.10.0): + - FirebaseCore (~> 12.10.0) + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/UserDefaults (~> 8.1) + - PromisesObjC (~> 2.4) + - FirebaseMessaging (12.10.0): + - FirebaseCore (~> 12.10.0) + - FirebaseInstallations (~> 12.10.0) + - GoogleDataTransport (~> 10.1) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/Reachability (~> 8.1) + - GoogleUtilities/UserDefaults (~> 8.1) + - nanopb (~> 3.30910.0) - fmt (11.0.2) - glog (0.3.5) - google-cast-sdk-dynamic-xcframework-no-bluetooth (4.8.0) + - GoogleDataTransport (10.1.0): + - nanopb (~> 3.30910.0) + - PromisesObjC (~> 2.4) + - GoogleUtilities (8.1.1): + - GoogleUtilities/AppDelegateSwizzler (= 8.1.1) + - GoogleUtilities/Environment (= 8.1.1) + - GoogleUtilities/Logger (= 8.1.1) + - GoogleUtilities/MethodSwizzler (= 8.1.1) + - GoogleUtilities/Network (= 8.1.1) + - "GoogleUtilities/NSData+zlib (= 8.1.1)" + - GoogleUtilities/Privacy (= 8.1.1) + - GoogleUtilities/Reachability (= 8.1.1) + - GoogleUtilities/SwizzlerTestHelpers (= 8.1.1) + - GoogleUtilities/UserDefaults (= 8.1.1) + - GoogleUtilities/AppDelegateSwizzler (8.1.1): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Privacy + - GoogleUtilities/Environment (8.1.1): + - GoogleUtilities/Privacy + - GoogleUtilities/Logger (8.1.1): + - GoogleUtilities/Environment + - GoogleUtilities/Privacy + - GoogleUtilities/MethodSwizzler (8.1.1): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - GoogleUtilities/Network (8.1.1): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Privacy + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (8.1.1)": + - GoogleUtilities/Privacy + - GoogleUtilities/Privacy (8.1.1) + - GoogleUtilities/Reachability (8.1.1): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - GoogleUtilities/SwizzlerTestHelpers (8.1.1): + - GoogleUtilities/MethodSwizzler + - GoogleUtilities/UserDefaults (8.1.1): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy - hermes-engine (0.81.6): - hermes-engine/Pre-built (= 0.81.6) - hermes-engine/Pre-built (0.81.6) @@ -81,6 +151,11 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga + - nanopb (3.30910.0): + - nanopb/decode (= 3.30910.0) + - nanopb/encode (= 3.30910.0) + - nanopb/decode (3.30910.0) + - nanopb/encode (3.30910.0) - nSure (1.3.13) - PromisesObjC (2.4.0) - RCT-Folly (2024.11.18.00): @@ -1957,8 +2032,6 @@ PODS: - Yoga - react-native-netinfo (11.4.1): - React-Core - - react-native-notifications (5.1.0): - - React-Core - react-native-pager-view (6.7.1): - boost - DoubleConversion @@ -2654,6 +2727,66 @@ PODS: - Yoga - RNDateTimePicker (8.3.0): - React-Core + - RNFBApp (24.1.1): + - boost + - DoubleConversion + - fast_float + - Firebase/CoreOnly (= 12.10.0) + - 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 + - RNFBMessaging (24.1.1): + - boost + - DoubleConversion + - fast_float + - Firebase/Messaging (= 12.10.0) + - FirebaseCoreExtension + - 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 + - RNFBApp + - SocketRocket + - Yoga - RNFlashList (1.8.3): - boost - DoubleConversion @@ -2719,6 +2852,11 @@ PODS: - React-Core - React-RCTImage - TOCropViewController (~> 2.7.4) + - RNNotifee (9.1.8): + - React-Core + - RNNotifee/NotifeeCore (= 9.1.8) + - RNNotifee/NotifeeCore (9.1.8): + - React-Core - RNPermissions (5.4.4): - React-Core - RNReactNativeHapticFeedback (2.3.3): @@ -3029,8 +3167,10 @@ DEPENDENCIES: - ffmpeg-kit-react-native/full-gpl (from `../../../node_modules/ffmpeg-kit-react-native/ffmpeg-kit-react-native.podspec`) - fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`) - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) + - GoogleUtilities - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) - lottie-react-native (from `../node_modules/lottie-react-native`) + - nanopb - nSure - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - RCTDeprecation (from `../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`) @@ -3076,7 +3216,6 @@ DEPENDENCIES: - react-native-in-app-review (from `../node_modules/react-native-in-app-review`) - react-native-keyboard-controller (from `../../../node_modules/react-native-keyboard-controller`) - "react-native-netinfo (from `../../../node_modules/@react-native-community/netinfo`)" - - react-native-notifications (from `../node_modules/react-native-notifications`) - react-native-pager-view (from `../../../node_modules/react-native-pager-view`) - react-native-randombytes (from `../../../node_modules/react-native-randombytes`) - react-native-restart (from `../node_modules/react-native-restart`) @@ -3122,9 +3261,12 @@ DEPENDENCIES: - "RNCClipboard (from `../../../node_modules/@react-native-clipboard/clipboard`)" - "RNCMaskedView (from `../node_modules/@react-native-masked-view/masked-view`)" - "RNDateTimePicker (from `../node_modules/@react-native-community/datetimepicker`)" + - "RNFBApp (from `../../../node_modules/@react-native-firebase/app`)" + - "RNFBMessaging (from `../../../node_modules/@react-native-firebase/messaging`)" - "RNFlashList (from `../../../node_modules/@shopify/flash-list`)" - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - RNImageCropPicker (from `../node_modules/react-native-image-crop-picker`) + - "RNNotifee (from `../../../node_modules/@notifee/react-native`)" - RNPermissions (from `../../../node_modules/react-native-permissions`) - RNReactNativeHapticFeedback (from `../../../node_modules/react-native-haptic-feedback`) - RNReanimated (from `../node_modules/react-native-reanimated`) @@ -3140,9 +3282,18 @@ DEPENDENCIES: SPEC REPOS: trunk: - Base64 + - Firebase + - FirebaseCore + - FirebaseCoreExtension + - FirebaseCoreInternal + - FirebaseInstallations + - FirebaseMessaging - google-cast-sdk-dynamic-xcframework-no-bluetooth + - GoogleDataTransport + - GoogleUtilities - JWT - lottie-ios + - nanopb - nSure - PromisesObjC - SnapSDK @@ -3267,8 +3418,6 @@ EXTERNAL SOURCES: :path: "../../../node_modules/react-native-keyboard-controller" react-native-netinfo: :path: "../../../node_modules/@react-native-community/netinfo" - react-native-notifications: - :path: "../node_modules/react-native-notifications" react-native-pager-view: :path: "../../../node_modules/react-native-pager-view" react-native-randombytes: @@ -3359,12 +3508,18 @@ EXTERNAL SOURCES: :path: "../node_modules/@react-native-masked-view/masked-view" RNDateTimePicker: :path: "../node_modules/@react-native-community/datetimepicker" + RNFBApp: + :path: "../../../node_modules/@react-native-firebase/app" + RNFBMessaging: + :path: "../../../node_modules/@react-native-firebase/messaging" RNFlashList: :path: "../../../node_modules/@shopify/flash-list" RNGestureHandler: :path: "../node_modules/react-native-gesture-handler" RNImageCropPicker: :path: "../node_modules/react-native-image-crop-picker" + RNNotifee: + :path: "../../../node_modules/@notifee/react-native" RNPermissions: :path: "../../../node_modules/react-native-permissions" RNReactNativeHapticFeedback: @@ -3397,13 +3552,22 @@ SPEC CHECKSUMS: FBLazyVector: 812782f0506ba4c4a1b9f4b793e6de1bfdfc4c25 ffmpeg-kit-ios-full-gpl: 7d416729f7b3604b64cee2a752c42608a8d228e0 ffmpeg-kit-react-native: 3cea88c9c5cfad62e1465279ea7d800dfbba3b00 + Firebase: 99f203d3a114c6ba591f3b32263a9626e450af65 + FirebaseCore: f4428e22415ea3b3eca652c2098413dabf2a23a9 + FirebaseCoreExtension: 3473a6ec16a91aee29fb75994a86c0220d2cddf3 + FirebaseCoreInternal: e7bbaeb00ab73011298f35ed223aa7371e212948 + FirebaseInstallations: 047343aa91fd6a1ebfa3eb374ddecf36a8aaddfd + FirebaseMessaging: ed18fb50634e6e85b5d3e77e628c21e2c928cc53 fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd glog: 5683914934d5b6e4240e497e0f4a3b42d1854183 google-cast-sdk-dynamic-xcframework-no-bluetooth: 09d47191ef8821d82e813e754bcf98bc61a3dbc9 + GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 + GoogleUtilities: 4f2618a4a1e762a1ee134a1e2323bba9843e06da hermes-engine: 7219f6e751ad6ec7f3d7ec121830ee34dae40749 JWT: ef71dfb03e1f842081e64dc42eef0e164f35d251 lottie-ios: a881093fab623c467d3bce374367755c272bdd59 lottie-react-native: 38ad382c9420a1a079c7e6958a11e26c3183d943 + nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 nSure: 2fc3fc973c44aa0be9a3446f84cb514adc475205 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 RCT-Folly: 59ec0ac1f2f39672a0c6e6cecdd39383b764646f @@ -3449,7 +3613,6 @@ SPEC CHECKSUMS: react-native-in-app-review: db8bb167a5f238e7ceca5c242d6b36ce8c4404a4 react-native-keyboard-controller: 2edd7dd2cd307e900df976ed0468673439aabc15 react-native-netinfo: f0a9899081c185db1de5bb2fdc1c88c202a059ac - react-native-notifications: 4601a5a8db4ced6ae7cfc43b44d35fe437ac50c4 react-native-pager-view: 01b7c8baa189186cf739920448124e8c133f0567 react-native-randombytes: 421f1c7d48c0af8dbcd471b0324393ebf8fe7846 react-native-restart: 7595693413fe3ca15893702f2c8306c62a708162 @@ -3495,9 +3658,12 @@ SPEC CHECKSUMS: RNCClipboard: dfeb43751adff21e588657b5b6c888c72f3aa68e RNCMaskedView: 7d91ca73421e3a6563757c9831c3abb3f3702ad0 RNDateTimePicker: a793ed8822283f576dd0a205a0916c5098c2611f + RNFBApp: afd2a671d2696f39d9545eb903d86807bd92b5d6 + RNFBMessaging: 9ba95a6c588ce9ec60e92aef31e04cadeb079f21 RNFlashList: aa7f4d103a58c6f5fadfdb01b160721bb00c7bff RNGestureHandler: 9f3ba2baa5ce32c87bc40026a5d0552dcaf4a837 RNImageCropPicker: 64b6bec9ec185ee59b71a93d7058816bd0e1916e + RNNotifee: 4a6ee5c7deaf00e005050052d73ee6315dff7ec9 RNPermissions: 0a18cdcfdd1773b969f817ce20d47c2b74724af8 RNReactNativeHapticFeedback: c56813c4217519c6bff2f640a7d48f07c30902c2 RNReanimated: ccfe7506f472ab209c81a80a9805d49185b5205b @@ -3516,6 +3682,6 @@ SPEC CHECKSUMS: TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654 Yoga: 3196a843cc5f4bc433f1a41cf17cdf804a01a529 -PODFILE CHECKSUM: 0ea83090adc693808f933bbd5e29fa542a3e3602 +PODFILE CHECKSUM: b5d1f5f43c8739940374728b4ee92b99f57e7de7 COCOAPODS: 1.15.2 diff --git a/packages/mobile/ios/PrivacyInfo.xcprivacy b/packages/mobile/ios/PrivacyInfo.xcprivacy index fa469f312af..657971cb2ec 100644 --- a/packages/mobile/ios/PrivacyInfo.xcprivacy +++ b/packages/mobile/ios/PrivacyInfo.xcprivacy @@ -6,20 +6,22 @@ NSPrivacyAccessedAPIType - NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPICategoryUserDefaults NSPrivacyAccessedAPITypeReasons - C617.1 - 3B52.1 - 0A2A.1 + CA92.1 + 1C8F.1 + C56D.1 NSPrivacyAccessedAPIType - NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPICategoryFileTimestamp NSPrivacyAccessedAPITypeReasons - CA92.1 + C617.1 + 3B52.1 + 0A2A.1 diff --git a/packages/mobile/package.json b/packages/mobile/package.json index 776be96165b..b1412643f29 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -59,6 +59,7 @@ "@hcaptcha/react-native-hcaptcha": "1.6.0", "@noble/hashes": "^1.1.5", "@noble/secp256k1": "1.7.0", + "@notifee/react-native": "^9.1.8", "@optimizely/optimizely-sdk": "4.9.2", "@peculiar/webcrypto": "1.4.3", "@react-native-async-storage/async-storage": "^2.0.0", @@ -69,6 +70,8 @@ "@react-native-community/netinfo": "^11.3.3", "@react-native-community/slider": "4.5.0", "@react-native-documents/picker": "^10.1.2", + "@react-native-firebase/app": "^24.1.1", + "@react-native-firebase/messaging": "^24.1.1", "@react-native-masked-view/masked-view": "^0.3.2", "@react-navigation/bottom-tabs": "6.5.11", "@react-navigation/core": "^7.13.3", @@ -133,7 +136,6 @@ "react-native-markdown-display": "7.0.2", "react-native-modal": "^13.0.0", "react-native-modal-datetime-picker": "18.0.0", - "react-native-notifications": "5.1.0", "react-native-pager-view": "6.7.1", "react-native-permissions": "5.4.4", "react-native-qrcode-svg": "6.2.0", diff --git a/packages/mobile/src/notifications.ts b/packages/mobile/src/notifications.ts index 163f67e459a..1a448c825d0 100644 --- a/packages/mobile/src/notifications.ts +++ b/packages/mobile/src/notifications.ts @@ -1,10 +1,11 @@ import { getCurrentAccountQueryKey } from '@audius/common/api' import { MobileOS } from '@audius/common/models' import type { AccountState } from '@audius/common/store' +import notifee from '@notifee/react-native' import AsyncStorage from '@react-native-async-storage/async-storage' +import messaging from '@react-native-firebase/messaging' +import type { FirebaseMessagingTypes } from '@react-native-firebase/messaging' import { Platform } from 'react-native' -import { Notifications } from 'react-native-notifications' -import type { Registered, Notification } from 'react-native-notifications' import { requestNotifications } from 'react-native-permissions' import { track, make } from 'app/services/analytics' @@ -16,13 +17,13 @@ import { EventNames } from 'app/types/analytics' import { DEVICE_TOKEN } from './constants/storage-keys' /** - * On Android, FCM delivers all `data` payload values as strings. - * This means numeric IDs like `initiator` and `entityId` arrive as "123" - * instead of 123, and nested objects/arrays arrive as stringified JSON. - * This function restores the original types so notification handlers - * can navigate correctly. + * Firebase Cloud Messaging delivers every `data` payload value as a string on + * both platforms. This means numeric IDs like `initiator` and `entityId` + * arrive as "123" instead of 123, and nested objects/arrays arrive as + * stringified JSON. This function restores the original types so notification + * handlers can navigate correctly. */ -function parseAndroidNotificationData(data: Record): any { +function parseNotificationData(data: Record): any { const parsed: Record = {} for (const [key, value] of Object.entries(data)) { if (typeof value !== 'string') { @@ -51,14 +52,20 @@ function parseAndroidNotificationData(data: Record): any { return parsed } -function extractNotificationCampaignIdFromPayload( - payload: Notification['payload'] +function extractNotificationCampaignId( + data: Record | undefined ): string | undefined { - const target = payload?.data?.data ?? payload?.data ?? payload ?? undefined - if (!target || typeof target !== 'object') return undefined - const o = target as Record - const v = o.notification_campaign_id ?? o.notificationCampaignId - return typeof v === 'string' && v.length > 0 ? v : undefined + if (!data || typeof data !== 'object') return undefined + // The navigation payload may live at the top level of `data` or nested under + // a `data` key, so check both. + const candidates = [data, (data as Record).data].filter( + (d) => d && typeof d === 'object' + ) as Record[] + for (const o of candidates) { + const v = o.notification_campaign_id ?? o.notificationCampaignId + if (typeof v === 'string' && v.length > 0) return v + } + return undefined } type Token = { @@ -87,33 +94,36 @@ async function reportNotificationCampaignPushOpen( }) } -// Set to true while the push notification service is registering with the os -let isRegistering = false - // Singleton class class PushNotifications { lastId: number token: Token | null navigation: NotificationNavigation | null + private unsubscribeOpened: (() => void) | null = null + private unsubscribeTokenRefresh: (() => void) | null = null - // onNotification is a function passed in that is to be called when a - // notification is to be emitted. constructor() { - this.configure() this.lastId = 0 this.token = null this.navigation = null + this.configure() } setNavigation = (navigation: NotificationNavigation) => { this.navigation = navigation } - onNotification = (notification: Notification) => { - console.info(`Received notification ${JSON.stringify(notification)}`) - const { title, body, payload } = notification - const notificationCampaignId = - extractNotificationCampaignIdFromPayload(payload) + // Called when the user taps a remote push notification (firebase RemoteMessage) + onNotificationOpened = ( + remoteMessage: FirebaseMessagingTypes.RemoteMessage + ) => { + console.info(`Received notification ${JSON.stringify(remoteMessage)}`) + const title = remoteMessage.notification?.title + const body = remoteMessage.notification?.body + // FCM delivers all `data` values as strings on both platforms, breaking + // numeric ID fields and nested objects. Parse them back to native types. + const data = parseNotificationData(remoteMessage.data ?? {}) + const notificationCampaignId = extractNotificationCampaignId(data) track( make({ eventName: EventNames.NOTIFICATIONS_OPEN_PUSH_NOTIFICATION, @@ -127,39 +137,52 @@ class PushNotifications { reportNotificationCampaignPushOpen(notificationCampaignId) ).catch(() => {}) } - let data = payload?.data?.data ?? payload?.data ?? payload - // On Android, FCM delivers all data values as strings, breaking - // numeric ID fields and nested objects. Parse them back. - if (Platform.OS === MobileOS.ANDROID && data && typeof data === 'object') { - data = parseAndroidNotificationData(data) - } - this.navigation?.navigate(data) + // The navigation payload may be nested under a `data` key + const navigationData = data?.data ?? data + this.navigation?.navigate(navigationData) } // Method used to open the push notification that the user pressed while the app was closed openInitialNotification = async () => { - const notification = await Notifications.getInitialNotification() - if (notification) { - this.onNotification(notification) + const remoteMessage = await messaging().getInitialNotification() + if (remoteMessage) { + this.onNotificationOpened(remoteMessage) } } - async onRegister(event: Registered) { - const token = { token: event.deviceToken, os: Platform.OS } + private persistToken = async (fcmToken: string) => { + const token = { token: fcmToken, os: Platform.OS } this.token = token await AsyncStorage.setItem(DEVICE_TOKEN, JSON.stringify(token)) - isRegistering = false + return token } - deregister() { - AsyncStorage.removeItem(DEVICE_TOKEN) + async deregister() { + await AsyncStorage.removeItem(DEVICE_TOKEN) + this.token = null + try { + await messaging().deleteToken() + } catch (e) { + console.error('Failed to delete FCM token', e) + } } async configure() { - Notifications.events().registerRemoteNotificationsRegistered( - this.onRegister + // Handle notification taps that bring the app from background to foreground + this.unsubscribeOpened = messaging().onNotificationOpenedApp( + (remoteMessage) => { + if (remoteMessage) { + this.onNotificationOpened(remoteMessage) + } + } ) - Notifications.events().registerNotificationOpened(this.onNotification) + + // Keep the persisted token in sync when FCM rotates it + this.unsubscribeTokenRefresh = messaging().onTokenRefresh((fcmToken) => { + this.persistToken(fcmToken).catch((e) => + console.error('Failed to persist refreshed FCM token', e) + ) + }) try { const token = await AsyncStorage.getItem(DEVICE_TOKEN) @@ -174,45 +197,48 @@ class PushNotifications { } async hasPermission(): Promise { - return await Notifications.isRegisteredForRemoteNotifications() + const status = await messaging().hasPermission() + return ( + status === messaging.AuthorizationStatus.AUTHORIZED || + status === messaging.AuthorizationStatus.PROVISIONAL + ) } async requestPermission() { - isRegistering = true - if (Platform.OS === MobileOS.ANDROID) { // Android 13+ needs POST_NOTIFICATIONS. Use requestNotifications — PERMISSIONS.ANDROID // does not expose POST_NOTIFICATIONS in react-native-permissions v5, so request(undefined) crashed native code. await requestNotifications() } - Notifications.registerRemoteNotifications() + await messaging().requestPermission() } - cancelNotif() { - Notifications.cancelLocalNotification(this.lastId) + async cancelNotif() { + await notifee.cancelNotification(String(this.lastId)) } - cancelAll() { - Notifications.ios.cancelAllLocalNotifications() + async cancelAll() { + await notifee.cancelAllNotifications() } setBadgeCount(count: number) { - Notifications.ios.setBadgeCount(count) + notifee.setBadgeCount(count) } async getToken() { - // Wait until the device token and OS are persisted to async storage - // isRegistering modified as global - // eslint-disable-next-line no-unmodified-loop-condition - while (isRegistering) { - await new Promise((resolve) => setTimeout(resolve, 100)) - } - const token = await AsyncStorage.getItem(DEVICE_TOKEN) - if (token) { - return JSON.parse(token) + try { + const fcmToken = await messaging().getToken() + return await this.persistToken(fcmToken) + } catch (e) { + console.error('Failed to fetch FCM token', e) + // Fall back to a previously persisted token, if any + const token = await AsyncStorage.getItem(DEVICE_TOKEN) + if (token) { + return JSON.parse(token) + } + return {} } - return {} } }