mirror of
https://github.com/koodo-reader/koodo-reader.git
synced 2026-06-18 21:00:35 -04:00
feat: update version to 1.9.8, enhance update dialog with version stability badge, and improve popup reference handling
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "koodo-reader",
|
||||
"main": "main.js",
|
||||
"version": "1.9.9",
|
||||
"version": "1.9.8",
|
||||
"description": "Koodo Reader is a cross-platform ebook reader",
|
||||
"author": {
|
||||
"name": "App by Troye",
|
||||
|
||||
@@ -45,7 +45,6 @@ class UpdateInfo extends React.Component<UpdateInfoProps, UpdateInfoState> {
|
||||
res = await checkDeveloperUpdate();
|
||||
}
|
||||
const newVersion = res.version;
|
||||
|
||||
await sleep(500);
|
||||
if (
|
||||
res.stable === "no" &&
|
||||
@@ -89,6 +88,21 @@ class UpdateInfo extends React.Component<UpdateInfoProps, UpdateInfoState> {
|
||||
<div className="new-version-title">
|
||||
<Trans>Update to</Trans>
|
||||
{" " + this.state.updateLog.version}
|
||||
<div
|
||||
className="new-version-badge"
|
||||
style={{
|
||||
backgroundColor:
|
||||
this.state.updateLog.stable === "yes"
|
||||
? "#4CAF50"
|
||||
: "#0295D7",
|
||||
}}
|
||||
>
|
||||
{this.props.t(
|
||||
this.state.updateLog.stable === "yes"
|
||||
? "Stable version"
|
||||
: "Developer version"
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{(this.props.isAuthed &&
|
||||
this.state.updateLog.skippable === "yes") ||
|
||||
@@ -143,28 +157,32 @@ class UpdateInfo extends React.Component<UpdateInfoProps, UpdateInfoState> {
|
||||
<Trans>Download</Trans>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="new-version-skip"
|
||||
onClick={() => {
|
||||
ConfigService.setReaderConfig(
|
||||
"skipVersion",
|
||||
this.state.updateLog.version
|
||||
);
|
||||
this.handleClose();
|
||||
}}
|
||||
style={{ marginTop: 5 }}
|
||||
>
|
||||
<Trans>Skip this version</Trans>
|
||||
</div>
|
||||
<div
|
||||
className="new-version-skip"
|
||||
onClick={() => {
|
||||
ConfigService.setReaderConfig("updateChannel", "stable");
|
||||
this.handleClose();
|
||||
}}
|
||||
>
|
||||
<Trans>Only receive stable version</Trans>
|
||||
</div>
|
||||
{this.state.updateLog.stable !== "yes" && (
|
||||
<div
|
||||
className="new-version-skip"
|
||||
onClick={() => {
|
||||
ConfigService.setReaderConfig(
|
||||
"skipVersion",
|
||||
this.state.updateLog.version
|
||||
);
|
||||
this.handleClose();
|
||||
}}
|
||||
style={{ marginTop: 5 }}
|
||||
>
|
||||
<Trans>Skip this version</Trans>
|
||||
</div>
|
||||
)}
|
||||
{this.state.updateLog.stable !== "yes" && (
|
||||
<div
|
||||
className="new-version-skip"
|
||||
onClick={() => {
|
||||
ConfigService.setReaderConfig("updateChannel", "stable");
|
||||
this.handleClose();
|
||||
}}
|
||||
>
|
||||
<Trans>Only receive stable version</Trans>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{this.state.updateLog && (
|
||||
<>
|
||||
|
||||
@@ -129,3 +129,16 @@
|
||||
transition: 0.1s;
|
||||
font-size: 13px;
|
||||
}
|
||||
.new-version-badge {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
margin-left: 10px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
border-radius: 5px;
|
||||
padding: 0px 5px;
|
||||
position: relative;
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { PopupReferProps, PopupReferStates } from "./interface";
|
||||
import { getIframeDoc } from "../../../utils/reader/docUtil";
|
||||
import { getTargetHref, openExternalUrl } from "../../../utils/common";
|
||||
import Parser from "html-react-parser";
|
||||
import DOMPurify from "dompurify";
|
||||
|
||||
class PopupRefer extends React.Component<PopupReferProps, PopupReferStates> {
|
||||
highlighter: any;
|
||||
timer!: NodeJS.Timeout;
|
||||
@@ -63,10 +63,44 @@ class PopupRefer extends React.Component<PopupReferProps, PopupReferStates> {
|
||||
let node = doc.body.querySelector("#" + id);
|
||||
if (!node) return false;
|
||||
console.log("node", event.target.getBoundingClientRect());
|
||||
console.log(node.innerHTML, "innerHTML");
|
||||
//将html代码中的img标签由blob转换为base64
|
||||
let htmlContent = node.innerHTML;
|
||||
|
||||
const convertBlobToDataURL = async (blobUrl) => {
|
||||
const response = await fetch(blobUrl);
|
||||
const blob = await response.blob();
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => resolve(reader.result);
|
||||
reader.onerror = reject;
|
||||
reader.readAsDataURL(blob);
|
||||
});
|
||||
};
|
||||
|
||||
const processHtml = async (html) => {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(html, "text/html");
|
||||
const images: any[] = Array.from(doc.getElementsByTagName("img"));
|
||||
for (const img of images) {
|
||||
if (img.src && img.src.startsWith("blob:")) {
|
||||
try {
|
||||
const dataUrl = await convertBlobToDataURL(img.src);
|
||||
img.src = dataUrl;
|
||||
img.style.maxWidth = "100%"; // 确保图片不会超出容器宽度
|
||||
} catch (error) {
|
||||
console.error("Error converting blob to data URL:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
return doc.body.innerHTML;
|
||||
};
|
||||
|
||||
htmlContent = await processHtml(htmlContent);
|
||||
this.setState(
|
||||
{
|
||||
rect: event.target.getBoundingClientRect(),
|
||||
footnote: node.innerHTML,
|
||||
footnote: htmlContent,
|
||||
isOpenMenu: true,
|
||||
},
|
||||
() => {
|
||||
@@ -185,8 +219,15 @@ class PopupRefer extends React.Component<PopupReferProps, PopupReferStates> {
|
||||
className="popup-menu-container popup-ref-container"
|
||||
style={this.state.isOpenMenu ? {} : { display: "none" }}
|
||||
>
|
||||
<div className="popup-menu-box popup-ref-box">
|
||||
{Parser(DOMPurify.sanitize(this.state.footnote))}
|
||||
<div
|
||||
className="popup-menu-box popup-ref-box"
|
||||
onClick={(event) => {
|
||||
this.handleLinkJump(event, this.props.rendition);
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}}
|
||||
>
|
||||
{Parser(this.state.footnote)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,4 +3,19 @@
|
||||
max-height: 250px;
|
||||
padding: 20px;
|
||||
overflow: scroll;
|
||||
overflow-x: hidden;
|
||||
line-height: 1.25;
|
||||
}
|
||||
.popup-ref-box {
|
||||
user-select: text;
|
||||
-webkit-user-select: text;
|
||||
-moz-user-select: text;
|
||||
-ms-user-select: text;
|
||||
}
|
||||
|
||||
.popup-ref-box * {
|
||||
user-select: text;
|
||||
-webkit-user-select: text;
|
||||
-moz-user-select: text;
|
||||
-ms-user-select: text;
|
||||
}
|
||||
|
||||
@@ -372,6 +372,7 @@ class AccountSetting extends React.Component<
|
||||
let userRequest = await getUserRequest();
|
||||
let response = await userRequest.sendEmailCode({
|
||||
email: this.state.loginConfig.email,
|
||||
lang: ConfigService.getReaderConfig("lang"),
|
||||
});
|
||||
if (response.code === 200) {
|
||||
toast.success(this.props.t("Send successfully"), {
|
||||
|
||||
@@ -614,6 +614,7 @@ class Login extends React.Component<LoginProps, LoginState> {
|
||||
let userRequest = await getUserRequest();
|
||||
let response = await userRequest.sendEmailCode({
|
||||
email: this.state.loginConfig.email,
|
||||
lang: ConfigService.getReaderConfig("lang"),
|
||||
});
|
||||
if (response.code === 200) {
|
||||
toast.success(this.props.t("Send successfully"), {
|
||||
|
||||
@@ -356,9 +356,6 @@ class BookUtil {
|
||||
const { ipcRenderer } = window.require("electron");
|
||||
|
||||
let tokenConfig = await getCloudConfig(service);
|
||||
toast.loading(i18n.t("Uploading book"), {
|
||||
id: "upload-book",
|
||||
});
|
||||
let result = await ipcRenderer.invoke("cloud-upload", {
|
||||
...tokenConfig,
|
||||
fileName: key + "." + format.toLowerCase(),
|
||||
@@ -372,18 +369,12 @@ class BookUtil {
|
||||
});
|
||||
return;
|
||||
}
|
||||
toast.success(i18n.t("Upload successful"), {
|
||||
id: "upload-book",
|
||||
});
|
||||
} else {
|
||||
let syncUtil = await SyncService.getSyncUtil();
|
||||
let bookBuffer: any = await this.fetchBook(key, format, true, "");
|
||||
let bookBlob = new Blob([bookBuffer], {
|
||||
type: CommonTool.getMimeType(format.toLowerCase()),
|
||||
});
|
||||
toast.loading(i18n.t("Uploading book"), {
|
||||
id: "upload-book",
|
||||
});
|
||||
let result = await syncUtil.uploadFile(
|
||||
key + "." + format.toLowerCase(),
|
||||
"book",
|
||||
@@ -395,9 +386,6 @@ class BookUtil {
|
||||
});
|
||||
return;
|
||||
}
|
||||
toast.success(i18n.t("Upload successful"), {
|
||||
id: "upload-book",
|
||||
});
|
||||
}
|
||||
}
|
||||
static async deleteCloudBook(key: string, format: string) {
|
||||
|
||||
Reference in New Issue
Block a user