diff --git a/.gitignore b/.gitignore index 11254ed0..8d9e48f6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -/node_modules -/server/data -/server/public -/server/ipfs -/dist +/node_modules +/server/.liberama* +/dist +dev*.sh + diff --git a/README.md b/README.md index 674324f7..f8a2b627 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,156 @@ # Liberama -Браузерная онлайн-читалка книг и децентрализованная библиотека. +Браузерная онлайн-читалка книг. -Читалка [OmniReader](https://omnireader.ru) является частью данного проекта, размещенной на VPS: +Выглядит соледующим образом: [OmniReader](https://omnireader.ru) ![](docs/assets/face.jpg) ![](docs/assets/reader.jpg) +При запуске приложения, по умолчанию веб-сервер доступен по адресу [http://127.0.0.1:44080](http://127.0.0.1:44080) + +Для указания местоположения рабочей директории, воспользуйтесь [параметрами командной строки](#cli). +Дополнительные параметры сервера настраиваются в [конфигурационном файле](#config). + +[Отблагодарить автора проекта](https://donatty.com/liberama) + +## +* [Возможности читалки](#capabilities) +* [Использование](#usage) + * [Параметры командной строки](#cli) + * [Конфигурация](#config) + * [Разворачивание на VPS](#vps) +* [Сборка проекта](#build) +* [Разработка](#development) + + + +## Возможности читалки +- загрузка любой страницы интернета +- синхронизация данных (настроек и читаемых книг) между различными устройствами +- работа в автономном режиме (без связи) +- изменение цвета фона, текста, размер и тип шрифта и прочее +- установка и запоминание текущей позиции и настроек в браузере и на сервере +- кэширование файлов книг на клиенте и на сервере +- открытие книг с локального диска +- плавный скроллинг текста +- анимация перелистывания +- поиск по тексту и копирование фрагмента +- запоминание недавних книг, скачивание книги из читалки в формате fb2 +- управление кликом и с клавиатуры +- регистрация не требуется +- поддерживаемые браузеры: Google Chrome, Mozilla Firefox последних версий +- релизы сервера под Linux, MacOS и Windows + + + +## Использование +Приложение представляет собой полноценный веб-сервер в виде единого исполнимого файла. +При первом запуске, будет создана рабочая директория `.liberama` (по умолчанию - в той же папке, где исполнимый файл), +в которой хранится конфигурационный файл `config.json`, файлы веб-приложения, файлы базы данных, журналы и прочее. +Изменить рабочую директорию можно с помощью cli-параметра --app-dir + +По умолчанию веб-интерфейс будет доступен по адресу [http://127.0.0.1:44080](http://127.0.0.1:44080) + + + +### Параметры командной строки +Запустите `liberama --help`, чтобы увидеть список опций: +```console +Usage: liberama [options] + +Options: + --help Показать опции командной строки + --app-dir= Задать рабочую директорию, по умолчанию: /.liberama + --auto-repair Починить БД приложения при запуске, если она повреждена +``` + + + +### Конфигурация +При первом запуске в рабочей директории будет создан конфигурационный файл `config.json`: +```js +{ + // Максимальный размер файла загружаемой книги (в байтах) + "maxUploadFileSize": 52428800, + + // Максимальный размер каталога /public-files/tmp для хранения конвертированных + // файлов книг пользователей (в байтах) + "maxTempPublicDirSize": 536870912, + + // Максимальный размер каталога /public-files/upload для хранения + // загруженных в /upload (кнопка "Загрузить файл с диска") файлов книг пользователей (в байтах) + "maxUploadPublicDirSize": 209715200, + + // Использование внешних конвертеров (только в среде Linux) + // Без них читалка может работать только с файлами формата fb2, txt, html, xml + // Инструкции установки внешних конвертеров см. в docs/omnireader.ru/README.md + "useExternalBookConverter": false, + + // Настройки для списка серверов. + // Приложение может запускать одновременно несколько веб-серверов на разных портах + "servers": [ + { + // Произвольное название сервера + "serverName": "1", + + // Режим работы сервера: + // "reader" - обычная читалка + // "omnireader" - модификации для сайта omnireader.ru + // "liberama" - модификации для сайта liberama.top + // "book_update_checker" - сервер обновлений + "mode": "reader", + + // Хост, порт сервера + "ip": "0.0.0.0", + "port": "44080" + } + ], + + // Настройки удаленного хранилища + "remoteStorage": false, + + // Для веб-приложения: включение/выключение работы с сервером обновлений + "bucEnabled": false, + + // Подключение себя, как клиента, к серверу обновлений + "bucServer": false +} +``` + +При необходимости, можно настроить нужный параметр в этом файле вручную. + + + ## VPS Для разворачивания читалки на чистом VPS с нуля смотрите [docs/omnireader.ru](docs/omnireader.ru/README.md) -## Сборка проекта -Необходима версия node.js не ниже 14. + -``` -$ git clone https://github.com/bookpauk/liberama -$ cd liberama -$ npm i +### Сборка проекта +Сборка только в среде Linux. +Необходима версия node.js не ниже 16. + +Для сборки linux-arm64 необходимо предварительно установить [QEMU](https://wiki.debian.org/QemuUserEmulation). + +```sh +git clone https://github.com/bookpauk/liberama +cd liberama +npm i ``` -### Windows -``` -$ npm run build:win +#### Релизы +```sh +npm run release ``` -### Linux -``` -$ npm run build:linux -``` +Результат сборки будет доступен в каталоге `dist/release` -Результат сборки будет доступен в каталоге `dist/linux|win` в виде исполнимого (standalone) файла + ### Разработка -``` -$ npm run dev +```sh +npm run dev ``` -## Помочь проекту - -* bitcoin: bc1q3tyumaj648pp2e69jalsez2lnt462ttc33nup9 -* litecoin: MP39Riec4oSNB3XMjiquKoLWxbufRYNXxZ -* monero: 8BQPnvHcPSHM5gMQsmuypDgx9NNsYqwXKfDDuswEyF2Q2ewQSfd2pkK6ydH2wmMyq2JViZvy9DQ35hLMx7g72mFWNJTPtnz +Связаться с автором проекта: [bookpauk@gmail.com](mailto:bookpauk@gmail.com) diff --git a/build/includer.js b/build/includer.js deleted file mode 100644 index eff3e452..00000000 --- a/build/includer.js +++ /dev/null @@ -1,31 +0,0 @@ -const path = require('path'); -const fs = require('fs'); - -//пример в коде: -// @@include('./test/testFile.inc'); - -function includeRecursive(self, parentFile, source, depth) { - depth = (depth ? depth : 0); - if (depth > 50) - throw new Error('includer: stack too big'); - const lines = source.split('\n'); - let result = []; - for (const line of lines) { - const trimmed = line.trim(); - const m = trimmed.match(/^@@[\s]*?include[\s]*?\(['"](.*)['"]\)/); - if (m) { - const includedFile = path.resolve(path.dirname(parentFile), m[1]); - self.addDependency(includedFile); - - const fileContent = fs.readFileSync(includedFile, 'utf8'); - result = result.concat(includeRecursive(self, includedFile, fileContent, depth + 1)); - } else { - result.push(line); - } - } - return result; -} - -exports.default = function includer(source) { - return includeRecursive(this, this.resourcePath, source).join('\n'); -} \ No newline at end of file diff --git a/build/linux.js b/build/linux.js deleted file mode 100644 index 5abdf1bf..00000000 --- a/build/linux.js +++ /dev/null @@ -1,51 +0,0 @@ -const fs = require('fs-extra'); -const path = require('path'); -const util = require('util'); -const stream = require('stream'); -const pipeline = util.promisify(stream.pipeline); - -const axios = require('axios'); -const FileDecompressor = require('../server/core/FileDecompressor'); - -const distDir = path.resolve(__dirname, '../dist'); -const publicDir = `${distDir}/tmp/public`; -const outDir = `${distDir}/linux`; - -const tempDownloadDir = `${distDir}/tmp/download`; - -async function main() { - const decomp = new FileDecompressor(); - - await fs.emptyDir(outDir); - // перемещаем public на место - if (await fs.pathExists(publicDir)) - await fs.move(publicDir, `${outDir}/public`); - - await fs.ensureDir(tempDownloadDir); - - //ipfs - const ipfsDecompressedFilename = `${tempDownloadDir}/go-ipfs/ipfs`; - if (!await fs.pathExists(ipfsDecompressedFilename)) { - // Скачиваем ipfs - const ipfsRemoteUrl = 'https://dist.ipfs.io/go-ipfs/v0.4.18/go-ipfs_v0.4.18_linux-amd64.tar.gz'; - - const res = await axios.get(ipfsRemoteUrl, {responseType: 'stream'}) - await pipeline(res.data, fs.createWriteStream(`${tempDownloadDir}/ipfs.tar.gz`)); - console.log(`done downloading ${ipfsRemoteUrl}`); - - //распаковываем - console.log(await decomp.unpackTarZZ(`${tempDownloadDir}/ipfs.tar.gz`, tempDownloadDir)); - console.log('files decompressed'); - } - - // копируем в дистрибутив - await fs.copy(ipfsDecompressedFilename, `${outDir}/ipfs`); - console.log(`copied ${tempDownloadDir}/go-ipfs/ipfs to ${outDir}/ipfs`); - //для development - const devIpfsFile = path.resolve(__dirname, '../server/ipfs'); - if (!await fs.pathExists(devIpfsFile)) { - await fs.copy(ipfsDecompressedFilename, devIpfsFile); - } -} - -main(); diff --git a/build/prepkg.js b/build/prepkg.js new file mode 100644 index 00000000..ea915758 --- /dev/null +++ b/build/prepkg.js @@ -0,0 +1,51 @@ +const fs = require('fs-extra'); +const path = require('path'); +const { execSync } = require('child_process'); + +const showdown = require('showdown'); + +const platform = process.argv[2]; + +const distDir = path.resolve(__dirname, '../dist'); +const tmpDir = `${distDir}/tmp`; +const publicDir = `${tmpDir}/public`; +const outDir = `${distDir}/${platform}`; + +async function build() { + if (!platform) + throw new Error(`Please set platform`); + + await fs.emptyDir(outDir); + + //добавляем readme в релиз + let readme = await fs.readFile(path.resolve(__dirname, '../README.md'), 'utf-8'); + const converter = new showdown.Converter(); + readme = converter.makeHtml(readme); + await fs.writeFile(`${outDir}/readme.html`, readme); + + // перемещаем public на место + if (await fs.pathExists(publicDir)) { + + const zipFile = `${tmpDir}/public.zip`; + const jsonFile = `${distDir}/public.json`;//distDir !!! + + await fs.remove(zipFile); + execSync(`zip -r ${zipFile} .`, {cwd: publicDir, stdio: 'inherit'}); + + const data = (await fs.readFile(zipFile)).toString('base64'); + await fs.writeFile(jsonFile, JSON.stringify({data})); + } else { + throw new Error(`publicDir: ${publicDir} does not exist`); + } +} + +async function main() { + try { + await build(); + } catch(e) { + console.error(e); + process.exit(1); + } +} + +main(); diff --git a/build/release.js b/build/release.js new file mode 100644 index 00000000..85839d1d --- /dev/null +++ b/build/release.js @@ -0,0 +1,33 @@ +const fs = require('fs-extra'); +const path = require('path'); +const { execSync } = require('child_process'); + +const pckg = require('../package.json'); + +const distDir = path.resolve(__dirname, '../dist'); +const outDir = `${distDir}/release`; + +async function makeRelease(target) { + const srcDir = `${distDir}/${target}`; + + if (await fs.pathExists(srcDir)) { + const zipFile = `${outDir}/${pckg.name}-${pckg.version}-${target}.zip`; + + execSync(`zip -r ${zipFile} .`, {cwd: srcDir, stdio: 'inherit'}); + } +} + +async function main() { + try { + await fs.emptyDir(outDir); + await makeRelease('win'); + await makeRelease('linux'); + await makeRelease('linux-arm64'); + await makeRelease('macos'); + } catch(e) { + console.error(e); + process.exit(1); + } +} + +main(); diff --git a/build/webpack.base.config.js b/build/webpack.base.config.js index a2d6e82d..90a876c2 100644 --- a/build/webpack.base.config.js +++ b/build/webpack.base.config.js @@ -30,10 +30,6 @@ module.exports = { } }*/ }, - { - resourceQuery: /^\?vue/, - use: path.resolve(__dirname, 'includer.js') - }, { test: /\.js$/, loader: 'babel-loader', diff --git a/build/webpack.dev.config.js b/build/webpack.dev.config.js index 088f3cf5..19556b5c 100644 --- a/build/webpack.dev.config.js +++ b/build/webpack.dev.config.js @@ -1,5 +1,6 @@ const path = require('path'); const webpack = require('webpack'); +const pckg = require('../package.json'); const { merge } = require('webpack-merge'); const baseWpConfig = require('./webpack.base.config'); @@ -8,16 +9,15 @@ baseWpConfig.entry.unshift('webpack-hot-middleware/client'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); -const publicDir = path.resolve(__dirname, '../server/public'); +const publicDir = path.resolve(__dirname, `../server/.${pckg.name}/public`); const clientDir = path.resolve(__dirname, '../client'); module.exports = merge(baseWpConfig, { mode: 'development', devtool: 'inline-source-map', output: { - path: `${publicDir}/app`, + path: `${publicDir}${baseWpConfig.output.publicPath}`, filename: 'bundle.js', - clean: true }, module: { diff --git a/build/webpack.prod.config.js b/build/webpack.prod.config.js index 2ee142b2..fa63de06 100644 --- a/build/webpack.prod.config.js +++ b/build/webpack.prod.config.js @@ -17,9 +17,8 @@ const clientDir = path.resolve(__dirname, '../client'); module.exports = merge(baseWpConfig, { mode: 'production', output: { - path: `${publicDir}/app_new`, + path: `${publicDir}${baseWpConfig.output.publicPath}`, filename: 'bundle.[contenthash].js', - clean: true }, module: { rules: [ diff --git a/build/win.js b/build/win.js deleted file mode 100644 index 016e236b..00000000 --- a/build/win.js +++ /dev/null @@ -1,45 +0,0 @@ -const fs = require('fs-extra'); -const path = require('path'); -const util = require('util'); -const stream = require('stream'); -const pipeline = util.promisify(stream.pipeline); - -const axios = require('axios'); -const FileDecompressor = require('../server/core/FileDecompressor'); - -const distDir = path.resolve(__dirname, '../dist'); -const publicDir = `${distDir}/tmp/public`; -const outDir = `${distDir}/win`; - -const tempDownloadDir = `${distDir}/tmp/download`; - -async function main() { - const decomp = new FileDecompressor(); - - await fs.emptyDir(outDir); - // перемещаем public на место - if (await fs.pathExists(publicDir)) - await fs.move(publicDir, `${outDir}/public`); - - await fs.ensureDir(tempDownloadDir); - - //ipfs - const ipfsDecompressedFilename = `${tempDownloadDir}/go-ipfs/ipfs.exe`; - if (!await fs.pathExists(ipfsDecompressedFilename)) { - // Скачиваем ipfs - const ipfsRemoteUrl = 'https://dist.ipfs.io/go-ipfs/v0.4.18/go-ipfs_v0.4.18_windows-amd64.zip'; - - const res = await axios.get(ipfsRemoteUrl, {responseType: 'stream'}) - await pipeline(res.data, fs.createWriteStream(`${tempDownloadDir}/ipfs.zip`)); - console.log(`done downloading ${ipfsRemoteUrl}`); - - //распаковываем - console.log(await decomp.unpack(`${tempDownloadDir}/ipfs.zip`, tempDownloadDir)); - console.log('files decompressed'); - } - // копируем в дистрибутив - await fs.copy(ipfsDecompressedFilename, `${outDir}/ipfs.exe`); - console.log(`copied ${ipfsDecompressedFilename} to ${outDir}/ipfs.exe`); -} - -main(); \ No newline at end of file diff --git a/client/api/misc.js b/client/api/misc.js index 141fd3cf..e3c4b3fb 100644 --- a/client/api/misc.js +++ b/client/api/misc.js @@ -1,10 +1,5 @@ -import axios from 'axios'; import wsc from './webSocketConnection'; -const api = axios.create({ - baseURL: '/api' -}); - class Misc { async loadConfig() { @@ -12,18 +7,11 @@ class Misc { 'name', 'version', 'mode', 'maxUploadFileSize', 'useExternalBookConverter', 'acceptFileExt', 'bucEnabled', 'branch', ]}; - try { - const config = await wsc.message(await wsc.send(Object.assign({action: 'get-config'}, query))); - if (config.error) - throw new Error(config.error); - return config; - } catch (e) { - console.error(e); - } + const config = await wsc.message(await wsc.send(Object.assign({action: 'get-config'}, query))); + if (config.error) + throw new Error(config.error); - //если с WebSocket проблема, работаем по http - const response = await api.post('/config', query); - return response.data; + return config; } } diff --git a/client/api/reader.js b/client/api/reader.js index 1cf1339e..0beb0e07 100644 --- a/client/api/reader.js +++ b/client/api/reader.js @@ -7,9 +7,9 @@ const api = axios.create({ baseURL: '/api/reader' }); -const workerApi = axios.create({ +/*const workerApi = axios.create({ baseURL: '/api/worker' -}); +});*/ class Reader { constructor() { @@ -19,58 +19,24 @@ class Reader { if (!callback) callback = () => {}; let response = {}; - try { - const requestId = await wsc.send({action: 'worker-get-state-finish', workerId}); + const requestId = await wsc.send({action: 'worker-get-state-finish', workerId}); - let prevResponse = false; - while (1) {// eslint-disable-line no-constant-condition - response = await wsc.message(requestId); - - if (!response.state && prevResponse !== false) {//экономия траффика - callback(prevResponse); - } else {//были изменения worker state - if (!response.state) - throw new Error('Неверный ответ api'); - callback(response); - prevResponse = response; - } - - if (response.state == 'finish' || response.state == 'error') { - break; - } - } - return response; - } catch (e) { - console.error(e); - } - - //если с WebSocket проблема, работаем по http - const refreshPause = 500; - let i = 0; - response = {}; + let prevResponse = false; while (1) {// eslint-disable-line no-constant-condition - const prevProgress = response.progress || 0; - const prevState = response.state || 0; - response = await workerApi.post('/get-state', {workerId}); - response = response.data; - callback(response); + response = await wsc.message(requestId); - if (!response.state) - throw new Error('Неверный ответ api'); + if (!response.state && prevResponse !== false) {//экономия траффика + callback(prevResponse); + } else {//были изменения worker state + if (!response.state) + throw new Error('Неверный ответ api'); + callback(response); + prevResponse = response; + } if (response.state == 'finish' || response.state == 'error') { break; } - - if (i > 0) - await utils.sleep(refreshPause); - - i++; - if (i > 180*1000/refreshPause) {//3 мин ждем телодвижений воркера - throw new Error('Слишком долгое время ожидания'); - } - //проверка воркера - i = (prevProgress != response.progress || prevState != response.state ? 1 : i); } return response; @@ -79,14 +45,13 @@ class Reader { async loadBook(opts, callback) { if (!callback) callback = () => {}; - let response = await api.post('/load-book', opts); - - const workerId = response.data.workerId; + let response = await wsc.message(await wsc.send(Object.assign({action: 'load-book'}, opts))); + const workerId = response.workerId; if (!workerId) throw new Error('Неверный ответ api'); callback({totalSteps: 4}); - callback(response.data); + callback(response); response = await this.getWorkerStateFinish(workerId, callback); @@ -181,22 +146,13 @@ class Reader { } async storage(request) { - let response = null; - try { - response = await wsc.message(await wsc.send({action: 'reader-storage', body: request})); - } catch (e) { - console.error(e); - //если с WebSocket проблема, работаем по http - response = await api.post('/storage', request); - response = response.data; - } + const response = await wsc.message(await wsc.send({action: 'reader-storage', body: request})); - const state = response.state; - if (!state) - throw new Error('Неверный ответ api'); - if (state == 'error') { + if (response.error) throw new Error(response.error); - } + + if (!response.state) + throw new Error('Неверный ответ api'); return response; } diff --git a/client/components/App.vue b/client/components/App.vue index 5cd5aa22..5d8a4311 100644 --- a/client/components/App.vue +++ b/client/components/App.vue @@ -39,16 +39,6 @@ class App { _options = componentOptions; showPage = false; - itemRuText = { - '/cardindex': 'Картотека', - '/reader': 'Читалка', - '/forum': 'Форум-чат', - '/income': 'Поступления', - '/sources': 'Источники', - '/settings': 'Параметры', - '/help': 'Справка', - }; - created() { this.commit = this.$store.commit; this.state = this.$store.state; @@ -130,7 +120,7 @@ class App { this.setAppTitle(); (async() => { - //загрузим конфиг сревера + //загрузим конфиг сервера try { const config = await miscApi.loadConfig(); this.commit('config/setConfig', config); @@ -197,12 +187,12 @@ class App { setAppTitle(title) { if (!title) { - if (this.mode == 'liberama.top') { + if (this.mode == 'liberama') { document.title = `Liberama Reader - всегда с вами`; } else if (this.mode == 'omnireader') { document.title = `Omni Reader - всегда с вами`; } else if (this.config && this.mode !== null) { - document.title = `${this.config.name} - ${this.itemRuText[this.rootRoute]}`; + document.title = `Универсальная читалка книг и ресурсов интернета`; } } else { document.title = title; @@ -217,19 +207,12 @@ class App { return this.$store.state.config.mode; } - get showAsideBar() { - return (this.mode !== null && this.mode != 'reader' && this.mode != 'omnireader' && this.mode != 'liberama.top'); - } - - set showAsideBar(value) { - } - get isReaderActive() { return (this.rootRoute == '/reader' || this.rootRoute == '/external-libs'); } redirectIfNeeded() { - if ((this.mode == 'reader' || this.mode == 'omnireader' || this.mode == 'liberama.top')) { + if ((this.mode == 'reader' || this.mode == 'omnireader' || this.mode == 'liberama')) { const search = window.location.search.substr(1); //распознавание параметра url вида "?url=" и редирект при необходимости @@ -271,8 +254,8 @@ body, html, #app { font: normal 12pt ReaderDefault; } -.notify-margin { - margin-top: 55px; +.q-notifications__list--top { + top: 55px !important; } .dborder { diff --git a/client/components/CardIndex/Book/Book.vue b/client/components/CardIndex/Book/Book.vue deleted file mode 100644 index beef252c..00000000 --- a/client/components/CardIndex/Book/Book.vue +++ /dev/null @@ -1,19 +0,0 @@ - - - diff --git a/client/components/CardIndex/Card/Card.vue b/client/components/CardIndex/Card/Card.vue deleted file mode 100644 index 6da789f0..00000000 --- a/client/components/CardIndex/Card/Card.vue +++ /dev/null @@ -1,19 +0,0 @@ - - - diff --git a/client/components/CardIndex/CardIndex.vue b/client/components/CardIndex/CardIndex.vue deleted file mode 100644 index 5bbcfd18..00000000 --- a/client/components/CardIndex/CardIndex.vue +++ /dev/null @@ -1,93 +0,0 @@ - - - - - \ No newline at end of file diff --git a/client/components/CardIndex/History/History.vue b/client/components/CardIndex/History/History.vue deleted file mode 100644 index 9950a8c3..00000000 --- a/client/components/CardIndex/History/History.vue +++ /dev/null @@ -1,19 +0,0 @@ - - - diff --git a/client/components/CardIndex/Search/Search.vue b/client/components/CardIndex/Search/Search.vue deleted file mode 100644 index 1e5072c9..00000000 --- a/client/components/CardIndex/Search/Search.vue +++ /dev/null @@ -1,19 +0,0 @@ - - - diff --git a/client/components/ExternalLibs/BookmarkSettings/BookmarkSettings.vue b/client/components/ExternalLibs/BookmarkSettings/BookmarkSettings.vue index b43ac51c..162d1b66 100644 --- a/client/components/ExternalLibs/BookmarkSettings/BookmarkSettings.vue +++ b/client/components/ExternalLibs/BookmarkSettings/BookmarkSettings.vue @@ -347,6 +347,7 @@ export default vueComponent(BookmarkSettings); padding: 0px 10px 10px 10px; overflow-x: auto; overflow-y: auto; + max-width: 520px; } .selected { diff --git a/client/components/ExternalLibs/ExternalLibs.vue b/client/components/ExternalLibs/ExternalLibs.vue index aa2ddf7f..56881541 100644 --- a/client/components/ExternalLibs/ExternalLibs.vue +++ b/client/components/ExternalLibs/ExternalLibs.vue @@ -110,7 +110,7 @@
- +
@@ -304,6 +304,10 @@ class ExternalLibs { openInFrameOnAdd = false; frameScale = 1; + inpxReady = false; + inpxTitle = ''; + inpxUrl = ''; + created() { this.oldStartLink = ''; this.justOpened = true; @@ -321,8 +325,6 @@ class ExternalLibs { this.debouncedGoToLink = _.debounce((link) => { this.goToLink(link); }, 100, {'maxWait':200}); - //this.commit = this.$store.commit; - //this.commit('reader/setLibs', rstore.libsDefaults); } mounted() { @@ -334,10 +336,7 @@ class ExternalLibs { i++; } - if (this.mode != 'liberama.top') { - this.$router.replace('/404'); - return; - } + this.libsDefaults = rstore.getLibsDefaults(this.mode); this.$refs.window.init(); @@ -348,17 +347,28 @@ class ExternalLibs { const openerOrigin2 = `https://${openerHost}`; window.addEventListener('message', (event) => { + //from inpx-web + if (_.isObject(event.data) && event.data.from === 'inpx-web') { + //console.log(event); + + this.inpxOrigin = event.origin; + + this.recvInpxMessage(event.data); + return; + } + + //from parent if (event.origin !== openerOrigin1 && event.origin !== openerOrigin2) return; + if (!_.isObject(event.data) || event.data.from != 'LibsPage') return; if (event.origin == openerOrigin1) this.opener = window.opener; else this.opener = event.source; - this.openerOrigin = event.origin; - //console.log(event); + this.openerOrigin = event.origin; this.recvMessage(event.data); }); @@ -389,7 +399,8 @@ class ExternalLibs { } } else if (d.type == 'libs') { this.ready = true; - this.libs = _.cloneDeep(d.data); + if (d.data) + this.libs = _.cloneDeep(d.data); } else if (d.type == 'notify') { this.$root.notify.success(d.data, '', {position: 'bottom-right'}); } @@ -403,6 +414,30 @@ class ExternalLibs { })(); } + recvInpxMessage(d) { + if (d.type == 'mes') { + switch(d.data) { + case 'hello-from-inpx-web': + this.sendInpxMessage({type: 'mes', data: 'ready'}); + break; + case 'ready': + this.inpxReady = true; + break; + } + } else if (d.type == 'submitUrl') { + this.submitUrl(d.data); + } else if (d.type == 'titleChange') { + this.inpxTitle = d.data; + } else if (d.type == 'urlChange') { + this.inpxUrl = d.data; + } + } + + sendInpxMessage(d) { + if (this.$refs.frame && this.inpxOrigin) + this.$refs.frame.contentWindow.postMessage(Object.assign({}, {from: 'ExternalLibs'}, d), this.inpxOrigin); + } + async checkOpener() { if (this.opener.closed) { await this.$root.stdDialog.alert('Потеряна связь с читалкой. Окно будет закрыто', 'Ошибка'); @@ -461,7 +496,10 @@ class ExternalLibs { get header() { let result = (this.ready ? 'Сетевая библиотека' : 'Загрузка...'); if (this.ready && this.selectedLink) { - result += ` | ${(this.libs.comment ? this.libs.comment + ' ': '') + lu.removeProtocol(this.libs.startLink)}`; + let title = `${(this.libs.comment ? this.libs.comment + ' ': '') + lu.removeProtocol(this.libs.startLink)}`; + if (this.inpxReady && this.inpxTitle) + title = `${this.inpxTitle} ${lu.removeProtocol(this.inpxUrl)}`; + result += ` | ${title}`; } this.$root.setAppTitle(result); return result; @@ -532,7 +570,7 @@ class ExternalLibs { get defaultRootLinkOptions() { let result = []; - rstore.libsDefaults.groups.forEach(group => { + this.libsDefaults.groups.forEach(group => { result.push({label: lu.removeProtocol(group.r), value: group.r}); }); @@ -561,6 +599,11 @@ class ExternalLibs { } goToLink(link) { + this.inpxReady = false; + this.inpxTitle = ''; + this.inpxUrl = ''; + this.inpxOrigin = false; + if (!this.ready || !link) return; @@ -576,6 +619,7 @@ class ExternalLibs { this.frameVisible = true; this.$nextTick(() => { if (this.$refs.frame) { + this.$refs.frame.contentWindow.location.reload(true); this.$refs.frame.contentWindow.focus(); this.frameResize(); } @@ -648,13 +692,17 @@ class ExternalLibs { this.updateStartLink(true); } - submitUrl() { - if (this.bookUrl) { + submitUrl(url) { + if (!url) { + url = this.bookUrl; + this.bookUrl = ''; + } + + if (url) { this.sendMessage({type: 'submitUrl', data: { - url: this.bookUrl, + url, force: true }}); - this.bookUrl = ''; if (this.closeAfterSubmit) this.close(); } @@ -668,6 +716,12 @@ class ExternalLibs { } else { this.bookmarkLink = this.bookUrl; this.bookmarkDesc = ''; + + if (!this.bookmarkLink && this.inpxReady && this.inpxUrl) { + this.bookmarkLink = this.inpxUrl; + if (this.inpxTitle) + this.bookmarkDesc = this.inpxTitle; + } } this.addBookmarkMode = mode; @@ -679,10 +733,10 @@ class ExternalLibs { } updateBookmarkLink() { - const index = lu.getSafeRootIndexByUrl(rstore.libsDefaults.groups, this.defaultRootLink); + const index = lu.getSafeRootIndexByUrl(this.libsDefaults.groups, this.defaultRootLink); if (index >= 0) { - this.bookmarkLink = rstore.libsDefaults.groups[index].s; - this.bookmarkDesc = this.getCommentByLink(rstore.libsDefaults.groups[index].list, this.bookmarkLink); + this.bookmarkLink = this.libsDefaults.groups[index].s; + this.bookmarkDesc = this.getCommentByLink(this.libsDefaults.groups[index].list, this.bookmarkLink); } else { this.bookmarkLink = ''; this.bookmarkDesc = ''; @@ -837,20 +891,22 @@ class ExternalLibs {

Окно 'Сетевая библиотека' позволяет открывать ссылки в читалке без переключения между окнами, что особенно актуально для мобильных устройств. Имеется возможность управлять закладками на понравившиеся ресурсы, книги или страницы авторов. Открытие ссылок и навигация происходят во фрейме, но, -к сожалению, в нем открываются не все страницы.

+к сожалению, в нем открываются не все страницы.

` + -

Доступ к сайтам http://flibusta.is и http://fantasy-worlds.org работает через прокси. +(this.mode === 'liberama' ? +`

Доступ к сайтам http://flibusta.is и http://fantasy-worlds.org работает через прокси.
ПРЕДУПРЕЖДЕНИЕ! Доступ предназначен только для просмотра и скачивания книг. Авторизоваться на этих сайтах из фрейма категорически не рекомендуется, т.к. ваше подключение не защищено и данные могут попасть к третьим лицам.

+` +: '') + -

Из-за проблем с безопасностью, навигация 'вперед-назад' во фрейме осуществляется с помощью контекстного меню правой кнопкой мыши. +`

Из-за проблем с безопасностью, навигация 'вперед-назад' во фрейме осуществляется с помощью контекстного меню правой кнопкой мыши. На мобильных устройствах для этого служит системная клавиша 'Назад (стрелка влево)' и опция 'Вперед (стрелка вправо)' в меню браузера.

-

Приятного пользования ;-)

`, 'Справка', {iconName: 'la la-info-circle'}); diff --git a/client/components/Help/Help.vue b/client/components/Help/Help.vue deleted file mode 100644 index de1da849..00000000 --- a/client/components/Help/Help.vue +++ /dev/null @@ -1,19 +0,0 @@ - - - diff --git a/client/components/Income/Income.vue b/client/components/Income/Income.vue deleted file mode 100644 index 737aeb59..00000000 --- a/client/components/Income/Income.vue +++ /dev/null @@ -1,19 +0,0 @@ - - - diff --git a/client/components/NotFound404/NotFound404.vue b/client/components/NotFound404/NotFound404.vue deleted file mode 100644 index 336348d1..00000000 --- a/client/components/NotFound404/NotFound404.vue +++ /dev/null @@ -1,19 +0,0 @@ - - - diff --git a/client/components/Reader/HelpPage/CommonHelpPage/CommonHelpPage.vue b/client/components/Reader/HelpPage/CommonHelpPage/CommonHelpPage.vue index d518ce75..4aebb906 100644 --- a/client/components/Reader/HelpPage/CommonHelpPage/CommonHelpPage.vue +++ b/client/components/Reader/HelpPage/CommonHelpPage/CommonHelpPage.vue @@ -24,7 +24,7 @@

Поддерживаемые форматы: fb2, fb2.zip, html, txt и другие.

-
+

Вы можете добавить в свой браузер закладку, указав в ее свойствах вместо адреса следующий код:
{{ bookmarkText }} diff --git a/client/components/Reader/HelpPage/DonateHelpPage/assets/bitcoin.png b/client/components/Reader/HelpPage/DonateHelpPage/assets/bitcoin.png deleted file mode 100644 index e80ad6d1..00000000 Binary files a/client/components/Reader/HelpPage/DonateHelpPage/assets/bitcoin.png and /dev/null differ diff --git a/client/components/Reader/HelpPage/DonateHelpPage/assets/litecoin.png b/client/components/Reader/HelpPage/DonateHelpPage/assets/litecoin.png deleted file mode 100644 index 900e9b96..00000000 Binary files a/client/components/Reader/HelpPage/DonateHelpPage/assets/litecoin.png and /dev/null differ diff --git a/client/components/Reader/HelpPage/DonateHelpPage/assets/monero.png b/client/components/Reader/HelpPage/DonateHelpPage/assets/monero.png deleted file mode 100644 index a5112aea..00000000 Binary files a/client/components/Reader/HelpPage/DonateHelpPage/assets/monero.png and /dev/null differ diff --git a/client/components/Reader/HelpPage/HotkeysHelpPage/HotkeysHelpPage.vue b/client/components/Reader/HelpPage/HotkeysHelpPage/HotkeysHelpPage.vue index ff0f541e..733b7beb 100644 --- a/client/components/Reader/HelpPage/HotkeysHelpPage/HotkeysHelpPage.vue +++ b/client/components/Reader/HelpPage/HotkeysHelpPage/HotkeysHelpPage.vue @@ -19,7 +19,7 @@ //----------------------------------------------------------------------------- import vueComponent from '../../../vueComponent.js'; -import UserHotKeys from '../../SettingsPage/UserHotKeys/UserHotKeys.vue'; +import UserHotKeys from '../../SettingsPage/KeysTab/UserHotKeys/UserHotKeys.vue'; const componentOptions = { components: { diff --git a/client/components/Reader/HelpPage/MouseHelpPage/MouseHelpPage.vue b/client/components/Reader/HelpPage/MouseHelpPage/MouseHelpPage.vue index 3e819403..17be9055 100644 --- a/client/components/Reader/HelpPage/MouseHelpPage/MouseHelpPage.vue +++ b/client/components/Reader/HelpPage/MouseHelpPage/MouseHelpPage.vue @@ -13,7 +13,7 @@

  • Жесты для тачскрина: