From 64d72f7ab30e2ae93c35262f27cd7c92a57adec3 Mon Sep 17 00:00:00 2001 From: jamshale <31809382+jamshale@users.noreply.github.com> Date: Tue, 26 Sep 2023 09:11:39 -0700 Subject: [PATCH] feat: push notifications (#1389) Signed-off-by: jamshale --- .github/workflows/main.yaml | 8 + DEVELOPER.md | 6 +- app/.env.sample | 5 + app/App.tsx | 2 + app/__mocks__/@aries-framework/react-hooks.ts | 5 + .../@react-native-firebase/messaging.ts | 7 + app/__mocks__/react-native-permissions.ts | 14 + app/android/app/build.gradle | 4 + app/android/app/google-services.json | 46 ++ app/android/app/src/main/AndroidManifest.xml | 59 +- .../app/src/main/res/drawable/logo.png | Bin 0 -> 11004 bytes .../app/src/main/res/values/colors.xml | 4 + app/android/build.gradle | 1 + app/ios/AriesBifold.xcodeproj/project.pbxproj | 18 + app/ios/AriesBifold/AppDelegate.m | 4 +- .../DEVELOPMENT.AriesBifold.entitlements | 8 + app/ios/AriesBifold/Info.plist | 4 + app/ios/GoogleService-Info.plist | 34 + app/ios/Podfile | 6 + app/ios/Podfile.lock | 167 +++- app/package.json | 2 + app/src/assets/img/push-notifications.png | Bin 0 -> 21460 bytes app/src/components/PushNotifications.tsx | 41 + app/src/helpers/PushNotificationsHelper.ts | 188 +++++ app/src/localization/en/index.ts | 13 + app/src/localization/fr/index.ts | 13 + app/src/localization/pt-br/index.ts | 13 + app/src/modals/PushNotificationsModal.tsx | 105 +++ app/src/screens/Developer.tsx | 57 +- yarn.lock | 763 +++++++++++++++++- 30 files changed, 1542 insertions(+), 55 deletions(-) create mode 100644 app/.env.sample create mode 100644 app/__mocks__/@react-native-firebase/messaging.ts create mode 100644 app/__mocks__/react-native-permissions.ts create mode 100644 app/android/app/google-services.json create mode 100644 app/android/app/src/main/res/drawable/logo.png create mode 100644 app/android/app/src/main/res/values/colors.xml create mode 100644 app/ios/AriesBifold/DEVELOPMENT.AriesBifold.entitlements create mode 100644 app/ios/GoogleService-Info.plist create mode 100644 app/src/assets/img/push-notifications.png create mode 100644 app/src/components/PushNotifications.tsx create mode 100644 app/src/helpers/PushNotificationsHelper.ts create mode 100644 app/src/modals/PushNotificationsModal.tsx diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 2737c3e88..f7d460fc5 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -141,6 +141,14 @@ jobs: echo "IAS_PORTAL_URL=${IAS_PORTAL_URL}" >>.env echo "IAS_AGENT_INVITE_URL=${IAS_AGENT_INVITE_URL}" >>.env echo "OCA_URL=${OCA_URL}" >>.env + + - name: Set Push Notification Capability + working-directory: app/ios/AriesBifold + env: + MEDIATOR_USE_PUSH_NOTIFICATIONS: false + MEDIATOR_LABEL: Mediator + run: | + mv DEVELOPMENT.AriesBifold.entitlements AriesBifold.entitlements - name: Archive build working-directory: app/ios diff --git a/DEVELOPER.md b/DEVELOPER.md index 8c1f44898..8b4e49cff 100644 --- a/DEVELOPER.md +++ b/DEVELOPER.md @@ -62,11 +62,15 @@ yarn install ## Configuration -In the `./app/` directory add an `.env` file containing: +In the `./app/` directory copy the .env.sample `cp .env.sample .env` ``` MEDIATOR_URL= + +MEDIATOR_USE_PUSH_NOTIFICATIONS=false +MEDIATOR_LABEL=Mediator ``` +Push notifications can be used locally on android if the mediator service has the firebase plugin and it's configured correctly. ### Adding ledger configurations diff --git a/app/.env.sample b/app/.env.sample new file mode 100644 index 000000000..e41dbc7fe --- /dev/null +++ b/app/.env.sample @@ -0,0 +1,5 @@ +MEDIATOR_URL=https://f326-207-194-65-204.ngrok.io?c_i=eyJAdHlwZSI6ICJodHRwczovL2RpZGNvbW0ub3JnL2Nvbm5lY3Rpb25zLzEuMC9pbnZpdGF0aW9uIiwgIkBpZCI6ICI2MjQ0ZThiNS0wNWYzLTRhYWItYjM1Yy1lYWVlMWNmZTAyM2MiLCAicmVjaXBpZW50S2V5cyI6IFsiQ3lqM1BHRUJzQ3RyUGFtTTQyRngza3BlYmR2QWdNd1lGejlFS3RmNnlUN3giXSwgImxhYmVsIjogIk1lZGlhdG9yIiwgInNlcnZpY2VFbmRwb2ludCI6ICJodHRwczovL2YzMjYtMjA3LTE5NC02NS0yMDQubmdyb2suaW8ifQ== + +# Push notification variables +MEDIATOR_USE_PUSH_NOTIFICATIONS=false +MEDIATOR_LABEL=Mediator \ No newline at end of file diff --git a/app/App.tsx b/app/App.tsx index e2deae47f..4b0173280 100644 --- a/app/App.tsx +++ b/app/App.tsx @@ -25,6 +25,7 @@ import SplashScreen from 'react-native-splash-screen' import Toast from 'react-native-toast-message' import bcwallet from './src' +import PushNotifications from './src/components/PushNotifications' import { credentialOfferTourSteps } from './src/components/tours/CredentialOfferTourSteps' import { credentialsTourSteps } from './src/components/tours/CredentialsTourSteps' import { homeTourSteps } from './src/components/tours/HomeTourSteps' @@ -139,6 +140,7 @@ const App = () => { + diff --git a/app/__mocks__/@aries-framework/react-hooks.ts b/app/__mocks__/@aries-framework/react-hooks.ts index c849c39b9..9dd991f54 100644 --- a/app/__mocks__/@aries-framework/react-hooks.ts +++ b/app/__mocks__/@aries-framework/react-hooks.ts @@ -23,6 +23,11 @@ const useAgent = () => ({ agent: { credentials: mockCredentialModule, proofs: mockProofModule, + config: { + logger: { + info: jest.fn(), + }, + }, }, }) const useCredentialById = jest.fn() diff --git a/app/__mocks__/@react-native-firebase/messaging.ts b/app/__mocks__/@react-native-firebase/messaging.ts new file mode 100644 index 000000000..c1d7cc6d7 --- /dev/null +++ b/app/__mocks__/@react-native-firebase/messaging.ts @@ -0,0 +1,7 @@ +const messaging = jest.fn().mockReturnValue({ + setBackgroundMessageHandler: jest.fn(), + onMessage: jest.fn(), + requestPermission: jest.fn(), +}) + +export default messaging diff --git a/app/__mocks__/react-native-permissions.ts b/app/__mocks__/react-native-permissions.ts new file mode 100644 index 000000000..7323dd6fb --- /dev/null +++ b/app/__mocks__/react-native-permissions.ts @@ -0,0 +1,14 @@ +const check = jest.fn() +const request = jest.fn().mockResolvedValue('not-granted') + +const PERMISSIONS = { + ANDROID: { + POST_NOTIFICATIONS: 'POST_NOTIFICATIONS', + }, +} + +const RESULTS = { + GRANTED: 'granted', +} + +export { check, request, PERMISSIONS, RESULTS } diff --git a/app/android/app/build.gradle b/app/android/app/build.gradle index 0f356608a..063345bb3 100644 --- a/app/android/app/build.gradle +++ b/app/android/app/build.gradle @@ -221,6 +221,8 @@ dependencies { implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" + implementation platform('com.google.firebase:firebase-bom:26.4.0') + if (enableHermes) { def hermesPath = "../../node_modules/hermes-engine/android/"; debugImplementation files(hermesPath + "hermes-debug.aar") @@ -239,3 +241,5 @@ task copyDownloadableDepsToLibs(type: Copy) { apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle" apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) + +apply plugin: 'com.google.gms.google-services' \ No newline at end of file diff --git a/app/android/app/google-services.json b/app/android/app/google-services.json new file mode 100644 index 000000000..6ed9a3cca --- /dev/null +++ b/app/android/app/google-services.json @@ -0,0 +1,46 @@ +{ + "project_info": { + "project_number": "493003137594", + "project_id": "ca-bc-gov-bcwallet-2d4df", + "storage_bucket": "ca-bc-gov-bcwallet-2d4df.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:493003137594:android:eecbd78be4bc043c890b03", + "android_client_info": { + "package_name": "ca.bc.gov.BCWallet" + } + }, + "oauth_client": [ + { + "client_id": "493003137594-rgc6pc7cg0nmamc16c5kqs8dpf2gv117.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyDIeHtE0Ds_P8LduRhUqDsQqroi-0Ruric" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "493003137594-rgc6pc7cg0nmamc16c5kqs8dpf2gv117.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "493003137594-0cimp1ta4jlq83j63chv1gvi0vgk8sm6.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "ca.bc.gov.BCWallet" + } + } + ] + } + } + } + ], + "configuration_version": "1" + } \ No newline at end of file diff --git a/app/android/app/src/main/AndroidManifest.xml b/app/android/app/src/main/AndroidManifest.xml index acff360ce..55500690c 100644 --- a/app/android/app/src/main/AndroidManifest.xml +++ b/app/android/app/src/main/AndroidManifest.xml @@ -1,46 +1,47 @@ - + - - + + + - + - + - - - + + + + - - + + - + - - - - - + + + + + diff --git a/app/android/app/src/main/res/drawable/logo.png b/app/android/app/src/main/res/drawable/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..5768883b2daf1386f07bce2bdf9b224ef15ac786 GIT binary patch literal 11004 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 literal 0 HcmV?d00001 diff --git a/app/android/app/src/main/res/values/colors.xml b/app/android/app/src/main/res/values/colors.xml new file mode 100644 index 000000000..22ee6bed6 --- /dev/null +++ b/app/android/app/src/main/res/values/colors.xml @@ -0,0 +1,4 @@ + + + #003468 + \ No newline at end of file diff --git a/app/android/build.gradle b/app/android/build.gradle index d22de4476..bf04c1b20 100644 --- a/app/android/build.gradle +++ b/app/android/build.gradle @@ -18,6 +18,7 @@ buildscript { } dependencies { classpath("com.android.tools.build:gradle:7.2.1") + classpath 'com.google.gms:google-services:4.3.15' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/app/ios/AriesBifold.xcodeproj/project.pbxproj b/app/ios/AriesBifold.xcodeproj/project.pbxproj index 4ea3dba69..c3b0bb323 100644 --- a/app/ios/AriesBifold.xcodeproj/project.pbxproj +++ b/app/ios/AriesBifold.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 2F9994AE2A8D83CB004E773E /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 2F9994AD2A8D83CB004E773E /* GoogleService-Info.plist */; }; 33D4F52D89D299BE528C3520 /* libPods-AriesBifold.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E31DDF0185498B58541BF97 /* libPods-AriesBifold.a */; }; 43857C042720ABB4004F54E0 /* libswiftWebKit.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 43857C032720ABB4004F54E0 /* libswiftWebKit.tbd */; }; 566FB1F2273B158E003E9BEE /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 566FB1F1273B158E003E9BEE /* Media.xcassets */; }; @@ -42,6 +43,7 @@ 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = AriesBifold/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = AriesBifold/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = AriesBifold/main.m; sourceTree = ""; }; + 2F9994AD2A8D83CB004E773E /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 31CB7D155F80E7F43020B57A /* Pods-AriesBifold-AriesBifoldTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AriesBifold-AriesBifoldTests.debug.xcconfig"; path = "Target Support Files/Pods-AriesBifold-AriesBifoldTests/Pods-AriesBifold-AriesBifoldTests.debug.xcconfig"; sourceTree = ""; }; 433E773D26557E1B00F569EE /* Indy.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Indy.framework; path = Pods/Frameworks/Indy.framework; sourceTree = ""; }; 43857C032720ABB4004F54E0 /* libswiftWebKit.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libswiftWebKit.tbd; path = usr/lib/swift/libswiftWebKit.tbd; sourceTree = SDKROOT; }; @@ -143,6 +145,7 @@ 83CBB9F61A601CBA00E9B192 = { isa = PBXGroup; children = ( + 2F9994AD2A8D83CB004E773E /* GoogleService-Info.plist */, 566FB1F1273B158E003E9BEE /* Media.xcassets */, 13B07FAE1A68108700A75B9A /* AriesBifold */, 00E356EF1AD99517003FC87E /* AriesBifoldTests */, @@ -213,6 +216,7 @@ 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, F5B7D67D9BBF4C5FE692BB29 /* [CP] Embed Pods Frameworks */, EB396EB204E0E4C9F6307F29 /* [CP] Copy Pods Resources */, + 185F3FDE1CBC207BCDB826A8 /* [CP-User] [RNFB] Core Configuration */, ); buildRules = ( ); @@ -272,6 +276,7 @@ buildActionMask = 2147483647; files = ( 56A83D6127D95E24002FE8FE /* BCSans-Italic.ttf in Resources */, + 2F9994AE2A8D83CB004E773E /* GoogleService-Info.plist in Resources */, 56A83D6027D95E24002FE8FE /* BCSans-BoldItalic.ttf in Resources */, 56A83D5E27D95E24002FE8FE /* BCSans-Bold.ttf in Resources */, 56A83D5F27D95E24002FE8FE /* BCSans-Regular.ttf in Resources */, @@ -298,6 +303,19 @@ shellPath = /bin/sh; shellScript = "set -e\n\nexport NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n"; }; + 185F3FDE1CBC207BCDB826A8 /* [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#\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 -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 _RN_ROOT_EXISTS=$(ruby -e \"require 'rubygems';require 'json'; output=JSON.parse('$_JSON_OUTPUT_RAW'); puts output[$_JSON_ROOT]\" || echo '')\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_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.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"; + }; 4EC0E8613979411464217753 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; diff --git a/app/ios/AriesBifold/AppDelegate.m b/app/ios/AriesBifold/AppDelegate.m index 73c69b897..a95eb5758 100644 --- a/app/ios/AriesBifold/AppDelegate.m +++ b/app/ios/AriesBifold/AppDelegate.m @@ -1,5 +1,6 @@ #import "AppDelegate.h" +#import #import #import #import @@ -9,6 +10,7 @@ @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [FIRApp configure]; RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge @@ -45,4 +47,4 @@ - (BOOL)application:(UIApplication *)application return [RCTLinkingManager application:application openURL:url options:options]; } -@end \ No newline at end of file +@end diff --git a/app/ios/AriesBifold/DEVELOPMENT.AriesBifold.entitlements b/app/ios/AriesBifold/DEVELOPMENT.AriesBifold.entitlements new file mode 100644 index 000000000..308ab6fa5 --- /dev/null +++ b/app/ios/AriesBifold/DEVELOPMENT.AriesBifold.entitlements @@ -0,0 +1,8 @@ + + + + + aps-environment + development + + \ No newline at end of file diff --git a/app/ios/AriesBifold/Info.plist b/app/ios/AriesBifold/Info.plist index 2b0d162a8..e982b60f7 100644 --- a/app/ios/AriesBifold/Info.plist +++ b/app/ios/AriesBifold/Info.plist @@ -69,6 +69,10 @@ BCSans-Italic.ttf BCSans-Regular.ttf + UIBackgroundModes + + remote-notification + UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities diff --git a/app/ios/GoogleService-Info.plist b/app/ios/GoogleService-Info.plist new file mode 100644 index 000000000..f085053ba --- /dev/null +++ b/app/ios/GoogleService-Info.plist @@ -0,0 +1,34 @@ + + + + + CLIENT_ID + 493003137594-0cimp1ta4jlq83j63chv1gvi0vgk8sm6.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.493003137594-0cimp1ta4jlq83j63chv1gvi0vgk8sm6 + API_KEY + AIzaSyByvJDrhcSJTy27Qqu9_1SPlt_u7r1bi6o + GCM_SENDER_ID + 493003137594 + PLIST_VERSION + 1 + BUNDLE_ID + ca.bc.gov.BCWallet + PROJECT_ID + ca-bc-gov-bcwallet-2d4df + STORAGE_BUCKET + ca-bc-gov-bcwallet-2d4df.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:493003137594:ios:3ea44407af9632cd890b03 + + \ No newline at end of file diff --git a/app/ios/Podfile b/app/ios/Podfile index 0d23ff114..c36ece8a4 100644 --- a/app/ios/Podfile +++ b/app/ios/Podfile @@ -23,9 +23,15 @@ end target 'AriesBifold' do config = use_native_modules! + pod 'Firebase', :modular_headers => true + pod 'FirebaseCore', :modular_headers => true + pod 'FirebaseCoreInternal', :modular_headers => true + pod 'GoogleUtilities', :modular_headers => true + permissions_path = '../node_modules/react-native-permissions/ios' pod 'Permission-Camera', :path => "#{permissions_path}/Camera" + pod 'Permission-Notifications', :path => "#{permissions_path}/Notifications" use_react_native!( :path => config[:reactNativePath], diff --git a/app/ios/Podfile.lock b/app/ios/Podfile.lock index 72ccd1b37..f4900a242 100644 --- a/app/ios/Podfile.lock +++ b/app/ios/Podfile.lock @@ -19,16 +19,134 @@ PODS: - React-Core (= 0.66.5) - React-jsi (= 0.66.5) - ReactCommon/turbomodule/core (= 0.66.5) + - Firebase (8.15.0): + - Firebase/Core (= 8.15.0) + - Firebase/Core (8.15.0): + - Firebase/CoreOnly + - FirebaseAnalytics (~> 8.15.0) + - Firebase/CoreOnly (8.15.0): + - FirebaseCore (= 8.15.0) + - Firebase/Messaging (8.15.0): + - Firebase/CoreOnly + - FirebaseMessaging (~> 8.15.0) + - FirebaseAnalytics (8.15.0): + - FirebaseAnalytics/AdIdSupport (= 8.15.0) + - FirebaseCore (~> 8.0) + - FirebaseInstallations (~> 8.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.7) + - GoogleUtilities/MethodSwizzler (~> 7.7) + - GoogleUtilities/Network (~> 7.7) + - "GoogleUtilities/NSData+zlib (~> 7.7)" + - nanopb (~> 2.30908.0) + - FirebaseAnalytics/AdIdSupport (8.15.0): + - FirebaseCore (~> 8.0) + - FirebaseInstallations (~> 8.0) + - GoogleAppMeasurement (= 8.15.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.7) + - GoogleUtilities/MethodSwizzler (~> 7.7) + - GoogleUtilities/Network (~> 7.7) + - "GoogleUtilities/NSData+zlib (~> 7.7)" + - nanopb (~> 2.30908.0) + - FirebaseCore (8.15.0): + - FirebaseCoreDiagnostics (~> 8.0) + - GoogleUtilities/Environment (~> 7.7) + - GoogleUtilities/Logger (~> 7.7) + - FirebaseCoreDiagnostics (8.15.0): + - GoogleDataTransport (~> 9.1) + - GoogleUtilities/Environment (~> 7.7) + - GoogleUtilities/Logger (~> 7.7) + - nanopb (~> 2.30908.0) + - FirebaseCoreInternal (10.13.0): + - "GoogleUtilities/NSData+zlib (~> 7.8)" + - FirebaseInstallations (8.15.0): + - FirebaseCore (~> 8.0) + - GoogleUtilities/Environment (~> 7.7) + - GoogleUtilities/UserDefaults (~> 7.7) + - PromisesObjC (< 3.0, >= 1.2) + - FirebaseMessaging (8.15.0): + - FirebaseCore (~> 8.0) + - FirebaseInstallations (~> 8.0) + - GoogleDataTransport (~> 9.1) + - GoogleUtilities/AppDelegateSwizzler (~> 7.7) + - GoogleUtilities/Environment (~> 7.7) + - GoogleUtilities/Reachability (~> 7.7) + - GoogleUtilities/UserDefaults (~> 7.7) + - nanopb (~> 2.30908.0) - fmt (6.2.1) - glog (0.3.5) + - GoogleAppMeasurement (8.15.0): + - GoogleAppMeasurement/AdIdSupport (= 8.15.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.7) + - GoogleUtilities/MethodSwizzler (~> 7.7) + - GoogleUtilities/Network (~> 7.7) + - "GoogleUtilities/NSData+zlib (~> 7.7)" + - nanopb (~> 2.30908.0) + - GoogleAppMeasurement/AdIdSupport (8.15.0): + - GoogleAppMeasurement/WithoutAdIdSupport (= 8.15.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.7) + - GoogleUtilities/MethodSwizzler (~> 7.7) + - GoogleUtilities/Network (~> 7.7) + - "GoogleUtilities/NSData+zlib (~> 7.7)" + - nanopb (~> 2.30908.0) + - GoogleAppMeasurement/WithoutAdIdSupport (8.15.0): + - GoogleUtilities/AppDelegateSwizzler (~> 7.7) + - GoogleUtilities/MethodSwizzler (~> 7.7) + - GoogleUtilities/Network (~> 7.7) + - "GoogleUtilities/NSData+zlib (~> 7.7)" + - nanopb (~> 2.30908.0) + - GoogleDataTransport (9.2.5): + - GoogleUtilities/Environment (~> 7.7) + - nanopb (< 2.30910.0, >= 2.30908.0) + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities (7.11.5): + - GoogleUtilities/AppDelegateSwizzler (= 7.11.5) + - GoogleUtilities/Environment (= 7.11.5) + - GoogleUtilities/ISASwizzler (= 7.11.5) + - GoogleUtilities/Logger (= 7.11.5) + - GoogleUtilities/MethodSwizzler (= 7.11.5) + - GoogleUtilities/Network (= 7.11.5) + - "GoogleUtilities/NSData+zlib (= 7.11.5)" + - GoogleUtilities/Reachability (= 7.11.5) + - GoogleUtilities/SwizzlerTestHelpers (= 7.11.5) + - GoogleUtilities/UserDefaults (= 7.11.5) + - GoogleUtilities/AppDelegateSwizzler (7.11.5): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Environment (7.11.5): + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/ISASwizzler (7.11.5) + - GoogleUtilities/Logger (7.11.5): + - GoogleUtilities/Environment + - GoogleUtilities/MethodSwizzler (7.11.5): + - GoogleUtilities/Logger + - GoogleUtilities/Network (7.11.5): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (7.11.5)" + - GoogleUtilities/Reachability (7.11.5): + - GoogleUtilities/Logger + - GoogleUtilities/SwizzlerTestHelpers (7.11.5): + - GoogleUtilities/MethodSwizzler + - GoogleUtilities/UserDefaults (7.11.5): + - GoogleUtilities/Logger - hermes-engine (0.9.0) - indy-vdr (0.1.0): - React - React-callinvoker - React-Core - libevent (2.1.12) + - nanopb (2.30908.0): + - nanopb/decode (= 2.30908.0) + - nanopb/encode (= 2.30908.0) + - nanopb/decode (2.30908.0) + - nanopb/encode (2.30908.0) - Permission-Camera (3.8.0): - RNPermissions + - Permission-Notifications (3.8.0): + - RNPermissions + - PromisesObjC (2.3.1) - RCT-Folly (2021.06.28.00-v2): - boost - DoubleConversion @@ -345,6 +463,13 @@ PODS: - React - RNDeviceInfo (8.4.9): - React-Core + - RNFBApp (14.12.0): + - Firebase/CoreOnly (= 8.15.0) + - React-Core + - RNFBMessaging (14.12.0): + - Firebase/Messaging (= 8.15.0) + - React-Core + - RNFBApp - RNFS (2.18.0): - React - RNGestureHandler (1.10.3): @@ -402,11 +527,16 @@ DEPENDENCIES: - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`) + - Firebase + - FirebaseCore + - FirebaseCoreInternal - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) + - GoogleUtilities - hermes-engine (~> 0.9.0) - "indy-vdr (from `../node_modules/@hyperledger/indy-vdr-react-native`)" - libevent (~> 2.1.12) - Permission-Camera (from `../node_modules/react-native-permissions/ios/Camera`) + - Permission-Notifications (from `../node_modules/react-native-permissions/ios/Notifications`) - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`) - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) @@ -447,6 +577,8 @@ DEPENDENCIES: - "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)" - "RNCMaskedView (from `../node_modules/@react-native-community/masked-view`)" - RNDeviceInfo (from `../node_modules/react-native-device-info`) + - "RNFBApp (from `../node_modules/@react-native-firebase/app`)" + - "RNFBMessaging (from `../node_modules/@react-native-firebase/messaging`)" - RNFS (from `../node_modules/react-native-fs`) - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - RNInAppBrowser (from `../node_modules/react-native-inappbrowser-reborn`) @@ -463,9 +595,21 @@ SPEC REPOS: trunk: - CatCrypto - CocoaAsyncSocket + - Firebase + - FirebaseAnalytics + - FirebaseCore + - FirebaseCoreDiagnostics + - FirebaseCoreInternal + - FirebaseInstallations + - FirebaseMessaging - fmt + - GoogleAppMeasurement + - GoogleDataTransport + - GoogleUtilities - hermes-engine - libevent + - nanopb + - PromisesObjC EXTERNAL SOURCES: anoncreds: @@ -486,6 +630,8 @@ EXTERNAL SOURCES: :path: "../node_modules/@hyperledger/indy-vdr-react-native" Permission-Camera: :path: "../node_modules/react-native-permissions/ios/Camera" + Permission-Notifications: + :path: "../node_modules/react-native-permissions/ios/Notifications" RCT-Folly: :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" RCTRequired: @@ -562,6 +708,10 @@ EXTERNAL SOURCES: :path: "../node_modules/@react-native-community/masked-view" RNDeviceInfo: :path: "../node_modules/react-native-device-info" + RNFBApp: + :path: "../node_modules/@react-native-firebase/app" + RNFBMessaging: + :path: "../node_modules/@react-native-firebase/messaging" RNFS: :path: "../node_modules/react-native-fs" RNGestureHandler: @@ -594,12 +744,25 @@ SPEC CHECKSUMS: DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662 FBLazyVector: a926a9aaa3596b181972abf0f47eff3dee796222 FBReactNativeSpec: f1141d5407f4a27c397bca5db03cc03919357b0d + Firebase: 5f8193dff4b5b7c5d5ef72ae54bb76c08e2b841d + FirebaseAnalytics: 7761cbadb00a717d8d0939363eb46041526474fa + FirebaseCore: 5743c5785c074a794d35f2fff7ecc254a91e08b1 + FirebaseCoreDiagnostics: 92e07a649aeb66352b319d43bdd2ee3942af84cb + FirebaseCoreInternal: b342e37cd4f5b4454ec34308f073420e7920858e + FirebaseInstallations: 40bd9054049b2eae9a2c38ef1c3dd213df3605cd + FirebaseMessaging: 5e5118a2383b3531e730d974680954c679ca0a13 fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 glog: 5337263514dd6f09803962437687240c5dc39aa4 + GoogleAppMeasurement: 4c19f031220c72464d460c9daa1fb5d1acce958e + GoogleDataTransport: 54dee9d48d14580407f8f5fbf2f496e92437a2f2 + GoogleUtilities: 13e2c67ede716b8741c7989e26893d151b2b2084 hermes-engine: bf7577d12ac6ccf53ab8b5af3c6ccf0dd8458c5c indy-vdr: 85cd66089f151256581323440e78988891f4082e libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 + nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 Permission-Camera: e6d142d7d8b714afe0a83e5e6ae17eb949f1e3e9 + Permission-Notifications: aa91ec29236626ff59cb60e08389c0a59a9d32c5 + PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4 RCT-Folly: a21c126816d8025b547704b777a2ba552f3d9fa9 RCTRequired: 405e24508a0feed1771d48caebb85c581db53122 RCTTypeSafety: 0654998ea6afd3dccecbf6bb379d7c10d1361a72 @@ -638,6 +801,8 @@ SPEC CHECKSUMS: RNCAsyncStorage: eb05c0284dd6b50b32f92fad55e2a41e03358c43 RNCMaskedView: 0e1bc4bfa8365eba5fbbb71e07fbdc0555249489 RNDeviceInfo: 4944cf8787b9c5bffaf301fda68cc1a2ec003341 + RNFBApp: e4439717c23252458da2b41b81b4b475c86f90c4 + RNFBMessaging: 40dac204b4197a2661dec5be964780c6ec39bf65 RNFS: 3ab21fa6c56d65566d1fb26c2228e2b6132e5e32 RNGestureHandler: a479ebd5ed4221a810967000735517df0d2db211 RNInAppBrowser: e36d6935517101ccba0e875bac8ad7b0cb655364 @@ -650,6 +815,6 @@ SPEC CHECKSUMS: RNVectorIcons: 31cebfcf94e8cf8686eb5303ae0357da64d7a5a4 Yoga: b316a990bb6d115afa1b436b5626ac7c61717d17 -PODFILE CHECKSUM: 4266f539fd5c242abd4005c88814b151329c70f6 +PODFILE CHECKSUM: d8c135e878f21c2bf8dfc27d38fd85e819cbe188 COCOAPODS: 1.12.1 diff --git a/app/package.json b/app/package.json index 417fa41d0..852f8a399 100644 --- a/app/package.json +++ b/app/package.json @@ -63,6 +63,8 @@ "@react-native-async-storage/async-storage": "1.15.11", "@react-native-community/masked-view": "0.1.11", "@react-native-community/netinfo": "9.3.0", + "@react-native-firebase/app": "^14.12.0", + "@react-native-firebase/messaging": "^14.12.0", "@react-navigation/bottom-tabs": "6.0.9", "@react-navigation/devtools": "6.0.8", "@react-navigation/native": "6.0.6", diff --git a/app/src/assets/img/push-notifications.png b/app/src/assets/img/push-notifications.png new file mode 100644 index 0000000000000000000000000000000000000000..3087b539610f8d4cab2ad9163d980d99ac7c329e GIT binary patch literal 21460 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
V6=N+e0A4){M}4SObHC8RK^T$r2YW{jyv?{ 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 literal 0 HcmV?d00001 diff --git a/app/src/components/PushNotifications.tsx b/app/src/components/PushNotifications.tsx new file mode 100644 index 000000000..a519e9d56 --- /dev/null +++ b/app/src/components/PushNotifications.tsx @@ -0,0 +1,41 @@ +import { useAgent } from '@aries-framework/react-hooks' +import React, { useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' + +import { isMediatorCapable, isRegistered, setup, isUserDenied } from '../helpers/PushNotificationsHelper' +import PushNotificationsModal from '../modals/PushNotificationsModal' + +const PushNotifications = () => { + const { agent } = useAgent() + const [infoModalVisible, setInfoModalVisible] = useState(false) + const { t } = useTranslation() + + const setupPushNotifications = async () => { + setInfoModalVisible(false) + if (!agent || (await isUserDenied())) { + return + } + setup(agent, true) + } + + const initializeCapabilityRequest = async () => { + if (!agent || !(await isMediatorCapable(agent)) || (await isRegistered())) { + return + } + setInfoModalVisible(true) + } + + useEffect(() => { + initializeCapabilityRequest() + }, [agent]) // Reload if agent becomes defined + + return ( + + ) +} + +export default PushNotifications diff --git a/app/src/helpers/PushNotificationsHelper.ts b/app/src/helpers/PushNotificationsHelper.ts new file mode 100644 index 000000000..249b042f7 --- /dev/null +++ b/app/src/helpers/PushNotificationsHelper.ts @@ -0,0 +1,188 @@ +import { Agent, ConnectionRecord } from '@aries-framework/core' +import AsyncStorage from '@react-native-async-storage/async-storage' +import messaging from '@react-native-firebase/messaging' +import { Platform } from 'react-native' +import { Config } from 'react-native-config' +import { request, check, PERMISSIONS, RESULTS, PermissionStatus } from 'react-native-permissions' + +const TOKEN_STORAGE_KEY = 'deviceToken' + +/** + * Handler Section + */ + +const _backgroundHandler = (): void => { + return messaging().setBackgroundMessageHandler(async () => { + // Do nothing with background messages. Defaults to login and home screen flow + }) +} + +const _foregroundHandler = (): (() => void) => { + return messaging().onMessage(async () => { + // Ignore foreground messages + }) +} + +/** + * Permissions Section + */ + +const _requestNotificationPermission = async (agent: Agent): Promise => { + agent.config.logger.info('Requesting push notification permission...') + const result = await request(PERMISSIONS.ANDROID.POST_NOTIFICATIONS) + agent.config.logger.info(`push notification permission is now [${result}]`) + return result +} + +const _checkNotificationPermission = async (agent: Agent): Promise => { + agent.config.logger.info('Checking push notification permission...') + const result = await check(PERMISSIONS.ANDROID.POST_NOTIFICATIONS) + agent.config.logger.info(`push notification permission is [${result}]`) + return result +} + +const _requestPermission = async (agent: Agent): Promise => { + // IOS doesn't need the extra permission logic like android + if (Platform.OS === 'ios') { + await messaging().requestPermission() + return + } + + const checkPermission = await _checkNotificationPermission(agent) + if (checkPermission !== RESULTS.GRANTED) { + const request = await _requestNotificationPermission(agent) + if (request !== RESULTS.GRANTED) { + agent.config.logger.warn(`push notification permission was not granted by user`) + } + } +} + +/** + * Helper Functions Section + */ + +const _getMediatorConnection = async (agent: Agent): Promise => { + const connections = await agent.connections.getAll() + for (const connection of connections) { + if (connection.theirLabel === Config.MEDIATOR_LABEL) { + return connection + } + } + agent.config.logger.warn(`Mediator connection with label [${Config.MEDIATOR_LABEL}] not found`) + return undefined +} + +/** + * Checks wether the user denied permissions on the info modal + * @returns {Promise} + */ +const isUserDenied = async (): Promise => { + return (await AsyncStorage.getItem('userDeniedPushNotifications')) === 'true' +} + +/** + * Uses the discover didcomm protocol to check with the mediator if it supports the firebase push notification protocol + * @param agent - The active aries agent + * @returns {Promise} + */ +const isMediatorCapable = async (agent: Agent): Promise => { + if (!Config.MEDIATOR_LABEL || Config.MEDIATOR_USE_PUSH_NOTIFICATIONS === 'false') { + return false + } + + const mediator = await _getMediatorConnection(agent) + if (!mediator) return + + const response = await agent.discovery.queryFeatures({ + awaitDisclosures: true, + connectionId: mediator.id, + protocolVersion: 'v1', + queries: [ + { + featureType: 'protocol', + match: 'https://didcomm.org/push-notifications-fcm/1.0', + }, + ], + }) + if (response.features && response.features?.length > 0) { + return true + } + return false +} + +/** + * Checks if the device token is already registered by checking the permission was granted and the storage key was used + * @param token - If defined will use this token instead of fetching with firebase + * @returns {Promise} + */ +const isRegistered = async (): Promise => { + const authorized = (await messaging().hasPermission()) === messaging.AuthorizationStatus.AUTHORIZED + + // Need to register for push notification capability on iOS + if (Platform.OS === 'ios' && !messaging().isDeviceRegisteredForRemoteMessages) { + await messaging().registerDeviceForRemoteMessages() + } + + if (authorized && (await AsyncStorage.getItem(TOKEN_STORAGE_KEY)) !== null) { + return true + } + return false +} + +/** + * Checks if push notifications are enabled by checking if the stored token matches the expected firebase token + * @returns {Promise} + */ +const isEnabled = async (): Promise => { + return (await messaging().getToken()) === (await AsyncStorage.getItem(TOKEN_STORAGE_KEY)) +} + +/** + * Attempts to send the device token to the mediator agent. If the token is blank this is equivalent to disabling + * @param agent - The active aries agent + * @param blankDeviceToken - If true, will send an empty string as the device token to the mediator + * @returns {Promise} + */ +const setDeviceInfo = async (agent: Agent, blankDeviceToken = false): Promise => { + let token + if (blankDeviceToken) { + token = '' + } else { + token = await messaging().getToken() + } + + const mediator = await _getMediatorConnection(agent) + if (!mediator) { + return + } + + agent.config.logger.info(`Trying to send device info to mediator with connection [${mediator.id}]`) + try { + await agent.modules.pushNotificationsFcm.setDeviceInfo(mediator.id, { + deviceToken: token, + }) + if (blankDeviceToken) { + AsyncStorage.setItem(TOKEN_STORAGE_KEY, 'blank') + } else { + AsyncStorage.setItem(TOKEN_STORAGE_KEY, token) + } + } catch (error) { + agent.config.logger.error('Error sending device token info to mediator agent') + } +} + +/** + * Attempts to send the device token to the mediator agent, register handlers and requests permissions + * @param agent - The active aries agent + * @prarm blankDeviceToken - If true, will setup the device token as blank (disabled) + * @returns {Promise} + */ +const setup = async (agent: Agent, blankDeviceToken = false): Promise => { + // FIXME: Currently set the token to blank (disabled) on initialization. + setDeviceInfo(agent, blankDeviceToken) + _backgroundHandler() + _foregroundHandler() + _requestPermission(agent) +} + +export { isEnabled, isRegistered, isMediatorCapable, isUserDenied, setDeviceInfo, setup } diff --git a/app/src/localization/en/index.ts b/app/src/localization/en/index.ts index 45d9372f9..103022166 100644 --- a/app/src/localization/en/index.ts +++ b/app/src/localization/en/index.ts @@ -152,6 +152,19 @@ const translation = { }, "Feedback": { "GiveFeedback": "Give Feedback", + }, + "PushNotifications": { + "BulletFour": "new messages", + "BulletOne": "new credential offers", + "BulletThree": "updates to your credentials", + "BulletTwo": "new proof requests", + "DeveloperTitle": "Push Notifications", + "HeadingOne": "It is recommended you allow this app to receive push notifications.", + "HeadingTwo": "Notifications will be sent to you when you receive:", + "NotAvailable": " (Not Available)", + "Title": "Notifications", + "PushNotifications": "Push Notifications", + "Continue": "Continue", } } diff --git a/app/src/localization/fr/index.ts b/app/src/localization/fr/index.ts index c948cba8a..e90e0231e 100644 --- a/app/src/localization/fr/index.ts +++ b/app/src/localization/fr/index.ts @@ -151,6 +151,19 @@ const translation = { }, "Feedback": { "GiveFeedback": "Give Feedback (FR)", + }, + "PushNotifications": { + "BulletFour": "new messages (FR)", + "BulletOne": "new credential offers (FR)", + "BulletThree": "updates to your credentials (FR)", + "BulletTwo": "new proof requests (FR)", + "DeveloperTitle": "Push Notifications (FR)", + "HeadingOne": "It is recommended you allow this app to receive push notifications. (FR)", + "HeadingTwo": "Notifications will be sent to you when you receive: (FR)", + "NotAvailable": " (Not Available) (FR)", + "Title": "Notifications (FR)", + "PushNotifications": "Push Notifications (FR)", + "Continue": "Continue (FR)", } } diff --git a/app/src/localization/pt-br/index.ts b/app/src/localization/pt-br/index.ts index 204b0d96d..c44d358ae 100644 --- a/app/src/localization/pt-br/index.ts +++ b/app/src/localization/pt-br/index.ts @@ -151,6 +151,19 @@ const translation = { }, "Feedback": { "GiveFeedback": "Give Feedback (PT-BR)", + }, + "PushNotifications": { + "BulletFour": "new messages (PT-BR)", + "BulletOne": "new credential offers (PT-BR)", + "BulletThree": "updates to your credentials (PT-BR)", + "BulletTwo": "new proof requests (PT-BR)", + "DeveloperTitle": "Push Notifications (PT-BR)", + "HeadingOne": "It is recommended you allow this app to receive push notifications. (PT-BR)", + "HeadingTwo": "Notifications will be sent to you when you receive: (PT-BR)", + "NotAvailable": " (Not Available) (PT-BR)", + "Title": "Notifications (PT-BR)", + "PushNotifications": "Push Notifications (PT-BR)", + "Continue": "Continue (PT-BR)", } } diff --git a/app/src/modals/PushNotificationsModal.tsx b/app/src/modals/PushNotificationsModal.tsx new file mode 100644 index 000000000..cf62904ea --- /dev/null +++ b/app/src/modals/PushNotificationsModal.tsx @@ -0,0 +1,105 @@ +import AsyncStorage from '@react-native-async-storage/async-storage' +import { Button, ButtonType, useTheme } from 'aries-bifold' +import React, { useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { Image, Modal, StyleSheet, Text, View } from 'react-native' +import { ScrollView } from 'react-native-gesture-handler' +import { SafeAreaView } from 'react-native-safe-area-context' + +import useTourImageDimensions from '../hooks/tour-image-dimensions' + +interface PushNotificationsModalProps { + title: string + doneTitle?: string + doneType?: ButtonType + doneAccessibilityLabel?: string + onDone?: () => void + onHome?: () => void + doneVisible?: boolean + homeVisible?: boolean + testID?: string + visible?: boolean +} + +const PushNotificationsModal: React.FC = ({ title, onDone, visible }) => { + const { t } = useTranslation() + const [modalVisible, setModalVisible] = useState(true) + const { ColorPallet, TextTheme } = useTheme() + const { imageWidth, imageHeight } = useTourImageDimensions() + + useEffect(() => { + if (visible !== undefined) { + setModalVisible(visible) + } + }, [visible]) + + const styles = StyleSheet.create({ + container: { + height: '100%', + backgroundColor: ColorPallet.brand.modalPrimaryBackground, + padding: 20, + }, + messageText: { + marginTop: 30, + }, + controlsContainer: { + backgroundColor: ColorPallet.brand.modalPrimaryBackground, + marginTop: 'auto', + margin: 20, + }, + buttonContainer: { + paddingTop: 10, + }, + }) + + const onNotNow = async () => { + await AsyncStorage.setItem('userDeniedPushNotifications', 'true') + setModalVisible(false) + } + + return ( + + + + {title} + + {t('PushNotifications.HeadingOne')} + {t('PushNotifications.HeadingTwo')} + {' \u2022 ' + t('PushNotifications.BulletOne')} + {' \u2022 ' + t('PushNotifications.BulletTwo')} + {' \u2022 ' + t('PushNotifications.BulletThree')} + {' \u2022 ' + t('PushNotifications.BulletFour')} + + + +