diff --git a/package.json b/package.json index edee9ee..39fcaef 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,8 @@ "description": "Lingo is a small browser extension to help you learn languages", "main": "webpack.config.js", "dependencies": { + "@sveltejs/svelte-virtual-list": "^3.0.1", + "svelte-feather-icons": "^3.2.2", "svelte-select": "^3.11.0", "tippy.js": "^6.2.5" }, @@ -52,6 +54,9 @@ } }, "lint-staged": { - "*/**": ["eslint --fix ","prettier --write"] + "*/**": [ + "eslint --fix ", + "prettier --write" + ] } } diff --git a/src/background/background.ts b/src/background/background.ts index 7adb803..bb65c86 100644 --- a/src/background/background.ts +++ b/src/background/background.ts @@ -7,9 +7,13 @@ import content_script from "../frontend/content_script/content_script.ts?file"; const con = new BackgroundMessenger(); const scraper = new GTranslateScraper(); -con.addMessageListener("translate", async (toTranslate, sender) => - scraper.translate(toTranslate, await getCurrentLanguages(), sender!.tab?.id) -); +con.addMessageListener("translate", async (toTranslate, sender) => { + return scraper.translate( + toTranslate, + await getCurrentLanguages(), + sender!.tab?.id + ); +}); con.addMessageListener("getLanguages", () => scraper.languages); diff --git a/src/background/database.ts b/src/background/database.ts index 617ae61..cb597ee 100644 --- a/src/background/database.ts +++ b/src/background/database.ts @@ -1,7 +1,12 @@ import { newPromise } from "../utils"; +type flashcardChangedCallback = ( + card: Flashcard, + change: "added" | "removed" +) => void; export class Flashcards { db: Promise; + private callbacks: flashcardChangedCallback[] = []; constructor() { const [promise, resolve, reject] = newPromise(); @@ -28,8 +33,8 @@ export class Flashcards { } async addFlashcard(t: Translation | Flashcard): Promise { - let card: Flashcard; - if ("dateAdded" in t && "exported" in t) card = t; + let card: Omit & { id?: IDBValidKey }; + if ("id" in t) card = t; else { card = { ...t, @@ -41,11 +46,16 @@ export class Flashcards { const req = (await this.db) .transaction(["flashcards"], "readwrite") .objectStore("flashcards") - .add(card); + .put(card); + const [promise, resolve, reject] = newPromise(); req.onsuccess = () => { - card.id = req.result; - resolve(card); + const newCard = { + id: req.result, + ...card, + }; + resolve(newCard); + this.callbacks.forEach((c) => c(newCard, "added")); }; req.onerror = () => reject(req.error); return promise; @@ -59,7 +69,21 @@ export class Flashcards { .delete(card.id); const [promise, resolve, reject] = newPromise(); - req.onsuccess = () => resolve(); + req.onsuccess = () => { + resolve(); + this.callbacks.forEach((c) => c(card, "removed")); + }; + req.onerror = () => reject(req.error); + return promise; + } + async getCard(cardID: IDBValidKey): Promise { + const req = (await this.db) + .transaction(["flashcards"], "readonly") + .objectStore("flashcards") + .get(cardID); + + const [promise, resolve, reject] = newPromise(); + req.onsuccess = () => resolve(req.result); req.onerror = () => reject(req.error); return promise; } @@ -75,4 +99,48 @@ export class Flashcards { req.onerror = () => reject(req.error); return promise; } + + async getAllKeys(): Promise { + const req = (await this.db) + .transaction(["flashcards"], "readonly") + .objectStore("flashcards") + .getAllKeys(); + + const [promise, resolve, reject] = newPromise(); + req.onsuccess = () => resolve(req.result); + req.onerror = () => reject(req.error); + return promise; + } + + async getCardFromTransRequest(src: string, language: LanguagePair) { + const [promise, resolve, reject] = newPromise(); + + const req = (await this.db) + .transaction(["flashcards"], "readonly") + .objectStore("flashcards") + .index("src") + .openCursor(IDBKeyRange.only(src)); + req.onsuccess = () => { + const cursor = req.result; + const res = cursor?.value as Flashcard | undefined; + + if ( + res?.languages?.srcLang?.code == language.srcLang.code && + res?.languages?.dstLang?.code == language.dstLang.code + ) + resolve(res); + else if (cursor) cursor.continue(); + else resolve(undefined); + }; + req.onerror = reject; + return promise; + } + addChangeCallback(callback: flashcardChangedCallback) { + this.callbacks.push(callback); + console.log(this.callbacks); + } + + removeChangeCallback(callback: flashcardChangedCallback) { + this.callbacks = this.callbacks.filter((f) => f != callback); + } } diff --git a/src/background_frontend_commands.ts b/src/background_frontend_commands.ts index 7bb70f9..c3e9056 100644 --- a/src/background_frontend_commands.ts +++ b/src/background_frontend_commands.ts @@ -30,7 +30,7 @@ interface getLanguages extends commandFunction { interface translate extends commandFunction { args: string; - return: Translation; + return: Promise; } interface getCurrentLanguages extends commandFunction { @@ -80,7 +80,7 @@ export class FrontendMessenger extends Communicator< runCommand( command: K, args: BackgroundCommands[K]["args"] - ): BackgroundCommands[K]["return"] { + ): Promise { const message = this.getCommandMessage(command, args); return browser.runtime.sendMessage(message); } diff --git a/src/file.d.ts b/src/file.d.ts index ecba911..67fcda8 100644 --- a/src/file.d.ts +++ b/src/file.d.ts @@ -2,3 +2,8 @@ declare module "*?file" { const file: string; export = file; } + +declare module "*.svg" { + const file: string; + export = file; +} diff --git a/src/frontend/content_script/Translated.svelte b/src/frontend/content_script/Translated.svelte index f875e6c..ed1a730 100644 --- a/src/frontend/content_script/Translated.svelte +++ b/src/frontend/content_script/Translated.svelte @@ -3,9 +3,10 @@ import type { FrontendMessenger } from "../../background_frontend_commands"; let connection = getContext("connection") as FrontendMessenger; - export let trans: Translation; + export let trans: Translation | Flashcard; let card: Flashcard | undefined; + $: if ("id" in trans) card = trans; const addFlashcard = async () => { card = await connection.runCommand("addFlashcard", trans); }; @@ -26,7 +27,7 @@ card {:else} -