Repository: hovancik/stretchly
Pull request #568 (2020-04-25 12:02:48)

Time format fixes


Patch:
diff --git a/app/locales/en.json b/app/locales/en.json
index b97a3ea..4dc71d6 100644
--- a/app/locales/en.json
+++ b/app/locales/en.json
@@ -1,8 +1,8 @@
 {
   "main": {
     "toolTipHeader": "Stretchly - The break time reminder app",
-    "microbreakIn": "Mini Break in {{seconds}} seconds…",
-    "breakIn": "Long Break in {{seconds}} seconds…",
+    "microbreakIn": "Mini Break in $t(utils.seconds, {'count': {{seconds}} })",
+    "breakIn": "Long Break in $t(utils.seconds, {'count': {{seconds}} })",
     "resumingBreaks": "Resuming breaks",
     "downloadLatestVersion": "Download latest version",
     "toBreak": "Long Break",
@@ -155,7 +155,7 @@
     "aboutMinutesRemaining": "About $t(utils.minutes, {'count': {{minutes}} }) remaining",
     "aboutHoursRemaining": "About $t(utils.hours, {'count': {{hours}} }) remaining",
     "aboutHoursMinutesRemaining": "About $t(utils.hours, {'count': {{hours}} }) $t(utils.minutes, {'count': {{minutes}} }) remaining",
-    "inSeconds": "$t(utils.seconds, {'count': {{seconds}} }) remaining",
+    "inSeconds": "in $t(utils.seconds, {'count': {{seconds}} })",
     "inAboutMinutes": "in about $t(utils.minutes, {'count': {{minutes}} })",
     "inAboutHours": "in about $t(utils.hours, {'count': {{hours}} })",
     "inAboutHoursMinutes": "in about $t(utils.hours, {'count': {{hours}} }) $t(utils.minutes, {'count': {{minutes}} })"
diff --git a/app/process.js b/app/process.js
index f6c2a4c..c3dbd86 100644
--- a/app/process.js
+++ b/app/process.js
@@ -30,14 +30,14 @@ ipcRenderer.on('checkVersion', (event, { oldVersion, notify, silent }) => {
 })
 
 ipcRenderer.on('showNotification', (event, { text, silent }) => {
-  new Notification('stretchly', { // eslint-disable-line no-new
+  new Notification('Stretchly', { // eslint-disable-line no-new
     body: text,
     silent
   })
 })
 
 function notifyNewVersion (silent) {
-  const notification = new Notification('stretchly', {
+  const notification = new Notification('Stretchly', {
     body: i18next.t('process.newVersionAvailable'),
     silent
   })
diff --git a/app/utils/utils.js b/app/utils/utils.js
index 6b36635..8005fca 100644
--- a/app/utils/utils.js
+++ b/app/utils/utils.js
@@ -1,53 +1,56 @@
-const i18next = require('i18next')
+// TODO if I am not wrong, formating function are mathemticaly the same
+// Would be nice to not have the same code
+const formatTimeRemaining = function (milliseconds, i18next = require('i18next')) {
+  const seconds = Math.ceil(milliseconds / 1000.0)
+  const minutes = Math.ceil(seconds / 60.0)
+  const hours = Math.ceil(minutes / 60.0)
 
-// Returns `XXX remaining`
-// `About 2 hours 53 mintes remaining`
-// `About 53 mintes remaining`
-// `10 seconds remaining`
-const formatTimeRemaining = function (milliseconds) {
-  const seconds = milliseconds / 1000
   if (seconds < 60) {
-    return i18next.t('utils.secondsRemaining', { seconds: Math.trunc(seconds + 1) })
+    return i18next.t('utils.secondsRemaining', { seconds: seconds })
   }
-  if (seconds >= 60 && seconds < 3600) {
-    return i18next.t('utils.aboutMinutesRemaining', { minutes: Math.trunc((seconds / 60) + 1) })
+
+  if (seconds >= 60 && minutes < 60) {
+    if (seconds === 60) {
+      return i18next.t('utils.aboutMinutesRemaining', { minutes: 2 })
+    }
+    return i18next.t('utils.aboutMinutesRemaining', { minutes: minutes })
   }
-  if (seconds >= 3600) {
-    const hours = Math.trunc(seconds / 3600)
-    const minutes = Math.trunc(((seconds - hours * 3600) / 60) + 1)
-    if (seconds % 3600 === 0) {
+
+  if (minutes >= 60) {
+    if (minutes % 60 === 0) {
       return i18next.t('utils.aboutHoursRemaining', { hours: hours })
-    } else if (minutes === 60) {
-      return i18next.t('utils.aboutHoursRemaining', { hours: hours + 1 })
-    } else {
-      return i18next.t('utils.aboutHoursMinutesRemaining', { minutes: minutes, hours: hours })
     }
+    return i18next.t('utils.aboutHoursMinutesRemaining',
+      { minutes: minutes - (hours - 1) * 60, hours: hours - 1 })
   }
+  return 'Uknown time remainig'
 }
 
-// Returns `XXX remaining`
-// `in about 2 hours 53 mintes`
-// `in about 53 mintes`
-// `in 10 seconds`
-const formatTimeIn = function (milliseconds) {
-  const seconds = milliseconds / 1000
+const formatTimeIn = function (milliseconds, i18next = require('i18next')) {
+  const seconds = Math.ceil(milliseconds / 1000.0)
+  const minutes = Math.ceil(seconds / 60.0)
+  const hours = Math.ceil(minutes / 60.0)
+
   if (seconds < 60) {
-    return i18next.t('utils.inSeconds', { seconds: Math.trunc(seconds + 1) })
+    return i18next.t('utils.inSeconds', { seconds: seconds })
   }
-  if (seconds >= 60 && seconds < 3600) {
-    return i18next.t('utils.inAboutMinutes', { minutes: Math.trunc((seconds / 60) + 1) })
+
+  if (seconds >= 60 && minutes < 60) {
+    if (seconds === 60) {
+      return i18next.t('utils.inAboutMinutes', { minutes: 2 })
+    }
+    return i18next.t('utils.inAboutMinutes', { minutes: minutes })
   }
-  if (seconds >= 3600) {
-    const hours = Math.trunc(seconds / 3600)
-    const minutes = Math.trunc(((seconds - hours * 3600) / 60) + 1)
-    if (seconds % 3600 === 0) {
+
+  if (minutes >= 60) {
+    if (minutes % 60 === 0) {
       return i18next.t('utils.inAboutHours', { hours: hours })
-    } else if (minutes === 60) {
-      return i18next.t('utils.inAboutHours', { hours: hours + 1 })
     } else {
-      return i18next.t('utils.inAboutHoursMinutes', { minutes: minutes, hours: hours })
+      return i18next.t('utils.inAboutHoursMinutes',
+        { minutes: minutes - (hours - 1) * 60, hours: hours - 1 })
     }
   }
+  return 'in unknown time'
 }
 
 // does not consider `postponesLimit`


Test patch:
diff --git a/test/utils.js b/test/utils.js
index 5939206..afe42b2 100644
--- a/test/utils.js
+++ b/test/utils.js
@@ -1,48 +1,76 @@
-// TODO temp. disabled. see https://github.com/hovancik/i18next-test
-// const chai = require('chai')
-// const Utils = require('../app/utils/utils')
-// const i18next = require('i18next')
-// const Backend = require('i18next-node-fs-backend')
-//
-// chai.should()
-//
-// describe('Utils', function () {
-//   beforeEach(function (done) {
-//     i18next
-//       .use(Backend)
-//       .init({
-//         lng: 'en',
-//         fallbackLng: 'en',
-//         debug: false,
-//         backend: {
-//           loadPath: `${__dirname}/../app/locales/{{lng}}.json`,
-//           jsonIndent: 2
-//         }
-//       }, function (err, t) {
-//         i18next.changeLanguage('en')
-//         if (err) {
-//           console.log(err.stack)
-//         }
-//         done()
-//       })
-//   })
-//
-//   it('formats remaining seconds into correct format', function () {
-//     Utils.formatTimeRemaining(10).should.equal('11 seconds left')
-//     Utils.formatTimeRemaining(60).should.equal('2 minutes left')
-//     Utils.formatTimeRemaining(61).should.equal('2 minutes left')
-//     Utils.formatTimeRemaining(150).should.equal('3 minutes left')
-//   })
-//
-// })
-
 const chai = require('chai')
-const { canPostpone, canSkip } = require('../app/utils/utils')
+const { formatTimeRemaining, formatTimeIn, canSkip, canPostpone } = require('../app/utils/utils')
+const i18next = require('i18next')
+const Backend = require('i18next-node-fs-backend')
 const sinon = require('sinon')
 
 chai.should()
 
-describe('Time Until Next Break', () => {
+describe('Times formatters', function () {
+  beforeEach(function (done) {
+    i18next
+      .use(Backend)
+      .init({
+        lng: 'en',
+        fallbackLng: 'en',
+        debug: false,
+        backend: {
+          loadPath: `${__dirname}/../app/locales/{{lng}}.json`,
+          jsonIndent: 2
+        }
+      }, function (err, t) {
+        if (err) {
+          console.log(err)
+          return done(err)
+        }
+        done()
+      })
+  })
+
+  it('formats "remaining" milliseconds into correct format', function () {
+    formatTimeRemaining(800, i18next).should.equal('1 second remaining')
+    formatTimeRemaining(1000, i18next).should.equal('1 second remaining')
+    formatTimeRemaining(1001, i18next).should.equal('2 seconds remaining')
+    formatTimeRemaining(2000, i18next).should.equal('2 seconds remaining')
+    formatTimeRemaining(59001, i18next).should.equal('About 2 minutes remaining')
+    formatTimeRemaining(60001, i18next).should.equal('About 2 minutes remaining')
+    formatTimeRemaining(118001, i18next).should.equal('About 2 minutes remaining')
+    formatTimeRemaining(119001, i18next).should.equal('About 2 minutes remaining')
+    formatTimeRemaining(120000, i18next).should.equal('About 2 minutes remaining')
+    formatTimeRemaining(120001, i18next).should.equal('About 3 minutes remaining')
+    formatTimeRemaining(3480001, i18next).should.equal('About 59 minutes remaining')
+    formatTimeRemaining(3540001, i18next).should.equal('About 1 hour remaining')
+    formatTimeRemaining(3600000, i18next).should.equal('About 1 hour remaining')
+    formatTimeRemaining(3600001, i18next).should.equal('About 1 hour 1 minute remaining')
+    formatTimeRemaining(7080001, i18next).should.equal('About 1 hour 59 minutes remaining')
+    formatTimeRemaining(7080001, i18next).should.equal('About 1 hour 59 minutes remaining')
+    formatTimeRemaining(7180001, i18next).should.equal('About 2 hours remaining')
+  })
+
+  it('formats "in" milliseconds into correct format', function () {
+    formatTimeIn(800, i18next).should.equal('in 1 second')
+    formatTimeIn(1000, i18next).should.equal('in 1 second')
+    formatTimeIn(1001, i18next).should.equal('in 2 seconds')
+    formatTimeIn(2000, i18next).should.equal('in 2 seconds')
+    formatTimeIn(58001, i18next).should.equal('in 59 seconds')
+    formatTimeIn(59000, i18next).should.equal('in 59 seconds')
+    formatTimeIn(59001, i18next).should.equal('in about 2 minutes')
+    formatTimeIn(60001, i18next).should.equal('in about 2 minutes')
+    formatTimeIn(118001, i18next).should.equal('in about 2 minutes')
+    formatTimeIn(119001, i18next).should.equal('in about 2 minutes')
+    formatTimeIn(120001, i18next).should.equal('in about 3 minutes')
+    formatTimeIn(3480000, i18next).should.equal('in about 58 minutes')
+    formatTimeIn(3540001, i18next).should.equal('in about 1 hour')
+    formatTimeIn(3600001, i18next).should.equal('in about 1 hour 1 minute')
+    formatTimeIn(3660000, i18next).should.equal('in about 1 hour 1 minute')
+    formatTimeIn(3660001, i18next).should.equal('in about 1 hour 2 minutes')
+    formatTimeIn(7140000, i18next).should.equal('in about 1 hour 59 minutes')
+    formatTimeIn(7200000, i18next).should.equal('in about 2 hours')
+    formatTimeIn(7260000, i18next).should.equal('in about 2 hours 1 minute')
+  })
+})
+
+describe('canSkip and canPostpone', () => {
   // stubbing date
   before(() => {
     this.sandbox = sinon.createSandbox()