mirror of
https://github.com/koodo-reader/koodo-reader.git
synced 2026-06-19 05:11:09 -04:00
feat: update version to 1.9.8 and enhance cloud sync functionality
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "koodo-reader",
|
||||
"main": "main.js",
|
||||
"version": "1.9.7",
|
||||
"version": "1.9.8",
|
||||
"description": "Koodo Reader is a cross-platform ebook reader",
|
||||
"author": {
|
||||
"name": "App by Troye",
|
||||
|
||||
2
src/assets/lib/kookit-extra-browser.min.js
vendored
2
src/assets/lib/kookit-extra-browser.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
src/assets/lib/kookit.min.js
vendored
2
src/assets/lib/kookit.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -341,6 +341,7 @@
|
||||
"Beta pharse": "内测期间",
|
||||
"Trial user": "试用用户",
|
||||
"Paid user": "付费用户",
|
||||
"Pro user": "专业版用户",
|
||||
"Free user": "免费用户",
|
||||
"Get device identifier": "获取设备标识符",
|
||||
"Sync failed": "同步失败",
|
||||
@@ -588,7 +589,9 @@
|
||||
"Reset": "重置",
|
||||
"Pro version": "升级专业版",
|
||||
"Upgrade to Pro": "升级专业版",
|
||||
"To ensure a smooth synchronization experience, your reading progress, notes, highlights, bookmarks, and other data will be stored and synced through our cloud service": "为了确保流畅的同步体验,您的阅读进度、笔记、高亮、书签等数据将通过我们的云服务进行存储和同步",
|
||||
"Enable this option to increase synchronization speed. Your reading progress, notes, highlights, bookmarks, and other reading-related data will be stored and synced via our cloud service. Turning off this option will remove the above data from our cloud.": "启用此选项以提高同步速度。您的阅读进度、笔记、高亮、书签等阅读相关数据将通过我们的云服务进行存储和同步。关闭此选项将从我们的云端删除上述数据",
|
||||
"Access may be unstable in China": "国内网络访问可能不稳定",
|
||||
"Only WebDAV service provided by Alist is directly supported in Browser, Other WebDAV services need to enable CORS to work properly": "由于浏览器的限制,仅支持 Alist 提供的 WebDAV 服务,其他 WebDAV 服务需要开启 CORS 才能正常使用",
|
||||
"Enable Koodo Sync": "启用 Koodo Sync",
|
||||
"Disable automatic sync": "禁用自动同步",
|
||||
"By default, Koodo Reader will automatically synchronize your data when you open the app and exit reading": "默认情况下,Koodo Reader 会在您打开应用和退出阅读时自动同步数据",
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
/* .dict-container li div {
|
||||
display: inline;
|
||||
} */
|
||||
.dict-container ul {
|
||||
/* .dict-container ul {
|
||||
counter-reset: my-counter;
|
||||
}
|
||||
|
||||
@@ -117,6 +117,37 @@
|
||||
.dict-container li::before {
|
||||
content: counter(my-counter) ". ";
|
||||
font-weight: bold;
|
||||
} */
|
||||
/* 在 popupDict.css 中添加这些规则,它们会覆盖 reset.css 中的全局规则 */
|
||||
.dict-container ul {
|
||||
list-style-type: disc !important; /* 实心圆点 */
|
||||
padding-left: 2em !important; /* 添加缩进 */
|
||||
margin: 1em 0 !important; /* 添加上下边距 */
|
||||
}
|
||||
|
||||
.dict-container ol {
|
||||
list-style-type: decimal !important; /* 数字编号 */
|
||||
padding-left: 2em !important; /* 添加缩进 */
|
||||
margin: 1em 0 !important; /* 添加上下边距 */
|
||||
}
|
||||
|
||||
.dict-container li {
|
||||
list-style: inherit !important; /* 继承父元素的列表样式 */
|
||||
display: list-item !important; /* 显示为列表项 */
|
||||
margin: 0.5em 0 !important; /* 添加上下边距 */
|
||||
}
|
||||
|
||||
/* 定义嵌套列表的样式 */
|
||||
.dict-container ul ul {
|
||||
list-style-type: circle !important; /* 二级列表使用空心圆点 */
|
||||
}
|
||||
|
||||
.dict-container ul ul ul {
|
||||
list-style-type: square !important; /* 三级列表使用方块 */
|
||||
}
|
||||
|
||||
.dict-container ol ol {
|
||||
list-style-type: lower-alpha !important; /* 二级有序列表使用小写字母 */
|
||||
}
|
||||
.audio-player {
|
||||
width: 100px;
|
||||
|
||||
@@ -4,10 +4,9 @@ export const driveList = [
|
||||
value: "webdav",
|
||||
icon: "icon-webdav",
|
||||
isPro: false,
|
||||
support: ["desktop", "phone"],
|
||||
support: ["desktop", "browser", "phone"],
|
||||
scoped: false,
|
||||
},
|
||||
|
||||
{
|
||||
label: "Dropbox",
|
||||
value: "dropbox",
|
||||
|
||||
@@ -82,7 +82,7 @@ export const syncSettingList = [
|
||||
{
|
||||
isElectron: false,
|
||||
title: "Enable Koodo Sync",
|
||||
desc: "To ensure a smooth synchronization experience, your reading progress, notes, highlights, bookmarks, and other data will be stored and synced through our cloud service",
|
||||
desc: "Enable this option to increase synchronization speed. Your reading progress, notes, highlights, bookmarks, and other reading-related data will be stored and synced via our cloud service. Turning off this option will remove the above data from our cloud.",
|
||||
propName: "isEnableKoodoSync",
|
||||
},
|
||||
{
|
||||
|
||||
@@ -114,6 +114,21 @@ class Header extends React.Component<HeaderProps, HeaderState> {
|
||||
this.props.handleFetchNotes();
|
||||
this.props.handleFetchBookmarks();
|
||||
});
|
||||
this.props.handleCloudSyncFunc(this.handleCloudSync);
|
||||
document.addEventListener("visibilitychange", () => {
|
||||
if (document.visibilityState === "visible") {
|
||||
if (ConfigService.getItem("isFinshReading") === "yes") {
|
||||
ConfigService.setItem("isFinshReading", "no");
|
||||
if (
|
||||
this.state.isAutoSync &&
|
||||
ConfigService.getItem("defaultSyncOption")
|
||||
) {
|
||||
this.setState({ isSync: true });
|
||||
this.handleCloudSync();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
async UNSAFE_componentWillReceiveProps(
|
||||
nextProps: Readonly<HeaderProps>,
|
||||
@@ -136,11 +151,10 @@ class Header extends React.Component<HeaderProps, HeaderState> {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
if (this.state.isAutoSync) {
|
||||
if (this.state.isAutoSync && ConfigService.getItem("defaultSyncOption")) {
|
||||
this.setState({ isSync: true });
|
||||
await this.handleCloudSync();
|
||||
}
|
||||
nextProps.handleFetchUserConfig();
|
||||
}
|
||||
if (!nextProps.isAuthed && nextProps.isAuthed !== this.props.isAuthed) {
|
||||
if (isElectron) {
|
||||
@@ -338,9 +352,9 @@ class Header extends React.Component<HeaderProps, HeaderState> {
|
||||
if (ConfigService.getReaderConfig("isEnableKoodoSync") === "yes") {
|
||||
ConfigUtil.updateSyncData();
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.props.history.push("/manager/home");
|
||||
}, 1000);
|
||||
// setTimeout(() => {
|
||||
// this.props.history.push("/manager/home");
|
||||
// }, 1000);
|
||||
};
|
||||
handleSync = async (compareResult) => {
|
||||
try {
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
handleFetchDefaultSyncOption,
|
||||
handleFetchLoginOptionList,
|
||||
handleFetchDataSourceList,
|
||||
handleFetchUserConfig,
|
||||
handleCloudSyncFunc,
|
||||
} from "../../store/actions";
|
||||
import { stateType } from "../../store";
|
||||
import Header from "./component";
|
||||
@@ -46,7 +46,7 @@ const actionCreator = {
|
||||
handleFetchDefaultSyncOption,
|
||||
handleFetchLoginOptionList,
|
||||
handleFetchDataSourceList,
|
||||
handleFetchUserConfig,
|
||||
handleCloudSyncFunc,
|
||||
};
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
|
||||
@@ -20,7 +20,6 @@ export interface HeaderProps extends RouteComponentProps<any> {
|
||||
handleImportDialog: (isOpenImportDialog: boolean) => void;
|
||||
handleFeedbackDialog: (isShow: boolean) => void;
|
||||
handleFetchAuthed: () => void;
|
||||
handleFetchUserConfig: () => void;
|
||||
handleFetchDefaultSyncOption: () => void;
|
||||
handleFetchLoginOptionList: () => void;
|
||||
handleFetchDataSourceList: () => void;
|
||||
@@ -29,6 +28,9 @@ export interface HeaderProps extends RouteComponentProps<any> {
|
||||
t: (title: string) => string;
|
||||
handleFetchNotes: () => void;
|
||||
handleFetchBookmarks: () => void;
|
||||
handleCloudSyncFunc: (
|
||||
cloudSyncFunc: () => Promise<false | undefined>
|
||||
) => void;
|
||||
}
|
||||
|
||||
export interface HeaderState {
|
||||
|
||||
@@ -75,8 +75,9 @@ class OperationPanel extends React.Component<
|
||||
ConfigService.setReaderConfig("isFullscreen", "yes");
|
||||
}
|
||||
}
|
||||
handleExit() {
|
||||
async handleExit() {
|
||||
ConfigService.setReaderConfig("isFullscreen", "no");
|
||||
ConfigService.setItem("isFinshReading", "yes");
|
||||
this.props.handleReadingState(false);
|
||||
this.props.handleSearch(false);
|
||||
window.speechSynthesis && window.speechSynthesis.cancel();
|
||||
@@ -85,7 +86,6 @@ class OperationPanel extends React.Component<
|
||||
if (this.props.htmlBook) {
|
||||
this.props.handleHtmlBook(null);
|
||||
}
|
||||
|
||||
if (isElectron) {
|
||||
if (ConfigService.getReaderConfig("isOpenInMain") === "yes") {
|
||||
window.require("electron").ipcRenderer.invoke("exit-tab", "ping");
|
||||
|
||||
@@ -21,7 +21,6 @@ export interface OperationPanelProps extends RouteComponentProps<any> {
|
||||
handleOpenMenu: (isOpenMenu: boolean) => void;
|
||||
handleShowBookmark: (isShowBookmark: boolean) => void;
|
||||
handleReadingBook: (currentBook: BookModel | object) => void;
|
||||
|
||||
t: (title: string) => string;
|
||||
handleHtmlBook: (htmlBook: HtmlBookModel | null) => void;
|
||||
}
|
||||
|
||||
@@ -647,7 +647,7 @@ class AccountSetting extends React.Component<
|
||||
{this.props.userInfo.type === "trial"
|
||||
? "Trial user"
|
||||
: this.props.userInfo.type === "pro"
|
||||
? "Paid user"
|
||||
? "Pro user"
|
||||
: "Free user"}
|
||||
</Trans>
|
||||
<>
|
||||
|
||||
@@ -11,6 +11,7 @@ import toast from "react-hot-toast";
|
||||
import {
|
||||
handleContextMenu,
|
||||
openExternalUrl,
|
||||
testConnection,
|
||||
WEBSITE_URL,
|
||||
} from "../../../utils/common";
|
||||
import { getStorageLocation } from "../../../utils/common";
|
||||
@@ -89,12 +90,13 @@ class SyncSetting extends React.Component<SettingInfoProps, SettingInfoState> {
|
||||
this.handleRest(this.state[stateName]);
|
||||
};
|
||||
handleAddDataSource = (event: any) => {
|
||||
if (!event.target.value) {
|
||||
let targetDrive = event.target.value;
|
||||
if (!targetDrive) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!driveList
|
||||
.find((item) => item.value === event.target.value)
|
||||
.find((item) => item.value === targetDrive)
|
||||
?.support.includes("browser") &&
|
||||
!isElectron
|
||||
) {
|
||||
@@ -106,14 +108,14 @@ class SyncSetting extends React.Component<SettingInfoProps, SettingInfoState> {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
driveList.find((item) => item.value === event.target.value)?.isPro &&
|
||||
driveList.find((item) => item.value === targetDrive)?.isPro &&
|
||||
!this.props.isAuthed
|
||||
) {
|
||||
toast(this.props.t("This feature is not available in the free version"));
|
||||
return;
|
||||
}
|
||||
this.props.handleSettingDrive(event.target.value);
|
||||
let settingDrive = event.target.value;
|
||||
this.props.handleSettingDrive(targetDrive);
|
||||
let settingDrive = targetDrive;
|
||||
if (
|
||||
settingDrive === "dropbox" ||
|
||||
settingDrive === "google" ||
|
||||
@@ -126,20 +128,25 @@ class SyncSetting extends React.Component<SettingInfoProps, SettingInfoState> {
|
||||
}
|
||||
};
|
||||
handleDeleteDataSource = async (event: any) => {
|
||||
if (!event.target.value) {
|
||||
let targetDrive = event.target.value;
|
||||
if (!targetDrive) {
|
||||
return;
|
||||
}
|
||||
await TokenService.setToken(event.target.value + "_token", "");
|
||||
SyncService.removeSyncUtil(event.target.value);
|
||||
removeCloudConfig(event.target.value);
|
||||
await TokenService.setToken(targetDrive + "_token", "");
|
||||
SyncService.removeSyncUtil(targetDrive);
|
||||
removeCloudConfig(targetDrive);
|
||||
if (isElectron) {
|
||||
const { ipcRenderer } = window.require("electron");
|
||||
await ipcRenderer.invoke("cloud-close", {
|
||||
service: event.target.value,
|
||||
service: targetDrive,
|
||||
});
|
||||
}
|
||||
ConfigService.deleteListConfig(event.target.value, "dataSourceList");
|
||||
ConfigService.deleteListConfig(targetDrive, "dataSourceList");
|
||||
this.props.handleFetchDataSourceList();
|
||||
if (targetDrive === ConfigService.getItem("defaultSyncOption")) {
|
||||
ConfigService.removeItem("defaultSyncOption");
|
||||
this.props.handleFetchDefaultSyncOption();
|
||||
}
|
||||
toast.success(this.props.t("Deletion successful"));
|
||||
};
|
||||
handleSetDefaultSyncOption = (event: any) => {
|
||||
@@ -328,10 +335,37 @@ class SyncSetting extends React.Component<SettingInfoProps, SettingInfoState> {
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{this.props.settingDrive === "webdav" && !isElectron && (
|
||||
<div
|
||||
className="token-dialog-tip"
|
||||
style={{
|
||||
marginTop: "10px",
|
||||
fontSize: "13px",
|
||||
lineHeight: "16px",
|
||||
color: "rgba(231, 69, 69, 0.8)",
|
||||
}}
|
||||
>
|
||||
{this.props.t(
|
||||
"Only WebDAV service provided by Alist is directly supported in Browser, Other WebDAV services need to enable CORS to work properly"
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="token-dialog-button-container">
|
||||
<div
|
||||
className="voice-add-confirm"
|
||||
onClick={async () => {
|
||||
if (
|
||||
this.props.settingDrive === "webdav" ||
|
||||
this.props.settingDrive === "s3compatible"
|
||||
) {
|
||||
let result = await testConnection(
|
||||
this.props.settingDrive,
|
||||
this.state.driveConfig
|
||||
);
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.handleConfirmDrive();
|
||||
}}
|
||||
>
|
||||
@@ -365,62 +399,24 @@ class SyncSetting extends React.Component<SettingInfoProps, SettingInfoState> {
|
||||
<Trans>Authorize</Trans>
|
||||
</div>
|
||||
)}
|
||||
{isElectron &&
|
||||
(this.props.settingDrive === "webdav" ||
|
||||
this.props.settingDrive === "ftp" ||
|
||||
this.props.settingDrive === "sftp" ||
|
||||
this.props.settingDrive === "mega" ||
|
||||
this.props.settingDrive === "s3compatible") && (
|
||||
<div
|
||||
className="voice-add-confirm"
|
||||
style={{ marginRight: "10px" }}
|
||||
onClick={async () => {
|
||||
toast.loading(this.props.t("Testing connection..."), {
|
||||
id: "testing-connection-id",
|
||||
});
|
||||
const { ipcRenderer } = window.require("electron");
|
||||
const fs = window.require("fs");
|
||||
fs.writeFileSync(
|
||||
getStorageLocation() + "/config/test.txt",
|
||||
"Hello world!"
|
||||
);
|
||||
let driveConfig: any = {};
|
||||
for (let item in this.state.driveConfig) {
|
||||
driveConfig[item] = this.state.driveConfig[item];
|
||||
}
|
||||
let result = await ipcRenderer.invoke("cloud-upload", {
|
||||
...driveConfig,
|
||||
fileName: "test.txt",
|
||||
service: this.props.settingDrive,
|
||||
type: "config",
|
||||
storagePath: getStorageLocation(),
|
||||
isUseCache: false,
|
||||
});
|
||||
if (result) {
|
||||
toast.success(this.props.t("Connection successful"), {
|
||||
id: "testing-connection-id",
|
||||
});
|
||||
await ipcRenderer.invoke("cloud-delete", {
|
||||
...driveConfig,
|
||||
fileName: "test.txt",
|
||||
service: this.props.settingDrive,
|
||||
type: "config",
|
||||
storagePath: getStorageLocation(),
|
||||
isUseCache: false,
|
||||
});
|
||||
} else {
|
||||
toast.error(this.props.t("Connection failed"), {
|
||||
id: "testing-connection-id",
|
||||
});
|
||||
}
|
||||
fs.unlinkSync(
|
||||
getStorageLocation() + "/config/test.txt"
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Trans>Test</Trans>
|
||||
</div>
|
||||
)}
|
||||
{(this.props.settingDrive === "webdav" ||
|
||||
this.props.settingDrive === "ftp" ||
|
||||
this.props.settingDrive === "sftp" ||
|
||||
this.props.settingDrive === "mega" ||
|
||||
this.props.settingDrive === "s3compatible") && (
|
||||
<div
|
||||
className="voice-add-confirm"
|
||||
style={{ marginRight: "10px" }}
|
||||
onClick={async () => {
|
||||
testConnection(
|
||||
this.props.settingDrive,
|
||||
this.state.driveConfig
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Trans>Test</Trans>
|
||||
</div>
|
||||
)}
|
||||
{(this.props.settingDrive === "webdav" ||
|
||||
this.props.settingDrive === "ftp" ||
|
||||
this.props.settingDrive === "s3compatible" ||
|
||||
|
||||
@@ -401,6 +401,15 @@ class Login extends React.Component<LoginProps, LoginState> {
|
||||
{this.props.t("Recommended (use with Nutstore)")}
|
||||
</div>
|
||||
)}
|
||||
{ConfigService.getReaderConfig("lang") &&
|
||||
ConfigService.getReaderConfig("lang").startsWith(
|
||||
"zh"
|
||||
) &&
|
||||
item.value === "microsoft" && (
|
||||
<div className="login-sync-text">
|
||||
{this.props.t("Access may be unstable in China")}
|
||||
</div>
|
||||
)}
|
||||
<div className="login-sync-subtitle">
|
||||
<div>
|
||||
{item.support.map((support) => {
|
||||
|
||||
@@ -65,6 +65,9 @@ class Reader extends React.Component<ReaderProps, ReaderState> {
|
||||
);
|
||||
}
|
||||
}, 1000);
|
||||
window.addEventListener("beforeunload", function (event) {
|
||||
ConfigService.setItem("isFinshReading", "yes");
|
||||
});
|
||||
}
|
||||
UNSAFE_componentWillMount() {
|
||||
let url = document.location.href;
|
||||
|
||||
@@ -14,6 +14,9 @@ export function handleRenderBookFunc(renderBookFunc: () => void) {
|
||||
export function handleImportBookFunc(importBookFunc: () => void) {
|
||||
return { type: "HANDLE_IMPORT_BOOK_FUNC", payload: importBookFunc };
|
||||
}
|
||||
export function handleCloudSyncFunc(cloudSyncFunc: () => void) {
|
||||
return { type: "HANDLE_CLOUD_SYNC_FUNC", payload: cloudSyncFunc };
|
||||
}
|
||||
export function handleRenderNoteFunc(renderNoteFunc: () => void) {
|
||||
return { type: "HANDLE_RENDER_NOTE_FUNC", payload: renderNoteFunc };
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import BookModel from "../../models/Book";
|
||||
import PluginModel from "../../models/Plugin";
|
||||
import { Dispatch } from "redux";
|
||||
import DatabaseService from "../../utils/storage/databaseService";
|
||||
import { fetchUserConfig, fetchUserInfo } from "../../utils/request/user";
|
||||
import { fetchUserInfo } from "../../utils/request/user";
|
||||
import {
|
||||
officialDictList,
|
||||
officialTranList,
|
||||
@@ -30,9 +30,6 @@ export function handleSearch(isSearch: boolean) {
|
||||
export function handleUserInfo(userInfo: any) {
|
||||
return { type: "HANDLE_USER_INFO", payload: userInfo };
|
||||
}
|
||||
export function handleUserConfig(userConfig: any) {
|
||||
return { type: "HANDLE_USER_CONFIG", payload: userConfig };
|
||||
}
|
||||
export function handleDetailDialog(isDetailDialog: boolean) {
|
||||
return { type: "HANDLE_DETAIL_DIALOG", payload: isDetailDialog };
|
||||
}
|
||||
@@ -115,6 +112,10 @@ export function handleFetchUserInfo() {
|
||||
let userInfo: any = null;
|
||||
if (response.code === 200) {
|
||||
userInfo = response.data;
|
||||
ConfigService.setReaderConfig(
|
||||
"isEnableKoodoSync",
|
||||
userInfo.is_enable_koodo_sync || "no"
|
||||
);
|
||||
}
|
||||
if (
|
||||
userInfo &&
|
||||
@@ -125,21 +126,6 @@ export function handleFetchUserInfo() {
|
||||
dispatch(handleUserInfo(userInfo));
|
||||
};
|
||||
}
|
||||
export function handleFetchUserConfig() {
|
||||
return async (dispatch: Dispatch) => {
|
||||
let response = await fetchUserConfig();
|
||||
let userConfig: any = null;
|
||||
if (response.code === 200) {
|
||||
userConfig = response.data;
|
||||
console.log("UserConfig", userConfig);
|
||||
ConfigService.setReaderConfig(
|
||||
"isEnableKoodoSync",
|
||||
userConfig.is_enable_koodo_sync
|
||||
);
|
||||
}
|
||||
dispatch(handleUserConfig(userConfig));
|
||||
};
|
||||
}
|
||||
export function handleFetchPlugins() {
|
||||
return async (dispatch: Dispatch) => {
|
||||
DatabaseService.getAllRecords("plugins").then((pluginList) => {
|
||||
|
||||
@@ -47,7 +47,6 @@ export type stateType = {
|
||||
isShowSupport: boolean;
|
||||
isShowNew: boolean;
|
||||
userInfo: any;
|
||||
userConfig: any;
|
||||
isAuthed: boolean;
|
||||
isNewWarning: boolean;
|
||||
isSelectBook: boolean;
|
||||
@@ -72,6 +71,7 @@ export type stateType = {
|
||||
currentBook: BookModel;
|
||||
renderBookFunc: () => void;
|
||||
importBookFunc: (file: any) => Promise<void>;
|
||||
cloudSyncFunc: () => Promise<void>;
|
||||
renderNoteFunc: () => void;
|
||||
};
|
||||
backupPage: {
|
||||
|
||||
@@ -10,6 +10,7 @@ const initState = {
|
||||
totalPage: 1,
|
||||
renderBookFunc: () => {},
|
||||
importBookFunc: () => {},
|
||||
cloudSyncFunc: () => {},
|
||||
renderNoteFunc: () => {},
|
||||
};
|
||||
export function book(
|
||||
@@ -42,6 +43,11 @@ export function book(
|
||||
...state,
|
||||
importBookFunc: action.payload,
|
||||
};
|
||||
case "HANDLE_CLOUD_SYNC_FUNC":
|
||||
return {
|
||||
...state,
|
||||
cloudSyncFunc: action.payload,
|
||||
};
|
||||
case "HANDLE_RENDER_NOTE_FUNC":
|
||||
return {
|
||||
...state,
|
||||
|
||||
@@ -5,12 +5,16 @@ import {
|
||||
BookHelper,
|
||||
CommonTool,
|
||||
ConfigService,
|
||||
SyncUtil,
|
||||
} from "../assets/lib/kookit-extra-browser.min";
|
||||
import Book from "../models/Book";
|
||||
import BookUtil from "./file/bookUtil";
|
||||
import * as Kookit from "../assets/lib/kookit.min";
|
||||
import DatabaseService from "./storage/databaseService";
|
||||
import packageJson from "../../package.json";
|
||||
import toast from "react-hot-toast";
|
||||
import i18n from "../i18n";
|
||||
import { getThirdpartyRequest } from "./request/thirdparty";
|
||||
declare var window: any;
|
||||
export const calculateFileMD5 = (file: File): Promise<string> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -494,3 +498,62 @@ export const checkMissingBook = (bookList: Book[]) => {
|
||||
}
|
||||
}
|
||||
};
|
||||
export const testConnection = async (driveName: string, driveConfig: any) => {
|
||||
toast.loading(i18n.t("Testing connection..."), {
|
||||
id: "testing-connection-id",
|
||||
});
|
||||
if (isElectron) {
|
||||
const { ipcRenderer } = window.require("electron");
|
||||
const fs = window.require("fs");
|
||||
fs.writeFileSync(getStorageLocation() + "/config/test.txt", "Hello world!");
|
||||
let result = await ipcRenderer.invoke("cloud-upload", {
|
||||
...driveConfig,
|
||||
fileName: "test.txt",
|
||||
service: driveName,
|
||||
type: "config",
|
||||
storagePath: getStorageLocation(),
|
||||
isUseCache: false,
|
||||
});
|
||||
if (result) {
|
||||
toast.success(i18n.t("Connection successful"), {
|
||||
id: "testing-connection-id",
|
||||
});
|
||||
await ipcRenderer.invoke("cloud-delete", {
|
||||
...driveConfig,
|
||||
fileName: "test.txt",
|
||||
service: driveName,
|
||||
type: "config",
|
||||
storagePath: getStorageLocation(),
|
||||
isUseCache: false,
|
||||
});
|
||||
} else {
|
||||
toast.error(i18n.t("Connection failed"), {
|
||||
id: "testing-connection-id",
|
||||
});
|
||||
}
|
||||
fs.unlinkSync(getStorageLocation() + "/config/test.txt");
|
||||
return result;
|
||||
} else {
|
||||
let thirdpartyRequest = await getThirdpartyRequest();
|
||||
let syncUtil = new SyncUtil(driveName, driveConfig, thirdpartyRequest);
|
||||
// 上传到云端
|
||||
let result = await syncUtil.uploadFile(
|
||||
"test.txt",
|
||||
"config",
|
||||
new Blob(["Hello world!"])
|
||||
);
|
||||
if (!result) {
|
||||
toast.error(i18n.t("Connection failed"), {
|
||||
id: "testing-connection-id",
|
||||
});
|
||||
return false;
|
||||
} else {
|
||||
toast.success(i18n.t("Connection successful"), {
|
||||
id: "testing-connection-id",
|
||||
});
|
||||
}
|
||||
|
||||
// 删除云端文件
|
||||
return await syncUtil.deleteFile("test.txt", "config");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -116,7 +116,6 @@ class ConfigUtil {
|
||||
let response = await thirdpartyRequest.getSyncData();
|
||||
if (response.code === 200) {
|
||||
let syncData = response.data;
|
||||
console.log("getSyncData", syncData);
|
||||
this.syncData = syncData;
|
||||
return JSON.parse(this.syncData[type] || defaultValue);
|
||||
} else if (response.code === 401) {
|
||||
@@ -130,8 +129,6 @@ class ConfigUtil {
|
||||
}
|
||||
}
|
||||
static async updateSyncData() {
|
||||
console.log("updateSyncData", this.updateData);
|
||||
|
||||
let thirdpartyRequest = await getThirdpartyRequest();
|
||||
|
||||
let response = await thirdpartyRequest.updateSyncData(this.updateData);
|
||||
@@ -159,7 +156,6 @@ class ConfigUtil {
|
||||
static async getCloudDatabase(database: string) {
|
||||
if (ConfigService.getReaderConfig("isEnableKoodoSync") === "yes") {
|
||||
let data = await this.getSyncData(database);
|
||||
console.log("getCloudDatabase", database, data);
|
||||
return data || [];
|
||||
}
|
||||
if (isElectron) {
|
||||
@@ -210,7 +206,6 @@ class ConfigUtil {
|
||||
return record;
|
||||
});
|
||||
}
|
||||
console.log("uploadDatabase", type, data);
|
||||
this.updateData[type] = JSON.stringify(data);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -54,14 +54,6 @@ export const fetchUserInfo = async () => {
|
||||
}
|
||||
return response;
|
||||
};
|
||||
export const fetchUserConfig = async () => {
|
||||
let userRequest = await getUserRequest();
|
||||
let response = await userRequest.getUserConfig();
|
||||
if (response.code === 401) {
|
||||
handleExitApp();
|
||||
}
|
||||
return response;
|
||||
};
|
||||
export const updateUserConfig = async (config: any) => {
|
||||
let userRequest = await getUserRequest();
|
||||
let response = await userRequest.updateUserConfig(config);
|
||||
|
||||
Reference in New Issue
Block a user