Глобальный рефакторинг SettingsPage (в процессе)
This commit is contained in:
@@ -1,101 +0,0 @@
|
|||||||
<div class="part-header">Управление синхронизацией данных</div>
|
|
||||||
|
|
||||||
<div class="item row">
|
|
||||||
<div class="label-1"></div>
|
|
||||||
<q-checkbox class="col" v-model="serverSyncEnabled" size="xs" label="Включить синхронизацию с сервером" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-show="serverSyncEnabled">
|
|
||||||
<!---------------------------------------------->
|
|
||||||
<div class="part-header">Профили устройств</div>
|
|
||||||
|
|
||||||
<div class="item row">
|
|
||||||
<div class="label-1"></div>
|
|
||||||
<div class="text col">
|
|
||||||
Выберите или добавьте профиль устройства, чтобы начать синхронизацию настроек с сервером.
|
|
||||||
<br>При выборе "Нет" синхронизация настроек (но не книг) отключается.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="item row">
|
|
||||||
<div class="label-1">Устройство</div>
|
|
||||||
<div class="col">
|
|
||||||
<q-select v-model="currentProfile" :options="currentProfileOptions"
|
|
||||||
style="width: 275px"
|
|
||||||
dropdown-icon="la la-angle-down la-sm"
|
|
||||||
outlined dense emit-value map-options display-value-sanitize options-sanitize
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="item row">
|
|
||||||
<div class="label-1"></div>
|
|
||||||
<q-btn class="button" dense no-caps @click="addProfile">Добавить</q-btn>
|
|
||||||
<q-btn class="button" dense no-caps @click="delProfile">Удалить</q-btn>
|
|
||||||
<q-btn class="button" dense no-caps @click="delAllProfiles">Удалить все</q-btn>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!---------------------------------------------->
|
|
||||||
<div class="part-header">Ключ доступа</div>
|
|
||||||
|
|
||||||
<div class="item row">
|
|
||||||
<div class="label-1"></div>
|
|
||||||
<div class="text col">
|
|
||||||
Ключ доступа позволяет восстановить профили с настройками и список читаемых книг.
|
|
||||||
Для этого необходимо передать ключ на новое устройство через почту, мессенджер или другим способом.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="item row">
|
|
||||||
<div class="label-1"></div>
|
|
||||||
<q-btn class="button" style="width: 250px" dense no-caps @click="showServerStorageKey">
|
|
||||||
<span v-show="serverStorageKeyVisible">Скрыть</span>
|
|
||||||
<span v-show="!serverStorageKeyVisible">Показать</span>
|
|
||||||
ключ доступа
|
|
||||||
</q-btn>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="item row">
|
|
||||||
<div class="label-1"></div>
|
|
||||||
<div v-if="!serverStorageKeyVisible" class="col">
|
|
||||||
<hr/>
|
|
||||||
<b>{{ partialStorageKey }}</b> (часть вашего ключа)
|
|
||||||
<hr/>
|
|
||||||
</div>
|
|
||||||
<div v-else class="col" style="line-height: 100%">
|
|
||||||
<hr/>
|
|
||||||
<div style="width: 300px; padding-top: 5px; overflow-wrap: break-word;">
|
|
||||||
<b>{{ serverStorageKey }}</b>
|
|
||||||
<q-icon class="copy-icon" name="la la-copy" @click="copyToClip(serverStorageKey, 'Ключ')">
|
|
||||||
<q-tooltip :delay="1000" anchor="top middle" self="center middle" content-style="font-size: 80%">Скопировать</q-tooltip>
|
|
||||||
</q-icon>
|
|
||||||
</div>
|
|
||||||
<div v-if="mode == 'omnireader' || mode == 'liberama.top'">
|
|
||||||
<br>Переход по ссылке позволит автоматически ввести ключ доступа:
|
|
||||||
<br><div class="text-center" style="margin-top: 5px">
|
|
||||||
<a :href="setStorageKeyLink" target="_blank">Ссылка для ввода ключа</a>
|
|
||||||
<q-icon class="copy-icon" name="la la-copy" @click="copyToClip(setStorageKeyLink, 'Ссылка')">
|
|
||||||
<q-tooltip :delay="1000" anchor="top middle" self="center middle" content-style="font-size: 80%">Скопировать</q-tooltip>
|
|
||||||
</q-icon>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="item row">
|
|
||||||
<div class="label-1"></div>
|
|
||||||
<q-btn class="button" style="width: 250px" dense no-caps @click="enterServerStorageKey">Ввести ключ доступа</q-btn>
|
|
||||||
</div>
|
|
||||||
<div class="item row">
|
|
||||||
<div class="label-1"></div>
|
|
||||||
<q-btn class="button" style="width: 250px" dense no-caps @click="generateServerStorageKey">Сгенерировать новый ключ</q-btn>
|
|
||||||
</div>
|
|
||||||
<div class="item row">
|
|
||||||
<div class="label-1"></div>
|
|
||||||
<div class="text col">
|
|
||||||
Рекомендуется сохранить ключ в надежном месте, чтобы всегда иметь возможность восстановить настройки,
|
|
||||||
например, после переустановки ОС или чистки/смены браузера.<br>
|
|
||||||
<b>ПРЕДУПРЕЖДЕНИЕ!</b> При утере ключа, НИКТО не сможет восстановить ваши данные, т.к. они сжимаются
|
|
||||||
и шифруются ключом доступа перед отправкой на сервер.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -0,0 +1,362 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="sets-part-header">
|
||||||
|
Управление синхронизацией данных
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sets-item row">
|
||||||
|
<div class="sets-label label"></div>
|
||||||
|
<q-checkbox v-model="serverSyncEnabled" class="col" size="xs" label="Включить синхронизацию с сервером" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-show="serverSyncEnabled">
|
||||||
|
<!---------------------------------------------->
|
||||||
|
<div class="sets-part-header">
|
||||||
|
Профили устройств
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sets-item row">
|
||||||
|
<div class="sets-label label"></div>
|
||||||
|
<div class="text col">
|
||||||
|
Выберите или добавьте профиль устройства, чтобы начать синхронизацию настроек с сервером.
|
||||||
|
<br>При выборе "Нет" синхронизация настроек (но не книг) отключается.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sets-item row">
|
||||||
|
<div class="sets-label label">
|
||||||
|
Устройство
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<q-select
|
||||||
|
v-model="currentProfile" :options="currentProfileOptions"
|
||||||
|
style="width: 275px"
|
||||||
|
dropdown-icon="la la-angle-down la-sm"
|
||||||
|
outlined dense emit-value map-options display-value-sanitize options-sanitize
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sets-item row">
|
||||||
|
<div class="sets-label label"></div>
|
||||||
|
<q-btn class="sets-button" dense no-caps @click="addProfile">
|
||||||
|
Добавить
|
||||||
|
</q-btn>
|
||||||
|
<q-btn class="sets-button" dense no-caps @click="delProfile">
|
||||||
|
Удалить
|
||||||
|
</q-btn>
|
||||||
|
<q-btn class="sets-button" dense no-caps @click="delAllProfiles">
|
||||||
|
Удалить все
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!---------------------------------------------->
|
||||||
|
<div class="sets-part-header">
|
||||||
|
Ключ доступа
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sets-item row">
|
||||||
|
<div class="sets-label label"></div>
|
||||||
|
<div class="text col">
|
||||||
|
Ключ доступа позволяет восстановить профили с настройками и список читаемых книг.
|
||||||
|
Для этого необходимо передать ключ на новое устройство через почту, мессенджер или другим способом.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sets-item row">
|
||||||
|
<div class="sets-label label"></div>
|
||||||
|
<q-btn class="sets-button" style="width: 250px" dense no-caps @click="showServerStorageKey">
|
||||||
|
<span v-show="serverStorageKeyVisible">Скрыть</span>
|
||||||
|
<span v-show="!serverStorageKeyVisible">Показать</span>
|
||||||
|
ключ доступа
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sets-item row">
|
||||||
|
<div class="sets-label label"></div>
|
||||||
|
<div v-if="!serverStorageKeyVisible" class="col">
|
||||||
|
<hr />
|
||||||
|
<b>{{ partialStorageKey }}</b> (часть вашего ключа)
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
<div v-else class="col" style="line-height: 100%">
|
||||||
|
<hr />
|
||||||
|
<div style="width: 300px; padding-top: 5px; overflow-wrap: break-word;">
|
||||||
|
<b>{{ serverStorageKey }}</b>
|
||||||
|
<q-icon class="copy-icon" name="la la-copy" @click="copyToClip(serverStorageKey, 'Ключ')">
|
||||||
|
<q-tooltip :delay="1000" anchor="top middle" self="center middle" content-style="font-size: 80%">
|
||||||
|
Скопировать
|
||||||
|
</q-tooltip>
|
||||||
|
</q-icon>
|
||||||
|
</div>
|
||||||
|
<div v-if="mode == 'omnireader' || mode == 'liberama.top'">
|
||||||
|
<br>Переход по ссылке позволит автоматически ввести ключ доступа:
|
||||||
|
<br><div class="text-center" style="margin-top: 5px">
|
||||||
|
<a :href="setStorageKeyLink" target="_blank">Ссылка для ввода ключа</a>
|
||||||
|
<q-icon class="copy-icon" name="la la-copy" @click="copyToClip(setStorageKeyLink, 'Ссылка')">
|
||||||
|
<q-tooltip :delay="1000" anchor="top middle" self="center middle" content-style="font-size: 80%">
|
||||||
|
Скопировать
|
||||||
|
</q-tooltip>
|
||||||
|
</q-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sets-item row">
|
||||||
|
<div class="sets-label label"></div>
|
||||||
|
<q-btn class="sets-button" style="width: 250px" dense no-caps @click="enterServerStorageKey">
|
||||||
|
Ввести ключ доступа
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
<div class="sets-item row">
|
||||||
|
<div class="sets-label label"></div>
|
||||||
|
<q-btn class="sets-button" style="width: 250px" dense no-caps @click="generateServerStorageKey">
|
||||||
|
Сгенерировать новый ключ
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
<div class="sets-item row">
|
||||||
|
<div class="sets-label label"></div>
|
||||||
|
<div class="text col">
|
||||||
|
Рекомендуется сохранить ключ в надежном месте, чтобы всегда иметь возможность восстановить настройки,
|
||||||
|
например, после переустановки ОС или чистки/смены браузера.<br>
|
||||||
|
<b>ПРЕДУПРЕЖДЕНИЕ!</b> При утере ключа, НИКТО не сможет восстановить ваши данные, т.к. они сжимаются
|
||||||
|
и шифруются ключом доступа перед отправкой на сервер.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
import vueComponent from '../../../vueComponent.js';
|
||||||
|
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
import * as utils from '../../../../share/utils';
|
||||||
|
import rstore from '../../../../store/modules/reader';
|
||||||
|
|
||||||
|
const componentOptions = {
|
||||||
|
watch: {
|
||||||
|
},
|
||||||
|
};
|
||||||
|
class ProfilesTab {
|
||||||
|
_options = componentOptions;
|
||||||
|
_props = {
|
||||||
|
form: Object,
|
||||||
|
};
|
||||||
|
|
||||||
|
rstore = rstore;
|
||||||
|
|
||||||
|
serverStorageKeyVisible = false;
|
||||||
|
|
||||||
|
created() {
|
||||||
|
this.commit = this.$store.commit;
|
||||||
|
}
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
}
|
||||||
|
|
||||||
|
get mode() {
|
||||||
|
return this.$store.state.config.mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
get serverSyncEnabled() {
|
||||||
|
return this.$store.state.reader.serverSyncEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
set serverSyncEnabled(newValue) {
|
||||||
|
this.commit('reader/setServerSyncEnabled', newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
get currentProfile() {
|
||||||
|
return this.$store.state.reader.currentProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
set currentProfile(newValue) {
|
||||||
|
this.commit('reader/setCurrentProfile', newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
get profiles() {
|
||||||
|
return this.$store.state.reader.profiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
get currentProfileOptions() {
|
||||||
|
const profNames = Object.keys(this.profiles)
|
||||||
|
profNames.sort();
|
||||||
|
|
||||||
|
let result = [{label: 'Нет', value: ''}];
|
||||||
|
profNames.forEach(name => {
|
||||||
|
result.push({label: name, value: name});
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
get partialStorageKey() {
|
||||||
|
return this.serverStorageKey.substr(0, 7) + '***';
|
||||||
|
}
|
||||||
|
|
||||||
|
get serverStorageKey() {
|
||||||
|
return this.$store.state.reader.serverStorageKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
get setStorageKeyLink() {
|
||||||
|
return `https://${window.location.host}/#/reader?setStorageAccessKey=${utils.toBase58(this.serverStorageKey)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async addProfile() {
|
||||||
|
try {
|
||||||
|
if (Object.keys(this.profiles).length >= 100) {
|
||||||
|
this.$root.stdDialog.alert('Достигнут предел количества профилей', 'Ошибка');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const result = await this.$root.stdDialog.prompt('Введите произвольное название для профиля устройства:', ' ', {
|
||||||
|
inputValidator: (str) => { if (!str) return 'Название не должно быть пустым'; else if (str.length > 50) return 'Слишком длинное название'; else return true; },
|
||||||
|
});
|
||||||
|
if (result && result.value) {
|
||||||
|
if (this.profiles[result.value]) {
|
||||||
|
this.$root.stdDialog.alert('Такой профиль уже существует', 'Ошибка');
|
||||||
|
} else {
|
||||||
|
const newProfiles = Object.assign({}, this.profiles, {[result.value]: 1});
|
||||||
|
this.commit('reader/setAllowProfilesSave', true);
|
||||||
|
await this.$nextTick();//ждем обработчики watch
|
||||||
|
this.commit('reader/setProfiles', newProfiles);
|
||||||
|
await this.$nextTick();//ждем обработчики watch
|
||||||
|
this.commit('reader/setAllowProfilesSave', false);
|
||||||
|
this.currentProfile = result.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async delProfile() {
|
||||||
|
if (!this.currentProfile)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await this.$root.stdDialog.prompt(`<b>Предупреждение!</b> Удаление профиля '${this.$root.sanitize(this.currentProfile)}' необратимо.` +
|
||||||
|
`<br>Все настройки профиля будут потеряны, однако список читаемых книг сохранится.` +
|
||||||
|
`<br><br>Введите 'да' для подтверждения удаления:`, ' ', {
|
||||||
|
inputValidator: (str) => { if (str && str.toLowerCase() === 'да') return true; else return 'Удаление не подтверждено'; },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result && result.value && result.value.toLowerCase() == 'да') {
|
||||||
|
if (this.profiles[this.currentProfile]) {
|
||||||
|
const newProfiles = Object.assign({}, this.profiles);
|
||||||
|
delete newProfiles[this.currentProfile];
|
||||||
|
this.commit('reader/setAllowProfilesSave', true);
|
||||||
|
await this.$nextTick();//ждем обработчики watch
|
||||||
|
this.commit('reader/setProfiles', newProfiles);
|
||||||
|
await this.$nextTick();//ждем обработчики watch
|
||||||
|
this.commit('reader/setAllowProfilesSave', false);
|
||||||
|
this.currentProfile = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async delAllProfiles() {
|
||||||
|
if (!Object.keys(this.profiles).length)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await this.$root.stdDialog.prompt(`<b>Предупреждение!</b> Удаление ВСЕХ профилей с настройками необратимо.` +
|
||||||
|
`<br><br>Введите 'да' для подтверждения удаления:`, ' ', {
|
||||||
|
inputValidator: (str) => { if (str && str.toLowerCase() === 'да') return true; else return 'Удаление не подтверждено'; },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result && result.value && result.value.toLowerCase() == 'да') {
|
||||||
|
this.commit('reader/setAllowProfilesSave', true);
|
||||||
|
await this.$nextTick();//ждем обработчики watch
|
||||||
|
this.commit('reader/setProfiles', {});
|
||||||
|
await this.$nextTick();//ждем обработчики watch
|
||||||
|
this.commit('reader/setAllowProfilesSave', false);
|
||||||
|
this.currentProfile = '';
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async showServerStorageKey() {
|
||||||
|
this.serverStorageKeyVisible = !this.serverStorageKeyVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
async enterServerStorageKey(key) {
|
||||||
|
try {
|
||||||
|
const result = await this.$root.stdDialog.prompt(`<b>Предупреждение!</b> Изменение ключа доступа приведет к замене всех профилей и читаемых книг в читалке.` +
|
||||||
|
`<br><br>Введите новый ключ доступа:`, ' ', {
|
||||||
|
inputValidator: (str) => {
|
||||||
|
try {
|
||||||
|
if (str && utils.fromBase58(str).length == 32) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
return 'Неверный формат ключа';
|
||||||
|
},
|
||||||
|
inputValue: (key && _.isString(key) ? key : null),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result && result.value && utils.fromBase58(result.value).length == 32) {
|
||||||
|
this.commit('reader/setServerStorageKey', result.value);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async generateServerStorageKey() {
|
||||||
|
try {
|
||||||
|
const result = await this.$root.stdDialog.prompt(`<b>Предупреждение!</b> Генерация нового ключа доступа приведет к удалению всех профилей и читаемых книг в читалке.` +
|
||||||
|
`<br><br>Введите 'да' для подтверждения генерации нового ключа:`, ' ', {
|
||||||
|
inputValidator: (str) => { if (str && str.toLowerCase() === 'да') return true; else return 'Генерация не подтверждена'; },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result && result.value && result.value.toLowerCase() == 'да') {
|
||||||
|
if (this.$root.generateNewServerStorageKey)
|
||||||
|
this.$root.generateNewServerStorageKey();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async copyToClip(text, prefix) {
|
||||||
|
const result = await utils.copyTextToClipboard(text);
|
||||||
|
const suf = (prefix.substr(-1) == 'а' ? 'а' : '');
|
||||||
|
const msg = (result ? `${prefix} успешно скопирован${suf} в буфер обмена` : 'Копирование не удалось');
|
||||||
|
if (result)
|
||||||
|
this.$root.notify.success(msg);
|
||||||
|
else
|
||||||
|
this.$root.notify.error(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default vueComponent(ProfilesTab);
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.label {
|
||||||
|
width: 75px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
font-size: 90%;
|
||||||
|
line-height: 130%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-icon {
|
||||||
|
margin-left: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 120%;
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -39,9 +39,9 @@
|
|||||||
|
|
||||||
<div class="col fit">
|
<div class="col fit">
|
||||||
<!-- Профили --------------------------------------------------------------------->
|
<!-- Профили --------------------------------------------------------------------->
|
||||||
<!--div v-if="selectedTab == 'profiles'" class="fit tab-panel">
|
<div v-if="selectedTab == 'profiles'" class="fit tab-panel">
|
||||||
@@include('./ProfilesTab.inc');
|
<ProfilesTab :form="form" />
|
||||||
</div-->
|
</div>
|
||||||
<!-- Вид ------------------------------------------------------------------------->
|
<!-- Вид ------------------------------------------------------------------------->
|
||||||
<!--div v-if="selectedTab == 'view'" class="fit column">
|
<!--div v-if="selectedTab == 'view'" class="fit column">
|
||||||
<q-tabs
|
<q-tabs
|
||||||
@@ -136,6 +136,7 @@ import rstore from '../../../store/modules/reader';
|
|||||||
import defPalette from './defPalette';
|
import defPalette from './defPalette';
|
||||||
|
|
||||||
//pages
|
//pages
|
||||||
|
import ProfilesTab from './ProfilesTab/ProfilesTab.vue';
|
||||||
import ToolBarTab from './ToolBarTab/ToolBarTab.vue';
|
import ToolBarTab from './ToolBarTab/ToolBarTab.vue';
|
||||||
|
|
||||||
const hex = /^#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$/;
|
const hex = /^#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$/;
|
||||||
@@ -146,16 +147,16 @@ const componentOptions = {
|
|||||||
NumInput,
|
NumInput,
|
||||||
UserHotKeys,
|
UserHotKeys,
|
||||||
//pages
|
//pages
|
||||||
|
ProfilesTab,
|
||||||
ToolBarTab,
|
ToolBarTab,
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
settings: function() {
|
settings: function() {
|
||||||
this.settingsChanged();
|
this.settingsChanged();//no await
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
handler(newValue) {
|
handler(newValue) {
|
||||||
if (this.inited) {
|
if (this.inited && !this.setsChanged) {
|
||||||
console.log('save settings');
|
|
||||||
this.commit('reader/setSettings', _.cloneDeep(newValue));
|
this.commit('reader/setSettings', _.cloneDeep(newValue));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -240,13 +241,13 @@ class SettingsPage {
|
|||||||
webFonts = [];
|
webFonts = [];
|
||||||
fonts = [];
|
fonts = [];
|
||||||
|
|
||||||
serverStorageKeyVisible = false;
|
setsChanged = false;
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
this.commit = this.$store.commit;
|
this.commit = this.$store.commit;
|
||||||
this.reader = this.$store.state.reader;
|
this.reader = this.$store.state.reader;
|
||||||
|
|
||||||
this.settingsChanged();
|
this.settingsChanged();//no await
|
||||||
}
|
}
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
@@ -263,10 +264,12 @@ class SettingsPage {
|
|||||||
this.inited = true;
|
this.inited = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
settingsChanged() {
|
async settingsChanged() {
|
||||||
if (_.isEqual(this.form, this.settings))
|
if (_.isEqual(this.form, this.settings))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
this.setsChanged = true;
|
||||||
|
try {
|
||||||
this.form = _.cloneDeep(this.settings);
|
this.form = _.cloneDeep(this.settings);
|
||||||
const form = this.form;
|
const form = this.form;
|
||||||
|
|
||||||
@@ -281,6 +284,10 @@ class SettingsPage {
|
|||||||
this.bgColorFiltered = form.backgroundColor;
|
this.bgColorFiltered = form.backgroundColor;
|
||||||
this.dualDivColorFiltered = form.dualDivColor;
|
this.dualDivColorFiltered = form.dualDivColor;
|
||||||
this.statusBarColorFiltered = form.statusBarColor;
|
this.statusBarColorFiltered = form.statusBarColor;
|
||||||
|
} finally {
|
||||||
|
await this.$nextTick();
|
||||||
|
this.setsChanged = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get mode() {
|
get mode() {
|
||||||
@@ -295,33 +302,10 @@ class SettingsPage {
|
|||||||
return this.$store.state.reader.settings;
|
return this.$store.state.reader.settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
get serverSyncEnabled() {
|
|
||||||
return this.$store.state.reader.serverSyncEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
set serverSyncEnabled(newValue) {
|
|
||||||
this.commit('reader/setServerSyncEnabled', newValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
get profiles() {
|
|
||||||
return this.$store.state.reader.profiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
get configBucEnabled() {
|
get configBucEnabled() {
|
||||||
return this.$store.state.config.bucEnabled;
|
return this.$store.state.config.bucEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
get currentProfileOptions() {
|
|
||||||
const profNames = Object.keys(this.profiles)
|
|
||||||
profNames.sort();
|
|
||||||
|
|
||||||
let result = [{label: 'Нет', value: ''}];
|
|
||||||
profNames.forEach(name => {
|
|
||||||
result.push({label: name, value: name});
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
get wallpaperOptions() {
|
get wallpaperOptions() {
|
||||||
let result = [{label: 'Нет', value: ''}];
|
let result = [{label: 'Нет', value: ''}];
|
||||||
|
|
||||||
@@ -372,26 +356,6 @@ class SettingsPage {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
get currentProfile() {
|
|
||||||
return this.$store.state.reader.currentProfile;
|
|
||||||
}
|
|
||||||
|
|
||||||
set currentProfile(newValue) {
|
|
||||||
this.commit('reader/setCurrentProfile', newValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
get partialStorageKey() {
|
|
||||||
return this.serverStorageKey.substr(0, 7) + '***';
|
|
||||||
}
|
|
||||||
|
|
||||||
get serverStorageKey() {
|
|
||||||
return this.$store.state.reader.serverStorageKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
get setStorageKeyLink() {
|
|
||||||
return `https://${window.location.host}/#/reader?setStorageAccessKey=${utils.toBase58(this.serverStorageKey)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
get predefineTextColors() {
|
get predefineTextColors() {
|
||||||
return defPalette.concat([
|
return defPalette.concat([
|
||||||
'#ffffff',
|
'#ffffff',
|
||||||
@@ -466,140 +430,6 @@ class SettingsPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async addProfile() {
|
|
||||||
try {
|
|
||||||
if (Object.keys(this.profiles).length >= 100) {
|
|
||||||
this.$root.stdDialog.alert('Достигнут предел количества профилей', 'Ошибка');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const result = await this.$root.stdDialog.prompt('Введите произвольное название для профиля устройства:', ' ', {
|
|
||||||
inputValidator: (str) => { if (!str) return 'Название не должно быть пустым'; else if (str.length > 50) return 'Слишком длинное название'; else return true; },
|
|
||||||
});
|
|
||||||
if (result && result.value) {
|
|
||||||
if (this.profiles[result.value]) {
|
|
||||||
this.$root.stdDialog.alert('Такой профиль уже существует', 'Ошибка');
|
|
||||||
} else {
|
|
||||||
const newProfiles = Object.assign({}, this.profiles, {[result.value]: 1});
|
|
||||||
this.commit('reader/setAllowProfilesSave', true);
|
|
||||||
await this.$nextTick();//ждем обработчики watch
|
|
||||||
this.commit('reader/setProfiles', newProfiles);
|
|
||||||
await this.$nextTick();//ждем обработчики watch
|
|
||||||
this.commit('reader/setAllowProfilesSave', false);
|
|
||||||
this.currentProfile = result.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async delProfile() {
|
|
||||||
if (!this.currentProfile)
|
|
||||||
return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = await this.$root.stdDialog.prompt(`<b>Предупреждение!</b> Удаление профиля '${this.$root.sanitize(this.currentProfile)}' необратимо.` +
|
|
||||||
`<br>Все настройки профиля будут потеряны, однако список читаемых книг сохранится.` +
|
|
||||||
`<br><br>Введите 'да' для подтверждения удаления:`, ' ', {
|
|
||||||
inputValidator: (str) => { if (str && str.toLowerCase() === 'да') return true; else return 'Удаление не подтверждено'; },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result && result.value && result.value.toLowerCase() == 'да') {
|
|
||||||
if (this.profiles[this.currentProfile]) {
|
|
||||||
const newProfiles = Object.assign({}, this.profiles);
|
|
||||||
delete newProfiles[this.currentProfile];
|
|
||||||
this.commit('reader/setAllowProfilesSave', true);
|
|
||||||
await this.$nextTick();//ждем обработчики watch
|
|
||||||
this.commit('reader/setProfiles', newProfiles);
|
|
||||||
await this.$nextTick();//ждем обработчики watch
|
|
||||||
this.commit('reader/setAllowProfilesSave', false);
|
|
||||||
this.currentProfile = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async delAllProfiles() {
|
|
||||||
if (!Object.keys(this.profiles).length)
|
|
||||||
return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = await this.$root.stdDialog.prompt(`<b>Предупреждение!</b> Удаление ВСЕХ профилей с настройками необратимо.` +
|
|
||||||
`<br><br>Введите 'да' для подтверждения удаления:`, ' ', {
|
|
||||||
inputValidator: (str) => { if (str && str.toLowerCase() === 'да') return true; else return 'Удаление не подтверждено'; },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result && result.value && result.value.toLowerCase() == 'да') {
|
|
||||||
this.commit('reader/setAllowProfilesSave', true);
|
|
||||||
await this.$nextTick();//ждем обработчики watch
|
|
||||||
this.commit('reader/setProfiles', {});
|
|
||||||
await this.$nextTick();//ждем обработчики watch
|
|
||||||
this.commit('reader/setAllowProfilesSave', false);
|
|
||||||
this.currentProfile = '';
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async copyToClip(text, prefix) {
|
|
||||||
const result = await utils.copyTextToClipboard(text);
|
|
||||||
const suf = (prefix.substr(-1) == 'а' ? 'а' : '');
|
|
||||||
const msg = (result ? `${prefix} успешно скопирован${suf} в буфер обмена` : 'Копирование не удалось');
|
|
||||||
if (result)
|
|
||||||
this.$root.notify.success(msg);
|
|
||||||
else
|
|
||||||
this.$root.notify.error(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
async showServerStorageKey() {
|
|
||||||
this.serverStorageKeyVisible = !this.serverStorageKeyVisible;
|
|
||||||
}
|
|
||||||
|
|
||||||
async enterServerStorageKey(key) {
|
|
||||||
try {
|
|
||||||
const result = await this.$root.stdDialog.prompt(`<b>Предупреждение!</b> Изменение ключа доступа приведет к замене всех профилей и читаемых книг в читалке.` +
|
|
||||||
`<br><br>Введите новый ключ доступа:`, ' ', {
|
|
||||||
inputValidator: (str) => {
|
|
||||||
try {
|
|
||||||
if (str && utils.fromBase58(str).length == 32) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
return 'Неверный формат ключа';
|
|
||||||
},
|
|
||||||
inputValue: (key && _.isString(key) ? key : null),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result && result.value && utils.fromBase58(result.value).length == 32) {
|
|
||||||
this.commit('reader/setServerStorageKey', result.value);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async generateServerStorageKey() {
|
|
||||||
try {
|
|
||||||
const result = await this.$root.stdDialog.prompt(`<b>Предупреждение!</b> Генерация нового ключа доступа приведет к удалению всех профилей и читаемых книг в читалке.` +
|
|
||||||
`<br><br>Введите 'да' для подтверждения генерации нового ключа:`, ' ', {
|
|
||||||
inputValidator: (str) => { if (str && str.toLowerCase() === 'да') return true; else return 'Генерация не подтверждена'; },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result && result.value && result.value.toLowerCase() == 'да') {
|
|
||||||
if (this.$root.generateNewServerStorageKey)
|
|
||||||
this.$root.generateNewServerStorageKey();
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
loadWallpaperFileClick() {
|
loadWallpaperFileClick() {
|
||||||
this.$refs.file.click();
|
this.$refs.file.click();
|
||||||
}
|
}
|
||||||
@@ -719,7 +549,7 @@ export default vueComponent(SettingsPage);
|
|||||||
padding: 0 10px 15px 10px;
|
padding: 0 10px 15px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.label-1, .label-3, .label-7 {
|
.label-7 {
|
||||||
width: 75px;
|
width: 75px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -731,23 +561,6 @@ export default vueComponent(SettingsPage);
|
|||||||
width: 100px;
|
width: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
|
||||||
font-size: 90%;
|
|
||||||
line-height: 130%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
margin: 3px 15px 3px 0;
|
|
||||||
padding: 0 5px 0 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.copy-icon {
|
|
||||||
margin-left: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 120%;
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
max-width: 150px;
|
max-width: 150px;
|
||||||
}
|
}
|
||||||
@@ -786,4 +599,8 @@ export default vueComponent(SettingsPage);
|
|||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sets-button {
|
||||||
|
margin: 3px 15px 3px 0;
|
||||||
|
padding: 0 5px 0 5px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
<div class="part-header">Отображение</div>
|
|
||||||
|
|
||||||
<div class="item row no-wrap">
|
|
||||||
<div class="label-3"></div>
|
|
||||||
<q-checkbox size="xs" v-model="toolBarHideOnScroll" label="Скрывать/показывать панель при прокрутке" >
|
|
||||||
<q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
|
|
||||||
Скрывать/показывть панель при прокрутке текста вперед/назад
|
|
||||||
</q-tooltip>
|
|
||||||
</q-checkbox>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="part-header">Показывать кнопки</div>
|
|
||||||
|
|
||||||
<div class="item row no-wrap" v-for="item in toolButtons" :key="item.name" v-show="item.name != 'libs' || mode == 'liberama.top'">
|
|
||||||
<div class="label-3"></div>
|
|
||||||
<q-checkbox size="xs" v-model="showToolButton[item.name]" :label="rstore.readerActions[item.name]"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
Reference in New Issue
Block a user