diff --git a/.gitignore b/.gitignore index 98d45fd..f56cd4e 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,9 @@ Icon # Thumbnails ._* +# Webstorm Directories +.idea + # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd diff --git a/README.md b/README.md index 144cc4f..5c9315e 100644 --- a/README.md +++ b/README.md @@ -26,9 +26,10 @@ Monitor [Reddit](//www.reddit.com/)'s [/r/thebutton](//www.reddit.com/r/thebutto - [x] Import historical data on first load - [x] Replay functionality (started as [a pull request from jmlsteele](//github.com/treyp/thebutton/pull/5)) - [x] Show time zone on time axes +- [x] Beep support like Squire +- [X] Cookie to save settings [ implemented as localStorage ] - [ ] Dual chart display (request from [/u/argash](//www.reddit.com/user/argash)) - [ ] Breakdown of percentage of time spent in each color (request from [/u/carpevirginem](//www.reddit.com/user/carpevirginem)) -- [ ] Beep support like Squire - [ ] Scroll to zoom, or at least support clamping to start/end times selectable in options bar - [ ] Horizontal option for log chart - [ ] Export graph to an image @@ -36,4 +37,4 @@ Monitor [Reddit](//www.reddit.com/)'s [/r/thebutton](//www.reddit.com/r/thebutto - [ ] Replace pulse dot with a clearer "live" text that fades out after 1 second (with a 1 second fade) - [ ] Auto dot size and bar height option: starts large, shrinks down based on window size, minimum of 1 - [ ] Add pause button -- [ ] Cookie to save settings + diff --git a/react-components/button-snitch.js b/react-components/button-snitch.js index 9981889..dfae845 100644 --- a/react-components/button-snitch.js +++ b/react-components/button-snitch.js @@ -1,6 +1,14 @@ var ButtonSnitch = React.createClass({ getInitialState: function () { - return this.getInitialStateReal(); + // Alter this to get the local storage settings and merge them into the state. + var initialState = this.getInitialStateReal(); + var savedSettings = this.loadLocalSettings(); + + for(var s in savedSettings) { + initialState[s] = savedSettings[s]; + } + + return initialState; // return this.getInitialStateFake(5e3); }, parseJson: function (serializedJson) { @@ -213,6 +221,15 @@ var ButtonSnitch = React.createClass({ notifiedForCurrentClick: false, lastTimeTrackedForCurrentClick: 60, beep: false, + beepOnce: false, + beepOnceTime: 50, + beepOnceMuted: false, + beepTwice: false, + beepTwiceTime: 40, + beepTwiceMuted: false, + beepThrice: false, + beepThriceTime: 30, + beepThriceMuted: false, discardAfter: false, nightMode: false, displayImportNotice: false, @@ -254,6 +271,15 @@ var ButtonSnitch = React.createClass({ notifiedForCurrentClick: false, lastTimeTrackedForCurrentClick: 60, beep: false, + beepOnce: false, + beepOnceTime: 50, + beepOnceMuted: false, + beepTwice: false, + beepTwiceTime: 40, + beepTwiceMuted: false, + beepThrice: false, + beepThriceTime: 30, + beepThriceMuted: false, discardAfter: false, nightMode: false, displayImportNotice: false, @@ -322,6 +348,18 @@ var ButtonSnitch = React.createClass({ if (this.state.beep) { React.findDOMNode(this.refs.audio).play(); } + + /* Reset beeps when clicked */ + if(this.state.beepOnce && this.state.beepOnceMuted) { + this.state.beepOnceMuted = false; + } + if(this.state.beepTwice && this.state.beepTwiceMuted) { + this.state.beepTwiceMuted = false; + } + if(this.state.beepThrice && this.state.beepThriceMuted) { + this.state.beepThriceMuted = false; + } + }, flairClass: function (seconds) { if (seconds > 51) { @@ -387,6 +425,54 @@ var ButtonSnitch = React.createClass({ midRangeBottom = (histogramIndex + 1); return (midRangeTop + midRangeBottom) / 2; }, + playBeep: function() { + React.findDOMNode(this.refs.audio).play(); + }, + playNumBeeps: function(number) { + for (var i = 0; i < number; i++) { + window.setTimeout(this.playBeep, 500 * i); + } + }, + loadLocalSettings: function() { + // Load settings out of local storage + var snitchSettings = localStorage.getItem('button-snitch-settings'); + return JSON.parse(snitchSettings); + }, + saveLocalSettings: function() { + // Build a list of settings we want to save. + var snitchSettings = { + alertTime: this.state.alertTime, + beep: this.state.beep, + beepOnce: this.state.beepOnce, + beepOnceTime: this.state.beepOnceTime, + beepTwice: this.state.beepTwice, + beepTwiceTime: this.state.beepTwiceTime, + beepThrice: this.state.beepThrice, + beepThriceTime: this.state.beepThriceTime, + nightMode: this.state.nightMode + }; + + // Persist settings to local storage + localStorage.setItem('button-snitch-settings', JSON.stringify(snitchSettings)); + }, + sendBeeps: function(seconds) { + if (this.state.beepOnce && seconds <= this.state.beepOnceTime && !this.state.beepOnceMuted) { + this.playNumBeeps(1); + this.state.beepOnceMuted = true; + } + if (this.state.beepTwice && seconds <= this.state.beepTwiceTime && !this.state.beepTwiceMuted) { + this.playNumBeeps(2); + this.state.beepTwiceMuted = true; + } + if (this.state.beepThrice && seconds <= this.state.beepThriceTime && !this.state.beepThriceMuted) { + this.playNumBeeps(3); + this.state.beepThriceMuted = true; + } + }, + setupInitialSettingsLeftovers : function() { + // call the things that need to be done after the initial state load + this.updateNightMode(this.state.nightMode); + }, updateChartSelection: function (chart) { this.setState({chartSelected: chart}); }, @@ -421,6 +507,27 @@ var ButtonSnitch = React.createClass({ updateBeep: function (beep) { this.setState({beep: beep}); }, + updateBeepOnce: function(beepOnce, beepOnceTime) { + this.setState({ + beepOnce: beepOnce, + beepOnceTime: beepOnceTime, + beepOnceMuted: false + }); + }, + updateBeepTwice: function(beepTwice, beepTwiceTime) { + this.setState({ + beepTwice: beepTwice, + beepTwiceTime: beepTwiceTime, + beepTwiceMuted: false + }); + }, + updateBeepThrice: function(beepThrice, beepThriceTime) { + this.setState({ + beepThrice: beepThrice, + beepThriceTime: beepThriceTime, + beepThriceMuted: false + }); + }, updateDiscardAfter: function (clicks) { clicks = parseInt(clicks, 10) || false; this.setState({discardAfter: clicks}); @@ -565,6 +672,8 @@ var ButtonSnitch = React.createClass({ self.sendNecessaryNotifications(tick.seconds_left); + self.sendBeeps(tick.seconds_left); + currentParticipants = parseInt( tick.participants_text.replace(/,/g, ""), 10 @@ -622,6 +731,9 @@ var ButtonSnitch = React.createClass({ this.findWebSocketFromReddit(); this.downloadClickHistory(); + + // Finish the load from local storage by calling the extras. + this.setupInitialSettingsLeftovers(); }, componentWillUnmount: function () { clearInterval(this.interval); @@ -669,7 +781,22 @@ var ButtonSnitch = React.createClass({ alertTime={this.state.alertTime} updateAlertTime={this.updateAlertTime} beep={this.state.beep} + beepOnce={this.state.beepOnce} + beepOnceTime={this.state.beepOnceTime} + beepOnceMuted={this.state.beepOnceMuted} + + beepTwice={this.state.beepTwice} + beepTwiceTime={this.state.beepTwiceTime} + beepTwiceMuted={this.state.beepTwiceMuted} + + beepThrice={this.state.beepThrice} + beepThriceTime={this.state.beepThriceTime} + beepThriceMuted={this.state.beepThriceMuted} + saveLocalSettings={this.saveLocalSettings} updateBeep={this.updateBeep} + updateBeepOnce={this.updateBeepOnce} + updateBeepTwice={this.updateBeepTwice} + updateBeepThrice={this.updateBeepThrice} discardAfter={this.state.discardAfter} updateDiscardAfter={this.updateDiscardAfter} nightMode={this.state.nightMode} diff --git a/react-components/settings.js b/react-components/settings.js index 8c23c77..d3f00dc 100644 --- a/react-components/settings.js +++ b/react-components/settings.js @@ -22,6 +22,10 @@ var Settings = React.createClass({ this.setState({discardAfter: newProps.discardAfter}); } }, + componentDidUpdate: function(prevOps, prevState){ + // After we update the settings, persist them to local storage. + this.props.saveLocalSettings(); + }, updateAlertTime: function () { this.props.updateAlertTime( React.findDOMNode(this.refs.time).value.trim() @@ -30,6 +34,15 @@ var Settings = React.createClass({ updateBeep: function () { this.props.updateBeep(React.findDOMNode(this.refs.beep).checked); }, + updateBeepOnce: function() { + this.props.updateBeepOnce(React.findDOMNode(this.refs.beepOnce).checked, React.findDOMNode(this.refs.beepOnceTime).value); + }, + updateBeepTwice: function() { + this.props.updateBeepTwice(React.findDOMNode(this.refs.beepTwice).checked, React.findDOMNode(this.refs.beepTwiceTime).value); + }, + updateBeepThrice: function() { + this.props.updateBeepThrice(React.findDOMNode(this.refs.beepThrice).checked, React.findDOMNode(this.refs.beepThriceTime).value); + }, submitDiscard: function (e) { e.preventDefault(); this.props.updateDiscardAfter( @@ -119,6 +132,73 @@ var Settings = React.createClass({ Play a beep for new clicks +