aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--index.js5
-rw-r--r--ios/Frontend.xcodeproj/project.pbxproj20
-rw-r--r--ios/Frontend/AppDelegate.m12
-rw-r--r--ios/Frontend/FrontendDebug.entitlements8
-rw-r--r--ios/Frontend/Info.plist4
-rw-r--r--ios/GoogleService-Info.plist36
-rw-r--r--ios/Podfile5
-rw-r--r--package.json4
-rw-r--r--src/App.tsx8
-rw-r--r--src/constants/api.ts4
-rw-r--r--src/screens/onboarding/CategorySelection.tsx3
-rw-r--r--src/screens/onboarding/Login.tsx3
-rw-r--r--src/services/FCMService.ts177
-rw-r--r--src/services/index.ts1
14 files changed, 287 insertions, 3 deletions
diff --git a/index.js b/index.js
index 4117260f..e1cf0015 100644
--- a/index.js
+++ b/index.js
@@ -6,5 +6,10 @@ import 'react-native-gesture-handler';
import {AppRegistry} from 'react-native';
import App from './src';
import {name as appName} from './app.json';
+import messaging from '@react-native-firebase/messaging';
+
+messaging().setBackgroundMessageHandler(async (remoteMessage) => {
+ console.log('Message handled in background ', remoteMessage);
+});
AppRegistry.registerComponent(appName, () => App);
diff --git a/ios/Frontend.xcodeproj/project.pbxproj b/ios/Frontend.xcodeproj/project.pbxproj
index 9b00868f..f763dd62 100644
--- a/ios/Frontend.xcodeproj/project.pbxproj
+++ b/ios/Frontend.xcodeproj/project.pbxproj
@@ -19,6 +19,8 @@
2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
2DCD954D1E0B4F2C00145EB5 /* FrontendTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* FrontendTests.m */; };
493A42D1B1923CF79BDBA497 /* libPods-Frontend-FrontendTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F9583A4B15B8BC744CC480BD /* libPods-Frontend-FrontendTests.a */; };
+ B7B3F698256E07C400EF4575 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B7B3F697256E07C400EF4575 /* GoogleService-Info.plist */; };
+ B7B3F699256E07C400EF4575 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B7B3F697256E07C400EF4575 /* GoogleService-Info.plist */; };
FE86B674D4BDCF5024DDFC4A /* libPods-Frontend.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A25FA993A8B7865AC123945B /* libPods-Frontend.a */; };
/* End PBXBuildFile section */
@@ -61,6 +63,8 @@
9F8E4573A72CA8220DAA9AF5 /* libPods-Frontend-tvOSTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Frontend-tvOSTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
A25FA993A8B7865AC123945B /* libPods-Frontend.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Frontend.a"; sourceTree = BUILT_PRODUCTS_DIR; };
A967DF75AA8AD56D8D15B597 /* Pods-Frontend-tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Frontend-tvOS.release.xcconfig"; path = "Target Support Files/Pods-Frontend-tvOS/Pods-Frontend-tvOS.release.xcconfig"; sourceTree = "<group>"; };
+ B7B3F697256E07C400EF4575 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
+ B7D7DF1925880C7800C18E93 /* FrontendDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = FrontendDebug.entitlements; path = Frontend/FrontendDebug.entitlements; sourceTree = "<group>"; };
C43E63ACF638693AFCCCEFD3 /* Pods-Frontend-FrontendTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Frontend-FrontendTests.debug.xcconfig"; path = "Target Support Files/Pods-Frontend-FrontendTests/Pods-Frontend-FrontendTests.debug.xcconfig"; sourceTree = "<group>"; };
C7E3BB8A63613F3856CDAFF6 /* Pods-Frontend-tvOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Frontend-tvOSTests.release.xcconfig"; path = "Target Support Files/Pods-Frontend-tvOSTests/Pods-Frontend-tvOSTests.release.xcconfig"; sourceTree = "<group>"; };
CCC6F52EEBC98F07FC072D7F /* Pods-Frontend.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Frontend.release.xcconfig"; path = "Target Support Files/Pods-Frontend/Pods-Frontend.release.xcconfig"; sourceTree = "<group>"; };
@@ -126,10 +130,12 @@
13B07FAE1A68108700A75B9A /* Frontend */ = {
isa = PBXGroup;
children = (
+ B7D7DF1925880C7800C18E93 /* FrontendDebug.entitlements */,
69C3C9D224E1B813003075D8 /* Fonts */,
008F07F21AC5B25A0029DE68 /* main.jsbundle */,
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
13B07FB01A68108700A75B9A /* AppDelegate.m */,
+ B7B3F697256E07C400EF4575 /* GoogleService-Info.plist */,
13B07FB51A68108700A75B9A /* Images.xcassets */,
13B07FB61A68108700A75B9A /* Info.plist */,
13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
@@ -241,6 +247,7 @@
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
5F19A5045F2557A3A7A71AA2 /* [CP] Copy Pods Resources */,
+ 4136E2305F8E25C7C69F8EEF /* [CP-User] [RNFB] Core Configuration */,
);
buildRules = (
);
@@ -354,6 +361,7 @@
files = (
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */,
+ B7B3F698256E07C400EF4575 /* GoogleService-Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -362,6 +370,7 @@
buildActionMask = 2147483647;
files = (
2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */,
+ B7B3F699256E07C400EF4575 /* GoogleService-Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -425,6 +434,16 @@
shellPath = /bin/sh;
shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
};
+ 4136E2305F8E25C7C69F8EEF /* [CP-User] [RNFB] Core Configuration */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ 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 | 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 _JSON_OUTPUT_BASE64=$(python -c 'import json,sys,base64;print(base64.b64encode(json.dumps(json.loads(open('\"'${_SEARCH_RESULT}'\"').read())['${_JSON_ROOT}'])))' || 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.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.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\n\n # config.admob_delay_app_measurement_init\n _ADMOB_DELAY_APP_MEASUREMENT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"admob_delay_app_measurement_init\")\n if [[ $_ADMOB_DELAY_APP_MEASUREMENT == \"true\" ]]; then\n _PLIST_ENTRY_KEYS+=(\"GADDelayAppMeasurementInit\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"YES\")\n fi\n\n # config.admob_ios_app_id\n _ADMOB_IOS_APP_ID=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"admob_ios_app_id\")\n if [[ $_ADMOB_IOS_APP_ID ]]; then\n _PLIST_ENTRY_KEYS+=(\"GADApplicationIdentifier\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_ADMOB_IOS_APP_ID\")\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";
+ };
5369A53AD507805BDB117490 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -755,6 +774,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Frontend/FrontendDebug.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
diff --git a/ios/Frontend/AppDelegate.m b/ios/Frontend/AppDelegate.m
index 31f06642..2b52605f 100644
--- a/ios/Frontend/AppDelegate.m
+++ b/ios/Frontend/AppDelegate.m
@@ -4,6 +4,7 @@
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import "RNSplashScreen.h"
+#import <Firebase.h>
#if DEBUG
#import <FlipperKit/FlipperClient.h>
@@ -26,12 +27,23 @@ static void InitializeFlipper(UIApplication *application) {
@implementation AppDelegate
+- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
+ fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
+ // If you are receiving a notification message while your app is in the background,
+ // this callback will not be fired till the user taps on the notification launching the application.
+
+ completionHandler(UIBackgroundFetchResultNewData);
+}
+
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
#if DEBUG
InitializeFlipper(application);
#endif
+ if ([FIRApp defaultApp] == nil) {
+ [FIRApp configure];
+ }
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"Frontend"
diff --git a/ios/Frontend/FrontendDebug.entitlements b/ios/Frontend/FrontendDebug.entitlements
new file mode 100644
index 00000000..903def2a
--- /dev/null
+++ b/ios/Frontend/FrontendDebug.entitlements
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>aps-environment</key>
+ <string>development</string>
+</dict>
+</plist>
diff --git a/ios/Frontend/Info.plist b/ios/Frontend/Info.plist
index ebd7ab42..0f036f56 100644
--- a/ios/Frontend/Info.plist
+++ b/ios/Frontend/Info.plist
@@ -58,6 +58,10 @@
<array>
<string>Feather.ttf</string>
</array>
+ <key>UIBackgroundModes</key>
+ <array>
+ <string>remote-notification</string>
+ </array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
diff --git a/ios/GoogleService-Info.plist b/ios/GoogleService-Info.plist
new file mode 100644
index 00000000..3d26d583
--- /dev/null
+++ b/ios/GoogleService-Info.plist
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CLIENT_ID</key>
+ <string>70695794810-til15sn23sl3rvfjhts1i23ar91rcdlp.apps.googleusercontent.com</string>
+ <key>REVERSED_CLIENT_ID</key>
+ <string>com.googleusercontent.apps.70695794810-til15sn23sl3rvfjhts1i23ar91rcdlp</string>
+ <key>API_KEY</key>
+ <string>AIzaSyCSd9VFojlYo5uQmZZ8UuUmp9cFs34QoaA</string>
+ <key>GCM_SENDER_ID</key>
+ <string>70695794810</string>
+ <key>PLIST_VERSION</key>
+ <string>1</string>
+ <key>BUNDLE_ID</key>
+ <string>com.taggid.taggid</string>
+ <key>PROJECT_ID</key>
+ <string>tagg-32a02</string>
+ <key>STORAGE_BUCKET</key>
+ <string>tagg-32a02.appspot.com</string>
+ <key>IS_ADS_ENABLED</key>
+ <false></false>
+ <key>IS_ANALYTICS_ENABLED</key>
+ <false></false>
+ <key>IS_APPINVITE_ENABLED</key>
+ <true></true>
+ <key>IS_GCM_ENABLED</key>
+ <true></true>
+ <key>IS_SIGNIN_ENABLED</key>
+ <true></true>
+ <key>GOOGLE_APP_ID</key>
+ <string>1:70695794810:ios:f79314e27c58272c5ce3c3</string>
+ <key>DATABASE_URL</key>
+ <string>https://tagg-32a02.firebaseio.com</string>
+</dict>
+</plist> \ No newline at end of file
diff --git a/ios/Podfile b/ios/Podfile
index dd88c40a..64383591 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -30,3 +30,8 @@ target 'Frontend-tvOS' do
# Pods for testing
end
end
+
+# add the Firebase pod for Google Analytics
+pod 'Firebase/Analytics'
+# add pods for any other desired Firebase products
+# https://firebase.google.com/docs/ios/setup#available-pods \ No newline at end of file
diff --git a/package.json b/package.json
index ad71b04a..a40111a0 100644
--- a/package.json
+++ b/package.json
@@ -14,6 +14,8 @@
"@react-native-community/async-storage": "^1.12.1",
"@react-native-community/blur": "^3.6.0",
"@react-native-community/masked-view": "^0.1.10",
+ "@react-native-firebase/app": "^10.0.0",
+ "@react-native-firebase/messaging": "^10.0.0",
"@react-navigation/bottom-tabs": "^5.7.2",
"@react-navigation/native": "^5.6.1",
"@react-navigation/stack": "^5.6.2",
@@ -26,6 +28,7 @@
"react-native-animatable": "^1.3.3",
"react-native-confirmation-code-field": "^6.5.0",
"react-native-date-picker": "^3.2.5",
+ "react-native-device-info": "^7.3.1",
"react-native-elements": "^2.3.2",
"react-native-gesture-handler": "^1.6.1",
"react-native-hyperlink": "^0.0.19",
@@ -33,6 +36,7 @@
"react-native-inappbrowser-reborn": "^3.4.0",
"react-native-linear-gradient": "^2.5.6",
"react-native-picker-select": "^7.0.0",
+ "react-native-push-notifications": "^3.0.10",
"react-native-reanimated": "^1.9.0",
"react-native-safe-area-context": "^3.0.6",
"react-native-screens": "^2.9.0",
diff --git a/src/App.tsx b/src/App.tsx
index e1cd83cc..92d26ba7 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,10 +1,16 @@
-import React from 'react';
+import React, {useEffect} from 'react';
import {NavigationContainer} from '@react-navigation/native';
import Routes from './routes';
import {Provider} from 'react-redux';
import store from './store/configureStore';
+import {fcmService} from './services/FCMService';
const App = () => {
+ useEffect(() => {
+ fcmService.setUpPushNotifications();
+ //If permissions are not there, deactivateFcmService
+ });
+
return (
/**
* This is the provider from the redux store, it acts as the root provider for our application
diff --git a/src/constants/api.ts b/src/constants/api.ts
index 890ef102..e1658993 100644
--- a/src/constants/api.ts
+++ b/src/constants/api.ts
@@ -1,5 +1,6 @@
// const BASE_URL: string = 'http://3.22.188.127/'; // prod server
const BASE_URL: string = 'http://127.0.0.1:8000/'; // local server
+
const API_URL: string = BASE_URL + 'api/';
export const LOGIN_ENDPOINT: string = API_URL + 'login/';
export const LOGOUT_ENDPOINT: string = API_URL + 'logout/';
@@ -28,6 +29,9 @@ export const BLOCK_USER_ENDPOINT: string = API_URL + 'block/';
export const PASSWORD_RESET_ENDPOINT: string = API_URL + 'password-reset/';
export const MOMENT_CATEGORY_ENDPOINT: string = API_URL + 'moment-category/';
+// Register as FCM device
+export const FCM_ENDPOINT: string = API_URL + 'fcm/';
+
// Register Social Link (Non-integrated)
export const LINK_SNAPCHAT_ENDPOINT: string = API_URL + 'link-sc/';
export const LINK_TIKTOK_ENDPOINT: string = API_URL + 'link-tt/';
diff --git a/src/screens/onboarding/CategorySelection.tsx b/src/screens/onboarding/CategorySelection.tsx
index f92b7e39..b9677ed4 100644
--- a/src/screens/onboarding/CategorySelection.tsx
+++ b/src/screens/onboarding/CategorySelection.tsx
@@ -21,7 +21,7 @@ import {MOMENT_CATEGORIES} from '../../constants';
import {OnboardingStackParams} from '../../routes';
import {StackNavigationProp} from '@react-navigation/stack';
import {getTokenOrLogout, userLogin} from '../../utils';
-import {postMomentCategories} from '../../services';
+import {fcmService, postMomentCategories} from '../../services';
import {updateMomentCategories} from '../../store/actions/momentCategories';
import {ScrollView} from 'react-native-gesture-handler';
@@ -135,6 +135,7 @@ const CategorySelection: React.FC<CategorySelectionProps> = ({
const token = await getTokenOrLogout(dispatch);
await postMomentCategories(selectedCategories, token);
userLogin(dispatch, {userId: userId, username: username});
+ fcmService.sendFcmTokenToServer();
} else {
dispatch(updateMomentCategories(selectedCategories, true, userId));
navigation.goBack();
diff --git a/src/screens/onboarding/Login.tsx b/src/screens/onboarding/Login.tsx
index 1315fdf5..3e59b00e 100644
--- a/src/screens/onboarding/Login.tsx
+++ b/src/screens/onboarding/Login.tsx
@@ -12,7 +12,7 @@ import {
KeyboardAvoidingView,
Platform,
} from 'react-native';
-
+import {fcmService} from '../../services';
import {OnboardingStackParams} from '../../routes/onboarding';
import {Background, TaggInput, SubmitButton} from '../../components';
import {usernameRegex, LOGIN_ENDPOINT} from '../../constants';
@@ -164,6 +164,7 @@ const Login: React.FC<LoginProps> = ({navigation}: LoginProps) => {
await AsyncStorage.setItem('userId', data.UserID);
await AsyncStorage.setItem('username', username);
userLogin(dispatch, {userId: data.UserID, username});
+ fcmService.sendFcmTokenToServer();
} catch (err) {
setUser(NO_USER);
console.log(data);
diff --git a/src/services/FCMService.ts b/src/services/FCMService.ts
new file mode 100644
index 00000000..11cb7510
--- /dev/null
+++ b/src/services/FCMService.ts
@@ -0,0 +1,177 @@
+import AsyncStorage from '@react-native-community/async-storage';
+import messaging from '@react-native-firebase/messaging';
+import {Platform} from 'react-native';
+import {getDeviceId, getDeviceName} from 'react-native-device-info';
+import {FCM_ENDPOINT} from '../constants';
+
+class FCMService {
+ setUpPushNotifications = () => {
+ // Requesting user to permit notifications
+ this.checkPermission();
+
+ // Registering with FCM to receive unique device/app token
+ this.registerAppWithFCM();
+
+ //Store registration_id/device token to AsyncStorage
+ this.getToken();
+
+ // Receive a notification
+ this.createNotificationListeners();
+
+ // // Schedule a local notification
+ // PushNotification.localNotificationSchedule({
+ // //... You can use all the options from localNotifications
+ // message: 'My Notification Message', // (required)
+ // date: new Date(Date.now() + 60 * 1000), // in 60 secs
+ // allowWhileIdle: false, // (optional) set notification to work while on doze, default: false
+ // });
+
+ // // Send local notification when app in foreground since remote notifications
+ // // aren't displayed when app is in the foreground
+ // PushNotification.localNotification({
+ // //... You can use all the options from localNotifications
+ // message: 'My Notification Message', // (required)
+ // date: new Date(Date.now() + 60 * 1000), // in 60 secs
+ // allowWhileIdle: false, // (optional) set notification to work while on doze, default: false
+ //});
+ };
+
+ registerAppWithFCM = async () => {
+ if (Platform.OS === 'ios') {
+ if (!messaging().isDeviceRegisteredForRemoteMessages) {
+ await messaging().registerDeviceForRemoteMessages();
+ }
+ await messaging().setAutoInitEnabled(true);
+ }
+ };
+
+ checkPermission = async () => {
+ try {
+ const permission = await messaging().hasPermission();
+ // Permission might be 0 (not allowed), 1 (allowed), -1(unknown)
+ if (permission !== 1) {
+ await messaging().requestPermission({
+ sound: true,
+ announcement: true,
+ badge: true,
+ alert: true,
+ });
+ }
+ } catch (error) {
+ console.log('[FCMService] Permission Rejected ', error);
+ }
+ };
+
+ // Receiving fcm unique device token to receive remote messages through fcm
+ getToken = async () => {
+ messaging()
+ .getToken()
+ .then(async (fcmToken) => {
+ if (fcmToken) {
+ await AsyncStorage.setItem('@fcmToken', fcmToken);
+ return fcmToken;
+ }
+ })
+ .catch((error) => {
+ console.log('[FCMService] getToken rejected', error);
+ });
+ return '';
+ };
+
+ sendFcmTokenToServer = async () => {
+ const registration_id: string | null = await AsyncStorage.getItem(
+ '@fcmToken',
+ );
+ const device_id = getDeviceId();
+ const type = Platform.OS;
+ let active: boolean = false;
+ let name: string = '';
+ await getDeviceName().then((deviceName) => {
+ name = deviceName;
+ });
+
+ await messaging()
+ .hasPermission()
+ .then((hasPermission) => {
+ active = hasPermission === 1;
+ });
+ const token = await AsyncStorage.getItem('token');
+
+ if (registration_id && type) {
+ let response = await fetch(FCM_ENDPOINT, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: 'Token ' + token,
+ },
+ body: JSON.stringify({
+ registration_id,
+ type,
+ device_id,
+ name,
+ active,
+ }),
+ });
+
+ if (response.status === 201) {
+ console.log('Successfully stored device token!');
+ } else {
+ console.log('Failed to store device token!');
+ console.log(response);
+ }
+ }
+ };
+
+ deactivateFcmService = async () => {
+ //Make PATCH call to deactivate device
+ console.log('Deactivating FCM device');
+ };
+
+ createNotificationListeners = () => {
+ // messaging().onNotificationOpenedApp((remoteMessage) => {
+ // console.log(
+ // '[FCMService] onNotificationOpenedApp Notification caused app to open',
+ // );
+ // if (remoteMessage) {
+ // const notification = remoteMessage.notification;
+ // onOpenNotification(notification);
+ // }
+ // });
+
+ // messaging()
+ // .getInitialNotification()
+ // .then((remoteMessage) => {
+ // console.log(
+ // '[FCMService] getInitialNotification Notification caused app to open',
+ // );
+
+ // if (remoteMessage) {
+ // const notification = remoteMessage.notification;
+ // onOpenNotification(notification);
+ // }
+ // });
+
+ messaging().onMessage((remoteMessage) => {
+ console.log('Received a remote notification!!');
+ if (remoteMessage) {
+ let notification = remoteMessage.notification;
+ let notificationId = remoteMessage.messageId;
+ console.log(
+ 'notificationsId: ',
+ notificationId,
+ ' notification: ',
+ notification,
+ );
+ }
+ });
+
+ messaging().onTokenRefresh((fcmToken) => {
+ AsyncStorage.setItem('@fcmToken', fcmToken).catch((err) => {
+ console.log('Failed to store new token!');
+ console.log(err);
+ });
+ });
+ };
+}
+
+export const fcmService = new FCMService();
diff --git a/src/services/index.ts b/src/services/index.ts
index d98996ba..81a09b92 100644
--- a/src/services/index.ts
+++ b/src/services/index.ts
@@ -6,3 +6,4 @@ export * from './UserFollowServices';
export * from './ReportingService';
export * from './BlockUserService';
export * from './MomentCategoryService';
+export * from './FCMService';