From 567e678a6f327c053700b85f7dd8bac91db165b5 Mon Sep 17 00:00:00 2001 From: dragonwocky Date: Mon, 30 Jan 2023 23:11:55 +1100 Subject: [PATCH] chore(menu): add telemetry toggle --- src/api/electron.cjs | 6 ++- src/core/menu/components/Description.mjs | 4 +- src/core/menu/components/Heading.mjs | 6 +-- src/core/menu/islands/Banner.mjs | 7 ++-- src/core/menu/islands/GetStarted.mjs | 40 +++++++++++++++++++ src/core/menu/islands/Options.mjs | 6 ++- src/core/menu/islands/Telemetry.mjs | 50 ++++++++++++++++++++++++ src/core/menu/menu.css | 14 +++++-- src/core/menu/menu.mjs | 6 ++- 9 files changed, 121 insertions(+), 18 deletions(-) create mode 100644 src/core/menu/islands/GetStarted.mjs create mode 100644 src/core/menu/islands/Telemetry.mjs diff --git a/src/api/electron.cjs b/src/api/electron.cjs index be38e83..c4bd916 100644 --- a/src/api/electron.cjs +++ b/src/api/electron.cjs @@ -71,12 +71,14 @@ const initDatabase = (namespace, fallbacks = {}) => { )`); init.run(); - // schema: - // - ("profileIds") = $profileId[] + // schema: + // - ("profileIds") -> $profileId[] // - ("activeProfile") -> $profileId // - $profileId: ("profileName") -> string + // - $profileId: ("telemetryEnabled") -> boolean // - $profileId__enabledMods: ($modId) -> boolean // - $profileId__$modId: ($optionKey) -> value + __statements = { insert: db.prepare(`INSERT INTO ${table} (key, value) VALUES (?, ?)`), update: db.prepare(`UPDATE ${table} SET value = ? WHERE key = ?`), diff --git a/src/core/menu/components/Description.mjs b/src/core/menu/components/Description.mjs index 8008e7d..34f4661 100644 --- a/src/core/menu/components/Description.mjs +++ b/src/core/menu/components/Description.mjs @@ -9,8 +9,8 @@ import { extendProps } from "../state.mjs"; function Description(props, ...children) { const { html } = globalThis.__enhancerApi; extendProps(props, { - class: `notion-enhancer--menu-description leading-[16px] - text-([12px] [color:var(--theme--fg-secondary)])`, + class: `notion-enhancer--menu-description typography + leading-[16px] text-([12px] [color:var(--theme--fg-secondary)])`, }); return html`

${children}

`; } diff --git a/src/core/menu/components/Heading.mjs b/src/core/menu/components/Heading.mjs index ea67197..1334e92 100644 --- a/src/core/menu/components/Heading.mjs +++ b/src/core/menu/components/Heading.mjs @@ -6,11 +6,11 @@ import { extendProps } from "../state.mjs"; -function Heading(props, children) { +function Heading(props, ...children) { const { html } = globalThis.__enhancerApi; extendProps(props, { - class: `notion-enhancer--menu-heading font-semibold - mb-[16px] mt-[48px] first:mt-0 pb-[12px] text-[16px] + class: `notion-enhancer--menu-heading flex items-center gap-[4px] + text-[16px] font-semibold mb-[16px] mt-[48px] first:mt-0 pb-[12px] border-b-(& [color:var(--theme--fg-border)])`, }); return html`

${children}

`; diff --git a/src/core/menu/islands/Banner.mjs b/src/core/menu/islands/Banner.mjs index 2e1d394..a025f1a 100644 --- a/src/core/menu/islands/Banner.mjs +++ b/src/core/menu/islands/Banner.mjs @@ -62,8 +62,9 @@ function Circle(rect) { >`; } -function Banner({ version }) { - const { html } = globalThis.__enhancerApi; +function Banner() { + const { html, enhancerVersion } = globalThis.__enhancerApi; + // todo: show popup if update available return html`
v${version} + >v${enhancerVersion}
diff --git a/src/core/menu/islands/GetStarted.mjs b/src/core/menu/islands/GetStarted.mjs new file mode 100644 index 0000000..2c6262e --- /dev/null +++ b/src/core/menu/islands/GetStarted.mjs @@ -0,0 +1,40 @@ +/** + * notion-enhancer + * (c) 2023 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +import { Heading } from "../components/Heading.mjs"; +import { Description } from "../components/Description.mjs"; +import { Checkbox } from "../components/Checkbox.mjs"; +import { Option } from "./Options.mjs"; + +const privacyPolicy = "https://notion-enhancer.github.io/about/privacy-policy/", + tsAndCs = "https://notion-enhancer.github.io/about/terms-and-conditions/"; + +function GetStarted() { + const { html } = globalThis.__enhancerApi; + + return html` + <${Heading}>Get Started + +
+ <${Checkbox} + ...${{ _get: () => Promise.resolve(true), _set: () => {} }} + onchange=${(event) => (event.target.checked = true)} + /> +

+ I have read and agreed to the + Privacy Policy + and Terms & Conditions. +

+
+ `; +} + +export { GetStarted }; + +// - deidentified / anonymous +// - once a week +// - privacy policy +// - learn how the notion-enhancer is used and what parts need focusing on diff --git a/src/core/menu/islands/Options.mjs b/src/core/menu/islands/Options.mjs index d71b36d..4cd82e2 100644 --- a/src/core/menu/islands/Options.mjs +++ b/src/core/menu/islands/Options.mjs @@ -47,7 +47,9 @@ function Option({ _get, _set, ...opt }) { ...${{ _get, _set }} />` : ""} - <${Description} innerHTML=${opt.description} /> + ${["string", "undefined"].includes(typeof opt.description) + ? html`<${Description} innerHTML=${opt.description} />` + : html`<${Description}>${opt.description}`} ${["number", "hotkey", "color"].includes(opt.type) ? html`<${Input} @@ -83,4 +85,4 @@ function Options({ mod }) { }); } -export { Options }; +export { Options, Option }; diff --git a/src/core/menu/islands/Telemetry.mjs b/src/core/menu/islands/Telemetry.mjs new file mode 100644 index 0000000..54a8b7c --- /dev/null +++ b/src/core/menu/islands/Telemetry.mjs @@ -0,0 +1,50 @@ +/** + * notion-enhancer + * (c) 2023 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +import { useState, setState } from "../state.mjs"; +import { Option } from "./Options.mjs"; + +const privacyPolicy = "https://notion-enhancer.github.io/about/privacy-policy/"; +function Telemetry() { + const { html, platform, getMods } = globalThis.__enhancerApi, + { getProfile, isEnabled, initDatabase } = globalThis.__enhancerApi, + timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; + + const $enabledMods = html``; + useState(["rerender"], async () => { + let enabledMods = []; + for (const mod of await getMods()) { + if (mod._src === "core") continue; + if (await isEnabled(mod.id)) enabledMods.push(mod.id); + } + $enabledMods.innerText = JSON.stringify(enabledMods); + }); + + const _get = async () => { + // defaults to true, must be explicitly set to false to disable + return initDatabase([await getProfile()]).get("telemetryEnabled") ?? true; + }, + _set = async (value) => { + await initDatabase([await getProfile()]).set("telemetryEnabled", value); + setState({ rerender: true, databaseUpdated: true }); + }; + + // todo: actually collect telemetry + return html`<${Option} + type="toggle" + label="Telemetry" + description=${html`If telemetry is enabled, basic usage data will be + collected at a regular interval from your device in order to better + understand how and where the notion-enhancer is used. This data is + anonymous and includes only your platform ("${platform}"), + timezone ("${timezone}") and enabled mods (${$enabledMods}). + You can opt in or out of telemetry at any time. For more information, + please read the privacy policy.`} + ...${{ _get, _set }} + />`; +} + +export { Telemetry }; diff --git a/src/core/menu/menu.css b/src/core/menu/menu.css index de87ad2..ed3a766 100644 --- a/src/core/menu/menu.css +++ b/src/core/menu/menu.css @@ -94,17 +94,23 @@ body > #skeleton .row-group .shimmer { height: 11px; } -.notion-enhancer--menu-description mark { - color: inherit; +.typography mark { padding: 0 4px; border-radius: 3px; background-color: var(--theme--bg-hover); + color: inherit; } -.notion-enhancer--menu-description a { +.typography code { + padding: 0 4px; + border-radius: 3px; + background-color: var(--theme--code-inline_bg); + color: var(--theme--code-inline_fg); +} +.typography a { text-decoration: underline; transition: 100ms ease-in; } -.notion-enhancer--menu-description a:hover { +.typography a:hover { color: var(--theme--accent-secondary); } diff --git a/src/core/menu/menu.mjs b/src/core/menu/menu.mjs index 948de34..3a105cd 100644 --- a/src/core/menu/menu.mjs +++ b/src/core/menu/menu.mjs @@ -8,6 +8,7 @@ import { setState, useState } from "./state.mjs"; import { Sidebar } from "./islands/Sidebar.mjs"; import { Footer } from "./islands/Footer.mjs"; import { Banner } from "./islands/Banner.mjs"; +import { Telemetry } from "./islands/Telemetry.mjs"; import { View } from "./islands/View.mjs"; import { List } from "./islands/List.mjs"; import { Mod } from "./islands/Mod.mjs"; @@ -77,7 +78,7 @@ const categories = [ ]; const render = async () => { - const { html, enhancerVersion } = globalThis.__enhancerApi, + const { html, platform } = globalThis.__enhancerApi, { getMods, isEnabled, setEnabled } = globalThis.__enhancerApi, [icon, renderStarted] = useState(["icon", "renderStarted"]); if (!html || !getMods || !icon || renderStarted) return; @@ -116,9 +117,10 @@ const render = async () => {
- <${View} id="welcome"><${Banner} version=${enhancerVersion} /> + <${View} id="welcome"><${Banner} /> <${View} id="core"> <${Options} mod=${mods.find(({ _src }) => _src === "core")} /> + <${Telemetry} /> <${Profiles} /> ${[...categories, ...mods]