diff --git a/package-lock.json b/package-lock.json index 5eb13ed..6c329a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8220,6 +8220,11 @@ } } }, + "parse-duration": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/parse-duration/-/parse-duration-0.1.1.tgz", + "integrity": "sha1-ExFN3JiRwezSgANiRFVN5DZHoiY=" + }, "parse-entities": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.1.1.tgz", diff --git a/package.json b/package.json index 275c887..b18e2ba 100644 --- a/package.json +++ b/package.json @@ -99,6 +99,7 @@ "node-loader": "^0.6.0", "object-get": "^2.1.0", "opn": "^5.2.0", + "parse-duration": "^0.1.1", "performance-now": "^2.1.0", "pretty-ms": "^3.1.0", "prop-types": "^15.6.0", diff --git a/src/containers/timer/timer-container.js b/src/containers/timer/timer-container.js index d4c0d44..aa6a64a 100644 --- a/src/containers/timer/timer-container.js +++ b/src/containers/timer/timer-container.js @@ -3,9 +3,10 @@ import React, { Component, Fragment } from 'react' import { connect } from 'react-redux' import styled from 'styled-components' import find from 'lodash.find' +import parseDuration from 'parse-duration' import { formatSecondsToStopWatch, roundToNearestMinutes, secondsHuman } from '../../lib/time' import { openInJira } from '../../lib/jira' -import { deleteTimer, pauseTimer, postTimer } from '../../modules/timer' +import { deleteTimer, pauseTimer, postTimer, updateTimer } from '../../modules/timer' import FontAwesomeIcon from '@fortawesome/react-fontawesome' import faPlay from '@fortawesome/fontawesome-free-solid/faPlay' import faPause from '@fortawesome/fontawesome-free-solid/faPause' @@ -23,11 +24,19 @@ class TimerContainer extends Component { this.renderTime = true this.lastTitleUpdate = null this.state = { - timers: [] + timers: [], + editingTimer: null, + editedTime: '' } this.onOpenOptions = this.onOpenOptions.bind(this) this.displayTimers = this.displayTimers.bind(this) + this.onEditTime = this.onEditTime.bind(this) + this.onSaveEditedTime = this.onSaveEditedTime.bind(this) + this.onChange = this.onChange.bind(this) + this.onPlay = this.onPlay.bind(this) + this.onResetEditTime = this.onResetEditTime.bind(this) + this.onKeyPress = this.onKeyPress.bind(this) } componentDidMount () { @@ -40,6 +49,47 @@ class TimerContainer extends Component { this.renderTime = false } + onKeyPress (e, timerId) { + if (e.key === 'Enter') + this.onSaveEditedTime(timerId) + + if (e.key === 'Escape') + this.onResetEditTime() + } + + onEditTime (timerId) { + this.props.pauseTimer(timerId, true) + + this.setState({ editingTimer: timerId }) + } + + onChange (event) { + this.setState({ editedTime: event.target.value }) + } + + onSaveEditedTime (timerId) { + + let editedTime = this.state.editedTime + if (editedTime != '') { + let ms = parseDuration(editedTime) + + // Is the timer entered valid? + if (ms > 0) + this.props.updateTimer(timerId, ms) + } + + this.onResetEditTime() + } + + onResetEditTime () { + this.setState({ editingTimer: null, editedTime: '' }) + } + + onPlay (timerId) { + this.onResetEditTime() + this.props.pauseTimer(timerId, false) + } + displayTimers () { if (!this.renderTime) @@ -134,7 +184,7 @@ class TimerContainer extends Component { ) : ( {timer.paused ? ( - this.props.pauseTimer(timer.id, false)}> + this.onPlay(timer.id)}> ) : ( @@ -145,7 +195,21 @@ class TimerContainer extends Component { )} - + {timer.key} {timer.summary} this.onOpenOptions(timer)} @@ -159,6 +223,22 @@ class TimerContainer extends Component { } } +const EditTime = styled.input` + background: none; + border: none; + color: #FFF; + outline: none; + width: 50px; + font-weight: 500; + letter-spacing: 0.04em; + font-size: 13px; + + &::placeholder { + color: #FFF; + font-style: italic; + } +` + const TimerWrapper = styled.div` padding: 0 15px 0 4px; background: #2381FA; @@ -186,7 +266,8 @@ const Time = styled.span` const mapDispatchToProps = { deleteTimer, pauseTimer, - postTimer + postTimer, + updateTimer } const mapStateToProps = state => ({ diff --git a/src/modules/timer.js b/src/modules/timer.js index d68ab19..abcb373 100644 --- a/src/modules/timer.js +++ b/src/modules/timer.js @@ -12,6 +12,7 @@ const ADD_TIMER = 'jt/timer/ADD_TIMER' const DELETE_TIMER = 'jt/timer/DELETE_TIMER' const PAUSE_TIMER = 'jt/timer/PAUSE_TIMER' const POST_TIMER = 'jt/timer/POST_TIMER' +const UPDATE_TIMER = 'jt/timer/UPDATE_TIMER' const initialState = Immutable({ list: [] @@ -79,6 +80,20 @@ export default function reducer (state = initialState, action = {}) { } } + case UPDATE_TIMER: { + let list = Immutable.asMutable(state.list, {deep: true}) + let timerIndex = findIndex(list, ['id', action.timerId]) + + if (timerIndex > -1) { + let timer = list[timerIndex] + timer.previouslyElapsed = action.ms + + return state.set('list', Immutable(list)) + } else { + return state + } + } + default: return state } } @@ -101,6 +116,12 @@ export const postingTimer = (timerId, posting) => ({ posting }) +export const updateTimer = (timerId, ms) => ({ + type: UPDATE_TIMER, + timerId, + ms +}) + // Side effects export const addTimer = (id, key, summary) => dispatch => { let timer = { @@ -154,7 +175,7 @@ export const postTimer = stateTimer => async (dispatch, getState) => { // Save to recents dispatch(addRecentTask(timer)) - dispatch(fetchWorklogs()) + dispatch(fetchWorklogs(false)) }) .catch(error => { console.log('Error posting timer', error)