diff --git a/.gitignore b/.gitignore index 7233a1c..39359e5 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ build/ local.properties *.iml *.hprof +google-services.json # Own # diff --git a/NOTIFICATIONS.md b/NOTIFICATIONS.md new file mode 100644 index 0000000..90507e0 --- /dev/null +++ b/NOTIFICATIONS.md @@ -0,0 +1,5 @@ +## 🎛 Notifications + +Detailed guide will be published soon... + +Meanwhile, you can check this [tutorial](https://dev.to/jakubkoci/react-native-push-notifications-313i) which helped me to set up and test notifications. \ No newline at end of file diff --git a/README.md b/README.md index e4a8629..ef55e36 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ yarn ios yarn android ``` -👁‍🗨 If you are planning to use Expo modules, such as preconfigured `expo-updates` and others, or add new ones, what I strongly recommend, then proceed to [Expo Configuration](/EXPO_CONFIGURATION.md) and follow the steps carefully. +👁‍🗨 If you are planning to use Expo modules, such as preconfigured `expo-updates` and others, or add new ones, what I strongly recommend, then proceed to [Expo Configuration](/EXPO_CONFIGURATION.md) and follow the steps. 🟢 If you would like to rename the app, you can use [react-native-rename-next](https://github.com/mayconmesquita/react-native-rename-next). Don't forget to run `yarn ios:pods` after the process is finished. Also keep in mind that bundle identifier must be valid for both platforms or change it manually. @@ -52,28 +52,31 @@ yarn android - [Reanimated 2](https://github.com/software-mansion/react-native-reanimated) - React Native's Animated library reimplemented - [MobX](https://github.com/mobxjs/mobx) - simple, scalable state management, with [mobx-persist](https://github.com/pinqy520/mobx-persist) for persisting your stores ### Extra helpful libraries -- [React Native Navigation Hooks](https://github.com/underscopeio/react-native-navigation-hooks) - a set of React hooks for React Native Navigation +- [React Native Notifications](https://github.com/wix/react-native-notifications) - a library that takes care of handling notifications +- [React Native Navigation Hooks](https://github.com/underscopeio/react-native-navigation-hooks) - a set of hooks for React Native Navigation - [React Native Vector Icons](https://github.com/oblador/react-native-vector-icons) - customizable icons for React Native. Even though Expo SDK includes `expo-icons` which you can still use, but the reason of adding `react-native-vector-icons` is to get icons as sources before launching tab based app - [React Native Gesture Handler](https://github.com/kmagiera/react-native-gesture-handler) - native touches and gesture system for React Native - [React Native Defice Info](https://github.com/react-native-community/react-native-device-info) - device information for React Native iOS and Android - [Hermes Engine](https://reactnative.dev/docs/hermes) - a JavaScript engine optimized for running React Native apps on Android. - [Typescript](https://www.typescriptlang.org/) - strict syntactical superset of JavaScript -### Small useful libraries/hooks from me +### Small useful services/hooks from me - `useStyles()` - a hook that takes care of dark mode in your app. Supports toggling modes while you are in app. No dependencies (needs only `react-native`, so could be reusable). - `appUpdates` - a service that shows and simplifies integration with `expo-updates`. In order to use it, you will need to change `Expo.plist` and `AndroidManifest.xml` with your actual information. More information about [expo-updates](https://docs.expo.io/versions/latest/sdk/updates/). - `translate` - a service that shows and simplifies integration with `expo-localization` and `i18n-js`. You can see an example of `en`, `ru` and `de` localizations in `ExpoScreen`. +- `notifications` - a service that takes care of setting up notifications which utilizes [React Native Notifications](https://github.com/wix/react-native-notifications). For more information, please, take a look at this [guide](/NOTIFICATIONS.md). ## Enhancements There are still some things I would like to add to the starter: - 🔳 Dark Mode support. - 🔳 Localization via [expo-localization](https://docs.expo.io/versions/latest/sdk/localization) and [i18n-js](https://github.com/fnando/i18n-js). -- ⬜️ Notifications (remote). +- 🔳 Notifications (remote) using [React Native Notifications](https://github.com/wix/react-native-notifications). Feel free to open an issue for suggestions. ## Known issues (warnings) - Expo splash screen. There is some weird behavior using `expo-splash-screen` with `react-native-navigation`. That is why this module has been excluded: [ios](https://github.com/kanzitelli/expo-rnn-starter/blob/master/ios/Podfile#L8) & [android](https://github.com/kanzitelli/expo-rnn-starter/blob/master/android/app/build.gradle#L196). - Dark Mode in Android. React Native Navigation doesn't toggle navigation and tab bars' background color to dark when dark mode is toggled on. However it does so on iOS. As a workaround, we can subscribe to toggle events and then using `Navigation.mergeOptions` & `Navigation.setDefaultOptions` to change stylings for navigations and tab bars. Anyways, it needs some time to dive into it and come up with better solution. Current progress could be found [here](https://github.com/kanzitelli/expo-rnn-starter/tree/android-dark-mode). +- Notifications received in background for iOS and Android. There is an issue with getting a callback triggered for notifications received while the app in background. For more information, please, check this [issue](https://github.com/wix/react-native-notifications/issues/670). Feel free to open an issue for any other warning or problems. diff --git a/android/app/build.gradle b/android/app/build.gradle index 2943c92..18a2a9a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -216,6 +216,9 @@ dependencies { } else { implementation jscFlavor } + + implementation project(':react-native-notifications') + implementation 'com.google.firebase:firebase-core:16.0.0' } // Run this once to be able to run the application with BUCK @@ -226,4 +229,5 @@ task copyDownloadableDepsToLibs(type: Copy) { } apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) -apply from: "../../node_modules/react-native-vector-icons/fonts.gradle" \ No newline at end of file +apply from: "../../node_modules/react-native-vector-icons/fonts.gradle" +apply plugin: 'com.google.gms.google-services' \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 43e5ad7..186f86a 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -26,6 +26,17 @@ + + + + + + + + + diff --git a/android/app/src/main/res/drawable-hdpi/notification_icon.png b/android/app/src/main/res/drawable-hdpi/notification_icon.png new file mode 100644 index 0000000..715a891 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/notification_icon.png differ diff --git a/android/app/src/main/res/drawable-mdpi/notification_icon.png b/android/app/src/main/res/drawable-mdpi/notification_icon.png new file mode 100644 index 0000000..73850fc Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/notification_icon.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/notification_icon.png b/android/app/src/main/res/drawable-xhdpi/notification_icon.png new file mode 100644 index 0000000..cf12ca4 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/notification_icon.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/notification_icon.png b/android/app/src/main/res/drawable-xxhdpi/notification_icon.png new file mode 100644 index 0000000..ef0e23c Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/notification_icon.png differ diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..d33d01b --- /dev/null +++ b/android/app/src/main/res/values/colors.xml @@ -0,0 +1,4 @@ + + + #4D7198 + \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index acf9299..132bbe5 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -17,6 +17,7 @@ buildscript { classpath('com.android.tools.build:gradle:4.0.0') // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files + classpath 'com.google.gms:google-services:4.3.4' } } diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 2360dd1..63e3f83 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -225,6 +225,8 @@ PODS: - React-cxxreact (= 0.63.4) - React-jsi (= 0.63.4) - React-jsinspector (0.63.4) + - react-native-notifications (3.4.1): + - React-Core - React-RCTActionSheet (0.63.4): - React-Core/RCTActionSheetHeaders (= 0.63.4) - React-RCTAnimation (0.63.4): @@ -383,6 +385,7 @@ DEPENDENCIES: - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`) - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`) + - react-native-notifications (from `../node_modules/react-native-notifications`) - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`) - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`) @@ -479,6 +482,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/jsiexecutor" React-jsinspector: :path: "../node_modules/react-native/ReactCommon/jsinspector" + react-native-notifications: + :path: "../node_modules/react-native-notifications" React-RCTActionSheet: :path: "../node_modules/react-native/Libraries/ActionSheetIOS" React-RCTAnimation: @@ -572,6 +577,7 @@ SPEC CHECKSUMS: React-jsi: a0418934cf48f25b485631deb27c64dc40fb4c31 React-jsiexecutor: 93bd528844ad21dc07aab1c67cb10abae6df6949 React-jsinspector: 58aef7155bc9a9683f5b60b35eccea8722a4f53a + react-native-notifications: 77c7c863273e79caaa2523f239e714b033995262 React-RCTActionSheet: 89a0ca9f4a06c1f93c26067af074ccdce0f40336 React-RCTAnimation: 1bde3ecc0c104c55df246eda516e0deb03c4e49b React-RCTBlob: a97d378b527740cc667e03ebfa183a75231ab0f0 diff --git a/ios/rnn_starter.xcodeproj/project.pbxproj b/ios/rnn_starter.xcodeproj/project.pbxproj index 0b9c638..d7c586f 100644 --- a/ios/rnn_starter.xcodeproj/project.pbxproj +++ b/ios/rnn_starter.xcodeproj/project.pbxproj @@ -745,7 +745,7 @@ "-ObjC", "-lc++", ); - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_BUNDLE_IDENTIFIER = "io.batyr.expo-rnn-starter"; PRODUCT_NAME = rnn_starter; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -771,7 +771,7 @@ "-ObjC", "-lc++", ); - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_BUNDLE_IDENTIFIER = "io.batyr.expo-rnn-starter"; PRODUCT_NAME = rnn_starter; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/ios/rnn_starter/AppDelegate.m b/ios/rnn_starter/AppDelegate.m index 59eafaa..5f4a609 100644 --- a/ios/rnn_starter/AppDelegate.m +++ b/ios/rnn_starter/AppDelegate.m @@ -8,6 +8,8 @@ #import #import +#import "RNNotifications.h" + #ifdef FB_SONARKIT_ENABLED #import #import @@ -42,6 +44,8 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( InitializeFlipper(application); #endif + [RNNotifications startMonitorNotifications]; + self.moduleRegistryAdapter = [[UMModuleRegistryAdapter alloc] initWithModuleRegistryProvider:[[UMModuleRegistryProvider alloc] init]]; self.launchOptions = launchOptions; @@ -89,4 +93,14 @@ - (void)appController:(EXUpdatesAppController *)appController didStartWithSucces appController.bridge = [self initializeReactNativeApp]; } +-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken +{ + [RNNotifications didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; +} + +- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error +{ + [RNNotifications didFailToRegisterForRemoteNotificationsWithError:error]; +} + @end diff --git a/package.json b/package.json index f5a3b29..9c31f2d 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "react-native-gesture-handler": "^1.9.0", "react-native-navigation": "^7.6.0", "react-native-navigation-hooks": "^6.2.0", + "react-native-notifications": "^3.4.1", "react-native-reanimated": "2.0.0-rc.0", "react-native-unimodules": "^0.12.0", "react-native-vector-icons": "^7.1.0" diff --git a/src/services/index.tsx b/src/services/index.tsx index c75a68a..108bce4 100644 --- a/src/services/index.tsx +++ b/src/services/index.tsx @@ -3,11 +3,13 @@ import React from 'react'; import NavigationService from './navigation'; import AppUpdatesService from './appUpdates'; import TranslateService from './translate'; +import NotificationsService from './notifications'; export const services = { navigation: NavigationService, appUpdates: AppUpdatesService, t: TranslateService, + notifications: NotificationsService, }; const servicesContext = React.createContext(services); diff --git a/src/services/notifications.ts b/src/services/notifications.ts new file mode 100644 index 0000000..e05fa1d --- /dev/null +++ b/src/services/notifications.ts @@ -0,0 +1,55 @@ +import { NotificationBackgroundFetchResult, Notifications as RNNotifications } from 'react-native-notifications'; + +class NotificationsService implements IService { + init = async () => { + // this.registerReactNativeNotifications(); + } + + private registerReactNativeNotifications() { + this.registerDevice() + this.registerNotificationEvents() + } + + private registerDevice() { + RNNotifications.events().registerRemoteNotificationsRegistered(event => { + // TODO: Send the token to my server so it could send back push notifications... + console.log('[RNNotification] Device Token Received', event.deviceToken) + }) + RNNotifications.events().registerRemoteNotificationsRegistrationFailed(event => { + console.error(event) + }) + + RNNotifications.registerRemoteNotifications() + } + + private registerNotificationEvents() { + RNNotifications.events().registerNotificationReceivedForeground((notification, completion) => { + console.log('[RNNotification] Notification Received - Foreground', notification) + + // Calling completion on iOS with `alert: true` will present the native iOS inApp notification. + completion({ alert: false, sound: false, badge: false }) + }) + + RNNotifications.events().registerNotificationOpened((notification, completion) => { + console.log('[RNNotification] Notification opened by device user', notification) + console.log(`[RNNotification] Notification opened with an action identifier: ${notification.identifier}`) + completion() + }) + + // doesn't work for some reason + RNNotifications.events().registerNotificationReceivedBackground((notification, completion) => { + console.log('[RNNotification] Notification Received - Background', notification) + + // Calling completion on iOS with `alert: true` will present the native iOS inApp notification. + completion(NotificationBackgroundFetchResult.NEW_DATA); + }) + + RNNotifications.getInitialNotification() + .then(notification => { + console.log('[RNNotification] Initial notification was:', notification || 'N/A') + }) + .catch(err => console.error('[RNNotification] getInitialNotifiation() failed', err)) + } +} + +export default new NotificationsService(); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 0c7f348..5efca5f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7966,6 +7966,11 @@ react-native-navigation@^7.6.0: react-lifecycles-compat "2.0.0" tslib "1.9.3" +react-native-notifications@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/react-native-notifications/-/react-native-notifications-3.4.1.tgz#3046bae51863e1073d71820598f0b85a3b389535" + integrity sha512-jJHo5z5XR0smZIVj4yjoqDWZ3HX26TL8FMsUIBiIy1Hh9b0q/HAl1xN651sTW/HM49+Mr2F+ShSf6nXBnGB4fQ== + react-native-reanimated@2.0.0-rc.0: version "2.0.0-rc.0" resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-2.0.0-rc.0.tgz#7a1b0bfd48e3de9dfa985a524463b6a216531358"