Compare commits

..

15 Commits
0.6.4 ... 0.6.6

Author SHA1 Message Date
Book Pauk
cc3d7f1eac Merge branch 'release/0.6.6' 2019-03-28 14:47:52 +07:00
Book Pauk
4107282fbf Версия 0.6.6 2019-03-28 14:47:28 +07:00
Book Pauk
c29ffc3fcd Поправки багов 2019-03-28 14:45:42 +07:00
Book Pauk
f648bcda13 Доработки, оптимизация сохранения recentLast 2019-03-28 14:05:13 +07:00
Book Pauk
aa0044eed2 package-lock.json 2019-03-28 13:15:29 +07:00
Book Pauk
2312a721ae Поправлен текст помощи для автономной загрузки читалки 2019-03-28 13:14:57 +07:00
Book Pauk
b93fc39b00 Мелкая поправка 2019-03-28 12:44:27 +07:00
Book Pauk
2dc2cd700f Merge tag '0.6.5' into develop
0.6.5
2019-03-25 14:04:51 +07:00
Book Pauk
d69e534f8b Merge branch 'release/0.6.5' 2019-03-25 14:04:43 +07:00
Book Pauk
1de9ddd394 Версия 0.6.5 2019-03-25 14:04:16 +07:00
Book Pauk
77c68d4e11 Небольшие поправки 2019-03-25 14:03:50 +07:00
Book Pauk
2a0d1dcfce Поправка бага 2019-03-25 13:06:48 +07:00
Book Pauk
5a19cca407 Поправка текста 2019-03-25 12:53:50 +07:00
Book Pauk
4e8773ecde Мелкая поправка 2019-03-25 12:51:01 +07:00
Book Pauk
4c7dada809 Merge tag '0.6.4' into develop
0.6.4
2019-03-24 14:33:19 +07:00
7 changed files with 147 additions and 31 deletions

View File

@@ -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
}); });
} }
}); });

View File

@@ -4,7 +4,8 @@
<ul> <ul>
<li>загрузка любой страницы интернета</li> <li>загрузка любой страницы интернета</li>
<li>изменение цвета фона, текста, размер и тип шрифта и прочее</li> <li>изменение цвета фона, текста, размер и тип шрифта и прочее</li>
<li>установка и запоминание текущей позиции и настроек в браузере (в будущем планируется сохранение и на сервер)</li> <li>установка и запоминание текущей позиции и настроек в браузере и на сервере</li>
<li>синхронизация данных (настроек и читаемых книг) между различными устройствами</li>
<li>кэширование файлов книг на клиенте и на сервере</li> <li>кэширование файлов книг на клиенте и на сервере</li>
<li>открытие книг с локального диска</li> <li>открытие книг с локального диска</li>
<li>плавный скроллинг текста</li> <li>плавный скроллинг текста</li>
@@ -17,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>
@@ -31,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 {
@@ -40,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>
@@ -63,4 +81,10 @@ class CommonHelpPage extends Vue {
h4 { h4 {
margin: 0; margin: 0;
} }
.clickable {
color: blue;
text-decoration: underline;
cursor: pointer;
}
</style> </style>

View File

@@ -37,6 +37,7 @@ export default @Component({
class ServerStorage extends Vue { class ServerStorage extends Vue {
created() { created() {
this.inited = false; this.inited = false;
this.keyInited = false;
this.commit = this.$store.commit; this.commit = this.$store.commit;
this.prevServerStorageKey = null; this.prevServerStorageKey = null;
this.$root.$on('generateNewServerStorageKey', () => {this.generateNewServerStorageKey()}); this.$root.$on('generateNewServerStorageKey', () => {this.generateNewServerStorageKey()});
@@ -49,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() {
@@ -88,6 +90,7 @@ class ServerStorage extends Vue {
if (this.prevServerStorageKey != this.serverStorageKey) { if (this.prevServerStorageKey != this.serverStorageKey) {
this.prevServerStorageKey = this.serverStorageKey; this.prevServerStorageKey = this.serverStorageKey;
this.hashedStorageKey = utils.toBase58(cryptoUtils.sha256(this.serverStorageKey)); this.hashedStorageKey = utils.toBase58(cryptoUtils.sha256(this.serverStorageKey));
this.keyInited = true;
await this.loadProfiles(force); await this.loadProfiles(force);
this.checkCurrentProfile(); this.checkCurrentProfile();
@@ -163,7 +166,7 @@ class ServerStorage extends Vue {
} }
async loadSettings(force) { async loadSettings(force) {
if (!this.serverSyncEnabled || !this.currentProfile) if (!this.keyInited || !this.serverSyncEnabled || !this.currentProfile)
return; return;
const setsId = `settings-${this.currentProfile}`; const setsId = `settings-${this.currentProfile}`;
@@ -206,7 +209,7 @@ class ServerStorage extends Vue {
} }
async saveSettings() { async saveSettings() {
if (!this.serverSyncEnabled || !this.currentProfile || this.savingSettings) if (!this.keyInited || !this.serverSyncEnabled || !this.currentProfile || this.savingSettings)
return; return;
const diff = utils.getObjDiff(this.oldSettings, this.settings); const diff = utils.getObjDiff(this.oldSettings, this.settings);
@@ -252,7 +255,7 @@ class ServerStorage extends Vue {
} }
async loadProfiles(force) { async loadProfiles(force) {
if (!this.serverSyncEnabled) if (!this.keyInited || !this.serverSyncEnabled)
return; return;
const oldRev = this.profilesRev; const oldRev = this.profilesRev;
@@ -294,7 +297,7 @@ class ServerStorage extends Vue {
} }
async saveProfiles() { async saveProfiles() {
if (!this.serverSyncEnabled || this.savingProfiles) if (!this.keyInited || !this.serverSyncEnabled || this.savingProfiles)
return; return;
const diff = utils.getObjDiff(this.oldProfiles, this.profiles); const diff = utils.getObjDiff(this.oldProfiles, this.profiles);
@@ -346,18 +349,20 @@ class ServerStorage extends Vue {
} }
async loadRecent(force) { async loadRecent(force) {
if (!this.serverSyncEnabled) if (!this.keyInited || !this.serverSyncEnabled)
return; return;
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) {
@@ -389,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}`);
} }
@@ -416,7 +429,7 @@ class ServerStorage extends Vue {
} }
async saveRecent() { async saveRecent() {
if (!this.serverSyncEnabled || this.savingRecent) if (!this.keyInited || !this.serverSyncEnabled || this.savingRecent)
return; return;
const bm = bookManager; const bm = bookManager;
@@ -462,7 +475,7 @@ class ServerStorage extends Vue {
} }
async saveRecentLast(force = false) { async saveRecentLast(force = false) {
if (!this.serverSyncEnabled || this.savingRecentLast) if (!this.keyInited || !this.serverSyncEnabled || this.savingRecentLast)
return; return;
const bm = bookManager; const bm = bookManager;
@@ -474,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: ''};
@@ -513,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);
} }

View File

@@ -452,7 +452,7 @@
</el-tab-pane> </el-tab-pane>
<!-- Сброс -------------------------------------------------------------------------> <!-- Сброс ------------------------------------------------------------------------->
<el-tab-pane label="Сброс"> <el-tab-pane label="Сброс">
<el-button @click="setDefaults">Установить по-умолчанию</el-button> <el-button @click="setDefaults">Установить по умолчанию</el-button>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>

View File

@@ -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;
@@ -79,19 +80,22 @@ class BookManager {
} }
} }
let key = null; //"ленивая" загрузка
len = await bmRecentStore.length(); (async() => {
for (let i = 0; i < len; i++) { let key = null;
key = await bmRecentStore.key(i); len = await bmRecentStore.length();
if (key) { for (let i = 0; i < len; i++) {
let r = await bmRecentStore.getItem(key); key = await bmRecentStore.key(i);
if (_.isObject(r) && r.key) { if (key) {
this.recent[r.key] = r; let r = await bmRecentStore.getItem(key);
if (_.isObject(r) && r.key) {
this.recent[r.key] = r;
}
} else {
await bmRecentStore.removeItem(key);
} }
} else {
await bmRecentStore.removeItem(key);
} }
} })();
//размножение для дебага //размножение для дебага
/*if (key) { /*if (key) {
@@ -410,6 +414,11 @@ class BookManager {
this.recentLast = value; this.recentLast = value;
await bmCacheStore.setItem('recent-last', this.recentLast); await bmCacheStore.setItem('recent-last', this.recentLast);
if (value && value.key) { if (value && value.key) {
//гарантия переключения книги
const mostRecent = this.mostRecentBook();
if (mostRecent)
this.recent[mostRecent.key].touchTime = value.touchTime - 1;
this.recent[value.key] = value; this.recent[value.key] = value;
await bmRecentStore.setItem(value.key, value); await bmRecentStore.setItem(value.key, value);
await bmCacheStore.setItem('recent', this.recent); await bmCacheStore.setItem('recent', this.recent);
@@ -420,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);

2
package-lock.json generated
View File

@@ -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": {

View File

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