Options page

This commit is contained in:
a 2020-08-20 15:40:04 +02:00
parent 5451b3713e
commit 9ede15c7b3
11 changed files with 119 additions and 39 deletions

View file

@ -4,6 +4,8 @@
"description": "Lingo is a small browser extension to help you learn languages", "description": "Lingo is a small browser extension to help you learn languages",
"main": "webpack.config.js", "main": "webpack.config.js",
"dependencies": { "dependencies": {
"@sveltejs/svelte-virtual-list": "^3.0.1",
"svelte-feather-icons": "^3.2.2",
"svelte-select": "^3.11.0", "svelte-select": "^3.11.0",
"tippy.js": "^6.2.5" "tippy.js": "^6.2.5"
}, },
@ -52,6 +54,9 @@
} }
}, },
"lint-staged": { "lint-staged": {
"*/**": ["eslint --fix ","prettier --write"] "*/**": [
"eslint --fix ",
"prettier --write"
]
} }
} }

View file

@ -7,9 +7,13 @@ import content_script from "../frontend/content_script/content_script.ts?file";
const con = new BackgroundMessenger(); const con = new BackgroundMessenger();
const scraper = new GTranslateScraper(); const scraper = new GTranslateScraper();
con.addMessageListener("translate", async (toTranslate, sender) => con.addMessageListener("translate", async (toTranslate, sender) => {
scraper.translate(toTranslate, await getCurrentLanguages(), sender!.tab?.id) return scraper.translate(
); toTranslate,
await getCurrentLanguages(),
sender!.tab?.id
);
});
con.addMessageListener("getLanguages", () => scraper.languages); con.addMessageListener("getLanguages", () => scraper.languages);

View file

@ -1,7 +1,12 @@
import { newPromise } from "../utils"; import { newPromise } from "../utils";
type flashcardChangedCallback = (
card: Flashcard,
change: "added" | "removed"
) => void;
export class Flashcards { export class Flashcards {
db: Promise<IDBDatabase>; db: Promise<IDBDatabase>;
private callbacks: flashcardChangedCallback[] = [];
constructor() { constructor() {
const [promise, resolve, reject] = newPromise<IDBDatabase>(); const [promise, resolve, reject] = newPromise<IDBDatabase>();
@ -28,8 +33,8 @@ export class Flashcards {
} }
async addFlashcard(t: Translation | Flashcard): Promise<Flashcard> { async addFlashcard(t: Translation | Flashcard): Promise<Flashcard> {
let card: Flashcard; let card: Omit<Flashcard, "id"> & { id?: IDBValidKey };
if ("dateAdded" in t && "exported" in t) card = t; if ("id" in t) card = t;
else { else {
card = { card = {
...t, ...t,
@ -41,11 +46,16 @@ export class Flashcards {
const req = (await this.db) const req = (await this.db)
.transaction(["flashcards"], "readwrite") .transaction(["flashcards"], "readwrite")
.objectStore("flashcards") .objectStore("flashcards")
.add(card); .put(card);
const [promise, resolve, reject] = newPromise<Flashcard>(); const [promise, resolve, reject] = newPromise<Flashcard>();
req.onsuccess = () => { req.onsuccess = () => {
card.id = req.result; const newCard = {
resolve(card); id: req.result,
...card,
};
resolve(newCard);
this.callbacks.forEach((c) => c(newCard, "added"));
}; };
req.onerror = () => reject(req.error); req.onerror = () => reject(req.error);
return promise; return promise;
@ -59,7 +69,21 @@ export class Flashcards {
.delete(card.id); .delete(card.id);
const [promise, resolve, reject] = newPromise<void>(); const [promise, resolve, reject] = newPromise<void>();
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<Flashcard | undefined> {
const req = (await this.db)
.transaction(["flashcards"], "readonly")
.objectStore("flashcards")
.get(cardID);
const [promise, resolve, reject] = newPromise<Flashcard | undefined>();
req.onsuccess = () => resolve(req.result);
req.onerror = () => reject(req.error); req.onerror = () => reject(req.error);
return promise; return promise;
} }
@ -75,4 +99,48 @@ export class Flashcards {
req.onerror = () => reject(req.error); req.onerror = () => reject(req.error);
return promise; return promise;
} }
async getAllKeys(): Promise<IDBValidKey[]> {
const req = (await this.db)
.transaction(["flashcards"], "readonly")
.objectStore("flashcards")
.getAllKeys();
const [promise, resolve, reject] = newPromise<IDBValidKey[]>();
req.onsuccess = () => resolve(req.result);
req.onerror = () => reject(req.error);
return promise;
}
async getCardFromTransRequest(src: string, language: LanguagePair) {
const [promise, resolve, reject] = newPromise<Flashcard | undefined>();
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);
}
} }

View file

@ -30,7 +30,7 @@ interface getLanguages extends commandFunction {
interface translate extends commandFunction { interface translate extends commandFunction {
args: string; args: string;
return: Translation; return: Promise<Translation | Flashcard>;
} }
interface getCurrentLanguages extends commandFunction { interface getCurrentLanguages extends commandFunction {
@ -80,7 +80,7 @@ export class FrontendMessenger extends Communicator<
runCommand<K extends keyof BackgroundCommands>( runCommand<K extends keyof BackgroundCommands>(
command: K, command: K,
args: BackgroundCommands[K]["args"] args: BackgroundCommands[K]["args"]
): BackgroundCommands[K]["return"] { ): Promise<BackgroundCommands[K]["return"]> {
const message = this.getCommandMessage(command, args); const message = this.getCommandMessage(command, args);
return browser.runtime.sendMessage(message); return browser.runtime.sendMessage(message);
} }

5
src/file.d.ts vendored
View file

@ -2,3 +2,8 @@ declare module "*?file" {
const file: string; const file: string;
export = file; export = file;
} }
declare module "*.svg" {
const file: string;
export = file;
}

View file

@ -3,9 +3,10 @@
import type { FrontendMessenger } from "../../background_frontend_commands"; import type { FrontendMessenger } from "../../background_frontend_commands";
let connection = getContext("connection") as FrontendMessenger; let connection = getContext("connection") as FrontendMessenger;
export let trans: Translation; export let trans: Translation | Flashcard;
let card: Flashcard | undefined; let card: Flashcard | undefined;
$: if ("id" in trans) card = trans;
const addFlashcard = async () => { const addFlashcard = async () => {
card = await connection.runCommand("addFlashcard", trans); card = await connection.runCommand("addFlashcard", trans);
}; };
@ -26,7 +27,7 @@
card card
</button> </button>
{:else} {:else}
<button on:click={removeFlashcard} class="enabled"> <button on:click={removeFlashcard} class="remove">
Remove Remove
<br /> <br />
card card
@ -37,6 +38,7 @@
<style lang="scss"> <style lang="scss">
@use "../color_scheme"; @use "../color_scheme";
@use "../common";
.translatedContainer { .translatedContainer {
display: flex; display: flex;
@ -48,23 +50,16 @@
} }
button { button {
@include common.button;
font-size: 0.8em; font-size: 0.8em;
border: 0; border: 0;
min-width: 9ch; min-width: 9ch;
border-radius: 2px; border-radius: 2px;
min-height: 100%; min-height: 100%;
cursor: pointer; @include common.button_green;
background-color: var(--green-1); &.remove {
&:hover { @include common.button_red;
background-color: var(--green-2);
}
&.enabled {
background-color: var(--red-1);
&:hover {
background-color: var(--red-2);
}
} }
} }
</style> </style>

View file

@ -33,7 +33,8 @@
</div> </div>
<style lang="scss"> <style lang="scss">
@use "../color_scheme.scss"; @use "../color_scheme";
@use "../common";
#container { #container {
display: flex; display: flex;
@ -50,6 +51,7 @@
display: none; display: none;
} }
label { label {
@include common.button;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -61,17 +63,9 @@
font-weight: 500; font-weight: 500;
cursor: pointer; @include common.button_green;
background-color: var(--green-1);
&:hover {
background-color: var(--green-2);
}
&.enabled { &.enabled {
background-color: var(--red-1); @include common.button_red;
&:hover {
background-color: var(--red-2);
}
} }
} }
</style> </style>

View file

@ -8,7 +8,7 @@ declare global {
} }
interface Flashcard { interface Flashcard {
id?: IDBValidKey; id: IDBValidKey;
src: string; src: string;
result: string; result: string;
dateAdded: Date; dateAdded: Date;

View file

@ -1,5 +1,5 @@
type resolveCallback<T> = (value: T) => void; type resolveCallback<T> = (value: T) => void;
type rejectCallback = (reason: unknown) => void; type rejectCallback = (reason?: unknown) => void;
export const newPromise = <T>(): [ export const newPromise = <T>(): [
Promise<T>, Promise<T>,

View file

@ -54,7 +54,6 @@ let options = {
}, },
{ {
test: /\.(html|svelte)$/, test: /\.(html|svelte)$/,
exclude: /node_modules/,
use: { use: {
loader: "svelte-loader", loader: "svelte-loader",
options: { options: {

View file

@ -112,6 +112,11 @@
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==
"@sveltejs/svelte-virtual-list@^3.0.1":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@sveltejs/svelte-virtual-list/-/svelte-virtual-list-3.0.1.tgz#3784d6426508836b4471bba8ba293430e73717d8"
integrity sha512-aF9TptS7NKKS7/TqpsxQBSDJ9Q0XBYzBehCeIC5DzdMEgrJZpIYao9LRLnyyo6SVodpapm2B7FE/Lj+FSA5/SQ==
"@szmarczak/http-timer@^1.1.2": "@szmarczak/http-timer@^1.1.2":
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421"
@ -6195,6 +6200,11 @@ svelte-dev-helper@^1.1.9:
resolved "https://registry.yarnpkg.com/svelte-dev-helper/-/svelte-dev-helper-1.1.9.tgz#7d187db5c6cdbbd64d75a32f91b8998bde3273c3" resolved "https://registry.yarnpkg.com/svelte-dev-helper/-/svelte-dev-helper-1.1.9.tgz#7d187db5c6cdbbd64d75a32f91b8998bde3273c3"
integrity sha1-fRh9tcbNu9ZNdaMvkbiZi94yc8M= integrity sha1-fRh9tcbNu9ZNdaMvkbiZi94yc8M=
svelte-feather-icons@^3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/svelte-feather-icons/-/svelte-feather-icons-3.2.2.tgz#fdb4ecc7ac9127442d06d8bbeee52d3be0491834"
integrity sha512-GY86mGZYjZCRRnY2t5caiY+2gzSWrYvG8dUjhnOi6YlaWVZEAzDOBuTDiZ1abOz2mM9fCpdpbgj1JZajjzEEAw==
svelte-language-server@*: svelte-language-server@*:
version "0.10.90" version "0.10.90"
resolved "https://registry.yarnpkg.com/svelte-language-server/-/svelte-language-server-0.10.90.tgz#888d1371f0ea8ba2d9bf56da8f086ce0de1df60f" resolved "https://registry.yarnpkg.com/svelte-language-server/-/svelte-language-server-0.10.90.tgz#888d1371f0ea8ba2d9bf56da8f086ce0de1df60f"