├── src ├── components │ └── index.js ├── index.js ├── views │ ├── index.js │ └── DefaultPopup.js └── utils │ └── index.js ├── .eslintignore ├── .gitignore ├── .babelrc ├── .editorconfig ├── types.d.ts ├── .eslintrc.js ├── LICENSE ├── package.json └── README.md /src/components/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react-native"] 3 | } 4 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { DefaultPopup } from './views'; 2 | 3 | export default DefaultPopup; 4 | -------------------------------------------------------------------------------- /src/views/index.js: -------------------------------------------------------------------------------- 1 | import DefaultPopup from './DefaultPopup'; 2 | 3 | export { 4 | DefaultPopup 5 | }; 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | quote_type = single 11 | -------------------------------------------------------------------------------- /types.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | declare module "react-native-push-notification-popup" { 5 | import { ReactElement, Component } from "react"; 6 | import { StyleProp, ViewStyle, ImageSourcePropType } from "react-native"; 7 | 8 | interface ContentOptionsBase { 9 | appIconSource?: ImageSourcePropType; 10 | appTitle?: string; 11 | timeText?: string; 12 | title?: string; 13 | body?: string; 14 | } 15 | 16 | interface ShowOptions extends ContentOptionsBase { 17 | onPress?: () => void; 18 | slideOutTime?: number; 19 | } 20 | 21 | interface PushNotificationPopupProps { 22 | renderPopupContent?: (options: ContentOptionsBase) => ReactElement; 23 | shouldChildHandleResponderStart?: boolean; 24 | shouldChildHandleResponderMove?: boolean; 25 | isSkipStatusBarPadding?: boolean; 26 | } 27 | 28 | export default class ReactNativePushNotificationPopup extends Component { 29 | public show(options: ShowOptions): void; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "node": true 6 | }, 7 | "extends": ["eslint:recommended", "plugin:react/recommended"], 8 | "parser": "babel-eslint", 9 | "parserOptions": { 10 | "ecmaVersion": 2017, 11 | "ecmaFeatures": { 12 | "experimentalObjectRestSpread": true, 13 | "jsx": true 14 | }, 15 | "sourceType": "module" 16 | }, 17 | "plugins": [ 18 | "react" 19 | ], 20 | "rules": { 21 | "indent": [ 22 | "error", 23 | 2, 24 | { "flatTernaryExpressions": true, "SwitchCase": 1 } 25 | ], 26 | "linebreak-style": [ 27 | "error", 28 | "unix" 29 | ], 30 | "no-console": 0, 31 | "quotes": [ 32 | "error", 33 | "single" 34 | ], 35 | "semi": [ 36 | "error", 37 | "always" 38 | ], 39 | "no-unused-vars": ["error", { "args": "none" }] 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Carson Wah 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-push-notification-popup", 3 | "version": "1.7.0", 4 | "description": "React Native Push Notification Popup Component", 5 | "main": "src/index.js", 6 | "types": "./types.d.ts", 7 | "scripts": { 8 | "lint": "eslint --fix --ext js src" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/carsonwah/react-native-push-notification-popup.git" 13 | }, 14 | "keywords": [ 15 | "react", 16 | "react-native", 17 | "react-component", 18 | "react-native-component", 19 | "push-notification", 20 | "ios", 21 | "android", 22 | "modal", 23 | "popup", 24 | "dialog" 25 | ], 26 | "author": "Carson Wah", 27 | "license": "MIT", 28 | "bugs": { 29 | "url": "https://github.com/carsonwah/react-native-push-notification-popup/issues" 30 | }, 31 | "homepage": "https://github.com/carsonwah/react-native-push-notification-popup#readme", 32 | "devDependencies": { 33 | "babel-eslint": "^8.2.3", 34 | "babel-preset-react-native": "^4.0.0", 35 | "eslint": "^4.19.1", 36 | "eslint-plugin-react": "^7.7.0", 37 | "react": "^16.8.6", 38 | "react-native": "^0.59.8" 39 | }, 40 | "dependencies": { 41 | "prop-types": "^15.7.2" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | import { Dimensions, Platform, StatusBar } from 'react-native'; 2 | 3 | // Code borrowed from https://github.com/ovr/react-native-status-bar-height 4 | const STATUSBAR_DEFAULT_HEIGHT = 20; 5 | const STATUSBAR_X_HEIGHT = 44; 6 | const STATUSBAR_IP12_HEIGHT = 47; 7 | const STATUSBAR_IP12MAX_HEIGHT = 47; 8 | const STATUSBAR_IP14PRO_HEIGHT = 54; 9 | const STATUSBAR_IP14PROMAX_HEIGHT = 54; 10 | 11 | const X_WIDTH = 375; 12 | const X_HEIGHT = 812; 13 | 14 | const XSMAX_WIDTH = 414; 15 | const XSMAX_HEIGHT = 896; 16 | 17 | const IP12_WIDTH = 390; 18 | const IP12_HEIGHT = 844; 19 | 20 | const IP12MAX_WIDTH = 428; 21 | const IP12MAX_HEIGHT = 926; 22 | 23 | const IP14PRO_WIDTH = 393; 24 | const IP14PRO_HEIGHT = 852; 25 | 26 | const IP14PROMAX_WIDTH = 430; 27 | const IP14PROMAX_HEIGHT = 932; 28 | 29 | const { height: W_HEIGHT, width: W_WIDTH } = Dimensions.get('window'); 30 | 31 | let statusBarHeight = STATUSBAR_DEFAULT_HEIGHT; 32 | 33 | if (Platform.OS === 'ios' && !Platform.isPad && !Platform.isTVOS) { 34 | if (W_WIDTH === X_WIDTH && W_HEIGHT === X_HEIGHT) { 35 | statusBarHeight = STATUSBAR_X_HEIGHT; 36 | } else if (W_WIDTH === XSMAX_WIDTH && W_HEIGHT === XSMAX_HEIGHT) { 37 | statusBarHeight = STATUSBAR_X_HEIGHT; 38 | } else if (W_WIDTH === IP12_WIDTH && W_HEIGHT === IP12_HEIGHT) { 39 | statusBarHeight = STATUSBAR_IP12_HEIGHT; 40 | } else if (W_WIDTH === IP12MAX_WIDTH && W_HEIGHT === IP12MAX_HEIGHT) { 41 | statusBarHeight = STATUSBAR_IP12MAX_HEIGHT; 42 | } else if (W_WIDTH === IP14PRO_WIDTH && W_HEIGHT === IP14PRO_HEIGHT) { 43 | statusBarHeight = STATUSBAR_IP14PRO_HEIGHT; 44 | } else if (W_WIDTH === IP14PROMAX_WIDTH && W_HEIGHT === IP14PROMAX_HEIGHT) { 45 | statusBarHeight = STATUSBAR_IP14PROMAX_HEIGHT; 46 | } 47 | } 48 | 49 | export function getStatusBarHeight() { 50 | return Platform.select({ 51 | ios: statusBarHeight, 52 | android: StatusBar.currentHeight, 53 | default: 0, 54 | }); 55 | } 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Native Push Notification Popup 2 | 3 | ![npm version](https://img.shields.io/npm/v/react-native-push-notification-popup) 4 | ![npm downloads](https://img.shields.io/npm/dm/react-native-push-notification-popup) 5 | ![npm license](https://img.shields.io/npm/l/react-native-push-notification-popup) 6 | ![maintained](https://img.shields.io/badge/Maintained%3F-yes-success) 7 | ![ask me](https://img.shields.io/badge/Ask%20me-anything-27b092) 8 | 9 | ![iOS Preview](https://github.com/carsonwah/_file_hosting/blob/master/react-native-push-notification-popup/ios-example.gif?raw=true) ![Android Preview](https://github.com/carsonwah/_file_hosting/blob/master/react-native-push-notification-popup/android-example.gif?raw=true) 10 | 11 | ## Features 12 | 13 | - Support "pan" gesture 14 | - Support "onPress" gesture feedback 15 | - Written in pure-JS using official react-native `Animation` package 16 | - *Which means it supports all Expo/CRNA apps* 17 | - Support iPhone X, XS, Max (yeah that notch) 18 | - Support Android native "elevation" 19 | 20 | ## Motivations 21 | 22 | [Blog post](https://medium.com/@carsonwah/show-push-notification-popup-in-react-native-19db965a5603) 23 | 24 | 1. In some apps, you may just want to display reminders to user, without going through those troublesome push notification setups 25 | 2. Expo/CNRA apps [cannot display push notification while app is in foreground](https://docs.expo.io/versions/v27.0.0/guides/push-notifications#notification-handling-timing) 26 | 3. Even if you eject, you still need to configure [iOS](https://stackoverflow.com/questions/14872088/get-push-notification-while-app-in-foreground-ios) and [Android](https://stackoverflow.com/questions/38451235/how-to-handle-the-fire-base-notification-when-app-is-in-foreground) separately with native codes 27 | 28 | This package is here to help. Just show your own notification popup to your users! 29 | 30 | ## Installation 31 | 32 | ```bash 33 | # yarn, recommended 34 | yarn add react-native-push-notification-popup 35 | 36 | # or npm 37 | npm install react-native-push-notification-popup --save 38 | ``` 39 | 40 | ## Usage 41 | 42 | ### Declare Component 43 | 44 | Put it in a wrapper component. (Maybe where you handle your incoming push notifications) 45 | 46 | ```javascript 47 | import NotificationPopup from 'react-native-push-notification-popup'; 48 | 49 | class MyComponent extends React.Component { 50 | render() { 51 | return ( 52 | 53 | 54 | this.popup = ref} /> 55 | 56 | ); 57 | } 58 | // ... 59 | ``` 60 | 61 | > **IMPORTANT**: Remember to put it on the **bottom of other components**, because React render from back to front in order of declaration. We do not use `zIndex` becuase it is [problematic on Android](https://github.com/carsonwah/react-native-push-notification-popup/issues/21). 62 | 63 | #### Optional: Customize your popup 64 | 65 | ```javascript 66 | // Render function 67 | const renderCustomPopup = ({ appIconSource, appTitle, timeText, title, body }) => ( 68 | 69 | {title} 70 | {body} 71 |