diff --git a/client/components/App.vue b/client/components/App.vue index 7cb8b160..a568a3a9 100644 --- a/client/components/App.vue +++ b/client/components/App.vue @@ -113,10 +113,13 @@ class App extends Vue { this.dispatch('config/loadConfig'); this.$watch('apiError', function(newError) { if (newError) { + let mes = newError.message; + if (newError.response && newError.response.config) + mes = newError.response.config.url + '
' + newError.response.statusText; this.$notify.error({ title: 'Ошибка API', dangerouslyUseHTMLString: true, - message: newError.response.config.url + '
' + newError.response.statusText + message: mes }); } }); diff --git a/client/components/Reader/HelpPage/CommonHelpPage/CommonHelpPage.vue b/client/components/Reader/HelpPage/CommonHelpPage/CommonHelpPage.vue index 6ac25e0b..9a0c5769 100644 --- a/client/components/Reader/HelpPage/CommonHelpPage/CommonHelpPage.vue +++ b/client/components/Reader/HelpPage/CommonHelpPage/CommonHelpPage.vue @@ -18,9 +18,15 @@
  • поддерживаемые браузеры: Google Chrome, Mozilla Firefox последних версий
  • -

    В качестве URL можно задавать html-страничку с книгой, либо прямую ссылку +

    В качестве URL книги можно задавать html-страничку с книгой, либо прямую ссылку на файл из онлайн-библиотеки (например, скопировав адрес ссылки или кнопки "скачать fb2").

    -

    Поддерживаемые форматы: fb2, fb2.zip, html, txt и другие

    +

    Поддерживаемые форматы: fb2, fb2.zip, html, txt и другие.

    + +

    Для автономной загрузки читалки (без интернета):
    + В Google Chrome можно установить флаг chrome://flags/#show-saved-copy + в значение "Primary". В этом случае на стандартной странице "нет соединения" появится кнопка для автономной загрузки сайта из кэша.
    + В Mozilla Firefox в автономном режиме сайт загружается из кэша автоматически. Если этого не происходит, можно установить опцию + "Веб-разработка" -> "Работать автономно".

    Связаться с разработчиком: bookpauk@gmail.com

    @@ -32,6 +38,8 @@ import Vue from 'vue'; import Component from 'vue-class-component'; +import {copyTextToClipboard} from '../../../../share/utils'; + export default @Component({ }) class CommonHelpPage extends Vue { @@ -41,13 +49,22 @@ class CommonHelpPage extends Vue { get automationHtml() { if (this.config.mode == 'omnireader') { - return `

    Вы можете добавить в свой браузер закладку, указав в ее свойствах вместо адреса следующий код: + return `

    Вы также можете добавить в свой браузер закладку, указав в ее свойствах вместо адреса следующий код:
    javascript:location.href='http://omnireader.ru/?url='+location.href;
    Тогда, нажав на получившуюся кнопку на любой странице интернета, вы автоматически откроете ее в Omni Reader.

    `; } else { 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}); + } } //----------------------------------------------------------------------------- @@ -64,4 +81,10 @@ class CommonHelpPage extends Vue { h4 { margin: 0; } + +.clickable { + color: blue; + text-decoration: underline; + cursor: pointer; +} diff --git a/client/components/Reader/ServerStorage/ServerStorage.vue b/client/components/Reader/ServerStorage/ServerStorage.vue index d59b598d..3f27f755 100644 --- a/client/components/Reader/ServerStorage/ServerStorage.vue +++ b/client/components/Reader/ServerStorage/ServerStorage.vue @@ -50,6 +50,7 @@ class ServerStorage extends Vue { this.oldSettings = {}; this.oldRecent = {}; this.oldRecentLast = {}; + this.oldRecentLastDiff = {}; } async init() { @@ -353,13 +354,15 @@ class ServerStorage extends Vue { const oldRev = bookManager.recentRev; const oldLastRev = bookManager.recentLastRev; + const oldLastDiffRev = bookManager.recentLastDiffRev; //проверим ревизию на сервере let revs = null; if (!force) { try { - revs = await this.storageCheck({recent: {}, recentLast: {}}); + revs = await this.storageCheck({recent: {}, recentLast: {}, recentLastDiff: {}}); 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; } } 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; try { - recentLast = await this.storageGet({recentLast: {}}); + recentLast = await this.storageGet({recentLast: {}, recentLastDiff: {}}); } catch(e) { this.error(`Ошибка соединения с сервером: ${e.message}`); return; } if (recentLast.state == 'success') { + const recentLastDiff = recentLast.items.recentLastDiff; recentLast = recentLast.items.recentLast; if (recentLast.rev == 0) recentLast.data = {}; + if (recentLastDiff.rev == 0) + recentLastDiff.data = {}; + this.oldRecentLastDiff = _.cloneDeep(recentLastDiff.data); this.oldRecentLast = _.cloneDeep(recentLast.data); + + recentLast.data = utils.applyObjDiff(recentLast.data, recentLastDiff.data); + await bookManager.setRecentLast(recentLast.data); await bookManager.setRecentLastRev(recentLast.rev); + await bookManager.setRecentLastDiffRev(recentLastDiff.rev); } else { this.warning(`Неверный ответ сервера: ${recentLast.state}`); } @@ -476,6 +487,11 @@ class ServerStorage extends Vue { if (utils.isEmptyObjDiff(diff)) return; + if (this.oldRecentLast.key == recentLast.key && JSON.stringify(recentLast) > JSON.stringify(diff)) { + await this.saveRecentLastDiff(diff, force); + return; + } + this.savingRecentLast = true; try { let result = {state: ''}; @@ -515,12 +531,69 @@ class ServerStorage extends Vue { } else { this.oldRecentLast = _.cloneDeep(recentLast); await bm.setRecentLastRev(lastRev + 1); + await this.saveRecentLastDiff({}, true); } } finally { 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) { return await this.storageApi('check', items); } diff --git a/client/components/Reader/share/bookManager.js b/client/components/Reader/share/bookManager.js index 59ad205e..0bb8972e 100644 --- a/client/components/Reader/share/bookManager.js +++ b/client/components/Reader/share/bookManager.js @@ -38,6 +38,7 @@ class BookManager { this.recent[this.recentLast.key] = this.recentLast; this.recentRev = await bmRecentStore.getItem('recent-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.recentChanged2 = true; @@ -428,10 +429,15 @@ class BookManager { } async setRecentLastRev(value) { - bmRecentStore.setItem('recent-last-rev', value); + await bmRecentStore.setItem('recent-last-rev', value); this.recentLastRev = value; } + async setRecentLastDiffRev(value) { + await bmRecentStore.setItem('recent-last-diff-rev', value); + this.recentLastDiffRev = value; + } + addEventListener(listener) { if (this.eventListeners.indexOf(listener) < 0) this.eventListeners.push(listener); diff --git a/package-lock.json b/package-lock.json index fc13440b..8b8e16b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "Liberama", - "version": "0.5.6", + "version": "0.6.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 0a58b078..83774c3d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Liberama", - "version": "0.6.5", + "version": "0.6.6", "engines": { "node": ">=10.0.0" },