v1.0.5: user dropdown from Notion API, settingsCache pruning

This commit is contained in:
pdmarf
2026-04-10 20:42:11 +01:00
parent 2029b5187e
commit d6282d7396
6 changed files with 82 additions and 34 deletions

View File

@@ -6327,19 +6327,6 @@ var BOOK_COLOR = {
pink: "\u{1F4D4}", pink: "\u{1F4D4}",
purple: "\u{1F4D3}" 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 = { var ICON_NAME = {
graduate: "\u{1F393}", graduate: "\u{1F393}",
science: "\u{1F52C}", science: "\u{1F52C}",
@@ -6369,11 +6356,19 @@ function notionIconToEmoji(page) {
if (icon.type === "icon") { if (icon.type === "icon") {
const { name, color } = icon.icon ?? {}; const { name, color } = icon.icon ?? {};
if (name === "book") return BOOK_COLOR[color] ?? "\u{1F4DA}"; 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 ICON_NAME[name] ?? "";
} }
return ""; 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) { async function fetchProjects(token, dbId) {
const resp = await fetch(`${NOTION_BASE}/databases/${dbId}/query`, { const resp = await fetch(`${NOTION_BASE}/databases/${dbId}/query`, {
method: "POST", method: "POST",
@@ -6433,7 +6428,7 @@ async function stopTimer(token, entryId) {
} }
// src/plugin.ts // 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 GITEA_BASE = "http://100.120.125.113:3000/pdm/stream_deck_notion_timer/raw/branch/master";
var SIGNING_PUBLIC_KEY = `-----BEGIN PUBLIC KEY----- var SIGNING_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAN7ko8TUpuPzPAJuKAZCRjV0c4ZSlou5d9pUAF6o12b4= MCowBQYDK2VwAyEAN7ko8TUpuPzPAJuKAZCRjV0c4ZSlou5d9pUAF6o12b4=
@@ -6490,6 +6485,9 @@ function buttonTitle(projectName) {
} }
var TimerToggle = class extends SingletonAction { var TimerToggle = class extends SingletonAction {
settingsCache = /* @__PURE__ */ new Map(); settingsCache = /* @__PURE__ */ new Map();
async onWillDisappear(ev) {
this.settingsCache.delete(ev.action.id);
}
async onWillAppear(ev) { async onWillAppear(ev) {
this.settingsCache.set(ev.action.id, ev.payload.settings); this.settingsCache.set(ev.action.id, ev.payload.settings);
const { activeEntryId, projectName } = 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 }); await plugin_default.ui.sendToPropertyInspector({ event: "projects", data: [], error: "Configure Notion credentials in plugin settings first.", version: CURRENT_VERSION });
return; return;
} }
const projects = await fetchProjects(global.notionToken, global.projectsDbId); const [projects, users] = await Promise.all([
await plugin_default.ui.sendToPropertyInspector({ event: "projects", data: projects, version: CURRENT_VERSION }); fetchProjects(global.notionToken, global.projectsDbId),
fetchUsers(global.notionToken)
]);
await plugin_default.ui.sendToPropertyInspector({ event: "projects", data: projects, users, version: CURRENT_VERSION });
} catch (err) { } catch (err) {
plugin_default.logger.error("Failed to fetch projects:", err); plugin_default.logger.error("Failed to fetch projects:", err);
await plugin_default.ui.sendToPropertyInspector({ event: "projects", data: [], error: String(err), version: CURRENT_VERSION }); await plugin_default.ui.sendToPropertyInspector({ event: "projects", data: [], error: String(err), version: CURRENT_VERSION });

View File

@@ -1 +1 @@
sRE¸ ć6TÝßţă<ćÝE]&Z¸pŚĐÝĽŤÓĐ^ßlt!°BDuÖs屟ę1)Žë€6Ç_/ř`9 ╦Ъ? НО╡Л╝KV

View File

@@ -73,8 +73,8 @@
#statusText.running { color: #4caf50; } #statusText.running { color: #4caf50; }
#statusText.error { color: #e57373; } #statusText.error { color: #e57373; }
#versionText { #versionText {
font-size: 10px; font-size: 12px;
color: #444; color: #fff;
text-align: center; text-align: center;
padding-top: 8px; padding-top: 8px;
} }
@@ -102,10 +102,12 @@
<input type="password" id="notionToken" placeholder="ntn_…"> <input type="password" id="notionToken" placeholder="ntn_…">
</div> </div>
<div class="row"> <div class="row">
<label>User ID</label> <label>Your Name</label>
<input type="text" id="userId" placeholder="Your Notion user UUID"> <select id="userId">
<option value="">— Select your name —</option>
</select>
</div> </div>
<p class="hint">Shared across all buttons. Enter once per device.</p> <p class="hint">Shared across all buttons. Select once per device.</p>
<p id="credStatus"></p> <p id="credStatus"></p>
<hr class="divider"> <hr class="divider">
</div> </div>
@@ -166,12 +168,25 @@
function saveCredentials() { function saveCredentials() {
var creds = { var creds = {
notionToken: document.getElementById("notionToken").value.trim(), notionToken: document.getElementById("notionToken").value.trim(),
userId: document.getElementById("userId").value.trim(), userId: document.getElementById("userId").value,
}; };
$PI.setGlobalSettings(creds); $PI.setGlobalSettings(creds);
setCredStatus("Credentials saved.", "ok"); setCredStatus("Credentials saved.", "ok");
} }
function populateUsers(users, savedUserId) {
var sel = document.getElementById("userId");
var current = savedUserId || sel.value;
sel.innerHTML = '<option value="">— Select your name —</option>';
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() { function scheduleCredSave() {
clearTimeout(credSaveTimer); clearTimeout(credSaveTimer);
credSaveTimer = setTimeout(saveCredentials, 600); credSaveTimer = setTimeout(saveCredentials, 600);
@@ -212,15 +227,19 @@
$PI.getGlobalSettings(); $PI.getGlobalSettings();
document.getElementById("projectSelect").addEventListener("change", save); document.getElementById("projectSelect").addEventListener("change", save);
["notionToken", "userId"].forEach(function(id) { document.getElementById("notionToken").addEventListener("input", scheduleCredSave);
document.getElementById(id).addEventListener("input", scheduleCredSave); document.getElementById("userId").addEventListener("change", saveCredentials);
});
}); });
$PI.onDidReceiveGlobalSettings(function(jsn) { $PI.onDidReceiveGlobalSettings(function(jsn) {
var s = jsn.payload.settings || {}; var s = jsn.payload.settings || {};
document.getElementById("notionToken").value = s.notionToken || ""; 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); credConfigured = !!(s.notionToken && s.userId);
@@ -253,6 +272,10 @@
if (payload.version) { if (payload.version) {
document.getElementById("versionText").textContent = "v" + 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) { if (payload.error) {
setStatus(payload.error, "error"); setStatus(payload.error, "error");
} else { } else {

View File

@@ -39,12 +39,29 @@ function notionIconToEmoji(page: any): string {
if (icon.type === "icon") { if (icon.type === "icon") {
const { name, color } = icon.icon ?? {}; const { name, color } = icon.icon ?? {};
if (name === "book") return BOOK_COLOR[color] ?? "📚"; 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 ICON_NAME[name] ?? "";
} }
return ""; return "";
} }
export interface NotionUser {
id: string;
name: string;
}
export async function fetchUsers(token: string): Promise<NotionUser[]> {
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<NotionProject[]> { export async function fetchProjects(token: string, dbId: string): Promise<NotionProject[]> {
const resp = await fetch(`${NOTION_BASE}/databases/${dbId}/query`, { const resp = await fetch(`${NOTION_BASE}/databases/${dbId}/query`, {
method: "POST", method: "POST",

View File

@@ -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 GITEA_BASE = "http://100.120.125.113:3000/pdm/stream_deck_notion_timer/raw/branch/master";
const SIGNING_PUBLIC_KEY = `-----BEGIN PUBLIC KEY----- const SIGNING_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAN7ko8TUpuPzPAJuKAZCRjV0c4ZSlou5d9pUAF6o12b4= MCowBQYDK2VwAyEAN7ko8TUpuPzPAJuKAZCRjV0c4ZSlou5d9pUAF6o12b4=
@@ -53,7 +53,7 @@ import streamDeck, {
SingletonAction, SingletonAction,
WillAppearEvent, WillAppearEvent,
} from "@elgato/streamdeck"; } from "@elgato/streamdeck";
import { fetchProjects, startTimer, stopTimer } from "./notion.js"; import { fetchProjects, fetchUsers, startTimer, stopTimer } from "./notion.js";
interface GlobalSettings { interface GlobalSettings {
notionToken: string; notionToken: string;
@@ -91,6 +91,10 @@ function buttonTitle(projectName: string): string {
class TimerToggle extends SingletonAction<TimerSettings> { class TimerToggle extends SingletonAction<TimerSettings> {
private settingsCache = new Map<string, TimerSettings>(); private settingsCache = new Map<string, TimerSettings>();
async onWillDisappear(ev: WillAppearEvent<TimerSettings>): Promise<void> {
this.settingsCache.delete(ev.action.id);
}
async onWillAppear(ev: WillAppearEvent<TimerSettings>): Promise<void> { async onWillAppear(ev: WillAppearEvent<TimerSettings>): Promise<void> {
this.settingsCache.set(ev.action.id, ev.payload.settings); this.settingsCache.set(ev.action.id, ev.payload.settings);
const { activeEntryId, projectName } = ev.payload.settings; const { activeEntryId, projectName } = ev.payload.settings;
@@ -109,8 +113,11 @@ class TimerToggle extends SingletonAction<TimerSettings> {
await streamDeck.ui.sendToPropertyInspector({ event: "projects", data: [], error: "Configure Notion credentials in plugin settings first.", version: CURRENT_VERSION }); await streamDeck.ui.sendToPropertyInspector({ event: "projects", data: [], error: "Configure Notion credentials in plugin settings first.", version: CURRENT_VERSION });
return; return;
} }
const projects = await fetchProjects(global.notionToken, global.projectsDbId); const [projects, users] = await Promise.all([
await streamDeck.ui.sendToPropertyInspector({ event: "projects", data: projects, version: CURRENT_VERSION }); fetchProjects(global.notionToken, global.projectsDbId),
fetchUsers(global.notionToken),
]);
await streamDeck.ui.sendToPropertyInspector({ event: "projects", data: projects, users, version: CURRENT_VERSION });
} catch (err) { } catch (err) {
streamDeck.logger.error("Failed to fetch projects:", err); streamDeck.logger.error("Failed to fetch projects:", err);
await streamDeck.ui.sendToPropertyInspector({ event: "projects", data: [], error: String(err), version: CURRENT_VERSION }); await streamDeck.ui.sendToPropertyInspector({ event: "projects", data: [], error: String(err), version: CURRENT_VERSION });

View File

@@ -1 +1 @@
{ "version": "1.0.4" } { "version": "1.0.5" }