Draft functionality for fetching card data from Wiktionary

This commit is contained in:
Thiago Chaves 2022-07-25 00:28:53 +03:00
parent ec836a8465
commit f19f2d5601
11 changed files with 191 additions and 8 deletions

View File

@ -12,7 +12,7 @@ export function Navigation() {
<NavigationItem <NavigationItem
text="settings" text="settings"
iconUrl="/icons/check.svg" iconUrl="/icons/check.svg"
href="/check" href="/settings"
/> />
</nav> </nav>
); );

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -154,6 +154,15 @@ export async function addExpressionWithRelationships({
expression_set_id, expression_set_id,
category_ids, category_ids,
}: addExpressionWithRelationshipsParams) { }: addExpressionWithRelationshipsParams) {
const existing = await database.expressions
.where("prompt")
.equals(expression.prompt)
.first();
if (existing) {
const error_message = `Expression ${expression.prompt} already exists in database`;
console.error(error_message);
throw new Error(error_message);
}
return await database.transaction( return await database.transaction(
"rw", "rw",
database.expressions, database.expressions,
@ -165,9 +174,11 @@ export async function addExpressionWithRelationships({
expression_id, expression_id,
expression_set_id, expression_set_id,
}); });
if (category_ids.length) {
database.expression_to_category.bulkAdd( database.expression_to_category.bulkAdd(
category_ids.map((category_id) => ({ expression_id, category_id })) category_ids.map((category_id) => ({ expression_id, category_id }))
); );
}
}); });
} }
); );

View File

@ -8,8 +8,8 @@ export function parseWiktionaryData(
if (typeof window === "undefined") return null; if (typeof window === "undefined") return null;
const parser = new DOMParser(); const parser = new DOMParser();
const description = JSON.parse(data).parse.text["*"]; console.log("Parsing", data);
const document = parser.parseFromString(description, "text/html"); const document = parser.parseFromString(data, "text/html");
// TODO settings-based language selection // TODO settings-based language selection
const header = document.getElementById("Finnish")?.parentElement; const header = document.getElementById("Finnish")?.parentElement;

View File

@ -0,0 +1,18 @@
import type { NextPage } from "next";
import dynamic from "next/dynamic";
import { Page } from "../../components";
import { AddExpressionView } from "../../views/AddExpressionView";
const PageTitle = "Settings - Add expression";
const AddExpressionPage: NextPage = () => {
return (
<Page title={PageTitle}>
<AddExpressionView />
</Page>
);
};
export default dynamic(() => Promise.resolve(AddExpressionPage), {
ssr: false,
});

View File

@ -0,0 +1,3 @@
import AddExpressionPage from "./add-expression";
export default AddExpressionPage;

View File

@ -40,6 +40,13 @@
line-height: 1.3rem; line-height: 1.3rem;
} }
.text-error {
color: brown;
font-family: monospace;
font-size: 11px;
text-transform: uppercase;
}
/* Page */ /* Page */
.page { .page {
@ -106,6 +113,10 @@
background-color: bisque; background-color: bisque;
} }
.navigation-item:disabled {
background-color: slategray;
}
.icon-button { .icon-button {
display: flex; display: flex;
align-items: center; align-items: center;
@ -146,6 +157,17 @@
margin-top: 16px; margin-top: 16px;
} }
.content-query {
display: flex;
flex-direction: column;
margin: auto;
padding: 30px 16px;
}
.content-query > label {
margin: 10px 0px;
}
/* Margins and paddings */ /* Margins and paddings */
.padding-small { .padding-small {

View File

@ -0,0 +1,127 @@
import { useState } from "react";
import URL from "url";
import { ExpressionCard } from "../../components";
import { ExpressionDescription } from "../../components/ExpressionDescription";
import {
addExpression,
addExpressionWithRelationships,
Expression,
} from "../../model";
import { parseWiktionaryData } from "../../model/parseWiktionaryData";
export function AddExpressionView() {
const [prompt, setPrompt] = useState("");
const [expression, setExpression] = useState<Expression | null>(null);
const [submitStatus, setSubmitStatus] = useState<string | undefined>(
undefined
);
const [error, setError] = useState<any>(undefined);
return (
<div className="page-with-bottom-navigation">
<section className="padding-small scroll">
<div className="padding-small">
{error ? <p className="text-error">{error.message}</p> : null}
{submitStatus ? <p className="text-meta">{submitStatus}</p> : null}
</div>
{expression ? (
<ExpressionCard
prompt={expression.prompt}
categories={[]}
description={<ExpressionDescription expression={expression} />}
show_description
/>
) : (
<div className="content-query">
<label htmlFor="query">Word: </label>
<input
type="text"
id="query"
name="query"
value={prompt}
placeholder="Type card prompt here"
onChange={(event) => setPrompt(event.target.value)}
/>
</div>
)}
</section>
<section className="navigation-bottom">
{!expression ? (
<button
className="navigation-item bottom text-navigation grow"
type="submit"
disabled={prompt === ""}
onClick={async () => {
try {
const url = URL.format({
protocol: "https",
hostname: "en.wiktionary.org",
pathname: "/w/api.php",
query: {
action: "parse",
format: "json",
page: prompt,
prop: "text",
origin: "*",
},
});
const result = await fetch(url);
const data = await result.json();
setExpression(
parseWiktionaryData(prompt, data.parse?.text?.["*"] || "")
);
} catch (error) {
setError(error);
}
}}
>
Fetch Wiktionary data
</button>
) : (
<>
<button
className="navigation-item bottom text-navigation grow"
type="button"
onClick={() => {
setExpression(null);
setSubmitStatus(undefined);
setError(undefined);
}}
>
Discard card
</button>
<button
className="navigation-item bottom text-navigation grow"
type="button"
onClick={async () => {
if (!expression) {
console.error("Attempted to add expression but none exists");
return;
}
try {
await addExpressionWithRelationships({
expression,
expression_set_id: 1,
category_ids: [],
});
setExpression(null);
setSubmitStatus(`Card "${expression.prompt}" saved`);
setError(undefined);
} catch (error) {
setSubmitStatus(undefined);
setError(error);
}
}}
>
Save card
</button>
</>
)}
</section>
</div>
);
}
function buildUrl(query: string) {
return `https://en.wiktionary.org/w/api.php?action=parse&format=json&prop=text&page=${query}`;
}

View File

@ -0,0 +1 @@
export * from "./AddExpressionView";

View File

@ -35,6 +35,7 @@ export function ExpressionPracticeCardView({
) : ( ) : (
<button <button
className="navigation-item bottom text-navigation grow" className="navigation-item bottom text-navigation grow"
type="button"
onClick={() => setRevealed(true)} onClick={() => setRevealed(true)}
> >
Reveal Reveal