Draft functionality for fetching card data from Wiktionary
This commit is contained in:
parent
ec836a8465
commit
f19f2d5601
@ -12,7 +12,7 @@ export function Navigation() {
|
||||
<NavigationItem
|
||||
text="settings"
|
||||
iconUrl="/icons/check.svg"
|
||||
href="/check"
|
||||
href="/settings"
|
||||
/>
|
||||
</nav>
|
||||
);
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -154,6 +154,15 @@ export async function addExpressionWithRelationships({
|
||||
expression_set_id,
|
||||
category_ids,
|
||||
}: 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(
|
||||
"rw",
|
||||
database.expressions,
|
||||
@ -165,9 +174,11 @@ export async function addExpressionWithRelationships({
|
||||
expression_id,
|
||||
expression_set_id,
|
||||
});
|
||||
database.expression_to_category.bulkAdd(
|
||||
category_ids.map((category_id) => ({ expression_id, category_id }))
|
||||
);
|
||||
if (category_ids.length) {
|
||||
database.expression_to_category.bulkAdd(
|
||||
category_ids.map((category_id) => ({ expression_id, category_id }))
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
@ -8,8 +8,8 @@ export function parseWiktionaryData(
|
||||
if (typeof window === "undefined") return null;
|
||||
|
||||
const parser = new DOMParser();
|
||||
const description = JSON.parse(data).parse.text["*"];
|
||||
const document = parser.parseFromString(description, "text/html");
|
||||
console.log("Parsing", data);
|
||||
const document = parser.parseFromString(data, "text/html");
|
||||
|
||||
// TODO settings-based language selection
|
||||
const header = document.getElementById("Finnish")?.parentElement;
|
||||
|
18
src/pages/settings/add-expression.tsx
Normal file
18
src/pages/settings/add-expression.tsx
Normal 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,
|
||||
});
|
3
src/pages/settings/index.tsx
Normal file
3
src/pages/settings/index.tsx
Normal file
@ -0,0 +1,3 @@
|
||||
import AddExpressionPage from "./add-expression";
|
||||
|
||||
export default AddExpressionPage;
|
@ -40,6 +40,13 @@
|
||||
line-height: 1.3rem;
|
||||
}
|
||||
|
||||
.text-error {
|
||||
color: brown;
|
||||
font-family: monospace;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* Page */
|
||||
|
||||
.page {
|
||||
@ -106,6 +113,10 @@
|
||||
background-color: bisque;
|
||||
}
|
||||
|
||||
.navigation-item:disabled {
|
||||
background-color: slategray;
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -146,6 +157,17 @@
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.content-query {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: auto;
|
||||
padding: 30px 16px;
|
||||
}
|
||||
|
||||
.content-query > label {
|
||||
margin: 10px 0px;
|
||||
}
|
||||
|
||||
/* Margins and paddings */
|
||||
|
||||
.padding-small {
|
||||
|
127
src/views/AddExpressionView/AddExpressionView.tsx
Normal file
127
src/views/AddExpressionView/AddExpressionView.tsx
Normal 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}`;
|
||||
}
|
1
src/views/AddExpressionView/index.ts
Normal file
1
src/views/AddExpressionView/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from "./AddExpressionView";
|
@ -35,6 +35,7 @@ export function ExpressionPracticeCardView({
|
||||
) : (
|
||||
<button
|
||||
className="navigation-item bottom text-navigation grow"
|
||||
type="button"
|
||||
onClick={() => setRevealed(true)}
|
||||
>
|
||||
Reveal
|
||||
|
Loading…
Reference in New Issue
Block a user