Browse Source

Improve sync progress

Gerald 9 years ago
parent
commit
84efc768ab

+ 17 - 0
.eslintrc.yml

@@ -0,0 +1,17 @@
+rules:
+  no-undef:
+    - 0
+  no-unused-vars:
+    - 0
+  comma-dangle:
+    - 0
+  no-console:
+    - 0
+  no-empty:
+    - 0
+
+env:
+  browser: true
+  es6: true
+
+extends: 'eslint:recommended'

+ 10 - 0
gulpfile.js

@@ -5,6 +5,7 @@ const merge2 = require('merge2');
 const cssnano = require('gulp-cssnano');
 const gulpFilter = require('gulp-filter');
 const order = require('gulp-order');
+const eslint = require('gulp-eslint');
 const del = require('del');
 const templateCache = require('./scripts/templateCache');
 const i18n = require('./scripts/i18n');
@@ -41,6 +42,15 @@ gulp.task('watch', () => {
   gulp.watch(paths.locales, ['copy-i18n']);
 });
 
+gulp.task('eslint', () => (
+  gulp.src([
+    'src/**/*.js',
+    '!src/public/**',
+  ])
+  .pipe(eslint())
+  .pipe(eslint.format())
+));
+
 gulp.task('templates', () => (
   merge2([
     gulp.src(paths.cache),

+ 2 - 0
package.json

@@ -5,6 +5,7 @@
     "clean": "node -e \"require('del')(['dist'])\"",
     "dev": "gulp build && gulp watch",
     "i18n": "gulp i18n",
+    "eslint": "gulp eslint",
     "update": "node scripts/updateLib",
     "version": "gulp build"
   },
@@ -17,6 +18,7 @@
     "gulp": "^3.9.1",
     "gulp-concat": "^2.6.0",
     "gulp-cssnano": "^2.1.1",
+    "gulp-eslint": "^2.0.0",
     "gulp-filter": "^3.0.1",
     "gulp-order": "^1.1.1",
     "gulp-replace": "^0.5.4",

+ 7 - 4
src/_locales/cs/messages.yml

@@ -340,14 +340,17 @@ labelSyncTo:
 lastSync:
   description: Label for last sync timestamp.
   message: ''
-msgSyncError:
-  description: Message shown when sync failed.
+msgSyncInit:
+  description: Message shown when sync service is initializing.
+  message: ''
+msgSyncReady:
+  description: Message shown when sync will start soon.
   message: ''
 msgSyncing:
   description: Message shown when sync is in progress.
   message: ''
-msgSyncInit:
-  description: Message shown when sync service is initializing.
+msgSyncError:
+  description: Message shown when sync failed.
   message: ''
 msgNamespaceConflict:
   description: Message shown when namespace of the new script conflicts with an existent one.

+ 7 - 4
src/_locales/de/messages.yml

@@ -340,14 +340,17 @@ labelSyncTo:
 lastSync:
   description: Label for last sync timestamp.
   message: ''
-msgSyncError:
-  description: Message shown when sync failed.
+msgSyncInit:
+  description: Message shown when sync service is initializing.
+  message: ''
+msgSyncReady:
+  description: Message shown when sync will start soon.
   message: ''
 msgSyncing:
   description: Message shown when sync is in progress.
   message: ''
-msgSyncInit:
-  description: Message shown when sync service is initializing.
+msgSyncError:
+  description: Message shown when sync failed.
   message: ''
 msgNamespaceConflict:
   description: Message shown when namespace of the new script conflicts with an existent one.

+ 9 - 6
src/_locales/en/messages.yml

@@ -342,15 +342,18 @@ labelSyncTo:
 lastSync:
   description: Label for last sync timestamp.
   message: Last sync at $1
-msgSyncError:
-  description: Message shown when sync failed.
-  message: 'Sync error!'
-msgSyncing:
-  description: Message shown when sync is in progress.
-  message: Syncing...
 msgSyncInit:
   description: Message shown when sync service is initializing.
   message: Initializing...
+msgSyncReady:
+  description: Message shown when sync will start soon.
+  message: Sync will start soon...
+msgSyncing:
+  description: Message shown when sync is in progress.
+  message: Sync in progress...
+msgSyncError:
+  description: Message shown when sync failed.
+  message: 'Sync error!'
 msgNamespaceConflict:
   description: Message shown when namespace of the new script conflicts with an existent one.
   message: 'Script namespace conflicts! Please modify @name and @namespace.'

+ 7 - 4
src/_locales/pl/messages.yml

@@ -340,14 +340,17 @@ labelSyncTo:
 lastSync:
   description: Label for last sync timestamp.
   message: ''
-msgSyncError:
-  description: Message shown when sync failed.
+msgSyncInit:
+  description: Message shown when sync service is initializing.
+  message: ''
+msgSyncReady:
+  description: Message shown when sync will start soon.
   message: ''
 msgSyncing:
   description: Message shown when sync is in progress.
   message: ''
-msgSyncInit:
-  description: Message shown when sync service is initializing.
+msgSyncError:
+  description: Message shown when sync failed.
   message: ''
 msgNamespaceConflict:
   description: Message shown when namespace of the new script conflicts with an existent one.

+ 7 - 4
src/_locales/ru/messages.yml

@@ -342,14 +342,17 @@ labelSyncTo:
 lastSync:
   description: Label for last sync timestamp.
   message: ''
-msgSyncError:
-  description: Message shown when sync failed.
+msgSyncInit:
+  description: Message shown when sync service is initializing.
+  message: ''
+msgSyncReady:
+  description: Message shown when sync will start soon.
   message: ''
 msgSyncing:
   description: Message shown when sync is in progress.
   message: ''
-msgSyncInit:
-  description: Message shown when sync service is initializing.
+msgSyncError:
+  description: Message shown when sync failed.
   message: ''
 msgNamespaceConflict:
   description: Message shown when namespace of the new script conflicts with an existent one.

+ 9 - 6
src/_locales/zh/messages.yml

@@ -342,15 +342,18 @@ labelSyncTo:
 lastSync:
   description: Label for last sync timestamp.
   message: 最近同步时间为 $1
-msgSyncError:
-  description: Message shown when sync failed.
-  message: 同步出错!
-msgSyncing:
-  description: Message shown when sync is in progress.
-  message: 正在同步...
 msgSyncInit:
   description: Message shown when sync service is initializing.
   message: 正在初始化...
+msgSyncReady:
+  description: Message shown when sync will start soon.
+  message: 即将开始同步...
+msgSyncing:
+  description: Message shown when sync is in progress.
+  message: 正在同步...
+msgSyncError:
+  description: Message shown when sync failed.
+  message: 同步出错!
 msgNamespaceConflict:
   description: Message shown when namespace of the new script conflicts with an existent one.
   message: 脚本命名空间冲突,请修改@name和@namespace!

+ 46 - 44
src/background/sync/index.js

@@ -1,20 +1,9 @@
 var sync = function () {
   var services = [];
   var servicesReady = [];
-  var queue, nextQueue = [];
-  var syncing;
   var inited;
-  var debouncedSync = _.debounce(function () {
-    console.log('Start to sync');
-    queue = nextQueue;
-    nextQueue = [];
-    process();
-    autoSync();
-  }, 10 * 1000);
-  var autoSync = _.debounce(function () {
-    console.log('Auto start to sync');
-    sync();
-  }, 60 * 60 * 1000);
+  var current = Promise.resolve();
+  var autoSync = _.debounce(sync, 60 * 60 * 1000);
 
   function ServiceConfig(name) {
     this.prefix = name;
@@ -112,40 +101,23 @@ var sync = function () {
     });
   }
   function sync(service) {
-    if (service) {
-      service.config.getOption('enabled') && nextQueue.push(service);
-    } else if (!syncing) {
-      nextQueue = servicesReady.filter(function (service) {
-        return service.config.getOption('enabled');
-      });
-    }
-    start();
-  }
-  function start() {
-    if (syncing) return;
-    if (nextQueue.length) {
-      console.log('Ready to sync');
-      debouncedSync();
-      return true;
-    }
-  }
-  function stopSync() {
-    console.log('Sync ended');
-    syncing = false;
-    // start another check in case there are changes during sync
-    start() || autoSync();
-  }
-  function process() {
-    var service = queue.shift();
-    if (!service) return stopSync();
-    syncing = true;
-    service.sync().then(process);
+    var services = (service ? [service] : servicesReady)
+    .filter(function (service) {
+      return service.config.getOption('enabled')
+      && !service.syncState.is('ready') && !service.syncState.is('syncing');
+    });
+    return Promise.all(services.map(function (service) {
+      return service.startSync();
+    })).then(function () {
+      autoSync();
+    });
   }
   function init() {
     inited = true;
     services.forEach(function (service) {
       service.checkSync();
     });
+    sync();
   }
   function getFilename(uri) {
     return 'vm-' + encodeURIComponent(uri);
@@ -194,12 +166,14 @@ var sync = function () {
       ], null, _this.onStateChange),
       _this.syncState = serviceState([
         'idle',
+        'ready',
         'syncing',
         'error',
       ], null, _this.onStateChange),
       _this.initHeaders();
       _this.events = getEventEmitter();
       _this.lastFetch = Promise.resolve();
+      _this.startSync = _this.syncFactory();
     },
     on: function () {
       return this.events.on.apply(null, arguments);
@@ -216,6 +190,36 @@ var sync = function () {
         data: getStates(),
       });
     },
+    syncFactory: function () {
+      var _this = this;
+      var promise, debouncedResolve;
+      function shouldSync() {
+        return _this.authState.is('authorized') && _this.config.getOption('enabled');
+      }
+      function init() {
+        if (!shouldSync()) return Promise.resolve();
+        console.log('Ready to sync:', _this.displayName);
+        _this.syncState.set('ready');
+        promise = current = current.then(function () {
+          return new Promise(function (resolve, reject) {
+            debouncedResolve = _.debounce(resolve, 10 * 1000);
+            debouncedResolve();
+          });
+        }).then(function () {
+          if (shouldSync()) {
+            return _this.sync();
+          }
+          _this.syncState.set('idle');
+        }).then(function () {
+          promise = debouncedResolve = null;
+        });
+      }
+      return function () {
+        if (!promise) init();
+        debouncedResolve && debouncedResolve();
+        return promise;
+      };
+    },
     prepare: function () {
       var _this = this;
       var token = _this.token = _this.config.get('token');
@@ -228,7 +232,7 @@ var sync = function () {
       .then(function () {
         _this.authState.set('authorized');
         servicesReady.push(_this);
-        sync(_this);
+        return _this.startSync();
       }, function (err) {
         if (err) {
           if (err.status === 401) {
@@ -316,8 +320,6 @@ var sync = function () {
     },
     sync: function () {
       var _this = this;
-      if (!_this.authState.is('authorized') || !_this.config.getOption('enabled'))
-        return Promise.resolve();
       _this.syncState.set('syncing');
       return _this.getMeta()
       .then(function (meta) {

+ 1 - 1
src/background/utils.js

@@ -29,7 +29,7 @@ var scriptUtils = {
       grant: [],
     };
     var flag = -1;
-    code.replace(/(?:^|\n)\/\/\s*([@=]\S+)(.*)/g, function(value, group1, group2) {
+    code.replace(/(?:^|\n)\/\/\s*([@=]\S+)(.*)/g, function(m, group1, group2) {
       if (flag < 0 && group1 == '==UserScript==')
         // start meta
         flag = 1;

+ 4 - 3
src/injected.js

@@ -375,10 +375,11 @@ var comm = {
 
           // script object
           addProperty('script', {value:{}}, obj);
-          for(var i in data) {
+          var i;
+          for(i in data) {
             addProperty(i, {value: data[i]}, obj.script);
           }
-          for(var i in script.meta.resources)
+          for(i in script.meta.resources)
             addProperty(i, {value: script.meta.resources[i]}, obj.script.resources);
 
           return obj;
@@ -519,7 +520,7 @@ var comm = {
       else
         code = [];
       for(var i = 0; i < require.length; i ++)
-        if(part = data.require[require[i]]) code.push(part);
+        if((part = data.require[require[i]])) code.push(part);
       // wrap code to make 'use strict' work
       code.push('!function(){' + script.code + '\n}.call(this)');
       code.push('}.call(this);');

+ 6 - 2
src/options/templates/sync-service.html

@@ -14,12 +14,16 @@
     authorizing: _.i18n('buttonAuthorizing'),
   }[it.authState] || _.i18n('buttonAuthorize')
 %></button>
-<button <%= !_.includes(['authorized', 'error'], it.authState) || it.syncing ? 'disabled' : '' %> class="sync-start">
+<button <%=
+!_.includes(['authorized', 'error'], it.authState) ||
+it.syncState === 'syncing' ? 'disabled' : ''
+%> class="sync-start">
   <i class="fa fa-refresh"></i>
 </button>
 <span><%=
   it.authState === 'initializing' ? _.i18n('msgSyncInit') :
   it.syncState === 'error' ? _.i18n('msgSyncError') :
-  it.syncing ? _.i18n('msgSyncing') :
+  it.syncState === 'ready' ? _.i18n('msgSyncReady') :
+  it.syncState === 'syncing' ? _.i18n('msgSyncing') :
   it.lastSync ? _.i18n('lastSync', it.lastSync) : ''
 %></span>

+ 0 - 1
src/options/views/sync-service.js

@@ -7,7 +7,6 @@ var SyncServiceView = BaseView.extend({
   _render: function () {
     var it = this.model.toJSON();
     it.enabled = _.options.get(it.name + 'Enabled');
-    it.syncing = it.syncState === 'syncing';
     if (it.lastSync) it.lastSync = new Date(it.lastSync).toLocaleString();
     this.$el.html(this.templateFn(it));
   },