├── .gitignore ├── .npmignore ├── README.md ├── package-lock.json ├── package.json ├── src ├── index.ts └── notifications │ ├── Notification.css │ ├── Notifications.tsx │ ├── PushNotification.tsx │ └── Storage.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabioshub/react-push-notification/2b5d8a0863b096a51c30e703c4152f4c514d858d/.npmignore -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![npm version](https://badgen.net/npm/v/react-push-notification)](https://www.npmjs.com/package/react-push-notification) 3 | 4 | --- 5 | 6 | # react-push-notification 7 | 8 | Easy, type-safe, & lightweight push notification library for React.js. 9 | Written in TypeScript & compiled to JavaScript for robust code. 10 | 11 | In-app notification system, as well as web native Notification support. 12 | 13 | ![](https://i.imgur.com/SorfHNa.gif) 14 | ![](https://i.imgur.com/IKppymi.gif) 15 | 16 | 17 | ### Install 18 | 19 | ```bash 20 | yarn add react-push-notification 21 | ``` 22 | or 23 | ```bash 24 | npm i react-push-notification 25 | ``` 26 | 27 | ### Sneakpeak 28 | 29 | In-app notification example. Regular React components. 30 | 31 | ![](https://i.imgur.com/SorfHNa.gif) 32 | 33 | Web native notification example. Web native components. Send push notifications outside of the browser while the browser is running in the background or just idle. 34 | 35 | Mac OSX example: 36 | 37 | ![](https://i.imgur.com/IKppymi.gif) 38 | ![](https://imgur.com/HwA1Bf5.png) 39 | 40 | 41 | 42 | ### Set-up 43 | 44 | Add the notifications component to the top of your React.js project. 45 | This is probably `index.js` or `app.js`. When using `native: true`, this step is not required. 46 | 47 | 48 | ```jsx 49 | import { Notifications } from 'react-push-notification'; 50 | 51 | const App = () => { 52 | return ( 53 |
54 | // Top of DOM tree 55 | 56 |
57 |
58 | Hello world. 59 |
60 |
61 |
62 | ); 63 | } 64 | }; 65 | 66 | export default App; 67 | ``` 68 | 69 | ### Usage 70 | 71 | import the `addNotification` function and call it. 72 | 73 | ```jsx 74 | import addNotification from 'react-push-notification'; 75 | 76 | const Page = () => { 77 | 78 | const buttonClick = () => { 79 | addNotification({ 80 | title: 'Warning', 81 | subtitle: 'This is a subtitle', 82 | message: 'This is a very long message', 83 | theme: 'darkblue', 84 | native: true // when using native, your OS will handle theming. 85 | }); 86 | }; 87 | 88 | return ( 89 |
90 | 93 |
94 | ); 95 | } 96 | }; 97 | 98 | export default Page; 99 | ``` 100 | 101 | ## Props 102 | 103 | 104 | | Property | Description | 105 | | ---------------------------------- | ------------------------------------------------------------------ | 106 | | position `string` | One of `top-left`, `top-middle`, `top-right`, `bottom-left`, `bottom-middle`, `bottom-right`.
Default: `top-left` | 107 | 108 | 109 | 110 | ## `addNotification({Options})` argument properties 111 | 112 | The `addNotification()` function has the following function type: 113 | 114 | ```tsx 115 | 116 | const options = { 117 | title: 'title', 118 | subtitle: 'subtitle', //optional 119 | message: 'message', //optional 120 | onClick: (e: Event | Notification) => void, //optional, onClick callback. 121 | theme: 'red', //optional, default: undefined 122 | duration: 3000, //optional, default: 5000, 123 | backgroundTop: 'green', //optional, background color of top container. 124 | backgroundBottom: 'darkgreen', //optional, background color of bottom container. 125 | colorTop: 'green', //optional, font color of top container. 126 | colorBottom: 'darkgreen', //optional, font color of bottom container. 127 | closeButton: 'Go away', //optional, text or html/jsx element for close text. Default: Close, 128 | native?: boolean, //optional, makes the push notification a native OS notification 129 | icon?: string, // optional, Native only. Sets an icon for the notification. 130 | vibrate?: number | number[], // optional, Native only. Sets a vibration for the notification. 131 | silent?: boolean // optional, Native only. Makes the notification silent. 132 | 133 | }; 134 | 135 | const addNotification: (options: Options) => void; 136 | 137 | ``` 138 | 139 | react-push-notification 140 | 141 | 142 | The `addNotification()` function takes an object as argument with the follow properties: 143 | 144 | 145 | | Property | Description | 146 | | ---------------------------------- | ------------------------------------------------------------------ | 147 | | title `string` | Required. Title of the push notification | 148 | | subtitle `string` | Optional. Subtitle of the push notification | 149 | | message `string` | Optional. Message of the push notification | 150 | | onClick `(e: Event OR Notification) => void` | Optional. onClick callback of push notification.
When `native: true` `e` will be of type `Notification`.
Else `e` will be of type `Event`. | 151 | | theme `string` | Optional. One of `darkblue`, `red`, `light`, `undefined`.
Default: `undefined` | 152 | | duration `number` | Optional. Duration of the push notification in ms.
Default: 3000 | 153 | | backgroundTop `string` | Optional. background color of top container. | 154 | | backgroundBottom `string` | Optional. background color of bottom container. | 155 | | colorTop `string` | Optional. font color of top container. | 156 | | colorBottom `string` | Optional. font color of bottom container. | 157 | | closeButton `string` | Optional. text or html/jsx element for close text.
Default: `Close` | 158 | | native `boolean` | Optional. Turns the notification into a native web notification.
Default: `false` | 159 | | icon `string` | Optional. Native only. Shows an icon in the notification. | 160 | | vibrate `number` | `number[]` | Optional. Native only. Makes the notification vibrate. | 161 | | silent `boolean` | Optional. Native only. Makes the notification silent. | 162 | 163 | 164 | 165 | The custom background or font colors will always override a chosen theme. 166 | 167 | ### Changelog 168 | 169 | v1.3.0 170 | 171 | Added native OS push notification support, as well as an `onClick` callback function. 172 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-push-notification", 3 | "version": "1.5.4", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "react-push-notification", 9 | "version": "1.5.4", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "@types/react": "^16.9.21", 13 | "typescript": "^3.9.10" 14 | }, 15 | "peerDependencies": { 16 | "react": "^18.2.0" 17 | } 18 | }, 19 | "node_modules/@types/prop-types": { 20 | "version": "15.7.3", 21 | "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", 22 | "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", 23 | "dev": true 24 | }, 25 | "node_modules/@types/react": { 26 | "version": "16.9.23", 27 | "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.23.tgz", 28 | "integrity": "sha512-SsGVT4E7L2wLN3tPYLiF20hmZTPGuzaayVunfgXzUn1x4uHVsKH6QDJQ/TdpHqwsTLd4CwrmQ2vOgxN7gE24gw==", 29 | "dev": true, 30 | "dependencies": { 31 | "@types/prop-types": "*", 32 | "csstype": "^2.2.0" 33 | } 34 | }, 35 | "node_modules/csstype": { 36 | "version": "2.6.9", 37 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.9.tgz", 38 | "integrity": "sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q==", 39 | "dev": true 40 | }, 41 | "node_modules/js-tokens": { 42 | "version": "4.0.0", 43 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 44 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 45 | "peer": true 46 | }, 47 | "node_modules/loose-envify": { 48 | "version": "1.4.0", 49 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 50 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 51 | "peer": true, 52 | "dependencies": { 53 | "js-tokens": "^3.0.0 || ^4.0.0" 54 | }, 55 | "bin": { 56 | "loose-envify": "cli.js" 57 | } 58 | }, 59 | "node_modules/react": { 60 | "version": "18.2.0", 61 | "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", 62 | "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", 63 | "peer": true, 64 | "dependencies": { 65 | "loose-envify": "^1.1.0" 66 | }, 67 | "engines": { 68 | "node": ">=0.10.0" 69 | } 70 | }, 71 | "node_modules/typescript": { 72 | "version": "3.9.10", 73 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", 74 | "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", 75 | "dev": true, 76 | "bin": { 77 | "tsc": "bin/tsc", 78 | "tsserver": "bin/tsserver" 79 | }, 80 | "engines": { 81 | "node": ">=4.2.0" 82 | } 83 | } 84 | }, 85 | "dependencies": { 86 | "@types/prop-types": { 87 | "version": "15.7.3", 88 | "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", 89 | "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", 90 | "dev": true 91 | }, 92 | "@types/react": { 93 | "version": "16.9.23", 94 | "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.23.tgz", 95 | "integrity": "sha512-SsGVT4E7L2wLN3tPYLiF20hmZTPGuzaayVunfgXzUn1x4uHVsKH6QDJQ/TdpHqwsTLd4CwrmQ2vOgxN7gE24gw==", 96 | "dev": true, 97 | "requires": { 98 | "@types/prop-types": "*", 99 | "csstype": "^2.2.0" 100 | } 101 | }, 102 | "csstype": { 103 | "version": "2.6.9", 104 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.9.tgz", 105 | "integrity": "sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q==", 106 | "dev": true 107 | }, 108 | "js-tokens": { 109 | "version": "4.0.0", 110 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 111 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 112 | "peer": true 113 | }, 114 | "loose-envify": { 115 | "version": "1.4.0", 116 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 117 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 118 | "peer": true, 119 | "requires": { 120 | "js-tokens": "^3.0.0 || ^4.0.0" 121 | } 122 | }, 123 | "react": { 124 | "version": "18.2.0", 125 | "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", 126 | "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", 127 | "peer": true, 128 | "requires": { 129 | "loose-envify": "^1.1.0" 130 | } 131 | }, 132 | "typescript": { 133 | "version": "3.9.10", 134 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", 135 | "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", 136 | "dev": true 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-push-notification", 3 | "version": "1.5.4", 4 | "description": "React push notifications", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "license": "MIT", 8 | "bundleDependencies": [], 9 | "devDependencies": { 10 | "@types/react": "^16.9.21", 11 | "typescript": "^3.9.10" 12 | }, 13 | "peerDependencies": { 14 | "react": "^18.2.0" 15 | }, 16 | "scripts": { 17 | "start": "tsc -w", 18 | "build": "npx tsc && cp src/Notifications/Notification.css dist/Notifications/Notification.css", 19 | "publish": "git push && npm publish", 20 | "build:check": "tsc --noEmit" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/fabioshub/react-push-notification" 25 | }, 26 | "author": "Fabio de Bruijn ", 27 | "keywords": [ 28 | "push notifications", 29 | "notifications", 30 | "messages", 31 | "react", 32 | "react push notification", 33 | "react push notifications", 34 | "react notifications", 35 | "react native OS push notifications", 36 | "native OS", 37 | "native push notifications" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import Notifications from './notifications/Notifications'; 2 | import Storage, { Options } from './notifications/Storage'; 3 | 4 | /** 5 | * Add a new notification. 6 | * Must pass an object with the params to the function. 7 | * @param {Options} options 8 | * @property {string} options.title - Title of the push notification. 9 | * @property {string} [options.subtitle] - Subtitle of the push notification. 10 | * @property {string} [options.message] - Message of the push notification. 11 | * @property {function} [options.onClick] - onclick callback. Optional parameter. 12 | * @property {('darkblue'|'red'|'light')} [options.theme=undefined] - Theme of the push notification. 13 | * @property {number} [options.duration=3000] - duration of the push notification in ms. 14 | * @property {string} [options.backgroundTop=undefined] - Background color of the top container of push notification. 15 | * @property {string} [options.backgroundBottom=undefined] - Background color of the bottom container of push notification. 16 | * @property {string} [options.colorTop=undefined] - Color of the top text of push notification. 17 | * @property {string} [options.colorBottom=undefined] - Color of the bottom text of push notification. 18 | * @property {(string|JSX.Element)} [options.closeButton="close"] - Color of the bottom text of push notification. 19 | * @property {boolean} [options.native=false] - Uses native browser notifications. Will prompt for user permission if not granted. 20 | * @property {string} [options.icon] - Native only. Link to image to show in notification. 21 | * @property {boolean} [options.silent] - Native only. Makes the notification silent. 22 | * @property {(number|numer[])} [options.vibrate] - Native only. Makes the notification vibrate. 23 | * 24 | */ 25 | const addNotification: (options: Options) => void = Storage.addNotification; 26 | export { Notifications }; 27 | export default addNotification; 28 | -------------------------------------------------------------------------------- /src/notifications/Notification.css: -------------------------------------------------------------------------------- 1 | .rpn-notification-holder { 2 | position: fixed; 3 | display: flex; 4 | flex-direction: column; 5 | z-index: 100000; 6 | } 7 | 8 | .rpn-notification-holder.top-left { 9 | left: 10px; 10 | } 11 | 12 | .rpn-notification-holder.top-middle { 13 | left: 50%; 14 | transform: translateX(-50%); 15 | } 16 | 17 | .rpn-notification-holder.top-right { 18 | right: 30px; 19 | } 20 | 21 | .rpn-notification-holder.bottom-left { 22 | left: 10px; 23 | bottom: 10px; 24 | } 25 | 26 | .rpn-notification-holder.bottom-middle { 27 | left: 50%; 28 | transform: translateX(-50%); 29 | bottom: 10px; 30 | } 31 | 32 | .rpn-notification-holder.bottom-right { 33 | right: 30px; 34 | bottom: 10px; 35 | } 36 | 37 | .rpn-notification-card { 38 | display: flex; 39 | flex-direction: column; 40 | width: 250px; 41 | margin-top: 15px; 42 | -webkit-animation: fadein 0.2s; /* Safari, Chrome and Opera > 12.1 */ 43 | -moz-animation: fadein 0.2s; /* Firefox < 16 */ 44 | -ms-animation: fadein 0.2s; /* Internet Explorer */ 45 | -o-animation: fadein 0.2s; /* Opera < 12.1 */ 46 | animation: fadein 0.2s; 47 | } 48 | 49 | .rpn-notification-card-top { 50 | width: 100%; 51 | display: flex; 52 | justify-content: space-between; 53 | flex-wrap: wrap; 54 | padding: 10px; 55 | font-weight: bold; 56 | font-size: 11px; 57 | background-color: rgb(51, 51, 51); 58 | color: white; 59 | border-radius: 5px 5px 0 0; 60 | 61 | } 62 | 63 | .rpn-notification-card-bottom { 64 | width: 100%; 65 | display: flex; 66 | flex-direction: column; 67 | align-items: flex-start; 68 | padding: 10px 10px 15px 10px; 69 | flex-wrap: wrap; 70 | background-color: rgb(59, 59, 59); 71 | color: white; 72 | border-radius: 0 0 5px 5px; 73 | justify-content: center; 74 | box-shadow: 0 5px 0px 0px rgb(0, 0, 0); 75 | } 76 | 77 | .rpn-notification-card-bottom span { 78 | padding: 3px 0; 79 | } 80 | 81 | .rpn-notification-card-bottom .subtitle { 82 | font-weight: bold; 83 | font-size: 12px; 84 | } 85 | 86 | .rpn-notification-card-bottom .message { 87 | font-size: 14px; 88 | } 89 | 90 | .rpn-notification-card-close { 91 | font-size: 10px; 92 | cursor: pointer; 93 | height: 100%; 94 | } 95 | 96 | .rpn-notification-card-top.light { 97 | background-color: rgb(240, 240, 240); 98 | color: black; 99 | } 100 | 101 | .rpn-notification-card-bottom.light { 102 | background-color: rgb(245, 245, 245); 103 | color: black; 104 | box-shadow: 0 5px 0px 0px rgb(218, 217, 217); 105 | } 106 | 107 | .rpn-notification-card-top.darkblue { 108 | background-color: #2d3542; 109 | color: white; 110 | } 111 | 112 | .rpn-notification-card-bottom.darkblue { 113 | background-color: #394357; 114 | color: white; 115 | box-shadow: 0 5px 0px 0px #1f2b42; 116 | 117 | } 118 | 119 | .rpn-notification-card-top.red { 120 | background-color: rgb(187, 37, 37); 121 | color: white; 122 | } 123 | 124 | .rpn-notification-card-bottom.red { 125 | background-color: #D32F2F; 126 | color: white; 127 | box-shadow: 0 5px 0px 0px rgb(145, 1, 1); 128 | } 129 | 130 | @keyframes fadein { 131 | from { opacity: 0; } 132 | to { opacity: 1; } 133 | } 134 | 135 | /* Firefox < 16 */ 136 | @-moz-keyframes fadein { 137 | from { opacity: 0; } 138 | to { opacity: 1; } 139 | } 140 | 141 | /* Safari, Chrome and Opera > 12.1 */ 142 | @-webkit-keyframes fadein { 143 | from { opacity: 0; } 144 | to { opacity: 1; } 145 | } 146 | 147 | /* Internet Explorer */ 148 | @-ms-keyframes fadein { 149 | from { opacity: 0; } 150 | to { opacity: 1; } 151 | } 152 | 153 | /* Opera < 12.1 */ 154 | @-o-keyframes fadein { 155 | from { opacity: 0; } 156 | to { opacity: 1; } 157 | } -------------------------------------------------------------------------------- /src/notifications/Notifications.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Storage, { PushNotification as Not } from './Storage'; 3 | import './Notification.css'; 4 | import PushNotification from './PushNotification'; 5 | 6 | type Position = 'top-left' | 'top-middle' | 'top-right' | 'bottom-left' | 'bottom-middle' | 'bottom-right'; 7 | 8 | interface Props { 9 | position?: Position; 10 | } 11 | 12 | interface State { 13 | value: Array; 14 | } 15 | 16 | /** 17 | * Notification injector, which renders 18 | * the push notifications rendered 19 | * by the addNotifcation({}) function. 20 | * 21 | * @param {string} position - Must pass as prop. Sets the position of the push notification. 22 | * position can me 'top-left', 'top-middle', 'top-right', 'bottom-left', 'bottom-middle', 'bottom-right'. 23 | * Example 24 | * 25 | */ 26 | class Notifications extends React.Component { 27 | state: State = { 28 | value: [], 29 | }; 30 | 31 | componentDidMount() { 32 | Storage.addListener((v: Array): void => this.setState({ value: v })); 33 | } 34 | 35 | render(): JSX.Element { 36 | const { position } = this.props; 37 | const classN: string = `rpn-notification-holder ${position || 'top-middle'} supertest`; 38 | return
39 | {this.state.value.map((note: Not, i: number): JSX.Element => { 40 | return 41 | })} 42 |
43 | } 44 | }; 45 | 46 | 47 | export default Notifications; -------------------------------------------------------------------------------- /src/notifications/PushNotification.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Color, Styling, eventFunc } from './Storage'; 3 | 4 | interface Props { 5 | title: string; 6 | id: number; 7 | message?: string; 8 | subtitle?: string; 9 | theme?: Color; 10 | styling?: Styling; 11 | closeButton?: JSX.Element | string; 12 | onClick?: eventFunc; 13 | closeNotification: (id: number) => void; 14 | } 15 | 16 | type Style = { 17 | backgroundColor?: string, 18 | color?: string 19 | } 20 | 21 | const PushNotification = (props: Props): JSX.Element => { 22 | const { title, subtitle, message, theme, id, closeNotification, styling, closeButton, onClick } = props; 23 | let topStyling: Style = {}; 24 | let bottomStyling: Style = {}; 25 | if (styling) { 26 | topStyling.backgroundColor = styling.backgroundTop; 27 | topStyling.color = styling.colorTop; 28 | bottomStyling.backgroundColor = styling.backgroundBottom; 29 | bottomStyling.color = styling.colorBottom; 30 | } 31 | return
32 |
33 | {title} 34 | closeNotification(id)}>{closeButton || 'close'} 35 |
36 |
37 | {subtitle} 38 | {message} 39 | 40 |
41 |
; 42 | } 43 | 44 | export default PushNotification; -------------------------------------------------------------------------------- /src/notifications/Storage.ts: -------------------------------------------------------------------------------- 1 | 2 | export type Color = 'light' | 'darkblue' | 'red' | undefined; 3 | 4 | const defaultDuration = 3000; 5 | 6 | type voidFunc = () => void; 7 | export type eventFunc = (e: any) => void; 8 | 9 | export type onClickType = voidFunc | eventFunc | undefined; 10 | 11 | export type Options = { 12 | title: string, 13 | subtitle?: string, 14 | message?: string, 15 | onClick?: onClickType, 16 | theme?: Color, 17 | duration?: number, 18 | backgroundTop?: string, 19 | backgroundBottom?: string, 20 | colorTop?: string, 21 | colorBottom?: string, 22 | closeButton?: JSX.Element | string, 23 | native?: boolean, 24 | icon?: string, 25 | vibrate?: number | number[], 26 | silent?: boolean 27 | } 28 | 29 | export type Styling = { 30 | backgroundTop?: string, 31 | backgroundBottom?: string, 32 | colorTop?: string, 33 | colorBottom?: string 34 | } 35 | 36 | export interface PushNotificationObject { 37 | title: string; 38 | subtitle?: string; 39 | message?: string; 40 | theme?: Color; 41 | styling?: Styling; 42 | closeButton?: JSX.Element | string; 43 | onClick?: onClickType; 44 | } 45 | 46 | export class PushNotification { 47 | title: string; 48 | subtitle?: string; 49 | message?: string; 50 | theme?: Color; 51 | id: number; 52 | styling?: Styling; 53 | closeButton?: JSX.Element | string; 54 | onClick?: onClickType; 55 | constructor(op: PushNotificationObject) { 56 | this.title = op.title; 57 | this.subtitle = op.subtitle; 58 | this.message = op.message; 59 | this.theme = op.theme; 60 | this.id = Math.random(); 61 | this.styling = op.styling; 62 | this.closeButton = op.closeButton; 63 | this.onClick = op.onClick; 64 | } 65 | } 66 | 67 | class Storage { 68 | Storage: Array = []; 69 | Listener: (storage: any) => void = () => this.Storage; 70 | 71 | popAndPush = (NotificationId: number) => { 72 | let i: number = 0; 73 | while (i < this.Storage.length) { 74 | if (this.Storage[i].id === NotificationId) { 75 | this.Storage.splice(i, 1); 76 | } 77 | else { 78 | ++i; 79 | } 80 | } 81 | this.Listener(this.Storage); 82 | }; 83 | 84 | setTimer = (NotificationId: number, duration: number) => { 85 | setTimeout(() => this.popAndPush(NotificationId), duration); 86 | }; 87 | 88 | addListener = (listener: (v: Array) => void): void => { 89 | this.Listener = listener; 90 | }; 91 | 92 | addNativeNotification = async (options: Options): Promise => { 93 | const { title, subtitle, message, duration, icon, vibrate, silent, onClick } = options; 94 | if (Notification.permission === 'default' || Notification.permission === 'denied') { 95 | await Notification.requestPermission(); 96 | } 97 | if (Notification.permission === 'granted') { 98 | const not: Notification = new Notification(title, { 99 | body: message, 100 | data: subtitle, 101 | icon, 102 | vibrate, 103 | silent 104 | }); 105 | not.onclick = onClick || null; 106 | setTimeout(not.close.bind(not), duration || defaultDuration); 107 | } 108 | }; 109 | 110 | addWebNotification = (options: Options): void => { 111 | const { title, subtitle, message, theme, duration, backgroundBottom, backgroundTop, colorBottom, colorTop, closeButton, onClick } = options; 112 | const styling: Styling = { 113 | backgroundTop, 114 | backgroundBottom, 115 | colorTop, 116 | colorBottom 117 | }; 118 | const newNotification: PushNotification = new PushNotification({ title, subtitle, message, theme, styling, closeButton, onClick }); 119 | this.Storage.push(newNotification); 120 | this.setTimer(newNotification.id, duration || defaultDuration); 121 | this.Listener(this.Storage); 122 | }; 123 | 124 | addNotification = async (options: Options): Promise => { 125 | const { native } = options; 126 | if (native) { 127 | return this.addNativeNotification(options); 128 | } 129 | return this.addWebNotification(options); 130 | 131 | }; 132 | } 133 | 134 | export default new Storage(); 135 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "react", 4 | "esModuleInterop": true, 5 | "target": "ES6", 6 | "module": "commonjs", 7 | "declaration": true, 8 | "noImplicitAny": true, 9 | "removeComments": false, 10 | "outDir": "dist/", 11 | "rootDir": "src", 12 | "strict": true 13 | }, 14 | "include": [ 15 | "src/**/*" 16 | ], 17 | "exclude": [ 18 | "node_modules", 19 | "dist", 20 | "**/*.spec.ts" 21 | ] 22 | } --------------------------------------------------------------------------------