This commit is contained in:
troyeguo
2025-01-29 16:17:20 +08:00
parent 7b33a673f5
commit c13bfe5582
16 changed files with 455 additions and 676 deletions

View File

File diff suppressed because one or more lines are too long

View File

@@ -36,7 +36,11 @@
"Downloading, please wait": "Downloading, please wait",
"Uploading, please wait": "Uploading, please wait",
"Import": "Import",
"Sync and backup": "Sync and backup",
"Add data source": "Add data source",
"Please select": "Please select",
"Backup": "Backup",
"Server path": "Server path",
"Search my library": "Search my library",
"Search my notes": "Search my notes",
"Search my highlights": "Search my highlights",
@@ -413,6 +417,10 @@
"Add new plugin": "Add new plugin",
"Pot is running": "Pot is running",
"Please select the service": "Please select the service",
"Set default sync option": "Set default sync option",
"This feature is not available in the free version": "This feature is not available in the free version",
"Please add data source in the setting": "Please add data source in the setting",
"Select data source": "Select data source",
"Upgrade failed": "Upgrade failed",
"Restore failed": "Restore failed",
"Backup failed": "Backup failed",

View File

@@ -412,13 +412,25 @@
"Pot is running": "Pot 正在运行中",
"Plugin verification failed": "插件校验失败",
"Please select the service": "请选择服务",
"Delete data source": "删除数据源",
"Please select": "请选择",
"Set default sync option": "设置默认同步选项",
"This feature is not available in the free version": "免费版不支持此功能",
"Upgrade failed": "升级失败",
"Restore failed": "恢复失败",
"Backup failed": "备份失败",
"Reset reader window's position": "重置阅读窗口的位置",
"Reset successful": "重置成功",
"Select data source": "选择数据源",
"Please add data source in the setting": "请在设置中添加数据源",
"Reset failed": "重置失败",
"Reset": "重置",
"Backup to": "备份到",
"Restore from": "恢复自",
"Sync and backup": "同步和备份",
"Server path": "服务器路径",
"Add data source": "添加数据源",
"S3 Compatible": "S3 兼容",
"This feature is only available in the developer version": "本功能仅支持开发版",
"Paste the code of the plugin here, check out document to learn how to get more plugins": "将插件的代码粘贴在这里,查看帮助文档了解如何获取更多插件",
"Full screen": "全屏模式"

View File

@@ -235,3 +235,16 @@
line-height: 50px;
font-size: 18px;
}
.backup-source-dropdown {
display: inline-block;
margin-top: 10px;
margin-bottom: 10px;
border-radius: 5px;
border: 0px;
font-size: 15px;
line-height: 18px;
opacity: 1;
/* padding-left: 10px; */
cursor: pointer;
width: 60px;
}

View File

@@ -5,7 +5,6 @@ import { backup } from "../../../utils/file/backup";
import { restore } from "../../../utils/file/restore";
import { Trans } from "react-i18next";
import { BackupDialogProps, BackupDialogState } from "./interface";
import TokenDialog from "../tokenDialog";
import ConfigService from "../../../utils/storage/configService";
import Lottie from "react-lottie";
import animationSuccess from "../../../assets/lotties/success.json";
@@ -59,67 +58,34 @@ class BackupDialog extends React.Component<
showMessage = (message: string) => {
toast(this.props.t(message));
};
handleBackup = (name: string) => {
this.setState({ currentDrive: name }, async () => {
if (name === "local") {
let result = await backup(name);
if (result) {
this.handleFinish();
} else {
this.showMessage("Upload failed, check your connection");
}
return;
}
if (name === "onedrive" || name === "googledrive" || name === "dropbox") {
if (!this.state.isDeveloperVer) {
this.showMessage(
"This feature is only available in the developer version"
);
return;
}
}
if (!ConfigService.getReaderConfig(name + "_token") && name !== "local") {
this.props.handleTokenDialog(true);
return;
}
this.showMessage("Uploading, please wait");
this.props.handleLoadingDialog(true);
handleBackup = async () => {
let name = this.state.currentDrive;
if (name === "local") {
let result = await backup(name);
if (result) {
this.handleFinish();
} else {
this.showMessage("Upload failed, check your connection");
}
});
return;
}
if (!ConfigService.getReaderConfig(name + "_token") && name !== "local") {
this.props.handleTokenDialog(true);
return;
}
this.showMessage("Uploading, please wait");
this.props.handleLoadingDialog(true);
let result = await backup(name);
if (result) {
this.handleFinish();
} else {
this.showMessage("Upload failed, check your connection");
}
};
handleRestore = (name: string) => {
this.setState({ currentDrive: name }, async () => {
if (name === "local") {
let result = await restore(name);
if (result) {
this.handleFinish();
} else {
this.showMessage("Download failed,network problem or no backup");
this.props.handleLoadingDialog(false);
}
return;
}
if (name === "onedrive" || name === "googledrive" || name === "dropbox") {
if (!this.state.isDeveloperVer) {
this.showMessage(
"This feature is only available in the developer version"
);
return;
}
}
if (!ConfigService.getReaderConfig(name + "_token")) {
this.props.handleTokenDialog(true);
return;
}
this.props.handleLoadingDialog(true);
this.showMessage("Downloading, please wait");
handleRestore = async () => {
let name = this.state.currentDrive;
if (name === "local") {
let result = await restore(name);
if (result) {
this.handleFinish();
@@ -127,123 +93,135 @@ class BackupDialog extends React.Component<
this.showMessage("Download failed,network problem or no backup");
this.props.handleLoadingDialog(false);
}
});
return;
}
if (!ConfigService.getReaderConfig(name + "_token")) {
this.props.handleTokenDialog(true);
return;
}
this.props.handleLoadingDialog(true);
this.showMessage("Downloading, please wait");
let result = await restore(name);
if (result) {
this.handleFinish();
} else {
this.showMessage("Download failed,network problem or no backup");
this.props.handleLoadingDialog(false);
}
};
handleSelectSource = (event: any) => {
if (
(event.target.value === "ftp" ||
event.target.value === "webdav" ||
event.target.value === "sftp") &&
!isElectron
) {
toast(
this.props.t(
"Koodo Reader's web version are limited by the browser, for more powerful features, please download the desktop version."
)
);
return;
}
if (
event.target.value === "google" ||
event.target.value === "s3compatible" ||
event.target.value === "microsoft" ||
event.target.value === "dropbox"
) {
toast(this.props.t("This feature is not available in the free version"));
return;
}
if (event.target.value === "add") {
toast(this.props.t("Please add data source in the setting"));
return;
}
this.setState({ currentDrive: event.target.value });
};
render() {
const renderDrivePage = () => {
return driveList.map((item) => {
return (
<li
key={item.value}
className="backup-page-list-item"
onClick={() => {
//webdav is avavilible on desktop
if (
(item.value === "ftp" || item.value === "sftp") &&
!isElectron
) {
toast(
this.props.t(
"Koodo Reader's web version are limited by the browser, for more powerful features, please download the desktop version."
)
);
return;
}
if (this.state.isBackup === "yes") {
this.handleBackup(item.value);
} else {
this.handleRestore(item.value);
}
}}
>
<div className="backup-page-list-item-container">
<span
className={`icon-${item.value} backup-page-list-icon`}
></span>
{ConfigService.getReaderConfig(item.value + "_token") ? (
<div
className="backup-page-list-title"
onClick={() => {
ConfigService.setReaderConfig(item.value + "_token", "");
this.showMessage("Unauthorize successful");
}}
style={{ color: "rgb(0, 120, 212)" }}
>
<Trans>Unauthorize</Trans>
</div>
) : (
<div className="backup-page-list-title">
<Trans>{item.label}</Trans>
</div>
)}
</div>
</li>
);
});
};
let syncUtil = new SyncUtil(this.state.currentDrive, {});
const dialogProps = {
driveName: this.state.currentDrive,
url: syncUtil.getAuthUrl(),
title:
driveList[
_.findLastIndex(driveList, {
value: this.state.currentDrive,
})
].label,
};
return (
<div className="backup-page-container">
{this.props.isOpenTokenDialog ? <TokenDialog {...dialogProps} /> : null}
{this.state.currentStep === 0 ? (
<div className="backup-page-option">
<div
className={
this.state.isBackup === "yes"
? "backup-page-backup active"
: "backup-page-backup"
}
onClick={() => {
this.setState({ isBackup: "yes" });
}}
>
<span className="icon-backup"></span>
<div style={{ lineHeight: 1.0 }}>
<Trans>Backup</Trans>
</div>
</div>
<div
className={
this.state.isBackup === "no"
? "backup-page-backup active"
: "backup-page-backup"
}
onClick={(event) => {
if (!isElectron) {
event.preventDefault();
toast(
this.props.t(
"Koodo Reader's web version are limited by the browser, for more powerful features, please download the desktop version."
<div className="backup-page-backup">
<span
className="icon-backup"
onClick={() => {
this.setState({ currentStep: 1, isBackup: "yes" });
this.handleBackup();
}}
></span>
<div style={{ lineHeight: 1.0, fontSize: 15 }}>
<Trans>Backup to</Trans>
<select
name=""
className="backup-source-dropdown"
onChange={this.handleSelectSource}
>
{[...driveList, { label: "Add data source", value: "add" }]
.filter(
(item) =>
this.props.dataSourceList.includes(item.value) ||
item.value === "local" ||
item.value === "add"
)
);
return;
}
this.setState({ isBackup: "no" });
}}
>
<span className="icon-restore"></span>
<div style={{ lineHeight: 1.0 }}>
<Trans>Restore</Trans>
.map((item) => (
<option
value={item.value}
key={item.value}
className="lang-setting-option"
>
{this.props.t(item.label)}
</option>
))}
</select>
</div>
</div>
<div className="backup-page-backup">
<span
className="icon-restore"
onClick={(event) => {
if (!isElectron) {
event.preventDefault();
toast(
this.props.t(
"Koodo Reader's web version are limited by the browser, for more powerful features, please download the desktop version."
)
);
return;
}
this.setState({ currentStep: 1, isBackup: "no" });
this.handleRestore();
}}
></span>
<div style={{ lineHeight: 1.0, fontSize: 15 }}>
<Trans>Restore from</Trans>
<select
name=""
className="backup-source-dropdown"
onChange={this.handleSelectSource}
>
{[...driveList, { label: "Add data source", value: "add" }]
.filter(
(item) =>
this.props.dataSourceList.includes(item.value) ||
item.value === "local" ||
item.value === "add"
)
.map((item) => (
<option
value={item.value}
key={item.value}
className="lang-setting-option"
>
{this.props.t(item.label)}
</option>
))}
</select>
</div>
</div>
</div>
) : this.state.currentStep === 1 ? (
<div className="backup-page-drive-container">
<div>{renderDrivePage()}</div>
</div>
) : (
<div className="backup-page-finish-container">
@@ -256,11 +234,6 @@ class BackupDialog extends React.Component<
: "Restore successful"}
</Trans>
</div>
{this.state.isBackup ? null : (
<div style={{ opacity: 0.6 }}>
<Trans>Try refresh or restart</Trans>
</div>
)}
</div>
</div>
)}

View File

@@ -17,6 +17,7 @@ const mapStateToProps = (state: stateType) => {
notes: state.reader.notes,
digests: state.reader.digests,
isOpenTokenDialog: state.backupPage.isOpenTokenDialog,
dataSourceList: state.backupPage.dataSourceList,
};
};
const actionCreator = {

View File

@@ -14,6 +14,7 @@ export interface BackupDialogProps extends RouteComponentProps<any> {
books: BookModel[];
notes: NoteModel[];
digests: NoteModel[];
dataSourceList: string[];
bookmarks: BookmarkModel[];
}
export interface BackupDialogState {

View File

@@ -27,7 +27,8 @@ import {
} from "../../../utils/common";
import { getStorageLocation, reloadManager } from "../../../utils/common";
import DatabaseService from "../../../utils/storage/databaseService";
import { driveList } from "../../../constants/driveList";
import { driveInputConfig, driveList } from "../../../constants/driveList";
import { SyncUtil } from "../../../assets/lib/kookit-extra-browser.min";
declare var window: any;
class SettingDialog extends React.Component<
SettingInfoProps,
@@ -72,18 +73,16 @@ class SettingDialog extends React.Component<
}),
storageLocation: getStorageLocation() || "",
isAddNew: false,
currentDrive: "",
driveConfig: {},
};
}
componentDidMount(): void {
this.props.handleFetchPlugins();
this.loadFont();
let dataSourceList = ConfigService.getReaderConfig("dataSourceList") || [
"local",
];
if (dataSourceList) {
dataSourceList = JSON.parse(dataSourceList);
this.props.setDataSource(dataSourceList);
}
let dataSourceList = ConfigService.getAllListConfig("dataSourceList");
this.props.setDataSource(dataSourceList);
}
loadFont = () => {
if (dropdownList[0].option.length <= 2) {
@@ -207,6 +206,36 @@ class SettingDialog extends React.Component<
}
this.handleSetting("isOpenInMain");
};
handleCancel = () => {
this.setState({ currentDrive: "" });
};
handleConfirm = async () => {
if (
this.state.currentDrive === "webdav" ||
this.state.currentDrive === "ftp" ||
this.state.currentDrive === "sftp" ||
this.state.currentDrive === "s3compatible"
) {
ConfigService.setReaderConfig(
`${this.state.currentDrive}_token`,
JSON.stringify(this.state.driveConfig)
);
} else {
let syncUtil = new SyncUtil(this.state.currentDrive, {});
let refreshToken = await syncUtil.authToken(this.state.driveConfig.token);
ConfigService.setReaderConfig(
`${this.state.currentDrive}_token`,
JSON.stringify({ refresh_token: refreshToken })
);
}
ConfigService.setListConfig(this.state.currentDrive, "dataSourceList");
this.props.setDataSource(
ConfigService.getAllListConfig("dataSourceList") || []
);
this.setState({ currentDrive: "" });
toast.success(this.props.t("Addition successful"));
};
render() {
return (
<div className="setting-dialog-container">
@@ -280,7 +309,7 @@ class SettingDialog extends React.Component<
this.handleChangeTab("sync");
}}
>
<Trans>Sync and Backup</Trans>
<Trans>Sync and backup</Trans>
</span>
<span
className="book-bookmark-title"
@@ -653,6 +682,96 @@ class SettingDialog extends React.Component<
</>
) : this.state.currentTab === "sync" ? (
<>
{this.state.currentDrive && (
<div
className="voice-add-new-container"
style={{
marginLeft: "25px",
width: "calc(100% - 50px)",
fontWeight: 500,
}}
>
{this.state.currentDrive === "webdav" ||
this.state.currentDrive === "ftp" ||
this.state.currentDrive === "sftp" ||
this.state.currentDrive === "s3compatible" ? (
<>
{driveInputConfig[this.state.currentDrive].map((item) => {
return (
<input
type={item.type}
name={item.value}
key={item.value}
placeholder={this.props.t(item.label)}
onChange={(e) => {
this.setState((prevState) => ({
driveConfig: {
...prevState.driveConfig,
[item.value]: e.target.value,
},
}));
}}
id={"token-dialog-" + item.value + "-box"}
className="token-dialog-username-box"
/>
);
})}
</>
) : (
<>
<textarea
className="token-dialog-token-box"
id="token-dialog-token-box"
placeholder={this.props.t(
"Please authorize your account, and fill the following box with the token"
)}
onChange={(e) => {
this.setState((prevState) => ({
driveConfig: {
...prevState.driveConfig,
token: e.target.value,
},
}));
}}
/>
</>
)}
<div className="token-dialog-button-container">
<div
className="voice-add-confirm"
onClick={async () => {
this.handleConfirm();
}}
>
<Trans>Confirm</Trans>
</div>
<div className="voice-add-button-container">
<div
className="voice-add-cancel"
onClick={() => {
this.handleCancel();
}}
>
<Trans>Cancel</Trans>
</div>
<div
className="voice-add-cancel"
style={{ marginRight: "10px" }}
onClick={() => {
this.handleJump(
new SyncUtil(
this.state.currentDrive,
{}
).getAuthUrl()
);
}}
>
<Trans>Authorize</Trans>
</div>
</div>
</div>
</div>
)}
<div className="setting-dialog-new-title">
<Trans>Add data source</Trans>
<select
@@ -661,6 +780,7 @@ class SettingDialog extends React.Component<
onChange={(event) => {
if (
(event.target.value === "ftp" ||
event.target.value === "webdav" ||
event.target.value === "sftp") &&
!isElectron
) {
@@ -671,12 +791,27 @@ class SettingDialog extends React.Component<
);
return;
}
let dataSourceList = this.props.dataSourceList;
if (
event.target.value === "google" ||
event.target.value === "s3compatible" ||
event.target.value === "microsoft" ||
event.target.value === "dropbox"
) {
toast(
this.props.t(
"This feature is not available in the free version"
)
);
return;
}
this.setState({ currentDrive: event.target.value });
}}
>
{driveList
{[{ label: "Please select", value: "" }, ...driveList]
.filter(
(item) => !this.props.dataSourceList.includes(item.value)
(item) =>
!this.props.dataSourceList.includes(item.value) &&
item.value !== "local"
)
.map((item) => (
<option
@@ -684,7 +819,59 @@ class SettingDialog extends React.Component<
key={item.value}
className="lang-setting-option"
>
{item.label}
{this.props.t(item.label)}
</option>
))}
</select>
</div>
<div className="setting-dialog-new-title">
<Trans>Delete data source</Trans>
<select
name=""
className="lang-setting-dropdown"
onChange={(event) => {
this.setState({ currentDrive: event.target.value });
}}
>
{[{ label: "Please select", value: "" }, ...driveList]
.filter(
(item) =>
this.props.dataSourceList.includes(item.value) ||
item.value === ""
)
.map((item) => (
<option
value={item.value}
key={item.value}
className="lang-setting-option"
>
{this.props.t(item.label)}
</option>
))}
</select>
</div>
<div className="setting-dialog-new-title">
<Trans>Set default sync option</Trans>
<select
name=""
className="lang-setting-dropdown"
onChange={(event) => {
this.setState({ currentDrive: event.target.value });
}}
>
{[{ label: "Please select", value: "" }, ...driveList]
.filter(
(item) =>
this.props.dataSourceList.includes(item.value) ||
item.value === ""
)
.map((item) => (
<option
value={item.value}
key={item.value}
className="lang-setting-option"
>
{this.props.t(item.label)}
</option>
))}
</select>
@@ -693,24 +880,23 @@ class SettingDialog extends React.Component<
) : (
<>
{(this.props.plugins.length === 0 || this.state.isAddNew) && (
<div className="navigation-panel-empty-bookmark">
<div
className="voice-add-new-container"
style={{
marginLeft: "10px",
width: "88%",
fontWeight: 500,
}}
>
<textarea
name="url"
placeholder={this.props.t(
"Paste the code of the plugin here, check out document to learn how to get more plugins"
)}
id="voice-add-content-box"
className="voice-add-content-box"
/>
<div
className="voice-add-new-container"
style={{
marginLeft: "25px",
width: "calc(100% - 50px)",
fontWeight: 500,
}}
>
<textarea
name="url"
placeholder={this.props.t(
"Paste the code of the plugin here, check out document to learn how to get more plugins"
)}
id="voice-add-content-box"
className="voice-add-content-box"
/>
<div className="token-dialog-button-container">
<div
className="voice-add-confirm"
onClick={async () => {

View File

@@ -8,6 +8,7 @@ import {
handleFetchBooks,
handleFetchPlugins,
setDataSource,
handleTokenDialog,
} from "../../../store/actions";
import { stateType } from "../../../store";
@@ -18,6 +19,7 @@ const mapStateToProps = (state: stateType) => {
plugins: state.manager.plugins,
notes: state.reader.notes,
dataSourceList: state.backupPage.dataSourceList,
isOpenTokenDialog: state.backupPage.isOpenTokenDialog,
};
};
const actionCreator = {
@@ -27,6 +29,7 @@ const actionCreator = {
handleFetchBooks,
handleFetchPlugins,
setDataSource,
handleTokenDialog,
};
export default connect(
mapStateToProps,

View File

@@ -5,6 +5,7 @@ import PluginModel from "../../../models/Plugin";
export interface SettingInfoProps {
handleSetting: (isSettingOpen: boolean) => void;
handleTipDialog: (isTipDialog: boolean) => void;
handleTokenDialog: (isOpenTokenDialog: boolean) => void;
handleTip: (tip: string) => void;
setDataSource: (dataSourceList: string[]) => void;
t: (title: string) => string;
@@ -12,6 +13,7 @@ export interface SettingInfoProps {
handleFetchPlugins: () => void;
bookmarks: BookmarkModel[];
notes: NoteModel[];
isOpenTokenDialog: boolean;
plugins: PluginModel[];
books: BookModel[];
dataSourceList: string[];
@@ -42,4 +44,6 @@ export interface SettingInfoState {
isAddNew: boolean;
currentThemeIndex: number;
currentTab: string;
currentDrive: string;
driveConfig: any;
}

View File

@@ -23,8 +23,8 @@
line-height: 20px;
opacity: 1;
margin-top: 10px;
padding-left: 30px;
padding-right: 30px;
margin-left: 25px;
width: calc(100% - 50px);
margin-bottom: 5px;
/* width: 280px; */
display: flex;
@@ -183,3 +183,33 @@
right: 10px;
cursor: pointer;
}
.token-dialog-button-container {
margin-top: 20px;
margin-bottom: 30px;
}
.token-dialog-token-box {
width: 100%;
height: 100px;
opacity: 1;
font-size: 13px;
line-height: 14px;
opacity: 1;
box-sizing: border-box;
/* left: 30px; */
border-radius: 5px;
padding: 5px;
margin-top: 10px;
box-sizing: border-box;
}
.token-dialog-username-box {
width: 100%;
height: 30px;
opacity: 1;
font-size: 13px;
line-height: 14px;
opacity: 1;
margin-top: 9px;
padding-left: 5px;
border-radius: 5px;
box-sizing: border-box;
}

View File

@@ -1,307 +0,0 @@
import React, { Component } from "react";
import "./tokenDialog.css";
import { Trans } from "react-i18next";
import { TokenDialogProps, TokenDialogState } from "./interface";
import ConfigService from "../../../utils/storage/configService";
import { SyncUtil } from "../../../assets/lib/kookit-extra-browser.min";
import toast from "react-hot-toast";
import { openExternalUrl } from "../../../utils/common";
import { driveInputConfig, driveList } from "../../../constants/driveList";
class TokenDialog extends Component<TokenDialogProps, TokenDialogState> {
constructor(props: TokenDialogProps) {
super(props);
this.state = { isNew: false, config: {} };
}
handleCancel = () => {
this.props.handleTokenDialog(false);
};
handleDropboxComfirm = async () => {
let code: string = (
document.querySelector("#token-dialog-token-box") as HTMLTextAreaElement
).value;
let syncUtil = new SyncUtil("dropbox", {});
let refreshToken = await syncUtil.authToken(code);
ConfigService.setReaderConfig(
`${this.props.driveName}_token`,
JSON.stringify({ refresh_token: refreshToken })
);
this.props.handleTokenDialog(false);
toast.success(this.props.t("Addition successful"));
};
handleOneDriveComfirm = async () => {
let code: string = (
document.querySelector("#token-dialog-token-box") as HTMLTextAreaElement
).value;
let syncUtil = new SyncUtil("onedrive", {});
let refreshToken = await syncUtil.authToken(code);
ConfigService.setReaderConfig(
`${this.props.driveName}_token`,
JSON.stringify({ refresh_token: refreshToken })
);
this.props.handleTokenDialog(false);
toast.success(this.props.t("Addition successful"));
};
handleGoogleDriveComfirm = async () => {
let code: string = (
document.querySelector("#token-dialog-token-box") as HTMLTextAreaElement
).value;
let syncUtil = new SyncUtil("googledrive", {});
let refreshToken = await syncUtil.authToken(code);
ConfigService.setReaderConfig(
`${this.props.driveName}_token`,
JSON.stringify({ refresh_token: refreshToken })
);
this.props.handleTokenDialog(false);
toast.success(this.props.t("Addition successful"));
};
handleDavComfirm = () => {
let url: string = (
document.querySelector("#token-dialog-url-box") as HTMLTextAreaElement
).value;
let username: string = (
document.querySelector(
"#token-dialog-username-box"
) as HTMLTextAreaElement
).value;
let password: string = (
document.querySelector(
"#token-dialog-password-box"
) as HTMLTextAreaElement
).value;
ConfigService.setReaderConfig(
`${this.props.driveName}_token`,
JSON.stringify({ url, username, password })
);
this.props.handleTokenDialog(false);
toast.success(this.props.t("Addition successful"));
};
handleFTPComfirm = () => {
let url: string = (
document.querySelector("#token-dialog-url-box") as HTMLTextAreaElement
).value;
let username: string = (
document.querySelector(
"#token-dialog-username-box"
) as HTMLTextAreaElement
).value;
let password: string = (
document.querySelector(
"#token-dialog-password-box"
) as HTMLTextAreaElement
).value;
let dir: string = (
document.querySelector("#token-dialog-path-box") as HTMLTextAreaElement
).value;
let ssl: string = (
document.querySelector("#token-dialog-ssl-box") as HTMLTextAreaElement
).value;
ConfigService.setReaderConfig(
`${this.props.driveName}_token`,
JSON.stringify({ url, username, password, dir, ssl })
);
this.props.handleTokenDialog(false);
toast.success(this.props.t("Addition successful"));
};
handleS3Comfirm = () => {
let endpoint: string = (
document.querySelector(
"#token-dialog-endpoint-box"
) as HTMLTextAreaElement
).value;
let region: string = (
document.querySelector("#token-dialog-region-box") as HTMLTextAreaElement
).value;
let bucketName: string = (
document.querySelector("#token-dialog-bucket-box") as HTMLTextAreaElement
).value;
let accessKeyId: string = (
document.querySelector("#token-dialog-id-box") as HTMLTextAreaElement
).value;
let secretAccessKey: string = (
document.querySelector("#token-dialog-key-box") as HTMLTextAreaElement
).value;
ConfigService.setReaderConfig(
`${this.props.driveName}_token`,
JSON.stringify({
endpoint,
region,
bucketName,
accessKeyId,
secretAccessKey,
})
);
this.props.handleTokenDialog(false);
toast.success(this.props.t("Addition successful"));
};
handleSFTPComfirm = () => {
let url: string = (
document.querySelector("#token-dialog-url-box") as HTMLTextAreaElement
).value;
let username: string = (
document.querySelector(
"#token-dialog-username-box"
) as HTMLTextAreaElement
).value;
let password: string = (
document.querySelector(
"#token-dialog-password-box"
) as HTMLTextAreaElement
).value;
let dir: string = (
document.querySelector("#token-dialog-path-box") as HTMLTextAreaElement
).value;
let port: string = (
document.querySelector("#token-dialog-port-box") as HTMLTextAreaElement
).value;
ConfigService.setReaderConfig(
`${this.props.driveName}_token`,
JSON.stringify({ url, username, password, dir, port })
);
this.props.handleTokenDialog(false);
toast.success(this.props.t("Addition successful"));
};
handleJump = (url: string) => {
openExternalUrl(url);
};
handleConfirm = async () => {
if (
this.props.driveName === "webdav" ||
this.props.driveName === "ftp" ||
this.props.driveName === "sftp" ||
this.props.driveName === "s3compatible"
) {
ConfigService.setReaderConfig(
`${this.props.driveName}_token`,
JSON.stringify(this.state.config)
);
} else {
let syncUtil = new SyncUtil(this.props.driveName, {});
let refreshToken = await syncUtil.authToken(this.state.config.token);
ConfigService.setReaderConfig(
`${this.props.driveName}_token`,
JSON.stringify({ refresh_token: refreshToken })
);
}
this.props.handleTokenDialog(false);
toast.success(this.props.t("Addition successful"));
};
render() {
return (
<div className="token-dialog-container">
<div className="token-dialog-box">
<div className="token-dialog-title">
<Trans>Authorize</Trans>
&nbsp;
{this.props.title}&nbsp;
<Trans>Token</Trans>
</div>
{this.props.driveName === "webdav" ||
this.props.driveName === "ftp" ||
this.props.driveName === "sftp" ||
this.props.driveName === "s3compatible" ? (
<>
<div
className="token-dialog-info-text"
style={
ConfigService.getReaderConfig("lang") === "en"
? { fontSize: "14px" }
: {}
}
>
<Trans>
{
driveList.find(
(item) => item.value === this.props.driveName
)!.label
}{" "}
Info
</Trans>
</div>
{driveInputConfig[this.props.driveName].map((item) => {
return (
<input
type={item.type}
name={item.value}
placeholder={
this.props.t(item.label) + ", " + item.placeholder
}
onChange={(e) => {
this.setState((prevState) => ({
config: {
...prevState.config,
[item.value]: e.target.value,
},
}));
}}
id={"token-dialog-" + item.value + "-box"}
className="token-dialog-username-box"
/>
);
})}
</>
) : (
<>
<div
className="token-dialog-info-text"
style={
ConfigService.getReaderConfig("lang") === "en"
? { fontSize: "14px" }
: {}
}
>
<Trans>
Please authorize your account, and fill the following box with
the token
</Trans>
</div>
<div
className="token-dialog-link-text"
onClick={() => {
this.handleJump(this.props.url);
}}
>
<Trans>Authorize</Trans>
</div>
<textarea
className="token-dialog-token-box"
id="token-dialog-token-box"
placeholder={this.props.t("Token")}
onChange={(e) => {
this.setState((prevState) => ({
config: {
...prevState.config,
token: e.target.value,
},
}));
}}
/>
</>
)}
<div className="add-dialog-button-container">
<div
className="add-dialog-cancel"
onClick={() => {
this.handleCancel();
}}
>
<Trans>Cancel</Trans>
</div>
<div
className="add-dialog-confirm"
onClick={() => {
this.handleConfirm();
}}
>
<Trans>Confirm</Trans>
</div>
</div>
</div>
</div>
);
}
}
export default TokenDialog;

View File

@@ -1,22 +0,0 @@
import { connect } from "react-redux";
import { handleTokenDialog } from "../../../store/actions";
import { stateType } from "../../../store";
import { withTranslation } from "react-i18next";
import TokenDialog from "./component";
const mapStateToProps = (state: stateType) => {
return {
books: state.manager.books,
isOpenDeleteDialog: state.book.isOpenDeleteDialog,
currentBook: state.book.currentBook,
bookmarks: state.reader.bookmarks,
notes: state.reader.notes,
digests: state.reader.digests,
};
};
const actionCreator = {
handleTokenDialog,
};
export default connect(
mapStateToProps,
actionCreator
)(withTranslation()(TokenDialog as any) as any);

View File

@@ -1,13 +0,0 @@
import BookModel from "../../../models/Book";
export interface TokenDialogProps {
handleTokenDialog: (isShow: boolean) => void;
currentBook: BookModel;
driveName: string;
title: string;
url: string;
t: (title: string) => string;
}
export interface TokenDialogState {
isNew: boolean;
config: any;
}

View File

@@ -1,110 +0,0 @@
.token-dialog-container {
width: 454px;
height: 278px;
position: absolute;
left: calc(50% - 227px);
top: calc(50% - 139px);
overflow: hidden;
opacity: 1;
z-index: 20;
animation: popup 0.1s ease-in-out 0s 1;
border-radius: 5px;
}
.token-dialog-title {
width: 120px;
height: 20px;
font-size: 18px;
opacity: 1;
width: 100%;
text-align: center;
margin-top: 15px;
font-weight: 500;
}
.token-dialog-info-text {
width: 90%;
height: 46px;
font-size: 15px;
font-weight: 500;
line-height: 20px;
opacity: 1;
text-align: center;
overflow-y: scroll;
margin-top: 10px;
margin-left: 5%;
}
.token-dialog-link-text {
width: 94px;
height: 29px;
opacity: 1;
position: absolute;
left: 181px;
top: 100px;
font-size: 13px;
font-weight: 500;
line-height: 29px;
text-align: center;
cursor: pointer;
border-radius: 5px;
}
.token-dialog-token-box {
width: calc(100% - 50px);
margin-left: 25px;
height: 70px;
opacity: 1;
font-size: 13px;
line-height: 14px;
opacity: 1;
position: absolute;
top: 144px;
box-sizing: border-box;
/* left: 30px; */
border-radius: 5px;
}
.token-dialog-url-box,
.token-dialog-username-box,
.token-dialog-ssl-box,
.token-dialog-path-box,
.token-dialog-password-box {
width: 322px;
height: 23px;
opacity: 1;
font-size: 13px;
line-height: 14px;
opacity: 1;
margin-left: 60px;
margin-top: 9px;
padding-left: 5px;
border-radius: 5px;
}
.token-dialog-cancel {
width: 60px;
height: 26px;
opacity: 1;
position: absolute;
left: 157px;
top: 236px;
font-size: 13px;
line-height: 26px;
opacity: 1;
text-align: center;
cursor: pointer;
border-radius: 5px;
}
.token-dialog-confirm {
width: 64px;
height: 30px;
opacity: 1;
position: absolute;
left: 237px;
top: 236px;
font-size: 13px;
line-height: 30px;
opacity: 1;
text-align: center;
cursor: pointer;
border-radius: 5px;
}

View File

@@ -16,52 +16,57 @@ export const driveList = [
label: "SFTP",
value: "sftp",
},
{
label: "S3 Compatible",
label: "S3 Compatible (Pro)",
value: "s3compatible",
},
{
label: "Dropbox",
label: "Dropbox (Pro)",
value: "dropbox",
isPro: true,
},
{
label: "OneDrive",
label: "OneDrive (Pro)",
value: "microsoft",
isPro: true,
},
{
label: "Google Drive",
label: "Google Drive (Pro)",
value: "google",
isPro: true,
},
];
export const driveInputConfig = {
interface ConfigItem {
label: string;
value: string;
type: string;
}
// Type the driveInputConfig
interface DriveInputConfig {
[key: string]: ConfigItem[];
}
export const driveInputConfig: DriveInputConfig = {
webdav: [
{
label: "Server address",
value: "url",
type: "text",
placeholder: "https://example.com",
},
{
label: "Path",
label: "Server path",
value: "path",
type: "text",
placeholder: "/path/to/folder",
},
{
label: "Username",
value: "username",
type: "text",
placeholder: "username",
},
{
label: "Password",
value: "password",
type: "password",
placeholder: "password",
},
],
ftp: [
@@ -69,31 +74,26 @@ export const driveInputConfig = {
label: "Server address",
value: "url",
type: "text",
placeholder: "ftp://example.com",
},
{
label: "Server port",
value: "port",
type: "text",
placeholder: "21",
},
{
label: "Server Path",
label: "Server path",
value: "path",
type: "text",
placeholder: "/path/to/folder",
},
{
label: "Username",
value: "username",
type: "text",
placeholder: "username",
},
{
label: "Password",
value: "password",
type: "password",
placeholder: "password",
},
],
sftp: [
@@ -101,31 +101,26 @@ export const driveInputConfig = {
label: "Server address",
value: "url",
type: "text",
placeholder: "sftp://example.com",
},
{
label: "Server port",
value: "port",
type: "text",
placeholder: "22",
},
{
label: "Server Path",
value: "path",
type: "text",
placeholder: "/path/to/folder",
},
{
label: "Username",
value: "username",
type: "text",
placeholder: "username",
},
{
label: "Password",
value: "password",
type: "password",
placeholder: "password",
},
],
s3compatible: [
@@ -144,6 +139,11 @@ export const driveInputConfig = {
value: "bucketName",
type: "text",
},
{
label: "Server Path",
value: "path",
type: "text",
},
{
label: "AccessKeyId",
value: "accessKeyId",