mirror of
https://github.com/notion-enhancer/notion-enhancer.git
synced 2025-04-08 06:29:03 +00:00
fix: more resilient ipc for db operations, proper fallback handling
+ minor refactoring of files from api/ to shared/ + adjusting wording of onboarding terms + inject menu button b/w the `settings & members` and the `new page` buttons
This commit is contained in:
parent
a88c74d1c3
commit
025bbca44c
@ -37,14 +37,15 @@ const doThemeOverride = async (db) => {
|
|||||||
</style>`);
|
</style>`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const insertMenu = async (db) => {
|
const initMenu = async (db) => {
|
||||||
const notionSidebar = `.notion-sidebar-container .notion-sidebar > :nth-child(3) > div > :nth-child(2)`,
|
const notionSidebar = `.notion-sidebar-container .notion-sidebar > :nth-child(3) > div > :nth-child(2)`,
|
||||||
|
notionSettingsAndMembers = `${notionSidebar} > [role="button"]:nth-child(3)`,
|
||||||
{ html, addKeyListener, addMutationListener } = globalThis.__enhancerApi,
|
{ html, addKeyListener, addMutationListener } = globalThis.__enhancerApi,
|
||||||
{ platform, enhancerUrl, onMessage } = globalThis.__enhancerApi,
|
{ platform, enhancerUrl, onMessage } = globalThis.__enhancerApi,
|
||||||
menuButtonIconStyle = await db.get("menuButtonIconStyle"),
|
menuButtonIconStyle = await db.get("menuButtonIconStyle"),
|
||||||
openMenuHotkey = await db.get("openMenuHotkey"),
|
openMenuHotkey = await db.get("openMenuHotkey"),
|
||||||
renderPing = {
|
renderPing = {
|
||||||
namespace: "notion-enhancer",
|
channel: "notion-enhancer",
|
||||||
hotkey: openMenuHotkey,
|
hotkey: openMenuHotkey,
|
||||||
icon: menuButtonIconStyle,
|
icon: menuButtonIconStyle,
|
||||||
};
|
};
|
||||||
@ -88,12 +89,15 @@ const insertMenu = async (db) => {
|
|||||||
: " text-[16px]"}"
|
: " text-[16px]"}"
|
||||||
>notion-enhancer
|
>notion-enhancer
|
||||||
<//>`;
|
<//>`;
|
||||||
document.body.append($modal);
|
const insertMenu = () => {
|
||||||
addMutationListener(notionSidebar, () => {
|
if (!document.contains($modal)) document.body.append($modal);
|
||||||
if (document.contains($button)) return;
|
if (!document.querySelector(notionSidebar)?.contains($button)) {
|
||||||
document.querySelector(notionSidebar)?.append($button);
|
document.querySelector(notionSettingsAndMembers)?.after($button);
|
||||||
});
|
}
|
||||||
document.querySelector(notionSidebar)?.append($button);
|
};
|
||||||
|
addMutationListener(notionSidebar, insertMenu);
|
||||||
|
insertMenu();
|
||||||
|
|
||||||
addMutationListener("body", sendThemePing);
|
addMutationListener("body", sendThemePing);
|
||||||
window.addEventListener("focus", sendRenderPing);
|
window.addEventListener("focus", sendRenderPing);
|
||||||
|
|
||||||
@ -102,7 +106,7 @@ const insertMenu = async (db) => {
|
|||||||
$modal.open();
|
$modal.open();
|
||||||
});
|
});
|
||||||
window.addEventListener("message", (event) => {
|
window.addEventListener("message", (event) => {
|
||||||
if (event.data?.namespace !== "notion-enhancer") return;
|
if (event.data?.channel !== "notion-enhancer") return;
|
||||||
if (event.data?.action === "close-menu") $modal.close();
|
if (event.data?.action === "close-menu") $modal.close();
|
||||||
if (event.data?.action === "open-menu") $modal.open();
|
if (event.data?.action === "open-menu") $modal.open();
|
||||||
});
|
});
|
||||||
@ -115,7 +119,7 @@ export default async (api, db) => {
|
|||||||
await Promise.all([
|
await Promise.all([
|
||||||
overrideThemes(db),
|
overrideThemes(db),
|
||||||
insertCustomStyles(db),
|
insertCustomStyles(db),
|
||||||
insertMenu(db),
|
initMenu(db),
|
||||||
sendTelemetryPing(),
|
sendTelemetryPing(),
|
||||||
]);
|
]);
|
||||||
api.sendMessage("notion-enhancer", "load-complete");
|
api.sendMessage("notion-enhancer", "load-complete");
|
||||||
|
@ -29,12 +29,12 @@ function Onboarding() {
|
|||||||
<${Description}>
|
<${Description}>
|
||||||
In order for the notion-enhancer to function, it may access, collect,
|
In order for the notion-enhancer to function, it may access, collect,
|
||||||
process and/or store data on your device (including workspace content,
|
process and/or store data on your device (including workspace content,
|
||||||
device metadata, and notion-enhancer configuration) according to its
|
device metadata, and notion-enhancer configuration) as described in its
|
||||||
privacy policy. Unless otherwise stated for telemetry purposes, the
|
privacy policy. Unless otherwise stated, the notion-enhancer will never
|
||||||
notion-enhancer will never transmit any of your data from your device.
|
transmit your information from your device. Collection of anonymous
|
||||||
Telemetry can be disabled at any time through the menu.
|
telemetry data is enabled by default and can be disabled at any time
|
||||||
<br />
|
through the menu.
|
||||||
<br />
|
<br /><br />
|
||||||
The notion-enhancer is free and open-source software distributed under
|
The notion-enhancer is free and open-source software distributed under
|
||||||
the <a href="${tsAndCs}#license">MIT License</a> without warranty of any
|
the <a href="${tsAndCs}#license">MIT License</a> without warranty of any
|
||||||
kind. In no event shall the authors be liable for any consequences of
|
kind. In no event shall the authors be liable for any consequences of
|
||||||
@ -72,7 +72,7 @@ function Onboarding() {
|
|||||||
>Check out the usage guide.
|
>Check out the usage guide.
|
||||||
<//>
|
<//>
|
||||||
<${Tile}
|
<${Tile}
|
||||||
href="https://notion-enhancer.github.io/getting-started/basic-usage/"
|
href="https://notion-enhancer.github.io/documentation/mods/"
|
||||||
icon="package-plus"
|
icon="package-plus"
|
||||||
title="Something missing?"
|
title="Something missing?"
|
||||||
>Build your own extension.
|
>Build your own extension.
|
||||||
|
@ -160,7 +160,7 @@ window.addEventListener("focus", () => {
|
|||||||
setState({ focus: true, rerender: true });
|
setState({ focus: true, rerender: true });
|
||||||
});
|
});
|
||||||
window.addEventListener("message", (event) => {
|
window.addEventListener("message", (event) => {
|
||||||
if (event.data?.namespace !== "notion-enhancer") return;
|
if (event.data?.channel !== "notion-enhancer") return;
|
||||||
const [hotkey, theme, icon] = useState(["hotkey", "theme", "icon"]);
|
const [hotkey, theme, icon] = useState(["hotkey", "theme", "icon"]);
|
||||||
setState({
|
setState({
|
||||||
rerender: true,
|
rerender: true,
|
||||||
@ -176,13 +176,13 @@ useState(["hotkey"], ([hotkey]) => {
|
|||||||
setState({ hotkeyRegistered: true });
|
setState({ hotkeyRegistered: true });
|
||||||
addKeyListener(hotkey, (event) => {
|
addKeyListener(hotkey, (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const msg = { namespace: "notion-enhancer", action: "open-menu" };
|
const msg = { channel: "notion-enhancer", action: "open-menu" };
|
||||||
parent?.postMessage(msg, "*");
|
parent?.postMessage(msg, "*");
|
||||||
});
|
});
|
||||||
addKeyListener("Escape", () => {
|
addKeyListener("Escape", () => {
|
||||||
const [popupOpen] = useState(["popupOpen"]);
|
const [popupOpen] = useState(["popupOpen"]);
|
||||||
if (!popupOpen) {
|
if (!popupOpen) {
|
||||||
const msg = { namespace: "notion-enhancer", action: "close-menu" };
|
const msg = { channel: "notion-enhancer", action: "close-menu" };
|
||||||
parent?.postMessage(msg, "*");
|
parent?.postMessage(msg, "*");
|
||||||
} else setState({ rerender: true });
|
} else setState({ rerender: true });
|
||||||
});
|
});
|
||||||
@ -199,7 +199,7 @@ useState(["rerender"], async () => {
|
|||||||
// but extension:// pages can access chrome apis
|
// but extension:// pages can access chrome apis
|
||||||
// => notion-enhancer api is imported directly
|
// => notion-enhancer api is imported directly
|
||||||
if (typeof globalThis.__enhancerApi === "undefined") {
|
if (typeof globalThis.__enhancerApi === "undefined") {
|
||||||
await import("../../api/system.js");
|
await import("../../shared/system.js");
|
||||||
// in electron this isn't necessary, as a) scripts are
|
// in electron this isn't necessary, as a) scripts are
|
||||||
// not running in an isolated execution context and b)
|
// not running in an isolated execution context and b)
|
||||||
// the notion:// protocol csp bypass allows scripts to
|
// the notion:// protocol csp bypass allows scripts to
|
||||||
|
17
src/init.js
17
src/init.js
@ -14,15 +14,15 @@ const isElectron = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (isElectron()) {
|
if (isElectron()) {
|
||||||
require("./api/system.js");
|
require("./shared/system.js");
|
||||||
require("./api/mods.js");
|
require("./shared/registry.js");
|
||||||
const { enhancerUrl } = globalThis.__enhancerApi,
|
const { enhancerUrl } = globalThis.__enhancerApi,
|
||||||
{ getMods, isEnabled, modDatabase } = globalThis.__enhancerApi;
|
{ getMods, isEnabled, modDatabase } = globalThis.__enhancerApi;
|
||||||
|
|
||||||
// calling require("electron") in a process require()-d
|
// calling require("electron") in a process require()-d
|
||||||
// from these paths throws "websocket connection to __ failed"
|
// from these paths throws "websocket connection to __ failed"
|
||||||
// and triggers infinite loading => ignore for now, but
|
// and triggers infinite loading => ignore for now, but will
|
||||||
// requires further investigation later
|
// require further investigation later
|
||||||
const ignoredPaths = [
|
const ignoredPaths = [
|
||||||
"shared/sqliteTypes",
|
"shared/sqliteTypes",
|
||||||
"shared/TimeSource",
|
"shared/TimeSource",
|
||||||
@ -37,7 +37,7 @@ if (isElectron()) {
|
|||||||
|
|
||||||
module.exports = async (target, __exports, __eval) => {
|
module.exports = async (target, __exports, __eval) => {
|
||||||
if (ignoredPaths.includes(target)) return;
|
if (ignoredPaths.includes(target)) return;
|
||||||
if (target === "main/main") require("./worker.js");
|
if (target.startsWith("main/")) require("./worker.js");
|
||||||
|
|
||||||
// clientStyles
|
// clientStyles
|
||||||
// clientScripts
|
// clientScripts
|
||||||
@ -63,9 +63,6 @@ if (isElectron()) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// clientStyles
|
import(chrome.runtime.getURL("/shared/system.js")) //
|
||||||
// clientScripts
|
.then(() => import(chrome.runtime.getURL("/load.mjs")));
|
||||||
import(chrome.runtime.getURL("/api/system.js")).then(() => {
|
|
||||||
import(chrome.runtime.getURL("/load.mjs"));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
@ -20,10 +20,12 @@ export default (async () => {
|
|||||||
import(enhancerUrl("vendor/twind.min.js")),
|
import(enhancerUrl("vendor/twind.min.js")),
|
||||||
import(enhancerUrl("vendor/lucide.min.js")),
|
import(enhancerUrl("vendor/lucide.min.js")),
|
||||||
import(enhancerUrl("vendor/htm.min.js")),
|
import(enhancerUrl("vendor/htm.min.js")),
|
||||||
import(enhancerUrl("api/events.js")),
|
|
||||||
import(enhancerUrl("api/mods.js")),
|
|
||||||
]);
|
]);
|
||||||
await import(enhancerUrl("api/interface.js"));
|
await Promise.all([
|
||||||
|
import(enhancerUrl("shared/events.js")),
|
||||||
|
import(enhancerUrl("shared/registry.js")),
|
||||||
|
import(enhancerUrl("shared/markup.js")),
|
||||||
|
]);
|
||||||
const { getMods, isEnabled, modDatabase } = globalThis.__enhancerApi;
|
const { getMods, isEnabled, modDatabase } = globalThis.__enhancerApi;
|
||||||
|
|
||||||
for (const mod of await getMods()) {
|
for (const mod of await getMods()) {
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const IS_ELECTRON = typeof module !== "undefined";
|
const IS_ELECTRON = typeof module !== "undefined",
|
||||||
|
IS_RENDERER = IS_ELECTRON && process.type === "renderer";
|
||||||
|
|
||||||
// expected values: 'linux', 'win32', 'darwin' (== macos), 'firefox'
|
// expected values: 'linux', 'win32', 'darwin' (== macos), 'firefox'
|
||||||
// and 'chromium' (inc. chromium-based browsers like edge and brave)
|
// and 'chromium' (inc. chromium-based browsers like edge and brave)
|
||||||
@ -26,16 +27,18 @@ const platform = IS_ELECTRON
|
|||||||
IS_ELECTRON
|
IS_ELECTRON
|
||||||
? `notion://www.notion.so/__notion-enhancer/${target.replace(/^\//, "")}`
|
? `notion://www.notion.so/__notion-enhancer/${target.replace(/^\//, "")}`
|
||||||
: chrome.runtime.getURL(target),
|
: chrome.runtime.getURL(target),
|
||||||
// should only be used from an electron main process, does nothing elsewhere
|
// require a file from the root of notion's app/ folder,
|
||||||
notionRequire = (target) => IS_ELECTRON && require(`../../../${target}`);
|
// only available in an electron main process
|
||||||
|
notionRequire = (target) =>
|
||||||
|
IS_ELECTRON && !IS_RENDERER ? require(`../../../${target}`) : undefined;
|
||||||
|
|
||||||
let __port;
|
let __port;
|
||||||
const onMessage = (channel, listener) => {
|
const onMessage = (channel, listener) => {
|
||||||
// from worker to client
|
// from worker to client
|
||||||
if (IS_ELECTRON) {
|
if (IS_RENDERER) {
|
||||||
const { ipcRenderer } = require("electron");
|
const { ipcRenderer } = require("electron");
|
||||||
ipcRenderer.on(channel, listener);
|
ipcRenderer.on(channel, listener);
|
||||||
} else {
|
} else if (!IS_ELECTRON) {
|
||||||
__port ??= chrome.runtime.connect();
|
__port ??= chrome.runtime.connect();
|
||||||
__port.onMessage.addListener((msg) => {
|
__port.onMessage.addListener((msg) => {
|
||||||
if (msg?.channel !== channel || msg?.invocation) return;
|
if (msg?.channel !== channel || msg?.invocation) return;
|
||||||
@ -45,19 +48,21 @@ const onMessage = (channel, listener) => {
|
|||||||
},
|
},
|
||||||
sendMessage = (channel, message) => {
|
sendMessage = (channel, message) => {
|
||||||
// to worker from client
|
// to worker from client
|
||||||
if (IS_ELECTRON) {
|
if (IS_RENDERER) {
|
||||||
const { ipcRenderer } = require("electron");
|
const { ipcRenderer } = require("electron");
|
||||||
ipcRenderer.send(channel, message);
|
ipcRenderer.send(channel, message);
|
||||||
} else {
|
} else if (!IS_ELECTRON) {
|
||||||
__port ??= chrome.runtime.connect();
|
__port ??= chrome.runtime.connect();
|
||||||
__port.postMessage({ channel, message });
|
__port.postMessage({ channel, message });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
invokeInWorker = (channel, message) => {
|
invokeInWorker = (channel, message) => {
|
||||||
if (IS_ELECTRON) {
|
// sends a payload to the worker/main
|
||||||
|
// process and waits for a response
|
||||||
|
if (IS_RENDERER) {
|
||||||
const { ipcRenderer } = require("electron");
|
const { ipcRenderer } = require("electron");
|
||||||
return ipcRenderer.invoke(channel, message);
|
return ipcRenderer.invoke(channel, message);
|
||||||
} else {
|
} else if (!IS_ELECTRON) {
|
||||||
// polyfills the electron.ipcRenderer.invoke method in
|
// polyfills the electron.ipcRenderer.invoke method in
|
||||||
// the browser: uses a long-lived ipc connection to
|
// the browser: uses a long-lived ipc connection to
|
||||||
// pass messages and handle responses asynchronously
|
// pass messages and handle responses asynchronously
|
||||||
@ -98,15 +103,35 @@ const readFile = (file) => {
|
|||||||
if (IS_ELECTRON) {
|
if (IS_ELECTRON) {
|
||||||
if (!file.startsWith("http")) {
|
if (!file.startsWith("http")) {
|
||||||
const { resolve } = require("path");
|
const { resolve } = require("path");
|
||||||
return require(resolve(`${__dirname}/../${file}`), "utf-8");
|
return require(resolve(`${__dirname}/../${file}`));
|
||||||
}
|
}
|
||||||
const notionProtocol = "notion://www.notion.so/";
|
const notionProtocol = "notion://www.notion.so/";
|
||||||
file = file.replace(/^https:\/\/www\.notion\.so\//, notionProtocol);
|
file = file.replace(/^https:\/\/www\.notion\.so\//, notionProtocol);
|
||||||
} else file = file.startsWith("http") ? file : enhancerUrl(file);
|
} else file = file.startsWith("http") ? file : enhancerUrl(file);
|
||||||
return fetch(file).then((res) => res.json());
|
return fetch(file).then((res) => res.json());
|
||||||
|
};
|
||||||
|
|
||||||
|
const initDatabase = (namespace, fallbacks = {}) => {
|
||||||
|
// all db operations are performed via ipc:
|
||||||
|
// with nodeintegration disabled, sqlite cannot
|
||||||
|
// be require()-d from the renderer process
|
||||||
|
const query = (query, args = {}) =>
|
||||||
|
IS_ELECTRON && !IS_RENDERER
|
||||||
|
? globalThis.__enhancerApi.queryDatabase(namespace, query, args)
|
||||||
|
: invokeInWorker("notion-enhancer:db", {
|
||||||
|
action: "query-database",
|
||||||
|
data: { namespace, query, args },
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
get: (key) => query("get", { key, fallbacks }),
|
||||||
|
set: (key, value) => query("set", { key, value }),
|
||||||
|
remove: (keys) => query("remove", { keys }),
|
||||||
|
export: () => query("export"),
|
||||||
|
import: (obj) => query("import", { obj }),
|
||||||
|
};
|
||||||
},
|
},
|
||||||
reloadApp = () => {
|
reloadApp = () => {
|
||||||
if (IS_ELECTRON && require("electron").app) {
|
if (IS_ELECTRON && !IS_RENDERER) {
|
||||||
const { app } = require("electron"),
|
const { app } = require("electron"),
|
||||||
args = process.argv.slice(1).filter((arg) => arg !== "--startup");
|
args = process.argv.slice(1).filter((arg) => arg !== "--startup");
|
||||||
app.relaunch({ args });
|
app.relaunch({ args });
|
||||||
@ -114,26 +139,6 @@ const readFile = (file) => {
|
|||||||
} else sendMessage("notion-enhancer", "reload-app");
|
} else sendMessage("notion-enhancer", "reload-app");
|
||||||
};
|
};
|
||||||
|
|
||||||
const initDatabase = (namespace, fallbacks = {}) => {
|
|
||||||
// all db operations are performed via ipc:
|
|
||||||
// with nodeintegration disabled, sqlite cannot
|
|
||||||
// be require()-d from the renderer process
|
|
||||||
const operation = (type, args = {}) =>
|
|
||||||
invokeInWorker("notion-enhancer:db", {
|
|
||||||
namespace,
|
|
||||||
fallbacks,
|
|
||||||
operation: type,
|
|
||||||
args,
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
get: (key) => operation("get", { key }),
|
|
||||||
set: (key, value) => operation("set", { key, value }),
|
|
||||||
remove: (keys) => operation("remove", { keys }),
|
|
||||||
export: () => operation("export"),
|
|
||||||
import: (obj) => operation("import", { obj }),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
globalThis.__enhancerApi ??= {};
|
globalThis.__enhancerApi ??= {};
|
||||||
Object.assign(globalThis.__enhancerApi, {
|
Object.assign(globalThis.__enhancerApi, {
|
||||||
platform,
|
platform,
|
||||||
@ -145,6 +150,6 @@ Object.assign(globalThis.__enhancerApi, {
|
|||||||
invokeInWorker,
|
invokeInWorker,
|
||||||
readFile,
|
readFile,
|
||||||
readJson,
|
readJson,
|
||||||
reloadApp,
|
|
||||||
initDatabase,
|
initDatabase,
|
||||||
|
reloadApp,
|
||||||
});
|
});
|
100
src/worker.js
100
src/worker.js
@ -54,7 +54,7 @@ const initDatabase = async () => {
|
|||||||
};
|
};
|
||||||
return db;
|
return db;
|
||||||
},
|
},
|
||||||
executeOperation = async (namespace, fallbacks, operation, args) => {
|
queryDatabase = async (namespace, query, args) => {
|
||||||
namespace ??= "";
|
namespace ??= "";
|
||||||
if (Array.isArray(namespace)) namespace = namespace.join("__");
|
if (Array.isArray(namespace)) namespace = namespace.join("__");
|
||||||
if (namespace?.length) namespace += "__";
|
if (namespace?.length) namespace += "__";
|
||||||
@ -62,7 +62,7 @@ const initDatabase = async () => {
|
|||||||
key.startsWith(namespace) ? key : namespace + key;
|
key.startsWith(namespace) ? key : namespace + key;
|
||||||
|
|
||||||
await (__db ??= initDatabase());
|
await (__db ??= initDatabase());
|
||||||
switch (operation) {
|
switch (query) {
|
||||||
case "get": {
|
case "get": {
|
||||||
const key = namespaceify(args.key);
|
const key = namespaceify(args.key);
|
||||||
let value;
|
let value;
|
||||||
@ -71,7 +71,7 @@ const initDatabase = async () => {
|
|||||||
value = JSON.parse(__statements.select.get(key)?.value);
|
value = JSON.parse(__statements.select.get(key)?.value);
|
||||||
} catch {}
|
} catch {}
|
||||||
} else value = (await chrome.storage.local.get([key]))[key];
|
} else value = (await chrome.storage.local.get([key]))[key];
|
||||||
return value ?? fallbacks[key];
|
return value ?? args.fallbacks[args.key];
|
||||||
}
|
}
|
||||||
case "set": {
|
case "set": {
|
||||||
const key = namespaceify(args.key),
|
const key = namespaceify(args.key),
|
||||||
@ -114,95 +114,61 @@ const initDatabase = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (IS_ELECTRON) {
|
if (IS_ELECTRON) {
|
||||||
const { app, ipcMain } = require("electron"),
|
const { ipcMain } = require("electron"),
|
||||||
reloadApp = () => {
|
{ reloadApp } = globalThis.__enhancerApi;
|
||||||
const args = process.argv.slice(1).filter((arg) => arg !== "--startup");
|
ipcMain.handle("notion-enhancer:db", ({}, message) => {
|
||||||
app.relaunch({ args });
|
if (message?.action !== "query-database") return;
|
||||||
app.exit();
|
const { namespace, query, args } = message.data;
|
||||||
};
|
return queryDatabase(namespace, query, args);
|
||||||
|
|
||||||
ipcMain.handle("notion-enhancer:db", (_event, message) => {
|
|
||||||
return executeOperation(
|
|
||||||
message.namespace,
|
|
||||||
message.fallbacks,
|
|
||||||
message.operation,
|
|
||||||
message.args
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
ipcMain.on("notion-enhancer", ({ sender }, message) => {
|
||||||
ipcMain.on("notion-enhancer", (_event, message) => {
|
if (message === "reload-app") reloadApp();
|
||||||
if (message === "open-menu") {
|
|
||||||
// todo
|
|
||||||
} else if (message === "reload-app") {
|
|
||||||
reloadApp();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const notionUrl = "https://www.notion.so/",
|
const notionUrl = "https://www.notion.so/",
|
||||||
isNotionTab = (tab) => tab?.url?.startsWith(notionUrl);
|
isNotionTab = (tab) => tab?.url?.startsWith(notionUrl);
|
||||||
|
|
||||||
const tabQueue = new Set(),
|
const tabQueue = new Set(),
|
||||||
|
openMenu = { channel: "notion-enhancer", message: "open-menu" },
|
||||||
openEnhancerMenu = async (tab) => {
|
openEnhancerMenu = async (tab) => {
|
||||||
if (!isNotionTab(tab)) {
|
if (!isNotionTab(tab)) {
|
||||||
const openTabs = await chrome.tabs.query({
|
const windowId = chrome.windows.WINDOW_ID_CURRENT,
|
||||||
windowId: chrome.windows.WINDOW_ID_CURRENT,
|
windowTabs = await chrome.tabs.query({ windowId });
|
||||||
});
|
tab = windowTabs.find(isNotionTab);
|
||||||
tab = openTabs.find(isNotionTab);
|
|
||||||
tab ??= await chrome.tabs.create({ url: notionUrl });
|
tab ??= await chrome.tabs.create({ url: notionUrl });
|
||||||
}
|
}
|
||||||
chrome.tabs.highlight({ tabs: [tab.index] });
|
chrome.tabs.highlight({ tabs: [tab.index] });
|
||||||
if (tab.status === "complete") {
|
if (tab.status === "complete") {
|
||||||
chrome.tabs.sendMessage(tab.id, {
|
chrome.tabs.sendMessage(tab.id, openMenu);
|
||||||
channel: "notion-enhancer",
|
|
||||||
message: "open-menu",
|
|
||||||
});
|
|
||||||
} else tabQueue.add(tab.id);
|
} else tabQueue.add(tab.id);
|
||||||
},
|
},
|
||||||
reloadNotionTabs = async () => {
|
reloadNotionTabs = async () => {
|
||||||
const openTabs = await chrome.tabs.query({
|
const windowId = chrome.windows.WINDOW_ID_CURRENT;
|
||||||
windowId: chrome.windows.WINDOW_ID_CURRENT,
|
(await chrome.tabs.query({ windowId }))
|
||||||
}),
|
.filter(isNotionTab)
|
||||||
notionTabs = openTabs.filter(isNotionTab);
|
.forEach((tab) => chrome.tabs.reload(tab.id));
|
||||||
notionTabs.forEach((tab) => chrome.tabs.reload(tab.id));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// listen for invoke: https://developer.chrome.com/docs/extensions/mv3/messaging/
|
// listen for invoke: https://developer.chrome.com/docs/extensions/mv3/messaging/
|
||||||
ipcMain.handle("notion-enhancer:db", (_event, message) => {
|
|
||||||
return executeOperation(
|
|
||||||
message.namespace,
|
|
||||||
message.fallbacks,
|
|
||||||
message.operation,
|
|
||||||
message.args
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
chrome.action.onClicked.addListener(openEnhancerMenu);
|
chrome.action.onClicked.addListener(openEnhancerMenu);
|
||||||
chrome.runtime.onConnect.addListener((port) => {
|
chrome.runtime.onConnect.addListener((port) => {
|
||||||
port.onMessage.addListener((msg, sender) => {
|
port.onMessage.addListener((msg, sender) => {
|
||||||
if (msg?.channel === "notion-enhancer:db") {
|
if (msg?.channel !== "notion-enhancer") return;
|
||||||
const { invocation } = msg,
|
const { message, invocation } = msg;
|
||||||
res = executeOperation(
|
if (message.action === "query-database") {
|
||||||
msg.namespace,
|
const { namespace, query, args } = message.data,
|
||||||
msg.fallbacks,
|
res = queryDatabase(namespace, query, args);
|
||||||
msg.operation,
|
|
||||||
msg.args
|
|
||||||
);
|
|
||||||
if (invocation) port.postMessage({ invocation, message: res });
|
if (invocation) port.postMessage({ invocation, message: res });
|
||||||
}
|
}
|
||||||
|
if (message === "load-complete") {
|
||||||
if (msg?.channel === "notion-enhancer") {
|
if (!tabQueue.has(sender?.tab?.id)) return;
|
||||||
if (sender.tab && msg.message === "load-complete") {
|
chrome.tabs.sendMessage(sender.tab.id, openMenu);
|
||||||
if (tabQueue.has(sender.tab.id)) {
|
tabQueue.delete(sender.tab.id);
|
||||||
chrome.tabs.sendMessage(sender.tab.id, {
|
|
||||||
channel: "notion-enhancer",
|
|
||||||
message: "open-menu",
|
|
||||||
});
|
|
||||||
tabQueue.delete(sender.tab.id);
|
|
||||||
}
|
|
||||||
} else if (msg.message === "reload-app") {
|
|
||||||
reloadNotionTabs();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (message === "reload-app") reloadNotionTabs();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
globalThis.__enhancerApi ??= {};
|
||||||
|
Object.assign(globalThis.__enhancerApi, { queryDatabase });
|
||||||
|
Loading…
Reference in New Issue
Block a user