Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cafdb5b04b | ||
|
|
697774978e | ||
|
|
8c2c2fe2fc | ||
|
|
e3770463a1 | ||
|
|
d3ad23e9e4 | ||
|
|
79d1e0b30d | ||
|
|
1370bae4d6 | ||
|
|
01fbdf38fa | ||
|
|
be6b07a0cf | ||
|
|
1b057029c8 | ||
|
|
b6b567f20b | ||
|
|
c4c109fe0e | ||
|
|
4c8c921b03 | ||
|
|
69a2e5cda3 |
@@ -115,6 +115,10 @@ Options:
|
|||||||
|
|
||||||
// Подключение себя, как клиента, к серверу обновлений
|
// Подключение себя, как клиента, к серверу обновлений
|
||||||
"bucServer": false
|
"bucServer": false
|
||||||
|
|
||||||
|
// Сcылка для открытия в новом окне брауpера по клику на кнопку "Сетевая библиотека"
|
||||||
|
// Если не задано, открывается внутренний менеджер библиотек с использванием фрейма
|
||||||
|
"networkLibraryLink": "http://samlib.ru/"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ class Misc {
|
|||||||
async loadConfig() {
|
async loadConfig() {
|
||||||
|
|
||||||
const query = {params: [
|
const query = {params: [
|
||||||
'name', 'version', 'mode', 'maxUploadFileSize', 'useExternalBookConverter', 'acceptFileExt', 'bucEnabled', 'branch',
|
'name', 'version', 'mode', 'maxUploadFileSize', 'useExternalBookConverter', 'acceptFileExt', 'bucEnabled', 'branch', 'networkLibraryLink',
|
||||||
]};
|
]};
|
||||||
|
|
||||||
const config = await wsc.message(await wsc.send(Object.assign({action: 'get-config'}, query)));
|
const config = await wsc.message(await wsc.send(Object.assign({action: 'get-config'}, query)));
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ class LibsPage {
|
|||||||
if (!this.mode)
|
if (!this.mode)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//TODO: убрать второе условие в 24г
|
//TODO: убрать условие с mode в 24г
|
||||||
if (!this.libs || (this.mode === 'omnireader' && this.libs.mode !== this.mode)) {
|
if (!this.libs || !this.libs.groups || (this.mode === 'omnireader' && this.libs.mode !== this.mode)) {
|
||||||
const defaults = rstore.getLibsDefaults(this.mode);
|
const defaults = rstore.getLibsDefaults(this.mode);
|
||||||
this.commit('reader/setLibs', defaults);
|
this.commit('reader/setLibs', defaults);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -393,6 +393,9 @@ class Reader {
|
|||||||
this.recentItemKeys = [];
|
this.recentItemKeys = [];
|
||||||
//сохранение в удаленном хранилище
|
//сохранение в удаленном хранилище
|
||||||
await this.$refs.serverStorage.saveRecent(itemKeys);
|
await this.$refs.serverStorage.saveRecent(itemKeys);
|
||||||
|
|
||||||
|
//periodicTasks
|
||||||
|
this.periodicTasks();//no await
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!this.offlineModeActive)
|
if (!this.offlineModeActive)
|
||||||
this.$root.notify.error(e.message);
|
this.$root.notify.error(e.message);
|
||||||
@@ -442,26 +445,15 @@ class Reader {
|
|||||||
this.$refs.recentBooksPage.init();
|
this.$refs.recentBooksPage.init();
|
||||||
})();
|
})();
|
||||||
|
|
||||||
//проверки обновлений читалки
|
//единственный запуск periodicTasks при инициализации
|
||||||
|
//дальнейшие запуски periodicTasks выполняются из debouncedSaveRecent
|
||||||
|
//т.е. только по действию пользователя
|
||||||
(async() => {
|
(async() => {
|
||||||
|
await utils.sleep(15*1000);
|
||||||
this.isFirstNeedUpdateNotify = true;
|
this.isFirstNeedUpdateNotify = true;
|
||||||
//вечный цикл, запрашиваем периодически конфиг для проверки выхода новой версии читалки
|
|
||||||
while (1) {// eslint-disable-line no-constant-condition
|
|
||||||
await this.checkNewVersionAvailable();
|
|
||||||
await utils.sleep(60*60*1000); //каждый час
|
|
||||||
}
|
|
||||||
//дальше хода нет
|
|
||||||
})();
|
|
||||||
|
|
||||||
//проверки обновлений книг
|
this.allowPeriodicTasks = true;
|
||||||
(async() => {
|
this.periodicTasks();//no await
|
||||||
await utils.sleep(15*1000); //подождем неск. секунд перед первым запросом
|
|
||||||
//вечный цикл, запрашиваем периодически обновления
|
|
||||||
while (1) {// eslint-disable-line no-constant-condition
|
|
||||||
await this.checkBuc();
|
|
||||||
await utils.sleep(70*60*1000); //каждые 70 минут
|
|
||||||
}
|
|
||||||
//дальше хода нет
|
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -560,26 +552,56 @@ class Reader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkNewVersionAvailable() {
|
async periodicTasks() {
|
||||||
if (!this.checkingNewVersion && this.showNeedUpdateNotify) {
|
if (!this.allowPeriodicTasks || this.doingPeriodicTasks)
|
||||||
this.checkingNewVersion = true;
|
return;
|
||||||
try {
|
|
||||||
await utils.sleep(15*1000); //подождем 15 секунд, чтобы прогрузился ServiceWorker при выходе новой версии
|
|
||||||
const config = await miscApi.loadConfig();
|
|
||||||
this.commit('config/setConfig', config);
|
|
||||||
|
|
||||||
let againMes = '';
|
this.doingPeriodicTasks = true;
|
||||||
if (this.isFirstNeedUpdateNotify) {
|
try {
|
||||||
againMes = ' еще один раз';
|
if (!this.taskList) {
|
||||||
|
const taskArr = [
|
||||||
|
[this.checkNewVersionAvailable, 60], //проверки обновлений читалки, каждый час
|
||||||
|
[this.checkBuc, 70], //проверки обновлений книг, каждые 70 минут
|
||||||
|
];
|
||||||
|
|
||||||
|
this.taskList = [];
|
||||||
|
for (const task of taskArr) {
|
||||||
|
const [method, period] = task;
|
||||||
|
this.taskList.push({method, period, lastRunTime: 0});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const task of this.taskList) {
|
||||||
|
if (Date.now() - task.lastRunTime >= task.period*60*1000) {
|
||||||
|
try {
|
||||||
|
//console.log('task run', task.method.name);
|
||||||
|
await task.method();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
task.lastRunTime = Date.now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
} finally {
|
||||||
|
this.doingPeriodicTasks = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkNewVersionAvailable() {
|
||||||
|
if (this.showNeedUpdateNotify) {
|
||||||
|
const config = await miscApi.loadConfig();
|
||||||
|
this.commit('config/setConfig', config);
|
||||||
|
|
||||||
|
let againMes = '';
|
||||||
|
if (this.isFirstNeedUpdateNotify) {
|
||||||
|
againMes = ' еще один раз';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.version != this.clientVersion)
|
||||||
|
this.$root.notify.info(`Вышла новая версия (v${this.version}) читалки.<br>Пожалуйста, обновите страницу${againMes}.`, 'Обновление');
|
||||||
|
|
||||||
if (this.version != this.clientVersion)
|
|
||||||
this.$root.notify.info(`Вышла новая версия (v${this.version}) читалки.<br>Пожалуйста, обновите страницу${againMes}.`, 'Обновление');
|
|
||||||
} catch(e) {
|
|
||||||
console.error(e);
|
|
||||||
} finally {
|
|
||||||
this.checkingNewVersion = false;
|
|
||||||
}
|
|
||||||
this.isFirstNeedUpdateNotify = false;
|
this.isFirstNeedUpdateNotify = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -588,82 +610,78 @@ class Reader {
|
|||||||
if (!this.bothBucEnabled)
|
if (!this.bothBucEnabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try {
|
const sorted = bookManager.getSortedRecent();
|
||||||
const sorted = bookManager.getSortedRecent();
|
|
||||||
|
|
||||||
//выберем все кандидиаты на обновление
|
//выберем все кандидиаты на обновление
|
||||||
const updateUrls = new Set();
|
const updateUrls = new Set();
|
||||||
for (const book of sorted) {
|
for (const book of sorted) {
|
||||||
if (!book.deleted && book.checkBuc && book.url && book.url.indexOf('disk://') !== 0)
|
if (!book.deleted && book.checkBuc && book.url && book.url.indexOf('disk://') !== 0)
|
||||||
updateUrls.add(book.url);
|
updateUrls.add(book.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
//теперь по кусочкам запросим сервер
|
||||||
|
const arr = Array.from(updateUrls);
|
||||||
|
const bucSize = {};
|
||||||
|
const chunkSize = 100;
|
||||||
|
for (let i = 0; i < arr.length; i += chunkSize) {
|
||||||
|
const chunk = arr.slice(i, i + chunkSize);
|
||||||
|
|
||||||
|
const data = await readerApi.checkBuc(chunk);
|
||||||
|
|
||||||
|
for (const item of data) {
|
||||||
|
bucSize[item.id] = item.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
//теперь по кусочкам запросим сервер
|
await utils.sleep(1000);//чтобы не ддосить сервер
|
||||||
const arr = Array.from(updateUrls);
|
}
|
||||||
const bucSize = {};
|
|
||||||
const chunkSize = 100;
|
|
||||||
for (let i = 0; i < arr.length; i += chunkSize) {
|
|
||||||
const chunk = arr.slice(i, i + chunkSize);
|
|
||||||
|
|
||||||
const data = await readerApi.checkBuc(chunk);
|
const checkSetTime = {};
|
||||||
|
//проставим новые размеры у книг
|
||||||
for (const item of data) {
|
for (const book of sorted) {
|
||||||
bucSize[item.id] = item.size;
|
if (book.deleted)
|
||||||
}
|
continue;
|
||||||
|
|
||||||
await utils.sleep(1000);//чтобы не ддосить сервер
|
//размер 0 считаем отсутствующим
|
||||||
|
if (book.url && bucSize[book.url] && bucSize[book.url] !== book.bucSize) {
|
||||||
|
book.bucSize = bucSize[book.url];
|
||||||
|
await bookManager.recentSetItem(book);
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkSetTime = {};
|
//подготовка к следующему шагу, ищем книгу по url с максимальной датой установки checkBucTime/loadTime
|
||||||
//проставим новые размеры у книг
|
//от этой даты будем потом отсчитывать bucCancelDays
|
||||||
for (const book of sorted) {
|
if (updateUrls.has(book.url)) {
|
||||||
if (book.deleted)
|
let rec = checkSetTime[book.url] || {time: 0, loadTime: 0};
|
||||||
continue;
|
|
||||||
|
|
||||||
//размер 0 считаем отсутствующим
|
|
||||||
if (book.url && bucSize[book.url] && bucSize[book.url] !== book.bucSize) {
|
|
||||||
book.bucSize = bucSize[book.url];
|
|
||||||
await bookManager.recentSetItem(book);
|
|
||||||
}
|
|
||||||
|
|
||||||
//подготовка к следующему шагу, ищем книгу по url с максимальной датой установки checkBucTime/loadTime
|
const time = (book.checkBucTime ? book.checkBucTime : (rec.loadTime || 0));
|
||||||
//от этой даты будем потом отсчитывать bucCancelDays
|
if (time > rec.time || (time == rec.time && (book.loadTime > rec.loadTime)))
|
||||||
if (updateUrls.has(book.url)) {
|
rec = {time, loadTime: book.loadTime, key: book.key};
|
||||||
let rec = checkSetTime[book.url] || {time: 0, loadTime: 0};
|
|
||||||
|
|
||||||
const time = (book.checkBucTime ? book.checkBucTime : (rec.loadTime || 0));
|
checkSetTime[book.url] = rec;
|
||||||
if (time > rec.time || (time == rec.time && (book.loadTime > rec.loadTime)))
|
|
||||||
rec = {time, loadTime: book.loadTime, key: book.key};
|
|
||||||
|
|
||||||
checkSetTime[book.url] = rec;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//bucCancelEnabled и bucCancelDays
|
//bucCancelEnabled и bucCancelDays
|
||||||
//снимем флаг checkBuc у необновлявшихся bucCancelDays
|
//снимем флаг checkBuc у необновлявшихся bucCancelDays
|
||||||
if (this.bucCancelEnabled) {
|
if (this.bucCancelEnabled) {
|
||||||
for (const rec of Object.values(checkSetTime)) {
|
for (const rec of Object.values(checkSetTime)) {
|
||||||
if (rec.time && Date.now() - rec.time > this.bucCancelDays*24*3600*1000) {
|
if (rec.time && Date.now() - rec.time > this.bucCancelDays*24*3600*1000) {
|
||||||
const book = await bookManager.getRecentBook({key: rec.key});
|
const book = await bookManager.getRecentBook({key: rec.key});
|
||||||
const needBookUpdate =
|
const needBookUpdate =
|
||||||
book.checkBuc
|
book.checkBuc
|
||||||
&& book.bucSize
|
&& book.bucSize
|
||||||
&& utils.hasProp(book, 'downloadSize')
|
&& utils.hasProp(book, 'downloadSize')
|
||||||
&& book.bucSize !== book.downloadSize
|
&& book.bucSize !== book.downloadSize
|
||||||
&& (book.bucSize - book.downloadSize >= this.bucSizeDiff)
|
&& (book.bucSize - book.downloadSize >= this.bucSizeDiff)
|
||||||
;
|
;
|
||||||
|
|
||||||
if (book && !needBookUpdate) {
|
if (book && !needBookUpdate) {
|
||||||
await bookManager.setCheckBuc(book, undefined);//!!!
|
await bookManager.setCheckBuc(book, undefined);//!!!
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.$refs.recentBooksPage.updateTableData();
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.$refs.recentBooksPage.updateTableData();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCountChanged(event) {
|
updateCountChanged(event) {
|
||||||
@@ -1014,6 +1032,11 @@ class Reader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
libsToogle() {
|
libsToogle() {
|
||||||
|
if (this.config.networkLibraryLink) {
|
||||||
|
window.open(this.config.networkLibraryLink, '_blank');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.libsActive = !this.libsActive;
|
this.libsActive = !this.libsActive;
|
||||||
if (this.libsActive) {
|
if (this.libsActive) {
|
||||||
this.$refs.libsPage.init();//no await
|
this.$refs.libsPage.init();//no await
|
||||||
@@ -1361,6 +1384,7 @@ class Reader {
|
|||||||
found = (found ? _.cloneDeep(found) : found);
|
found = (found ? _.cloneDeep(found) : found);
|
||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
|
//если такой файл уже не загружен (path не совпадают)
|
||||||
if (wasOpened.sameBookKey != found.sameBookKey) {
|
if (wasOpened.sameBookKey != found.sameBookKey) {
|
||||||
//спрашиваем, надо ли объединить файлы
|
//спрашиваем, надо ли объединить файлы
|
||||||
const askResult = bookManager.keysEqual(found.path, addedBook.path) ||
|
const askResult = bookManager.keysEqual(found.path, addedBook.path) ||
|
||||||
@@ -1409,8 +1433,6 @@ class Reader {
|
|||||||
if (!this.showHelpOnErrorIfNeeded(url)) {
|
if (!this.showHelpOnErrorIfNeeded(url)) {
|
||||||
this.$root.stdDialog.alert(e.message, 'Ошибка', {color: 'negative'});
|
this.$root.stdDialog.alert(e.message, 'Ошибка', {color: 'negative'});
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
this.checkNewVersionAvailable();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -201,7 +201,7 @@
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
class="del-button self-end row justify-center items-center clickable"
|
class="del-button self-end row justify-center items-center clickable"
|
||||||
@click="handleDel(item.key)"
|
@click="handleDel(item)"
|
||||||
>
|
>
|
||||||
<q-icon class="la la-times" size="12px" />
|
<q-icon class="la la-times" size="12px" />
|
||||||
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">
|
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">
|
||||||
@@ -212,7 +212,7 @@
|
|||||||
<div
|
<div
|
||||||
v-show="showArchive"
|
v-show="showArchive"
|
||||||
class="restore-button self-start row justify-center items-center clickable"
|
class="restore-button self-start row justify-center items-center clickable"
|
||||||
@click="handleRestore(item.key)"
|
@click="handleRestore(item)"
|
||||||
>
|
>
|
||||||
<q-icon class="la la-arrow-left" size="14px" />
|
<q-icon class="la la-arrow-left" size="14px" />
|
||||||
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">
|
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">
|
||||||
@@ -593,21 +593,46 @@ class RecentBooksPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleDel(key) {
|
async handleDel(item) {
|
||||||
if (!this.showArchive) {
|
if (item.group) {
|
||||||
await bookManager.delRecentBook({key});
|
const keys = [{key: item.key}];
|
||||||
this.$root.notify.info('Перенесено в архив');
|
for (const book of item.group)
|
||||||
|
keys.push({key: book.key});
|
||||||
|
|
||||||
|
if (!this.showArchive) {
|
||||||
|
await bookManager.delRecentBooks(keys);
|
||||||
|
this.$root.notify.info(`Группа книг (всего ${keys.length}) перенесена в архив`);
|
||||||
|
} else {
|
||||||
|
if (await this.$root.stdDialog.confirm(`Подтвердите удаление группы книг (всего ${keys.length}) из архива:`, ' ')) {
|
||||||
|
await bookManager.delRecentBooks(keys, 2);
|
||||||
|
this.$root.notify.info('Группа книг удалена безвозвратно');
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (await this.$root.stdDialog.confirm('Подтвердите удаление из архива:', ' ')) {
|
if (!this.showArchive) {
|
||||||
await bookManager.delRecentBook({key}, 2);
|
await bookManager.delRecentBooks([{key: item.key}]);
|
||||||
this.$root.notify.info('Удалено безвозвратно');
|
this.$root.notify.info('Книга перенесена в архив');
|
||||||
|
} else {
|
||||||
|
if (await this.$root.stdDialog.confirm('Подтвердите удаление книги из архива:', ' ')) {
|
||||||
|
await bookManager.delRecentBooks([{key: item.key}], 2);
|
||||||
|
this.$root.notify.info('Книга удалено безвозвратно');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleRestore(key) {
|
async handleRestore(item) {
|
||||||
await bookManager.restoreRecentBook({key});
|
if (item.group) {
|
||||||
this.$root.notify.info('Восстановлено из архива');
|
const keys = [{key: item.key}];
|
||||||
|
for (const book of item.group)
|
||||||
|
keys.push({key: book.key});
|
||||||
|
|
||||||
|
await bookManager.restoreRecentBooks(keys);
|
||||||
|
this.$root.notify.info(`Группа книг (всего ${keys.length}) восстановлена из архива`);
|
||||||
|
} else {
|
||||||
|
await bookManager.restoreRecentBooks([{key: item.key}]);
|
||||||
|
this.$root.notify.info('Книга восстановлена из архива');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadBook(item, force = false) {
|
async loadBook(item, force = false) {
|
||||||
|
|||||||
@@ -22,10 +22,12 @@ const ssCacheStore = localForage.createInstance({
|
|||||||
const componentOptions = {
|
const componentOptions = {
|
||||||
watch: {
|
watch: {
|
||||||
serverSyncEnabled: function() {
|
serverSyncEnabled: function() {
|
||||||
this.serverSyncEnabledChanged();
|
if (this.inited)
|
||||||
|
this.serverSyncEnabledChanged();
|
||||||
},
|
},
|
||||||
serverStorageKey: function() {
|
serverStorageKey: function() {
|
||||||
this.serverStorageKeyChanged(true);
|
if (this.inited)
|
||||||
|
this.serverStorageKeyChanged(true);
|
||||||
},
|
},
|
||||||
settings: function() {
|
settings: function() {
|
||||||
this.debouncedSaveSettings();
|
this.debouncedSaveSettings();
|
||||||
@@ -85,6 +87,13 @@ class ServerStorage {
|
|||||||
if (!this.cachedRecentMod)
|
if (!this.cachedRecentMod)
|
||||||
await this.cleanCachedRecent('cachedRecentMod');
|
await this.cleanCachedRecent('cachedRecentMod');
|
||||||
|
|
||||||
|
//подстраховка хранения ключа, восстановим из IndexedDB при проблемах в localStorage
|
||||||
|
if (!this.serverStorageKey) {
|
||||||
|
const key = await ssCacheStore.getItem('storageKey');
|
||||||
|
if (key)
|
||||||
|
this.commit('reader/setServerStorageKey', key);
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.serverStorageKey) {
|
if (!this.serverStorageKey) {
|
||||||
//генерируем новый ключ
|
//генерируем новый ключ
|
||||||
await this.generateNewServerStorageKey();
|
await this.generateNewServerStorageKey();
|
||||||
@@ -123,6 +132,7 @@ class ServerStorage {
|
|||||||
async generateNewServerStorageKey() {
|
async generateNewServerStorageKey() {
|
||||||
const key = utils.toBase58(utils.randomArray(32));
|
const key = utils.toBase58(utils.randomArray(32));
|
||||||
this.commit('reader/setServerStorageKey', key);
|
this.commit('reader/setServerStorageKey', key);
|
||||||
|
//дождемся serverStorageKeyChanged, событие по watch не работает при this.inited == false
|
||||||
await this.serverStorageKeyChanged(true);
|
await this.serverStorageKeyChanged(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,6 +151,10 @@ class ServerStorage {
|
|||||||
async serverStorageKeyChanged(force) {
|
async serverStorageKeyChanged(force) {
|
||||||
if (this.prevServerStorageKey != this.serverStorageKey) {
|
if (this.prevServerStorageKey != this.serverStorageKey) {
|
||||||
this.prevServerStorageKey = this.serverStorageKey;
|
this.prevServerStorageKey = this.serverStorageKey;
|
||||||
|
|
||||||
|
//сохраним ключ также в IndexedDB, чтобы была возможность восстановить при проблемах с localStorage
|
||||||
|
await ssCacheStore.setItem('storageKey', this.serverStorageKey);
|
||||||
|
|
||||||
this.hashedStorageKey = utils.toBase58(cryptoUtils.sha256(this.serverStorageKey));
|
this.hashedStorageKey = utils.toBase58(cryptoUtils.sha256(this.serverStorageKey));
|
||||||
this.keyInited = true;
|
this.keyInited = true;
|
||||||
|
|
||||||
|
|||||||
@@ -438,61 +438,63 @@ export default class BookParser {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onEndNode = (elemName) => {// eslint-disable-line no-unused-vars
|
const onEndNode = (elemName) => {// eslint-disable-line no-unused-vars
|
||||||
if (tag == elemName) {
|
tag = elemName;
|
||||||
if (tag == 'binary') {
|
|
||||||
binaryId = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.indexOf('/fictionbook/body') == 0) {
|
|
||||||
if (tag == 'title') {
|
|
||||||
isFirstTitlePara = false;
|
|
||||||
bold = false;
|
|
||||||
center = false;
|
|
||||||
inTitle = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tag == 'section') {
|
if (tag == 'binary') {
|
||||||
sectionLevel--;
|
binaryId = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tag == 'emphasis' || tag == 'strong' || tag == 'sup' || tag == 'sub') {
|
if (path.indexOf('/fictionbook/body') == 0) {
|
||||||
growParagraph(`</${tag}>`, 0);
|
if (tag == 'title') {
|
||||||
}
|
isFirstTitlePara = false;
|
||||||
|
bold = false;
|
||||||
if (tag == 'p') {
|
center = false;
|
||||||
inPara = false;
|
inTitle = false;
|
||||||
}
|
|
||||||
|
|
||||||
if (tag == 'subtitle') {
|
|
||||||
isFirstTitlePara = false;
|
|
||||||
bold = false;
|
|
||||||
center = false;
|
|
||||||
inSubtitle = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tag == 'epigraph' || tag == 'annotation') {
|
|
||||||
italic = false;
|
|
||||||
space -= 1;
|
|
||||||
newParagraph();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tag == 'stanza') {
|
|
||||||
newParagraph();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tag == 'text-author') {
|
|
||||||
bold = false;
|
|
||||||
space -= 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
path = path.substr(0, path.length - tag.length - 1);
|
if (tag == 'section') {
|
||||||
let i = path.lastIndexOf('/');
|
sectionLevel--;
|
||||||
if (i >= 0) {
|
}
|
||||||
tag = path.substr(i + 1);
|
|
||||||
} else {
|
if (tag == 'emphasis' || tag == 'strong' || tag == 'sup' || tag == 'sub') {
|
||||||
|
growParagraph(`</${tag}>`, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag == 'p') {
|
||||||
|
inPara = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag == 'subtitle') {
|
||||||
|
isFirstTitlePara = false;
|
||||||
|
bold = false;
|
||||||
|
center = false;
|
||||||
|
inSubtitle = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag == 'epigraph' || tag == 'annotation') {
|
||||||
|
italic = false;
|
||||||
|
space -= 1;
|
||||||
|
newParagraph();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag == 'stanza') {
|
||||||
|
newParagraph();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag == 'text-author') {
|
||||||
|
bold = false;
|
||||||
|
space -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let i = path.lastIndexOf(tag);
|
||||||
|
if (i >= 0) {
|
||||||
|
path = path.substring(0, i - 1);
|
||||||
|
i = path.lastIndexOf('/');
|
||||||
|
if (i >= 0)
|
||||||
|
tag = path.substring(i + 1);
|
||||||
|
else
|
||||||
tag = path;
|
tag = path;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -467,7 +467,7 @@ class BookManager {
|
|||||||
async getRecentBook(value) {
|
async getRecentBook(value) {
|
||||||
return this.recent[value.key];
|
return this.recent[value.key];
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
async delRecentBook(value, delFlag = 1) {
|
async delRecentBook(value, delFlag = 1) {
|
||||||
const item = this.recent[value.key];
|
const item = this.recent[value.key];
|
||||||
item.deleted = delFlag;
|
item.deleted = delFlag;
|
||||||
@@ -479,13 +479,37 @@ class BookManager {
|
|||||||
await this.recentSetItem(item);
|
await this.recentSetItem(item);
|
||||||
this.emit('recent-deleted', value.key);
|
this.emit('recent-deleted', value.key);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
async delRecentBooks(values, delFlag = 1) {
|
||||||
|
for (const value of values) {
|
||||||
|
const item = this.recent[value.key];
|
||||||
|
item.deleted = delFlag;
|
||||||
|
|
||||||
|
if (this.recentLastKey == value.key) {
|
||||||
|
await this.recentSetLastKey(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.recentSetItem(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emit('recent-deleted');
|
||||||
|
}
|
||||||
|
/*
|
||||||
async restoreRecentBook(value) {
|
async restoreRecentBook(value) {
|
||||||
const item = this.recent[value.key];
|
const item = this.recent[value.key];
|
||||||
item.deleted = 0;
|
item.deleted = 0;
|
||||||
|
|
||||||
await this.recentSetItem(item);
|
await this.recentSetItem(item);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
async restoreRecentBooks(values) {
|
||||||
|
for (const value of values) {
|
||||||
|
const item = this.recent[value.key];
|
||||||
|
item.deleted = 0;
|
||||||
|
|
||||||
|
await this.recentSetItem(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async setCheckBuc(value, checkBuc) {
|
async setCheckBuc(value, checkBuc) {
|
||||||
const item = this.recent[value.key];
|
const item = this.recent[value.key];
|
||||||
|
|||||||
@@ -1,4 +1,31 @@
|
|||||||
export const versionHistory = [
|
export const versionHistory = [
|
||||||
|
{
|
||||||
|
version: '1.2.0',
|
||||||
|
releaseDate: '2024-03-25',
|
||||||
|
showUntil: '2024-03-24',
|
||||||
|
content:
|
||||||
|
`
|
||||||
|
<ul>
|
||||||
|
<li>в списке загруженных, книга в архив (из архива) переносится теперь со всей группой своих версий</li>
|
||||||
|
<li>добавлена возможность задавать в конфиге любую ссылку для кнопки "Сетевая библиотека", параматр networkLibraryLink (#47)</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
`
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
version: '1.1.3',
|
||||||
|
releaseDate: '2023-02-06',
|
||||||
|
showUntil: '2023-02-05',
|
||||||
|
content:
|
||||||
|
`
|
||||||
|
<ul>
|
||||||
|
<li>исправление багов</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
`
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
version: '1.1.2',
|
version: '1.1.2',
|
||||||
releaseDate: '2023-01-22',
|
releaseDate: '2023-01-22',
|
||||||
|
|||||||
@@ -325,7 +325,7 @@ const state = {
|
|||||||
currentProfile: '',
|
currentProfile: '',
|
||||||
settings: _.cloneDeep(settingDefaults),
|
settings: _.cloneDeep(settingDefaults),
|
||||||
settingsRev: {},
|
settingsRev: {},
|
||||||
libs: false,
|
libs: {},
|
||||||
libsRev: 0,
|
libsRev: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
8639
package-lock.json
generated
8639
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
66
package.json
66
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "liberama",
|
"name": "liberama",
|
||||||
"version": "1.1.2",
|
"version": "1.2.0",
|
||||||
"author": "Book Pauk <bookpauk@gmail.com>",
|
"author": "Book Pauk <bookpauk@gmail.com>",
|
||||||
"license": "CC0-1.0",
|
"license": "CC0-1.0",
|
||||||
"repository": "bookpauk/liberama",
|
"repository": "bookpauk/liberama",
|
||||||
@@ -25,41 +25,41 @@
|
|||||||
"scripts": "server/config/*.js"
|
"scripts": "server/config/*.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.20.5",
|
"@babel/core": "^7.23.5",
|
||||||
"@babel/eslint-parser": "^7.19.1",
|
"@babel/eslint-parser": "^7.23.3",
|
||||||
"@babel/eslint-plugin": "^7.19.1",
|
"@babel/eslint-plugin": "^7.23.5",
|
||||||
"@babel/plugin-proposal-decorators": "^7.20.5",
|
"@babel/plugin-proposal-decorators": "^7.23.5",
|
||||||
"@babel/preset-env": "^7.20.2",
|
"@babel/preset-env": "^7.23.5",
|
||||||
"@vue/compiler-sfc": "^3.2.22",
|
"@vue/compiler-sfc": "^3.2.22",
|
||||||
"babel-loader": "^9.1.0",
|
"babel-loader": "^9.1.3",
|
||||||
"copy-webpack-plugin": "^11.0.0",
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
"css-loader": "^6.7.3",
|
"css-loader": "^6.8.1",
|
||||||
"css-minimizer-webpack-plugin": "^4.2.2",
|
"css-minimizer-webpack-plugin": "^4.2.2",
|
||||||
"eslint": "^8.29.0",
|
"eslint": "^8.55.0",
|
||||||
"eslint-plugin-vue": "^9.8.0",
|
"eslint-plugin-vue": "^9.19.2",
|
||||||
"html-webpack-plugin": "^5.5.0",
|
"html-webpack-plugin": "^5.5.4",
|
||||||
"mini-css-extract-plugin": "^2.7.2",
|
"mini-css-extract-plugin": "^2.7.6",
|
||||||
"pkg": "^5.8.0",
|
"pkg": "^5.8.1",
|
||||||
"showdown": "^2.1.0",
|
"showdown": "^2.1.0",
|
||||||
"terser-webpack-plugin": "^5.3.6",
|
"terser-webpack-plugin": "^5.3.9",
|
||||||
"vue-eslint-parser": "^9.1.0",
|
"vue-eslint-parser": "^9.3.2",
|
||||||
"vue-loader": "^17.0.1",
|
"vue-loader": "^17.3.1",
|
||||||
"vue-style-loader": "^4.1.3",
|
"vue-style-loader": "^4.1.3",
|
||||||
"webpack": "^5.75.0",
|
"webpack": "^5.89.0",
|
||||||
"webpack-cli": "^5.0.1",
|
"webpack-cli": "^5.1.4",
|
||||||
"webpack-dev-middleware": "^6.0.1",
|
"webpack-dev-middleware": "^6.1.1",
|
||||||
"webpack-hot-middleware": "^2.25.3",
|
"webpack-hot-middleware": "^2.25.4",
|
||||||
"webpack-merge": "^5.8.0",
|
"webpack-merge": "^5.10.0",
|
||||||
"workbox-webpack-plugin": "^6.5.4"
|
"workbox-webpack-plugin": "^6.6.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@quasar/extras": "^1.15.8",
|
"@quasar/extras": "^1.16.9",
|
||||||
"@vue/compat": "^3.2.45",
|
"@vue/compat": "^3.3.10",
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
"base-x": "^4.0.0",
|
"base-x": "^4.0.0",
|
||||||
"chardet": "^1.5.0",
|
"chardet": "^1.6.0",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.10",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"fg-loadcss": "^3.1.0",
|
"fg-loadcss": "^3.1.0",
|
||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
@@ -68,23 +68,23 @@
|
|||||||
"jembadb": "^5.1.7",
|
"jembadb": "^5.1.7",
|
||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"minimist": "^1.2.7",
|
"minimist": "^1.2.8",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"pako": "^2.1.0",
|
"pako": "^2.1.0",
|
||||||
"path-browserify": "^1.0.1",
|
"path-browserify": "^1.0.1",
|
||||||
"pidusage": "^3.0.2",
|
"pidusage": "^3.0.2",
|
||||||
"quasar": "^2.10.2",
|
"quasar": "^2.14.1",
|
||||||
"safe-buffer": "^5.2.1",
|
"safe-buffer": "^5.2.1",
|
||||||
"sanitize-html": "^2.8.0",
|
"sanitize-html": "^2.11.0",
|
||||||
"sjcl": "^1.0.8",
|
"sjcl": "^1.0.8",
|
||||||
"tar-fs": "^2.1.1",
|
"tar-fs": "^2.1.1",
|
||||||
"unbzip2-stream": "^1.4.3",
|
"unbzip2-stream": "^1.4.3",
|
||||||
"vue": "^3.2.37",
|
"vue": "^3.2.37",
|
||||||
"vue-router": "^4.1.6",
|
"vue-router": "^4.2.5",
|
||||||
"vuex": "^4.1.0",
|
"vuex": "^4.1.0",
|
||||||
"vuex-persist": "^3.1.3",
|
"vuex-persist": "^3.1.3",
|
||||||
"webdav": "^4.11.2",
|
"webdav": "^4.11.3",
|
||||||
"ws": "^8.11.0",
|
"ws": "^8.14.2",
|
||||||
"zip-stream": "^4.1.0"
|
"zip-stream": "^4.1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ module.exports = {
|
|||||||
|
|
||||||
useExternalBookConverter: false,
|
useExternalBookConverter: false,
|
||||||
acceptFileExt: '.fb2, .fb3, .html, .txt, .zip, .bz2, .gz, .rar, .epub, .mobi, .rtf, .doc, .docx, .pdf, .djvu, .jpg, .jpeg, .png',
|
acceptFileExt: '.fb2, .fb3, .html, .txt, .zip, .bz2, .gz, .rar, .epub, .mobi, .rtf, .doc, .docx, .pdf, .djvu, .jpg, .jpeg, .png',
|
||||||
webConfigParams: ['name', 'version', 'mode', 'maxUploadFileSize', 'useExternalBookConverter', 'acceptFileExt', 'bucEnabled', 'branch'],
|
webConfigParams: ['name', 'version', 'mode', 'maxUploadFileSize', 'useExternalBookConverter', 'acceptFileExt', 'bucEnabled', 'branch', 'networkLibraryLink'],
|
||||||
|
|
||||||
jembaDb: [
|
jembaDb: [
|
||||||
{
|
{
|
||||||
@@ -74,5 +74,6 @@ module.exports = {
|
|||||||
accessToken: '',
|
accessToken: '',
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
networkLibraryLink: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ const propsToSave = [
|
|||||||
'remoteStorage',
|
'remoteStorage',
|
||||||
'bucEnabled',
|
'bucEnabled',
|
||||||
'bucServer',
|
'bucServer',
|
||||||
|
'networkLibraryLink',
|
||||||
];
|
];
|
||||||
|
|
||||||
let instance = null;
|
let instance = null;
|
||||||
|
|||||||
Reference in New Issue
Block a user