From d6d695e6c0a68216c641b61f592420cc22ca8c7d Mon Sep 17 00:00:00 2001 From: pdmarf <135653545+pdmarf@users.noreply.github.com> Date: Fri, 24 Apr 2026 07:47:32 +0100 Subject: [PATCH] v1.0.26: eliminate double-green flash when switching timers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both buttons now flip to their final visual state immediately on key press, before any API calls. Previously the old button stayed green throughout the stopTimer network round-trip (~0.5–2s), causing a window where both buttons appeared green simultaneously. Co-Authored-By: Claude Sonnet 4.6 --- com.pdma.notion-timer.sdPlugin/bin/plugin.js | 16 +++++++++----- .../bin/plugin.js.sig | 5 ++--- com.pdma.notion-timer.sdPlugin/manifest.json | 2 +- notion-timer.streamDeckPlugin | Bin 98756 -> 98770 bytes src/plugin.ts | 20 ++++++++++++------ 5 files changed, 28 insertions(+), 15 deletions(-) diff --git a/com.pdma.notion-timer.sdPlugin/bin/plugin.js b/com.pdma.notion-timer.sdPlugin/bin/plugin.js index 022e76d..4398be8 100644 --- a/com.pdma.notion-timer.sdPlugin/bin/plugin.js +++ b/com.pdma.notion-timer.sdPlugin/bin/plugin.js @@ -6438,7 +6438,7 @@ async function stopTimer(token, entryId) { } // src/plugin.ts -var CURRENT_VERSION = "1.0.25"; +var CURRENT_VERSION = "1.0.26"; var GITEA_BASE = "https://gitea.pdmarf.co.uk/pdm/stream_deck_notion_timer/raw/branch/master"; var SIGNING_PUBLIC_KEY = `-----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAN7ko8TUpuPzPAJuKAZCRjV0c4ZSlou5d9pUAF6o12b4= @@ -6589,6 +6589,16 @@ var TimerToggle = class extends SingletonAction { await setRunningEntry(null); } else { const prevEntryId = await getRunningEntryId(); + if (prevEntryId) { + for (const other of this.actions) { + if (other.id === ev.action.id) continue; + const otherSettings = this.settingsCache.get(other.id); + if (otherSettings?.activeEntryId === prevEntryId) { + await Promise.all([other.setState(0), other.setTitle(buttonTitle(otherSettings.projectName || ""))]); + } + } + } + await Promise.all([ev.action.setState(1), ev.action.setTitle(`\u23F1 ${title}`)]); if (prevEntryId) { await stopTimer(global.notionToken, prevEntryId); for (const other of this.actions) { @@ -6598,8 +6608,6 @@ var TimerToggle = class extends SingletonAction { const stopped = { ...otherSettings, activeEntryId: null }; await other.setSettings(stopped); this.settingsCache.set(other.id, stopped); - await other.setState(0); - await other.setTitle(buttonTitle(otherSettings.projectName || "")); } } } @@ -6614,8 +6622,6 @@ var TimerToggle = class extends SingletonAction { await ev.action.setSettings(started); this.settingsCache.set(ev.action.id, started); await setRunningEntry(entryId); - await ev.action.setState(1); - await ev.action.setTitle(`\u23F1 ${title}`); } } catch (err) { plugin_default.logger.error("Timer toggle failed:", err); diff --git a/com.pdma.notion-timer.sdPlugin/bin/plugin.js.sig b/com.pdma.notion-timer.sdPlugin/bin/plugin.js.sig index d1817a8..86c3f4c 100644 --- a/com.pdma.notion-timer.sdPlugin/bin/plugin.js.sig +++ b/com.pdma.notion-timer.sdPlugin/bin/plugin.js.sig @@ -1,3 +1,2 @@ -Һ -HJ{l >0^tq2lZ +e>W%"Y8ge8B$>W=kIďMe2K \ No newline at end of file diff --git a/com.pdma.notion-timer.sdPlugin/manifest.json b/com.pdma.notion-timer.sdPlugin/manifest.json index 76b72fc..8461c55 100644 --- a/com.pdma.notion-timer.sdPlugin/manifest.json +++ b/com.pdma.notion-timer.sdPlugin/manifest.json @@ -2,7 +2,7 @@ "Author": "Pete Marfleet", "Description": "Toggle Notion time tracking for a project with a single button press.", "Name": "Notion Timer", - "Version": "1.0.25", + "Version": "1.0.26", "SDKVersion": 2, "Software": { "MinimumVersion": "5.0" }, "OS": [{ "Platform": "mac", "MinimumVersion": "10.11" }], diff --git a/notion-timer.streamDeckPlugin b/notion-timer.streamDeckPlugin index d25c9f2b51774ca622923226682afa193dd38acc..37993c2964a9dab42187a56643d9877dfb1e2611 100644 GIT binary patch delta 1461 zcmZ8hXH*kb7|lx{LIQ-a1!RsrlZKvQpr{KyyM3@`` zB0DefJeIP}x@q)el(g8M*1BA+WS~|@o53nb@VxyhljGWKs`XC#y!6J&)32jw$w&y0 zTLKV>oQ70SbT15QFZCl5zQs~I|56XWj-U{FSBjV32bz8r)M_8mPT^^#58J!+8q{D2 zIe`!HF&=2`p}TE)O{8fGmI#w=BAcTCk-g^n`0)W!H3ZT_gF&`J^dVvKu@(v8v4<_< z*!*>x+@LgoLMTFDvR$Or5R7vw111d@L=JFzT)yXA4+&l1pnNS?@VdXetF|=Pz_h!6 z=O0u;J!r#-MV+GIxOQoOXYpBzz9D&ewoDswiPPJ-DXGgrw}a*7g}z=m)ZI1Pl0Zj} zo(Y`I7L6=eMjQRQ;XYMRYWK9Ue(pKlJ0o*`V@1cAB9E~_wGf;5(zPx2-*%ts#7KybeiT{4ZlR|ifHnANsI_x3G+&M!Su1RNZO@Ca_bF)?k0cW%@zvR zpBSGidq?{bUw?sD(4KMgg740n@z#x-y^Px*r|EB~iwS$Hv}lx!JnyE5I=D$epXqP| zr|U%a(OVh{(fu;?OqZH;=x3M!+|RG- zDr#!vCKd;e!)tlxp!krB-@M5Qh#7HpRL#{HJ<;%lof5}iUMha^ii%6mcJ8y9iMWbo zmxSsaOg%hgxX(~K%7dSGTf5-$rl+X+F-f8;5PkS_(#eWd!`@y#L1j_HEcfra!SN82 z@3T*cja9^a>#% zeMYp${pYHHDS6=ne*8Y|Hn}c(?v2Y_j;7O&Lm}vB<#Zt~BPQ~}W#=ssCeOuXeXS*- zN>=`G9k}lYzkvDydAb=7HLdI!K7)9;NL4TvRJvgps7E+wr|n2scm0B6e&D@(-U=jc zyg-ur&gR_mWCaKs&%ZVodV1n0MHmo73AQevk1k{kt1aCvf!4GQL^1rUGMmtpZL5U| z+a%fY9(lj`2MR*iY^Hlhibl<7--8u7^F6v%gwgWOt(0*3f|zE`DX!EZP9!mmLYtJ- zS9I1cTQrNw-lkJ>B_@(GDmQ&EM=`B$8nb9&j{=r@gUJJ#$d=N5DD1W){HvY*4@V<# zj?L8%$Ykn!P6;!#cji({W}9v_k(t&!EKnLGO>}e|+0fmye%jomiDbePoisEIgZ{=>F8o-evZJ@ehVXT*j>Rj)oD#FDl0$|0uX@Z2SX9-s{PvcYADHgNqU7UA>VlN#M$WD3-`J z=s$oQZY}dpDH<~S9rzFd)ByG=zW|Thm;_v5QX6o6=|Jn2YBDI0{~TW2s*}K5mKZWp zU**3k!!=6-Zl6dku2c!?5Me0QO&)*p~q`Vc*d{a+wUE_Rm*J7{CbL JkR;7n$lo!ljo<(P delta 1411 zcmY+EdpOg37{K@4Y;IxXc92lHMKet2Ii6!JkV`cQ5K*iSKgeu4y-@E@aFD^sA-ntVBfO;;AE~D+ z++;bu$_#90))lpNYlp1;BLQ36_IJK!k_{IItDzuZMlcOn=<#FQ$cK&vZ;N8z5?6x4 zlYpF*Bn+{3%WBm5npu_td|jcy0>!A+DiBTRsk^>04CpjyHoZjdoA!H|CxPZxY}(_T zALnu9Tn_19tVIOrFe!FtXk-OBh(2YNEysw1Oiz8FqkX0gWy;rpCyK{O^F)tHbU9a>wD|7@AUly4|_v57ufq)zNMF9}YD`-xwi zDfgFu=}TFEO)_N4QAa6eGoC>xc)i!5BlEIMT1E#I3r_n~WZS589>ikx`MPZm+#~kl zVQeNPz%qO)>`|Q|vRFk(!F1u;hp6o#T|)d)utO;~O=y$VpiFyG)*xCJggr|B)$eS` zBy#UWA$}J+3iT?oa)5optz}+x{`EM?EIG2xWGcMGl%3?N9+(h};Vkdps1o7)XJT7# z&PDyf?x~TtzvF@6%hH6UNnSPkxAHNUTv8~;-!Yr%JiOxpJAGIiMQp~6kfiFWhXEXc zX1=Sd8{vDx(agp)Gq7kQxk{!#{xYk!A38+^R*W@5zsAo17VL99K4OBtHM^C|X+@R> z%$-z@NcFDY{kUbnhMTE*)aNre&s6kE;{8}VeqZIYmW#Ao_pPqGt8Vo+?%$?3M65-K zT~m6+1vN#EWEsLQ`A(yxLmHfXH32&Voa}<#)#{21+hTd+4nu6VwZ-<-BtDq%=pt*>!v>qqF~qC=E>F7MBih}T78`F1E4uS`L616!yiYfb(5uPjuz3gJ?q0eJX|a06 zajhpLXoG@|UU&a7r7C*vDeUZquyVR=^^>tlo?&Qk?X(ZwG?s9iF{i5#Sz(M_dLJu} zBke54V9rtPUBjEIGlzF%1u4rJlHJaxB$C}rW;|;cPV%`=inothogY|0w}v#8Zf#dn z^@{Q4$#{tLI>nuMwyH~N^M%VeyS5fvudK>WC4;9md>3E*fnHgS4NV3gN}qoWwkbtxqfQIlG`Icl~7?Fay8NddiV+Ovp;gSIX3_vN#fR50nd{2@oKpwi040Qen iYhVgcLPSsjTiZ+vf>MA2VwGBmn*y{@WpS(8g#8COFmW9K diff --git a/src/plugin.ts b/src/plugin.ts index 0b40fcb..b611d66 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -1,4 +1,4 @@ -const CURRENT_VERSION = "1.0.25"; +const CURRENT_VERSION = "1.0.26"; const GITEA_BASE = "https://gitea.pdmarf.co.uk/pdm/stream_deck_notion_timer/raw/branch/master"; const SIGNING_PUBLIC_KEY = `-----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAN7ko8TUpuPzPAJuKAZCRjV0c4ZSlou5d9pUAF6o12b4= @@ -195,7 +195,19 @@ class TimerToggle extends SingletonAction { } else { const prevEntryId = await getRunningEntryId(); - // Stop previous timer + // Optimistically update visuals immediately — no waiting for API + if (prevEntryId) { + for (const other of this.actions) { + if (other.id === ev.action.id) continue; + const otherSettings = this.settingsCache.get(other.id); + if (otherSettings?.activeEntryId === prevEntryId) { + await Promise.all([other.setState(0), other.setTitle(buttonTitle(otherSettings.projectName || ""))]); + } + } + } + await Promise.all([ev.action.setState(1), ev.action.setTitle(`⏱ ${title}`)]); + + // Now do the API calls if (prevEntryId) { await stopTimer(global.notionToken, prevEntryId); for (const other of this.actions) { @@ -205,8 +217,6 @@ class TimerToggle extends SingletonAction { const stopped = { ...otherSettings, activeEntryId: null }; await other.setSettings(stopped); this.settingsCache.set(other.id, stopped); - await other.setState(0); - await other.setTitle(buttonTitle(otherSettings.projectName || "")); } } } @@ -222,8 +232,6 @@ class TimerToggle extends SingletonAction { await ev.action.setSettings(started); this.settingsCache.set(ev.action.id, started); await setRunningEntry(entryId); - await ev.action.setState(1); - await ev.action.setTitle(`⏱ ${title}`); } } catch (err) { streamDeck.logger.error("Timer toggle failed:", err);