mirror of
https://github.com/koodo-reader/koodo-reader.git
synced 2026-06-18 21:00:35 -04:00
fix bug
Former-commit-id: 1c0fa07b90b199638e40d714d6d25ce935abf593
This commit is contained in:
@@ -4,8 +4,8 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div align="center" width="128px" height="128px">
|
||||
<img src="https://i.loli.net/2020/04/26/wrO8EPokvUQWaf5.png" />
|
||||
<div align="center" >
|
||||
<img src="./assets/icons/256x256.png" width="96px" height="96px"/>
|
||||
</div>
|
||||
|
||||
<h1 align="center">
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
## Feature
|
||||
|
||||
Format support: **epub**, **pdf**, **mobi**, **azw3**, **txt**, **markdown**, **djvu**, **docx**, **rtf**, **cbz**, **cbr**, **cbt**, **fb2**, **html** and **xml**
|
||||
Format support: **epub**, **pdf**, **mobi**, **azw3**, **txt**, **md**, **djvu**, **docx**, **rtf**, **cbz**, **cbr**, **cbt**, **fb2**, **html** and **xml**
|
||||
|
||||
Platform support: **Windows** , **macOS**, **Linux** and **Web**
|
||||
|
||||
@@ -52,7 +52,7 @@ Origanize your books and notes with shelf and tag
|
||||
|
||||
Adjust font size, font family, line-spacing, paragraph spacing, background color, text color, margins, and brightness
|
||||
|
||||
Night mode and Theme color
|
||||
Night mode and theme color
|
||||
|
||||
Text highlight, underline, boldness, italics and shadow
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div align="center" width="128px" height="128px">
|
||||
<img src="https://i.loli.net/2020/04/26/wrO8EPokvUQWaf5.png" />
|
||||
<div align="center">
|
||||
<img src="./assets/icons/256x256.png" width="96px" height="96px"/>
|
||||
</div>
|
||||
|
||||
<h1 align="center">
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
## 特色
|
||||
|
||||
支持阅读 **epub**, **pdf**, **mobi**, **azw3**, **txt**, **markdown**, **djvu**, **docx**, **rtf**, **cbz**, **cbr**, **cbt**, **fb2**, **html** 和 **xml** 格式的图书
|
||||
支持阅读 **epub**, **pdf**, **mobi**, **azw3**, **txt**, **md**, **djvu**, **docx**, **rtf**, **cbz**, **cbr**, **cbt**, **fb2**, **html** 和 **xml** 格式的图书
|
||||
|
||||
支持 **Windows**,**macOS**,**Linux** 和 **网页版**
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div align="center" width="128px" height="128px">
|
||||
<img src="https://i.loli.net/2020/04/26/wrO8EPokvUQWaf5.png" />
|
||||
<div align="center" >
|
||||
<img src="./assets/icons/256x256.png" width="96px" height="96px"/>
|
||||
</div>
|
||||
|
||||
<h1 align="center">
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
## 特色
|
||||
|
||||
支持閱讀 **epub**, **pdf**, **mobi**, **azw3**, **txt**, **markdown**, **djvu**, **docx**, **rtf**, **cbz**, **cbr**, **cbt**, **fb2**, **html** 和 **xml** 格式的圖書
|
||||
支持閱讀 **epub**, **pdf**, **mobi**, **azw3**, **txt**, **md**, **djvu**, **docx**, **rtf**, **cbz**, **cbr**, **cbt**, **fb2**, **html** 和 **xml** 格式的圖書
|
||||
|
||||
支持 **Windows**,**macOS**,**Linux** 和**網頁版**
|
||||
|
||||
|
||||
10
appveyor.yml
10
appveyor.yml
@@ -7,20 +7,14 @@ branches:
|
||||
image:
|
||||
- Visual Studio 2019
|
||||
- macos
|
||||
- Ubuntu
|
||||
- Ubuntu2004
|
||||
|
||||
stack: node 10
|
||||
|
||||
cache:
|
||||
- node_modules
|
||||
- '%APPDATA%\npm-cache'
|
||||
- '%USERPROFILE%\.electron'
|
||||
- '%USERPROFILE%\AppData\Local\Yarn\cache'
|
||||
|
||||
init:
|
||||
- git config --global core.autocrlf input
|
||||
- sh: |
|
||||
if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" == "Ubuntu" ]
|
||||
if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" == "Ubuntu2004" ]
|
||||
then
|
||||
sudo apt-get install --no-install-recommends -y icnsutils graphicsmagick xz-utils
|
||||
sudo apt-get install snapd
|
||||
|
||||
14
main.js
14
main.js
@@ -1,4 +1,4 @@
|
||||
const { app, BrowserWindow, dialog, shell } = require("electron");
|
||||
const { app, BrowserWindow } = require("electron");
|
||||
const { ebtMain } = require("electron-baidu-tongji");
|
||||
|
||||
let mainWin;
|
||||
@@ -112,16 +112,10 @@ app.on("ready", () => {
|
||||
readerWindow = null;
|
||||
});
|
||||
});
|
||||
ipcMain.on("fonts-ready", (event, arg) => {
|
||||
ipcMain.handle("fonts-ready", async (event, arg) => {
|
||||
const fontList = require("font-list");
|
||||
fontList
|
||||
.getFonts({ disableQuoting: true })
|
||||
.then((fonts) => {
|
||||
event.returnValue = fonts;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
const fonts = await fontList.getFonts({ disableQuoting: true });
|
||||
return fonts;
|
||||
});
|
||||
|
||||
ipcMain.on("storage-location", (event, arg) => {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"copy-text-to-clipboard": "^2.2.0",
|
||||
"electron-baidu-tongji": "^1.0.5",
|
||||
"electron-is-dev": "^1.1.0",
|
||||
"font-list": "^1.2.15",
|
||||
"font-list": "1.2.15",
|
||||
"fs-extra": "^9.1.0",
|
||||
"i18next": "^20.2.4",
|
||||
"iconv-lite": "^0.6.2",
|
||||
|
||||
@@ -167,7 +167,8 @@ textarea::-ms-input-placeholder,
|
||||
.message-box-container {
|
||||
box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
::-webkit-scrollbar {
|
||||
::-webkit-scrollbar,
|
||||
.tag-list-item {
|
||||
background-color: white;
|
||||
}
|
||||
.add-bookmark-button,
|
||||
|
||||
@@ -20,133 +20,12 @@
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.loading-cover {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
.logo-container {
|
||||
width: 200px;
|
||||
}
|
||||
.loading-cover img {
|
||||
width: 80px;
|
||||
position: relative;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-left: -40px;
|
||||
margin-top: -80px;
|
||||
}
|
||||
.cover-spinner {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-left: -15px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
.sk-chase {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
animation: sk-chase 2.5s infinite linear both;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.sk-chase-dot {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
animation: sk-chase-dot 2s infinite ease-in-out both;
|
||||
}
|
||||
|
||||
.sk-chase-dot:before {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 25%;
|
||||
height: 25%;
|
||||
border-radius: 100%;
|
||||
animation: sk-chase-dot-before 2s infinite ease-in-out both;
|
||||
}
|
||||
|
||||
.sk-chase-dot:nth-child(1) {
|
||||
animation-delay: -1.1s;
|
||||
}
|
||||
.sk-chase-dot:nth-child(2) {
|
||||
animation-delay: -1s;
|
||||
}
|
||||
.sk-chase-dot:nth-child(3) {
|
||||
animation-delay: -0.9s;
|
||||
}
|
||||
.sk-chase-dot:nth-child(4) {
|
||||
animation-delay: -0.8s;
|
||||
}
|
||||
.sk-chase-dot:nth-child(5) {
|
||||
animation-delay: -0.7s;
|
||||
}
|
||||
.sk-chase-dot:nth-child(6) {
|
||||
animation-delay: -0.6s;
|
||||
}
|
||||
.sk-chase-dot:nth-child(1):before {
|
||||
animation-delay: -1.1s;
|
||||
}
|
||||
.sk-chase-dot:nth-child(2):before {
|
||||
animation-delay: -1s;
|
||||
}
|
||||
.sk-chase-dot:nth-child(3):before {
|
||||
animation-delay: -0.9s;
|
||||
}
|
||||
.sk-chase-dot:nth-child(4):before {
|
||||
animation-delay: -0.8s;
|
||||
}
|
||||
.sk-chase-dot:nth-child(5):before {
|
||||
animation-delay: -0.7s;
|
||||
}
|
||||
.sk-chase-dot:nth-child(6):before {
|
||||
animation-delay: -0.6s;
|
||||
}
|
||||
|
||||
@keyframes sk-chase {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes sk-chase-dot {
|
||||
80%,
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes sk-chase-dot-before {
|
||||
50% {
|
||||
transform: scale(0.4);
|
||||
}
|
||||
100%,
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<title>Koodo Reader</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div class="loading-cover">
|
||||
<img src="./favicon.png" alt="logo" class="loading-logo" />
|
||||
<div class="cover-spinner">
|
||||
<div class="sk-chase">
|
||||
<div class="sk-chase-dot"></div>
|
||||
<div class="sk-chase-dot"></div>
|
||||
<div class="sk-chase-dot"></div>
|
||||
<div class="sk-chase-dot"></div>
|
||||
<div class="sk-chase-dot"></div>
|
||||
<div class="sk-chase-dot"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="root"></div>
|
||||
<script>
|
||||
const style = document.createElement("link");
|
||||
|
||||
@@ -274,7 +274,7 @@
|
||||
"Collapse sidebar": "收起侧边栏",
|
||||
"Sync Successfully": "同步成功",
|
||||
"Show sidebar": "打开侧边栏",
|
||||
"File Size": "文件大小",
|
||||
"File size": "文件大小",
|
||||
"Take effect at next startup": "下次打开图书生效",
|
||||
"Use the fonts from your local computer": "支持设置电脑中的字体",
|
||||
"Backup your data with Webdav": "支持 Webdav 备份方式",
|
||||
|
||||
@@ -247,7 +247,7 @@
|
||||
"Turn on night mode": "Turn on night mode",
|
||||
"Turn on auto update": "Turn on auto update",
|
||||
"Auto Update relys on Github Release for package hosting, if your internet doesn't have stable connection to Github, we highly recommand you to turn off this option": "Auto Update relys on Github Release for package hosting, if your internet doesn't have stable connection to Github, we highly recommand you to turn off this option",
|
||||
"File Size": "File Size",
|
||||
"File size": "File size",
|
||||
"Take effect at next startup": "Take effect at next startup",
|
||||
"View Mode": "View Mode",
|
||||
"Download Desktop Version": "Download Desktop Version",
|
||||
|
||||
@@ -265,7 +265,7 @@
|
||||
"Tips": "Tips",
|
||||
"Turn on auto update": "Turn on auto update",
|
||||
"Brightness": "Brightness",
|
||||
"File Size": "File Size",
|
||||
"File size": "File size",
|
||||
"Take effect at next startup": "Take effect at next startup",
|
||||
"Invert color": "Invert color",
|
||||
|
||||
|
||||
@@ -219,75 +219,75 @@
|
||||
"Cover Mode": "封面模式",
|
||||
"Update to": "更新至",
|
||||
"Update Complete": "更新成功",
|
||||
"Changelog": "更新日志",
|
||||
"Download": "前往下载",
|
||||
"Theme Color": "选择主题色",
|
||||
"The deleted books will show up here": "被删除的图书会出现在这里,清空回收站才会彻底删除",
|
||||
"Disable update notification": "暂停接收更新",
|
||||
"Use the fonts from your local computer": "使用电脑中的字体",
|
||||
"Backup your data with Webdav": "使用 Webdav 来备份和恢复数据",
|
||||
"More formats supported": "支持更多的图书格式,mobi、azw3、txt",
|
||||
"Download Desktop Version": "下载客户端",
|
||||
"Koodo Reader's web version are limited by the browser, for more powerful features, please download the desktop version.": "网页版功能受浏览器限制,部分功能仅客户端支持",
|
||||
"Do you want to backup or restore?": "选择您的操作",
|
||||
"Where is your data?": "选择资料来源",
|
||||
"Where to keep your data?": "选择资料存放位置",
|
||||
"Not supported yet": "暂不支持",
|
||||
"Unauthorize": "取消授权",
|
||||
"Unauthorize Successfully": "取消授权成功",
|
||||
"Change storage location": "更改存储位置",
|
||||
"Changelog": "更新日誌",
|
||||
"Download": "前往下載",
|
||||
"Theme Color": "選擇主題色",
|
||||
"The deleted books will show up here": "被刪除的圖書會出現在這裏,清空回收站才會徹底刪除",
|
||||
"Disable update notification": "暫停接收更新",
|
||||
"Use the fonts from your local computer": "使用電腦中的字體",
|
||||
"Backup your data with Webdav": "使用 Webdav 來備份和恢復數據",
|
||||
"More formats supported": "支持更多的圖書格式,mobi、azw3、txt",
|
||||
"Download Desktop Version": "下載客戶端",
|
||||
"Koodo Reader's web version are limited by the browser, for more powerful features, please download the desktop version.": "網頁版功能受瀏覽器限製,部分功能僅客戶端支持",
|
||||
"Do you want to backup or restore?": "選擇您的操作",
|
||||
"Where is your data?": "選擇資料來源",
|
||||
"Where to keep your data?": "選擇資料存放位置",
|
||||
"Not supported yet": "暫不支持",
|
||||
"Unauthorize": "取消授權",
|
||||
"Unauthorize Successfully": "取消授權成功",
|
||||
"Change storage location": "更改存儲位置",
|
||||
"Change location": "更改",
|
||||
"Change Successfully": "更改成功",
|
||||
"Change Failed": "更改失败",
|
||||
"Change Failed": "更改失敗",
|
||||
"You successfully update to": "您已成功更新到",
|
||||
"Built-in font": "默认字体",
|
||||
"View Mode": "视图模式",
|
||||
"Letter Spacing": "字间距",
|
||||
"Built-in font": "默認字體",
|
||||
"View Mode": "視圖模式",
|
||||
"Letter Spacing": "字間距",
|
||||
"Voice": "音色",
|
||||
"Unlock": "解锁",
|
||||
"Lock": "锁定",
|
||||
"Speed": "语速",
|
||||
"Collapse sidebar": "收起侧边栏",
|
||||
"Show sidebar": "打开侧边栏",
|
||||
"Unlock": "解鎖",
|
||||
"Lock": "鎖定",
|
||||
"Speed": "語速",
|
||||
"Collapse sidebar": "收起側邊欄",
|
||||
"Show sidebar": "打開側邊欄",
|
||||
"Sync": "同步",
|
||||
"Help": "需要帮助",
|
||||
"Feedback": "反馈建议",
|
||||
"Default search engine": "默认搜索引擎",
|
||||
"Github Repo": "Github 仓库",
|
||||
"Turn on auto update": "开启自动更新",
|
||||
"Text Align": "文字对齐",
|
||||
"left": "左对齐",
|
||||
"justify": "两端对齐",
|
||||
"right": "右对齐",
|
||||
"Help": "需要幫助",
|
||||
"Feedback": "反饋建議",
|
||||
"Default search engine": "默認搜索引擎",
|
||||
"Github Repo": "Github 倉庫",
|
||||
"Turn on auto update": "開啟自動更新",
|
||||
"Text Align": "文字對齊",
|
||||
"left": "左對齊",
|
||||
"justify": "兩端對齊",
|
||||
"right": "右對齊",
|
||||
"Understand": "我知道了",
|
||||
|
||||
"Sync Successfully": "同步成功",
|
||||
"Permanently Delete": "永久删除",
|
||||
"Roadmap": "开发计划",
|
||||
"Paragraph Spacing": "段落间距",
|
||||
"Italic": "斜体",
|
||||
"Text Underline": "下划线",
|
||||
"Text Shadow": "文字阴影",
|
||||
"Speak the text": "朗读文字",
|
||||
"Try refresh or restart": "重启软件后生效",
|
||||
"Search on the internet": "网上搜索",
|
||||
"Search in the book": "全书搜索",
|
||||
"Remember window's size from last read": "记忆阅读器窗口大小",
|
||||
"Blue": "蓝色",
|
||||
"Customize": "自定义",
|
||||
"Red": "红色",
|
||||
"Permanently Delete": "永久刪除",
|
||||
"Roadmap": "開發計劃",
|
||||
"Paragraph Spacing": "段落間距",
|
||||
"Italic": "斜體",
|
||||
"Text Underline": "下劃線",
|
||||
"Text Shadow": "文字陰影",
|
||||
"Speak the text": "朗讀文字",
|
||||
"Try refresh or restart": "重啟軟件後生效",
|
||||
"Search on the internet": "網上搜索",
|
||||
"Search in the book": "全書搜索",
|
||||
"Remember window's size from last read": "記憶閱讀器窗口大小",
|
||||
"Blue": "藍色",
|
||||
"Customize": "自定義",
|
||||
"Red": "紅色",
|
||||
"Brightness": "屏幕亮度",
|
||||
"Green": "绿色",
|
||||
"Green": "綠色",
|
||||
"Purple": "紫色",
|
||||
"Auto open book in fullscreen": "图书窗口自动最大化",
|
||||
"System Font": "系统字体",
|
||||
"Auto open book in fullscreen": "圖書窗口自動最大化",
|
||||
"System Font": "系統字體",
|
||||
"Tips": "提示",
|
||||
"How sync works": "同步是如何实现的?",
|
||||
"You need to manually change the storage location to the same sync folder on different computers. When you click the sync button, Koodo Reader will automatically upload or download the data from this folder according the timestamp.": "同步功能需要配合第三方同步盘实现,在不同电脑上把数据存储位置修改为同一个同步文件夹,手动点击同步按钮后,Koodo会把同步文件夹中的数据更新到软件中,从而实现同步。",
|
||||
"Please choose an empty folder": "请选择空文件夹",
|
||||
"Data change detected, whether to update?": "检测到数据变化,是否更新",
|
||||
"File Size": "文件大小",
|
||||
"Take effect at next startup": "下次打开图书生效",
|
||||
"Auto Update relys on Github Release for package hosting, if your internet doesn't have stable connection to Github, we highly recommand you to turn off this option": "自动更新功能依赖Github,如果您本地网络无法稳定访问Github,则不建议开启此功能,开启之后,将不再单独提供更新提醒。",
|
||||
"Bold Font": "字体加粗"
|
||||
"How sync works": "同步是如何實現的?",
|
||||
"You need to manually change the storage location to the same sync folder on different computers. When you click the sync button, Koodo Reader will automatically upload or download the data from this folder according the timestamp.": "同步功能需要配合第三方同步盤實現,在不同電腦上把數據存儲位置修改為同一個同步文件夾,手動點擊同步按鈕後,Koodo會把同步文件夾中的數據更新到軟件中,從而實現同步。",
|
||||
"Please choose an empty folder": "請選擇空文件夾",
|
||||
"Data change detected, whether to update?": "檢測到數據變化,是否更新",
|
||||
"File size": "文件大小",
|
||||
"Take effect at next startup": "下次打開圖書生效",
|
||||
"Auto Update relys on Github Release for package hosting, if your internet doesn't have stable connection to Github, we highly recommand you to turn off this option": "自動更新功能依賴Github,如果您本地網絡無法穩定訪問Github,則不建議開啟此功能,開啟之後,將不再單獨提供更新提醒。",
|
||||
"Bold Font": "字體加粗"
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
width: 105px;
|
||||
height: 137px;
|
||||
opacity: 1;
|
||||
margin: 10px 15px 5px 15px;
|
||||
margin: 15px 15px 5px 15px;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -26,12 +26,12 @@ class BookCardItem extends React.Component<BookCardProps, BookCardState> {
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
async componentDidMount() {
|
||||
let filePath = "";
|
||||
//控制是否自动打开本书
|
||||
if (isElectron) {
|
||||
const { ipcRenderer } = window.require("electron");
|
||||
filePath = ipcRenderer.sendSync("get-file-data");
|
||||
filePath = await ipcRenderer.sendSync("get-file-data");
|
||||
}
|
||||
|
||||
if (
|
||||
|
||||
@@ -157,10 +157,13 @@ class ActionDialog extends React.Component<ActionDialogProps> {
|
||||
</div>
|
||||
<div>
|
||||
<p className="action-dialog-book-publisher">
|
||||
<Trans>File Size</Trans>:
|
||||
<Trans>File size</Trans>:
|
||||
</p>
|
||||
<p className="action-dialog-book-title">
|
||||
{parseInt(this.props.currentBook.size / 1024 + "")} Kb
|
||||
{this.props.currentBook.size
|
||||
? parseInt(this.props.currentBook.size / 1024 + "") + "Kb"
|
||||
: // eslint-disable-next-line
|
||||
"0" + "Kb"}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
z-index: 10;
|
||||
animation: popup 0.1s ease-in-out 0s 1;
|
||||
}
|
||||
.backup-close-icon {
|
||||
margin-top: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
@keyframes popup {
|
||||
0% {
|
||||
opacity: 0;
|
||||
|
||||
@@ -275,7 +275,7 @@ class BackupDialog extends React.Component<
|
||||
this.handleClose();
|
||||
}}
|
||||
>
|
||||
<span className="icon-close "></span>
|
||||
<span className="icon-close backup-close-icon"></span>
|
||||
</div>
|
||||
|
||||
{this.state.currentStep === 1 ? (
|
||||
|
||||
@@ -163,7 +163,7 @@ class SettingDialog extends React.Component<
|
||||
this.state.isDisplayDark ? "no" : "yes"
|
||||
);
|
||||
if (isElectron) {
|
||||
this.props.handleMessage("Take effect at next startup");
|
||||
this.props.handleMessage("Try refresh or restart");
|
||||
this.props.handleMessageBox(true);
|
||||
} else {
|
||||
window.location.reload();
|
||||
@@ -174,7 +174,7 @@ class SettingDialog extends React.Component<
|
||||
this.setState({ currentThemeIndex: index });
|
||||
OtherUtil.setReaderConfig("themeColor", name);
|
||||
if (isElectron) {
|
||||
this.props.handleMessage("Take effect at next startup");
|
||||
this.props.handleMessage("Try refresh or restart");
|
||||
this.props.handleMessageBox(true);
|
||||
} else {
|
||||
window.location.reload();
|
||||
|
||||
@@ -160,13 +160,10 @@ class PopupMenu extends React.Component<PopupMenuProps, PopupMenuStates> {
|
||||
renderHighlighters = () => {
|
||||
let highlighters: any = this.props.notes;
|
||||
if (!highlighters) return;
|
||||
if (
|
||||
!this.props.currentEpub ||
|
||||
!this.props.currentEpub.rendition.currentLocation
|
||||
) {
|
||||
if (!this.props.rendition || !this.props.rendition.currentLocation) {
|
||||
return;
|
||||
}
|
||||
const currentLocation = this.props.currentEpub.rendition.currentLocation();
|
||||
const currentLocation = this.props.rendition.currentLocation();
|
||||
if (!currentLocation || !currentLocation.start) return;
|
||||
let chapterIndex = currentLocation.start.index;
|
||||
let highlightersByChapter = highlighters.filter(
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
readerSettingList,
|
||||
htmlSettingList,
|
||||
} from "../../../constants/settingList";
|
||||
import { isElectron } from "react-device-detect";
|
||||
|
||||
class SettingSwitch extends React.Component<
|
||||
SettingSwitchProps,
|
||||
@@ -29,6 +30,14 @@ class SettingSwitch extends React.Component<
|
||||
handleRest = () => {
|
||||
this.props.renderFunc();
|
||||
};
|
||||
_handleRest = () => {
|
||||
if (isElectron) {
|
||||
this.props.handleMessage("Take effect at next startup");
|
||||
this.props.handleMessageBox(true);
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
};
|
||||
handleBold = () => {
|
||||
this.setState({ isBold: !this.state.isBold }, () => {
|
||||
OtherUtil.setReaderConfig("isBold", this.state.isBold ? "yes" : "no");
|
||||
@@ -83,7 +92,7 @@ class SettingSwitch extends React.Component<
|
||||
: this.props.handleMessage("Turn On Successfully");
|
||||
this.props.handleMessageBox(true);
|
||||
setTimeout(() => {
|
||||
this.handleRest();
|
||||
this._handleRest();
|
||||
}, 500);
|
||||
};
|
||||
handleFooter = () => {
|
||||
@@ -97,7 +106,7 @@ class SettingSwitch extends React.Component<
|
||||
: this.props.handleMessage("Turn Off Successfully");
|
||||
this.props.handleMessageBox(true);
|
||||
setTimeout(() => {
|
||||
this.handleRest();
|
||||
this._handleRest();
|
||||
}, 500);
|
||||
};
|
||||
handleHeader = () => {
|
||||
@@ -111,14 +120,14 @@ class SettingSwitch extends React.Component<
|
||||
: this.props.handleMessage("Turn Off Successfully");
|
||||
this.props.handleMessageBox(true);
|
||||
setTimeout(() => {
|
||||
this.handleRest();
|
||||
this._handleRest();
|
||||
}, 500);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
{this.props.currentEpub && <TextToSpeech />}
|
||||
{this.props.currentEpub.archived && <TextToSpeech />}
|
||||
{(this.props.currentEpub.rendition
|
||||
? readerSettingList
|
||||
: htmlSettingList
|
||||
|
||||
@@ -25,7 +25,7 @@ class SliderList extends React.Component<SliderListProps, SliderListState> {
|
||||
};
|
||||
}
|
||||
handleRest = () => {
|
||||
if (this.props.mode === "scale") {
|
||||
if (this.props.mode === "scale" || this.props.mode === "margin") {
|
||||
if (isElectron) {
|
||||
this.props.handleMessage("Take effect at next startup");
|
||||
this.props.handleMessageBox(true);
|
||||
|
||||
@@ -103,10 +103,14 @@ class TextToSpeech extends React.Component<
|
||||
msg.voice = window.speechSynthesis.getVoices()[voiceIndex];
|
||||
msg.rate = speed;
|
||||
window.speechSynthesis.speak(msg);
|
||||
msg.onerror = (err) => {
|
||||
console.log(err);
|
||||
};
|
||||
msg.onend = (event) => {
|
||||
if (!(this.state.isAudioOn && this.props.isReading)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.currentEpub.rendition.next().then(() => {
|
||||
this.handleAudio();
|
||||
});
|
||||
|
||||
@@ -19,11 +19,11 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
constructor(props: ReaderProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isOpenSettingPanel:
|
||||
isOpenRightPanel:
|
||||
OtherUtil.getReaderConfig("isSettingLocked") === "yes" ? true : false,
|
||||
isOpenOperationPanel: false,
|
||||
isOpenProgressPanel: false,
|
||||
isOpenNavPanel:
|
||||
isOpenTopPanel: false,
|
||||
isOpenBottomPanel: false,
|
||||
isOpenLeftPanel:
|
||||
OtherUtil.getReaderConfig("isNavLocked") === "yes" ? true : false,
|
||||
isMessage: false,
|
||||
rendition: null,
|
||||
@@ -64,9 +64,10 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
handleRenderBook = () => {
|
||||
let page = document.querySelector("#page-area");
|
||||
let epub = this.props.currentEpub;
|
||||
if (page?.innerHTML) {
|
||||
page.innerHTML = "";
|
||||
if (page!.getElementsByTagName("*").length > 0) {
|
||||
page!.innerHTML = "";
|
||||
}
|
||||
|
||||
this.setState({ rendition: null }, () => {
|
||||
(window as any).rangy.init(); // 初始化
|
||||
this.rendition = epub.renderTo(page, {
|
||||
@@ -84,38 +85,52 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
this.tickTimer = setInterval(() => {
|
||||
let time = this.state.time;
|
||||
time += 1;
|
||||
let page = document.querySelector("#page-area");
|
||||
//解决快速翻页过程中图书消失的bug
|
||||
let renderedBook = document.querySelector(".epub-view");
|
||||
if (renderedBook && !renderedBook.innerHTML) {
|
||||
if (
|
||||
(renderedBook &&
|
||||
!renderedBook.innerHTML &&
|
||||
this.state.readerMode !== "continuous") ||
|
||||
page!.getElementsByClassName("epub-container").length > 1
|
||||
) {
|
||||
this.handleRenderBook();
|
||||
}
|
||||
this.setState({ time });
|
||||
this.handleRecord();
|
||||
}, 1000);
|
||||
});
|
||||
};
|
||||
handleRecord() {
|
||||
OtherUtil.setReaderConfig("isFullScreen", "no");
|
||||
|
||||
OtherUtil.setReaderConfig("windowWidth", document.body.clientWidth + "");
|
||||
OtherUtil.setReaderConfig("windowHeight", document.body.clientHeight + "");
|
||||
OtherUtil.setReaderConfig("windowX", window.screenX + "");
|
||||
OtherUtil.setReaderConfig("windowY", window.screenY + "");
|
||||
}
|
||||
//进入阅读器
|
||||
handleEnterReader = (position: string) => {
|
||||
//控制上下左右的菜单的显示
|
||||
switch (position) {
|
||||
case "right":
|
||||
this.setState({
|
||||
isOpenSettingPanel: this.state.isOpenSettingPanel ? false : true,
|
||||
isOpenRightPanel: this.state.isOpenRightPanel ? false : true,
|
||||
});
|
||||
break;
|
||||
case "left":
|
||||
this.setState({
|
||||
isOpenNavPanel: this.state.isOpenNavPanel ? false : true,
|
||||
isOpenLeftPanel: this.state.isOpenLeftPanel ? false : true,
|
||||
});
|
||||
break;
|
||||
case "top":
|
||||
this.setState({
|
||||
isOpenOperationPanel: this.state.isOpenOperationPanel ? false : true,
|
||||
isOpenTopPanel: this.state.isOpenTopPanel ? false : true,
|
||||
});
|
||||
break;
|
||||
case "bottom":
|
||||
this.setState({
|
||||
isOpenProgressPanel: this.state.isOpenProgressPanel ? false : true,
|
||||
isOpenBottomPanel: this.state.isOpenBottomPanel ? false : true,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
@@ -130,7 +145,7 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
if (OtherUtil.getReaderConfig("isSettingLocked") === "yes") {
|
||||
break;
|
||||
} else {
|
||||
this.setState({ isOpenSettingPanel: false });
|
||||
this.setState({ isOpenRightPanel: false });
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -138,14 +153,14 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
if (OtherUtil.getReaderConfig("isNavLocked") === "yes") {
|
||||
break;
|
||||
} else {
|
||||
this.setState({ isOpenNavPanel: false });
|
||||
this.setState({ isOpenLeftPanel: false });
|
||||
break;
|
||||
}
|
||||
case "top":
|
||||
this.setState({ isOpenOperationPanel: false });
|
||||
this.setState({ isOpenTopPanel: false });
|
||||
break;
|
||||
case "bottom":
|
||||
this.setState({ isOpenProgressPanel: false });
|
||||
this.setState({ isOpenBottomPanel: false });
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -163,10 +178,10 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
handleLeaveReader: this.handleLeaveReader,
|
||||
handleEnterReader: this.handleEnterReader,
|
||||
isShow:
|
||||
this.state.isOpenNavPanel ||
|
||||
this.state.isOpenOperationPanel ||
|
||||
this.state.isOpenProgressPanel ||
|
||||
this.state.isOpenSettingPanel,
|
||||
this.state.isOpenLeftPanel ||
|
||||
this.state.isOpenTopPanel ||
|
||||
this.state.isOpenBottomPanel ||
|
||||
this.state.isOpenRightPanel,
|
||||
};
|
||||
return (
|
||||
<div
|
||||
@@ -210,7 +225,7 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
<div
|
||||
className="left-panel"
|
||||
onMouseEnter={() => {
|
||||
if (this.state.isTouch || this.state.isOpenNavPanel) {
|
||||
if (this.state.isTouch || this.state.isOpenLeftPanel) {
|
||||
return;
|
||||
}
|
||||
this.handleEnterReader("left");
|
||||
@@ -222,7 +237,7 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
<div
|
||||
className="right-panel"
|
||||
onMouseEnter={() => {
|
||||
if (this.state.isTouch || this.state.isOpenSettingPanel) {
|
||||
if (this.state.isTouch || this.state.isOpenRightPanel) {
|
||||
return;
|
||||
}
|
||||
this.handleEnterReader("right");
|
||||
@@ -234,7 +249,7 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
<div
|
||||
className="top-panel"
|
||||
onMouseEnter={() => {
|
||||
if (this.state.isTouch || this.state.isOpenOperationPanel) {
|
||||
if (this.state.isTouch || this.state.isOpenTopPanel) {
|
||||
return;
|
||||
}
|
||||
this.handleEnterReader("top");
|
||||
@@ -246,7 +261,7 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
<div
|
||||
className="bottom-panel"
|
||||
onMouseEnter={() => {
|
||||
if (this.state.isTouch || this.state.isOpenProgressPanel) {
|
||||
if (this.state.isTouch || this.state.isOpenBottomPanel) {
|
||||
return;
|
||||
}
|
||||
this.handleEnterReader("bottom");
|
||||
@@ -265,7 +280,7 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
this.handleLeaveReader("right");
|
||||
}}
|
||||
style={
|
||||
this.state.isOpenSettingPanel
|
||||
this.state.isOpenRightPanel
|
||||
? {}
|
||||
: {
|
||||
transform: "translateX(309px)",
|
||||
@@ -280,7 +295,7 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
this.handleLeaveReader("left");
|
||||
}}
|
||||
style={
|
||||
this.state.isOpenNavPanel
|
||||
this.state.isOpenLeftPanel
|
||||
? {}
|
||||
: {
|
||||
transform: "translateX(-309px)",
|
||||
@@ -295,7 +310,7 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
this.handleLeaveReader("bottom");
|
||||
}}
|
||||
style={
|
||||
this.state.isOpenProgressPanel
|
||||
this.state.isOpenBottomPanel
|
||||
? {}
|
||||
: {
|
||||
transform: "translateY(110px)",
|
||||
@@ -310,7 +325,7 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
this.handleLeaveReader("top");
|
||||
}}
|
||||
style={
|
||||
this.state.isOpenOperationPanel
|
||||
this.state.isOpenTopPanel
|
||||
? {}
|
||||
: {
|
||||
transform: "translateY(-110px)",
|
||||
|
||||
@@ -13,10 +13,10 @@ export interface ReaderProps {
|
||||
}
|
||||
|
||||
export interface ReaderState {
|
||||
isOpenSettingPanel: boolean;
|
||||
isOpenOperationPanel: boolean;
|
||||
isOpenProgressPanel: boolean;
|
||||
isOpenNavPanel: boolean;
|
||||
isOpenRightPanel: boolean;
|
||||
isOpenTopPanel: boolean;
|
||||
isOpenBottomPanel: boolean;
|
||||
isOpenLeftPanel: boolean;
|
||||
isMessage: boolean;
|
||||
isTouch: boolean;
|
||||
readerMode: string;
|
||||
|
||||
@@ -24,7 +24,6 @@ class Header extends React.Component<HeaderProps, HeaderState> {
|
||||
};
|
||||
}
|
||||
componentDidMount() {
|
||||
console.log(localStorage.getItem("lastSyncTime"), "localStorage1");
|
||||
if (isElectron) {
|
||||
const fs = window.require("fs");
|
||||
const path = window.require("path");
|
||||
@@ -80,7 +79,6 @@ class Header extends React.Component<HeaderProps, HeaderState> {
|
||||
return;
|
||||
}
|
||||
const readerConfig = JSON.parse(data);
|
||||
console.log(localStorage.getItem("lastSyncTime"), "localStorage2");
|
||||
if (
|
||||
localStorage.getItem("lastSyncTime") &&
|
||||
parseInt(readerConfig.lastSyncTime) >
|
||||
@@ -89,20 +87,6 @@ class Header extends React.Component<HeaderProps, HeaderState> {
|
||||
this.setState({ isdataChange: true });
|
||||
}
|
||||
});
|
||||
// try {
|
||||
// const readerConfig = JSON.parse(
|
||||
// fs.readFileSync(sourcePath, { encoding: "utf8", flag: "r" })
|
||||
// );
|
||||
// if (
|
||||
// localStorage.getItem("lastSyncTime") &&
|
||||
// parseInt(readerConfig.lastSyncTime) >
|
||||
// parseInt(localStorage.getItem("lastSyncTime")!)
|
||||
// ) {
|
||||
// this.setState({ isdataChange: true });
|
||||
// }
|
||||
// } catch (error) {
|
||||
// throw error;
|
||||
// }
|
||||
}
|
||||
|
||||
window.addEventListener("resize", () => {
|
||||
@@ -113,15 +97,12 @@ class Header extends React.Component<HeaderProps, HeaderState> {
|
||||
syncFromLocation = async () => {
|
||||
const fs = window.require("fs");
|
||||
const path = window.require("path");
|
||||
const { zip } = window.require("zip-a-folder");
|
||||
let storageLocation = localStorage.getItem("storageLocation")
|
||||
? localStorage.getItem("storageLocation")
|
||||
: window
|
||||
.require("electron")
|
||||
.ipcRenderer.sendSync("storage-location", "ping");
|
||||
let sourcePath = path.join(storageLocation, "config");
|
||||
let outPath = path.join(storageLocation, "config.zip");
|
||||
await zip(sourcePath, outPath);
|
||||
|
||||
var data = fs.readFileSync(outPath);
|
||||
|
||||
@@ -130,22 +111,36 @@ class Header extends React.Component<HeaderProps, HeaderState> {
|
||||
lastModified: new Date().getTime(),
|
||||
type: blobTemp.type,
|
||||
});
|
||||
|
||||
console.log(fileTemp);
|
||||
RestoreUtil.restore(
|
||||
fileTemp,
|
||||
() => {
|
||||
BackupUtil.backup(
|
||||
this.props.books,
|
||||
this.props.notes,
|
||||
this.props.bookmarks,
|
||||
() => {
|
||||
this.props.handleMessage("Sync Successfully");
|
||||
this.props.handleMessageBox(true);
|
||||
this.setState({ isdataChange: false });
|
||||
},
|
||||
5,
|
||||
() => {}
|
||||
this.setState({ isdataChange: false });
|
||||
//Check for data update
|
||||
let storageLocation = localStorage.getItem("storageLocation")
|
||||
? localStorage.getItem("storageLocation")
|
||||
: window
|
||||
.require("electron")
|
||||
.ipcRenderer.sendSync("storage-location", "ping");
|
||||
let sourcePath = path.join(
|
||||
storageLocation,
|
||||
"config",
|
||||
"readerConfig.json"
|
||||
);
|
||||
|
||||
fs.readFile(sourcePath, "utf8", (err, data) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
const readerConfig = JSON.parse(data);
|
||||
if (
|
||||
localStorage.getItem("lastSyncTime") &&
|
||||
readerConfig.lastSyncTime
|
||||
) {
|
||||
localStorage.setItem("lastSyncTime", readerConfig.lastSyncTime);
|
||||
}
|
||||
});
|
||||
},
|
||||
true
|
||||
);
|
||||
@@ -185,11 +180,6 @@ class Header extends React.Component<HeaderProps, HeaderState> {
|
||||
}
|
||||
const readerConfig = JSON.parse(data);
|
||||
|
||||
console.log(
|
||||
localStorage.getItem("lastSyncTime"),
|
||||
readerConfig.lastSyncTime,
|
||||
"localStorage3"
|
||||
);
|
||||
if (
|
||||
readerConfig &&
|
||||
localStorage.getItem("lastSyncTime") &&
|
||||
@@ -212,29 +202,6 @@ class Header extends React.Component<HeaderProps, HeaderState> {
|
||||
);
|
||||
}
|
||||
});
|
||||
//如果同步文件夹的记录较新,就从同步文件夹同步数据到Koodo
|
||||
|
||||
// if (
|
||||
// readerConfig &&
|
||||
// localStorage.getItem("lastSyncTime") &&
|
||||
// parseInt(readerConfig.lastSyncTime) >
|
||||
// parseInt(localStorage.getItem("lastSyncTime")!)
|
||||
// ) {
|
||||
// this.syncFromLocation();
|
||||
// } else {
|
||||
// //否则就把Koodo中数据同步到同步文件夹
|
||||
// BackupUtil.backup(
|
||||
// this.props.books,
|
||||
// this.props.notes,
|
||||
// this.props.bookmarks,
|
||||
// () => {
|
||||
// this.props.handleMessage("Sync Successfully");
|
||||
// this.props.handleMessageBox(true);
|
||||
// },
|
||||
// 5,
|
||||
// () => {}
|
||||
// );
|
||||
// }
|
||||
};
|
||||
|
||||
render() {
|
||||
|
||||
@@ -11,12 +11,13 @@ import marked from "marked";
|
||||
import iconv from "iconv-lite";
|
||||
import chardet from "chardet";
|
||||
import rtfToHTML from "@iarna/rtf-to-html";
|
||||
import { xmlBookTagFilter, xmlBookToObj } from "../../utils/xmlUtil";
|
||||
import { xmlBookTagFilter, xmlBookToObj, txtToHtml } from "../../utils/xmlUtil";
|
||||
import HtmlParser from "../../utils/htmlParser";
|
||||
import OtherUtil from "../../utils/otherUtil";
|
||||
import RecordLocation from "../../utils/readUtils/recordLocation";
|
||||
import { mimetype } from "../../constants/mimetype";
|
||||
import styleUtil from "../../utils/readUtils/styleUtil";
|
||||
import { setInterval } from "timers";
|
||||
|
||||
declare var window: any;
|
||||
|
||||
@@ -60,33 +61,29 @@ class Viewer extends React.Component<ViewerProps, ViewerState> {
|
||||
});
|
||||
});
|
||||
this.props.handleRenderFunc(this.handleRenderHtml);
|
||||
window.frames[0].document.addEventListener("wheel", (event) => {
|
||||
RecordLocation.recordScrollHeight(
|
||||
key,
|
||||
document.body.clientWidth,
|
||||
document.body.clientHeight,
|
||||
window.frames[0].document.scrollingElement!.scrollTop,
|
||||
window.frames[0].document.scrollingElement!.scrollHeight
|
||||
);
|
||||
});
|
||||
setInterval(() => {
|
||||
this.handleRecord();
|
||||
}, 1000);
|
||||
window.frames[0].document.addEventListener("click", (event) => {
|
||||
this.props.handleLeaveReader("left");
|
||||
this.props.handleLeaveReader("right");
|
||||
this.props.handleLeaveReader("top");
|
||||
this.props.handleLeaveReader("bottom");
|
||||
});
|
||||
|
||||
window.onbeforeunload = () => {
|
||||
this.handleExit();
|
||||
};
|
||||
}
|
||||
handleExit() {
|
||||
this.props.handleReadingState(false);
|
||||
|
||||
handleRecord() {
|
||||
OtherUtil.setReaderConfig("windowWidth", document.body.clientWidth + "");
|
||||
OtherUtil.setReaderConfig("windowHeight", document.body.clientHeight + "");
|
||||
OtherUtil.setReaderConfig("windowX", window.screenX + "");
|
||||
OtherUtil.setReaderConfig("windowY", window.screenY + "");
|
||||
RecordLocation.recordScrollHeight(
|
||||
this.state.key,
|
||||
document.body.clientWidth,
|
||||
document.body.clientHeight,
|
||||
window.frames[0].document.scrollingElement!.scrollTop,
|
||||
window.frames[0].document.scrollingElement!.scrollHeight
|
||||
);
|
||||
}
|
||||
handleRest = (docStr: string) => {
|
||||
let htmlParser = new HtmlParser(
|
||||
@@ -119,7 +116,7 @@ class Viewer extends React.Component<ViewerProps, ViewerState> {
|
||||
Buffer.from(result),
|
||||
chardet.detect(Buffer.from(result)) as string
|
||||
);
|
||||
this.handleRest(`<p>${text}</p>`);
|
||||
this.handleRest(txtToHtml(text));
|
||||
};
|
||||
handleMD = (result: ArrayBuffer) => {
|
||||
var blob = new Blob([result], { type: "text/plain" });
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
top: 115px;
|
||||
left: 190px;
|
||||
width: calc(100% - 190px);
|
||||
height: calc(100% - 110px);
|
||||
height: calc(100% - 115px);
|
||||
overflow: visible;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.book-list-container {
|
||||
width: 100%;
|
||||
|
||||
@@ -40,11 +40,7 @@ class ContentList extends React.Component<ContentListProps, ContentListState> {
|
||||
this.props.currentEpub.rendition.display(href);
|
||||
} else {
|
||||
let id = href.substr(1);
|
||||
console.log(
|
||||
id,
|
||||
window.frames[0].document,
|
||||
window.frames[0].document.getElementById(id)
|
||||
);
|
||||
|
||||
var top = window.frames[0].document.getElementById(id)?.offsetTop;
|
||||
if (!top) return;
|
||||
window.frames[0].scrollTo(0, top);
|
||||
|
||||
@@ -238,7 +238,7 @@ class NavigationPanel extends React.Component<
|
||||
|
||||
<Trans>Minute</Trans>
|
||||
</span>
|
||||
{this.props.currentEpub && (
|
||||
{this.props.currentEpub.archived && (
|
||||
<div className="navigation-search-box">
|
||||
<SearchBox {...searchProps} />
|
||||
</div>
|
||||
|
||||
@@ -57,7 +57,7 @@ class SettingPanel extends React.Component<
|
||||
<Trans>Reading Option</Trans>
|
||||
</div>
|
||||
<div className="setting-panel">
|
||||
{this.props.currentEpub && <ModeControl />}
|
||||
{this.props.currentEpub.archived && <ModeControl />}
|
||||
<ThemeList />
|
||||
<SliderList
|
||||
{...{
|
||||
@@ -70,7 +70,7 @@ class SettingPanel extends React.Component<
|
||||
title: "Font Size",
|
||||
}}
|
||||
/>
|
||||
{this.props.currentEpub && (
|
||||
{this.props.currentEpub.archived && (
|
||||
<SliderList
|
||||
{...{
|
||||
maxValue: 80,
|
||||
@@ -94,7 +94,7 @@ class SettingPanel extends React.Component<
|
||||
title: "Letter Spacing",
|
||||
}}
|
||||
/>
|
||||
{this.props.currentEpub && (
|
||||
{this.props.currentEpub.archived && (
|
||||
<SliderList
|
||||
{...{
|
||||
maxValue: 60,
|
||||
@@ -107,7 +107,8 @@ class SettingPanel extends React.Component<
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{this.state.readerMode && this.state.readerMode !== "double" ? (
|
||||
{(this.state.readerMode && this.state.readerMode !== "double") ||
|
||||
!this.props.currentEpub.archived ? (
|
||||
<SliderList
|
||||
{...{
|
||||
maxValue: 3,
|
||||
|
||||
@@ -35,7 +35,16 @@ if (
|
||||
navigator.appVersion.indexOf("NT 6.0") === -1
|
||||
) {
|
||||
const { ipcRenderer } = window.require("electron");
|
||||
dropdownList[0].option = ipcRenderer.sendSync("fonts-ready", "ping");
|
||||
dropdownList[0].option.push("Built-in font");
|
||||
ipcRenderer.invoke("fonts-ready", "ping").then((result) => {
|
||||
dropdownList[0].option = result;
|
||||
dropdownList[0].option.push("Built-in font");
|
||||
});
|
||||
}
|
||||
StyleUtil.applyTheme();
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<Router />
|
||||
</Provider>,
|
||||
document.getElementById("root")
|
||||
);
|
||||
|
||||
@@ -31,6 +31,9 @@ class Viewer extends React.Component<ViewerProps, ViewerState> {
|
||||
RecentBooks.setRecent(key);
|
||||
});
|
||||
});
|
||||
document
|
||||
.querySelector(".ebook-viewer")
|
||||
?.setAttribute("style", "height:100%");
|
||||
window.onbeforeunload = () => {
|
||||
this.handleExit();
|
||||
};
|
||||
|
||||
@@ -17,11 +17,11 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
constructor(props: ReaderProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isOpenSettingPanel:
|
||||
isOpenRightPanel:
|
||||
OtherUtil.getReaderConfig("isSettingLocked") === "yes" ? true : false,
|
||||
isOpenOperationPanel: false,
|
||||
isOpenProgressPanel: false,
|
||||
isOpenNavPanel:
|
||||
isOpenTopPanel: false,
|
||||
isOpenBottomPanel: false,
|
||||
isOpenLeftPanel:
|
||||
OtherUtil.getReaderConfig("isNavLocked") === "yes" ? true : false,
|
||||
isMessage: false,
|
||||
rendition: null,
|
||||
@@ -53,22 +53,22 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
switch (position) {
|
||||
case "right":
|
||||
this.setState({
|
||||
isOpenSettingPanel: this.state.isOpenSettingPanel ? false : true,
|
||||
isOpenRightPanel: this.state.isOpenRightPanel ? false : true,
|
||||
});
|
||||
break;
|
||||
case "left":
|
||||
this.setState({
|
||||
isOpenNavPanel: this.state.isOpenNavPanel ? false : true,
|
||||
isOpenLeftPanel: this.state.isOpenLeftPanel ? false : true,
|
||||
});
|
||||
break;
|
||||
case "top":
|
||||
this.setState({
|
||||
isOpenOperationPanel: this.state.isOpenOperationPanel ? false : true,
|
||||
isOpenTopPanel: this.state.isOpenTopPanel ? false : true,
|
||||
});
|
||||
break;
|
||||
case "bottom":
|
||||
this.setState({
|
||||
isOpenProgressPanel: this.state.isOpenProgressPanel ? false : true,
|
||||
isOpenBottomPanel: this.state.isOpenBottomPanel ? false : true,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
@@ -83,7 +83,7 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
if (OtherUtil.getReaderConfig("isSettingLocked") === "yes") {
|
||||
break;
|
||||
} else {
|
||||
this.setState({ isOpenSettingPanel: false });
|
||||
this.setState({ isOpenRightPanel: false });
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -91,14 +91,14 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
if (OtherUtil.getReaderConfig("isNavLocked") === "yes") {
|
||||
break;
|
||||
} else {
|
||||
this.setState({ isOpenNavPanel: false });
|
||||
this.setState({ isOpenLeftPanel: false });
|
||||
break;
|
||||
}
|
||||
case "top":
|
||||
this.setState({ isOpenOperationPanel: false });
|
||||
this.setState({ isOpenTopPanel: false });
|
||||
break;
|
||||
case "bottom":
|
||||
this.setState({ isOpenProgressPanel: false });
|
||||
this.setState({ isOpenBottomPanel: false });
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -116,29 +116,13 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
handleLeaveReader: this.handleLeaveReader,
|
||||
handleEnterReader: this.handleEnterReader,
|
||||
isShow:
|
||||
this.state.isOpenNavPanel ||
|
||||
this.state.isOpenOperationPanel ||
|
||||
this.state.isOpenProgressPanel ||
|
||||
this.state.isOpenSettingPanel,
|
||||
this.state.isOpenLeftPanel ||
|
||||
this.state.isOpenTopPanel ||
|
||||
this.state.isOpenBottomPanel ||
|
||||
this.state.isOpenRightPanel,
|
||||
};
|
||||
return (
|
||||
<div className="viewer">
|
||||
<div
|
||||
className="previous-chapter-single-container"
|
||||
onClick={() => {
|
||||
this.prevPage();
|
||||
}}
|
||||
>
|
||||
<span className="icon-dropdown previous-chapter-single"></span>
|
||||
</div>
|
||||
<div
|
||||
className="next-chapter-single-container"
|
||||
onClick={() => {
|
||||
this.nextPage();
|
||||
}}
|
||||
>
|
||||
<span className="icon-dropdown next-chapter-single"></span>
|
||||
</div>
|
||||
<div
|
||||
className="reader-setting-icon-container"
|
||||
onClick={() => {
|
||||
@@ -154,7 +138,7 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
<div
|
||||
className="left-panel"
|
||||
onMouseEnter={() => {
|
||||
if (this.state.isTouch || this.state.isOpenNavPanel) {
|
||||
if (this.state.isTouch || this.state.isOpenLeftPanel) {
|
||||
return;
|
||||
}
|
||||
this.handleEnterReader("left");
|
||||
@@ -166,7 +150,7 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
<div
|
||||
className="right-panel"
|
||||
onMouseEnter={() => {
|
||||
if (this.state.isTouch || this.state.isOpenSettingPanel) {
|
||||
if (this.state.isTouch || this.state.isOpenRightPanel) {
|
||||
return;
|
||||
}
|
||||
this.handleEnterReader("right");
|
||||
@@ -178,7 +162,7 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
<div
|
||||
className="top-panel"
|
||||
onMouseEnter={() => {
|
||||
if (this.state.isTouch || this.state.isOpenOperationPanel) {
|
||||
if (this.state.isTouch || this.state.isOpenTopPanel) {
|
||||
return;
|
||||
}
|
||||
this.handleEnterReader("top");
|
||||
@@ -187,18 +171,20 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
this.handleEnterReader("top");
|
||||
}}
|
||||
></div>
|
||||
<div
|
||||
className="bottom-panel"
|
||||
onMouseEnter={() => {
|
||||
if (this.state.isTouch || this.state.isOpenProgressPanel) {
|
||||
return;
|
||||
}
|
||||
this.handleEnterReader("bottom");
|
||||
}}
|
||||
onClick={() => {
|
||||
this.handleEnterReader("bottom");
|
||||
}}
|
||||
></div>
|
||||
{this.props.currentEpub.archived && (
|
||||
<div
|
||||
className="bottom-panel"
|
||||
onMouseEnter={() => {
|
||||
if (this.state.isTouch || this.state.isOpenBottomPanel) {
|
||||
return;
|
||||
}
|
||||
this.handleEnterReader("bottom");
|
||||
}}
|
||||
onClick={() => {
|
||||
this.handleEnterReader("bottom");
|
||||
}}
|
||||
></div>
|
||||
)}
|
||||
<Viewer {...renditionProps} />
|
||||
<div
|
||||
className="setting-panel-container"
|
||||
@@ -206,7 +192,7 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
this.handleLeaveReader("right");
|
||||
}}
|
||||
style={
|
||||
this.state.isOpenSettingPanel
|
||||
this.state.isOpenRightPanel
|
||||
? {}
|
||||
: {
|
||||
transform: "translateX(309px)",
|
||||
@@ -221,7 +207,7 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
this.handleLeaveReader("left");
|
||||
}}
|
||||
style={
|
||||
this.state.isOpenNavPanel
|
||||
this.state.isOpenLeftPanel
|
||||
? {}
|
||||
: {
|
||||
transform: "translateX(-309px)",
|
||||
@@ -230,30 +216,30 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
>
|
||||
<NavigationPanel {...{ time: this.state.time }} />
|
||||
</div>
|
||||
<div
|
||||
className="progress-panel-container"
|
||||
onMouseLeave={(event) => {
|
||||
this.handleLeaveReader("bottom");
|
||||
}}
|
||||
style={
|
||||
this.state.isOpenProgressPanel
|
||||
? {}
|
||||
: {
|
||||
transform: "translateY(110px)",
|
||||
}
|
||||
}
|
||||
>
|
||||
{this.props.currentEpub.rendition && (
|
||||
{this.props.currentEpub.archived && (
|
||||
<div
|
||||
className="progress-panel-container"
|
||||
onMouseLeave={(event) => {
|
||||
this.handleLeaveReader("bottom");
|
||||
}}
|
||||
style={
|
||||
this.state.isOpenBottomPanel
|
||||
? {}
|
||||
: {
|
||||
transform: "translateY(110px)",
|
||||
}
|
||||
}
|
||||
>
|
||||
<ProgressPanel {...{ time: this.state.time }} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className="operation-panel-container"
|
||||
onMouseLeave={(event) => {
|
||||
this.handleLeaveReader("top");
|
||||
}}
|
||||
style={
|
||||
this.state.isOpenOperationPanel
|
||||
this.state.isOpenTopPanel
|
||||
? {}
|
||||
: {
|
||||
transform: "translateY(-110px)",
|
||||
|
||||
@@ -14,10 +14,10 @@ export interface ReaderProps {
|
||||
}
|
||||
|
||||
export interface ReaderState {
|
||||
isOpenSettingPanel: boolean;
|
||||
isOpenOperationPanel: boolean;
|
||||
isOpenProgressPanel: boolean;
|
||||
isOpenNavPanel: boolean;
|
||||
isOpenRightPanel: boolean;
|
||||
isOpenTopPanel: boolean;
|
||||
isOpenBottomPanel: boolean;
|
||||
isOpenLeftPanel: boolean;
|
||||
isMessage: boolean;
|
||||
isTouch: boolean;
|
||||
readerMode: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const isTitle = (line: string) => {
|
||||
export const isTitle = (line: string) => {
|
||||
return (
|
||||
line.length < 30 &&
|
||||
line.indexOf("[") === -1 &&
|
||||
|
||||
@@ -16,8 +16,8 @@ class HtmlParser {
|
||||
let random = Math.floor(Math.random() * 900) + 100;
|
||||
this.contentTitleList.push({
|
||||
label: this.contentList[i].innerText,
|
||||
id: this.contentList[i].innerText.replaceAll(" ", "_") + random,
|
||||
href: "#" + this.contentList[i].innerText.replaceAll(" ", "_") + random,
|
||||
id: this.contentList[i].innerText.replace(/ /g, "_") + random,
|
||||
href: "#" + this.contentList[i].innerText.replace(/ /g, "_") + random,
|
||||
subitems: [],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ class styleUtil {
|
||||
let colors = ["#FBF1D1", "#EFEEB0", "#CAEFC9", "#76BEE9"];
|
||||
let lines = ["#FF0000", "#000080", "#0000FF", "#2EFF2E"];
|
||||
|
||||
return `::selection{background:#f3a6a68c}::-moz-selection{background:#f3a6a68c}[class*=color-]:hover{cursor:pointer;background-image:linear-gradient(0,rgba(0,0,0,.075),rgba(0,0,0,.075))}.color-0{background-color:${colors[0]}}.color-1{background-color:${colors[1]}}.color-2{background-color:${colors[2]}}.color-3{background-color:${colors[3]}}.line-0{border-bottom:2px solid ${lines[0]}}.line-1{border-bottom:2px solid ${lines[1]}}.line-2{border-bottom:2px solid ${lines[2]}}.line-3{border-bottom:2px solid ${lines[3]}}}`;
|
||||
return `::selection{background:#f3a6a68c}::-moz-selection{background:#f3a6a68c}[class*=color-]:hover{cursor:pointer;background-image:linear-gradient(0,rgba(0,0,0,.075),rgba(0,0,0,.075))}.color-0{background-color:${colors[0]}}.color-1{background-color:${colors[1]}}.color-2{background-color:${colors[2]}}.color-3{background-color:${colors[3]}}.line-0{border-bottom:2px solid ${lines[0]}}.line-1{border-bottom:2px solid ${lines[1]}}.line-2{border-bottom:2px solid ${lines[2]}}.line-3{border-bottom:2px solid ${lines[3]}}}::-webkit-scrollbar{width: 5px;height: 0.5rem;}::-webkit-scrollbar-track{border-radius: 0;}::-webkit-scrollbar-thumb{border-radius: 0;transition: all 0.2s;border-radius: 0.5rem;opacity: 0.5;}img{max-width:100%}`;
|
||||
}
|
||||
static getCustomCss(isJSON: boolean = true) {
|
||||
if (isJSON) {
|
||||
|
||||
@@ -40,7 +40,6 @@ class BackupUtil {
|
||||
}
|
||||
} else {
|
||||
let timestamp = new Date().getTime().toString();
|
||||
console.log(timestamp, "timestamp");
|
||||
OtherUtil.setReaderConfig("lastSyncTime", timestamp);
|
||||
|
||||
localStorage.setItem("lastSyncTime", timestamp);
|
||||
|
||||
@@ -62,24 +62,6 @@ export const moveData = (
|
||||
if (driveIndex === 5) {
|
||||
handleFinish();
|
||||
}
|
||||
// try {
|
||||
// const fs = window.require("fs-extra");
|
||||
|
||||
// fs.remove(path.join(dirPath, file.name), async (err) => {
|
||||
// if (err) console.log(err);
|
||||
// if (driveIndex === 4) {
|
||||
// let deleteBooks = books.map((item) => {
|
||||
// return localforage.removeItem(item.key);
|
||||
// });
|
||||
// await Promise.all(deleteBooks);
|
||||
// }
|
||||
// if (driveIndex === 5) {
|
||||
// handleFinish();
|
||||
// }
|
||||
// });
|
||||
// } catch (e) {
|
||||
// console.error(e, "移动失败");
|
||||
// }
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ class DropboxUitl {
|
||||
return false;
|
||||
}
|
||||
static DownloadFile(
|
||||
handleFinish: (mobileData: string) => void,
|
||||
handleFinish: () => void,
|
||||
showMessage: (message: string) => void,
|
||||
isSync: boolean = false
|
||||
) {
|
||||
|
||||
@@ -4,11 +4,7 @@ import BookUtil from "../bookUtil";
|
||||
let JSZip = (window as any).JSZip;
|
||||
|
||||
class RestoreUtil {
|
||||
static restore = (
|
||||
file: any,
|
||||
handleFinish: (mobileData) => void,
|
||||
isSync = false
|
||||
) => {
|
||||
static restore = (file: any, handleFinish: () => void, isSync = false) => {
|
||||
let configArr = [
|
||||
"notes",
|
||||
"books",
|
||||
@@ -27,25 +23,21 @@ class RestoreUtil {
|
||||
"recordLocation",
|
||||
];
|
||||
let zip = new JSZip();
|
||||
let mobileObject: Object = {};
|
||||
|
||||
// more files !
|
||||
configArr.forEach((item) => {
|
||||
zip
|
||||
.loadAsync(file)
|
||||
.then((content: any) => {
|
||||
// you now have every files contained in the loaded zip
|
||||
return content.files[
|
||||
isSync ? `${item}.json` : `config/${item}.json`
|
||||
].async("text"); // a promise of "Hello World\n"
|
||||
return content.files[`config/${item}.json`].async("text"); // a promise of "Hello World\n"
|
||||
})
|
||||
.then((text: any) => {
|
||||
if (text) {
|
||||
if (item === "notes" || item === "books" || item === "bookmarks") {
|
||||
localforage.setItem(item, JSON.parse(text));
|
||||
mobileObject[item] = text;
|
||||
} else {
|
||||
localStorage.setItem(item, text);
|
||||
mobileObject[item] = text;
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -89,7 +81,7 @@ class RestoreUtil {
|
||||
});
|
||||
});
|
||||
setTimeout(() => {
|
||||
handleFinish(JSON.stringify(mobileObject));
|
||||
handleFinish();
|
||||
}, 1000);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import xml2js from "xml2js";
|
||||
import { isTitle } from "./generateEpub";
|
||||
export const xmlBookToObj = (xml) => {
|
||||
var objBook: any = {};
|
||||
var informBook;
|
||||
@@ -50,3 +51,17 @@ export const xmlBookTagFilter = (bookString) => {
|
||||
|
||||
return bookBody;
|
||||
};
|
||||
export const txtToHtml = (text: string) => {
|
||||
const lines = text.split("\n");
|
||||
let html: string = "";
|
||||
for (let item of lines) {
|
||||
if (item.trim()) {
|
||||
if (isTitle(item.trim())) {
|
||||
html += `<h1>${item}</h1>`;
|
||||
} else {
|
||||
html += `<p>${item}</p>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
return html;
|
||||
};
|
||||
|
||||
@@ -6525,7 +6525,7 @@ follow-redirects@^1.10.0:
|
||||
resolved "https://registry.npm.taobao.org/follow-redirects/download/follow-redirects-1.13.1.tgz?cache=0&sync_timestamp=1607916846877&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffollow-redirects%2Fdownload%2Ffollow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7"
|
||||
integrity sha1-X2m4Ezds7k/QR0o6uoNd8Eq3Y7c=
|
||||
|
||||
font-list@^1.2.15:
|
||||
font-list@1.2.15:
|
||||
version "1.2.15"
|
||||
resolved "https://registry.nlark.com/font-list/download/font-list-1.2.15.tgz#dc5fa7038fa77aa60ed7faf66522aed872d9f395"
|
||||
integrity sha1-3F+nA4+neqYO1/r2ZSKu2HLZ85U=
|
||||
|
||||
Reference in New Issue
Block a user