Compare commits
2 Commits
f0c0639338
...
d9a91fe5b1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9a91fe5b1 | ||
|
|
c1384e942b |
@@ -6438,7 +6438,7 @@ async function stopTimer(token, entryId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// src/plugin.ts
|
// src/plugin.ts
|
||||||
var CURRENT_VERSION = "1.0.14";
|
var CURRENT_VERSION = "1.0.23";
|
||||||
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,6 +6492,19 @@ 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);
|
||||||
}
|
}
|
||||||
@@ -6501,13 +6514,22 @@ 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 || "");
|
||||||
if (activeEntryId) {
|
const running = await getRunningEntryId();
|
||||||
await Promise.all([ev.action.setState(1), ev.action.setTitle(`\u23F1 ${title}`)]);
|
const isRunning = !!activeEntryId && activeEntryId === running;
|
||||||
} else {
|
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)]);
|
await Promise.all([ev.action.setState(0), ev.action.setTitle(title)]);
|
||||||
|
} else {
|
||||||
|
this.settingsCache.set(ev.action.id, ev.payload.settings);
|
||||||
|
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) {
|
||||||
@@ -6551,18 +6573,21 @@ 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 ev.action.showOk();
|
await setRunningEntry(null);
|
||||||
} else {
|
} else {
|
||||||
for (const other of this.actions) {
|
const prevEntryId = await getRunningEntryId();
|
||||||
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 || ""));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const entryId = await startTimer(
|
const entryId = await startTimer(
|
||||||
@@ -6575,9 +6600,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);
|
||||||
|
|||||||
Binary file not shown.
BIN
com.pdma.notion-timer.sdPlugin/imgs/idle.png
Normal file
BIN
com.pdma.notion-timer.sdPlugin/imgs/idle.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
@@ -1,7 +0,0 @@
|
|||||||
<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>
|
|
||||||
|
Before Width: | Height: | Size: 572 B |
BIN
com.pdma.notion-timer.sdPlugin/imgs/running.png
Normal file
BIN
com.pdma.notion-timer.sdPlugin/imgs/running.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@@ -1,8 +0,0 @@
|
|||||||
<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>
|
|
||||||
|
Before Width: | Height: | Size: 624 B |
@@ -7,7 +7,7 @@ TMP_DIR=$(mktemp -d)
|
|||||||
PLUGIN_FILE="${TMP_DIR}/notion-timer.streamDeckPlugin"
|
PLUGIN_FILE="${TMP_DIR}/notion-timer.streamDeckPlugin"
|
||||||
|
|
||||||
echo "Downloading Notion Timer..."
|
echo "Downloading Notion Timer..."
|
||||||
curl -sL "${GITEA}/${REPO}/raw/branch/master/notion-timer.streamDeckPlugin" -o "${PLUGIN_FILE}"
|
curl -sL "${GITEA}/${REPO}/raw/branch/stable-rebuild/notion-timer.streamDeckPlugin" -o "${PLUGIN_FILE}"
|
||||||
|
|
||||||
echo "Installing — Stream Deck will open automatically..."
|
echo "Installing — Stream Deck will open automatically..."
|
||||||
open "${PLUGIN_FILE}"
|
open "${PLUGIN_FILE}"
|
||||||
|
|||||||
Binary file not shown.
@@ -1,4 +1,4 @@
|
|||||||
const CURRENT_VERSION = "1.0.14";
|
const CURRENT_VERSION = "1.0.23";
|
||||||
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,6 +66,7 @@ interface GlobalSettings {
|
|||||||
timingDbId: string;
|
timingDbId: string;
|
||||||
projectsDbId: string;
|
projectsDbId: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
|
runningEntryId?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TimerSettings {
|
interface TimerSettings {
|
||||||
@@ -84,6 +85,23 @@ 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);
|
||||||
}
|
}
|
||||||
@@ -98,13 +116,26 @@ 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) {
|
|
||||||
await Promise.all([ev.action.setState(1), ev.action.setTitle(`⏱ ${title}`)]);
|
// Use in-memory cache to determine correct state before rendering — no flash
|
||||||
} else {
|
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)]);
|
await Promise.all([ev.action.setState(0), ev.action.setTitle(title)]);
|
||||||
|
} else {
|
||||||
|
this.settingsCache.set(ev.action.id, ev.payload.settings);
|
||||||
|
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)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,19 +184,23 @@ 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 ev.action.showOk();
|
await setRunningEntry(null);
|
||||||
} else {
|
} else {
|
||||||
// Stop any other running timer first
|
const prevEntryId = await getRunningEntryId();
|
||||||
for (const other of this.actions) {
|
|
||||||
if (other.id === ev.action.id) continue;
|
// Stop previous timer
|
||||||
const otherSettings = this.settingsCache.get(other.id);
|
if (prevEntryId) {
|
||||||
if (otherSettings?.activeEntryId) {
|
await stopTimer(global.notionToken, prevEntryId);
|
||||||
await stopTimer(global.notionToken, otherSettings.activeEntryId);
|
for (const other of this.actions) {
|
||||||
const stopped = { ...otherSettings, activeEntryId: null };
|
if (other.id === ev.action.id) continue;
|
||||||
await other.setSettings(stopped);
|
const otherSettings = this.settingsCache.get(other.id);
|
||||||
this.settingsCache.set(other.id, stopped);
|
if (otherSettings?.activeEntryId === prevEntryId) {
|
||||||
await other.setState(0);
|
const stopped = { ...otherSettings, activeEntryId: null };
|
||||||
await other.setTitle(buttonTitle(otherSettings.projectName || ""));
|
await other.setSettings(stopped);
|
||||||
|
this.settingsCache.set(other.id, stopped);
|
||||||
|
await other.setState(0);
|
||||||
|
await other.setTitle(buttonTitle(otherSettings.projectName || ""));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,9 +214,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);
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{ "version": "1.0.14" }
|
{ "version": "1.0.15" }
|
||||||
|
|||||||
Reference in New Issue
Block a user