@ottttto/dsh-scheduled-send 0.3.6 → 0.3.9
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/dsh.plugin.json +1 -1
- package/lib/client.js +23 -6
- package/package.json +1 -1
- package/src/client-core.js +18 -5
- package/src/client-view.js +5 -1
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "@ottttto/dsh-scheduled-send",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"main": "./src/index.js",
|
|
5
5
|
"description": "定时发送:到点以你的身份把消息作为正常用户气泡发进当前会话,无人值守、会话隔离、断电补发。Scheduled send for DSH — fires as a normal user bubble into the bound conversation, unattended, per-session isolated, restart-safe.",
|
|
6
6
|
"engines": {
|
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. */
|
|
@@ -236,10 +239,20 @@ function createScheduledClientState({ fetchState, postSchedule, cancelSchedule,
|
|
|
236
239
|
},
|
|
237
240
|
|
|
238
241
|
/** Cancel a pending task: DELETE on the host + drop locally. */
|
|
242
|
+
/** Cross-instance optimistic removal by id (no fetch). */
|
|
243
|
+
removeTask(id) {
|
|
244
|
+
tasks = tasks.filter((t) => t.id !== id);
|
|
245
|
+
recentAdds.delete(id);
|
|
246
|
+
},
|
|
247
|
+
|
|
239
248
|
async cancelTask(id) {
|
|
240
|
-
|
|
249
|
+
// optimistic: drop locally FIRST so the row disappears instantly; the
|
|
250
|
+
// DELETE then persists it. onChanged lets the sibling instance (dock ↔
|
|
251
|
+
// sidebar panel) refresh immediately instead of waiting for its poll.
|
|
241
252
|
tasks = tasks.filter((t) => t.id !== id);
|
|
242
253
|
recentAdds.delete(id);
|
|
254
|
+
if (typeof this.onChanged === 'function') this.onChanged(id);
|
|
255
|
+
if (cancelSchedule) await cancelSchedule(id);
|
|
243
256
|
},
|
|
244
257
|
|
|
245
258
|
start(intervalMs = 4_000) {
|
|
@@ -487,7 +500,7 @@ function createClientPluginBody(React) {
|
|
|
487
500
|
h("div", { key: "src", style: srcStyle }, t.content),
|
|
488
501
|
h("div", { key: "meta", style: metaStyle }, [
|
|
489
502
|
h("span", { key: "at" }, formatLocalTime(t.sendAt)),
|
|
490
|
-
h("span", { key: "cd" }, formatCountdown(t.sendAt, Date.now())),
|
|
503
|
+
h("span", { key: "cd" }, formatCountdown(t.sendAt, Date.now()) + "后发送"),
|
|
491
504
|
h("span", { key: "sp", style: { marginLeft: "auto" } }, cancelBtn(t.id)),
|
|
492
505
|
]),
|
|
493
506
|
])),
|
|
@@ -678,6 +691,10 @@ function createClientPluginBody(React) {
|
|
|
678
691
|
const panelCore = createScheduledClientState({
|
|
679
692
|
fetchState: async () => {
|
|
680
693
|
const res = await doFetch(stateRoutePath, { headers: { accept: "application/json" } });
|
|
694
|
+
// cross-instance sync: a cancel/create in either surface refreshes the
|
|
695
|
+
// other immediately (no 4s poll lag)
|
|
696
|
+
core.onChanged = (id) => { if (id) panelCore.removeTask(id); void panelCore.refresh(); };
|
|
697
|
+
panelCore.onChanged = () => { void core.refresh(); };
|
|
681
698
|
if (!res.ok) throw new Error("state HTTP " + res.status);
|
|
682
699
|
return res.json();
|
|
683
700
|
},
|
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.9",
|
|
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. */
|
|
@@ -227,10 +230,20 @@ export function createScheduledClientState({ fetchState, postSchedule, cancelSch
|
|
|
227
230
|
},
|
|
228
231
|
|
|
229
232
|
/** Cancel a pending task: DELETE on the host + drop locally. */
|
|
233
|
+
/** Cross-instance optimistic removal by id (no fetch). */
|
|
234
|
+
removeTask(id) {
|
|
235
|
+
tasks = tasks.filter((t) => t.id !== id);
|
|
236
|
+
recentAdds.delete(id);
|
|
237
|
+
},
|
|
238
|
+
|
|
230
239
|
async cancelTask(id) {
|
|
231
|
-
|
|
240
|
+
// optimistic: drop locally FIRST so the row disappears instantly; the
|
|
241
|
+
// DELETE then persists it. onChanged lets the sibling instance (dock ↔
|
|
242
|
+
// sidebar panel) refresh immediately instead of waiting for its poll.
|
|
232
243
|
tasks = tasks.filter((t) => t.id !== id);
|
|
233
244
|
recentAdds.delete(id);
|
|
245
|
+
if (typeof this.onChanged === 'function') this.onChanged(id);
|
|
246
|
+
if (cancelSchedule) await cancelSchedule(id);
|
|
234
247
|
},
|
|
235
248
|
|
|
236
249
|
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
|
])),
|
|
@@ -414,6 +414,10 @@ function createClientPluginBody(React) {
|
|
|
414
414
|
const panelCore = createScheduledClientState({
|
|
415
415
|
fetchState: async () => {
|
|
416
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 = (id) => { if (id) panelCore.removeTask(id); void panelCore.refresh(); };
|
|
420
|
+
panelCore.onChanged = () => { void core.refresh(); };
|
|
417
421
|
if (!res.ok) throw new Error("state HTTP " + res.status);
|
|
418
422
|
return res.json();
|
|
419
423
|
},
|