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 { GTranslateScraper } from "./gtranslate_scraper";
import { Flashcards } from "./database";
import { Communicator, command, commandKinds } from "../communication";
const scraper = new GTranslateScraper();
const db = new Flashcards();
let com = new Communicator();
console.log(db);
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];
};
const isEnabled = async (tabID: number): Promise<boolean> =>
(await browser.sessions.getTabValue(tabID, "lingoEnabled")) || false;
const setEnabled = async (tabID: number, value: boolean) =>
const isEnabledSession = async (tabID: number): Promise<boolean> =>
(await browser.sessions.getTabValue(tabID, "lingoEnabled")) ?? false;
const setEnabledSession = async (tabID: number, value: boolean) =>
await browser.sessions.setTabValue(tabID, "lingoEnabled", value);
const injectScript = async (tabID: number, enabled?: boolean) => {
await browser.tabs.executeScript(tabID, {
file: "/content_script.bundle.js",
});
if (!enabled) enabled = await isEnabled(tabID);
if (!enabled) enabled = await isEnabledSession(tabID);
browser.tabs.sendMessage(tabID, {
commandKind: commands.setEnabled,
commandKind: commandKinds.setEnabled,
value: enabled,
});
};
const setEnabledCommand = async (v: boolean, s: Runtime.MessageSender) => {
com.setEnabledCallback = async (v: boolean, s: Runtime.MessageSender) => {
const tab = await getTab(s);
injectScript(tab.id);
await setEnabled(tab.id, v);
};
const getEnabledCommand = async (
s: Runtime.MessageSender
): Promise<boolean> => {
const tab = await getTab(s);
return isEnabled(tab.id);
await setEnabledSession(tab.id, v);
};
browser.runtime.onMessage.addListener(
(c: command, s: Runtime.MessageSender): Promise<any> => {
switch (c.commandKind) {
case commands.setEnabled:
return setEnabledCommand(c.value, s);
case commands.getEnabled:
return getEnabledCommand(s);
case commands.translate:
return scraper.translate(c.toTranslate);
case commands.addFlashcard:
return db.addTranslation(c.translation);
}
}
);
com.getEnabledCallback = async (s: Runtime.MessageSender): Promise<boolean> => {
const tab = await getTab(s);
return isEnabledSession(tab.id);
};
com.translateCallback = (toTranslate) => scraper.translate(toTranslate);
com.addFlashcardCallback = (card) => db.addTranslation(card);
browser.tabs.onUpdated.addListener(async (tabID, changeInfo) => {
if (changeInfo.status == "complete") {
if (await isEnabled(tabID)) {
if (await isEnabledSession(tabID)) {
injectScript(tabID);
}
}

View file

@ -26,12 +26,17 @@ export class Flashcards {
};
}
addTranslation(t: Translation): Promise<flashcard> {
let card: flashcard = {
...t,
dateAdded: new Date(),
exported: false,
};
addTranslation(t: Translation | flashcard): Promise<flashcard> {
let card: flashcard;
if ("dateAdded" in t && "exported" in t) card = t;
else {
card = {
...t,
dateAdded: new Date(),
exported: false,
};
}
let req = this.db
.transaction(["flashcards"], "readwrite")
.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 Communicator from "../../communication";
import tippy from "tippy.js";
import "tippy.js/dist/tippy.css";
import "./tippy.scss";
@ -14,13 +14,14 @@ declare global {
if (window.hasRun) return;
window.hasRun = true;
browser.runtime.onMessage.addListener((c: command) => {
switch (c.commandKind) {
case commands.setEnabled:
setEnabled(c.value);
break;
}
});
let con = new Communicator();
con.setEnabledCallback = (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();
};
const tippyInstance = setupTippy();
@ -32,14 +33,6 @@ declare global {
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() {
const translateElement = document.createElement("lingo-translate");
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";
class LingoTranslate extends HTMLElement {
@ -11,10 +11,7 @@ class LingoTranslate extends HTMLElement {
this.shadowRoot.innerHTML = "<lingo-loading></lingo-loading>";
const toTranslate = this.getAttribute("translate");
if (toTranslate) {
const translation = browser.runtime.sendMessage({
commandKind: commands.translate,
toTranslate: toTranslate,
});
const translation = Communicator.translate(toTranslate);
translation.then((t: Translation) => {
if (t.src == this.getAttribute("translate")) {
this.shadowRoot.innerHTML = "";
@ -66,12 +63,9 @@ class LingoTranslation extends HTMLElement {
this.srcElm = document.createElement("span");
this.addToCollection = document.createElement("button");
this.addToCollection.addEventListener("click", () =>
browser.runtime.sendMessage({
commandKind: commands.addFlashcard,
translation: {
src: this.getAttribute("src"),
translation: this.getAttribute("translation"),
},
Communicator.addFlashcard({
src: this.getAttribute("src"),
result: 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;
}
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;
interface Translation {
src: string;

View file

@ -9,10 +9,11 @@ let mode = process.env.NODE_ENV || "development";
let options = {
entry: {
popup: path.join(__dirname, "src", "popup", "popup.ts"),
popup: path.join(__dirname, "src", "frontend", "popup", "popup.ts"),
content_script: path.join(
__dirname,
"src",
"frontend",
"content_script",
"content_script.ts"
),
@ -73,7 +74,7 @@ let options = {
patterns: [{ from: path.resolve(__dirname, "src", "manifest.json") }],
}),
new HtmlWebpackPlugin({
template: path.join(__dirname, "src", "popup", "popup.html"),
template: path.join(__dirname, "src", "frontend", "popup", "popup.html"),
filename: "popup.html",
chunks: ["popup"],
}),