Repository: hovancik/stretchly (js)

Issue:
resumes from manual pause upon system resume
### Prerequisites

- [x] I'm using version: master at c9d960eeb801921820ad36435717e7c4cc204936 with https://github.com/hovancik/stretchly/pull/233 merged in.
- [x] I'm using version: master at c9d960eeb801921820ad36435717e7c4cc204936.

### Description

Stretchly resumes manual pause upon resume from suspend.

Operating system: Arch linux

### Steps to Reproduce

1. Start app
2. Select pause for 1 hour in menu
3. Suspend the pc
4. Resume from suspend

**Expected behavior:** Stretchly should stay paused

**Actual behavior:** Stretchly is no longer paused

**Reproduces how often:** always

Based on code inspection and in particular `keepAfterPowerResume` this seems to be the intented behavior but this seems wrong to me, so I suggest to change it to preserve any user breakand not only indefinite break. What do you think?

Also as there is currently no way to correct break time after resume from suspend (for example user start 1 hour pause, then suspends the pc for 2 hours and if not for the aforementioned behavior stretchly would still be paused) I suggest to add such correction mechanism to power-state listemers and breakPlanner. This may be done with simple system suspend time logging and then computing passed time upon resume to use for correction.


Pull request:
Fix manual breaks upon suspend/resume
* Only resume automatic for-suspend break
* If was on break then correct break time based on suspend duration
* Remove unnecessary logging

Issue: https://github.com/hovancik/stretchly/issues/259

closes #259 

### Requirements

- [x]  issue was opened to discuss proposed changes before starting implementation.
- [x]  during development, `node` version specified in `package.json` was used.
- [x]  package versions were not changed.
- [x]  app version number was not changed.
- [x]  all new code has tests to ensure against regressions. (most does excluding actual suspend/resume testing)
- [x] `npm run lint` reports no offenses. (fails for eslint-chai problems)
- [x] `npm run test` is error-free. (fails on non related test)
- [x]  README and CHANGELOG were updated accordingly.

### Description of the Change

This PR makes so that user breaks are preserved after system suspend/resume and corrects the break time accordingly.
Based on electron issues (see https://github.com/hovancik/stretchly/issues/259#issuecomment-401622387) it needs to be tested on win and mac devices more.
Also I'll add tests and readme/changelog changes later.

### Verification Process

- [x] Verified that it preserves user pause and corrects pause time (via logging pause time and suspending the pc)
- [x] Verified that it pauses/resumes breaks for suspend when the was no user pause (previous behavior) (via logging pause time and suspending the pc)

### Other

Why don't we use node-version list of all supported versions? It seemed to work just fine on node 10.5.0.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4e93a1b..ba2b1bb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 - Welcome and Tutorial windows on first run and in About window
 - Dutch translations for interface
 
+### Fixed
+- User pause will no longer be removed upon suspend/resume
+- User pause time will be corrected upon suspend/resume for the duration
+  of system sleep
+
 ## [0.17.0] - 2018-05-06
 ### Added
 - Ukrainian translations for interface
diff --git a/README.md b/README.md
index 8efe86b..ca5be93 100644
--- a/README.md
+++ b/README.md
@@ -157,6 +157,7 @@ Before implementing a feature, please open an Issue first, so we can be sure tha
 - Ciprian Rusen, [www.digitalcitizen.life](https://www.digitalcitizen.life)
 - Carlo Gandolfi, @cgand
 - Kavya Jain, @kavya-jain
+- Denys Otrishko, @lundibundi
 
 
 ### Humans and Tools
diff --git a/app/breaksPlanner.js b/app/breaksPlanner.js
index 97f9a35..607d29b 100644
--- a/app/breaksPlanner.js
+++ b/app/breaksPlanner.js
@@ -135,6 +135,10 @@ class BreaksPlanner extends EventEmitter {
     this.nextBreak()
   }
 
+  correctScheduler () {
+    if (this.scheduler) this.scheduler.correct()
+  }
+
   reset () {
     this.clear()
     this.resume()
diff --git a/app/main.js b/app/main.js
index 2aa9a96..cd387d1 100644
--- a/app/main.js
+++ b/app/main.js
@@ -23,7 +23,7 @@ let settingsWin = null
 let tutorialWin = null
 let welcomeWin = null
 let settings
-let isOnIndefinitePause
+let pausedForSuspend = false
 
 app.setAppUserModelId('net.hovancik.stretchly')
 
@@ -84,11 +84,20 @@ function startPowerMonitoring () {
   const electron = require('electron')
   electron.powerMonitor.on('suspend', () => {
     console.log('The system is going to sleep')
-    if (!isOnIndefinitePause) pauseBreaks(1)
+    if (!breakPlanner.isPaused) {
+      pausedForSuspend = true
+      pauseBreaks(1)
+    }
   })
   electron.powerMonitor.on('resume', () => {
     console.log('The system is resuming')
-    if (!isOnIndefinitePause) resumeBreaks()
+    if (pausedForSuspend) {
+      pausedForSuspend = false
+      resumeBreaks()
+    } else if (breakPlanner.isPaused) {
+      // corrrect the planner for the time spent in suspend
+      breakPlanner.correctScheduler()
+    }
   })
 }
 
@@ -402,8 +411,7 @@ function loadIdeas () {
   microbreakIdeas = new IdeasLoader(microbreakIdeasData).ideas()
 }
 
-function pauseBreaks (milliseconds, keepAfterPowerResume = false) {
-  isOnIndefinitePause = keepAfterPowerResume
+function pauseBreaks (milliseconds) {
   if (microbreakWins) {
     finishMicrobreak(false)
   }
@@ -416,7 +424,6 @@ function pauseBreaks (milliseconds, keepAfterPowerResume = false) {
 }
 
 function resumeBreaks () {
-  isOnIndefinitePause = false
   breakPlanner.resume()
   appIcon.setContextMenu(getTrayMenu())
   processWin.webContents.send('showNotification', i18next.t('main.resumingBreaks'))
@@ -569,7 +576,7 @@ function getTrayMenu () {
         }, {
           label: i18next.t('main.indefinitely'),
           click: function () {
-            pauseBreaks(1, true)
+            pauseBreaks(1)
           }
         }
       ]
diff --git a/app/utils/scheduler.js b/app/utils/scheduler.js
index 1676aed..747f233 100644
--- a/app/utils/scheduler.js
+++ b/app/utils/scheduler.js
@@ -17,6 +17,12 @@ class Scheduler {
     this.timer = setTimeout(this.func, this.delay)
   }
 
+  correct () {
+    if (this.timer === null) return
+    clearTimeout(this.timer)
+    this.timer = setTimeout(this.func, this.timeLeft)
+  }
+
   cancel () {
     clearTimeout(this.timer)
     this.timer = null


Test patch:
diff --git a/test/scheduler.js b/test/scheduler.js
index 6e85a92..76a7a8d 100644
--- a/test/scheduler.js
+++ b/test/scheduler.js
@@ -25,6 +25,26 @@ describe('scheduler', function () {
     }, 200)
   })
 
+  it('kinda runs schedule on time with correct', function (done) {
+    let time = 1000
+    let start = Date.now()
+    let callback = function () {
+      // allow margin due to event loop delay
+      (Date.now() - start).should.be
+        .within(time - 100, time + 100)
+      test = false
+    }
+    let schedule = new Scheduler(callback, time)
+    schedule.plan()
+    setTimeout(function () {
+      schedule.correct()
+    }, 200)
+    setTimeout(function () {
+      test.should.be.false
+      done()
+    }, time + 100)
+  })
+
   it('it cancels schedule on cancel()', function (done) {
     let time = 100
     let callback = function () {