v1.0.5: user dropdown from Notion API, settingsCache pruning
This commit is contained in:
@@ -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 });
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
MžsRE¸IŹ ć6TÝßţă<ćÝE]&Z¸pŚĐÝĽŤÓĐ^ßlt!°BDuÖs屟ę1)Žë€6Ç_/ř`9
|
╦Ъ?НCэО╡Л╝KV
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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 });
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{ "version": "1.0.4" }
|
{ "version": "1.0.5" }
|
||||||
|
|||||||
Reference in New Issue
Block a user