scope all await statements

This commit is contained in:
dragonwocky 2021-11-07 18:53:18 +11:00
parent 496305597a
commit a72441ee86
4 changed files with 415 additions and 383 deletions

View File

@ -8,7 +8,6 @@
import { fmt, web, registry, components } from '../../api/_.mjs'; import { fmt, web, registry, components } from '../../api/_.mjs';
import { notifications } from './notifications.mjs'; import { notifications } from './notifications.mjs';
const profileDB = await registry.profileDB();
export const modComponents = { export const modComponents = {
preview: (url) => web.html`<img preview: (url) => web.html`<img
@ -56,7 +55,8 @@ export const modComponents = {
export const options = { export const options = {
toggle: async (mod, opt) => { toggle: async (mod, opt) => {
const checked = await profileDB.get([mod.id, opt.key], opt.value), const profileDB = await registry.profileDB(),
checked = await profileDB.get([mod.id, opt.key], opt.value),
$toggle = modComponents.toggle(opt.label, checked), $toggle = modComponents.toggle(opt.label, checked),
$tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`, $tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`,
$label = $toggle.children[0], $label = $toggle.children[0],
@ -71,8 +71,10 @@ export const options = {
}); });
return $toggle; return $toggle;
}, },
select: async (mod, opt) => { select: async (mod, opt) => {
const value = await profileDB.get([mod.id, opt.key], opt.values[0]), const profileDB = await registry.profileDB(),
value = await profileDB.get([mod.id, opt.key], opt.values[0]),
$tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`, $tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`,
$label = web.render( $label = web.render(
web.html`<label class="input-label"></label>`, web.html`<label class="input-label"></label>`,
@ -96,8 +98,10 @@ export const options = {
}); });
return web.render($label, $select, $icon); return web.render($label, $select, $icon);
}, },
text: async (mod, opt) => { text: async (mod, opt) => {
const value = await profileDB.get([mod.id, opt.key], opt.value), const profileDB = await registry.profileDB(),
value = await profileDB.get([mod.id, opt.key], opt.value),
$tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`, $tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`,
$label = web.render( $label = web.render(
web.html`<label class="input-label"></label>`, web.html`<label class="input-label"></label>`,
@ -112,8 +116,10 @@ export const options = {
}); });
return web.render($label, $input, $icon); return web.render($label, $input, $icon);
}, },
number: async (mod, opt) => { number: async (mod, opt) => {
const value = await profileDB.get([mod.id, opt.key], opt.value), const profileDB = await registry.profileDB(),
value = await profileDB.get([mod.id, opt.key], opt.value),
$tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`, $tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`,
$label = web.render( $label = web.render(
web.html`<label class="input-label"></label>`, web.html`<label class="input-label"></label>`,
@ -128,8 +134,10 @@ export const options = {
}); });
return web.render($label, $input, $icon); return web.render($label, $input, $icon);
}, },
color: async (mod, opt) => { color: async (mod, opt) => {
const value = await profileDB.get([mod.id, opt.key], opt.value), const profileDB = await registry.profileDB(),
value = await profileDB.get([mod.id, opt.key], opt.value),
$tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`, $tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`,
$label = web.render( $label = web.render(
web.html`<label class="input-label"></label>`, web.html`<label class="input-label"></label>`,
@ -166,8 +174,10 @@ export const options = {
paint(); paint();
return web.render($label, $input, $icon); return web.render($label, $input, $icon);
}, },
file: async (mod, opt) => { file: async (mod, opt) => {
const { filename } = (await profileDB.get([mod.id, opt.key], {})) || {}, const profileDB = await registry.profileDB(),
{ filename } = (await profileDB.get([mod.id, opt.key], {})) || {},
$tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`, $tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`,
$label = web.render( $label = web.render(
web.html`<label class="input-label"></label>`, web.html`<label class="input-label"></label>`,
@ -204,8 +214,10 @@ export const options = {
$latest $latest
); );
}, },
hotkey: async (mod, opt) => { hotkey: async (mod, opt) => {
const value = await profileDB.get([mod.id, opt.key], opt.value), const profileDB = await registry.profileDB(),
value = await profileDB.get([mod.id, opt.key], opt.value),
$tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`, $tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`,
$label = web.render( $label = web.render(
web.html`<label class="input-label"></label>`, web.html`<label class="input-label"></label>`,

View File

@ -8,9 +8,14 @@
import * as api from '../../api/_.mjs'; import * as api from '../../api/_.mjs';
import { notifications, $changelogModal } from './notifications.mjs'; import { notifications, $changelogModal } from './notifications.mjs';
const { env, fs, storage, registry, web, components } = api; import { modComponents, options } from './components.mjs';
import * as router from './router.mjs';
import './styles.mjs';
for (const mod of await registry.list((mod) => registry.enabled(mod.id))) { (async () => {
const { env, fs, storage, registry, web, components } = api;
for (const mod of await registry.list((mod) => registry.enabled(mod.id))) {
for (const sheet of mod.css?.menu || []) { for (const sheet of mod.css?.menu || []) {
web.loadStylesheet(`repo/${mod._dir}/${sheet}`); web.loadStylesheet(`repo/${mod._dir}/${sheet}`);
} }
@ -18,9 +23,9 @@ for (const mod of await registry.list((mod) => registry.enabled(mod.id))) {
script = await import(fs.localPath(`repo/${mod._dir}/${script}`)); script = await import(fs.localPath(`repo/${mod._dir}/${script}`));
script.default(api, await registry.db(mod.id)); script.default(api, await registry.db(mod.id));
} }
} }
const errors = await registry.errors(); const errors = await registry.errors();
if (errors.length) { if (errors.length) {
console.log('[notion-enhancer] registry errors:'); console.log('[notion-enhancer] registry errors:');
console.table(errors); console.table(errors);
notifications.add({ notifications.add({
@ -28,31 +33,27 @@ if (errors.length) {
message: 'Failed to load mods (check console).', message: 'Failed to load mods (check console).',
color: 'red', color: 'red',
}); });
} }
import { modComponents, options } from './components.mjs'; const db = await registry.db('a6621988-551d-495a-97d8-3c568bca2e9e'),
import * as router from './router.mjs';
import './styles.mjs';
const db = await registry.db('a6621988-551d-495a-97d8-3c568bca2e9e'),
profileName = await registry.profileName(), profileName = await registry.profileName(),
profileDB = await registry.profileDB(); profileDB = await registry.profileDB();
web.addHotkeyListener(await db.get(['hotkey']), env.focusNotion); web.addHotkeyListener(await db.get(['hotkey']), env.focusNotion);
const loadTheme = async () => { const loadTheme = async () => {
document.documentElement.className = document.documentElement.className =
(await storage.get(['theme'], 'light')) === 'dark' ? 'dark' : ''; (await storage.get(['theme'], 'light')) === 'dark' ? 'dark' : '';
}; };
document.addEventListener('visibilitychange', loadTheme); document.addEventListener('visibilitychange', loadTheme);
loadTheme(); loadTheme();
window.addEventListener('beforeunload', (event) => { window.addEventListener('beforeunload', (event) => {
// trigger input save // trigger input save
document.activeElement.blur(); document.activeElement.blur();
}); });
const $main = web.html`<main class="main"></main>`, const $main = web.html`<main class="main"></main>`,
$sidebar = web.html`<article class="sidebar"></article>`, $sidebar = web.html`<article class="sidebar"></article>`,
$options = web.html`<div class="options-container"> $options = web.html`<div class="options-container">
<p class="options-placeholder">Select a mod to view and configure its options.</p> <p class="options-placeholder">Select a mod to view and configure its options.</p>
@ -61,10 +62,10 @@ const $main = web.html`<main class="main"></main>`,
Profile: ${web.escape(profileName)} Profile: ${web.escape(profileName)}
</button>`; </button>`;
// profile // profile
let _$profileConfig; let _$profileConfig;
const openProfileMenu = async () => { const openProfileMenu = async () => {
if (!_$profileConfig) { if (!_$profileConfig) {
const profileNames = [ const profileNames = [
...new Set([ ...new Set([
@ -203,17 +204,23 @@ const openProfileMenu = async () => {
$edit, $edit,
web.html`${await components.feather('type', { class: 'input-icon' })}` web.html`${await components.feather('type', { class: 'input-icon' })}`
), ),
web.render(web.html`<p class="profile-actions"></p>`, $export, $import, $save, $delete), web.render(
web.html`<p class="profile-actions"></p>`,
$export,
$import,
$save,
$delete
),
$error $error
); );
} }
web.render(web.empty($options), _$profileConfig); web.render(web.empty($options), _$profileConfig);
}; };
$profile.addEventListener('click', () => openSidebarMenu('profile')); $profile.addEventListener('click', () => openSidebarMenu('profile'));
// mods // mods
const $modLists = {}, const $modLists = {},
generators = { generators = {
options: async (mod) => { options: async (mod) => {
const $fragment = document.createDocumentFragment(); const $fragment = document.createDocumentFragment();
@ -311,7 +318,7 @@ const $modLists = {},
}, },
}; };
async function openModMenu(id) { async function openModMenu(id) {
let $mod; let $mod;
for (const $list of Object.values($modLists)) { for (const $list of Object.values($modLists)) {
$mod = $list.querySelector(`[data-id="${web.escape(id)}"]`); $mod = $list.querySelector(`[data-id="${web.escape(id)}"]`);
@ -327,32 +334,32 @@ async function openModMenu(id) {
await generators.options(mod), await generators.options(mod),
]; ];
web.render(web.empty($options), ...fragment); web.render(web.empty($options), ...fragment);
} }
// views // views
const $notionNavItem = web.html`<h1 class="nav-notion"> const $notionNavItem = web.html`<h1 class="nav-notion">
${(await fs.getText('media/colour.svg')).replace( ${(await fs.getText('media/colour.svg')).replace(
/width="\d+" height="\d+"/, /width="\d+" height="\d+"/,
`class="nav-notion-icon"` `class="nav-notion-icon"`
)} )}
<span>notion-enhancer</span> <span>notion-enhancer</span>
</h1>`; </h1>`;
$notionNavItem.addEventListener('click', env.focusNotion); $notionNavItem.addEventListener('click', env.focusNotion);
const $coreNavItem = web.html`<a href="?view=core" class="nav-item">core</a>`, const $coreNavItem = web.html`<a href="?view=core" class="nav-item">core</a>`,
$extensionsNavItem = web.html`<a href="?view=extensions" class="nav-item">extensions</a>`, $extensionsNavItem = web.html`<a href="?view=extensions" class="nav-item">extensions</a>`,
$themesNavItem = web.html`<a href="?view=themes" class="nav-item">themes</a>`, $themesNavItem = web.html`<a href="?view=themes" class="nav-item">themes</a>`,
$integrationsNavItem = web.html`<a href="?view=integrations" class="nav-item">integrations</a>`, $integrationsNavItem = web.html`<a href="?view=integrations" class="nav-item">integrations</a>`,
$changelogNavItem = web.html`<button class="nav-item nav-changelog"> $changelogNavItem = web.html`<button class="nav-item nav-changelog">
${await components.feather('clock', { class: 'nav-changelog-icon' })} ${await components.feather('clock', { class: 'nav-changelog-icon' })}
</button>`; </button>`;
$changelogNavItem.addEventListener('click', () => { $changelogNavItem.addEventListener('click', () => {
$changelogModal.scrollTop = 0; $changelogModal.scrollTop = 0;
$changelogModal.classList.add('modal-visible'); $changelogModal.classList.add('modal-visible');
}); });
web.render( web.render(
document.body, document.body,
web.render( web.render(
web.html`<div class="body-container"></div>`, web.html`<div class="body-container"></div>`,
@ -373,69 +380,69 @@ web.render(
), ),
web.render($sidebar, $profile, $options) web.render($sidebar, $profile, $options)
) )
); );
function selectNavItem($item) { function selectNavItem($item) {
for (const $selected of document.querySelectorAll('.nav-item-selected')) { for (const $selected of document.querySelectorAll('.nav-item-selected')) {
$selected.className = 'nav-item'; $selected.className = 'nav-item';
} }
$item.className = 'nav-item-selected'; $item.className = 'nav-item-selected';
} }
await generators.modList( await generators.modList(
'core', 'core',
`Core mods provide the basics required for `Core mods provide the basics required for
all other extensions and themes to work. They all other extensions and themes to work. They
can't be disabled, but they can be configured can't be disabled, but they can be configured
- just click on a mod to access its options.` - just click on a mod to access its options.`
); );
router.addView('core', async () => { router.addView('core', async () => {
web.empty($main); web.empty($main);
selectNavItem($coreNavItem); selectNavItem($coreNavItem);
return web.render($main, await generators.modList('core')); return web.render($main, await generators.modList('core'));
}); });
await generators.modList( await generators.modList(
'extension', 'extension',
`Extensions build on the functionality and layout of `Extensions build on the functionality and layout of
the Notion client, modifying and interacting with the Notion client, modifying and interacting with
existing interfaces.` existing interfaces.`
); );
router.addView('extensions', async () => { router.addView('extensions', async () => {
web.empty($main); web.empty($main);
selectNavItem($extensionsNavItem); selectNavItem($extensionsNavItem);
return web.render($main, await generators.modList('extension')); return web.render($main, await generators.modList('extension'));
}); });
await generators.modList( await generators.modList(
'theme', 'theme',
`Themes change Notion's colour scheme. `Themes change Notion's colour scheme.
Dark themes will only work when Notion is in dark mode, Dark themes will only work when Notion is in dark mode,
and light themes will only work when Notion is in light mode. and light themes will only work when Notion is in light mode.
Only one theme of each mode can be enabled at a time.` Only one theme of each mode can be enabled at a time.`
); );
router.addView('themes', async () => { router.addView('themes', async () => {
web.empty($main); web.empty($main);
selectNavItem($themesNavItem); selectNavItem($themesNavItem);
return web.render($main, await generators.modList('theme')); return web.render($main, await generators.modList('theme'));
}); });
await generators.modList( await generators.modList(
'integration', 'integration',
web.html`<span class="danger">Integrations are extensions that use an unofficial API web.html`<span class="danger">Integrations are extensions that use an unofficial API
to access and modify content. They are used just like to access and modify content. They are used just like
normal extensions, but may be more dangerous to use.</span>` normal extensions, but may be more dangerous to use.</span>`
); );
router.addView('integrations', async () => { router.addView('integrations', async () => {
web.empty($main); web.empty($main);
selectNavItem($integrationsNavItem); selectNavItem($integrationsNavItem);
return web.render($main, await generators.modList('integration')); return web.render($main, await generators.modList('integration'));
}); });
router.setDefaultView('extensions'); router.setDefaultView('extensions');
router.addQueryListener('id', openSidebarMenu); router.addQueryListener('id', openSidebarMenu);
async function openSidebarMenu(id) { async function openSidebarMenu(id) {
if (!id) return; if (!id) return;
id = web.escape(id); id = web.escape(id);
@ -450,4 +457,5 @@ async function openSidebarMenu(id) {
if (id === 'profile') { if (id === 'profile') {
openProfileMenu(); openProfileMenu();
} else openModMenu(id); } else openModMenu(id);
} }
})();

View File

@ -10,8 +10,6 @@ import { tw } from './styles.mjs';
const notificationsURL = 'https://notion-enhancer.github.io/notifications.json'; const notificationsURL = 'https://notion-enhancer.github.io/notifications.json';
export const notifications = { export const notifications = {
$container: web.html`<div class="notifications-container"></div>`, $container: web.html`<div class="notifications-container"></div>`,
cache: await storage.get(['notifications'], []),
provider: await fs.getJSON(notificationsURL),
async add({ icon, message, id = undefined, color = undefined, link = undefined }) { async add({ icon, message, id = undefined, color = undefined, link = undefined }) {
const $notification = link const $notification = link
? web.html`<a ? web.html`<a
@ -59,22 +57,37 @@ export const notifications = {
$notification.addEventListener('click', env.reload); $notification.addEventListener('click', env.reload);
}, },
}; };
web.render(document.body, notifications.$container);
for (const notification of notifications.provider) { (async () => {
notifications.cache = await storage.get(['notifications'], []);
notifications.provider = await fs.getJSON(notificationsURL);
web.render(document.body, notifications.$container);
for (const notification of notifications.provider) {
const cached = notifications.cache.includes(notification.id), const cached = notifications.cache.includes(notification.id),
versionMatches = notification.version === env.version, versionMatches = notification.version === env.version,
envMatches = !notification.environments || notification.environments.includes(env.name); envMatches = !notification.environments || notification.environments.includes(env.name);
if (!cached && versionMatches && envMatches) notifications.add(notification); if (!cached && versionMatches && envMatches) notifications.add(notification);
} }
})();
const lastReadChangelog = await storage.get(['last_read_changelog']),
$changelogModalButton = web.html`<button type="button" class="modal-button">
Accept & Continue
</button>`;
export const $changelogModal = web.render( export const $changelogModal = web.render(
web.html`<div class="modal" role="dialog" aria-modal="true"> web.html`<div class="modal" role="dialog" aria-modal="true">
<div class="modal-overlay" aria-hidden="true"></div> <div class="modal-overlay" aria-hidden="true"></div>
</div>`, </div>`
);
(async () => {
const $changelogModalButton = web.html`<button type="button" class="modal-button">
Accept & Continue
</button>`;
$changelogModalButton.addEventListener('click', async () => {
$changelogModal.classList.remove('modal-visible');
await storage.set(['last_read_changelog'], env.version);
});
web.render(
$changelogModal,
web.render( web.render(
web.html`<div class="modal-box"></div>`, web.html`<div class="modal-box"></div>`,
web.html`<div class="modal-body"> web.html`<div class="modal-body">
@ -151,12 +164,11 @@ export const $changelogModal = web.render(
</div>`, </div>`,
web.render(web.html`<div class="modal-actions"></div>`, $changelogModalButton) web.render(web.html`<div class="modal-actions"></div>`, $changelogModalButton)
) )
); );
web.render(document.body, $changelogModal);
if (lastReadChangelog !== env.version) { const lastReadChangelog = await storage.get(['last_read_changelog']);
web.render(document.body, $changelogModal);
if (lastReadChangelog !== env.version) {
$changelogModal.classList.add('modal-visible'); $changelogModal.classList.add('modal-visible');
} }
$changelogModalButton.addEventListener('click', async () => { })();
$changelogModal.classList.remove('modal-visible');
await storage.set(['last_read_changelog'], env.version);
});

View File

@ -165,7 +165,7 @@ body,
.notion-body.dark .notion-body.dark
.notion-default-overlay-container .notion-default-overlay-container
[style*='grid-template-columns: [boolean-start] 60px [boolean-end property-start] 120px [property-end opererator-start] 110px [operator-end value-start] auto [value-end menu-start] 32px [menu-end];'], [style*='grid-template-columns: [boolean-start] 60px [boolean-end property-start] 120px [property-end opererator-start] 110px [operator-end value-start] auto [value-end menu-start] 32px [menu-end];'],
.notion-focusable[style*='background: rgb(80, 85, 88);'], .notion-focusable[style*='background: rgb(80, 85, 88);']:not(.notion-help-button):not(.onboarding-checklist-button),
.notion-body:not(.dark) .notion-body:not(.dark)
.notion-default-overlay-container .notion-default-overlay-container
[style*='grid-template-columns: [boolean-start] 60px [boolean-end property-start] 120px [property-end opererator-start] 110px [operator-end value-start] auto [value-end menu-start] 32px [menu-end];'] [style*='grid-template-columns: [boolean-start] 60px [boolean-end property-start] 120px [property-end opererator-start] 110px [operator-end value-start] auto [value-end menu-start] 32px [menu-end];']