Compare commits

..

1 Commits

Author SHA1 Message Date
pdmarf
f0c0639338 v1.0.24: stable-rebuild from v1.0.14 (pure, no modifications)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 21:32:43 +01:00
9 changed files with 52 additions and 97 deletions

View File

@@ -6438,7 +6438,7 @@ async function stopTimer(token, entryId) {
} }
// src/plugin.ts // src/plugin.ts
var CURRENT_VERSION = "1.0.23"; var CURRENT_VERSION = "1.0.24";
var GITEA_BASE = "https://gitea.pdmarf.co.uk/pdm/stream_deck_notion_timer/raw/branch/master"; var GITEA_BASE = "https://gitea.pdmarf.co.uk/pdm/stream_deck_notion_timer/raw/branch/master";
var SIGNING_PUBLIC_KEY = `-----BEGIN PUBLIC KEY----- var SIGNING_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAN7ko8TUpuPzPAJuKAZCRjV0c4ZSlou5d9pUAF6o12b4= MCowBQYDK2VwAyEAN7ko8TUpuPzPAJuKAZCRjV0c4ZSlou5d9pUAF6o12b4=
@@ -6492,19 +6492,6 @@ async function getGlobal() {
const stored = await plugin_default.settings.getGlobalSettings(); const stored = await plugin_default.settings.getGlobalSettings();
return { ...stored, ...HARDCODED }; return { ...stored, ...HARDCODED };
} }
var memRunningEntryId = void 0;
async function getRunningEntryId() {
if (memRunningEntryId === void 0) {
const stored = await plugin_default.settings.getGlobalSettings();
memRunningEntryId = stored.runningEntryId ?? null;
}
return memRunningEntryId;
}
async function setRunningEntry(entryId) {
memRunningEntryId = entryId;
const stored = await plugin_default.settings.getGlobalSettings();
await plugin_default.settings.setGlobalSettings({ ...stored, runningEntryId: entryId });
}
function isConfigured(g) { function isConfigured(g) {
return !!(g.notionToken && g.userId); return !!(g.notionToken && g.userId);
} }
@@ -6514,22 +6501,13 @@ function buttonTitle(projectName) {
var TimerToggle = class extends SingletonAction { var TimerToggle = class extends SingletonAction {
settingsCache = /* @__PURE__ */ new Map(); settingsCache = /* @__PURE__ */ new Map();
async onWillAppear(ev) { async onWillAppear(ev) {
this.settingsCache.set(ev.action.id, ev.payload.settings);
const { activeEntryId, projectName } = ev.payload.settings; const { activeEntryId, projectName } = ev.payload.settings;
const title = buttonTitle(projectName || ""); const title = buttonTitle(projectName || "");
const running = await getRunningEntryId(); if (activeEntryId) {
const isRunning = !!activeEntryId && activeEntryId === running; await Promise.all([ev.action.setState(1), ev.action.setTitle(`\u23F1 ${title}`)]);
if (activeEntryId && !isRunning) {
const cleared = { ...ev.payload.settings, activeEntryId: null };
await ev.action.setSettings(cleared);
this.settingsCache.set(ev.action.id, cleared);
await Promise.all([ev.action.setState(0), ev.action.setTitle(title)]);
} else { } else {
this.settingsCache.set(ev.action.id, ev.payload.settings); await Promise.all([ev.action.setState(0), ev.action.setTitle(title)]);
if (isRunning) {
await Promise.all([ev.action.setState(1), ev.action.setTitle(`\u23F1 ${title}`)]);
} else {
await Promise.all([ev.action.setState(0), ev.action.setTitle(title)]);
}
} }
} }
async onPropertyInspectorDidAppear(ev) { async onPropertyInspectorDidAppear(ev) {
@@ -6573,21 +6551,18 @@ var TimerToggle = class extends SingletonAction {
this.settingsCache.set(ev.action.id, stopped); this.settingsCache.set(ev.action.id, stopped);
await ev.action.setState(0); await ev.action.setState(0);
await ev.action.setTitle(title); await ev.action.setTitle(title);
await setRunningEntry(null); await ev.action.showOk();
} else { } else {
const prevEntryId = await getRunningEntryId(); for (const other of this.actions) {
if (prevEntryId) { if (other.id === ev.action.id) continue;
await stopTimer(global.notionToken, prevEntryId); const otherSettings = this.settingsCache.get(other.id);
for (const other of this.actions) { if (otherSettings?.activeEntryId) {
if (other.id === ev.action.id) continue; await stopTimer(global.notionToken, otherSettings.activeEntryId);
const otherSettings = this.settingsCache.get(other.id); const stopped = { ...otherSettings, activeEntryId: null };
if (otherSettings?.activeEntryId === prevEntryId) { await other.setSettings(stopped);
const stopped = { ...otherSettings, activeEntryId: null }; this.settingsCache.set(other.id, stopped);
await other.setSettings(stopped); await other.setState(0);
this.settingsCache.set(other.id, stopped); await other.setTitle(buttonTitle(otherSettings.projectName || ""));
await other.setState(0);
await other.setTitle(buttonTitle(otherSettings.projectName || ""));
}
} }
} }
const entryId = await startTimer( const entryId = await startTimer(
@@ -6600,9 +6575,9 @@ var TimerToggle = class extends SingletonAction {
const started = { ...ev.payload.settings, activeEntryId: entryId }; const started = { ...ev.payload.settings, activeEntryId: entryId };
await ev.action.setSettings(started); await ev.action.setSettings(started);
this.settingsCache.set(ev.action.id, started); this.settingsCache.set(ev.action.id, started);
await setRunningEntry(entryId);
await ev.action.setState(1); await ev.action.setState(1);
await ev.action.setTitle(`\u23F1 ${title}`); await ev.action.setTitle(`\u23F1 ${title}`);
await ev.action.showOk();
} }
} catch (err) { } catch (err) {
plugin_default.logger.error("Timer toggle failed:", err); plugin_default.logger.error("Timer toggle failed:", err);

View File

@@ -1 +1 @@
ä(=uõÐüIXˆ<58>»pTüD0tQ ŽÚãεFïÊr•!Ÿ(\ç$À€{Þó ÞòÝRlgˆ¶ª={¸óÞÛd¼H•øXº¿žìÑ æJó0OTù1Š“Änï8àª?—שáƒà}IQÉjÊ[

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 144 144" width="144" height="144">
<circle cx="72" cy="58" r="42" fill="none" stroke="#888888" stroke-width="6"/>
<line x1="72" y1="58" x2="72" y2="28" stroke="#888888" stroke-width="6" stroke-linecap="round"/>
<line x1="72" y1="58" x2="94" y2="71" stroke="#888888" stroke-width="6" stroke-linecap="round"/>
<line x1="62" y1="12" x2="82" y2="12" stroke="#888888" stroke-width="6" stroke-linecap="round"/>
<line x1="72" y1="12" x2="72" y2="20" stroke="#888888" stroke-width="6" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 572 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 144 144" width="144" height="144">
<circle cx="72" cy="58" r="42" fill="#1a3a1a" stroke="#4caf50" stroke-width="6"/>
<line x1="72" y1="58" x2="72" y2="28" stroke="#4caf50" stroke-width="6" stroke-linecap="round"/>
<line x1="72" y1="58" x2="94" y2="71" stroke="#4caf50" stroke-width="6" stroke-linecap="round"/>
<line x1="62" y1="12" x2="82" y2="12" stroke="#4caf50" stroke-width="6" stroke-linecap="round"/>
<line x1="72" y1="12" x2="72" y2="20" stroke="#4caf50" stroke-width="6" stroke-linecap="round"/>
<circle cx="72" cy="58" r="5" fill="#4caf50"/>
</svg>

After

Width:  |  Height:  |  Size: 624 B

Binary file not shown.

View File

@@ -1,4 +1,4 @@
const CURRENT_VERSION = "1.0.23"; const CURRENT_VERSION = "1.0.24";
const GITEA_BASE = "https://gitea.pdmarf.co.uk/pdm/stream_deck_notion_timer/raw/branch/master"; const GITEA_BASE = "https://gitea.pdmarf.co.uk/pdm/stream_deck_notion_timer/raw/branch/master";
const SIGNING_PUBLIC_KEY = `-----BEGIN PUBLIC KEY----- const SIGNING_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAN7ko8TUpuPzPAJuKAZCRjV0c4ZSlou5d9pUAF6o12b4= MCowBQYDK2VwAyEAN7ko8TUpuPzPAJuKAZCRjV0c4ZSlou5d9pUAF6o12b4=
@@ -66,7 +66,6 @@ interface GlobalSettings {
timingDbId: string; timingDbId: string;
projectsDbId: string; projectsDbId: string;
userId: string; userId: string;
runningEntryId?: string | null;
} }
interface TimerSettings { interface TimerSettings {
@@ -85,23 +84,6 @@ async function getGlobal(): Promise<GlobalSettings> {
return { ...stored, ...HARDCODED }; return { ...stored, ...HARDCODED };
} }
// In-memory cache so onWillAppear can check running state without an async round-trip
let memRunningEntryId: string | null | undefined = undefined; // undefined = not yet loaded
async function getRunningEntryId(): Promise<string | null> {
if (memRunningEntryId === undefined) {
const stored = await streamDeck.settings.getGlobalSettings<GlobalSettings>();
memRunningEntryId = stored.runningEntryId ?? null;
}
return memRunningEntryId;
}
async function setRunningEntry(entryId: string | null): Promise<void> {
memRunningEntryId = entryId;
const stored = await streamDeck.settings.getGlobalSettings<GlobalSettings>();
await streamDeck.settings.setGlobalSettings({ ...stored, runningEntryId: entryId });
}
function isConfigured(g: GlobalSettings): boolean { function isConfigured(g: GlobalSettings): boolean {
return !!(g.notionToken && g.userId); return !!(g.notionToken && g.userId);
} }
@@ -116,26 +98,13 @@ class TimerToggle extends SingletonAction<TimerSettings> {
private settingsCache = new Map<string, TimerSettings>(); private settingsCache = new Map<string, TimerSettings>();
async onWillAppear(ev: WillAppearEvent<TimerSettings>): Promise<void> { async onWillAppear(ev: WillAppearEvent<TimerSettings>): Promise<void> {
this.settingsCache.set(ev.action.id, ev.payload.settings);
const { activeEntryId, projectName } = ev.payload.settings; const { activeEntryId, projectName } = ev.payload.settings;
const title = buttonTitle(projectName || ""); const title = buttonTitle(projectName || "");
if (activeEntryId) {
// Use in-memory cache to determine correct state before rendering — no flash await Promise.all([ev.action.setState(1), ev.action.setTitle(`${title}`)]);
const running = await getRunningEntryId();
const isRunning = !!activeEntryId && activeEntryId === running;
if (activeEntryId && !isRunning) {
// Self-heal: this button thinks it's running but it's not — clear it
const cleared = { ...ev.payload.settings, activeEntryId: null };
await ev.action.setSettings(cleared);
this.settingsCache.set(ev.action.id, cleared);
await Promise.all([ev.action.setState(0), ev.action.setTitle(title)]);
} else { } else {
this.settingsCache.set(ev.action.id, ev.payload.settings); await Promise.all([ev.action.setState(0), ev.action.setTitle(title)]);
if (isRunning) {
await Promise.all([ev.action.setState(1), ev.action.setTitle(`${title}`)]);
} else {
await Promise.all([ev.action.setState(0), ev.action.setTitle(title)]);
}
} }
} }
@@ -184,23 +153,19 @@ class TimerToggle extends SingletonAction<TimerSettings> {
this.settingsCache.set(ev.action.id, stopped); this.settingsCache.set(ev.action.id, stopped);
await ev.action.setState(0); await ev.action.setState(0);
await ev.action.setTitle(title); await ev.action.setTitle(title);
await setRunningEntry(null); await ev.action.showOk();
} else { } else {
const prevEntryId = await getRunningEntryId(); // Stop any other running timer first
for (const other of this.actions) {
// Stop previous timer if (other.id === ev.action.id) continue;
if (prevEntryId) { const otherSettings = this.settingsCache.get(other.id);
await stopTimer(global.notionToken, prevEntryId); if (otherSettings?.activeEntryId) {
for (const other of this.actions) { await stopTimer(global.notionToken, otherSettings.activeEntryId);
if (other.id === ev.action.id) continue; const stopped = { ...otherSettings, activeEntryId: null };
const otherSettings = this.settingsCache.get(other.id); await other.setSettings(stopped);
if (otherSettings?.activeEntryId === prevEntryId) { this.settingsCache.set(other.id, stopped);
const stopped = { ...otherSettings, activeEntryId: null }; await other.setState(0);
await other.setSettings(stopped); await other.setTitle(buttonTitle(otherSettings.projectName || ""));
this.settingsCache.set(other.id, stopped);
await other.setState(0);
await other.setTitle(buttonTitle(otherSettings.projectName || ""));
}
} }
} }
@@ -214,9 +179,9 @@ class TimerToggle extends SingletonAction<TimerSettings> {
const started = { ...ev.payload.settings, activeEntryId: entryId }; const started = { ...ev.payload.settings, activeEntryId: entryId };
await ev.action.setSettings(started); await ev.action.setSettings(started);
this.settingsCache.set(ev.action.id, started); this.settingsCache.set(ev.action.id, started);
await setRunningEntry(entryId);
await ev.action.setState(1); await ev.action.setState(1);
await ev.action.setTitle(`${title}`); await ev.action.setTitle(`${title}`);
await ev.action.showOk();
} }
} catch (err) { } catch (err) {
streamDeck.logger.error("Timer toggle failed:", err); streamDeck.logger.error("Timer toggle failed:", err);

View File

@@ -1 +1 @@
{ "version": "1.0.15" } { "version": "1.0.14" }