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 { 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,12 +26,17 @@ export class Flashcards {
|
|||
};
|
||||
}
|
||||
|
||||
addTranslation(t: Translation): Promise<flashcard> {
|
||||
let card: flashcard = {
|
||||
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
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 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, {
|
|
@ -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: {
|
||||
Communicator.addFlashcard({
|
||||
src: this.getAttribute("src"),
|
||||
translation: this.getAttribute("translation"),
|
||||
},
|
||||
result: 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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -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"],
|
||||
}),
|
||||
|
|
Loading…
Reference in a new issue