Files
koodo-reader/src/containers/header/component.tsx
troyeguo 8535da00ad Refactor components to improve logging and remove console statements
- Removed unnecessary console logs from ImportLocal, TextToSpeech, Header, and Viewer components to clean up the code and enhance performance.
- Streamlined the components for better readability and maintainability by eliminating debug statements that are no longer needed.
2026-06-03 15:44:10 +08:00

1001 lines
32 KiB
TypeScript

import React from "react";
import "./header.css";
import SearchBox from "../../components/searchBox";
import ImportLocal from "../../components/importLocal";
import { HeaderProps, HeaderState } from "./interface";
import {
ConfigService,
KookitConfig,
TokenService,
KOReaderUtil,
} from "../../assets/lib/kookit-extra-browser.min";
import UpdateInfo from "../../components/dialogs/updateDialog";
import { restoreFromConfigJson } from "../../utils/file/restore";
import { backupToConfigJson, generateSnapshot } from "../../utils/file/backup";
import { isElectron } from "react-device-detect";
import {
getCloudConfig,
getLastSyncTimeFromConfigJson,
removeCloudConfig,
upgradeConfig,
upgradeStorage,
} from "../../utils/file/common";
import toast from "react-hot-toast";
import { Trans } from "react-i18next";
import { SyncHelper } from "../../assets/lib/kookit-extra-browser.min";
import ConfigUtil from "../../utils/file/configUtil";
import DatabaseService from "../../utils/storage/databaseService";
import CoverUtil from "../../utils/file/coverUtil";
import BookUtil from "../../utils/file/bookUtil";
import {
addChatBox,
checkBrokenDatabase,
checkMissingBook,
generateSyncRecord,
getBookPartialMd5,
getChatLocale,
getTaskStats,
getWebsiteUrl,
openInBrowser,
removeChatBox,
resetKoodoSync,
showTaskProgress,
throttle,
vexComfirmAsync,
} from "../../utils/common";
import { driveList } from "../../constants/driveList";
import SupportDialog from "../../components/dialogs/supportDialog";
import SyncService from "../../utils/storage/syncService";
import { LocalFileManager } from "../../utils/file/localFile";
import packageJson from "../../../package.json";
import { getTempToken, updateUserConfig } from "../../utils/request/user";
import i18n from "../../i18n";
declare var window: any;
class Header extends React.Component<HeaderProps, HeaderState> {
timer: any;
scheduledSyncTimer: any;
private isSyncing: boolean = false;
private resizeHandler: (() => void) | null = null;
constructor(props: HeaderProps) {
super(props);
this.state = {
isOnlyLocal: false,
language: ConfigService.getReaderConfig("lang"),
isNewVersion: false,
width: document.body.clientWidth,
isDataChange: false,
isHidePro: false,
isSync: false,
};
}
async componentDidMount() {
if (isElectron) {
try {
await generateSnapshot();
} catch (error) {
console.error("Failed to generate snapshot:", error);
}
}
this.props.handleFetchAuthed();
this.props.handleFetchDefaultSyncOption();
this.props.handleFetchDataSourceList();
if (isElectron) {
const fs = window.require("fs");
const path = window.require("path");
const { ipcRenderer } = window.require("electron");
const dirPath = ipcRenderer.sendSync("user-data", "ping");
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(path.join(dirPath, "data", "book"), { recursive: true });
}
if (
ConfigService.getReaderConfig("storageLocation") &&
!ConfigService.getItem("storageLocation")
) {
ConfigService.setItem(
"storageLocation",
ConfigService.getReaderConfig("storageLocation")
);
}
if (ConfigService.getReaderConfig("isHidePro") === "yes") {
this.setState({ isHidePro: true });
}
//Check for data update
//upgrade data from old version
let res1 = await upgradeStorage(this.handleFinishUpgrade);
let res2 = upgradeConfig();
if (!res1 || !res2) {
console.error("upgrade failed");
}
//Detect data modification
let lastSyncTime = getLastSyncTimeFromConfigJson();
if (
ConfigService.getItem("lastSyncTime") &&
lastSyncTime > parseInt(ConfigService.getItem("lastSyncTime") || "0")
) {
this.setState({ isDataChange: true });
}
ipcRenderer.on("reading-finished", async (event: any, config: any) => {
this.handleFinishReading();
});
ipcRenderer.on(
"open-book-from-link",
async (_event: any, config: any) => {
const book = await DatabaseService.getRecord(config.bookKey, "books");
if (book) {
BookUtil.redirectBook(book);
}
}
);
ipcRenderer.on(
"open-note-from-link",
async (_event: any, config: any) => {
const note = await DatabaseService.getRecord(config.noteKey, "notes");
if (!note) return;
const book = await DatabaseService.getRecord(note.bookKey, "books");
if (!book) return;
let bookLocation: any = {};
try {
bookLocation = JSON.parse(note.cfi) || {};
} catch (error) {
bookLocation.cfi = note.cfi;
bookLocation.chapterTitle = note.chapter;
}
if (bookLocation.fingerprint) {
bookLocation.chapterDocIndex = bookLocation.page - 1 + "";
bookLocation.chapterHref = "title" + (bookLocation.page - 1);
}
ConfigService.setObjectConfig(
note.bookKey,
bookLocation,
"recordLocation"
);
BookUtil.redirectBook(book);
}
);
} else {
upgradeConfig();
const status = await LocalFileManager.getPermissionStatus();
if (
!ConfigService.getReaderConfig("isUseLocal") &&
LocalFileManager.isSupported()
) {
this.props.handleLocalFileDialog(true);
} else if (
ConfigService.getReaderConfig("isUseLocal") === "yes" &&
!status.directoryName
) {
this.props.handleLocalFileDialog(true);
} else if (
ConfigService.getReaderConfig("isUseLocal") === "yes" &&
(status.needsReauthorization || !status.hasAccess)
) {
this.props.handleLocalFileDialog(true);
}
}
this.resizeHandler = throttle(() => {
this.setState({ width: document.body.clientWidth });
});
window.addEventListener("resize", this.resizeHandler);
this.props.handleCloudSyncFunc(this.handleCloudSync);
document.addEventListener("visibilitychange", async (event) => {
if (
document.visibilityState === "visible" &&
!isElectron &&
ConfigService.getReaderConfig("isFinishWebReading") === "yes"
) {
await this.handleFinishReading();
// ConfigService.setReaderConfig("isFinishWebReading", "no");
}
});
let willAutoSync =
ConfigService.getReaderConfig("isDisableAutoSync") !== "yes" &&
ConfigService.getItem("defaultSyncOption");
if (!willAutoSync) {
this.handleOpenLastReadBook();
}
this.startScheduledSync();
}
componentWillUnmount() {
if (this.scheduledSyncTimer) {
clearInterval(this.scheduledSyncTimer);
this.scheduledSyncTimer = null;
}
if (this.resizeHandler) {
window.removeEventListener("resize", this.resizeHandler);
this.resizeHandler = null;
}
}
startScheduledSync = () => {
if (this.scheduledSyncTimer) {
clearInterval(this.scheduledSyncTimer);
this.scheduledSyncTimer = null;
}
const intervalMinutes = parseInt(
ConfigService.getReaderConfig("scheduledSyncInterval") || "0"
);
if (!intervalMinutes || intervalMinutes <= 0) {
return;
}
const intervalMs = intervalMinutes * 60 * 1000;
this.scheduledSyncTimer = setInterval(async () => {
const currentInterval = parseInt(
ConfigService.getReaderConfig("scheduledSyncInterval") || "0"
);
if (!currentInterval || currentInterval <= 0) {
clearInterval(this.scheduledSyncTimer);
this.scheduledSyncTimer = null;
return;
}
const defaultSyncOption = ConfigService.getItem("defaultSyncOption");
if (
!defaultSyncOption ||
ConfigService.getReaderConfig("isDisableAutoSync") === "yes"
) {
return;
}
if (!this.state.isSync && !this.isSyncing) {
const userInfo = await this.props.handleFetchUserInfo();
await this.handleCloudSync(userInfo);
}
}, intervalMs);
};
async UNSAFE_componentWillReceiveProps(
nextProps: Readonly<HeaderProps>,
_nextContext: any
) {
if (nextProps.isAuthed && nextProps.isAuthed !== this.props.isAuthed) {
if (isElectron) {
} else {
addChatBox();
}
if (ConfigService.getReaderConfig("isProUpgraded") !== "yes") {
try {
ConfigService.setReaderConfig("isProUpgraded", "yes");
await generateSyncRecord();
} catch (error) {
console.error(error);
}
}
let userInfo = await this.props.handleFetchUserInfo();
if (
ConfigService.getReaderConfig("isDisableAutoSync") !== "yes" &&
ConfigService.getItem("defaultSyncOption")
) {
this.setState({ isSync: true });
await this.handleCloudSync(userInfo);
await this.handleOpenLastReadBook();
}
}
if (!nextProps.isAuthed && nextProps.isAuthed !== this.props.isAuthed) {
if (isElectron) {
} else {
removeChatBox();
}
}
}
handleOpenLastReadBook = async () => {
let filePath = "";
//open book when app start
if (isElectron) {
const { ipcRenderer } = window.require("electron");
filePath = ipcRenderer.sendSync("check-file-data");
}
if (
ConfigService.getReaderConfig("isOpenBook") === "yes" &&
!this.props.currentBook.key &&
!filePath
) {
let lastReadBookKey = ConfigService.getAllListConfig("recentBooks")[0];
if (lastReadBookKey) {
let fullBook = await DatabaseService.getRecord(
lastReadBookKey,
"books"
);
if (fullBook) {
this.props.handleReadingBook(fullBook);
BookUtil.redirectBook(fullBook);
}
}
}
};
handleFinishReading = async () => {
if (
ConfigService.getReaderConfig("isDisableAutoSync") !== "yes" &&
ConfigService.getItem("defaultSyncOption") &&
!this.state.isSync
) {
ConfigService.setItem("isFinshReading", "yes");
let userInfo = await this.props.handleFetchUserInfo();
this.setState({ isSync: true }, async () => {
await this.handleCloudSync(userInfo);
ConfigService.setItem("isFinshReading", "no");
});
}
};
handleFinishUpgrade = () => {
setTimeout(() => {
if (this.props.mode === "home") {
this.props.history.push("/manager/home");
}
}, 2000);
};
syncFromLocation = async () => {
let result = await restoreFromConfigJson();
if (result) {
this.setState({ isDataChange: false });
//Check for data update
let lastSyncTime = getLastSyncTimeFromConfigJson();
if (ConfigService.getItem("lastSyncTime") && lastSyncTime) {
ConfigService.setItem("lastSyncTime", lastSyncTime + "");
} else {
let timestamp = new Date().getTime().toString();
ConfigService.setItem("lastSyncTime", timestamp);
}
}
if (!result) {
toast.error(this.props.t("Sync failed"));
} else {
toast.success(
this.props.t("Synchronisation successful") +
" (" +
this.props.t("Local") +
")"
);
toast.success(
this.props.t(
"Your data has been imported from your local folder, Upgrade to pro to get more advanced features"
),
{
duration: 4000,
}
);
}
};
handleLocalSync = async () => {
let lastSyncTime = getLastSyncTimeFromConfigJson();
if (!lastSyncTime) {
await this.syncToLocation();
} else {
if (
ConfigService.getItem("lastSyncTime") &&
lastSyncTime <= parseInt(ConfigService.getItem("lastSyncTime")!)
) {
await this.syncToLocation();
} else {
await this.syncFromLocation();
}
}
this.setState({ isSync: false });
};
handleKOReaderSync = async () => {
if (ConfigService.getReaderConfig("isEnableKoReaderSync") !== "yes") {
return;
}
toast.loading(this.props.t("Start syncing") + " (KOReader)", {
id: "koreader-sync",
position: "bottom-center",
});
try {
const koReaderUtil = new KOReaderUtil(
ConfigService,
TokenService,
DatabaseService
);
const summary =
await koReaderUtil.syncKOReaderProgress(getBookPartialMd5);
if (summary.pulledBooks > 0 || summary.pushedBooks > 0) {
this.props.handleFetchBooks();
}
toast.success(
this.props.t("Synchronisation successful") + " (KOReader)",
{
id: "koreader-sync",
}
);
} catch (error) {
console.error(error);
toast.error(
this.props.t("Sync failed") +
" (KOReader): " +
(error instanceof Error ? error.message : String(error)),
{
id: "koreader-sync",
duration: 6000,
}
);
}
};
beforeSync = async (userInfo: any) => {
if (!ConfigService.getItem("defaultSyncOption")) {
toast.error(this.props.t("Please add data source in the setting"));
return false;
}
if (
ConfigService.getReaderConfig("isEnableKoodoSync") === "yes" &&
userInfo &&
userInfo.default_sync_option &&
userInfo.default_sync_option !== this.props.defaultSyncOption
) {
toast.error(
this.props.t(
"The default sync options in the local and cloud are inconsistent, please set the local default sync option to "
) +
this.props.t(
driveList.find(
(item) => item.value === userInfo.default_sync_option
)?.label || ""
),
{
duration: 4000,
}
);
return false;
}
let config = await getCloudConfig(
ConfigService.getItem("defaultSyncOption") || ""
);
if (Object.keys(config).length === 0) {
toast.error(this.props.t("Cannot get sync config"));
return false;
}
if (
ConfigService.getItem("defaultSyncOption") === "google" &&
!config.version
) {
let targetDrive = "google";
await TokenService.setToken(targetDrive + "_token", "");
SyncService.removeSyncUtil(targetDrive);
removeCloudConfig(targetDrive);
if (isElectron) {
const { ipcRenderer } = window.require("electron");
await ipcRenderer.invoke("cloud-close", {
service: targetDrive,
});
}
ConfigService.deleteListConfig(targetDrive, "dataSourceList");
this.props.handleFetchDataSourceList();
if (targetDrive === ConfigService.getItem("defaultSyncOption")) {
ConfigService.removeItem("defaultSyncOption");
this.props.handleFetchDefaultSyncOption();
}
if (ConfigService.getReaderConfig("isEnableKoodoSync") === "yes") {
resetKoodoSync();
}
toast(
this.props.t(
"In order to let you directly manage your data in Google Drive, we have deprecated the old Google Drive token. Please reauthorize Google Drive in the settings. Your new data will be stored in the root directory of your Google Drive, and you can manage it directly in the Google Drive web interface."
),
{ duration: 4000 }
);
return false;
}
await checkMissingBook();
let checkResult = await checkBrokenDatabase();
if (checkResult) {
toast.error(
this.props.t(
"Broken data detected, please click the setting button to reset the sync records"
)
);
return false;
}
if (ConfigService.getReaderConfig("isEnableKoodoSync") !== "yes") {
toast.loading(
this.props.t("Start syncing") +
" (" +
this.props.t(
driveList.find(
(item) =>
item.value === ConfigService.getItem("defaultSyncOption")
)?.label || ""
) +
")",
{ id: "syncing", position: "bottom-center" }
);
}
return true;
};
getCompareResult = async () => {
let localSyncRecords = ConfigService.getAllSyncRecord();
let cloudSyncRecords = await ConfigUtil.getCloudConfig("sync");
return await SyncHelper.compareAll(
localSyncRecords,
cloudSyncRecords,
ConfigService,
TokenService,
ConfigUtil
);
};
handleSyncStateChange = (isSyncing: boolean) => {
this.setState({ isSync: isSyncing });
};
handleCloudSync = async (userInfo: any): Promise<false | undefined> => {
if (this.isSyncing) {
console.info("Sync already in progress, skipping...");
return false;
}
this.isSyncing = true;
try {
this.timer = await showTaskProgress(this.handleSyncStateChange);
if (!this.timer) {
this.setState({ isSync: false });
this.handleKOReaderSync();
return false;
}
let res = await this.beforeSync(userInfo);
if (!res) {
clearInterval(this.timer);
this.setState({ isSync: false });
this.handleKOReaderSync();
return false;
}
let compareResult = await this.getCompareResult();
await this.handleSync(compareResult);
clearInterval(this.timer);
this.setState({ isSync: false });
this.handleKOReaderSync();
} catch (error) {
console.error(error);
toast.error(
this.props.t("Sync failed") +
": " +
(error instanceof Error ? error.message : String(error))
);
clearInterval(this.timer);
this.setState({ isSync: false });
this.handleKOReaderSync();
return false;
} finally {
this.isSyncing = false;
}
setTimeout(() => {
toast.dismiss("syncing");
}, 3000);
return;
};
handleSuccess = async () => {
if (ConfigService.getItem("isFinshReading") !== "yes" || !isElectron) {
this.props.handleFetchBooks();
}
this.props.handleFetchBookmarks();
this.props.handleFetchNotes();
toast.success(this.props.t("Synchronisation successful"), {
id: "syncing",
});
if (
ConfigService.getItem("defaultSyncOption") === "adrive" &&
ConfigService.getReaderConfig("hasShowAliyunWarning") !== "yes"
) {
ConfigService.setReaderConfig("hasShowAliyunWarning", "yes");
toast.success(
this.props.t(
"We have bypassed the synchronization of book cover for Aliyun Drive, covers will be downloaded automatically when you open the book next time."
),
{
duration: 4000,
}
);
}
if (ConfigService.getReaderConfig("isEnableKoodoSync") === "yes") {
ConfigUtil.updateSyncData();
}
//when book is empty, need to refresh the book list
setTimeout(async () => {
if (this.props.mode === "home") {
this.props.history.push("/manager/home");
if (
ConfigService.getReaderConfig("isFirstSync") !== "no" &&
ConfigService.getReaderConfig("isEnableKoodoSync") !== "yes"
) {
ConfigService.setReaderConfig("isFirstSync", "no");
let config = await getCloudConfig(
ConfigService.getItem("defaultSyncOption") || ""
);
if (
config.url &&
(config.url.includes("192.168.") ||
config.url.includes("127.0.0.1") ||
config.url.includes("localhost"))
) {
return;
}
let result = await vexComfirmAsync(
`<h3>${this.props.t("Enable Koodo Sync")}</h3><p>${
this.props.t(
"To enjoy a faster and seamless synchronization experience."
) +
" " +
this.props.t(
"Your reading progress, notes, highlights, bookmarks, and other data will be stored and synced through our cloud service. Your books and covers will still be synced by your added data sources. All your data will be encrypted and stored securely in our cloud. You can disable this feature anytime in the settings."
)
}</p>`
);
if (result) {
ConfigService.setReaderConfig("isEnableKoodoSync", "yes");
let encryptedToken = await TokenService.getToken(
this.props.defaultSyncOption + "_token"
);
await updateUserConfig({
is_enable_koodo_sync: "yes",
default_sync_option: this.props.defaultSyncOption,
default_sync_token: encryptedToken || "",
});
let userInfo = await this.props.handleFetchUserInfo();
toast.success(this.props.t("Setup successful"));
this.handleCloudSync(userInfo);
}
}
}
}, 1000);
};
handleSync = async (compareResult) => {
try {
let tasks = await SyncHelper.startSync(
compareResult,
ConfigService,
DatabaseService,
ConfigUtil,
BookUtil,
CoverUtil
);
await SyncHelper.runTasksWithLimit(
tasks,
99,
ConfigService.getItem("defaultSyncOption")
);
clearInterval(this.timer);
this.setState({ isSync: false });
let stats = await getTaskStats();
if (stats.hasFailedTasks) {
toast.error(
this.props.t(
"Tasks failed after multiple retries, please check the network connection or reauthorize the data source in the settings"
),
{
id: "syncing",
duration: 6000,
}
);
return;
}
toast.loading(this.props.t("Almost finished"), {
id: "syncing",
position: "bottom-center",
});
await this.handleSuccess();
} catch (error) {
console.error(error);
clearInterval(this.timer);
this.setState({ isSync: false });
toast.error(
this.props.t("Sync failed") +
": " +
(error instanceof Error ? error.message : String(error))
);
return;
}
};
syncToLocation = async () => {
let timestamp = new Date().getTime().toString();
ConfigService.setItem("lastSyncTime", timestamp);
await backupToConfigJson();
toast.success(
this.props.t("Synchronisation successful") +
" (" +
this.props.t("Local") +
")"
);
toast.success(
this.props.t(
"Your data has been exported to your local folder, learn how to sync your data to your other devices by visiting our documentation, Upgrade to pro to get more advanced features"
),
{
duration: 4000,
}
);
};
render() {
return (
<div
className="header"
style={this.props.isCollapsed ? { marginLeft: "40px" } : {}}
>
{isElectron && this.props.isAuthed && (
<div
className="header-chat-widget"
onClick={() => {
window.require("electron").ipcRenderer.invoke("new-chat", {
url:
getWebsiteUrl() +
(ConfigService.getReaderConfig("lang").startsWith("zh")
? "/zh/faq"
: "/en/faq") +
"?referer=app&version=" +
packageJson.version +
"&client=desktop",
locale: getChatLocale(),
});
}}
>
<img
src={require("../../assets/images/chat-widget.png")}
alt="logo"
className="login-mobile-qr"
style={{
width: "100%",
height: "100%",
}}
/>
</div>
)}
<div
className="header-search-container"
style={this.props.isCollapsed ? { width: "369px" } : {}}
>
<SearchBox />
</div>
<div
className="setting-icon-parrent"
style={this.props.isCollapsed ? { marginLeft: "430px" } : {}}
>
<div
className="setting-icon-container"
onClick={() => {
this.props.handleSortDisplay(!this.props.isSortDisplay);
}}
onMouseLeave={() => {
this.props.handleSortDisplay(false);
}}
style={{ top: "18px" }}
>
<span
data-tooltip-id="my-tooltip"
data-tooltip-content={this.props.t("Sort by")}
data-tooltip-place="left"
>
<span className="icon-sort-desc header-sort-icon"></span>
</span>
</div>
<div
className="setting-icon-container"
onClick={() => {
this.props.handleSetting(true);
this.props.handleAbout(false);
}}
onMouseLeave={() => {
this.props.handleAbout(false);
}}
style={{ marginTop: "2px" }}
>
<span
data-tooltip-id="my-tooltip"
data-tooltip-content={this.props.t("Setting")}
data-tooltip-place="left"
>
<span className="icon-setting setting-icon"></span>
</span>
</div>
<div
className="setting-icon-container"
onClick={() => {
this.props.handleBackupDialog(true);
}}
onMouseLeave={() => {
this.props.handleSortDisplay(false);
}}
style={{ marginTop: "1px" }}
>
<span
data-tooltip-id="my-tooltip"
data-tooltip-content={this.props.t("Backup")}
data-tooltip-place="left"
>
<span className="icon-archive header-archive-icon"></span>
</span>
</div>
<div
className="setting-icon-container"
onClick={async () => {
if (!isElectron && !this.props.isAuthed) {
toast(
this.props.t("Please upgrade to Pro to use this feature")
);
this.props.handleSetting(true);
this.props.handleSettingMode("account");
return;
}
this.setState({ isSync: true });
if (this.props.isAuthed) {
let userInfo = await this.props.handleFetchUserInfo();
await this.handleCloudSync(userInfo);
} else {
await this.handleLocalSync();
this.handleKOReaderSync();
}
}}
style={{ marginTop: "2px" }}
>
<span
data-tooltip-id="my-tooltip"
data-tooltip-content={this.props.t("Sync")}
data-tooltip-place="left"
>
<span
className={
"icon-sync setting-icon" +
(this.state.isSync ? " icon-rotate" : "")
}
style={
this.state.isDataChange ? { color: "rgb(35, 170, 242)" } : {}
}
></span>
</span>
</div>
</div>
{!this.props.isAuthed && !this.state.isHidePro ? (
<div className="header-report-container">
<span
style={{ textDecoration: "underline" }}
onClick={() => {
if (
window.location.hostname !== "web.koodoreader.com" &&
!isElectron
) {
this.props.handleSetting(true);
this.props.handleSettingMode("account");
return;
}
this.props.history.push("/login");
}}
>
<Trans>Pro version</Trans>
<span> </span>
</span>
<span
className="icon-close icon-pro-close"
onClick={() => {
ConfigService.setReaderConfig("isHidePro", "yes");
this.setState({ isHidePro: true });
}}
></span>
</div>
) : null}
{this.props.isAuthed &&
this.props.userInfo &&
((this.props.userInfo.type === "pro" &&
this.props.userInfo.valid_until <
new Date().getTime() / 1000 + 30 * 24 * 3600) ||
(this.props.userInfo.type === "trial" &&
this.props.userInfo.valid_until <
new Date().getTime() / 1000 + 3 * 24 * 3600)) ? (
<div className="header-report-container">
<span
data-tooltip-id="my-tooltip"
data-tooltip-content={i18n.t("Your trial will expire in", {
ttl: Math.ceil(
(this.props.userInfo.valid_until -
new Date().getTime() / 1000) /
(24 * 3600)
),
})}
>
<span
style={{ textDecoration: "underline" }}
onClick={async () => {
let response = await getTempToken();
if (response.code === 200) {
let tempToken = response.data.access_token;
let deviceUuid = await TokenService.getFingerprint();
openInBrowser(
getWebsiteUrl() +
(ConfigService.getReaderConfig("lang").startsWith("zh")
? "/zh"
: "/en") +
"/pricing?temp_token=" +
tempToken +
"&device_uuid=" +
deviceUuid
);
} else if (response.code === 401) {
this.props.handleFetchAuthed();
}
}}
>
<Trans>Renew Pro</Trans>
</span>
</span>
</div>
) : null}
{this.props.isAuthed &&
this.props.userInfo &&
this.props.userInfo.type === "trial" &&
this.props.userInfo.valid_until >
new Date().getTime() / 1000 + 3 * 24 * 3600 ? (
<div className="header-report-container" style={{ right: "200px" }}>
<span
data-tooltip-id="my-tooltip"
data-tooltip-content={i18n.t("Your trial will expire in", {
ttl: Math.ceil(
(this.props.userInfo.valid_until -
new Date().getTime() / 1000) /
(24 * 3600)
),
})}
>
<span
style={{ textDecoration: "underline" }}
onClick={async () => {
let response = await getTempToken();
if (response.code === 200) {
let tempToken = response.data.access_token;
let deviceUuid = await TokenService.getFingerprint();
openInBrowser(
getWebsiteUrl() +
(ConfigService.getReaderConfig("lang").startsWith("zh")
? "/zh"
: "/en") +
"/pricing?temp_token=" +
tempToken +
"&device_uuid=" +
deviceUuid
);
} else if (response.code === 401) {
this.props.handleFetchAuthed();
}
}}
>
<Trans>In trial</Trans>
</span>
</span>
</div>
) : null}
{KookitConfig.CloudMode !== "production" ? (
<div className="header-report-container" style={{ right: "300px" }}>
<span
style={{
color: "red",
opacity: 1,
fontWeight: "bold",
}}
>
<Trans>TEST</Trans>
<span> </span>
</span>
</div>
) : null}
<ImportLocal
{...({
handleDrag: this.props.handleDrag,
} as any)}
/>
<SupportDialog />
<UpdateInfo />
</div>
);
}
}
export default Header;