diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 7ab518d..1bdeb97 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -55,3 +55,4 @@ Marinara is open source software and is built by volunteers from around the worl - [Iosif Dan](https://github.com/danutiosif): Romanian translation - [Brian L](https://github.com/brianl9995): Spanish translation - [Wesley Matos](https://github.com/wricke): Portuguese (Brazil) translation +- [Pat Finnigan](https://github.com/finnigantime): History Tab enhancement diff --git a/package/_locales/en/messages.json b/package/_locales/en/messages.json index 1585beb..b1f88e2 100644 --- a/package/_locales/en/messages.json +++ b/package/_locales/en/messages.json @@ -325,6 +325,18 @@ "message": "Finish a Pomodoro to see your history", "description": "Placeholder for history heatmap when no Pomodoros have been completed. Shown on the settings-history page." }, + "history_date_range": { + "message": "History Date Range", + "description": "Title for section for selecting the [start, end] date range of Pomodoro history data to display. Shown on the settings-history page." + }, + "history_date_range_start": { + "message": "Start", + "description": "Label for the Date input for the start of the date range of Pomodoro history data to display. Shown on the settings-history page." + }, + "history_date_range_end": { + "message": "End", + "description": "Label for the Date input for the end of the date range of Pomodoro history data to display. Shown on the settings-history page." + }, "daily_tooltip": { "message": "$pomodoros$ between $start$—$end$", "description": "Tooltip for daily distribution chart. Shown on the settings-history page.", diff --git a/src/Messages.js b/src/Messages.js index 3464b3c..391eb3b 100644 --- a/src/Messages.js +++ b/src/Messages.js @@ -273,6 +273,15 @@ class Messages get history() { return chrome.i18n.getMessage('history', []); } + get history_date_range() { + return chrome.i18n.getMessage('history_date_range', []); + } + get history_date_range_start() { + return chrome.i18n.getMessage('history_date_range_start', []); + } + get history_date_range_end() { + return chrome.i18n.getMessage('history_date_range_end', []); + } get history_empty_placeholder() { return chrome.i18n.getMessage('history_empty_placeholder', []); } diff --git a/src/background/History.js b/src/background/History.js index 25fa05c..ff4745a 100644 --- a/src/background/History.js +++ b/src/background/History.js @@ -112,27 +112,37 @@ class History }); } - async stats(since) { + async stats(since, dateRangeStart, dateRangeEnd) { return this.mutex.exclusive(async () => { let { pomodoros } = await this.storage.get('pomodoros'); + let filteredPomodoros = pomodoros; + if (dateRangeStart) { + filteredPomodoros = filteredPomodoros.filter(p => +History.date(p) > +(new Date(dateRangeStart))); + } + + if (dateRangeEnd) { + filteredPomodoros = filteredPomodoros.filter(p => +History.date(p) < +(new Date(dateRangeEnd))); + } - let total = pomodoros.length; - let delta = total === 0 ? 0 : (new Date() - History.date(pomodoros[0])); + let total = filteredPomodoros.length; + let start = History.date(filteredPomodoros[0]); + let delta = total === 0 ? 0 : (new Date() - start); let dayCount = Math.max(delta / 1000 / 60 / 60 / 24, 1); let weekCount = Math.max(dayCount / 7, 1); let monthCount = Math.max(dayCount / (365.25 / 12), 1); return { - day: this.countSince(pomodoros, History.today), + start: start, + day: this.countSince(filteredPomodoros, History.today), dayAverage: total / dayCount, - week: this.countSince(pomodoros, History.thisWeek), + week: this.countSince(filteredPomodoros, History.thisWeek), weekAverage: total / weekCount, - month: this.countSince(pomodoros, History.thisMonth), + month: this.countSince(filteredPomodoros, History.thisMonth), monthAverage: total / monthCount, - period: this.countSince(pomodoros, new Date(since)), + period: this.countSince(filteredPomodoros, new Date(since)), total: total, - daily: this.dailyGroups(pomodoros, since), - pomodoros: pomodoros.map(p => +History.date(p)) + daily: this.dailyGroups(filteredPomodoros, since), + pomodoros: filteredPomodoros.map(p => +History.date(p)) }; }); } diff --git a/src/background/Services.js b/src/background/Services.js index 8252e61..a712c95 100644 --- a/src/background/Services.js +++ b/src/background/Services.js @@ -69,8 +69,8 @@ class HistoryService extends Service this.history = history; } - async getStats(since) { - return await this.history.stats(since); + async getStats(since, dateRangeStart, dateRangeEnd) { + return await this.history.stats(since, dateRangeStart, dateRangeEnd); } async getCSV() { diff --git a/src/options/History.vue b/src/options/History.vue index 1a79788..b9078fc 100644 --- a/src/options/History.vue +++ b/src/options/History.vue @@ -22,6 +22,19 @@
{{ M.total }}
+
+
+

{{ M.history_date_range }}

+
+
+
{{ M.history_date_range_start }}
+ +
+
+
{{ M.history_date_range_end }}
+ +
+

{{ M.daily_distribution }}

@@ -126,6 +139,17 @@ display: inline; font-weight: normal; } +.date-row { + display: flex; + flex-direction: row; + margin: 8px; +} +.date-row-label { + width: 120px; +} +.date-range-selection .date { + width: 220px; +} .day-distribution .options input { display: none; } @@ -201,6 +225,19 @@ import DayDistribution from './DayDistribution'; import WeekDistribution from './WeekDistribution'; import M from '../Messages'; +function pad(num) { + return num >= 10 ? num : `0${num}`; +} + +function toFormattedString(inputDate) { + if (!inputDate) return; + + let y = inputDate.getFullYear(); + let m = inputDate.getMonth(); + let d = inputDate.getDate(); + return `${y}-${pad(m+1)}-${pad(d)}`; +} + export default { data() { return { @@ -208,9 +245,34 @@ export default { pomodoroClient: new PomodoroClient(), stats: null, historyStart: null, - dayDistributionBucketSize: 30 + dayDistributionBucketSize: 30, + dateRangeStart: null, + dateRangeEnd: null }; }, + computed: { + dateRangeStartString: { + get() { + return toFormattedString(this.dateRangeStart); + }, + set(newVal) { + this.dateRangeStart = new Date(newVal); + this.updateStats(); + } + }, + dateRangeEndString: { + get() { + return toFormattedString(this.dateRangeEnd); + }, + set(newVal) { + this.dateRangeEnd = new Date(newVal); + + // add 1 day, so that we include pomodoros on dateRangeEnd + this.dateRangeEnd.setDate(this.dateRangeEnd.getDate() + 1); + this.updateStats(); + } + } + }, async mounted() { this.updateStats(); this.pomodoroClient.on('expire', () => { @@ -266,8 +328,17 @@ export default { // Start at the first Sunday at least 39 weeks (~9 months) ago. start.setDate(start.getDate() - 273); start.setDate(start.getDate() - start.getDay()); - this.stats = await this.historyClient.getStats(+start); + this.stats = await this.historyClient.getStats(+start, this.dateRangeStart, this.dateRangeEnd); this.historyStart = start; + + // Initialize dateRangeStart and dateRangeEnd after we have fetched + // stats for the first time. + if (this.dateRangeStart === null) { + this.dateRangeStart = new Date(this.stats.start); + } + if (this.dateRangeEnd === null) { + this.dateRangeEnd = now; + } } }, filters: {