mirror of
https://github.com/notion-enhancer/notion-enhancer.git
synced 2025-04-12 00:09:03 +00:00
feat(menu): hotswapped profiles w/out reload
This commit is contained in:
parent
106d776d85
commit
23834475c0
@ -39,7 +39,7 @@ const getProfile = async () => {
|
|||||||
const { initDatabase } = globalThis.__enhancerApi,
|
const { initDatabase } = globalThis.__enhancerApi,
|
||||||
db = initDatabase();
|
db = initDatabase();
|
||||||
let activeProfile = await db.get("activeProfile");
|
let activeProfile = await db.get("activeProfile");
|
||||||
activeProfile ??= await db.get("profileIds")?.[0];
|
activeProfile ??= (await db.get("profileIds"))?.[0];
|
||||||
return activeProfile ?? "default";
|
return activeProfile ?? "default";
|
||||||
},
|
},
|
||||||
isEnabled = async (id) => {
|
isEnabled = async (id) => {
|
||||||
|
@ -21,7 +21,7 @@ function _Button(
|
|||||||
class="flex gap-[8px] items-center px-[12px] shrink-0
|
class="flex gap-[8px] items-center px-[12px] shrink-0
|
||||||
rounded-[4px] ${size === "sm" ? "h-[28px]" : "h-[32px]"}
|
rounded-[4px] ${size === "sm" ? "h-[28px]" : "h-[32px]"}
|
||||||
transition duration-[20ms] ${primary
|
transition duration-[20ms] ${primary
|
||||||
? `text-[color:var(--theme--accent-primary_contrast)]
|
? `text-[color:var(--theme--accent-primary\\_contrast)]
|
||||||
font-medium bg-[color:var(--theme--accent-primary)]
|
font-medium bg-[color:var(--theme--accent-primary)]
|
||||||
hover:bg-[color:var(--theme--accent-primary\\_hover)]`
|
hover:bg-[color:var(--theme--accent-primary\\_hover)]`
|
||||||
: `border-(& [color:var(--theme--fg-border)])
|
: `border-(& [color:var(--theme--fg-border)])
|
||||||
@ -776,8 +776,8 @@ function Profile({
|
|||||||
setName,
|
setName,
|
||||||
isActive,
|
isActive,
|
||||||
setActive,
|
setActive,
|
||||||
exportData,
|
exportJson,
|
||||||
importData,
|
importJson,
|
||||||
...props
|
...props
|
||||||
}) {
|
}) {
|
||||||
const { html } = globalThis.__enhancerApi,
|
const { html } = globalThis.__enhancerApi,
|
||||||
@ -785,12 +785,8 @@ function Profile({
|
|||||||
const file = event.target.files[0],
|
const file = event.target.files[0],
|
||||||
reader = new FileReader();
|
reader = new FileReader();
|
||||||
reader.onload = async (progress) => {
|
reader.onload = async (progress) => {
|
||||||
try {
|
const res = progress.currentTarget.result;
|
||||||
const res = JSON.parse(progress.currentTarget.result);
|
importJson(res);
|
||||||
importData(res);
|
|
||||||
} catch {
|
|
||||||
// throw error
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
},
|
},
|
||||||
@ -807,8 +803,8 @@ function Profile({
|
|||||||
const $a = html`<a
|
const $a = html`<a
|
||||||
class="hidden"
|
class="hidden"
|
||||||
download="notion-enhancer_${await getName()}_${date}.json"
|
download="notion-enhancer_${await getName()}_${date}.json"
|
||||||
href="data:text/plain;charset=utf-8,${encodeURIComponent(
|
href="data:text/json;charset=utf-8,${encodeURIComponent(
|
||||||
JSON.stringify(await exportData())
|
await exportJson()
|
||||||
)}"
|
)}"
|
||||||
/>`;
|
/>`;
|
||||||
document.body.append($a);
|
document.body.append($a);
|
||||||
@ -818,18 +814,16 @@ function Profile({
|
|||||||
|
|
||||||
return html`<li class="flex items-center my-[14px] gap-[8px]" ...${props}>
|
return html`<li class="flex items-center my-[14px] gap-[8px]" ...${props}>
|
||||||
<${Checkbox}
|
<${Checkbox}
|
||||||
checked=${isActive}
|
...${{ _get: isActive, _set: setActive }}
|
||||||
disabled=${isActive}
|
onchange=${(event) => (event.target.checked = true)}
|
||||||
...${{ _set: setActive }}
|
|
||||||
/>
|
/>
|
||||||
<${Input}
|
<${Input}
|
||||||
size="md"
|
size="md"
|
||||||
type="text"
|
type="text"
|
||||||
icon="file-cog"
|
icon="file-cog"
|
||||||
onchange=${(event) => setName(event.target.value)}
|
onchange=${(event) => setName(event.target.value)}
|
||||||
onrerender=${($input) => {
|
onrerender=${($input) =>
|
||||||
getName().then((value) => ($input.value = value));
|
getName().then((value) => ($input.value = value))}
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<${Label} size="sm" icon="import">
|
<${Label} size="sm" icon="import">
|
||||||
<input
|
<input
|
||||||
|
@ -4,7 +4,13 @@
|
|||||||
* (https://notion-enhancer.github.io/) under the MIT license
|
* (https://notion-enhancer.github.io/) under the MIT license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { getState, setState, useState } from "./state.mjs";
|
import {
|
||||||
|
getState,
|
||||||
|
setState,
|
||||||
|
useState,
|
||||||
|
setEnabled,
|
||||||
|
modDatabase,
|
||||||
|
} from "./state.mjs";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Description,
|
Description,
|
||||||
@ -66,12 +72,11 @@ const renderSidebar = (items, categories) => {
|
|||||||
return $sidebar;
|
return $sidebar;
|
||||||
},
|
},
|
||||||
renderList = async (id, mods, description) => {
|
renderList = async (id, mods, description) => {
|
||||||
const { html, getProfile, initDatabase } = globalThis.__enhancerApi,
|
const { html, isEnabled } = globalThis.__enhancerApi;
|
||||||
enabledMods = initDatabase([await getProfile(), "enabledMods"]);
|
|
||||||
mods = mods.map(async (mod) => {
|
mods = mods.map(async (mod) => {
|
||||||
const _get = () => enabledMods.get(mod.id),
|
const _get = () => isEnabled(mod.id),
|
||||||
_set = async (enabled) => {
|
_set = async (enabled) => {
|
||||||
await enabledMods.set(mod.id, enabled);
|
await setEnabled(mod.id, enabled);
|
||||||
setState({ rerender: true, databaseUpdated: true });
|
setState({ rerender: true, databaseUpdated: true });
|
||||||
};
|
};
|
||||||
return html`<${Mod} ...${{ ...mod, _get, _set }} />`;
|
return html`<${Mod} ...${{ ...mod, _get, _set }} />`;
|
||||||
@ -81,11 +86,8 @@ const renderSidebar = (items, categories) => {
|
|||||||
<//>`;
|
<//>`;
|
||||||
},
|
},
|
||||||
renderOptions = async (mod) => {
|
renderOptions = async (mod) => {
|
||||||
const { html, platform, getProfile } = globalThis.__enhancerApi,
|
const { html, platform, getProfile } = globalThis.__enhancerApi;
|
||||||
{ optionDefaults, initDatabase } = globalThis.__enhancerApi,
|
let options = mod.options.reduce((options, opt) => {
|
||||||
profile = await getProfile(),
|
|
||||||
db = initDatabase([profile, mod.id], await optionDefaults(mod.id));
|
|
||||||
let options = mod.options.reduce((options, opt, i) => {
|
|
||||||
if (!opt.key && (opt.type !== "heading" || !opt.label)) return options;
|
if (!opt.key && (opt.type !== "heading" || !opt.label)) return options;
|
||||||
if (opt.platforms && !opt.platforms.includes(platform)) return options;
|
if (opt.platforms && !opt.platforms.includes(platform)) return options;
|
||||||
const prevOpt = options[options.length - 1];
|
const prevOpt = options[options.length - 1];
|
||||||
@ -99,9 +101,9 @@ const renderSidebar = (items, categories) => {
|
|||||||
if (options[options.length - 1]?.type === "heading") options.pop();
|
if (options[options.length - 1]?.type === "heading") options.pop();
|
||||||
options = options.map(async (opt) => {
|
options = options.map(async (opt) => {
|
||||||
if (opt.type === "heading") return html`<${Option} ...${opt} />`;
|
if (opt.type === "heading") return html`<${Option} ...${opt} />`;
|
||||||
const _get = () => db.get(opt.key),
|
const _get = async () => (await modDatabase(mod.id)).get(opt.key),
|
||||||
_set = async (value) => {
|
_set = async (value) => {
|
||||||
await db.set(opt.key, value);
|
await (await modDatabase(mod.id)).set(opt.key, value);
|
||||||
setState({ rerender: true, databaseUpdated: true });
|
setState({ rerender: true, databaseUpdated: true });
|
||||||
};
|
};
|
||||||
return html`<${Option} ...${{ ...opt, _get, _set }} />`;
|
return html`<${Option} ...${{ ...opt, _get, _set }} />`;
|
||||||
@ -109,37 +111,49 @@ const renderSidebar = (items, categories) => {
|
|||||||
return Promise.all(options);
|
return Promise.all(options);
|
||||||
},
|
},
|
||||||
renderProfiles = async () => {
|
renderProfiles = async () => {
|
||||||
const { html, getProfile, initDatabase, reloadApp } =
|
|
||||||
globalThis.__enhancerApi,
|
|
||||||
db = initDatabase();
|
|
||||||
|
|
||||||
let profileIds;
|
let profileIds;
|
||||||
const $list = html`<ul></ul>`,
|
const { html, initDatabase, getProfile } = globalThis.__enhancerApi,
|
||||||
activeProfile = await getProfile(),
|
db = initDatabase(),
|
||||||
|
$list = html`<ul></ul>`,
|
||||||
renderProfile = (id) => {
|
renderProfile = (id) => {
|
||||||
const profile = initDatabase([id]);
|
const profile = initDatabase([id]);
|
||||||
return html`<${Profile}
|
return html`<${Profile}
|
||||||
|
id=${id}
|
||||||
getName=${async () =>
|
getName=${async () =>
|
||||||
(await profile.get("profileName")) ??
|
(await profile.get("profileName")) ??
|
||||||
(id === "default" ? "default" : "")}
|
(id === "default" ? "default" : "")}
|
||||||
setName=${(name) => profile.set("profileName", name)}
|
setName=${(name) => profile.set("profileName", name)}
|
||||||
isActive=${id === activeProfile}
|
isActive=${async () => id === (await getProfile())}
|
||||||
setActive=${async (active) => {
|
setActive=${async () => {
|
||||||
if (!active) return;
|
|
||||||
await db.set("activeProfile", id);
|
await db.set("activeProfile", id);
|
||||||
reloadApp();
|
|
||||||
}}
|
|
||||||
exportData=${profile.export}
|
|
||||||
importData=${async (data) => {
|
|
||||||
await profile.import(data);
|
|
||||||
setState({ rerender: true, databaseUpdated: true });
|
setState({ rerender: true, databaseUpdated: true });
|
||||||
}}
|
}}
|
||||||
|
exportJson=${async () => JSON.stringify(await profile.export())}
|
||||||
|
importJson=${async (json) => {
|
||||||
|
try {
|
||||||
|
await profile.import(JSON.parse(json));
|
||||||
|
setState({ rerender: true, databaseUpdated: true });
|
||||||
|
} catch {
|
||||||
|
// error
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>`;
|
/>`;
|
||||||
},
|
},
|
||||||
refreshProfiles = async () => {
|
refreshProfiles = async () => {
|
||||||
profileIds = (await db.get("profileIds")) ?? ["default"];
|
profileIds = await db.get("profileIds");
|
||||||
const profiles = await Promise.all(profileIds.map(renderProfile));
|
if (!profileIds?.length) profileIds = ["default"];
|
||||||
$list.replaceChildren(...profiles);
|
for (const $profile of $list.children) {
|
||||||
|
const exists = profileIds.includes($profile.id);
|
||||||
|
if (!exists) $profile.remove();
|
||||||
|
}
|
||||||
|
for (let i = 0; i < profileIds.length; i++) {
|
||||||
|
const id = profileIds[i];
|
||||||
|
if (document.getElementById(id)) continue;
|
||||||
|
const $profile = await renderProfile(id),
|
||||||
|
$next = document.getElementById(profileIds[i + 1]);
|
||||||
|
if ($next) $list.insertBefore($profile, $next);
|
||||||
|
else $list.append($profile);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
addProfile = async (name) => {
|
addProfile = async (name) => {
|
||||||
const id = crypto.randomUUID();
|
const id = crypto.randomUUID();
|
||||||
@ -148,9 +162,7 @@ const renderSidebar = (items, categories) => {
|
|||||||
await profile.set("profileName", name);
|
await profile.set("profileName", name);
|
||||||
refreshProfiles();
|
refreshProfiles();
|
||||||
};
|
};
|
||||||
useState(["rerender"], () => {
|
useState(["rerender"], () => refreshProfiles());
|
||||||
refreshProfiles();
|
|
||||||
});
|
|
||||||
|
|
||||||
// todo: deleting profiles inc. clearing db keys,
|
// todo: deleting profiles inc. clearing db keys,
|
||||||
// throwing errors on invalid json upload
|
// throwing errors on invalid json upload
|
||||||
@ -186,16 +198,15 @@ const renderSidebar = (items, categories) => {
|
|||||||
</div>`;
|
</div>`;
|
||||||
},
|
},
|
||||||
renderMods = async (mods) => {
|
renderMods = async (mods) => {
|
||||||
const { html, getProfile, initDatabase } = globalThis.__enhancerApi,
|
const { html, isEnabled } = globalThis.__enhancerApi;
|
||||||
enabledMods = initDatabase([await getProfile(), "enabledMods"]);
|
|
||||||
mods = mods
|
mods = mods
|
||||||
.filter((mod) => {
|
.filter((mod) => {
|
||||||
return mod.options?.filter((opt) => opt.type !== "heading").length;
|
return mod.options?.filter((opt) => opt.type !== "heading").length;
|
||||||
})
|
})
|
||||||
.map(async (mod) => {
|
.map(async (mod) => {
|
||||||
const _get = () => enabledMods.get(mod.id),
|
const _get = () => isEnabled(mod.id),
|
||||||
_set = async (enabled) => {
|
_set = async (enabled) => {
|
||||||
await enabledMods.set(mod.id, enabled);
|
await setEnabled(mod.id, enabled);
|
||||||
setState({ rerender: true, databaseUpdated: true });
|
setState({ rerender: true, databaseUpdated: true });
|
||||||
};
|
};
|
||||||
return html`<${View} id=${mod.id}>
|
return html`<${View} id=${mod.id}>
|
||||||
@ -218,9 +229,10 @@ const render = async () => {
|
|||||||
icon: "palette",
|
icon: "palette",
|
||||||
id: "themes",
|
id: "themes",
|
||||||
title: "Themes",
|
title: "Themes",
|
||||||
description: `Themes override Notion's colour schemes. To switch between
|
description: `Themes override Notion's colour schemes. Dark themes require
|
||||||
dark mode and light mode, go to <mark>Settings & members → My notifications
|
Notion to be in dark mode and light themes require Notion to be in light
|
||||||
& settings → My settings → Appearance</mark>.`,
|
mode. To switch between dark mode and light mode, go to <mark>Settings &
|
||||||
|
members → My notifications & settings → My settings → Appearance</mark>.`,
|
||||||
mods: compatibleMods(await getThemes()),
|
mods: compatibleMods(await getThemes()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -21,4 +21,18 @@ const _state = {},
|
|||||||
callback(getState(keys));
|
callback(getState(keys));
|
||||||
};
|
};
|
||||||
|
|
||||||
export { setState, useState, getState };
|
const setEnabled = async (id, enabled) => {
|
||||||
|
const { getProfile, initDatabase } = globalThis.__enhancerApi;
|
||||||
|
// prettier-ignore
|
||||||
|
return await initDatabase([
|
||||||
|
await getProfile(),
|
||||||
|
"enabledMods"
|
||||||
|
]).set(id, enabled);
|
||||||
|
},
|
||||||
|
modDatabase = async (id) => {
|
||||||
|
const { getProfile, initDatabase } = globalThis.__enhancerApi,
|
||||||
|
{ optionDefaults } = globalThis.__enhancerApi;
|
||||||
|
return initDatabase([await getProfile(), id], await optionDefaults(id));
|
||||||
|
};
|
||||||
|
|
||||||
|
export { setState, useState, getState, setEnabled, modDatabase };
|
||||||
|
Loading…
Reference in New Issue
Block a user