Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d69e534f8b | ||
|
|
1de9ddd394 | ||
|
|
77c68d4e11 | ||
|
|
2a0d1dcfce | ||
|
|
5a19cca407 | ||
|
|
4e8773ecde | ||
|
|
4c7dada809 | ||
|
|
65690b15da | ||
|
|
8ba07812ce | ||
|
|
2dd8f35001 | ||
|
|
2d15aa88d4 | ||
|
|
e4257e50f0 | ||
|
|
33ebc07915 | ||
|
|
bc07299626 | ||
|
|
25e8aeef53 | ||
|
|
a2ed34abf3 | ||
|
|
36a7b7b91a | ||
|
|
b4e8b7375f |
@@ -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>
|
||||||
|
|||||||
@@ -248,7 +248,10 @@ class HistoryPage extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isUrl(url) {
|
isUrl(url) {
|
||||||
return (url.indexOf('file://') != 0);
|
if (url)
|
||||||
|
return (url.indexOf('file://') != 0);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
|
|||||||
@@ -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()});
|
||||||
@@ -75,7 +76,12 @@ class ServerStorage extends Vue {
|
|||||||
async serverSyncEnabledChanged() {
|
async serverSyncEnabledChanged() {
|
||||||
if (this.serverSyncEnabled) {
|
if (this.serverSyncEnabled) {
|
||||||
this.prevServerStorageKey = null;
|
this.prevServerStorageKey = null;
|
||||||
await this.serverStorageKeyChanged(true);
|
if (!this.serverStorageKey) {
|
||||||
|
//генерируем новый ключ
|
||||||
|
await this.generateNewServerStorageKey();
|
||||||
|
} else {
|
||||||
|
await this.serverStorageKeyChanged(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +89,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();
|
||||||
@@ -158,7 +165,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}`;
|
||||||
@@ -201,7 +208,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);
|
||||||
@@ -247,7 +254,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;
|
||||||
@@ -289,7 +296,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);
|
||||||
@@ -341,7 +348,7 @@ 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;
|
||||||
@@ -411,7 +418,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;
|
||||||
@@ -457,7 +464,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;
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -751,12 +751,12 @@ class SettingsPage extends Vue {
|
|||||||
dangerouslyUseHTMLString: true,
|
dangerouslyUseHTMLString: true,
|
||||||
confirmButtonText: 'OK',
|
confirmButtonText: 'OK',
|
||||||
cancelButtonText: 'Отмена',
|
cancelButtonText: 'Отмена',
|
||||||
inputValidator: (str) => { if (str && str.length == 44) return true; else return 'Неверный формат ключа'; },
|
inputValidator: (str) => { if (str && utils.fromBase58(str).length == 32) return true; else return 'Неверный формат ключа'; },
|
||||||
inputValue: (key && _.isString(key) ? key : null),
|
inputValue: (key && _.isString(key) ? key : null),
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.value && result.value.length == 44) {
|
if (result.value && utils.fromBase58(result.value).length == 32) {
|
||||||
this.commit('reader/setServerStorageKey', result.value);
|
this.commit('reader/setServerStorageKey', result.value);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -79,27 +79,31 @@ 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) {
|
||||||
for (let i = 0; i < 1000; i++) {
|
for (let i = 0; i < 1000; i++) {
|
||||||
const k = this.keyFromUrl(i.toString());
|
const k = this.keyFromUrl(i.toString());
|
||||||
this.recent[k] = Object.assign({}, _.cloneDeep(this.recent[key]), {key: k, touchTime: Date.now() - 1000000});
|
this.recent[k] = Object.assign({}, _.cloneDeep(this.recent[key]), {key: k, touchTime: Date.now() - 1000000, url: utils.randomHexString(300)});
|
||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
await this.cleanBooks();
|
await this.cleanBooks();
|
||||||
|
|
||||||
//очистка позже
|
//очистка позже
|
||||||
@@ -370,9 +374,19 @@ class BookManager {
|
|||||||
|
|
||||||
Object.assign(mergedRecent, value);
|
Object.assign(mergedRecent, value);
|
||||||
const newRecent = {};
|
const newRecent = {};
|
||||||
|
|
||||||
|
//"ленивое" обновление хранилища
|
||||||
|
(async() => {
|
||||||
|
for (const rec of Object.values(mergedRecent)) {
|
||||||
|
if (rec.key) {
|
||||||
|
await bmRecentStore.setItem(rec.key, rec);
|
||||||
|
await utils.sleep(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
for (const rec of Object.values(mergedRecent)) {
|
for (const rec of Object.values(mergedRecent)) {
|
||||||
if (rec.key) {
|
if (rec.key) {
|
||||||
await bmRecentStore.setItem(rec.key, rec);
|
|
||||||
newRecent[rec.key] = rec;
|
newRecent[rec.key] = rec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -399,6 +413,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);
|
||||||
|
|||||||
@@ -2,13 +2,12 @@ import _ from 'lodash';
|
|||||||
import baseX from 'base-x';
|
import baseX from 'base-x';
|
||||||
import PAKO from 'pako';
|
import PAKO from 'pako';
|
||||||
import {Buffer} from 'safe-buffer';
|
import {Buffer} from 'safe-buffer';
|
||||||
|
import sjclWrapper from './sjclWrapper';
|
||||||
|
|
||||||
export const pako = PAKO;
|
export const pako = PAKO;
|
||||||
|
|
||||||
const BASE58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
|
const BASE58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
|
||||||
const BASE64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
||||||
const bs58 = baseX(BASE58);
|
const bs58 = baseX(BASE58);
|
||||||
const bs64 = baseX(BASE64);
|
|
||||||
|
|
||||||
export function sleep(ms) {
|
export function sleep(ms) {
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
@@ -86,12 +85,18 @@ export function fromBase58(data) {
|
|||||||
return bs58.decode(data);
|
return bs58.decode(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//base-x слишком тормозит, используем sjcl
|
||||||
export function toBase64(data) {
|
export function toBase64(data) {
|
||||||
return bs64.encode(Buffer.from(data));
|
return sjclWrapper.codec.base64.fromBits(
|
||||||
|
sjclWrapper.codec.bytes.toBits(Buffer.from(data))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//base-x слишком тормозит, используем sjcl
|
||||||
export function fromBase64(data) {
|
export function fromBase64(data) {
|
||||||
return bs64.decode(data);
|
return Buffer.from(sjclWrapper.codec.bytes.fromBits(
|
||||||
|
sjclWrapper.codec.base64.toBits(data)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getObjDiff(oldObj, newObj) {
|
export function getObjDiff(oldObj, newObj) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "Liberama",
|
"name": "Liberama",
|
||||||
"version": "0.6.1",
|
"version": "0.6.5",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ async function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
app.use(compression({ level: 1 }));
|
app.use(compression({ level: 1 }));
|
||||||
app.use(express.json());
|
app.use(express.json({limit: '10mb'}));
|
||||||
if (devModule)
|
if (devModule)
|
||||||
devModule.logQueries(app);
|
devModule.logQueries(app);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user