@ottttto/dsh-scheduled-send 0.3.5 → 0.3.7
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/lib/client.js +23 -9
- package/package.json +1 -1
- package/src/client-core.js +12 -5
- package/src/client-view.js +11 -4
package/lib/client.js
CHANGED
|
@@ -74,12 +74,15 @@ function formatCountdown(sendAt, now) {
|
|
|
74
74
|
const ms = sendAt - now;
|
|
75
75
|
if (ms <= 0) return '即将发送';
|
|
76
76
|
const totalSec = Math.floor(ms / 1000);
|
|
77
|
-
const
|
|
77
|
+
const d = Math.floor(totalSec / 86400);
|
|
78
|
+
const h = Math.floor((totalSec % 86400) / 3600);
|
|
78
79
|
const m = Math.floor((totalSec % 3600) / 60);
|
|
79
80
|
const s = totalSec % 60;
|
|
80
|
-
|
|
81
|
-
if (
|
|
82
|
-
return `${
|
|
81
|
+
// no trailing 后 — callers append their own verb (后发送 / 后重置 …)
|
|
82
|
+
if (d > 0) return `${d}天${h}小时`;
|
|
83
|
+
if (h > 0) return `${h}小时${m}分`;
|
|
84
|
+
if (m > 0) return `${m}分${s}秒`;
|
|
85
|
+
return `${s}秒`;
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
/** Default confirmation time = now + 5 minutes. */
|
|
@@ -237,9 +240,13 @@ function createScheduledClientState({ fetchState, postSchedule, cancelSchedule,
|
|
|
237
240
|
|
|
238
241
|
/** Cancel a pending task: DELETE on the host + drop locally. */
|
|
239
242
|
async cancelTask(id) {
|
|
240
|
-
|
|
243
|
+
// optimistic: drop locally FIRST so the row disappears instantly; the
|
|
244
|
+
// DELETE then persists it. onChanged lets the sibling instance (dock ↔
|
|
245
|
+
// sidebar panel) refresh immediately instead of waiting for its poll.
|
|
241
246
|
tasks = tasks.filter((t) => t.id !== id);
|
|
242
247
|
recentAdds.delete(id);
|
|
248
|
+
if (typeof this.onChanged === 'function') this.onChanged();
|
|
249
|
+
if (cancelSchedule) await cancelSchedule(id);
|
|
243
250
|
},
|
|
244
251
|
|
|
245
252
|
start(intervalMs = 4_000) {
|
|
@@ -487,7 +494,7 @@ function createClientPluginBody(React) {
|
|
|
487
494
|
h("div", { key: "src", style: srcStyle }, t.content),
|
|
488
495
|
h("div", { key: "meta", style: metaStyle }, [
|
|
489
496
|
h("span", { key: "at" }, formatLocalTime(t.sendAt)),
|
|
490
|
-
h("span", { key: "cd" }, formatCountdown(t.sendAt, Date.now())),
|
|
497
|
+
h("span", { key: "cd" }, formatCountdown(t.sendAt, Date.now()) + "后发送"),
|
|
491
498
|
h("span", { key: "sp", style: { marginLeft: "auto" } }, cancelBtn(t.id)),
|
|
492
499
|
]),
|
|
493
500
|
])),
|
|
@@ -626,11 +633,14 @@ function createClientPluginBody(React) {
|
|
|
626
633
|
}, [
|
|
627
634
|
h("div", { key: "c", style: { whiteSpace: "pre-wrap", fontFamily: "monospace" } },
|
|
628
635
|
summarizeContent(t.content) || "(空内容)"),
|
|
629
|
-
h("div", { key: "m", style: { display: "flex", alignItems: "center", gap: 8, color: "rgba(128,128,128,1)"
|
|
630
|
-
h("span", { key: "s", style: {
|
|
636
|
+
h("div", { key: "m", style: { display: "flex", alignItems: "center", gap: 8, color: "rgba(128,128,128,1)" } }, [
|
|
637
|
+
h("span", { key: "s", style: { flex: 1, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } },
|
|
631
638
|
t.sessionExists ? t.sessionTitle : "会话不存在"),
|
|
632
639
|
h("span", { key: "at" }, formatLocalTime(t.sendAt)),
|
|
633
|
-
|
|
640
|
+
]),
|
|
641
|
+
h("div", { key: "act", style: { display: "flex", alignItems: "center", gap: 8 } }, [
|
|
642
|
+
h("span", { key: "cd", style: { color: "#3b82f6", fontWeight: 600 } },
|
|
643
|
+
"⏳ " + formatCountdown(t.sendAt, Date.now()) + "后发送"),
|
|
634
644
|
h("span", { key: "sp", style: { marginLeft: "auto" } }, cancelBtn(t)),
|
|
635
645
|
]),
|
|
636
646
|
])),
|
|
@@ -675,6 +685,10 @@ function createClientPluginBody(React) {
|
|
|
675
685
|
const panelCore = createScheduledClientState({
|
|
676
686
|
fetchState: async () => {
|
|
677
687
|
const res = await doFetch(stateRoutePath, { headers: { accept: "application/json" } });
|
|
688
|
+
// cross-instance sync: a cancel/create in either surface refreshes the
|
|
689
|
+
// other immediately (no 4s poll lag)
|
|
690
|
+
core.onChanged = () => { void panelCore.refresh(); };
|
|
691
|
+
panelCore.onChanged = () => { void core.refresh(); };
|
|
678
692
|
if (!res.ok) throw new Error("state HTTP " + res.status);
|
|
679
693
|
return res.json();
|
|
680
694
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ottttto/dsh-scheduled-send",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
4
4
|
"description": "DSH plugin: scheduled sending (⏰ composer button, dock list of pending tasks, sidebar 定时任务 panel with cross-session cancel, due-time delivery as a normal user bubble)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
package/src/client-core.js
CHANGED
|
@@ -65,12 +65,15 @@ export function formatCountdown(sendAt, now) {
|
|
|
65
65
|
const ms = sendAt - now;
|
|
66
66
|
if (ms <= 0) return '即将发送';
|
|
67
67
|
const totalSec = Math.floor(ms / 1000);
|
|
68
|
-
const
|
|
68
|
+
const d = Math.floor(totalSec / 86400);
|
|
69
|
+
const h = Math.floor((totalSec % 86400) / 3600);
|
|
69
70
|
const m = Math.floor((totalSec % 3600) / 60);
|
|
70
71
|
const s = totalSec % 60;
|
|
71
|
-
|
|
72
|
-
if (
|
|
73
|
-
return `${
|
|
72
|
+
// no trailing 后 — callers append their own verb (后发送 / 后重置 …)
|
|
73
|
+
if (d > 0) return `${d}天${h}小时`;
|
|
74
|
+
if (h > 0) return `${h}小时${m}分`;
|
|
75
|
+
if (m > 0) return `${m}分${s}秒`;
|
|
76
|
+
return `${s}秒`;
|
|
74
77
|
}
|
|
75
78
|
|
|
76
79
|
/** Default confirmation time = now + 5 minutes. */
|
|
@@ -228,9 +231,13 @@ export function createScheduledClientState({ fetchState, postSchedule, cancelSch
|
|
|
228
231
|
|
|
229
232
|
/** Cancel a pending task: DELETE on the host + drop locally. */
|
|
230
233
|
async cancelTask(id) {
|
|
231
|
-
|
|
234
|
+
// optimistic: drop locally FIRST so the row disappears instantly; the
|
|
235
|
+
// DELETE then persists it. onChanged lets the sibling instance (dock ↔
|
|
236
|
+
// sidebar panel) refresh immediately instead of waiting for its poll.
|
|
232
237
|
tasks = tasks.filter((t) => t.id !== id);
|
|
233
238
|
recentAdds.delete(id);
|
|
239
|
+
if (typeof this.onChanged === 'function') this.onChanged();
|
|
240
|
+
if (cancelSchedule) await cancelSchedule(id);
|
|
234
241
|
},
|
|
235
242
|
|
|
236
243
|
start(intervalMs = 4_000) {
|
package/src/client-view.js
CHANGED
|
@@ -223,7 +223,7 @@ function createClientPluginBody(React) {
|
|
|
223
223
|
h("div", { key: "src", style: srcStyle }, t.content),
|
|
224
224
|
h("div", { key: "meta", style: metaStyle }, [
|
|
225
225
|
h("span", { key: "at" }, formatLocalTime(t.sendAt)),
|
|
226
|
-
h("span", { key: "cd" }, formatCountdown(t.sendAt, Date.now())),
|
|
226
|
+
h("span", { key: "cd" }, formatCountdown(t.sendAt, Date.now()) + "后发送"),
|
|
227
227
|
h("span", { key: "sp", style: { marginLeft: "auto" } }, cancelBtn(t.id)),
|
|
228
228
|
]),
|
|
229
229
|
])),
|
|
@@ -362,11 +362,14 @@ function createClientPluginBody(React) {
|
|
|
362
362
|
}, [
|
|
363
363
|
h("div", { key: "c", style: { whiteSpace: "pre-wrap", fontFamily: "monospace" } },
|
|
364
364
|
summarizeContent(t.content) || "(空内容)"),
|
|
365
|
-
h("div", { key: "m", style: { display: "flex", alignItems: "center", gap: 8, color: "rgba(128,128,128,1)"
|
|
366
|
-
h("span", { key: "s", style: {
|
|
365
|
+
h("div", { key: "m", style: { display: "flex", alignItems: "center", gap: 8, color: "rgba(128,128,128,1)" } }, [
|
|
366
|
+
h("span", { key: "s", style: { flex: 1, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } },
|
|
367
367
|
t.sessionExists ? t.sessionTitle : "会话不存在"),
|
|
368
368
|
h("span", { key: "at" }, formatLocalTime(t.sendAt)),
|
|
369
|
-
|
|
369
|
+
]),
|
|
370
|
+
h("div", { key: "act", style: { display: "flex", alignItems: "center", gap: 8 } }, [
|
|
371
|
+
h("span", { key: "cd", style: { color: "#3b82f6", fontWeight: 600 } },
|
|
372
|
+
"⏳ " + formatCountdown(t.sendAt, Date.now()) + "后发送"),
|
|
370
373
|
h("span", { key: "sp", style: { marginLeft: "auto" } }, cancelBtn(t)),
|
|
371
374
|
]),
|
|
372
375
|
])),
|
|
@@ -411,6 +414,10 @@ function createClientPluginBody(React) {
|
|
|
411
414
|
const panelCore = createScheduledClientState({
|
|
412
415
|
fetchState: async () => {
|
|
413
416
|
const res = await doFetch(stateRoutePath, { headers: { accept: "application/json" } });
|
|
417
|
+
// cross-instance sync: a cancel/create in either surface refreshes the
|
|
418
|
+
// other immediately (no 4s poll lag)
|
|
419
|
+
core.onChanged = () => { void panelCore.refresh(); };
|
|
420
|
+
panelCore.onChanged = () => { void core.refresh(); };
|
|
414
421
|
if (!res.ok) throw new Error("state HTTP " + res.status);
|
|
415
422
|
return res.json();
|
|
416
423
|
},
|