Procházet zdrojové kódy

Merge branch 'release/0.6.6'

Book Pauk před 6 roky
rodič
revize
cc3d7f1eac

+ 4 - 1
client/components/App.vue

@@ -113,10 +113,13 @@ class App extends Vue {
         this.dispatch('config/loadConfig');
         this.dispatch('config/loadConfig');
         this.$watch('apiError', function(newError) {
         this.$watch('apiError', function(newError) {
             if (newError) {
             if (newError) {
+                let mes = newError.message;
+                if (newError.response && newError.response.config)
+                    mes = newError.response.config.url + '<br>' + newError.response.statusText;
                 this.$notify.error({
                 this.$notify.error({
                     title: 'Ошибка API',
                     title: 'Ошибка API',
                     dangerouslyUseHTMLString: true,
                     dangerouslyUseHTMLString: true,
-                    message: newError.response.config.url + '<br>' + newError.response.statusText
+                    message: mes
                 });
                 });
             }
             }
         });
         });

+ 26 - 3
client/components/Reader/HelpPage/CommonHelpPage/CommonHelpPage.vue

@@ -18,9 +18,15 @@
             <li>поддерживаемые браузеры: Google Chrome, Mozilla Firefox последних версий</li>
             <li>поддерживаемые браузеры: Google Chrome, Mozilla Firefox последних версий</li>
         </ul>
         </ul>
 
 
-        <p>В качестве URL можно задавать html-страничку с книгой, либо прямую ссылку 
+        <p>В качестве URL книги можно задавать html-страничку с книгой, либо прямую ссылку 
         на файл из онлайн-библиотеки (например, скопировав адрес ссылки или кнопки "скачать fb2").</p>
         на файл из онлайн-библиотеки (например, скопировав адрес ссылки или кнопки "скачать fb2").</p>
-        <p>Поддерживаемые форматы: <b>fb2, fb2.zip, html, txt</b> и другие</p>
+        <p>Поддерживаемые форматы: <b>fb2, fb2.zip, html, txt</b> и другие.</p>
+
+        <p>Для автономной загрузки читалки (без интернета):<br>
+        В Google Chrome можно установить флаг <span class="clickable" @click="copyText('chrome://flags/#show-saved-copy')">chrome://flags/#show-saved-copy</span>
+           в значение "Primary". В этом случае на стандартной странице "нет соединения" появится кнопка для автономной загрузки сайта из кэша.<br>
+        В Mozilla Firefox в автономном режиме сайт загружается из кэша автоматически. Если этого не происходит, можно установить опцию
+        "Веб-разработка" -> "Работать автономно".</p>
 
 
         <div v-html="automationHtml"></div>
         <div v-html="automationHtml"></div>
         <p>Связаться с разработчиком: <a href="mailto:bookpauk@gmail.com">bookpauk@gmail.com</a></p>
         <p>Связаться с разработчиком: <a href="mailto:bookpauk@gmail.com">bookpauk@gmail.com</a></p>
@@ -32,6 +38,8 @@
 import Vue from 'vue';
 import Vue from 'vue';
 import Component from 'vue-class-component';
 import Component from 'vue-class-component';
 
 
+import {copyTextToClipboard} from '../../../../share/utils';
+
 export default @Component({
 export default @Component({
 })
 })
 class CommonHelpPage extends Vue {
 class CommonHelpPage extends Vue {
@@ -41,13 +49,22 @@ class CommonHelpPage extends Vue {
 
 
     get automationHtml() {
     get automationHtml() {
         if (this.config.mode == 'omnireader') {
         if (this.config.mode == 'omnireader') {
-            return `<p>Вы можете добавить в свой браузер закладку, указав в ее свойствах вместо адреса следующий код:
+            return `<p>Вы также можете добавить в свой браузер закладку, указав в ее свойствах вместо адреса следующий код:
                     <br><strong>javascript:location.href='http://omnireader.ru/?url='+location.href;</strong>
                     <br><strong>javascript:location.href='http://omnireader.ru/?url='+location.href;</strong>
                     <br>Тогда, нажав на получившуюся кнопку на любой странице интернета, вы автоматически откроете ее в Omni Reader.</p>`;
                     <br>Тогда, нажав на получившуюся кнопку на любой странице интернета, вы автоматически откроете ее в Omni Reader.</p>`;
         } else {
         } else {
             return '';
             return '';
         }
         }
     }
     }
+
+    async copyText(text) {
+        const result = await copyTextToClipboard(text);
+        const msg = (result ? `Ссылка на флаг успешно скопирована в буфер обмена. Можно открыть ее в новой вкладке.` : 'Копирование не удалось');
+        if (result)
+            this.$notify.success({message: msg});
+        else
+            this.$notify.error({message: msg});
+    }
 }
 }
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 </script>
 </script>
@@ -64,4 +81,10 @@ class CommonHelpPage extends Vue {
 h4 {
 h4 {
     margin: 0;
     margin: 0;
 }
 }
+
+.clickable {
+    color: blue;
+    text-decoration: underline;
+    cursor: pointer;
+}
 </style>
 </style>

+ 77 - 4
client/components/Reader/ServerStorage/ServerStorage.vue

@@ -50,6 +50,7 @@ class ServerStorage extends Vue {
         this.oldSettings = {};
         this.oldSettings = {};
         this.oldRecent = {};
         this.oldRecent = {};
         this.oldRecentLast = {};
         this.oldRecentLast = {};
+        this.oldRecentLastDiff = {};
     }
     }
 
 
     async init() {
     async init() {
@@ -353,13 +354,15 @@ class ServerStorage extends Vue {
 
 
         const oldRev = bookManager.recentRev;
         const oldRev = bookManager.recentRev;
         const oldLastRev = bookManager.recentLastRev;
         const oldLastRev = bookManager.recentLastRev;
+        const oldLastDiffRev = bookManager.recentLastDiffRev;
         //проверим ревизию на сервере
         //проверим ревизию на сервере
         let revs = null;
         let revs = null;
         if (!force) {
         if (!force) {
             try {
             try {
-                revs = await this.storageCheck({recent: {}, recentLast: {}});
+                revs = await this.storageCheck({recent: {}, recentLast: {}, recentLastDiff: {}});
                 if (revs.state == 'success' && revs.items.recent.rev == oldRev &&
                 if (revs.state == 'success' && revs.items.recent.rev == oldRev &&
-                    revs.items.recentLast.rev == oldLastRev) {
+                    revs.items.recentLast.rev == oldLastRev &&
+                    revs.items.recentLastDiff.rev == oldLastDiffRev) {
                     return;
                     return;
                 }
                 }
             } catch(e) {
             } catch(e) {
@@ -391,24 +394,32 @@ class ServerStorage extends Vue {
             }
             }
         }
         }
 
 
-        if (force || revs.items.recentLast.rev != oldLastRev) {
+        if (force || revs.items.recentLast.rev != oldLastRev || revs.items.recentLastDiff.rev != oldLastDiffRev) {
             let recentLast = null;
             let recentLast = null;
             try {
             try {
-                recentLast = await this.storageGet({recentLast: {}});
+                recentLast = await this.storageGet({recentLast: {}, recentLastDiff: {}});
             } catch(e) {
             } catch(e) {
                 this.error(`Ошибка соединения с сервером: ${e.message}`);
                 this.error(`Ошибка соединения с сервером: ${e.message}`);
                 return;
                 return;
             }
             }
 
 
             if (recentLast.state == 'success') {
             if (recentLast.state == 'success') {
+                const recentLastDiff = recentLast.items.recentLastDiff;
                 recentLast = recentLast.items.recentLast;
                 recentLast = recentLast.items.recentLast;
 
 
                 if (recentLast.rev == 0)
                 if (recentLast.rev == 0)
                     recentLast.data = {};
                     recentLast.data = {};
+                if (recentLastDiff.rev == 0)
+                    recentLastDiff.data = {};
 
 
+                this.oldRecentLastDiff = _.cloneDeep(recentLastDiff.data);
                 this.oldRecentLast = _.cloneDeep(recentLast.data);
                 this.oldRecentLast = _.cloneDeep(recentLast.data);
+
+                recentLast.data = utils.applyObjDiff(recentLast.data, recentLastDiff.data);
+
                 await bookManager.setRecentLast(recentLast.data);
                 await bookManager.setRecentLast(recentLast.data);
                 await bookManager.setRecentLastRev(recentLast.rev);
                 await bookManager.setRecentLastRev(recentLast.rev);
+                await bookManager.setRecentLastDiffRev(recentLastDiff.rev);
             } else {
             } else {
                 this.warning(`Неверный ответ сервера: ${recentLast.state}`);
                 this.warning(`Неверный ответ сервера: ${recentLast.state}`);
             }
             }
@@ -476,6 +487,11 @@ class ServerStorage extends Vue {
         if (utils.isEmptyObjDiff(diff))
         if (utils.isEmptyObjDiff(diff))
             return;
             return;
 
 
+        if (this.oldRecentLast.key == recentLast.key && JSON.stringify(recentLast) > JSON.stringify(diff)) {
+            await this.saveRecentLastDiff(diff, force);
+            return;
+        }
+
         this.savingRecentLast = true;
         this.savingRecentLast = true;
         try {
         try {
             let result = {state: ''};
             let result = {state: ''};
@@ -515,12 +531,69 @@ class ServerStorage extends Vue {
             } else {
             } else {
                 this.oldRecentLast = _.cloneDeep(recentLast);
                 this.oldRecentLast = _.cloneDeep(recentLast);
                 await bm.setRecentLastRev(lastRev + 1);
                 await bm.setRecentLastRev(lastRev + 1);
+                await this.saveRecentLastDiff({}, true);
             }
             }
         } finally {
         } finally {
             this.savingRecentLast = false;
             this.savingRecentLast = false;
         }
         }
     }
     }
 
 
+    async saveRecentLastDiff(diff, force = false) {
+        if (!this.keyInited || !this.serverSyncEnabled || this.savingRecentLastDiff)
+            return;
+
+        const bm = bookManager;
+        let lastRev = bm.recentLastDiffRev;
+
+        const d = utils.getObjDiff(this.oldRecentLastDiff, diff);
+        if (utils.isEmptyObjDiff(d))
+            return;
+
+        this.savingRecentLastDiff = true;
+        try {
+            let result = {state: ''};
+            let tries = 0;
+            while (result.state != 'success' && tries < maxSetTries) {
+                if (force) {
+                    try {
+                        const revs = await this.storageCheck({recentLastDiff: {}});
+                        if (revs.items.recentLastDiff.rev)
+                            lastRev = revs.items.recentLastDiff.rev;
+                    } catch(e) {
+                        this.error(`Ошибка соединения с сервером: ${e.message}`);
+                        return;
+                    }
+                }
+
+                try {
+                    result = await this.storageSet({recentLastDiff: {rev: lastRev + 1, data: diff}}, force);
+                } catch(e) {
+                    this.savingRecentLastDiff = false;
+                    this.error(`Ошибка соединения с сервером: (${e.message}). Изменения не сохранены.`);
+                    return;
+                }
+
+                if (result.state == 'reject') {
+                    await this.loadRecent(false);
+                    this.savingRecentLastDiff = false;
+                    return;
+                }
+
+                tries++;
+            }
+
+            if (tries >= maxSetTries) {
+                console.error(result);
+                this.error('Не удалось отправить данные на сервер. Данные не сохранены и могут быть перезаписаны.');
+            } else {
+                this.oldRecentLastDiff = _.cloneDeep(diff);
+                await bm.setRecentLastDiffRev(lastRev + 1);
+            }
+        } finally {
+            this.savingRecentLastDiff = false;
+        }
+    }
+
     async storageCheck(items) {
     async storageCheck(items) {
         return await this.storageApi('check', items);
         return await this.storageApi('check', items);
     }
     }

+ 7 - 1
client/components/Reader/share/bookManager.js

@@ -38,6 +38,7 @@ class BookManager {
             this.recent[this.recentLast.key] = this.recentLast;
             this.recent[this.recentLast.key] = this.recentLast;
         this.recentRev = await bmRecentStore.getItem('recent-rev') || 0;
         this.recentRev = await bmRecentStore.getItem('recent-rev') || 0;
         this.recentLastRev = await bmRecentStore.getItem('recent-last-rev') || 0;
         this.recentLastRev = await bmRecentStore.getItem('recent-last-rev') || 0;
+        this.recentLastDiffRev = await bmRecentStore.getItem('recent-last-diff-rev') || 0;
         this.books = Object.assign({}, this.booksCached);
         this.books = Object.assign({}, this.booksCached);
 
 
         this.recentChanged2 = true;
         this.recentChanged2 = true;
@@ -428,10 +429,15 @@ class BookManager {
     }
     }
 
 
     async setRecentLastRev(value) {
     async setRecentLastRev(value) {
-        bmRecentStore.setItem('recent-last-rev', value);
+        await bmRecentStore.setItem('recent-last-rev', value);
         this.recentLastRev = value;
         this.recentLastRev = value;
     }
     }
 
 
+    async setRecentLastDiffRev(value) {
+        await bmRecentStore.setItem('recent-last-diff-rev', value);
+        this.recentLastDiffRev = value;
+    }
+
     addEventListener(listener) {
     addEventListener(listener) {
         if (this.eventListeners.indexOf(listener) < 0)
         if (this.eventListeners.indexOf(listener) < 0)
             this.eventListeners.push(listener);        
             this.eventListeners.push(listener);        

+ 1 - 1
package-lock.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "Liberama",
   "name": "Liberama",
-  "version": "0.5.6",
+  "version": "0.6.5",
   "lockfileVersion": 1,
   "lockfileVersion": 1,
   "requires": true,
   "requires": true,
   "dependencies": {
   "dependencies": {

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "Liberama",
   "name": "Liberama",
-  "version": "0.6.5",
+  "version": "0.6.6",
   "engines": {
   "engines": {
     "node": ">=10.0.0"
     "node": ">=10.0.0"
   },
   },