[🚧 Fix] <PLS & FS> Improve PLS download logic & UX

1. [+] 增加了 PLS 下载操作的取消功能
2. [/] 修复了 FS IPC 中 `downloadFile` 时, 过早地从 downloadTasks 中删除任务的逻辑错误。
3. [↑] 改进了 PLS IPC 中 `handlePLSDownload` 获取版本信息时的逻辑, 现在该函数会从全局 API 信息中逐个尝试 API 域名。减少了极端网络环境下, 版本信息获取失败的可能性。
4. [/] 修复了下载失败后, 下载按钮依然保持灰显的问题。
5. [+] 为 PLS 下载增加了进度条显示。
6. [/] 优化了 `plsConnectionManager` 中一些不必要的 IPC 状态同步 (有些时候还会导致逻辑错误)。
This commit is contained in:
Minoricew
2025-06-13 11:49:22 +08:00
parent a9d3772b51
commit ca5d94ebd8
10 changed files with 444 additions and 84 deletions

View File

@@ -6,7 +6,7 @@
detached: false,
connected: false,
launched: false,
status: "unknown",
status: "dead",
version: "未知",
authToken: "",
};
@@ -259,7 +259,10 @@
`${IPC_METHOD_BASE}.getPlsStats`
);
let updatedPlsStats = {};
if (curPlsStats === null || !curPlsStats.success) {
if (
(curPlsStats === null || !curPlsStats.success) &&
curPlsStats.status !== "downloading"
) {
updatedPlsStats = {
installed: false,
launched: false,
@@ -269,15 +272,14 @@
status: "dead",
authToken: "66ccff0d000721114514191981023333",
};
const isPlsFolderExists = (
await global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.getPlsBinExists`)
).data.isExists;
updatedPlsStats.installed = isPlsFolderExists;
} else {
updatedPlsStats = curPlsStats.data;
}
const isPlsFolderExists = (
await global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.getPlsBinExists`)
).data.isExists;
updatedPlsStats.installed = isPlsFolderExists;
// @ts-expect-error
global.__HUGO_AURA__.plsStats = updatedPlsStats;
console.debug(

View File

@@ -33,6 +33,10 @@
margin-bottom: 1rem;
}
.acs-bc-psp-operations-container.acs-bc-psp-oper-ctnr-hidden {
display: none;
}
.acs-bc-psp-operation-btn {
--svg-color: rgb(17, 140, 255);
@@ -85,6 +89,50 @@
opacity: 0.3;
}
.acs-bc-psp-download-progress-area {
display: flex;
flex-direction: column;
width: 80%;
justify-content: center;
align-items: center;
}
.acs-bc-psp-download-progress-area.acs-bc-psp-dl-pbar-hidden {
display: none;
}
.acs-bc-psp-download-progress-area .progress {
height: 2px;
width: 80%;
margin-bottom: 0.6rem;
}
.acs-bc-psp-download-progress-area .progress-bar {
transition: all 0.15s;
}
.acs-bc-psp-download-progress-area #acsBcPspDownloadPbarDesc {
opacity: 0.6;
margin-bottom: 0.25rem;
}
.acs-bc-psp-download-progress-info-area {
display: flex;
}
.acs-bc-psp-download-progress-info-area .acs-bc-psp-operation-btn {
margin-left: 1rem;
margin-bottom: 0.25rem;
}
.acs-bc-psp-download-progress-info-area .acs-bc-psp-operation-btn.hidden {
display: none;
}
.acs-bc-psp-download-progress-info-area .acs-bc-psp-operation-btn svg {
margin-top: 1px;
}
.acs-bc-pls-status-page-status-el {
display: flex;
align-items: center;

View File

@@ -124,6 +124,37 @@
</div>
</div>
<div class="acs-bc-psp-download-progress-area acs-bc-psp-dl-pbar-hidden">
<div class="progress" role="progressbar">
<div class="progress-bar" id="acsBcPspDownloadPbarEl"></div>
</div>
<div class="acs-bc-psp-download-progress-info-area">
<p id="acsBcPspDownloadPbarDesc">等待中...</p>
<div
class="acs-bc-psp-operation-btn hidden"
id="acsBcPspDownloadPbarCancelBtn"
onclick="global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.cancelDownloadTask()"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 32 32"
>
<path
fill="var(--svg-color)"
d="M9 10.555L10.555 9L23 21.444L21.444 23z"
/>
<path
fill="var(--svg-color)"
d="M16 2A13.914 13.914 0 0 0 2 16a13.914 13.914 0 0 0 14 14a13.914 13.914 0 0 0 14-14A13.914 13.914 0 0 0 16 2m0 26a12 12 0 1 1 12-12a12.035 12.035 0 0 1-12 12"
/>
</svg>
<p>取消操作</p>
</div>
</div>
</div>
<div class="acs-bc-pls-status-page-status-el">
<p>安装状态</p>
<div
@@ -165,7 +196,11 @@
</div>
<div class="toast-container position-fixed bottom-0 end-0 p-3">
<div id="plsStatusNotifyToast" class="acs-bc-psp-toast toast" data-bs-autohide="false">
<div
id="plsStatusNotifyToast"
class="acs-bc-psp-toast toast"
data-bs-autohide="false"
>
<div class="toast-header">
<strong class="me-auto" id="plsStatusNotifyToastTitle"></strong>
<button

View File

@@ -17,6 +17,7 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
global.__HUGO_AURA_UI_REACTIVES__.subConfig.plsStatus = {
toastAutoHideTimeout: null,
curDlTaskId: null,
};
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus = {
@@ -361,6 +362,10 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
updateStatusContent: async () => {
const curPlsStats = await updatePlsStatusFromLocal();
if (curPlsStats.status === "downloading") {
GLOBAL_FUNCTIONS.downloadPLSBin(true);
}
const acIdInst = "acs-bc-psp-installStatus-container";
const atIdInst = "acs-bc-psp-installStatus-text";
switch (lifecycleStatus.installed) {
@@ -549,22 +554,101 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
}
},
downloadPLSBin: async () => {
/**
*
* @param {boolean} isShow
*/
switchPBarShowStatus: (isShow) => {
const operAreaEl = document.getElementsByClassName(
"acs-bc-psp-operations-container"
)[0];
const pBarAreaEl = document.getElementsByClassName(
"acs-bc-psp-download-progress-area"
)[0];
const pBarDescEl = document.getElementById("acsBcPspDownloadPbarDesc");
const pBarSelfEl = document.getElementById("acsBcPspDownloadPbarEl");
if (isShow) {
pBarAreaEl.classList.remove("acs-bc-psp-dl-pbar-hidden");
operAreaEl.classList.add("acs-bc-psp-oper-ctnr-hidden");
pBarDescEl.textContent = "等待中...";
pBarSelfEl.style["width"] = "0";
} else {
pBarAreaEl.classList.add("acs-bc-psp-dl-pbar-hidden");
operAreaEl.classList.remove("acs-bc-psp-oper-ctnr-hidden");
}
return true;
},
updatePBarStatus: async (
progress = null,
desc = null,
type = null,
isCancelShown = null
) => {
const pBarDescEl = document.getElementById("acsBcPspDownloadPbarDesc");
const pBarSelfEl = document.getElementById("acsBcPspDownloadPbarEl");
const pBarBtnEl = document.getElementById(
"acsBcPspDownloadPbarCancelBtn"
);
if (progress) {
pBarSelfEl.style["width"] = `${progress}%`;
}
if (type) {
pBarSelfEl.classList.remove("bg-success");
pBarSelfEl.classList.remove("bg-warning");
pBarSelfEl.classList.remove("bg-danger");
if (type !== "normal") {
pBarSelfEl.classList.add(`bg-${type}`);
}
}
if (desc) {
pBarDescEl.innerHTML = desc;
}
if (isCancelShown !== null) {
if (isCancelShown) {
pBarBtnEl.classList.remove("hidden");
} else {
pBarBtnEl.classList.add("hidden");
}
}
},
downloadPLSBin: async (retrieveMode = false) => {
const GLOBAL_FUNCTIONS =
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus;
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Download", true, "正在检查");
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", true);
const CUR_CHANNEL = `${IPC_METHOD_BASE}.post.reportPlsDownloadStatus`;
await ipcRenderer.invoke(`${IPC_METHOD_BASE}.ensurePlsInstallDir`);
GLOBAL_FUNCTIONS.updateToast(
"info",
"准备开始下载...",
null,
true,
true,
2000
);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Download", true, "等待下载");
if (!retrieveMode) {
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Download", true, "正在检查");
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", true);
await ipcRenderer.invoke(`${IPC_METHOD_BASE}.ensurePlsInstallDir`);
GLOBAL_FUNCTIONS.updateToast(
"info",
"准备开始下载...",
null,
true,
true,
2000
);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Download", true);
} else {
GLOBAL_FUNCTIONS.updateToast(
"info",
"正在恢复下载状态",
null,
true,
true,
2000
);
}
GLOBAL_FUNCTIONS.switchPBarShowStatus(true);
GLOBAL_FUNCTIONS.updatePBarStatus(0, "等待中...", "normal", false);
const callbackFn = (_evt, info) => {
switch (info.status) {
@@ -581,8 +665,24 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
true,
5000
);
GLOBAL_FUNCTIONS.updatePBarStatus(
100,
"下载时发生错误",
"danger",
false
);
setTimeout(() => {
GLOBAL_FUNCTIONS.switchPBarShowStatus(false);
}, 1000);
ipcRenderer.off(CUR_CHANNEL, callbackFn);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", false);
GLOBAL_FUNCTIONS.updateOperationBtnStatus(
"Download",
false,
"下载内核"
);
break;
case "done":
GLOBAL_FUNCTIONS.updateToast(
@@ -593,6 +693,17 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
true,
2500
);
GLOBAL_FUNCTIONS.updatePBarStatus(
100,
"下载成功",
"success",
false
);
setTimeout(() => {
GLOBAL_FUNCTIONS.switchPBarShowStatus(false);
}, 1000);
ipcRenderer.off(CUR_CHANNEL, callbackFn);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", false);
GLOBAL_FUNCTIONS.updateOperationBtnStatus(
@@ -609,17 +720,41 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
GLOBAL_FUNCTIONS.updateStatusContent();
break;
case "waiting":
GLOBAL_FUNCTIONS.updateOperationBtnStatus(
"Download",
true,
"等待中..."
);
GLOBAL_FUNCTIONS.updatePBarStatus(0, "正在连接", "normal");
if (
!global.__HUGO_AURA_UI_REACTIVES__.subConfig.plsStatus
.curDlTaskId ||
global.__HUGO_AURA_UI_REACTIVES__.subConfig.plsStatus
.curDlTaskId !== info.id
) {
global.__HUGO_AURA_UI_REACTIVES__.subConfig.plsStatus.curDlTaskId =
info.id;
}
break;
case "progressing":
const roundProgress = Math.round(info.progress);
GLOBAL_FUNCTIONS.updatePBarStatus(
roundProgress,
`正在下载中... ${roundProgress}% (${(
info.curBytes /
1024 /
1024
).toFixed(2)}MB / ${(info.totalBytes / 1024 / 1024).toFixed(
2
)}MB)`, // POWERED BY PRETTIER
"normal",
true
);
break;
case "struggling":
GLOBAL_FUNCTIONS.updatePBarStatus(100, info.message, "warning");
break;
case "cancelled":
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", false);
GLOBAL_FUNCTIONS.updateOperationBtnStatus(
"Download",
true,
`下载 ${Math.round(info.progress)}%`
false,
"下载内核"
);
break;
}
@@ -627,11 +762,56 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
ipcRenderer.on(CUR_CHANNEL, callbackFn);
ipcRenderer.invoke(`$aura.pls.downloadPls`, {
ipcRenderer.invoke(`${IPC_METHOD_BASE}.downloadPls`, {
channel: "stable",
reportTo: "assistant",
});
},
cancelDownloadTask: async () => {
const taskId =
global.__HUGO_AURA_UI_REACTIVES__.subConfig.plsStatus.curDlTaskId;
if (!taskId) {
GLOBAL_FUNCTIONS.updateToast(
"error",
"操作取消失败",
"<p>未能获取当前的下载任务 ID</p>",
true,
true,
3000
);
return false;
}
const result = await ipcRenderer.invoke(
"$aura.fs.dl.cancelDownloadTask",
{ targetTaskId: taskId }
);
if (result.success) {
GLOBAL_FUNCTIONS.updateToast(
"success",
"操作取消成功",
null,
true,
true,
2000
);
GLOBAL_FUNCTIONS.switchPBarShowStatus(false);
return true;
} else {
GLOBAL_FUNCTIONS.updateToast(
"error",
"操作取消失败",
`<p>错误代码: ${result.error}</p>`,
true,
true,
3000
);
return false;
}
},
};
const GLOBAL_FUNCTIONS =