dsh-life-pack 0.3.21 → 0.3.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.js +106 -38
- package/dist/client.js.map +1 -1
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -560,11 +560,36 @@ window.__ModuleLoader__.load({
|
|
|
560
560
|
}
|
|
561
561
|
//#endregion
|
|
562
562
|
//#region src/update-queue.ts
|
|
563
|
+
/** 一键全部更新的串行队列与并发门禁(纯函数:无 DOM、无 react;面板钩子与单测共用同一份)。
|
|
564
|
+
*
|
|
565
|
+
* 拍板出处(#925 决议,用户已逐条确认,不可改语义):
|
|
566
|
+
* 1. 一键排队串行:按目标顺序逐家查→装→轮询到收尾,一家收尾才起下一家;失败一家记 failed
|
|
567
|
+
* 继续下一家;单家重试走行按钮 recheck/retry;删并行发起与交叉轮询。
|
|
568
|
+
* 2. 安装中题头点不得:任一行 installing 时题头检查更新禁用;人话沿用 reasonText('update-busy') 原句。
|
|
569
|
+
* 3. 行按钮全局互斥:installing 行自锁,其余行禁用不进队列;act 入口有忙拒 update-busy 且不改 rows。
|
|
570
|
+
* 4. 进度各行自显:每行各自显示,他行保持 phase;题头不汇总排队数;待重启跳过安装继续下一家。
|
|
571
|
+
*
|
|
572
|
+
* 本文件只管「排队形状与忙判据」:传输动作由调用方注入(查/装/轮询的真实现住 `update-client.ts`,
|
|
573
|
+
* 面板钩子转交,单测给假件),故改坏串行形状必被门禁逮到。
|
|
574
|
+
*
|
|
575
|
+
* 忙判据的另一半(#980):忙时什么被拦、什么不被拦——批量入口(查/装/一键)被拦,
|
|
576
|
+
* 对话框重开(只读进度、不干活)永远不被拦。两者同住这里,面板与单测共用同一份。
|
|
577
|
+
*/
|
|
563
578
|
/** 任一行 installing 即忙:题头检查更新禁用、行按钮全局互斥、checkAll/act 入口直接返回的同一判据。 */
|
|
564
579
|
function hasInstallingRow(rows) {
|
|
565
580
|
for (const key of Object.keys(rows)) if (rows[key]?.phase === "installing") return true;
|
|
566
581
|
return false;
|
|
567
582
|
}
|
|
583
|
+
/** 忙时拒绝的那枚失败:码与人话都取 `update-busy` 原句(#678/#740 冻结文案,本文件不写第二套话)。 */
|
|
584
|
+
function updateBusyFailure(targetKey) {
|
|
585
|
+
return {
|
|
586
|
+
ok: false,
|
|
587
|
+
code: "update-busy",
|
|
588
|
+
message: reasonText("update-busy"),
|
|
589
|
+
details: { target: targetKey },
|
|
590
|
+
manual: null
|
|
591
|
+
};
|
|
592
|
+
}
|
|
568
593
|
/** 批量入口点不得:一键串行中、任一行安装中、检查轮未收尾,三者任一即拦。
|
|
569
594
|
*
|
|
570
595
|
* 面板 `checkAll`/`updateAll`/`act` 共用同一判据:双轮同时写同一行即错(#980:
|
|
@@ -931,6 +956,11 @@ window.__ModuleLoader__.load({
|
|
|
931
956
|
if (!detail) return null;
|
|
932
957
|
return react.createElement("code", { style: PANEL_STYLE.cmd }, "宿主原话:" + detail);
|
|
933
958
|
}
|
|
959
|
+
/** 忙注那一行(票 #989):守卫拦下点击时的可视回响。题头与缺席卡共用这一块,口径一处。 */
|
|
960
|
+
function busyNoteLine(note) {
|
|
961
|
+
if (!note) return null;
|
|
962
|
+
return react.createElement("div", { style: PANEL_STYLE.dialogNote }, note);
|
|
963
|
+
}
|
|
934
964
|
const ROW_TONE = {
|
|
935
965
|
update: {
|
|
936
966
|
mark: "↑",
|
|
@@ -989,6 +1019,9 @@ window.__ModuleLoader__.load({
|
|
|
989
1019
|
* - 任一行 installing 时 checkAll 入口直接返回、题头禁用;
|
|
990
1020
|
* - act 入口有忙直接返回(有忙拒 update-busy 且不改 rows,含同家第二点);
|
|
991
1021
|
* - 每行各自显示(patch 只写本家 key),题头不汇总排队数。
|
|
1022
|
+
*
|
|
1023
|
+
* 票 #989 补一条:上面三处"直接返回"必须留可视回响(忙注或行级失败),
|
|
1024
|
+
* 与"禁用的才点不得,点得动的必有回响"对齐——静默吞点击即红。
|
|
992
1025
|
*/
|
|
993
1026
|
function useUpdateRows(getCall) {
|
|
994
1027
|
const [targets, setTargets] = react.useState([]);
|
|
@@ -997,6 +1030,7 @@ window.__ModuleLoader__.load({
|
|
|
997
1030
|
const [loadError, setLoadError] = react.useState(null);
|
|
998
1031
|
const [checking, setChecking] = react.useState(false);
|
|
999
1032
|
const [updatingAll, setUpdatingAll] = react.useState(false);
|
|
1033
|
+
const [busyNote, setBusyNote] = react.useState(null);
|
|
1000
1034
|
const [open, setOpen] = react.useState(false);
|
|
1001
1035
|
const alive = react.useRef(true);
|
|
1002
1036
|
react.useEffect(() => () => {
|
|
@@ -1068,6 +1102,7 @@ window.__ModuleLoader__.load({
|
|
|
1068
1102
|
reload(true);
|
|
1069
1103
|
}, [reload]);
|
|
1070
1104
|
const patch = react.useCallback((key, next) => {
|
|
1105
|
+
setBusyNote(null);
|
|
1071
1106
|
setRows((previous) => ({
|
|
1072
1107
|
...previous,
|
|
1073
1108
|
[key]: next
|
|
@@ -1085,12 +1120,17 @@ window.__ModuleLoader__.load({
|
|
|
1085
1120
|
loadError,
|
|
1086
1121
|
checking,
|
|
1087
1122
|
updatingAll,
|
|
1123
|
+
busyNote,
|
|
1088
1124
|
open,
|
|
1089
1125
|
retry,
|
|
1090
1126
|
close: () => setOpen(resolveDialogOpen("hide")),
|
|
1091
1127
|
showProgress: () => setOpen(resolveDialogOpen("show")),
|
|
1092
1128
|
checkAll: react.useCallback(() => {
|
|
1093
|
-
if (batchBlocked())
|
|
1129
|
+
if (batchBlocked()) {
|
|
1130
|
+
setBusyNote(reasonText("update-busy"));
|
|
1131
|
+
return;
|
|
1132
|
+
}
|
|
1133
|
+
setBusyNote(null);
|
|
1094
1134
|
setChecking(true);
|
|
1095
1135
|
setOpen(resolveDialogOpen("show"));
|
|
1096
1136
|
const list = targetsRef.current;
|
|
@@ -1120,9 +1160,16 @@ window.__ModuleLoader__.load({
|
|
|
1120
1160
|
batchBlocked
|
|
1121
1161
|
]),
|
|
1122
1162
|
updateAll: react.useCallback(() => {
|
|
1123
|
-
if (batchBlocked())
|
|
1163
|
+
if (batchBlocked()) {
|
|
1164
|
+
setBusyNote(reasonText("update-busy"));
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1124
1167
|
const list = targetsRef.current;
|
|
1125
|
-
if (list.length === 0)
|
|
1168
|
+
if (list.length === 0) {
|
|
1169
|
+
setBusyNote(reasonText("update-busy"));
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1172
|
+
setBusyNote(null);
|
|
1126
1173
|
updatingAllRef.current = true;
|
|
1127
1174
|
setUpdatingAll(true);
|
|
1128
1175
|
setOpen(resolveDialogOpen("show"));
|
|
@@ -1149,7 +1196,14 @@ window.__ModuleLoader__.load({
|
|
|
1149
1196
|
batchBlocked
|
|
1150
1197
|
]),
|
|
1151
1198
|
act: react.useCallback((target, action) => {
|
|
1152
|
-
if (batchBlocked())
|
|
1199
|
+
if (batchBlocked()) {
|
|
1200
|
+
const current = rowsRef.current[target.key] ?? IDLE$1;
|
|
1201
|
+
patch(target.key, {
|
|
1202
|
+
...current,
|
|
1203
|
+
failure: updateBusyFailure(target.key)
|
|
1204
|
+
});
|
|
1205
|
+
return;
|
|
1206
|
+
}
|
|
1153
1207
|
const current = rowsRef.current[target.key] ?? IDLE$1;
|
|
1154
1208
|
if (action === "recheck") {
|
|
1155
1209
|
patch(target.key, {
|
|
@@ -1177,48 +1231,62 @@ window.__ModuleLoader__.load({
|
|
|
1177
1231
|
failure: null
|
|
1178
1232
|
});
|
|
1179
1233
|
(async () => {
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1234
|
+
try {
|
|
1235
|
+
const absent = isAbsent(target);
|
|
1236
|
+
let version = current.outcome?.snapshot.latestVersion ?? null;
|
|
1237
|
+
if (absent && version === null) {
|
|
1238
|
+
const checked = await checkTarget(getCall(), target);
|
|
1239
|
+
if (!checked.ok) {
|
|
1240
|
+
patch(target.key, {
|
|
1241
|
+
phase: "failed",
|
|
1242
|
+
outcome: null,
|
|
1243
|
+
failure: checked
|
|
1244
|
+
});
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
1185
1247
|
patch(target.key, {
|
|
1248
|
+
phase: "installing",
|
|
1249
|
+
outcome: checked.value,
|
|
1250
|
+
failure: null
|
|
1251
|
+
});
|
|
1252
|
+
version = checked.value.snapshot.latestVersion;
|
|
1253
|
+
}
|
|
1254
|
+
const result = absent ? await installAbsent(getCall(), target, version ?? "") : await updateInstalled(getCall(), target, pollMsRef.current);
|
|
1255
|
+
if (result.ok) {
|
|
1256
|
+
const refreshed = await checkTarget(getCall(), target, target.phones?.status);
|
|
1257
|
+
patch(target.key, refreshed.ok ? {
|
|
1258
|
+
phase: "ready",
|
|
1259
|
+
outcome: refreshed.value,
|
|
1260
|
+
failure: null
|
|
1261
|
+
} : {
|
|
1186
1262
|
phase: "failed",
|
|
1187
1263
|
outcome: null,
|
|
1188
|
-
failure:
|
|
1264
|
+
failure: refreshed
|
|
1189
1265
|
});
|
|
1266
|
+
await reload(false);
|
|
1190
1267
|
return;
|
|
1191
1268
|
}
|
|
1192
1269
|
patch(target.key, {
|
|
1193
|
-
phase: "
|
|
1194
|
-
outcome:
|
|
1195
|
-
failure:
|
|
1270
|
+
phase: "failed",
|
|
1271
|
+
outcome: current.outcome,
|
|
1272
|
+
failure: {
|
|
1273
|
+
...result,
|
|
1274
|
+
manual: result.manual ?? current.outcome?.manual ?? null
|
|
1275
|
+
}
|
|
1196
1276
|
});
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
const result = absent ? await installAbsent(getCall(), target, version ?? "") : await updateInstalled(getCall(), target, pollMsRef.current);
|
|
1200
|
-
if (result.ok) {
|
|
1201
|
-
const refreshed = await checkTarget(getCall(), target, target.phones?.status);
|
|
1202
|
-
patch(target.key, refreshed.ok ? {
|
|
1203
|
-
phase: "ready",
|
|
1204
|
-
outcome: refreshed.value,
|
|
1205
|
-
failure: null
|
|
1206
|
-
} : {
|
|
1277
|
+
} catch (error) {
|
|
1278
|
+
patch(target.key, {
|
|
1207
1279
|
phase: "failed",
|
|
1208
|
-
outcome:
|
|
1209
|
-
failure:
|
|
1280
|
+
outcome: current.outcome,
|
|
1281
|
+
failure: {
|
|
1282
|
+
ok: false,
|
|
1283
|
+
code: "internal",
|
|
1284
|
+
message: reasonText("internal"),
|
|
1285
|
+
details: { detail: String(error?.message ?? error) },
|
|
1286
|
+
manual: current.outcome?.manual ?? null
|
|
1287
|
+
}
|
|
1210
1288
|
});
|
|
1211
|
-
await reload(false);
|
|
1212
|
-
return;
|
|
1213
1289
|
}
|
|
1214
|
-
patch(target.key, {
|
|
1215
|
-
phase: "failed",
|
|
1216
|
-
outcome: current.outcome,
|
|
1217
|
-
failure: {
|
|
1218
|
-
...result,
|
|
1219
|
-
manual: result.manual ?? current.outcome?.manual ?? null
|
|
1220
|
-
}
|
|
1221
|
-
});
|
|
1222
1290
|
})();
|
|
1223
1291
|
}, [
|
|
1224
1292
|
getCall,
|
|
@@ -1391,7 +1459,7 @@ window.__ModuleLoader__.load({
|
|
|
1391
1459
|
onClick: () => showProgress()
|
|
1392
1460
|
}, "查看进度")) : null;
|
|
1393
1461
|
const dialogNote = installingNow ? "安装在后台继续:关掉这块不会中断它。点「查看进度」可随时重新打开。" : checking ? "正在检查更新:关掉这块不会中断它,查完点「查看进度」看结果。" : "安装在宿主后台继续:关掉这块不会中断它。再点「检查更新」看最新进度。";
|
|
1394
|
-
return react.createElement("div", null, react.createElement(RestartBanners, { face: props.face }), progressStrip, open ? react.createElement("div", {
|
|
1462
|
+
return react.createElement("div", null, react.createElement(RestartBanners, { face: props.face }), busyNoteLine(props.face.busyNote), progressStrip, open ? react.createElement("div", {
|
|
1395
1463
|
style: PANEL_STYLE.overlay,
|
|
1396
1464
|
role: "presentation",
|
|
1397
1465
|
onClick: () => close()
|
|
@@ -1451,7 +1519,7 @@ window.__ModuleLoader__.load({
|
|
|
1451
1519
|
break;
|
|
1452
1520
|
default: headline = target.packageName + " " + String(installed) + " 已安装,插件包里也有注册代码,但它的设置页没有加载到面板:重启 DSH 后再看这个页签;仍然这样,就在 DSH 日志里找它的报错。";
|
|
1453
1521
|
}
|
|
1454
|
-
return react.createElement("div", { style: PANEL_STYLE.reco }, react.createElement("div", null, headline), state === "absent" ? react.createElement("button", {
|
|
1522
|
+
return react.createElement("div", { style: PANEL_STYLE.reco }, busyNoteLine(props.face.busyNote), react.createElement("div", null, headline), state === "absent" ? react.createElement("button", {
|
|
1455
1523
|
type: "button",
|
|
1456
1524
|
style: locked ? {
|
|
1457
1525
|
...PANEL_STYLE.btn,
|
|
@@ -3110,7 +3178,7 @@ window.__ModuleLoader__.load({
|
|
|
3110
3178
|
}, react.createElement("span", { style: S.morePkg }, row.pkg), react.createElement("span", { style: S.moreDesc }, row.desc), react.createElement("span", { style: S.moreIcon }, react.createElement(ExternalIcon, null)))));
|
|
3111
3179
|
}
|
|
3112
3180
|
/** 版本胶囊那一行字:只在这里拼一次,屏上与用例读的是同一串。 */
|
|
3113
|
-
const MANAGER_VERSION_TEXT = "dsh-life-pack · 0.3.
|
|
3181
|
+
const MANAGER_VERSION_TEXT = "dsh-life-pack · 0.3.22";
|
|
3114
3182
|
/** 爱生活面板:总设置区 + 检查更新(七家)+ 爱生活页签条(slot 驱动)+ 技能设置页投影/缺席卡。 */
|
|
3115
3183
|
function LifePackSection(props) {
|
|
3116
3184
|
const tabsId = react.useId();
|