Refactoring
This commit is contained in:
parent
057bc77990
commit
9680b3574d
13 changed files with 171 additions and 149 deletions
|
@ -1,9 +1,11 @@
|
||||||
import { browser, Runtime, Tabs } from "webextension-polyfill-ts";
|
import { browser, Runtime, Tabs } from "webextension-polyfill-ts";
|
||||||
import { GTranslateScraper } from "./gtranslate_scraper";
|
import { GTranslateScraper } from "./gtranslate_scraper";
|
||||||
import { Flashcards } from "./database";
|
import { Flashcards } from "./database";
|
||||||
|
import { Communicator, command, commandKinds } from "../communication";
|
||||||
|
|
||||||
const scraper = new GTranslateScraper();
|
const scraper = new GTranslateScraper();
|
||||||
const db = new Flashcards();
|
const db = new Flashcards();
|
||||||
|
let com = new Communicator();
|
||||||
console.log(db);
|
console.log(db);
|
||||||
|
|
||||||
const getTab = async (s: Runtime.MessageSender): Promise<Tabs.Tab> => {
|
const getTab = async (s: Runtime.MessageSender): Promise<Tabs.Tab> => {
|
||||||
|
@ -15,53 +17,41 @@ const getTab = async (s: Runtime.MessageSender): Promise<Tabs.Tab> => {
|
||||||
return tabs[0];
|
return tabs[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
const isEnabled = async (tabID: number): Promise<boolean> =>
|
const isEnabledSession = async (tabID: number): Promise<boolean> =>
|
||||||
(await browser.sessions.getTabValue(tabID, "lingoEnabled")) || false;
|
(await browser.sessions.getTabValue(tabID, "lingoEnabled")) ?? false;
|
||||||
const setEnabled = async (tabID: number, value: boolean) =>
|
|
||||||
|
const setEnabledSession = async (tabID: number, value: boolean) =>
|
||||||
await browser.sessions.setTabValue(tabID, "lingoEnabled", value);
|
await browser.sessions.setTabValue(tabID, "lingoEnabled", value);
|
||||||
|
|
||||||
const injectScript = async (tabID: number, enabled?: boolean) => {
|
const injectScript = async (tabID: number, enabled?: boolean) => {
|
||||||
await browser.tabs.executeScript(tabID, {
|
await browser.tabs.executeScript(tabID, {
|
||||||
file: "/content_script.bundle.js",
|
file: "/content_script.bundle.js",
|
||||||
});
|
});
|
||||||
if (!enabled) enabled = await isEnabled(tabID);
|
if (!enabled) enabled = await isEnabledSession(tabID);
|
||||||
browser.tabs.sendMessage(tabID, {
|
browser.tabs.sendMessage(tabID, {
|
||||||
commandKind: commands.setEnabled,
|
commandKind: commandKinds.setEnabled,
|
||||||
value: enabled,
|
value: enabled,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const setEnabledCommand = async (v: boolean, s: Runtime.MessageSender) => {
|
com.setEnabledCallback = async (v: boolean, s: Runtime.MessageSender) => {
|
||||||
const tab = await getTab(s);
|
const tab = await getTab(s);
|
||||||
|
|
||||||
injectScript(tab.id);
|
injectScript(tab.id);
|
||||||
await setEnabled(tab.id, v);
|
await setEnabledSession(tab.id, v);
|
||||||
};
|
|
||||||
const getEnabledCommand = async (
|
|
||||||
s: Runtime.MessageSender
|
|
||||||
): Promise<boolean> => {
|
|
||||||
const tab = await getTab(s);
|
|
||||||
return isEnabled(tab.id);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
browser.runtime.onMessage.addListener(
|
com.getEnabledCallback = async (s: Runtime.MessageSender): Promise<boolean> => {
|
||||||
(c: command, s: Runtime.MessageSender): Promise<any> => {
|
const tab = await getTab(s);
|
||||||
switch (c.commandKind) {
|
return isEnabledSession(tab.id);
|
||||||
case commands.setEnabled:
|
};
|
||||||
return setEnabledCommand(c.value, s);
|
|
||||||
case commands.getEnabled:
|
com.translateCallback = (toTranslate) => scraper.translate(toTranslate);
|
||||||
return getEnabledCommand(s);
|
com.addFlashcardCallback = (card) => db.addTranslation(card);
|
||||||
case commands.translate:
|
|
||||||
return scraper.translate(c.toTranslate);
|
|
||||||
case commands.addFlashcard:
|
|
||||||
return db.addTranslation(c.translation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
browser.tabs.onUpdated.addListener(async (tabID, changeInfo) => {
|
browser.tabs.onUpdated.addListener(async (tabID, changeInfo) => {
|
||||||
if (changeInfo.status == "complete") {
|
if (changeInfo.status == "complete") {
|
||||||
if (await isEnabled(tabID)) {
|
if (await isEnabledSession(tabID)) {
|
||||||
injectScript(tabID);
|
injectScript(tabID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,12 +26,17 @@ export class Flashcards {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
addTranslation(t: Translation): Promise<flashcard> {
|
addTranslation(t: Translation | flashcard): Promise<flashcard> {
|
||||||
let card: flashcard = {
|
let card: flashcard;
|
||||||
...t,
|
if ("dateAdded" in t && "exported" in t) card = t;
|
||||||
dateAdded: new Date(),
|
else {
|
||||||
exported: false,
|
card = {
|
||||||
};
|
...t,
|
||||||
|
dateAdded: new Date(),
|
||||||
|
exported: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let req = this.db
|
let req = this.db
|
||||||
.transaction(["flashcards"], "readwrite")
|
.transaction(["flashcards"], "readwrite")
|
||||||
.objectStore("flashcards")
|
.objectStore("flashcards")
|
||||||
|
|
76
src/communication.ts
Normal file
76
src/communication.ts
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
//There has to be a better way to do this while remaining type safe...
|
||||||
|
import { browser, Runtime } from "webextension-polyfill-ts";
|
||||||
|
import { flashcard } from "./background/database";
|
||||||
|
|
||||||
|
export class Communicator {
|
||||||
|
setEnabledCallback: (value: boolean, sender: Runtime.MessageSender) => void;
|
||||||
|
getEnabledCallback: (sender: Runtime.MessageSender) => Promise<boolean>;
|
||||||
|
translateCallback: (
|
||||||
|
toTranslate: string,
|
||||||
|
sender: Runtime.MessageSender
|
||||||
|
) => Promise<Translation>;
|
||||||
|
|
||||||
|
addFlashcardCallback: (
|
||||||
|
value: Translation | flashcard,
|
||||||
|
sender: Runtime.MessageSender
|
||||||
|
) => Promise<flashcard>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
browser.runtime.onMessage.addListener(
|
||||||
|
(c: command, s: Runtime.MessageSender) => {
|
||||||
|
switch (c.commandKind) {
|
||||||
|
case commandKinds.setEnabled:
|
||||||
|
return this.setEnabledCallback(c.value, s);
|
||||||
|
case commandKinds.getEnabled:
|
||||||
|
return this.getEnabledCallback(s);
|
||||||
|
case commandKinds.translate:
|
||||||
|
return this.translateCallback(c.toTranslate, s);
|
||||||
|
case commandKinds.addFlashcard:
|
||||||
|
return this.addFlashcardCallback(c.card, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static setEnabled = (v: boolean) =>
|
||||||
|
sendMessage({ commandKind: commandKinds.setEnabled, value: v });
|
||||||
|
static getEnabled = (): Promise<boolean> =>
|
||||||
|
sendMessage({ commandKind: commandKinds.getEnabled });
|
||||||
|
static translate = (toTranslate: string): Promise<Translation> =>
|
||||||
|
sendMessage({
|
||||||
|
commandKind: commandKinds.translate,
|
||||||
|
toTranslate: toTranslate,
|
||||||
|
});
|
||||||
|
static addFlashcard = (card: flashcard | Translation) => {
|
||||||
|
sendMessage({ commandKind: commandKinds.addFlashcard, card: card });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const sendMessage = (m: command) => browser.runtime.sendMessage(m);
|
||||||
|
|
||||||
|
export const enum commandKinds {
|
||||||
|
setEnabled = "setEnabled",
|
||||||
|
getEnabled = "getEnabled",
|
||||||
|
translate = "translate",
|
||||||
|
addFlashcard = "addFlashcard",
|
||||||
|
}
|
||||||
|
interface setEnabled {
|
||||||
|
commandKind: commandKinds.setEnabled;
|
||||||
|
value: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface getEnabled {
|
||||||
|
commandKind: commandKinds.getEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface translate {
|
||||||
|
commandKind: commandKinds.translate;
|
||||||
|
toTranslate: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface addFlashcard {
|
||||||
|
commandKind: commandKinds.addFlashcard;
|
||||||
|
card: Translation | flashcard;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type command = setEnabled | getEnabled | translate | addFlashcard;
|
|
@ -1,5 +1,5 @@
|
||||||
import { browser } from "webextension-polyfill-ts";
|
|
||||||
import "./custom_elements";
|
import "./custom_elements";
|
||||||
|
import Communicator from "../../communication";
|
||||||
import tippy from "tippy.js";
|
import tippy from "tippy.js";
|
||||||
import "tippy.js/dist/tippy.css";
|
import "tippy.js/dist/tippy.css";
|
||||||
import "./tippy.scss";
|
import "./tippy.scss";
|
||||||
|
@ -14,13 +14,14 @@ declare global {
|
||||||
if (window.hasRun) return;
|
if (window.hasRun) return;
|
||||||
window.hasRun = true;
|
window.hasRun = true;
|
||||||
|
|
||||||
browser.runtime.onMessage.addListener((c: command) => {
|
let con = new Communicator();
|
||||||
switch (c.commandKind) {
|
con.setEnabledCallback = (v: boolean) => {
|
||||||
case commands.setEnabled:
|
document.removeEventListener("selectionchange", onSelectionChange); //Always remove it avoid duplicate event listeners
|
||||||
setEnabled(c.value);
|
if (v) {
|
||||||
break;
|
onSelectionChange(); // Call it since selection may have changed since it was enabled
|
||||||
}
|
document.addEventListener("selectionchange", onSelectionChange);
|
||||||
});
|
} else tippyInstance.hide();
|
||||||
|
};
|
||||||
|
|
||||||
const tippyInstance = setupTippy();
|
const tippyInstance = setupTippy();
|
||||||
|
|
||||||
|
@ -32,14 +33,6 @@ declare global {
|
||||||
timeoutID = window.setTimeout(tippyInstance.show, 500);
|
timeoutID = window.setTimeout(tippyInstance.show, 500);
|
||||||
};
|
};
|
||||||
|
|
||||||
function setEnabled(v: boolean) {
|
|
||||||
document.removeEventListener("selectionchange", onSelectionChange); //Always remove it avoid duplicate event listeners
|
|
||||||
if (v) {
|
|
||||||
onSelectionChange(); // Call it since selection may have changed since it was enabled
|
|
||||||
document.addEventListener("selectionchange", onSelectionChange);
|
|
||||||
} else tippyInstance.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupTippy() {
|
function setupTippy() {
|
||||||
const translateElement = document.createElement("lingo-translate");
|
const translateElement = document.createElement("lingo-translate");
|
||||||
return tippy(document.body, {
|
return tippy(document.body, {
|
|
@ -1,4 +1,4 @@
|
||||||
import { browser } from "webextension-polyfill-ts";
|
import { Communicator } from "../../communication";
|
||||||
import contents from "./spinner.scss?raw";
|
import contents from "./spinner.scss?raw";
|
||||||
|
|
||||||
class LingoTranslate extends HTMLElement {
|
class LingoTranslate extends HTMLElement {
|
||||||
|
@ -11,10 +11,7 @@ class LingoTranslate extends HTMLElement {
|
||||||
this.shadowRoot.innerHTML = "<lingo-loading></lingo-loading>";
|
this.shadowRoot.innerHTML = "<lingo-loading></lingo-loading>";
|
||||||
const toTranslate = this.getAttribute("translate");
|
const toTranslate = this.getAttribute("translate");
|
||||||
if (toTranslate) {
|
if (toTranslate) {
|
||||||
const translation = browser.runtime.sendMessage({
|
const translation = Communicator.translate(toTranslate);
|
||||||
commandKind: commands.translate,
|
|
||||||
toTranslate: toTranslate,
|
|
||||||
});
|
|
||||||
translation.then((t: Translation) => {
|
translation.then((t: Translation) => {
|
||||||
if (t.src == this.getAttribute("translate")) {
|
if (t.src == this.getAttribute("translate")) {
|
||||||
this.shadowRoot.innerHTML = "";
|
this.shadowRoot.innerHTML = "";
|
||||||
|
@ -66,12 +63,9 @@ class LingoTranslation extends HTMLElement {
|
||||||
this.srcElm = document.createElement("span");
|
this.srcElm = document.createElement("span");
|
||||||
this.addToCollection = document.createElement("button");
|
this.addToCollection = document.createElement("button");
|
||||||
this.addToCollection.addEventListener("click", () =>
|
this.addToCollection.addEventListener("click", () =>
|
||||||
browser.runtime.sendMessage({
|
Communicator.addFlashcard({
|
||||||
commandKind: commands.addFlashcard,
|
src: this.getAttribute("src"),
|
||||||
translation: {
|
result: this.getAttribute("translation"),
|
||||||
src: this.getAttribute("src"),
|
|
||||||
translation: this.getAttribute("translation"),
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
49
src/frontend/popup/popup.ts
Normal file
49
src/frontend/popup/popup.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import { Communicator } from "../../communication";
|
||||||
|
import "./popup.scss";
|
||||||
|
|
||||||
|
const con = new Communicator();
|
||||||
|
|
||||||
|
class ExtensionToggle extends HTMLButtonElement {
|
||||||
|
isON: boolean = false;
|
||||||
|
textContent: string = "OFF";
|
||||||
|
|
||||||
|
public set on(v: boolean) {
|
||||||
|
if (this.isON == v) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isON = v;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
public get on(): boolean {
|
||||||
|
return this.isON;
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
this.textContent = this.on ? "ON" : "OFF";
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.addEventListener("click", () => {
|
||||||
|
this.on = !this.on;
|
||||||
|
|
||||||
|
const toggleEvent = new CustomEvent("extensionToggled", {
|
||||||
|
detail: this.on,
|
||||||
|
});
|
||||||
|
this.dispatchEvent(toggleEvent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
customElements.define("extension-toggle", ExtensionToggle, {
|
||||||
|
extends: "button",
|
||||||
|
});
|
||||||
|
|
||||||
|
const toggle = <ExtensionToggle>(
|
||||||
|
document.querySelector("button[is=extension-toggle]")
|
||||||
|
);
|
||||||
|
toggle.addEventListener("extensionToggled", (e: CustomEvent) =>
|
||||||
|
Communicator.setEnabled(e.detail)
|
||||||
|
);
|
||||||
|
|
||||||
|
con.setEnabledCallback = (v) => (toggle.on = v);
|
||||||
|
Communicator.getEnabled().then((v) => (toggle.on = v));
|
|
@ -1,59 +0,0 @@
|
||||||
import { browser } from "webextension-polyfill-ts";
|
|
||||||
import "./popup.scss";
|
|
||||||
class ExtensionToggle extends HTMLButtonElement {
|
|
||||||
isON: boolean = false;
|
|
||||||
textContent: string = "OFF";
|
|
||||||
|
|
||||||
public set on(v: boolean) {
|
|
||||||
if (this.isON == v) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.isON = v;
|
|
||||||
this.render();
|
|
||||||
const toggleEvent = new CustomEvent("extensionToggled", {
|
|
||||||
detail: this.on,
|
|
||||||
});
|
|
||||||
this.dispatchEvent(toggleEvent);
|
|
||||||
}
|
|
||||||
public get on(): boolean {
|
|
||||||
return this.isON;
|
|
||||||
}
|
|
||||||
|
|
||||||
public render() {
|
|
||||||
this.textContent = this.on ? "ON" : "OFF";
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.addEventListener("click", () => (this.on = !this.on));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define("extension-toggle", ExtensionToggle, {
|
|
||||||
extends: "button",
|
|
||||||
});
|
|
||||||
|
|
||||||
const toggle = <ExtensionToggle>(
|
|
||||||
document.querySelector("button[is=extension-toggle]")
|
|
||||||
);
|
|
||||||
toggle.addEventListener("extensionToggled", async (e: CustomEvent) => {
|
|
||||||
browser.runtime.sendMessage({
|
|
||||||
commandKind: commands.setEnabled,
|
|
||||||
value: e.detail,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
browser.runtime.onMessage.addListener((c: command) => {
|
|
||||||
switch (c.commandKind) {
|
|
||||||
case commands.setEnabled:
|
|
||||||
toggle.on = c.value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
console.log("Unimplemented command");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
browser.runtime
|
|
||||||
.sendMessage({ commandKind: commands.getEnabled })
|
|
||||||
.then((r) => (toggle.on = r));
|
|
27
src/types.d.ts
vendored
27
src/types.d.ts
vendored
|
@ -3,33 +3,6 @@ declare module "*?raw" {
|
||||||
export = contents;
|
export = contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare const enum commands {
|
|
||||||
setEnabled = "setEnabled",
|
|
||||||
getEnabled = "getEnabled",
|
|
||||||
translate = "translate",
|
|
||||||
addFlashcard = "addFlashcard",
|
|
||||||
}
|
|
||||||
interface setEnabled {
|
|
||||||
commandKind: commands.setEnabled;
|
|
||||||
value: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface getEnabled {
|
|
||||||
commandKind: commands.getEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface translate {
|
|
||||||
commandKind: commands.translate;
|
|
||||||
toTranslate: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface addFlashcard {
|
|
||||||
commandKind: commands.addFlashcard;
|
|
||||||
translation: Translation;
|
|
||||||
}
|
|
||||||
|
|
||||||
type command = setEnabled | getEnabled | translate | addFlashcard;
|
|
||||||
|
|
||||||
type TranslationRequest = string;
|
type TranslationRequest = string;
|
||||||
interface Translation {
|
interface Translation {
|
||||||
src: string;
|
src: string;
|
||||||
|
|
|
@ -9,10 +9,11 @@ let mode = process.env.NODE_ENV || "development";
|
||||||
|
|
||||||
let options = {
|
let options = {
|
||||||
entry: {
|
entry: {
|
||||||
popup: path.join(__dirname, "src", "popup", "popup.ts"),
|
popup: path.join(__dirname, "src", "frontend", "popup", "popup.ts"),
|
||||||
content_script: path.join(
|
content_script: path.join(
|
||||||
__dirname,
|
__dirname,
|
||||||
"src",
|
"src",
|
||||||
|
"frontend",
|
||||||
"content_script",
|
"content_script",
|
||||||
"content_script.ts"
|
"content_script.ts"
|
||||||
),
|
),
|
||||||
|
@ -73,7 +74,7 @@ let options = {
|
||||||
patterns: [{ from: path.resolve(__dirname, "src", "manifest.json") }],
|
patterns: [{ from: path.resolve(__dirname, "src", "manifest.json") }],
|
||||||
}),
|
}),
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
template: path.join(__dirname, "src", "popup", "popup.html"),
|
template: path.join(__dirname, "src", "frontend", "popup", "popup.html"),
|
||||||
filename: "popup.html",
|
filename: "popup.html",
|
||||||
chunks: ["popup"],
|
chunks: ["popup"],
|
||||||
}),
|
}),
|
||||||
|
|
Loading…
Reference in a new issue