Compare commits

...

25 Commits

Author SHA1 Message Date
Book Pauk
956546585c Merge branch 'release/0.9.5-1' 2020-11-03 00:29:12 +07:00
Book Pauk
3ca0a92442 Временно отключил кнопку "Настроить закладки" 2020-11-03 00:28:15 +07:00
Book Pauk
213f7e48c9 Работа над BookmarkSettings 2020-11-03 00:26:25 +07:00
Book Pauk
8b66fd522d Добавлено восстановление дефолтных настроек в объекте settings при их отсутствии 2020-11-03 00:00:31 +07:00
Book Pauk
fdf5009999 Мелкая поправка 2020-11-02 23:51:41 +07:00
Book Pauk
bbdba0ef16 Поправки по результату тестирования 2020-11-02 23:45:59 +07:00
Book Pauk
a63602df7a Поправки багов 2020-11-02 14:33:15 +07:00
Book Pauk
587120f984 Merge tag '0.9.5' into develop
0.9.5
2020-11-01 19:55:53 +07:00
Book Pauk
e72ca0de7e Merge branch 'release/0.9.5' 2020-11-01 19:55:45 +07:00
Book Pauk
c44c27d3d2 Версия 0.9.5 2020-11-01 19:36:13 +07:00
Book Pauk
df4e201ccd Небольшие поправки 2020-11-01 19:33:02 +07:00
Book Pauk
c8c0e9ec1a Добавлена кнопка 'Обновить с разбиением на параграфы' 2020-11-01 16:42:20 +07:00
Book Pauk
9a4a84a367 Сертификаты для beta.liberama 2020-11-01 15:00:23 +07:00
Book Pauk
1dc3424411 Убрал лишнее 2020-11-01 14:53:51 +07:00
Book Pauk
c13745e913 Добавлен конфиг nginx для beta.liberama 2020-11-01 14:50:18 +07:00
Book Pauk
25c12309f2 Поправки поведения диалога на "Enter" 2020-11-01 14:41:40 +07:00
Book Pauk
4b632da5af Поправки поведения Select 2020-11-01 14:17:35 +07:00
Book Pauk
87c364b8ee Исправление поведения компонента select 2020-11-01 13:38:05 +07:00
Book Pauk
efa48fbc8a Поправки margin окна 2020-11-01 11:42:54 +07:00
Book Pauk
21df6c1d21 Поправка goToLink 2020-11-01 11:34:51 +07:00
Book Pauk
39d2ceb94b Мелкая поправка для fullscreen 2020-11-01 11:26:58 +07:00
Book Pauk
1dad013d60 Исправлен баг синхронизации при первом включении опции 2020-11-01 11:23:15 +07:00
Book Pauk
add7a03f88 Убираем ссылку на гитхаб для liberama.top 2020-10-31 21:20:30 +07:00
Book Pauk
0cefaa6d48 Мелкая поправка 2020-10-30 19:16:06 +07:00
Book Pauk
f08e73f359 Merge tag '0.9.4' into develop
Версия 0.9.4
2020-10-30 19:13:17 +07:00
20 changed files with 459 additions and 69 deletions

View File

@@ -0,0 +1,59 @@
<template>
<Window ref="window" width="600px" height="95%" @close="close">
<template slot="header">
Настроить закладки
</template>
</Window>
</template>
<script>
//-----------------------------------------------------------------------------
import Vue from 'vue';
import Component from 'vue-class-component';
import Window from '../../share/Window.vue';
const BookmarkSettingsProps = Vue.extend({
props: {
libs: Object,
}
});
export default @Component({
components: {
Window,
},
watch: {
libs: function() {
},
}
})
class BookmarkSettings extends BookmarkSettingsProps {
created() {
}
mounted() {
}
init() {
this.$refs.window.init();
}
close() {
this.$emit('close');
}
keyHook(event) {
if (event.type == 'keydown' && event.key == 'Escape') {
this.close();
return true;
}
return false;
}
}
//-----------------------------------------------------------------------------
</script>
<style scoped>
</style>

View File

@@ -1,5 +1,5 @@
<template> <template>
<Window ref="window" @close="close"> <Window ref="window" @close="close" margin="2px">
<template slot="header"> <template slot="header">
{{ header }} {{ header }}
</template> </template>
@@ -13,7 +13,8 @@
<div v-show="ready" class="col column" style="min-width: 600px"> <div v-show="ready" class="col column" style="min-width: 600px">
<div class="row items-center q-px-sm" style="height: 50px"> <div class="row items-center q-px-sm" style="height: 50px">
<q-select class="q-mr-sm" v-model="rootLink" :options="rootLinkOptions" <q-select class="q-mr-sm" ref="rootLink" v-model="rootLink" :options="rootLinkOptions" @input="rootLinkInput"
@popup-show="onSelectPopupShow" @popup-hide="onSelectPopupHide"
style="width: 230px" style="width: 230px"
dropdown-icon="la la-angle-down la-sm" dropdown-icon="la la-angle-down la-sm"
rounded outlined dense emit-value map-options display-value-sanitize options-sanitize rounded outlined dense emit-value map-options display-value-sanitize options-sanitize
@@ -22,21 +23,26 @@
<q-btn class="q-mr-xs" round dense color="blue" icon="la la-plus" @click.stop="addBookmark" size="12px"> <q-btn class="q-mr-xs" round dense color="blue" icon="la la-plus" @click.stop="addBookmark" size="12px">
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">Добавить закладку</q-tooltip> <q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">Добавить закладку</q-tooltip>
</q-btn> </q-btn>
<q-btn round dense color="blue" icon="la la-bars" @click.stop="bookmarkSettings" size="12px" disabled> <q-btn round dense color="blue" icon="la la-bars" @click.stop="bookmarkSettings" size="12px" disabled>
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">Настроить закладки (пока недоступно)</q-tooltip> <q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">Настроить закладки</q-tooltip>
</q-btn> </q-btn>
</template> </template>
<template v-slot:selected> <template v-slot:selected>
<div style="overflow: hidden; white-space: nowrap;">{{ removeProtocol(rootLink) }}</div> <div style="overflow: hidden; white-space: nowrap;">{{ removeProtocol(rootLink) }}</div>
</template> </template>
</q-select> </q-select>
<q-select class="q-mr-sm" v-model="selectedLink" :options="selectedLinkOptions" style="width: 50px"
<q-select class="q-mr-sm" ref="selectedLink" v-model="selectedLink" :options="selectedLinkOptions" @input="selectedLinkInput" style="width: 50px"
@popup-show="onSelectPopupShow" @popup-hide="onSelectPopupHide"
dropdown-icon="la la-angle-down la-sm" dropdown-icon="la la-angle-down la-sm"
rounded outlined dense emit-value map-options hide-selected display-value-sanitize options-sanitize rounded outlined dense emit-value map-options hide-selected display-value-sanitize options-sanitize
> >
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">Закладки</q-tooltip> <q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">Закладки</q-tooltip>
</q-select> </q-select>
<q-input class="col q-mr-sm" ref="input" rounded outlined dense bg-color="white" v-model="bookUrl" placeholder="Скопируйте сюда URL книги" @focus="onInputFocus">
<q-input class="col q-mr-sm" ref="input" rounded outlined dense bg-color="white" v-model="bookUrl" placeholder="Скопируйте сюда URL книги"
@focus="selectAllOnFocus" @keydown="bookUrlKeyDown"
>
<template v-slot:prepend> <template v-slot:prepend>
<q-btn class="q-mr-xs" round dense color="blue" icon="la la-home" @click="goToLink(libs.startLink)" size="12px"> <q-btn class="q-mr-xs" round dense color="blue" icon="la la-home" @click="goToLink(libs.startLink)" size="12px">
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">Вернуться на стартовую страницу</q-tooltip> <q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">Вернуться на стартовую страницу</q-tooltip>
@@ -46,13 +52,17 @@
</q-btn> </q-btn>
</template> </template>
</q-input> </q-input>
<q-btn rounded color="green-7" no-caps size="14px" @click="submitUrl">Открыть <q-btn rounded color="green-7" no-caps size="14px" @click="submitUrl">Открыть
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">Открыть в читалке</q-tooltip> <q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">Открыть в читалке</q-tooltip>
</q-btn> </q-btn>
</div> </div>
<div class="separator"></div> <div class="separator"></div>
<iframe v-if="frameVisible" class="col fit" ref="frame" :src="frameSrc" frameborder="0"></iframe> <div class="col fit" style="position: relative;">
<iframe v-if="frameVisible" class="fit" ref="frame" :src="frameSrc" frameborder="0"></iframe>
<div v-show="transparentLayoutVisible" ref="transparentLayout" class="fit transparent-layout" @click="transparentLayoutClick"></div>
</div>
<Dialog ref="dialogAddBookmark" v-model="addBookmarkVisible"> <Dialog ref="dialogAddBookmark" v-model="addBookmarkVisible">
<template slot="header"> <template slot="header">
@@ -63,11 +73,11 @@
</template> </template>
<div class="q-mx-md row"> <div class="q-mx-md row">
<q-input ref="bookmarkLink" class="col q-mr-sm" outlined dense bg-color="white" v-model="bookmarkLink" <q-input ref="bookmarkLink" class="col q-mr-sm" outlined dense bg-color="white" v-model="bookmarkLink" @keydown="bookmarkLinkKeyDown"
placeholder="Ссылка для закладки" maxlength="2000" @focus="onInputFocus"> placeholder="Ссылка для закладки" maxlength="2000" @focus="selectAllOnFocus">
</q-input> </q-input>
<q-select class="q-mr-sm" v-model="defaultRootLink" :options="defaultRootLinkOptions" style="width: 50px" <q-select class="q-mr-sm" ref="defaultRootLink" v-model="defaultRootLink" :options="defaultRootLinkOptions" @input="defaultRootLinkInput" style="width: 50px"
dropdown-icon="la la-angle-down la-sm" dropdown-icon="la la-angle-down la-sm"
outlined dense emit-value map-options hide-selected display-value-sanitize options-sanitize outlined dense emit-value map-options hide-selected display-value-sanitize options-sanitize
> >
@@ -76,8 +86,8 @@
</div> </div>
<div class="q-mx-md q-mt-md"> <div class="q-mx-md q-mt-md">
<q-input class="col q-mr-sm" outlined dense bg-color="white" v-model="bookmarkDesc" <q-input class="col q-mr-sm" ref="bookmarkDesc" outlined dense bg-color="white" v-model="bookmarkDesc" @keydown="bookmarkDescKeyDown"
placeholder="Описание" style="width: 400px" maxlength="100" @focus="onInputFocus"> placeholder="Описание" style="width: 400px" maxlength="100" @focus="selectAllOnFocus">
</q-input> </q-input>
</div> </div>
@@ -87,6 +97,7 @@
</template> </template>
</Dialog> </Dialog>
</div> </div>
<BookmarkSettings v-if="bookmarkSettingsActive" ref="bookmarkSettings" :libs="libs" @close="closeBookmarkSettings"></BookmarkSettings>
</Window> </Window>
</template> </template>
@@ -98,6 +109,8 @@ import _ from 'lodash';
import Window from '../share/Window.vue'; import Window from '../share/Window.vue';
import Dialog from '../share/Dialog.vue'; import Dialog from '../share/Dialog.vue';
import BookmarkSettings from './BookmarkSettings/BookmarkSettings.vue';
import rstore from '../../store/modules/reader'; import rstore from '../../store/modules/reader';
import * as utils from '../../share/utils'; import * as utils from '../../share/utils';
@@ -108,7 +121,8 @@ const proxySubst = {
export default @Component({ export default @Component({
components: { components: {
Window, Window,
Dialog Dialog,
BookmarkSettings
}, },
watch: { watch: {
libs: function() { libs: function() {
@@ -137,19 +151,49 @@ class ExternalLibs extends Vue {
libs = {}; libs = {};
fullScreenActive = false; fullScreenActive = false;
addBookmarkVisible = false; addBookmarkVisible = false;
transparentLayoutVisible = false;
bookmarkLink = ''; bookmarkLink = '';
bookmarkDesc = ''; bookmarkDesc = '';
defaultRootLink = ''; defaultRootLink = '';
bookmarkSettingsActive = false;
created() { created() {
this.$root.addKeyHook(this.keyHook); this.$root.addKeyHook(this.keyHook);
document.addEventListener('fullscreenchange', () => {
this.fullScreenActive = (document.fullscreenElement !== null);
});
//this.commit = this.$store.commit; //this.commit = this.$store.commit;
//this.commit('reader/setLibs', rstore.libsDefaults); //this.commit('reader/setLibs', rstore.libsDefaults);
} }
mounted() { mounted() {
//Поправка метода toggleOption компонента select фреймворка quasar, необходимо другое поведение
//$emit('input'.. вызывается всегда
this.toggleOption = function(opt, keepOpen) {
if (this.editable !== true || opt === void 0 || this.isOptionDisabled(opt) === true) {
return;
}
const optValue = this.getOptionValue(opt);
if (this.multiple !== true) {
if (keepOpen !== true) {
this.updateInputValue(this.fillInput === true ? this.getOptionLabel(opt) : '', true, true);
this.hidePopup();
}
this.$refs.target !== void 0 && this.$refs.target.focus();
this.$emit('input', this.emitValue === true ? optValue : opt);
}
};
this.$refs.rootLink.toggleOption = this.toggleOption;
this.$refs.selectedLink.toggleOption = this.toggleOption;
(async() => { (async() => {
//подождем this.mode //подождем this.mode
let i = 0; let i = 0;
@@ -320,8 +364,9 @@ class ExternalLibs extends Vue {
} }
openBookUrlInFrame() { openBookUrlInFrame() {
if (this.bookUrl) if (this.bookUrl) {
this.goToLink(this.addProtocol(this.bookUrl)); this.goToLink(this.addProtocol(this.bookUrl));
}
} }
goToLink(link) { goToLink(link) {
@@ -332,6 +377,9 @@ class ExternalLibs extends Vue {
this.frameVisible = false; this.frameVisible = false;
this.$nextTick(() => { this.$nextTick(() => {
this.frameVisible = true; this.frameVisible = true;
this.$nextTick(() => {
this.$refs.frame.contentWindow.focus();
});
}); });
} }
@@ -392,11 +440,20 @@ class ExternalLibs extends Vue {
return url; return url;
} }
onInputFocus(event) { selectAllOnFocus(event) {
if (event.target.select) if (event.target.select)
event.target.select(); event.target.select();
} }
rootLinkInput() {
this.updateSelectedLink();
this.updateStartLink();
}
selectedLinkInput() {
this.updateStartLink();
}
submitUrl() { submitUrl() {
if (this.bookUrl) { if (this.bookUrl) {
this.sendMessage({type: 'submitUrl', data: { this.sendMessage({type: 'submitUrl', data: {
@@ -415,6 +472,7 @@ class ExternalLibs extends Vue {
this.addBookmarkVisible = true; this.addBookmarkVisible = true;
this.$nextTick(() => { this.$nextTick(() => {
this.$refs.bookmarkLink.focus(); this.$refs.bookmarkLink.focus();
this.$refs.defaultRootLink.toggleOption = this.toggleOption;
}); });
} }
@@ -429,7 +487,28 @@ class ExternalLibs extends Vue {
} }
} }
defaultRootLinkInput() {
this.updateBookmarkLink();
}
bookmarkLinkKeyDown(event) {
if (event.key == 'Enter') {
this.$refs.bookmarkDesc.focus();
event.preventDefault();
}
}
bookmarkDescKeyDown(event) {
if (event.key == 'Enter') {
this.okAddBookmark();
event.preventDefault();
}
}
async okAddBookmark() { async okAddBookmark() {
if (!this.bookmarkLink)
return;
const link = this.addProtocol(this.bookmarkLink); const link = this.addProtocol(this.bookmarkLink);
let index = -1; let index = -1;
try { try {
@@ -476,9 +555,6 @@ class ExternalLibs extends Vue {
this.addBookmarkVisible = false; this.addBookmarkVisible = false;
} }
bookmarkSettings() {
}
fullScreenToggle() { fullScreenToggle() {
this.fullScreenActive = !this.fullScreenActive; this.fullScreenActive = !this.fullScreenActive;
if (this.fullScreenActive) { if (this.fullScreenActive) {
@@ -488,26 +564,60 @@ class ExternalLibs extends Vue {
} }
} }
transparentLayoutClick() {
this.transparentLayoutVisible = false;
}
onSelectPopupShow() {
this.transparentLayoutVisible = true;
}
onSelectPopupHide() {
this.transparentLayoutVisible = false;
}
close() { close() {
this.sendMessage({type: 'close'}); this.sendMessage({type: 'close'});
} }
keyHook() { bookUrlKeyDown(event) {
if (event.key == 'Enter') {
this.submitUrl();
event.preventDefault();
}
}
bookmarkSettings() {
this.bookmarkSettingsActive = true;
this.$nextTick(() => {
this.$refs.bookmarkSettings.init();
});
}
closeBookmarkSettings() {
this.bookmarkSettingsActive = false;
}
keyHook(event) {
if (this.$root.rootRoute() == '/external-libs') { if (this.$root.rootRoute() == '/external-libs') {
if (this.bookmarkSettingsActive && this.$refs.bookmarkSettings.keyHook(event))
return true;
if (this.$refs.dialogAddBookmark.active) if (this.$refs.dialogAddBookmark.active)
return false; return false;
//недостатки сторонних ui if (event.type == 'keydown' && event.key == 'F4') {
const input = this.$refs.input.$refs.input; this.addBookmark();
if (document.activeElement === input && event.type == 'keydown' && event.key == 'Enter') {
this.submitUrl();
return true; return true;
} }
if (event.type == 'keydown' && event.key == 'Escape') { if (event.type == 'keydown' && event.key == 'Escape' &&
(document.activeElement != this.$refs.rootLink.$refs.target || !this.$refs.rootLink.menu) &&
(document.activeElement != this.$refs.selectedLink.$refs.target || !this.$refs.selectedLink.menu)
) {
this.close(); this.close();
return true;
} }
return true;
} }
return false; return false;
} }
@@ -532,4 +642,9 @@ class ExternalLibs extends Vue {
background-color: #69C05F; background-color: #69C05F;
} }
.transparent-layout {
top: 0;
left: 0;
position: absolute;
}
</style> </style>

View File

@@ -1,6 +1,6 @@
<template> <template>
<div ref="main" class="column no-wrap" style="min-height: 500px"> <div ref="main" class="column no-wrap" style="min-height: 500px">
<div class="relative-position"> <div v-if="mode != 'liberama.top'" class="relative-position">
<GithubCorner url="https://github.com/bookpauk/liberama" cornerColor="#1B695F" gitColor="#EBE2C9"></GithubCorner> <GithubCorner url="https://github.com/bookpauk/liberama" cornerColor="#1B695F" gitColor="#EBE2C9"></GithubCorner>
</div> </div>
<div class="col column justify-center items-center no-wrap overflow-hidden" style="min-height: 230px"> <div class="col column justify-center items-center no-wrap overflow-hidden" style="min-height: 230px">

View File

@@ -39,6 +39,10 @@
<q-icon name="la la-copy" size="32px"/> <q-icon name="la la-copy" size="32px"/>
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">{{ rstore.readerActions['copyText'] }}</q-tooltip> <q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">{{ rstore.readerActions['copyText'] }}</q-tooltip>
</button> </button>
<button ref="splitToPara" v-show="showToolButton['splitToPara']" class="tool-button" :class="buttonActiveClass('splitToPara')" @click="buttonClick('splitToPara')" v-ripple>
<q-icon name="la la-retweet" size="32px"/>
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">{{ rstore.readerActions['splitToPara'] }}</q-tooltip>
</button>
<button ref="refresh" v-show="showToolButton['refresh']" class="tool-button" :class="buttonActiveClass('refresh')" @click="buttonClick('refresh')" v-ripple> <button ref="refresh" v-show="showToolButton['refresh']" class="tool-button" :class="buttonActiveClass('refresh')" @click="buttonClick('refresh')" v-ripple>
<q-icon name="la la-sync" size="32px" :class="{clear: !showRefreshIcon}"/> <q-icon name="la la-sync" size="32px" :class="{clear: !showRefreshIcon}"/>
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">{{ rstore.readerActions['refresh'] }}</q-tooltip> <q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">{{ rstore.readerActions['refresh'] }}</q-tooltip>
@@ -327,6 +331,12 @@ class Reader extends Vue {
this.checkActivateDonateHelpPage(); this.checkActivateDonateHelpPage();
this.loading = false; this.loading = false;
//проверим состояние Settings, и обновим, если надо
const newSettings = rstore.addDefaultsToSettings(this.settings);
if (newSettings) {
this.commit('reader/setSettings', newSettings);
}
this.updateRoute(); this.updateRoute();
await this.showWhatsNew(); await this.showWhatsNew();
@@ -699,6 +709,12 @@ class Reader extends Vue {
} }
} }
refreshBookSplitToPara() {
if (this.mostRecentBook()) {
this.loadBook({url: this.mostRecentBook().url, skipCheck: true, isText: true, force: true});
}
}
recentBooksClose() { recentBooksClose() {
this.recentBooksActive = false; this.recentBooksActive = false;
} }
@@ -816,6 +832,7 @@ class Reader extends Vue {
case 'scrolling': case 'scrolling':
case 'search': case 'search':
case 'copyText': case 'copyText':
case 'splitToPara':
case 'refresh': case 'refresh':
case 'libs': case 'libs':
case 'recentBooks': case 'recentBooks':
@@ -847,8 +864,9 @@ class Reader extends Vue {
case 'copyText': case 'copyText':
classResult = classDisabled; classResult = classDisabled;
break; break;
case 'recentBooks': case 'splitToPara':
case 'refresh': case 'refresh':
case 'recentBooks':
if (!this.mostRecentBookReactive) if (!this.mostRecentBookReactive)
classResult = classDisabled; classResult = classDisabled;
break; break;
@@ -1001,9 +1019,16 @@ class Reader extends Vue {
// не удалось, скачиваем книгу полностью с конвертацией // не удалось, скачиваем книгу полностью с конвертацией
let loadCached = true; let loadCached = true;
if (!book) { if (!book) {
book = await readerApi.loadBook({url, enableSitesFilter: this.enableSitesFilter}, (state) => { book = await readerApi.loadBook({
progress.setState(state); url,
}); skipCheck: (opts.skipCheck ? true : false),
isText: (opts.isText ? true : false),
enableSitesFilter: this.enableSitesFilter
},
(state) => {
progress.setState(state);
}
);
loadCached = false; loadCached = false;
} }
@@ -1122,6 +1147,9 @@ class Reader extends Vue {
case 'copyText': case 'copyText':
this.copyTextToggle(); this.copyTextToggle();
break; break;
case 'splitToPara':
this.refreshBookSplitToPara();
break;
case 'refresh': case 'refresh':
this.refreshBook(); this.refreshBook();
break; break;

View File

@@ -69,15 +69,15 @@ class ServerStorage extends Vue {
try { try {
this.cachedRecent = await ssCacheStore.getItem('recent'); this.cachedRecent = await ssCacheStore.getItem('recent');
if (!this.cachedRecent) if (!this.cachedRecent)
await this.setCachedRecent({rev: 0, data: {}}); await this.cleanCachedRecent('cachedRecent');
this.cachedRecentPatch = await ssCacheStore.getItem('recent-patch'); this.cachedRecentPatch = await ssCacheStore.getItem('recent-patch');
if (!this.cachedRecentPatch) if (!this.cachedRecentPatch)
await this.setCachedRecentPatch({rev: 0, data: {}}); await this.cleanCachedRecent('cachedRecentPatch');
this.cachedRecentMod = await ssCacheStore.getItem('recent-mod'); this.cachedRecentMod = await ssCacheStore.getItem('recent-mod');
if (!this.cachedRecentMod) if (!this.cachedRecentMod)
await this.setCachedRecentMod({rev: 0, data: {}}); await this.cleanCachedRecent('cachedRecentMod');
if (!this.serverStorageKey) { if (!this.serverStorageKey) {
//генерируем новый ключ //генерируем новый ключ
@@ -105,6 +105,15 @@ class ServerStorage extends Vue {
this.cachedRecentMod = value; this.cachedRecentMod = value;
} }
async cleanCachedRecent(whatToClean) {
if (whatToClean == 'cachedRecent' || whatToClean == 'all')
await this.setCachedRecent({rev: 0, data: {}});
if (whatToClean == 'cachedRecentPatch' || whatToClean == 'all')
await this.setCachedRecentPatch({rev: 0, data: {}});
if (whatToClean == 'cachedRecentMod' || whatToClean == 'all')
await this.setCachedRecentMod({rev: 0, data: {}});
}
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);
@@ -134,9 +143,12 @@ class ServerStorage extends Vue {
await this.currentProfileChanged(force); await this.currentProfileChanged(force);
await this.loadLibs(force); await this.loadLibs(force);
if (force)
await this.cleanCachedRecent('all');
const loadSuccess = await this.loadRecent(); const loadSuccess = await this.loadRecent();
if (loadSuccess && force) if (loadSuccess && force) {
await this.saveRecent(); await this.saveRecent();
}
} }
} }
@@ -493,7 +505,7 @@ class ServerStorage extends Vue {
const md = newRecentMod.data; const md = newRecentMod.data;
if (md.key && result[md.key]) if (md.key && result[md.key])
result[md.key] = utils.applyObjDiff(result[md.key], md.mod, true); result[md.key] = utils.applyObjDiff(result[md.key], md.mod, {isAddChanged: true});
if (!bookManager.loaded) { if (!bookManager.loaded) {
this.warning('Ожидание загрузки списка книг перед синхронизацией'); this.warning('Ожидание загрузки списка книг перед синхронизацией');
@@ -557,7 +569,7 @@ class ServerStorage extends Vue {
let applyMod = this.cachedRecentMod.data; let applyMod = this.cachedRecentMod.data;
if (applyMod && applyMod.key && newRecentPatch.data[applyMod.key]) if (applyMod && applyMod.key && newRecentPatch.data[applyMod.key])
newRecentPatch.data[applyMod.key] = utils.applyObjDiff(newRecentPatch.data[applyMod.key], applyMod.mod, true); newRecentPatch.data[applyMod.key] = utils.applyObjDiff(newRecentPatch.data[applyMod.key], applyMod.mod, {isAddChanged: true});
newRecentMod = {rev: this.cachedRecentMod.rev + 1, data: {}}; newRecentMod = {rev: this.cachedRecentMod.rev + 1, data: {}};
needSaveRecentPatch = true; needSaveRecentPatch = true;

View File

@@ -108,8 +108,10 @@ export default @Component({
}, },
vertShift: function(newValue) { vertShift: function(newValue) {
const font = (this.webFontName ? this.webFontName : this.fontName); const font = (this.webFontName ? this.webFontName : this.fontName);
this.fontShifts = Object.assign({}, this.fontShifts, {[font]: newValue}); if (this.fontShifts[font] != newValue) {
this.fontVertShift = newValue; this.fontShifts = Object.assign({}, this.fontShifts, {[font]: newValue});
this.fontVertShift = newValue;
}
}, },
fontName: function(newValue) { fontName: function(newValue) {
const font = (this.webFontName ? this.webFontName : newValue); const font = (this.webFontName ? this.webFontName : newValue);
@@ -185,10 +187,18 @@ class SettingsPage extends Vue {
settingsChanged() { settingsChanged() {
if (_.isEqual(this.form, this.settings)) if (_.isEqual(this.form, this.settings))
return; return;
this.form = Object.assign({}, this.settings); this.form = Object.assign({}, this.settings);
if (!this.unwatch)
this.unwatch = {};
for (let prop in rstore.settingDefaults) { for (let prop in rstore.settingDefaults) {
if (this.unwatch && this.unwatch[prop])
this.unwatch[prop]();
this[prop] = this.form[prop]; this[prop] = this.form[prop];
this.$watch(prop, (newValue) => {
this.unwatch[prop] = this.$watch(prop, (newValue) => {
this.form = Object.assign({}, this.form, {[prop]: newValue}); this.form = Object.assign({}, this.form, {[prop]: newValue});
}); });
} }

View File

@@ -1,4 +1,16 @@
export const versionHistory = [ export const versionHistory = [
{
showUntil: '2020-10-31',
header: '0.9.5 (2020-11-01)',
content:
`
<ul>
<li>на панель инструментов добавлена новая кнопка "Обновить с разбиением на параграфы"</li>
<li>исправления багов</li>
</ul>
`
},
{ {
showUntil: '2020-10-28', showUntil: '2020-10-28',
header: '0.9.4 (2020-10-29)', header: '0.9.4 (2020-10-29)',

View File

@@ -32,7 +32,7 @@ const DialogProps = Vue.extend({
props: { props: {
value: Boolean, value: Boolean,
} }
}) });
export default @Component({ export default @Component({
}) })

View File

@@ -1,7 +1,7 @@
<template> <template>
<div ref="main" class="main xyfit absolute" @click="close" @mouseup="onMouseUp" @mousemove="onMouseMove"> <div ref="main" class="main xyfit absolute" @click="close" @mouseup="onMouseUp" @mousemove="onMouseMove">
<div ref="windowBox" class="xyfit absolute flex no-wrap" @click.stop> <div ref="windowBox" class="xyfit absolute flex no-wrap" @click.stop>
<div class="window flexfit column no-wrap"> <div ref="window" class="window flexfit column no-wrap">
<div ref="header" class="header row justify-end" @mousedown.prevent.stop="onMouseDown" <div ref="header" class="header row justify-end" @mousedown.prevent.stop="onMouseDown"
@touchstart.stop="onTouchStart" @touchend.stop="onTouchEnd" @touchmove.stop="onTouchMove"> @touchstart.stop="onTouchStart" @touchend.stop="onTouchEnd" @touchmove.stop="onTouchMove">
<span class="header-text col"><slot name="header"></slot></span> <span class="header-text col"><slot name="header"></slot></span>
@@ -26,6 +26,7 @@ export default @Component({
width: { type: String, default: '100%' }, width: { type: String, default: '100%' },
maxWidth: { type: String, default: '' }, maxWidth: { type: String, default: '' },
topShift: { type: Number, default: 0 }, topShift: { type: Number, default: 0 },
margin: '',
} }
}) })
class Window extends Vue { class Window extends Vue {
@@ -40,6 +41,9 @@ class Window extends Vue {
const top = (this.$refs.main.offsetHeight - this.$refs.windowBox.offsetHeight)/2 + this.topShift; const top = (this.$refs.main.offsetHeight - this.$refs.windowBox.offsetHeight)/2 + this.topShift;
this.$refs.windowBox.style.left = (left > 0 ? left : 0) + 'px'; this.$refs.windowBox.style.left = (left > 0 ? left : 0) + 'px';
this.$refs.windowBox.style.top = (top > 0 ? top : 0) + 'px'; this.$refs.windowBox.style.top = (top > 0 ? top : 0) + 'px';
if (this.margin)
this.$refs.window.style.margin = this.margin;
}); });
} }
@@ -120,6 +124,7 @@ class Window extends Vue {
.main { .main {
background-color: transparent !important; background-color: transparent !important;
z-index: 50; z-index: 50;
overflow: hidden;
} }
.xyfit { .xyfit {

View File

@@ -130,20 +130,53 @@ export function getObjDiff(oldObj, newObj) {
} }
export function isObjDiff(diff) { export function isObjDiff(diff) {
return (_.isObject(diff) && diff.__isDiff); return (_.isObject(diff) && diff.__isDiff && diff.change && diff.add && diff.del);
} }
export function isEmptyObjDiff(diff) { export function isEmptyObjDiff(diff) {
return (!_.isObject(diff) || !diff.__isDiff || return (!isObjDiff(diff) ||
(!Object.keys(diff.change).length && !(Object.keys(diff.change).length ||
!Object.keys(diff.add).length && Object.keys(diff.add).length ||
!diff.del.length diff.del.length
) )
); );
} }
export function applyObjDiff(obj, diff, isAddChanged) { export function isEmptyObjDiffDeep(diff, opts = {}) {
const result = _.cloneDeep(obj); if (!isObjDiff(diff))
return true;
const {
isApplyChange = true,
isApplyAdd = true,
isApplyDel = true,
} = opts;
let notEmptyDeep = false;
const change = diff.change;
for (const key of Object.keys(change)) {
if (_.isObject(change[key]))
notEmptyDeep |= !isEmptyObjDiffDeep(change[key], opts);
else if (isApplyChange)
notEmptyDeep = true;
}
return !(
notEmptyDeep ||
(isApplyAdd && Object.keys(diff.add).length) ||
(isApplyDel && diff.del.length)
);
}
export function applyObjDiff(obj, diff, opts = {}) {
const {
isAddChanged = false,
isApplyChange = true,
isApplyAdd = true,
isApplyDel = true,
} = opts;
let result = _.cloneDeep(obj);
if (!diff.__isDiff) if (!diff.__isDiff)
return result; return result;
@@ -151,21 +184,28 @@ export function applyObjDiff(obj, diff, isAddChanged) {
for (const key of Object.keys(change)) { for (const key of Object.keys(change)) {
if (result.hasOwnProperty(key)) { if (result.hasOwnProperty(key)) {
if (_.isObject(change[key])) { if (_.isObject(change[key])) {
result[key] = applyObjDiff(result[key], change[key], isAddChanged); result[key] = applyObjDiff(result[key], change[key], opts);
} else { } else {
result[key] = _.cloneDeep(change[key]); if (isApplyChange)
result[key] = _.cloneDeep(change[key]);
} }
} else if (isAddChanged) { } else if (isAddChanged) {
result[key] = _.cloneDeep(change[key]); result[key] = _.cloneDeep(change[key]);
} }
} }
for (const key of Object.keys(diff.add)) { if (isApplyAdd) {
result[key] = _.cloneDeep(diff.add[key]); for (const key of Object.keys(diff.add)) {
result[key] = _.cloneDeep(diff.add[key]);
}
} }
for (const key of diff.del) { if (isApplyDel && diff.del.length) {
delete result[key]; for (const key of diff.del) {
delete result[key];
}
if (_.isArray(result))
result = result.filter(v => v);
} }
return result; return result;

View File

@@ -1,3 +1,5 @@
import * as utils from '../../share/utils';
const readerActions = { const readerActions = {
'help': 'Вызвать cправку', 'help': 'Вызвать cправку',
'loader': 'На страницу загрузки', 'loader': 'На страницу загрузки',
@@ -10,6 +12,7 @@ const readerActions = {
'setPosition': 'Установить позицию', 'setPosition': 'Установить позицию',
'search': 'Найти в тексте', 'search': 'Найти в тексте',
'copyText': 'Скопировать текст со страницы', 'copyText': 'Скопировать текст со страницы',
'splitToPara': 'Обновить с разбиением на параграфы',
'refresh': 'Принудительно обновить книгу', 'refresh': 'Принудительно обновить книгу',
'offlineMode': 'Автономный режим (без интернета)', 'offlineMode': 'Автономный режим (без интернета)',
'libs': 'Библиотека', 'libs': 'Библиотека',
@@ -37,6 +40,7 @@ const toolButtons = [
{name: 'setPosition', show: true}, {name: 'setPosition', show: true},
{name: 'search', show: true}, {name: 'search', show: true},
{name: 'copyText', show: false}, {name: 'copyText', show: false},
{name: 'splitToPara', show: false},
{name: 'refresh', show: true}, {name: 'refresh', show: true},
{name: 'libs', show: true}, {name: 'libs', show: true},
{name: 'recentBooks', show: true}, {name: 'recentBooks', show: true},
@@ -55,6 +59,7 @@ const hotKeys = [
{name: 'setPosition', codes: ['P']}, {name: 'setPosition', codes: ['P']},
{name: 'search', codes: ['Ctrl+F']}, {name: 'search', codes: ['Ctrl+F']},
{name: 'copyText', codes: ['Ctrl+C']}, {name: 'copyText', codes: ['Ctrl+C']},
{name: 'splitToPara', codes: ['Shift+R']},
{name: 'refresh', codes: ['R']}, {name: 'refresh', codes: ['R']},
{name: 'offlineMode', codes: ['O']}, {name: 'offlineMode', codes: ['O']},
{name: 'libs', codes: ['L']}, {name: 'libs', codes: ['L']},
@@ -253,6 +258,25 @@ const settingDefaults = {
userHotKeys: {}, userHotKeys: {},
}; };
for (const font of fonts)
settingDefaults.fontShifts[font.name] = font.fontVertShift;
for (const font of webFonts)
settingDefaults.fontShifts[font.name] = font.fontVertShift;
for (const button of toolButtons)
settingDefaults.showToolButton[button.name] = button.show;
for (const hotKey of hotKeys)
settingDefaults.userHotKeys[hotKey.name] = hotKey.codes;
function addDefaultsToSettings(settings) {
const diff = utils.getObjDiff(settings, settingDefaults);
if (!utils.isEmptyObjDiffDeep(diff, {isApplyChange: false})) {
return utils.applyObjDiff(settings, diff, {isApplyChange: false});
}
return false;
}
const libsDefaults = { const libsDefaults = {
startLink: 'http://flibusta.is', startLink: 'http://flibusta.is',
comment: 'Флибуста | Книжное братство', comment: 'Флибуста | Книжное братство',
@@ -276,15 +300,6 @@ const libsDefaults = {
] ]
}; };
for (const font of fonts)
settingDefaults.fontShifts[font.name] = font.fontVertShift;
for (const font of webFonts)
settingDefaults.fontShifts[font.name] = font.fontVertShift;
for (const button of toolButtons)
settingDefaults.showToolButton[button.name] = button.show;
for (const hotKey of hotKeys)
settingDefaults.userHotKeys[hotKey.name] = hotKey.codes;
// initial state // initial state
const state = { const state = {
toolBarActive: true, toolBarActive: true,
@@ -338,7 +353,13 @@ const mutations = {
state.currentProfile = value; state.currentProfile = value;
}, },
setSettings(state, value) { setSettings(state, value) {
state.settings = Object.assign({}, state.settings, value); const newSettings = Object.assign({}, state.settings, value);
const added = addDefaultsToSettings(newSettings);
if (added) {
state.settings = added;
} else {
state.settings = newSettings;
}
}, },
setSettingsRev(state, value) { setSettingsRev(state, value) {
state.settingsRev = Object.assign({}, state.settingsRev, value); state.settingsRev = Object.assign({}, state.settingsRev, value);
@@ -358,6 +379,7 @@ export default {
fonts, fonts,
webFonts, webFonts,
settingDefaults, settingDefaults,
addDefaultsToSettings,
libsDefaults, libsDefaults,
namespaced: true, namespaced: true,

85
docs/beta/beta.liberama Normal file
View File

@@ -0,0 +1,85 @@
server {
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/beta.liberama.top/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/beta.liberama.top/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
server_name beta.liberama.top;
client_max_body_size 50m;
proxy_read_timeout 1h;
gzip on;
gzip_min_length 1024;
gzip_proxied expired no-cache no-store private auth;
gzip_types *;
location /api {
proxy_pass http://127.0.0.1:34082;
}
location /ws {
proxy_pass http://127.0.0.1:34082;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location / {
root /home/beta.liberama/public;
location /tmp {
types { } default_type "application/xml; charset=utf-8";
add_header Content-Encoding gzip;
}
location ~* \.(?:manifest|appcache|html)$ {
expires -1;
}
}
}
server {
listen 80;
server_name beta.liberama.top;
return 301 https://$host$request_uri;
}
server {
listen 80;
server_name b.beta.liberama.top;
client_max_body_size 50m;
proxy_read_timeout 1h;
gzip on;
gzip_min_length 1024;
gzip_proxied expired no-cache no-store private auth;
gzip_types *;
location /api {
proxy_pass http://127.0.0.1:34082;
}
location /ws {
proxy_pass http://127.0.0.1:34082;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location / {
root /home/beta.liberama/public;
location /tmp {
types { } default_type "application/xml; charset=utf-8";
add_header Content-Encoding gzip;
}
location ~* \.(?:manifest|appcache|html)$ {
expires -1;
}
}
}

View File

@@ -64,7 +64,7 @@ sudo -u www-data cp -r docs/omnireader.ru/old/* /home/oldreader
## Запуск по крону ## Запуск по крону
``` ```
* * * * * /root/liberama/docs/omnireader/cron_server.sh * * * * * /root/liberama/docs/omnireader.ru/cron_server.sh
``` ```
## Деплой и запуск ## Деплой и запуск

2
package-lock.json generated
View File

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

View File

@@ -1,6 +1,6 @@
{ {
"name": "Liberama", "name": "Liberama",
"version": "0.9.4", "version": "0.9.5",
"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",

View File

@@ -19,7 +19,9 @@ class ReaderController extends BaseController {
throw new Error(`key 'url' is empty`); throw new Error(`key 'url' is empty`);
const workerId = this.readerWorker.loadBookUrl({ const workerId = this.readerWorker.loadBookUrl({
url: request.url, url: request.url,
enableSitesFilter: (request.hasOwnProperty('enableSitesFilter') ? request.enableSitesFilter : true) enableSitesFilter: (request.hasOwnProperty('enableSitesFilter') ? request.enableSitesFilter : true),
skipCheck: (request.hasOwnProperty('skipCheck') ? request.skipCheck : false),
isText: (request.hasOwnProperty('isText') ? request.isText : false),
}); });
const state = this.workerState.getState(workerId); const state = this.workerState.getState(workerId);
return (state ? state : {}); return (state ? state : {});

View File

@@ -200,7 +200,7 @@ class ConvertHtml extends ConvertBase {
titleInfo['book-title'] = title; titleInfo['book-title'] = title;
//подозрение на чистый текст, надо разбить на параграфы //подозрение на чистый текст, надо разбить на параграфы
if (isText || pars.length < buf.length/2000) { if (isText || (buf.length > 30*1024 && pars.length < buf.length/2000)) {
let total = 0; let total = 0;
let count = 1; let count = 1;
for (let i = 0; i < spaceCounter.length; i++) { for (let i = 0; i < spaceCounter.length; i++) {