diff --git a/main.js b/main.js index cf62a6b2..efe61b5f 100644 --- a/main.js +++ b/main.js @@ -18,6 +18,7 @@ app.on("ready", () => { nodeIntegration: true, nativeWindowOpen: true, nodeIntegrationInSubFrames: true, + allowRunningInsecureContent: true, }, show: false, // transparent: true, @@ -71,14 +72,165 @@ app.on("ready", () => { .getFonts() .then((fonts) => { event.returnValue = fonts; - - const server = require("./server"); }) .catch((err) => { console.log(err); }); }); + let isFirst = true; + ipcMain.on("start-server", (event, arg) => { + if (isFirst) startExpress(); + isFirst = false; + event.returnValue = "pong"; + }); }); app.on("window-all-closed", () => { app.quit(); }); +function startExpress() { + const express = require("express"); + const cors = require("cors"); + const bodyParser = require("body-parser"); + const fileUpload = require("express-fileupload"); + const path = require("path"); + const fs = require("fs"); + const Epub = require("epub-gen"); + const { readFileSync } = require("fs"); + const iconv = require("iconv-lite"); + const electron = require("electron"); + const configDir = (electron.app || electron.remote.app).getPath("userData"); + var dirPath = path.join(configDir, "uploads"); + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath); + console.log("文件夹创建成功"); + } else { + console.log("文件夹已存在"); + } + const server = express(); + server.use( + fileUpload({ + createParentPath: true, + }) + ); + server.use(cors()); + server.use(bodyParser.json()); + server.use(bodyParser.urlencoded({ extended: true })); + server.post("/ebook_parser", async (req, res) => { + let file = req.files.file; + file.mv(dirPath + file.name, () => { + const data = readFileSync(dirPath + file.name, { + encoding: "binary", + }); + const buf = new Buffer(data, "binary"); + const lines = iconv.decode(buf, "GBK").split("\n"); + const content = []; + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + // console.log(line, line.startsWith("序章"), "test"); + + if ( + line.startsWith("CHAPTER ") || + line.startsWith("Chapter") || + line.startsWith("第") || + line.startsWith("序章") || + line.startsWith("前言") || + line.startsWith("写在前面的话") || + line.startsWith("后记") || + line.startsWith("楔子") || + line.startsWith("后记") || + line.startsWith("后序") + ) { + if (content.length) { + content[content.length - 1].data = content[ + content.length - 1 + ].data.join("\n"); + } + content.push({ + data: [], + }); + } else if (line.trim() === "" && content.length) { + if (content[content.length - 1].data.length > 1) { + content[content.length - 1].data.push("

"); + } + content[content.length - 1].data.push("

"); + } else if (content.length) { + content[content.length - 1].data.push(line.trim()); + } + if ( + content[content.length - 1] && + content[content.length - 1].data && + i === lines.length - 1 + ) { + content[content.length - 1].data = content[ + content.length - 1 + ].data.join("\n"); + } + } + if (!content.length) { + content.push({ + title: "正文", + data: lines + .map((item) => { + return `

${item}

`; + }) + .join("\n"), + }); + } + const options = { + title: file.name.split(".")[0], + author: "Koodo Reader", + output: dirPath + `${file.name.split(".")[0]}.epub`, + content, + }; + new Epub(options).promise + .then(() => { + res.sendFile(dirPath + `${file.name.split(".")[0]}.epub`); + res.on("finish", function () { + try { + fs.unlink(dirPath + `${file.name.split(".")[0]}.epub`, (err) => { + if (err) throw err; + console.log("successfully deleted"); + }); + fs.unlink(dirPath + `${file.name}`, (err) => { + if (err) throw err; + console.log("successfully deleted"); + }); + } catch (e) { + console.log("error removing "); + } + }); + }) + .catch((err) => console.log("err")); + }); + }); + + async function start() { + try { + const port = 3366; + expressServer = await server.listen(port); + console.log("started"); + const address = expressServer.address(); + serverInfo = { + port: address.port, + local: "localhost", + url: `http://localhost:${address.port}`, + }; + return serverInfo; + } catch (e) { + return { message: e.message }; + } + } + + async function startServer() { + console.log("starting"); + const { port, local, message } = await start(); + if (message) { + console.log("err"); + console.error(message); + } else { + console.info(`启动成功,本地访问 http://${local}:${port}`); + } + } + + startServer(); +} diff --git a/src/components/bookCardtem/component.tsx b/src/components/bookCardtem/component.tsx index b74a9e8e..e341b4a9 100644 --- a/src/components/bookCardtem/component.tsx +++ b/src/components/bookCardtem/component.tsx @@ -105,7 +105,11 @@ class BookCardItem extends React.Component { >
diff --git a/src/components/bookListItem/component.tsx b/src/components/bookListItem/component.tsx index 13c80988..118f48e9 100644 --- a/src/components/bookListItem/component.tsx +++ b/src/components/bookListItem/component.tsx @@ -76,7 +76,11 @@ class BookListItem extends React.Component { }} > diff --git a/src/components/importLocal/component.tsx b/src/components/importLocal/component.tsx index bdb1301c..4df24532 100644 --- a/src/components/importLocal/component.tsx +++ b/src/components/importLocal/component.tsx @@ -13,11 +13,18 @@ import { config } from "../../constants/readerConfig"; import MobiFile from "../../utils/mobiUtil"; import iconv from "iconv-lite"; import isElectron from "is-electron"; +import { withRouter } from "react-router-dom"; declare var window: any; var pdfjsLib = window["pdfjs-dist/build/pdf"]; class ImportLocal extends React.Component { + componentDidMount() { + if (isElectron()) { + const { ipcRenderer } = window.require("electron"); + ipcRenderer.sendSync("start-server", "ping"); + } + } handleAddBook = (book: BookModel) => { return new Promise((resolve, reject) => { let bookArr = this.props.books; @@ -33,6 +40,7 @@ class ImportLocal extends React.Component { this.props.handleMessage("Add Successfully"); this.props.handleMessageBox(true); resolve(); + this.props.history.push("/manager/home"); }) .catch(() => { reject(); @@ -349,4 +357,4 @@ class ImportLocal extends React.Component { } } -export default ImportLocal; +export default withRouter(ImportLocal); diff --git a/src/components/importLocal/interface.tsx b/src/components/importLocal/interface.tsx index f0c2d716..dfbd632f 100644 --- a/src/components/importLocal/interface.tsx +++ b/src/components/importLocal/interface.tsx @@ -1,6 +1,6 @@ import BookModel from "../../model/Book"; - -export interface ImportLocalProps { +import { RouteComponentProps } from "react-router"; +export interface ImportLocalProps extends RouteComponentProps { books: BookModel[]; handleMessageBox: (isShow: boolean) => void; handleMessage: (message: string) => void; diff --git a/src/components/settingDialog/component.tsx b/src/components/settingDialog/component.tsx index 119bec8a..d4e3c73c 100644 --- a/src/components/settingDialog/component.tsx +++ b/src/components/settingDialog/component.tsx @@ -271,7 +271,11 @@ class SettingDialog extends React.Component<
diff --git a/src/components/updataDialog/component.tsx b/src/components/updataDialog/component.tsx index c918aa42..91a94d04 100644 --- a/src/components/updataDialog/component.tsx +++ b/src/components/updataDialog/component.tsx @@ -66,7 +66,11 @@ class UpdateDialog extends React.Component { koodo.960960.xyz

diff --git a/src/constants/readerConfig.tsx b/src/constants/readerConfig.tsx index 30324111..d3aed1e6 100644 --- a/src/constants/readerConfig.tsx +++ b/src/constants/readerConfig.tsx @@ -79,7 +79,7 @@ export const sideMenu = [ export const config = { callback_url: process.env.NODE_ENV === "production" - ? "https://reader.960960.xyz" + ? "https://koodo.960960.xyz" : "http://localhost:3000", token_url: process.env.NODE_ENV === "production" diff --git a/src/containers/bookList/component.tsx b/src/containers/bookList/component.tsx index 4abaa11a..b1645421 100644 --- a/src/containers/bookList/component.tsx +++ b/src/containers/bookList/component.tsx @@ -14,7 +14,7 @@ import OtherUtil from "../../utils/otherUtil"; import localforage from "localforage"; import DeletePopup from "../../components/deletePopup"; import EmptyPage from "../emptyPage"; -import { Redirect } from "react-router-dom"; +import { Redirect, withRouter } from "react-router-dom"; declare var window: any; @@ -28,6 +28,9 @@ class BookList extends React.Component { }; } componentDidMount() { + if (!this.props.books || !this.props.books[0]) { + return ; + } this.handleOldVersion(); } handleOldVersion = async () => { @@ -146,11 +149,10 @@ class BookList extends React.Component { SortUtil.sortBooks(this.props.books, this.props.sortCode) || [] ) : this.handleRecent(this.props.books, RecordRecent.getAllRecent()); - console.log(books.length, "books.length"); - if (books.length === 0) { - console.log("empty"); + if (this.props.mode === "shelf" && books.length === 0) { return ; } + return books.map((item: BookModel, index: number) => { return this.props.isList === "list" ? ( { this.setState({ isOpenDelete }); }; render() { - if (this.state.favoriteBooks === 0 && this.props.mode === "favorite") { + if ( + (this.state.favoriteBooks === 0 && this.props.mode === "favorite") || + !this.props.books || + !this.props.books[0] + ) { return ; } const deletePopupProps = { @@ -285,4 +291,4 @@ class BookList extends React.Component { } } -export default BookList; +export default withRouter(BookList); diff --git a/src/containers/bookList/interface.tsx b/src/containers/bookList/interface.tsx index be7a5ad1..f2fffe75 100644 --- a/src/containers/bookList/interface.tsx +++ b/src/containers/bookList/interface.tsx @@ -1,6 +1,6 @@ import BookModel from "../../model/Book"; - -export interface BookListProps { +import { RouteComponentProps } from "react-router"; +export interface BookListProps extends RouteComponentProps { books: BookModel[]; mode: string; shelfIndex: number; diff --git a/src/containers/deleteDialog/component.tsx b/src/containers/deleteDialog/component.tsx index 19591196..9310d961 100644 --- a/src/containers/deleteDialog/component.tsx +++ b/src/containers/deleteDialog/component.tsx @@ -8,6 +8,7 @@ import RecordLocation from "../../utils/recordLocation"; import AddFavorite from "../../utils/addFavorite"; import { Trans } from "react-i18next"; import { DeleteDialogProps } from "./interface"; +import { withRouter } from "react-router-dom"; class DeleteDialog extends React.Component { handleCancel = () => { @@ -74,6 +75,9 @@ class DeleteDialog extends React.Component { //删除书签,笔记,书摘,高亮 this.handleDeleteOther(); this.props.handleActionDialog(false); + if (this.props.books.length === 1) { + this.props.history.push("/manager/empty"); + } } this.props.handleMessage("Delete Successfully"); @@ -130,4 +134,4 @@ class DeleteDialog extends React.Component { } } -export default DeleteDialog; +export default withRouter(DeleteDialog); diff --git a/src/containers/deleteDialog/interface.tsx b/src/containers/deleteDialog/interface.tsx index 783a9d5c..7d6d5f88 100644 --- a/src/containers/deleteDialog/interface.tsx +++ b/src/containers/deleteDialog/interface.tsx @@ -1,8 +1,9 @@ import BookModel from "../../model/Book"; import NoteModel from "../../model/Note"; import BookmarkModel from "../../model/Bookmark"; +import { RouteComponentProps } from "react-router"; -export interface DeleteDialogProps { +export interface DeleteDialogProps extends RouteComponentProps { books: BookModel[]; isOpenDeleteDialog: boolean; currentBook: BookModel; diff --git a/src/containers/digestList/component.tsx b/src/containers/digestList/component.tsx index 94b4bfcb..81d1ccf5 100644 --- a/src/containers/digestList/component.tsx +++ b/src/containers/digestList/component.tsx @@ -13,6 +13,9 @@ class DigestList extends React.Component { tag: [], }; } + componentWillMount() { + this.props.handleFetchNotes(); + } handleFilter = (items: any, arr: number[]) => { let itemArr: any[] = []; arr.forEach((item) => { diff --git a/src/containers/digestList/index.tsx b/src/containers/digestList/index.tsx index 2ba97364..6815d148 100644 --- a/src/containers/digestList/index.tsx +++ b/src/containers/digestList/index.tsx @@ -3,6 +3,7 @@ import { connect } from "react-redux"; import { stateType } from "../../store"; import { withNamespaces } from "react-i18next"; import DigestList from "./component"; +import { handleFetchNotes } from "../../store/actions/reader"; const mapStateToProps = (state: stateType) => { return { @@ -11,7 +12,7 @@ const mapStateToProps = (state: stateType) => { searchResults: state.manager.searchResults, }; }; -const actionCreator = {}; +const actionCreator = { handleFetchNotes }; export default connect( mapStateToProps, actionCreator diff --git a/src/containers/digestList/interface.tsx b/src/containers/digestList/interface.tsx index 022bcb55..de39017c 100644 --- a/src/containers/digestList/interface.tsx +++ b/src/containers/digestList/interface.tsx @@ -4,6 +4,7 @@ export interface DigestListProps { digests: NoteModel[]; isSearch: boolean; searchResults: number[]; + handleFetchNotes: () => void; } export interface DigestListStates { tag: string[]; diff --git a/src/containers/emptyPage/component.tsx b/src/containers/emptyPage/component.tsx index fe2629a3..f66f050e 100644 --- a/src/containers/emptyPage/component.tsx +++ b/src/containers/emptyPage/component.tsx @@ -7,7 +7,6 @@ import { EmptyPageProps, EmptyPageState } from "./interface"; class EmptyPage extends React.Component { render() { - console.log("empty"); const renderEmptyList = () => { return emptyList.map((item) => { return ( @@ -40,7 +39,7 @@ class EmptyPage extends React.Component { { } } componentDidMount() { - console.log(window.location.href, "rendered"); let page = document.querySelector("#page-area"); let epub = this.props.currentEpub; (window as any).rangy.init(); // 初始化 diff --git a/src/containers/header/component.tsx b/src/containers/header/component.tsx index 12c32fe6..1391aa77 100644 --- a/src/containers/header/component.tsx +++ b/src/containers/header/component.tsx @@ -58,11 +58,7 @@ class Header extends React.Component { > - +
{ tag: [], }; } + componentWillMount() { + this.props.handleFetchNotes(); + } handleTag = (tag: string[]) => { this.setState({ tag }); }; diff --git a/src/containers/noteList/index.tsx b/src/containers/noteList/index.tsx index 6a99a780..72ac50be 100644 --- a/src/containers/noteList/index.tsx +++ b/src/containers/noteList/index.tsx @@ -3,6 +3,8 @@ import { connect } from "react-redux"; import { stateType } from "../../store"; import { withNamespaces } from "react-i18next"; import NoteList from "./component"; +import { handleFetchNotes } from "../../store/actions/reader"; + const mapStateToProps = (state: stateType) => { return { @@ -11,7 +13,7 @@ const mapStateToProps = (state: stateType) => { searchResults: state.manager.searchResults, }; }; -const actionCreator = {}; +const actionCreator = { handleFetchNotes }; export default connect( mapStateToProps, actionCreator diff --git a/src/containers/noteList/interface.tsx b/src/containers/noteList/interface.tsx index 292d9b6b..147d9930 100644 --- a/src/containers/noteList/interface.tsx +++ b/src/containers/noteList/interface.tsx @@ -4,6 +4,7 @@ export interface NoteListProps { notes: NoteModel[]; isSearch: boolean; searchResults: number[]; + handleFetchNotes: () => void; } export interface NoteListState { tag: string[]; diff --git a/src/containers/sidebar/component.tsx b/src/containers/sidebar/component.tsx index 74ac4509..b2fea482 100644 --- a/src/containers/sidebar/component.tsx +++ b/src/containers/sidebar/component.tsx @@ -22,6 +22,7 @@ class Sidebar extends React.Component { this.setState({ isCollapse: true }); this.props.history.push(`/manager/${mode}`); this.props.handleMode(mode); + this.props.handleSearch(false); }; render() { const renderSideMenu = () => { @@ -67,7 +68,7 @@ class Sidebar extends React.Component { { return { mode: state.sidebar.mode }; }; -const actionCreator = { handleMode }; +const actionCreator = { handleMode, handleSearch }; export default connect( mapStateToProps, diff --git a/src/containers/sidebar/interface.tsx b/src/containers/sidebar/interface.tsx index 795868f8..eb5bab0c 100644 --- a/src/containers/sidebar/interface.tsx +++ b/src/containers/sidebar/interface.tsx @@ -3,6 +3,7 @@ import { RouteComponentProps } from "react-router"; export interface SidebarProps extends RouteComponentProps { mode: string; handleMode: (mode: string) => void; + handleSearch: (isSearch: boolean) => void; } export interface SidebarState { diff --git a/src/pages/manager/component.tsx b/src/pages/manager/component.tsx index 86d5febe..39e66c34 100644 --- a/src/pages/manager/component.tsx +++ b/src/pages/manager/component.tsx @@ -154,7 +154,11 @@ class Manager extends React.Component {
@@ -183,7 +187,7 @@ class Manager extends React.Component { ) : null} {this.state.isUpdated ? : null} {this.props.isSettingOpen ? : null} - {!books ? ( + {!books && this.state.totalBooks ? ( ) : ( @@ -200,5 +204,4 @@ class Manager extends React.Component { ); } } - -export default Manager; +export default Manager; \ No newline at end of file diff --git a/src/pages/redirect/component.tsx b/src/pages/redirect/component.tsx new file mode 100644 index 00000000..bd1cbfd4 --- /dev/null +++ b/src/pages/redirect/component.tsx @@ -0,0 +1,85 @@ +import React from "react"; +import "./manager.css"; +import { RedirectProps, RedirectState } from "./interface"; +import { Trans } from "react-i18next"; +import { getParamsFromUrl } from "../../utils/syncUtils/common"; +import copy from "copy-text-to-clipboard"; +import { withRouter } from "react-router-dom"; +class Redirect extends React.Component { + timer!: NodeJS.Timeout; + constructor(props: RedirectProps) { + super(props); + this.state = { + isAuthed: false, + isError: false, + isCopied: false, + token: "", + }; + } + + componentDidMount() { + //判断是否是获取token后的回调页面 + let url = document.location.href; + if (url.indexOf("error") > -1) { + this.setState({ isError: true }); + return false; + } + if (url.indexOf("code") > -1) { + let params: any = getParamsFromUrl(); + console.log(params, "params"); + this.setState({ token: params.code }); + this.setState({ isAuthed: true }); + return false; + } + if (url.indexOf("access_token") > -1) { + let params: any = getParamsFromUrl(); + console.log(params, "params"); + this.setState({ token: params.access_token }); + this.setState({ isAuthed: true }); + return false; + } + } + + render() { + if (this.state.isError || this.state.isAuthed) { + return ( +
+
+ {this.state.isAuthed ? ( + + ) : ( + + )} + +
+ + {this.state.isAuthed + ? "Authorize Successfully" + : "Authorize Failed"} + +
+ {this.state.isAuthed ? ( +
{ + copy(this.state.token); + this.setState({ isCopied: true }); + }} + > + {this.state.isCopied ? ( + Copied + ) : ( + Copy Token + )} +
+ ) : null} +
+
+ ); + } + + return
你似乎来到了没有知识的荒原
; + } +} + +export default withRouter(Redirect); diff --git a/src/pages/redirect/index.tsx b/src/pages/redirect/index.tsx new file mode 100644 index 00000000..cacda737 --- /dev/null +++ b/src/pages/redirect/index.tsx @@ -0,0 +1,9 @@ +import { connect } from "react-redux"; +import "./manager.css"; +import { stateType } from "../../store"; +import Redirect from "./component"; +const mapStateToProps = (state: stateType) => { + return {}; +}; +const actionCreator = {}; +export default connect(mapStateToProps, actionCreator)(Redirect); diff --git a/src/pages/redirect/interface.tsx b/src/pages/redirect/interface.tsx new file mode 100644 index 00000000..5c9e8660 --- /dev/null +++ b/src/pages/redirect/interface.tsx @@ -0,0 +1,9 @@ +import { RouteComponentProps } from "react-router"; +export interface RedirectProps extends RouteComponentProps {} + +export interface RedirectState { + isAuthed: boolean; + isError: boolean; + isCopied: boolean; + token: string; +} diff --git a/src/pages/redirect/manager.css b/src/pages/redirect/manager.css new file mode 100644 index 00000000..a1a1bea6 --- /dev/null +++ b/src/pages/redirect/manager.css @@ -0,0 +1,22 @@ +.manager { + width: 100%; + height: 100%; +} +.token-dialog-token-text { + width: 94px; + height: 29px; + background: rgba(255, 255, 255, 1); + border: 2px solid rgba(45, 111, 200, 1); + opacity: 1; + font-size: 14px; + font-weight: 500; + line-height: 29px; + color: rgba(45, 111, 200, 1); + text-align: center; + cursor: pointer; +} +.auth-page-close-icon { + font-size: 60px !important; + float: none !important; +} + diff --git a/src/router/index.tsx b/src/router/index.tsx index 13b2874a..0100df15 100644 --- a/src/router/index.tsx +++ b/src/router/index.tsx @@ -1,24 +1,18 @@ import { hot } from "react-hot-loader/root"; import React from "react"; -import { - Route, - Switch, - Redirect, - HashRouter, - BrowserRouter, -} from "react-router-dom"; +import { Route, Switch, HashRouter } from "react-router-dom"; import Manager from "../pages/manager"; import EpubReader from "../pages/epubReader"; +import _Redirect from "../pages/redirect"; const Router = () => { return ( - - {/* - */} + + ); diff --git a/src/utils/syncUtils/common.tsx b/src/utils/syncUtils/common.tsx index 410efb77..ecef8c61 100644 --- a/src/utils/syncUtils/common.tsx +++ b/src/utils/syncUtils/common.tsx @@ -3,8 +3,8 @@ export function getParamsFromUrl() { var e, r = /([^&;=]+)=?([^&;]*)/g, q = - window.location.search.substring(1).split("#")[0] || - window.location.hash.substring(1); + window.location.hash.substring(1) || + window.location.search.substring(1).split("#")[0]; while ((e = r.exec(q))) { hashParams[e[1]] = decodeURIComponent(e[2]);