Refactoring

This commit is contained in:
a 2020-08-07 13:45:32 +02:00
parent 057bc77990
commit 9680b3574d
13 changed files with 171 additions and 149 deletions

View file

@ -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);
} }
} }

View file

@ -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
View 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;

View file

@ -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, {

View file

@ -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"),
},
}) })
); );

View 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));

View file

@ -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
View file

@ -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;

View file

@ -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"],
}), }),