v1.0.40: fix duplicate timer race condition on rapid double-press
onKeyDown is async and calls await startTimer (~1s network). A second press before that resolves saw the same state (isRunning=false, memRunningEntryId=null) and created a second Notion entry. Only the last startTimer call's ID was tracked, orphaning the first entry running indefinitely in Notion. pendingKeyDown Set acts as a per-action mutex: a second press while the first is in-flight is dropped. try/finally guarantees the lock is always released. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6438,7 +6438,7 @@ async function stopTimer(token, entryId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// src/plugin.ts
|
// src/plugin.ts
|
||||||
var CURRENT_VERSION = "1.0.39";
|
var CURRENT_VERSION = "1.0.40";
|
||||||
var GITEA_BASE = "https://gitea.pdmarf.co.uk/pdm/stream_deck_notion_timer/raw/branch/stable-rebuild";
|
var GITEA_BASE = "https://gitea.pdmarf.co.uk/pdm/stream_deck_notion_timer/raw/branch/stable-rebuild";
|
||||||
var SIGNING_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
|
var SIGNING_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
|
||||||
MCowBQYDK2VwAyEAN7ko8TUpuPzPAJuKAZCRjV0c4ZSlou5d9pUAF6o12b4=
|
MCowBQYDK2VwAyEAN7ko8TUpuPzPAJuKAZCRjV0c4ZSlou5d9pUAF6o12b4=
|
||||||
@@ -6569,6 +6569,7 @@ function buttonTitle(projectName) {
|
|||||||
}
|
}
|
||||||
var TimerToggle = class extends SingletonAction {
|
var TimerToggle = class extends SingletonAction {
|
||||||
projectCache = /* @__PURE__ */ new Map();
|
projectCache = /* @__PURE__ */ new Map();
|
||||||
|
pendingKeyDown = /* @__PURE__ */ new Set();
|
||||||
async onWillAppear(ev) {
|
async onWillAppear(ev) {
|
||||||
this.projectCache.set(ev.action.id, ev.payload.settings);
|
this.projectCache.set(ev.action.id, ev.payload.settings);
|
||||||
const title = buttonTitle(ev.payload.settings.projectName || "");
|
const title = buttonTitle(ev.payload.settings.projectName || "");
|
||||||
@@ -6584,6 +6585,9 @@ var TimerToggle = class extends SingletonAction {
|
|||||||
await sendProjectsToPI();
|
await sendProjectsToPI();
|
||||||
}
|
}
|
||||||
async onKeyDown(ev) {
|
async onKeyDown(ev) {
|
||||||
|
if (this.pendingKeyDown.has(ev.action.id)) return;
|
||||||
|
this.pendingKeyDown.add(ev.action.id);
|
||||||
|
try {
|
||||||
const { projectId, projectName } = ev.payload.settings;
|
const { projectId, projectName } = ev.payload.settings;
|
||||||
const title = buttonTitle(projectName || "");
|
const title = buttonTitle(projectName || "");
|
||||||
const isRunning = memRunningActionId === ev.action.id;
|
const isRunning = memRunningActionId === ev.action.id;
|
||||||
@@ -6631,6 +6635,9 @@ var TimerToggle = class extends SingletonAction {
|
|||||||
plugin_default.logger.error("Timer toggle failed:", err);
|
plugin_default.logger.error("Timer toggle failed:", err);
|
||||||
await ev.action.showAlert();
|
await ev.action.showAlert();
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
this.pendingKeyDown.delete(ev.action.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
TimerToggle = __decorateClass([
|
TimerToggle = __decorateClass([
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
в<╥!Ъ░xR√з└СRё;=▀LI"я╓╢HuэF├ы▐Ос─╒┘ sb▌Y╛{йH│╤Н.JщNЦь1╔l
|
{QÕ¿óîj¶?Íz_ÎDA+ÌOÎi °<u,&/Õ+ødeÆá¤"÷œSw+&öO.RC‘&óä^H-¯
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
const CURRENT_VERSION = "1.0.39";
|
const CURRENT_VERSION = "1.0.40";
|
||||||
const GITEA_BASE = "https://gitea.pdmarf.co.uk/pdm/stream_deck_notion_timer/raw/branch/stable-rebuild";
|
const GITEA_BASE = "https://gitea.pdmarf.co.uk/pdm/stream_deck_notion_timer/raw/branch/stable-rebuild";
|
||||||
const SIGNING_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
|
const SIGNING_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
|
||||||
MCowBQYDK2VwAyEAN7ko8TUpuPzPAJuKAZCRjV0c4ZSlou5d9pUAF6o12b4=
|
MCowBQYDK2VwAyEAN7ko8TUpuPzPAJuKAZCRjV0c4ZSlou5d9pUAF6o12b4=
|
||||||
@@ -167,6 +167,7 @@ function buttonTitle(projectName: string): string {
|
|||||||
@action({ UUID: "com.pdma.notion-timer.toggle" })
|
@action({ UUID: "com.pdma.notion-timer.toggle" })
|
||||||
class TimerToggle extends SingletonAction<TimerSettings> {
|
class TimerToggle extends SingletonAction<TimerSettings> {
|
||||||
private projectCache = new Map<string, TimerSettings>();
|
private projectCache = new Map<string, TimerSettings>();
|
||||||
|
private pendingKeyDown = new Set<string>();
|
||||||
|
|
||||||
async onWillAppear(ev: WillAppearEvent<TimerSettings>): Promise<void> {
|
async onWillAppear(ev: WillAppearEvent<TimerSettings>): Promise<void> {
|
||||||
this.projectCache.set(ev.action.id, ev.payload.settings);
|
this.projectCache.set(ev.action.id, ev.payload.settings);
|
||||||
@@ -185,6 +186,9 @@ class TimerToggle extends SingletonAction<TimerSettings> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async onKeyDown(ev: KeyDownEvent<TimerSettings>): Promise<void> {
|
async onKeyDown(ev: KeyDownEvent<TimerSettings>): Promise<void> {
|
||||||
|
if (this.pendingKeyDown.has(ev.action.id)) return;
|
||||||
|
this.pendingKeyDown.add(ev.action.id);
|
||||||
|
try {
|
||||||
const { projectId, projectName } = ev.payload.settings;
|
const { projectId, projectName } = ev.payload.settings;
|
||||||
const title = buttonTitle(projectName || "");
|
const title = buttonTitle(projectName || "");
|
||||||
const isRunning = memRunningActionId === ev.action.id;
|
const isRunning = memRunningActionId === ev.action.id;
|
||||||
@@ -231,6 +235,9 @@ class TimerToggle extends SingletonAction<TimerSettings> {
|
|||||||
streamDeck.logger.error("Timer toggle failed:", err);
|
streamDeck.logger.error("Timer toggle failed:", err);
|
||||||
await ev.action.showAlert();
|
await ev.action.showAlert();
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
this.pendingKeyDown.delete(ev.action.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user