├── .babelrc ├── .gitignore ├── LICENSE ├── README.md ├── build └── index.js ├── package.json ├── src ├── ReactNotifications.js └── index.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"], 3 | "plugins": [ 4 | "transform-object-rest-spread", 5 | "transform-react-jsx" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Kaushik Nagaraj 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-browser-notifications 2 | React component for the [Javascript Notifications API](https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API). The Notifications API allows web pages to control the display of system notifications to the end user. These are outside the top-level browsing context viewport, so therefore can be displayed even when the user has switched tabs or moved to a different app. This component is supported in modern web browsers such as Chrome, Safari, Firefox, Opera, and Edge. 3 | 4 | 5 | ## Demo 6 | [Live Demo](https://react-notif-demo.herokuapp.com/) 7 | 8 | 9 | ## Installation 10 | Using [npm](https://www.npmjs.com/): 11 | ``` 12 | npm install --save react-browser-notifications 13 | ``` 14 | 15 | 16 | ## Usage 17 | ```javascript 18 | import React from 'react'; 19 | import ReactNotifications from 'react-browser-notifications'; 20 | 21 | class Example extends React.Component { 22 | constructor() { 23 | super(); 24 | this.showNotifications = this.showNotifications.bind(this); 25 | this.handleClick = this.handleClick.bind(this); 26 | } 27 | 28 | showNotifications() { 29 | // If the Notifications API is supported by the browser 30 | // then show the notification 31 | if(this.n.supported()) this.n.show(); 32 | } 33 | 34 | handleClick(event) { 35 | // Do something here such as 36 | // console.log("Notification Clicked") OR 37 | // window.focus() OR 38 | // window.open("http://www.google.com") 39 | 40 | // Lastly, Close the notification 41 | this.n.close(event.target.tag); 42 | } 43 | 44 | render() { 45 | return ( 46 |
47 | 48 | (this.n = ref)} // Required 50 | title="Hey There!" // Required 51 | body="This is the body" 52 | icon="icon.png" 53 | tag="abcdef" 54 | timeout="2000" 55 | onClick={event => this.handleClick(event)} 56 | /> 57 | 58 | 61 | 62 |
63 | ) 64 | } 65 | } 66 | ``` 67 | 68 | 69 | ## Methods 70 | Once you have the reference of `ReactNotifications` by including the `onRef={ref => (this.n = ref)}` prop, you can call the below methods 71 | 72 | ```javascript 73 | // Returns true if the Notifications API is supported by the browser 74 | this.n.supported() 75 | 76 | // Triggers the browser notification 77 | this.n.show() 78 | 79 | // Close the notification, which is ID'ed by the tag prop 80 | this.n.close(tag) 81 | ``` 82 | 83 | 84 | ## Properties 85 | The `ReactNotifications` component accepts the following props 86 | 87 | Name | Type | Req/Opt | Description 88 | --- | --- | --- | --- 89 | `onRef` | function | **Required** | This is required to [reference](https://reactjs.org/docs/refs-and-the-dom.html) the `ReactNotifications` component. **Recommended:** `onRef={ref => (this.n = ref)}`, where n is any variable name 90 | `title` | string | **Required** | Title of the notification 91 | `body` | string | Optional | Text to display in the body of the notification 92 | `icon` | string | Optional | Link to image to be displayed as the icon 93 | `tag` | string | Optional | Unique identifier for the notification. If this is not provided as a prop, an unique [shortid](https://www.npmjs.com/package/shortid) is automatically generated for the notification 94 | `timeout` | string | Optional | Indicates, in milliseconds, the duration for which the notification will remain displayed, if less than the default timeout. Default timeout is dependent on the browser (typically 4s or 5s). Maximum duration is the default timeout 95 | `interaction` | boolean | Optional | *Only available in Chrome*. Setting this to `true` makes the notification remain displayed until the user interacts with the notification (clicks the *Close* button). `timeout` overrides `interaction` if both props are provided 96 | `onClick` | function | Optional | Fired when the notification is clicked 97 | 98 | 99 | ## Browser Support 100 | For up-to-date details on browser compatibility, please visit [caniuse](https://caniuse.com/#search=notifications) 101 | 102 | 103 | ## License 104 | MIT 105 | 106 | 107 | ## Credits 108 | Dependency: [Push.js](https://github.com/Nickersoft/push.js) by [Nickersoft](https://github.com/Nickersoft) 109 | -------------------------------------------------------------------------------- /build/index.js: -------------------------------------------------------------------------------- 1 | module.exports = 2 | /******/ (function(modules) { // webpackBootstrap 3 | /******/ // The module cache 4 | /******/ var installedModules = {}; 5 | /******/ 6 | /******/ // The require function 7 | /******/ function __webpack_require__(moduleId) { 8 | /******/ 9 | /******/ // Check if module is in cache 10 | /******/ if(installedModules[moduleId]) { 11 | /******/ return installedModules[moduleId].exports; 12 | /******/ } 13 | /******/ // Create a new module (and put it into the cache) 14 | /******/ var module = installedModules[moduleId] = { 15 | /******/ i: moduleId, 16 | /******/ l: false, 17 | /******/ exports: {} 18 | /******/ }; 19 | /******/ 20 | /******/ // Execute the module function 21 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 22 | /******/ 23 | /******/ // Flag the module as loaded 24 | /******/ module.l = true; 25 | /******/ 26 | /******/ // Return the exports of the module 27 | /******/ return module.exports; 28 | /******/ } 29 | /******/ 30 | /******/ 31 | /******/ // expose the modules object (__webpack_modules__) 32 | /******/ __webpack_require__.m = modules; 33 | /******/ 34 | /******/ // expose the module cache 35 | /******/ __webpack_require__.c = installedModules; 36 | /******/ 37 | /******/ // identity function for calling harmony imports with the correct context 38 | /******/ __webpack_require__.i = function(value) { return value; }; 39 | /******/ 40 | /******/ // define getter function for harmony exports 41 | /******/ __webpack_require__.d = function(exports, name, getter) { 42 | /******/ if(!__webpack_require__.o(exports, name)) { 43 | /******/ Object.defineProperty(exports, name, { 44 | /******/ configurable: false, 45 | /******/ enumerable: true, 46 | /******/ get: getter 47 | /******/ }); 48 | /******/ } 49 | /******/ }; 50 | /******/ 51 | /******/ // getDefaultExport function for compatibility with non-harmony modules 52 | /******/ __webpack_require__.n = function(module) { 53 | /******/ var getter = module && module.__esModule ? 54 | /******/ function getDefault() { return module['default']; } : 55 | /******/ function getModuleExports() { return module; }; 56 | /******/ __webpack_require__.d(getter, 'a', getter); 57 | /******/ return getter; 58 | /******/ }; 59 | /******/ 60 | /******/ // Object.prototype.hasOwnProperty.call 61 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 62 | /******/ 63 | /******/ // __webpack_public_path__ 64 | /******/ __webpack_require__.p = ""; 65 | /******/ 66 | /******/ // Load entry module and return exports 67 | /******/ return __webpack_require__(__webpack_require__.s = 3); 68 | /******/ }) 69 | /************************************************************************/ 70 | /******/ ([ 71 | /* 0 */ 72 | /***/ (function(module, exports, __webpack_require__) { 73 | 74 | "use strict"; 75 | 76 | 77 | var randomFromSeed = __webpack_require__(11); 78 | 79 | var ORIGINAL = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-'; 80 | var alphabet; 81 | var previousSeed; 82 | 83 | var shuffled; 84 | 85 | function reset() { 86 | shuffled = false; 87 | } 88 | 89 | function setCharacters(_alphabet_) { 90 | if (!_alphabet_) { 91 | if (alphabet !== ORIGINAL) { 92 | alphabet = ORIGINAL; 93 | reset(); 94 | } 95 | return; 96 | } 97 | 98 | if (_alphabet_ === alphabet) { 99 | return; 100 | } 101 | 102 | if (_alphabet_.length !== ORIGINAL.length) { 103 | throw new Error('Custom alphabet for shortid must be ' + ORIGINAL.length + ' unique characters. You submitted ' + _alphabet_.length + ' characters: ' + _alphabet_); 104 | } 105 | 106 | var unique = _alphabet_.split('').filter(function(item, ind, arr){ 107 | return ind !== arr.lastIndexOf(item); 108 | }); 109 | 110 | if (unique.length) { 111 | throw new Error('Custom alphabet for shortid must be ' + ORIGINAL.length + ' unique characters. These characters were not unique: ' + unique.join(', ')); 112 | } 113 | 114 | alphabet = _alphabet_; 115 | reset(); 116 | } 117 | 118 | function characters(_alphabet_) { 119 | setCharacters(_alphabet_); 120 | return alphabet; 121 | } 122 | 123 | function setSeed(seed) { 124 | randomFromSeed.seed(seed); 125 | if (previousSeed !== seed) { 126 | reset(); 127 | previousSeed = seed; 128 | } 129 | } 130 | 131 | function shuffle() { 132 | if (!alphabet) { 133 | setCharacters(ORIGINAL); 134 | } 135 | 136 | var sourceArray = alphabet.split(''); 137 | var targetArray = []; 138 | var r = randomFromSeed.nextValue(); 139 | var characterIndex; 140 | 141 | while (sourceArray.length > 0) { 142 | r = randomFromSeed.nextValue(); 143 | characterIndex = Math.floor(r * sourceArray.length); 144 | targetArray.push(sourceArray.splice(characterIndex, 1)[0]); 145 | } 146 | return targetArray.join(''); 147 | } 148 | 149 | function getShuffled() { 150 | if (shuffled) { 151 | return shuffled; 152 | } 153 | shuffled = shuffle(); 154 | return shuffled; 155 | } 156 | 157 | /** 158 | * lookup shuffled letter 159 | * @param index 160 | * @returns {string} 161 | */ 162 | function lookup(index) { 163 | var alphabetShuffled = getShuffled(); 164 | return alphabetShuffled[index]; 165 | } 166 | 167 | module.exports = { 168 | characters: characters, 169 | seed: setSeed, 170 | lookup: lookup, 171 | shuffled: getShuffled 172 | }; 173 | 174 | 175 | /***/ }), 176 | /* 1 */ 177 | /***/ (function(module, exports, __webpack_require__) { 178 | 179 | "use strict"; 180 | 181 | 182 | var randomByte = __webpack_require__(10); 183 | 184 | function encode(lookup, number) { 185 | var loopCounter = 0; 186 | var done; 187 | 188 | var str = ''; 189 | 190 | while (!done) { 191 | str = str + lookup( ( (number >> (4 * loopCounter)) & 0x0f ) | randomByte() ); 192 | done = number < (Math.pow(16, loopCounter + 1 ) ); 193 | loopCounter++; 194 | } 195 | return str; 196 | } 197 | 198 | module.exports = encode; 199 | 200 | 201 | /***/ }), 202 | /* 2 */ 203 | /***/ (function(module, exports, __webpack_require__) { 204 | 205 | "use strict"; 206 | 207 | 208 | Object.defineProperty(exports, "__esModule", { 209 | value: true 210 | }); 211 | 212 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 213 | 214 | var _react = __webpack_require__(13); 215 | 216 | var _react2 = _interopRequireDefault(_react); 217 | 218 | var _push = __webpack_require__(4); 219 | 220 | var _push2 = _interopRequireDefault(_push); 221 | 222 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 223 | 224 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 225 | 226 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 227 | 228 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 229 | 230 | var shortid = __webpack_require__(5); 231 | 232 | var ReactNotifications = function (_React$Component) { 233 | _inherits(ReactNotifications, _React$Component); 234 | 235 | function ReactNotifications() { 236 | _classCallCheck(this, ReactNotifications); 237 | 238 | var _this = _possibleConstructorReturn(this, (ReactNotifications.__proto__ || Object.getPrototypeOf(ReactNotifications)).call(this)); 239 | 240 | _this.supported = _this.supported.bind(_this); 241 | _this.show = _this.show.bind(_this); 242 | _this.close = _this.close.bind(_this); 243 | return _this; 244 | } 245 | 246 | _createClass(ReactNotifications, [{ 247 | key: 'componentDidMount', 248 | value: function componentDidMount() { 249 | this.props.onRef(this); 250 | } 251 | }, { 252 | key: 'componentWillUnmount', 253 | value: function componentWillUnmount() { 254 | this.props.onRef(undefined); 255 | } 256 | }, { 257 | key: 'supported', 258 | value: function supported() { 259 | if ('Notification' in window) return true;else return false; 260 | } 261 | }, { 262 | key: 'show', 263 | value: function show() { 264 | _push2.default.create(this.props.title, { 265 | body: this.props.body ? this.props.body : null, 266 | icon: this.props.icon ? this.props.icon : null, 267 | tag: this.props.tag ? this.props.tag : shortid.generate(), 268 | timeout: this.props.timeout ? this.props.timeout : null, 269 | requireInteraction: this.props.interaction ? this.props.interaction : false, 270 | onClick: this.props.onClick ? this.props.onClick : null 271 | }); 272 | } 273 | }, { 274 | key: 'close', 275 | value: function close(tag) { 276 | _push2.default.close(tag); 277 | } 278 | }, { 279 | key: 'render', 280 | value: function render() { 281 | return _react2.default.createElement('div', null); 282 | } 283 | }]); 284 | 285 | return ReactNotifications; 286 | }(_react2.default.Component); 287 | 288 | exports.default = ReactNotifications; 289 | 290 | /***/ }), 291 | /* 3 */ 292 | /***/ (function(module, exports, __webpack_require__) { 293 | 294 | "use strict"; 295 | 296 | 297 | Object.defineProperty(exports, "__esModule", { 298 | value: true 299 | }); 300 | 301 | var _ReactNotifications = __webpack_require__(2); 302 | 303 | var _ReactNotifications2 = _interopRequireDefault(_ReactNotifications); 304 | 305 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 306 | 307 | exports.default = _ReactNotifications2.default; 308 | 309 | /***/ }), 310 | /* 4 */ 311 | /***/ (function(module, exports, __webpack_require__) { 312 | 313 | var require;var require;/** 314 | * Push v1.0-beta 315 | * ============== 316 | * A compact, cross-browser solution for the JavaScript Notifications API 317 | * 318 | * Credits 319 | * ------- 320 | * Tsvetan Tsvetkov (ttsvetko) 321 | * Alex Gibson (alexgibson) 322 | * 323 | * License 324 | * ------- 325 | * 326 | * The MIT License (MIT) 327 | * 328 | * Copyright (c) 2015-2017 Tyler Nickerson 329 | * 330 | * Permission is hereby granted, free of charge, to any person obtaining a copy 331 | * of this software and associated documentation files (the "Software"), to deal 332 | * in the Software without restriction, including without limitation the rights 333 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 334 | * copies of the Software, and to permit persons to whom the Software is 335 | * furnished to do so, subject to the following conditions: 336 | * 337 | * The above copyright notice and this permission notice shall be included in 338 | * all copies or substantial portions of the Software. 339 | * 340 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 341 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 342 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 343 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 344 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 345 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 346 | * THE SOFTWARE. 347 | */ 348 | !function(t){if(true)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).Push=t()}}(function(){return function t(e,n,i){function o(s,a){if(!n[s]){if(!e[s]){var u="function"==typeof require&&require;if(!a&&u)return require(s,!0);if(r)return r(s,!0);var c=new Error("Cannot find module '"+s+"'");throw c.code="MODULE_NOT_FOUND",c}var f=n[s]={exports:{}};e[s][0].call(f.exports,function(t){var n=e[s][1][t];return o(n||t)},f,f.exports,t,e,n,i)}return n[s].exports}for(var r="function"==typeof require&&require,s=0;s0?this._requestWithCallback.apply(this,arguments):this._requestAsPromise()}},{key:"_requestWithCallback",value:function(t,e){var n=this,i=this.get(),o=function(){var i=arguments.length>0&&void 0!==arguments[0]?arguments[0]:n._win.Notification.permission;void 0===i&&n._win.webkitNotifications&&(i=n._win.webkitNotifications.checkPermission()),i===n.GRANTED||0===i?t&&t():e&&e()};i!==this.DEFAULT?o(i):this._win.webkitNotifications&&this._win.webkitNotifications.checkPermission?this._win.webkitNotifications.requestPermission(o):this._win.Notification&&this._win.Notification.requestPermission?this._win.Notification.requestPermission().then(o).catch(function(){e&&e()}):t&&t()}},{key:"_requestAsPromise",value:function(){var t=this,e=this.get(),n=function(e){return e===t.GRANTED||0===e},i=e!==this.DEFAULT,o=this._win.Notification&&this._win.Notification.requestPermission,r=this._win.webkitNotifications&&this._win.webkitNotifications.checkPermission;return new Promise(function(s,a){var u=function(t){return n(t)?s():a()};i?u(e):r?t._win.webkitNotifications.requestPermission(function(t){u(t)}):o?t._win.Notification.requestPermission().then(function(t){u(t)}).catch(a):s()})}},{key:"has",value:function(){return this.get()===this.GRANTED}},{key:"get",value:function(){return this._win.Notification&&this._win.Notification.permission?this._win.Notification.permission:this._win.webkitNotifications&&this._win.webkitNotifications.checkPermission?this._permissions[this._win.webkitNotifications.checkPermission()]:navigator.mozNotification?this.GRANTED:this._win.external&&this._win.external.msIsSiteMode?this._win.external.msIsSiteMode()?this.GRANTED:this.DEFAULT:this.GRANTED}}]),t}();n.default=r},{}],3:[function(t,e,n){"use strict";function i(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var r=function(){function t(t,e){for(var n=0;n 0) { 405 | str = str + encode(alphabet.lookup, counter); 406 | } 407 | str = str + encode(alphabet.lookup, seconds); 408 | 409 | return str; 410 | } 411 | 412 | module.exports = build; 413 | 414 | 415 | /***/ }), 416 | /* 7 */ 417 | /***/ (function(module, exports, __webpack_require__) { 418 | 419 | "use strict"; 420 | 421 | var alphabet = __webpack_require__(0); 422 | 423 | /** 424 | * Decode the id to get the version and worker 425 | * Mainly for debugging and testing. 426 | * @param id - the shortid-generated id. 427 | */ 428 | function decode(id) { 429 | var characters = alphabet.shuffled(); 430 | return { 431 | version: characters.indexOf(id.substr(0, 1)) & 0x0f, 432 | worker: characters.indexOf(id.substr(1, 1)) & 0x0f 433 | }; 434 | } 435 | 436 | module.exports = decode; 437 | 438 | 439 | /***/ }), 440 | /* 8 */ 441 | /***/ (function(module, exports, __webpack_require__) { 442 | 443 | "use strict"; 444 | 445 | 446 | var alphabet = __webpack_require__(0); 447 | var encode = __webpack_require__(1); 448 | var decode = __webpack_require__(7); 449 | var build = __webpack_require__(6); 450 | var isValid = __webpack_require__(9); 451 | 452 | // if you are using cluster or multiple servers use this to make each instance 453 | // has a unique value for worker 454 | // Note: I don't know if this is automatically set when using third 455 | // party cluster solutions such as pm2. 456 | var clusterWorkerId = __webpack_require__(12) || 0; 457 | 458 | /** 459 | * Set the seed. 460 | * Highly recommended if you don't want people to try to figure out your id schema. 461 | * exposed as shortid.seed(int) 462 | * @param seed Integer value to seed the random alphabet. ALWAYS USE THE SAME SEED or you might get overlaps. 463 | */ 464 | function seed(seedValue) { 465 | alphabet.seed(seedValue); 466 | return module.exports; 467 | } 468 | 469 | /** 470 | * Set the cluster worker or machine id 471 | * exposed as shortid.worker(int) 472 | * @param workerId worker must be positive integer. Number less than 16 is recommended. 473 | * returns shortid module so it can be chained. 474 | */ 475 | function worker(workerId) { 476 | clusterWorkerId = workerId; 477 | return module.exports; 478 | } 479 | 480 | /** 481 | * 482 | * sets new characters to use in the alphabet 483 | * returns the shuffled alphabet 484 | */ 485 | function characters(newCharacters) { 486 | if (newCharacters !== undefined) { 487 | alphabet.characters(newCharacters); 488 | } 489 | 490 | return alphabet.shuffled(); 491 | } 492 | 493 | /** 494 | * Generate unique id 495 | * Returns string id 496 | */ 497 | function generate() { 498 | return build(clusterWorkerId); 499 | } 500 | 501 | // Export all other functions as properties of the generate function 502 | module.exports = generate; 503 | module.exports.generate = generate; 504 | module.exports.seed = seed; 505 | module.exports.worker = worker; 506 | module.exports.characters = characters; 507 | module.exports.decode = decode; 508 | module.exports.isValid = isValid; 509 | 510 | 511 | /***/ }), 512 | /* 9 */ 513 | /***/ (function(module, exports, __webpack_require__) { 514 | 515 | "use strict"; 516 | 517 | var alphabet = __webpack_require__(0); 518 | 519 | function isShortId(id) { 520 | if (!id || typeof id !== 'string' || id.length < 6 ) { 521 | return false; 522 | } 523 | 524 | var characters = alphabet.characters(); 525 | var len = id.length; 526 | for(var i = 0; i < len;i++) { 527 | if (characters.indexOf(id[i]) === -1) { 528 | return false; 529 | } 530 | } 531 | return true; 532 | } 533 | 534 | module.exports = isShortId; 535 | 536 | 537 | /***/ }), 538 | /* 10 */ 539 | /***/ (function(module, exports, __webpack_require__) { 540 | 541 | "use strict"; 542 | 543 | 544 | var crypto = typeof window === 'object' && (window.crypto || window.msCrypto); // IE 11 uses window.msCrypto 545 | 546 | function randomByte() { 547 | if (!crypto || !crypto.getRandomValues) { 548 | return Math.floor(Math.random() * 256) & 0x30; 549 | } 550 | var dest = new Uint8Array(1); 551 | crypto.getRandomValues(dest); 552 | return dest[0] & 0x30; 553 | } 554 | 555 | module.exports = randomByte; 556 | 557 | 558 | /***/ }), 559 | /* 11 */ 560 | /***/ (function(module, exports, __webpack_require__) { 561 | 562 | "use strict"; 563 | 564 | 565 | // Found this seed-based random generator somewhere 566 | // Based on The Central Randomizer 1.3 (C) 1997 by Paul Houle (houle@msc.cornell.edu) 567 | 568 | var seed = 1; 569 | 570 | /** 571 | * return a random number based on a seed 572 | * @param seed 573 | * @returns {number} 574 | */ 575 | function getNextValue() { 576 | seed = (seed * 9301 + 49297) % 233280; 577 | return seed/(233280.0); 578 | } 579 | 580 | function setSeed(_seed_) { 581 | seed = _seed_; 582 | } 583 | 584 | module.exports = { 585 | nextValue: getNextValue, 586 | seed: setSeed 587 | }; 588 | 589 | 590 | /***/ }), 591 | /* 12 */ 592 | /***/ (function(module, exports, __webpack_require__) { 593 | 594 | "use strict"; 595 | 596 | 597 | module.exports = 0; 598 | 599 | 600 | /***/ }), 601 | /* 13 */ 602 | /***/ (function(module, exports) { 603 | 604 | module.exports = require("react"); 605 | 606 | /***/ }) 607 | /******/ ]); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-browser-notifications", 3 | "version": "1.0.14", 4 | "description": "React component for the Javascript Notification API", 5 | "main": "build/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "webpack --watch", 9 | "build": "webpack" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/knxyzkn/react-browser-notifications.git" 14 | }, 15 | "keywords": [ 16 | "react", 17 | "javascript", 18 | "browser", 19 | "api", 20 | "notifications" 21 | ], 22 | "author": "Kaushik Nagaraj", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/knxyzkn/react-browser-notifications/issues" 26 | }, 27 | "homepage": "https://github.com/knxyzkn/react-browser-notifications#readme", 28 | "devDependencies": { 29 | "babel-cli": "^6.24.1", 30 | "babel-core": "^6.24.1", 31 | "babel-loader": "^7.0.0", 32 | "babel-plugin-transform-object-rest-spread": "^6.23.0", 33 | "babel-plugin-transform-react-jsx": "^6.24.1", 34 | "babel-preset-env": "^1.5.1" 35 | }, 36 | "dependencies": { 37 | "push.js": "^1.0.5", 38 | "react": "^16.2.0", 39 | "shortid": "^2.2.8", 40 | "webpack": "^2.6.1" 41 | }, 42 | "babel": { 43 | "presets": [ 44 | "es2015", 45 | "react", 46 | "stage-2" 47 | ] 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/ReactNotifications.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Push from 'push.js'; 3 | const shortid = require('shortid'); 4 | 5 | class ReactNotifications extends React.Component { 6 | 7 | constructor() { 8 | super(); 9 | this.supported = this.supported.bind(this); 10 | this.show = this.show.bind(this); 11 | this.close = this.close.bind(this); 12 | } 13 | 14 | componentDidMount() { 15 | this.props.onRef(this) 16 | } 17 | 18 | componentWillUnmount() { 19 | this.props.onRef(undefined) 20 | } 21 | 22 | supported() { 23 | if ('Notification' in window) return true; 24 | else return false; 25 | } 26 | 27 | show() { 28 | Push.create(this.props.title, { 29 | body: this.props.body ? this.props.body : null, 30 | icon: this.props.icon ? this.props.icon : null, 31 | tag: this.props.tag ? this.props.tag : shortid.generate(), 32 | timeout: this.props.timeout ? this.props.timeout : null, 33 | requireInteraction: this.props.interaction ? this.props.interaction : false, 34 | onClick: this.props.onClick ? this.props.onClick : null 35 | }) 36 | } 37 | 38 | close(tag) { 39 | Push.close(tag); 40 | } 41 | 42 | render() { 43 | return ( 44 |
45 |
46 | ) 47 | } 48 | } 49 | 50 | export default ReactNotifications; 51 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import ReactNotifications from './ReactNotifications.js'; 2 | 3 | export default ReactNotifications; 4 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | module.exports = { 3 | entry: './src/index.js', 4 | output: { 5 | path: path.resolve(__dirname, 'build'), 6 | filename: 'index.js', 7 | libraryTarget: 'commonjs2' 8 | }, 9 | module: { 10 | rules: [ 11 | { 12 | test: /\.js$/, 13 | include: path.resolve(__dirname, 'src'), 14 | exclude: /(node_modules|bower_components|build)/, 15 | use: { 16 | loader: 'babel-loader', 17 | options: { 18 | presets: ['env'] 19 | } 20 | } 21 | } 22 | ] 23 | }, 24 | externals: { 25 | 'react': 'commonjs react' 26 | } 27 | }; 28 | --------------------------------------------------------------------------------