Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
759ff46c92 | ||
|
|
41957cdceb | ||
|
|
d418e3a1c9 | ||
|
|
f650124428 | ||
|
|
795d109c76 | ||
|
|
6868b3effc | ||
|
|
26747b7013 | ||
|
|
5198f8aa60 | ||
|
|
552da48a32 | ||
|
|
db8a688620 | ||
|
|
3088028d05 | ||
|
|
fd62ef865d |
@@ -55,6 +55,6 @@ module.exports = merge(baseWpConfig, {
|
||||
filename: `${publicDir}/index.html`
|
||||
}),
|
||||
new CopyWebpackPlugin([{from: `${clientDir}/assets/*`, to: `${publicDir}/`, flatten: true}]),
|
||||
//new AppCachePlugin({})
|
||||
new AppCachePlugin({exclude: ['../index.html']})
|
||||
]
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<h4>Возможности читалки:</h4>
|
||||
<ul>
|
||||
<li>загрузка любой страницы интернета</li>
|
||||
<li>работа в автономном режиме (без связи)</li>
|
||||
<li>изменение цвета фона, текста, размер и тип шрифта и прочее</li>
|
||||
<li>установка и запоминание текущей позиции и настроек в браузере и на сервере</li>
|
||||
<li>синхронизация данных (настроек и читаемых книг) между различными устройствами</li>
|
||||
@@ -13,7 +14,6 @@
|
||||
<li>поиск по тексту и копирование фрагмента</li>
|
||||
<li>запоминание недавних книг, скачивание книги из читалки в формате fb2</li>
|
||||
<li>управление кликом и с клавиатуры</li>
|
||||
<li>подключение к интернету не обязательно для чтения книги после ее загрузки</li>
|
||||
<li>регистрация не требуется</li>
|
||||
<li>поддерживаемые браузеры: Google Chrome, Mozilla Firefox последних версий</li>
|
||||
</ul>
|
||||
@@ -22,18 +22,13 @@
|
||||
на файл из онлайн-библиотеки (например, скопировав адрес ссылки или кнопки "скачать fb2").</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-show="mode == 'omnireader'">
|
||||
<p>Вы также можете добавить в свой браузер закладку, указав в ее свойствах вместо адреса следующий код:
|
||||
<p>Вы можете добавить в свой браузер закладку, указав в ее свойствах вместо адреса следующий код:
|
||||
<br><span class="clickable" @click="copyText('javascript:location.href=\'https://omnireader.ru/?url=\'+location.href;', 'Код для адреса закладки успешно скопирован в буфер обмена')">
|
||||
<strong>javascript:location.href='https://omnireader.ru/?url='+location.href;</strong>
|
||||
</span>
|
||||
<br>Тогда, нажав на получившуюся кнопку на любой странице интернета, вы автоматически откроете ее в Omni Reader.
|
||||
<br>Тогда, активировав получившуюся закладку на любой странице интернета, вы автоматически откроете ее в Omni Reader.
|
||||
<br>В Chrome для Android можно вызывать такую закладку по имени прямо в адресной строке браузера (имя стоит сделать попроще).
|
||||
</p>
|
||||
</div>
|
||||
<p>Связаться с разработчиком: <a href="mailto:bookpauk@gmail.com">bookpauk@gmail.com</a></p>
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<li><b>Ctrl+C</b> - скопировать текст со страницы</li>
|
||||
<li><b>R</b> - принудительно обновить книгу в обход кэша</li>
|
||||
<li><b>X</b> - открыть недавние</li>
|
||||
<li><b>O</b> - автономный режим</li>
|
||||
<li><b>S</b> - открыть окно настроек</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -38,6 +38,9 @@
|
||||
<el-tooltip v-show="showToolButton['recentBooks']" content="Открыть недавние" :open-delay="1000" effect="light">
|
||||
<el-button ref="recentBooks" class="tool-button" :class="buttonActiveClass('recentBooks')" @click="buttonClick('recentBooks')"><i class="el-icon-document"></i></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-show="showToolButton['offlineMode']" content="Автономный режим (без интернета)" :open-delay="1000" effect="light">
|
||||
<el-button ref="offlineMode" class="tool-button" :class="buttonActiveClass('offlineMode')" @click="buttonClick('offlineMode')"><i class="el-icon-connection"></i></el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
|
||||
<el-tooltip content="Настроить" :open-delay="1000" effect="light">
|
||||
@@ -257,6 +260,7 @@ class Reader extends Vue {
|
||||
searchActive = false;
|
||||
copyTextActive = false;
|
||||
recentBooksActive = false;
|
||||
offlineModeActive = false;
|
||||
settingsActive = false;
|
||||
helpActive = false;
|
||||
clickMapActive = false;
|
||||
@@ -696,6 +700,11 @@ class Reader extends Vue {
|
||||
}
|
||||
}
|
||||
|
||||
offlineModeToggle() {
|
||||
this.offlineModeActive = !this.offlineModeActive;
|
||||
this.$refs.serverStorage.offlineModeActive = this.offlineModeActive;
|
||||
}
|
||||
|
||||
settingsToggle() {
|
||||
this.settingsActive = !this.settingsActive;
|
||||
if (this.settingsActive) {
|
||||
@@ -781,11 +790,14 @@ class Reader extends Vue {
|
||||
case 'copyText':
|
||||
this.copyTextToggle();
|
||||
break;
|
||||
case 'refresh':
|
||||
this.refreshBook();
|
||||
break;
|
||||
case 'recentBooks':
|
||||
this.recentBooksToggle();
|
||||
break;
|
||||
case 'refresh':
|
||||
this.refreshBook();
|
||||
case 'offlineMode':
|
||||
this.offlineModeToggle();
|
||||
break;
|
||||
case 'settings':
|
||||
this.settingsToggle();
|
||||
@@ -806,6 +818,7 @@ class Reader extends Vue {
|
||||
case 'search':
|
||||
case 'copyText':
|
||||
case 'recentBooks':
|
||||
case 'offlineMode':
|
||||
case 'settings':
|
||||
if (this[`${button}Active`])
|
||||
classResult = classActive;
|
||||
@@ -1130,6 +1143,9 @@ class Reader extends Vue {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
break;
|
||||
case 'KeyO':
|
||||
this.offlineModeToggle();
|
||||
break;
|
||||
case 'KeyS':
|
||||
this.settingsToggle();
|
||||
break;
|
||||
|
||||
@@ -77,6 +77,7 @@ class ServerStorage extends Vue {
|
||||
if (eventName == 'recent-changed') {
|
||||
if (itemKey) {
|
||||
if (!this.recentDeltaInited) {
|
||||
await this.loadRecent();
|
||||
this.warning('Функции сохранения на сервер пока недоступны');
|
||||
return;
|
||||
}
|
||||
@@ -175,12 +176,12 @@ class ServerStorage extends Vue {
|
||||
}
|
||||
|
||||
warning(message) {
|
||||
if (this.showServerStorageMessages)
|
||||
if (this.showServerStorageMessages && !this.offlineModeActive)
|
||||
this.$notify.warning({message});
|
||||
}
|
||||
|
||||
error(message) {
|
||||
if (this.showServerStorageMessages)
|
||||
if (this.showServerStorageMessages && !this.offlineModeActive)
|
||||
this.$notify.error({message});
|
||||
}
|
||||
|
||||
@@ -361,78 +362,80 @@ class ServerStorage extends Vue {
|
||||
}
|
||||
}
|
||||
|
||||
async loadRecent(doNotifySuccess = true) {
|
||||
if (!this.keyInited || !this.serverSyncEnabled)
|
||||
async loadRecent(skipRevCheck = false, doNotifySuccess = true) {
|
||||
if (!this.keyInited || !this.serverSyncEnabled || this.loadingRecent)
|
||||
return;
|
||||
|
||||
const oldRecentRev = bookManager.recentRev;
|
||||
const oldRecentDeltaRev = bookManager.recentDeltaRev;
|
||||
//проверим ревизию на сервере
|
||||
let revs = null;
|
||||
this.loadingRecent = true;
|
||||
try {
|
||||
revs = await this.storageCheck({recent: {}, recentDelta: {}});
|
||||
if (revs.state == 'success' && revs.items.recent.rev == oldRecentRev &&
|
||||
revs.items.recentDelta.rev == oldRecentDeltaRev) {
|
||||
if (!this.recentDeltaInited)
|
||||
await this.initRecentDelta();
|
||||
const oldRecentRev = bookManager.recentRev;
|
||||
const oldRecentDeltaRev = bookManager.recentDeltaRev;
|
||||
//проверим ревизию на сервере
|
||||
let revs = null;
|
||||
if (!skipRevCheck) {
|
||||
try {
|
||||
revs = await this.storageCheck({recent: {}, recentDelta: {}});
|
||||
if (revs.state == 'success' && revs.items.recent.rev == oldRecentRev &&
|
||||
revs.items.recentDelta.rev == oldRecentDeltaRev) {
|
||||
if (!this.recentDeltaInited)
|
||||
await this.initRecentDelta();
|
||||
return;
|
||||
}
|
||||
} catch(e) {
|
||||
this.error(`Ошибка соединения с сервером: ${e.message}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let recent = null;
|
||||
try {
|
||||
recent = await this.storageGet({recent: {}, recentDelta: {}});
|
||||
} catch(e) {
|
||||
this.error(`Ошибка соединения с сервером: ${e.message}`);
|
||||
return;
|
||||
}
|
||||
} catch(e) {
|
||||
this.error(`Ошибка соединения с сервером: ${e.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
let recent = null;
|
||||
try {
|
||||
if (revs.items.recent.rev != oldRecentRev) {
|
||||
recent = await this.storageGet({recent: {}, recentDelta: {}});
|
||||
} else {
|
||||
recent = await this.storageGet({recentDelta: {}});
|
||||
recent.items.recent = {data: _.cloneDeep(bookManager.recent), rev: oldRecentRev};
|
||||
}
|
||||
} catch(e) {
|
||||
this.error(`Ошибка соединения с сервером: ${e.message}`);
|
||||
return;
|
||||
}
|
||||
if (recent.state == 'success') {
|
||||
let recentDelta = recent.items.recentDelta;
|
||||
recent = recent.items.recent;
|
||||
|
||||
if (recent.state == 'success') {
|
||||
let recentDelta = recent.items.recentDelta;
|
||||
recent = recent.items.recent;
|
||||
if (recent.rev == 0)
|
||||
recent.data = {};
|
||||
|
||||
if (recent.rev == 0)
|
||||
recent.data = {};
|
||||
|
||||
let newRecent = {};
|
||||
if (recentDelta && recentDelta.data) {
|
||||
if (recentDelta.data.diff) {
|
||||
newRecent = recent.data;
|
||||
const key = recentDelta.data.diff.key;
|
||||
if (newRecent[key])
|
||||
newRecent[key] = utils.applyObjDiff(newRecent[key], recentDelta.data.diff);
|
||||
let newRecent = {};
|
||||
if (recentDelta && recentDelta.data) {
|
||||
if (recentDelta.data.diff) {
|
||||
newRecent = recent.data;
|
||||
const key = recentDelta.data.diff.key;
|
||||
if (newRecent[key])
|
||||
newRecent[key] = utils.applyObjDiff(newRecent[key], recentDelta.data.diff);
|
||||
} else {
|
||||
newRecent = Object.assign(recent.data, recentDelta.data);
|
||||
}
|
||||
this.recentDelta = recentDelta.data;
|
||||
} else {
|
||||
newRecent = Object.assign(recent.data, recentDelta.data);
|
||||
newRecent = recent.data;
|
||||
this.recentDelta = {};
|
||||
}
|
||||
this.recentDelta = recentDelta.data;
|
||||
|
||||
this.recentDeltaInited = true;
|
||||
|
||||
if (!bookManager.loaded) {
|
||||
this.warning('Ожидание загрузки списка книг перед синхронизацией');
|
||||
while (!bookManager.loaded) await utils.sleep(100);
|
||||
}
|
||||
await bookManager.setRecent(newRecent);
|
||||
await bookManager.setRecentRev(recent.rev);
|
||||
await bookManager.setRecentDeltaRev(recentDelta.rev);
|
||||
} else {
|
||||
newRecent = recent.data;
|
||||
this.recentDelta = {};
|
||||
this.warning(`Неверный ответ сервера: ${recent.state}`);
|
||||
}
|
||||
|
||||
this.recentDeltaInited = true;
|
||||
|
||||
if (!bookManager.loaded) {
|
||||
this.warning('Ожидание загрузки списка книг перед синхронизацией');
|
||||
while (!bookManager.loaded) await utils.sleep(100);
|
||||
}
|
||||
await bookManager.setRecent(newRecent);
|
||||
await bookManager.setRecentRev(recent.rev);
|
||||
await bookManager.setRecentDeltaRev(recentDelta.rev);
|
||||
} else {
|
||||
this.warning(`Неверный ответ сервера: ${recent.state}`);
|
||||
if (doNotifySuccess)
|
||||
this.debouncedNotifySuccess();
|
||||
} finally {
|
||||
this.loadingRecent = false;
|
||||
}
|
||||
|
||||
if (doNotifySuccess)
|
||||
this.debouncedNotifySuccess();
|
||||
}
|
||||
|
||||
async saveRecent(itemKey, recurse) {
|
||||
@@ -458,7 +461,7 @@ class ServerStorage extends Vue {
|
||||
this.prevItemKey = itemKey;
|
||||
|
||||
//дифф от дельты для уменьшения размера передаваемых данных в частном случае
|
||||
/*if (this.makeDeltaDiff) {
|
||||
if (this.makeDeltaDiff) {
|
||||
this.recentDelta.diff = utils.getObjDiff(this.prevSavedItem, bm.recent[itemKey]);
|
||||
this.recentDelta.diff.key = itemKey;
|
||||
delete this.recentDelta[itemKey];
|
||||
@@ -470,8 +473,7 @@ class ServerStorage extends Vue {
|
||||
this.recentDelta[key] = utils.applyObjDiff(this.prevSavedItem, this.recentDelta.diff);
|
||||
}
|
||||
delete this.recentDelta.diff;
|
||||
}*/
|
||||
delete this.recentDelta.diff;
|
||||
}
|
||||
|
||||
//сохранение
|
||||
this.savingRecent = true;
|
||||
@@ -486,7 +488,9 @@ delete this.recentDelta.diff;
|
||||
}
|
||||
|
||||
if (result.state == 'reject') {
|
||||
await this.loadRecent(false);
|
||||
|
||||
await this.loadRecent(true, false);
|
||||
|
||||
this.warning(`Последние изменения отменены. Данные синхронизированы с сервером.`);
|
||||
if (!recurse) {
|
||||
this.savingRecent = false;
|
||||
@@ -512,7 +516,9 @@ delete this.recentDelta.diff;
|
||||
}
|
||||
|
||||
if (result.state == 'reject') {
|
||||
await this.loadRecent(false);
|
||||
|
||||
await this.loadRecent(true, false);
|
||||
|
||||
this.warning(`Последние изменения отменены. Данные синхронизированы с сервером.`);
|
||||
if (!recurse) {
|
||||
this.savingRecent = false;
|
||||
|
||||
@@ -6,6 +6,8 @@ export const versionHistory = [
|
||||
`
|
||||
<ul>
|
||||
<li>исправления багов</li>
|
||||
<li>на панель управления добавлена кнопка "Автономный режим"</li>
|
||||
<li>актуализирована справка</li>
|
||||
</ul>
|
||||
`
|
||||
},
|
||||
|
||||
@@ -9,6 +9,7 @@ const toolButtons = [
|
||||
{name: 'copyText', show: false, text: 'Скопировать текст со страницы'},
|
||||
{name: 'refresh', show: true, text: 'Принудительно обновить книгу'},
|
||||
{name: 'recentBooks', show: true, text: 'Открыть недавние'},
|
||||
{name: 'offlineMode', show: false, text: 'Автономный режим (без интернета)'},
|
||||
];
|
||||
|
||||
const fonts = [
|
||||
|
||||
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user