feat: update version to 1.9.8, enhance update dialog with version stability badge, and improve popup reference handling

This commit is contained in:
troyeguo
2025-05-30 09:52:54 +08:00
parent e04ab1a92c
commit e28d0a8a0e
8 changed files with 117 additions and 40 deletions

View File

@@ -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",

View File

@@ -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 && (
<>

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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"), {

View File

@@ -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"), {

View File

@@ -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) {