From d6282d7396978804e2f4f3dd2c7d314035899555 Mon Sep 17 00:00:00 2001 From: pdmarf <135653545+pdmarf@users.noreply.github.com> Date: Fri, 10 Apr 2026 20:42:11 +0100 Subject: [PATCH] v1.0.5: user dropdown from Notion API, settingsCache pruning --- com.pdma.notion-timer.sdPlugin/bin/plugin.js | 35 +++++++-------- .../bin/plugin.js.sig | 2 +- .../ui/property-inspector.html | 43 ++++++++++++++----- src/notion.ts | 19 +++++++- src/plugin.ts | 15 +++++-- version.json | 2 +- 6 files changed, 82 insertions(+), 34 deletions(-) diff --git a/com.pdma.notion-timer.sdPlugin/bin/plugin.js b/com.pdma.notion-timer.sdPlugin/bin/plugin.js index a57725a..8e49760 100644 --- a/com.pdma.notion-timer.sdPlugin/bin/plugin.js +++ b/com.pdma.notion-timer.sdPlugin/bin/plugin.js @@ -6327,19 +6327,6 @@ var BOOK_COLOR = { pink: "\u{1F4D4}", purple: "\u{1F4D3}" }; -var COLOR_CIRCLE = { - green: "\u{1F7E2}", - blue: "\u{1F535}", - red: "\u{1F534}", - orange: "\u{1F7E0}", - yellow: "\u{1F7E1}", - purple: "\u{1F7E3}", - pink: "\u{1FA77}", - brown: "\u{1F7E4}", - gray: "\u26AB", - lightgray: "\u2B1C", - default: "\u2B1C" -}; var ICON_NAME = { graduate: "\u{1F393}", science: "\u{1F52C}", @@ -6369,11 +6356,19 @@ function notionIconToEmoji(page) { if (icon.type === "icon") { const { name, color } = icon.icon ?? {}; if (name === "book") return BOOK_COLOR[color] ?? "\u{1F4DA}"; - if (name === "skip-forward") return `\u23ED ${COLOR_CIRCLE[color] ?? ""}`.trimEnd(); + if (name === "skip-forward") return "\u23ED"; return ICON_NAME[name] ?? ""; } return ""; } +async function fetchUsers(token) { + const resp = await fetch(`${NOTION_BASE}/users`, { + headers: headers(token) + }); + if (!resp.ok) throw new Error(`Failed to fetch users: ${resp.status}`); + const data = await resp.json(); + return data.results.filter((u) => u.type === "person" && u.person?.email).map((u) => ({ id: u.id, name: u.name })).sort((a, b) => a.name.localeCompare(b.name)); +} async function fetchProjects(token, dbId) { const resp = await fetch(`${NOTION_BASE}/databases/${dbId}/query`, { method: "POST", @@ -6433,7 +6428,7 @@ async function stopTimer(token, entryId) { } // src/plugin.ts -var CURRENT_VERSION = "1.0.4"; +var CURRENT_VERSION = "1.0.5"; var GITEA_BASE = "http://100.120.125.113:3000/pdm/stream_deck_notion_timer/raw/branch/master"; var SIGNING_PUBLIC_KEY = `-----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAN7ko8TUpuPzPAJuKAZCRjV0c4ZSlou5d9pUAF6o12b4= @@ -6490,6 +6485,9 @@ function buttonTitle(projectName) { } var TimerToggle = class extends SingletonAction { settingsCache = /* @__PURE__ */ new Map(); + async onWillDisappear(ev) { + this.settingsCache.delete(ev.action.id); + } async onWillAppear(ev) { this.settingsCache.set(ev.action.id, ev.payload.settings); const { activeEntryId, projectName } = ev.payload.settings; @@ -6507,8 +6505,11 @@ var TimerToggle = class extends SingletonAction { await plugin_default.ui.sendToPropertyInspector({ event: "projects", data: [], error: "Configure Notion credentials in plugin settings first.", version: CURRENT_VERSION }); return; } - const projects = await fetchProjects(global.notionToken, global.projectsDbId); - await plugin_default.ui.sendToPropertyInspector({ event: "projects", data: projects, version: CURRENT_VERSION }); + const [projects, users] = await Promise.all([ + fetchProjects(global.notionToken, global.projectsDbId), + fetchUsers(global.notionToken) + ]); + await plugin_default.ui.sendToPropertyInspector({ event: "projects", data: projects, users, version: CURRENT_VERSION }); } catch (err) { plugin_default.logger.error("Failed to fetch projects:", err); await plugin_default.ui.sendToPropertyInspector({ event: "projects", data: [], error: String(err), version: CURRENT_VERSION }); diff --git a/com.pdma.notion-timer.sdPlugin/bin/plugin.js.sig b/com.pdma.notion-timer.sdPlugin/bin/plugin.js.sig index 37b8ae4..34fa9b5 100644 --- a/com.pdma.notion-timer.sdPlugin/bin/plugin.js.sig +++ b/com.pdma.notion-timer.sdPlugin/bin/plugin.js.sig @@ -1 +1 @@ -MsREI 6TsH3|Q/`Qkwn9 \ No newline at end of file diff --git a/com.pdma.notion-timer.sdPlugin/ui/property-inspector.html b/com.pdma.notion-timer.sdPlugin/ui/property-inspector.html index e54c729..355496b 100644 --- a/com.pdma.notion-timer.sdPlugin/ui/property-inspector.html +++ b/com.pdma.notion-timer.sdPlugin/ui/property-inspector.html @@ -73,8 +73,8 @@ #statusText.running { color: #4caf50; } #statusText.error { color: #e57373; } #versionText { - font-size: 10px; - color: #444; + font-size: 12px; + color: #fff; text-align: center; padding-top: 8px; } @@ -102,10 +102,12 @@
- - + +
-

Shared across all buttons. Enter once per device.

+

Shared across all buttons. Select once per device.


@@ -166,12 +168,25 @@ function saveCredentials() { var creds = { notionToken: document.getElementById("notionToken").value.trim(), - userId: document.getElementById("userId").value.trim(), + userId: document.getElementById("userId").value, }; $PI.setGlobalSettings(creds); setCredStatus("Credentials saved.", "ok"); } + function populateUsers(users, savedUserId) { + var sel = document.getElementById("userId"); + var current = savedUserId || sel.value; + sel.innerHTML = ''; + users.forEach(function(u) { + var opt = document.createElement("option"); + opt.value = u.id; + opt.textContent = u.name; + if (u.id === current) opt.selected = true; + sel.appendChild(opt); + }); + } + function scheduleCredSave() { clearTimeout(credSaveTimer); credSaveTimer = setTimeout(saveCredentials, 600); @@ -212,15 +227,19 @@ $PI.getGlobalSettings(); document.getElementById("projectSelect").addEventListener("change", save); - ["notionToken", "userId"].forEach(function(id) { - document.getElementById(id).addEventListener("input", scheduleCredSave); - }); + document.getElementById("notionToken").addEventListener("input", scheduleCredSave); + document.getElementById("userId").addEventListener("change", saveCredentials); }); $PI.onDidReceiveGlobalSettings(function(jsn) { var s = jsn.payload.settings || {}; document.getElementById("notionToken").value = s.notionToken || ""; - document.getElementById("userId").value = s.userId || ""; + if (s.userId) { + var sel = document.getElementById("userId"); + if (sel.querySelector('option[value="' + s.userId + '"]')) { + sel.value = s.userId; + } + } credConfigured = !!(s.notionToken && s.userId); @@ -253,6 +272,10 @@ if (payload.version) { document.getElementById("versionText").textContent = "v" + payload.version; } + if (payload.users) { + var savedUserId = document.getElementById("userId").value; + populateUsers(payload.users, savedUserId); + } if (payload.error) { setStatus(payload.error, "error"); } else { diff --git a/src/notion.ts b/src/notion.ts index 6714a56..bf56888 100644 --- a/src/notion.ts +++ b/src/notion.ts @@ -39,12 +39,29 @@ function notionIconToEmoji(page: any): string { if (icon.type === "icon") { const { name, color } = icon.icon ?? {}; if (name === "book") return BOOK_COLOR[color] ?? "📚"; - if (name === "skip-forward") return `⏭ ${COLOR_CIRCLE[color] ?? ""}`.trimEnd(); + if (name === "skip-forward") return "⏭"; return ICON_NAME[name] ?? ""; } return ""; } +export interface NotionUser { + id: string; + name: string; +} + +export async function fetchUsers(token: string): Promise { + const resp = await fetch(`${NOTION_BASE}/users`, { + headers: headers(token), + }); + if (!resp.ok) throw new Error(`Failed to fetch users: ${resp.status}`); + const data = (await resp.json()) as { results: any[] }; + return data.results + .filter((u: any) => u.type === "person" && u.person?.email) + .map((u: any) => ({ id: u.id as string, name: u.name as string })) + .sort((a, b) => a.name.localeCompare(b.name)); +} + export async function fetchProjects(token: string, dbId: string): Promise { const resp = await fetch(`${NOTION_BASE}/databases/${dbId}/query`, { method: "POST", diff --git a/src/plugin.ts b/src/plugin.ts index e5cfbc9..792affb 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -1,4 +1,4 @@ -const CURRENT_VERSION = "1.0.4"; +const CURRENT_VERSION = "1.0.5"; const GITEA_BASE = "http://100.120.125.113:3000/pdm/stream_deck_notion_timer/raw/branch/master"; const SIGNING_PUBLIC_KEY = `-----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAN7ko8TUpuPzPAJuKAZCRjV0c4ZSlou5d9pUAF6o12b4= @@ -53,7 +53,7 @@ import streamDeck, { SingletonAction, WillAppearEvent, } from "@elgato/streamdeck"; -import { fetchProjects, startTimer, stopTimer } from "./notion.js"; +import { fetchProjects, fetchUsers, startTimer, stopTimer } from "./notion.js"; interface GlobalSettings { notionToken: string; @@ -91,6 +91,10 @@ function buttonTitle(projectName: string): string { class TimerToggle extends SingletonAction { private settingsCache = new Map(); + async onWillDisappear(ev: WillAppearEvent): Promise { + this.settingsCache.delete(ev.action.id); + } + async onWillAppear(ev: WillAppearEvent): Promise { this.settingsCache.set(ev.action.id, ev.payload.settings); const { activeEntryId, projectName } = ev.payload.settings; @@ -109,8 +113,11 @@ class TimerToggle extends SingletonAction { await streamDeck.ui.sendToPropertyInspector({ event: "projects", data: [], error: "Configure Notion credentials in plugin settings first.", version: CURRENT_VERSION }); return; } - const projects = await fetchProjects(global.notionToken, global.projectsDbId); - await streamDeck.ui.sendToPropertyInspector({ event: "projects", data: projects, version: CURRENT_VERSION }); + const [projects, users] = await Promise.all([ + fetchProjects(global.notionToken, global.projectsDbId), + fetchUsers(global.notionToken), + ]); + await streamDeck.ui.sendToPropertyInspector({ event: "projects", data: projects, users, version: CURRENT_VERSION }); } catch (err) { streamDeck.logger.error("Failed to fetch projects:", err); await streamDeck.ui.sendToPropertyInspector({ event: "projects", data: [], error: String(err), version: CURRENT_VERSION }); diff --git a/version.json b/version.json index 5db88d9..4dcb4fb 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{ "version": "1.0.4" } +{ "version": "1.0.5" }