still doesn't work
This commit is contained in:
parent
15cbfeb0d6
commit
4ecf26f4b2
19 changed files with 136 additions and 96 deletions
2
.prettierignore
Normal file
2
.prettierignore
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
node_modules
|
||||||
|
src-tauri
|
|
@ -4,4 +4,4 @@
|
||||||
singleQuote: false,
|
singleQuote: false,
|
||||||
trailingComma: "all",
|
trailingComma: "all",
|
||||||
printWidth: 100,
|
printWidth: 100,
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,4 +3,4 @@ export default {
|
||||||
tailwindcss: {},
|
tailwindcss: {},
|
||||||
autoprefixer: {},
|
autoprefixer: {},
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use sqlx::{Row, SqlitePool};
|
use sqlx::{sqlite::SqliteRow, Row, SqlitePool};
|
||||||
use tauri::State;
|
use tauri::State;
|
||||||
|
|
||||||
pub struct KanjiDb(pub SqlitePool);
|
pub struct KanjiDb(pub SqlitePool);
|
||||||
|
@ -6,9 +6,7 @@ pub struct KanjiDb(pub SqlitePool);
|
||||||
#[derive(Debug, Derivative, Serialize, Deserialize)]
|
#[derive(Debug, Derivative, Serialize, Deserialize)]
|
||||||
#[derivative(Default)]
|
#[derivative(Default)]
|
||||||
pub struct GetKanjiOptions {
|
pub struct GetKanjiOptions {
|
||||||
/// For looking up an existing one
|
#[serde(default)]
|
||||||
character: Option<String>,
|
|
||||||
|
|
||||||
#[derivative(Default(value = "10"))]
|
#[derivative(Default(value = "10"))]
|
||||||
how_many: u32,
|
how_many: u32,
|
||||||
}
|
}
|
||||||
|
@ -26,47 +24,68 @@ pub struct GetKanjiResult {
|
||||||
kanji: Vec<Kanji>,
|
kanji: Vec<Kanji>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_kanji(row: SqliteRow) -> Kanji {
|
||||||
|
let character = row.get("Character");
|
||||||
|
let meaning = row.get("Meaning");
|
||||||
|
let most_used_rank = row.get("MostUsedRank");
|
||||||
|
Kanji {
|
||||||
|
character,
|
||||||
|
meaning,
|
||||||
|
most_used_rank,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn get_kanji(
|
pub async fn get_kanji(
|
||||||
state: State<'_, KanjiDb>,
|
db: State<'_, KanjiDb>,
|
||||||
options: Option<GetKanjiOptions>,
|
options: Option<GetKanjiOptions>,
|
||||||
) -> Result<GetKanjiResult, ()> {
|
) -> Result<GetKanjiResult, ()> {
|
||||||
let opts = options.unwrap_or_default();
|
let opts = options.unwrap_or_default();
|
||||||
|
|
||||||
let result = sqlx::query(
|
let result = sqlx::query(
|
||||||
r#"
|
r#"
|
||||||
SELECT * FROM KanjiSet
|
SELECT * FROM KanjiSet
|
||||||
LEFT JOIN KanjiMeaningSet ON KanjiSet.ID = KanjiMeaningSet.Kanji_ID
|
LEFT JOIN KanjiMeaningSet ON KanjiSet.ID = KanjiMeaningSet.Kanji_ID
|
||||||
GROUP BY KanjiSet.ID
|
GROUP BY KanjiSet.ID
|
||||||
HAVING MostUsedRank IS NOT NULL
|
HAVING MostUsedRank IS NOT NULL
|
||||||
ORDER BY MostUsedRank
|
ORDER BY MostUsedRank
|
||||||
LIMIT ?
|
LIMIT ?
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(opts.how_many)
|
.bind(opts.how_many)
|
||||||
.fetch_all(&state.0)
|
.fetch_all(&db.0)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| ())?;
|
.map_err(|_| ())?;
|
||||||
|
|
||||||
let kanji = result
|
let kanji = result.into_iter().map(build_kanji).collect();
|
||||||
.into_iter()
|
|
||||||
.map(|row| {
|
|
||||||
let character = row.get("Character");
|
|
||||||
let meaning = row.get("Meaning");
|
|
||||||
let most_used_rank = row.get("MostUsedRank");
|
|
||||||
Kanji {
|
|
||||||
character,
|
|
||||||
meaning,
|
|
||||||
most_used_rank,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let count = sqlx::query("SELECT COUNT(*) FROM KanjiSet")
|
let count = sqlx::query("SELECT COUNT(*) FROM KanjiSet")
|
||||||
.fetch_one(&state.0)
|
.fetch_one(&db.0)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| ())?;
|
.map_err(|_| ())?;
|
||||||
let count = count.try_get(0).map_err(|_| ())?;
|
let count = count.try_get(0).map_err(|_| ())?;
|
||||||
|
|
||||||
Ok(GetKanjiResult { kanji, count })
|
Ok(GetKanjiResult { kanji, count })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn get_single_kanji(
|
||||||
|
db: State<'_, KanjiDb>,
|
||||||
|
character: String,
|
||||||
|
) -> Result<Option<Kanji>, ()> {
|
||||||
|
let result = sqlx::query(
|
||||||
|
r#"
|
||||||
|
SELECT * FROM KanjiSet
|
||||||
|
LEFT JOIN KanjiMeaningSet ON KanjiSet.ID = KanjiMeaningSet.Kanji_ID
|
||||||
|
GROUP BY KanjiSet.ID
|
||||||
|
HAVING MostUsedRank IS NOT NULL
|
||||||
|
AND Kanji.Character = ?
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(character)
|
||||||
|
.fetch_one(&db.0)
|
||||||
|
.await
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
|
||||||
|
Ok(Some(build_kanji(result)))
|
||||||
|
}
|
||||||
|
|
|
@ -41,7 +41,11 @@ async fn main() -> Result<()> {
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.manage(KanjiDb(kanji_pool))
|
.manage(KanjiDb(kanji_pool))
|
||||||
.system_tray(tray)
|
.system_tray(tray)
|
||||||
.invoke_handler(tauri::generate_handler![greet, kanji::get_kanji])
|
.invoke_handler(tauri::generate_handler![
|
||||||
|
greet,
|
||||||
|
kanji::get_kanji,
|
||||||
|
kanji::get_single_kanji
|
||||||
|
])
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.context("error while running tauri application")?;
|
.context("error while running tauri application")?;
|
||||||
|
|
||||||
|
|
|
@ -15,9 +15,8 @@ main.main {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.link-active {
|
.link-active {
|
||||||
background-color: skyblue;
|
background-color: skyblue;
|
||||||
}
|
}
|
||||||
|
|
41
src/App.tsx
41
src/App.tsx
|
@ -1,10 +1,10 @@
|
||||||
import { Link, RouterProvider, createHashRouter } from "react-router-dom";
|
import { Link, RouterProvider, createHashRouter } from "react-router-dom";
|
||||||
import KanjiPane from "./panes/KanjiPane";
|
import KanjiPane from "./panes/KanjiPane";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { ChakraProvider } from '@chakra-ui/react'
|
import { ChakraProvider } from "@chakra-ui/react";
|
||||||
import HomePane from "./panes/HomePane";
|
import HomePane from "./panes/HomePane";
|
||||||
import { createBrowserRouter } from "react-router-dom";
|
import { createBrowserRouter } from "react-router-dom";
|
||||||
import { Outlet, Route, createRoutesFromElements, useLocation } from "react-router";
|
import { Outlet, Route, createRoutesFromElements, matchPath, useLocation } from "react-router";
|
||||||
import SrsPane from "./panes/SrsPane";
|
import SrsPane from "./panes/SrsPane";
|
||||||
import VocabPane from "./panes/VocabPane";
|
import VocabPane from "./panes/VocabPane";
|
||||||
import SettingsPane from "./panes/SettingsPane";
|
import SettingsPane from "./panes/SettingsPane";
|
||||||
|
@ -20,11 +20,15 @@ function Layout() {
|
||||||
<ul className={styles.header}>
|
<ul className={styles.header}>
|
||||||
{routes.map((route) => {
|
{routes.map((route) => {
|
||||||
if (!route.title) return undefined;
|
if (!route.title) return undefined;
|
||||||
const active = route.path == location.pathname;
|
const active = matchPath({ path: route.path }, location.pathname);
|
||||||
const className = classNames(styles.link, active && styles['link-active']);
|
const className = classNames(styles.link, active && styles["link-active"]);
|
||||||
return <li key={route.path}>
|
return (
|
||||||
<Link to={route.path} className={className}>{route.title}</Link>
|
<li key={route.path}>
|
||||||
</li>
|
<Link to={route.path} className={className}>
|
||||||
|
{route.title}
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
@ -38,22 +42,19 @@ export default function App() {
|
||||||
createRoutesFromElements(
|
createRoutesFromElements(
|
||||||
<Route path="/" element={<Layout />}>
|
<Route path="/" element={<Layout />}>
|
||||||
{routes.map((route, idx) => (
|
{routes.map((route, idx) => (
|
||||||
<Route
|
<Route key={route.path} index={idx === 0} path={route.path} element={route.element} />
|
||||||
key={route.path}
|
|
||||||
index={idx === 0}
|
|
||||||
path={route.path}
|
|
||||||
element={route.element}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
</Route>
|
</Route>,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return <StrictMode>
|
return (
|
||||||
<ChakraProvider>
|
<StrictMode>
|
||||||
<RouterProvider router={router} />
|
<ChakraProvider>
|
||||||
</ChakraProvider>
|
<RouterProvider router={router} />
|
||||||
</StrictMode>
|
</ChakraProvider>
|
||||||
|
</StrictMode>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
|
|
|
@ -7,4 +7,4 @@ $kanjiDisplaySize: 80px;
|
||||||
font-size: $kanjiDisplaySize * 0.8;
|
font-size: $kanjiDisplaySize * 0.8;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { invoke } from "@tauri-apps/api/tauri";
|
import { invoke } from "@tauri-apps/api/tauri";
|
||||||
import { GetKanjiResult } from "../panes/KanjiPane";
|
import { GetKanjiResult } from "../panes/KanjiPane";
|
||||||
import { Kanji } from "../types/Kanji"
|
import { Kanji } from "../types/Kanji";
|
||||||
import styles from "./KanjiDisplay.module.scss"
|
import styles from "./KanjiDisplay.module.scss";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
|
||||||
interface KanjiDisplayProps {
|
interface KanjiDisplayProps {
|
||||||
|
@ -9,11 +9,23 @@ interface KanjiDisplayProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function KanjiDisplay({ kanjiCharacter }: KanjiDisplayProps) {
|
export default function KanjiDisplay({ kanjiCharacter }: KanjiDisplayProps) {
|
||||||
const { data, error, isLoading } = useSWR(["get_kanji", kanjiCharacter], invoke<GetKanjiResult>);
|
const { data, error, isLoading } = useSWR(["get_single_kanji", kanjiCharacter], ([command, character]) => invoke<GetKanjiResult>(command, { character }));
|
||||||
|
|
||||||
return <>
|
return (
|
||||||
<div className={styles.display}>
|
<>
|
||||||
{kanjiCharacter}
|
<div className={styles.display}>{kanjiCharacter}</div>
|
||||||
</div>
|
|
||||||
</>
|
{JSON.stringify(isLoading)}
|
||||||
}
|
|
||||||
|
<p>
|
||||||
|
data:
|
||||||
|
{JSON.stringify(data)}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
error:
|
||||||
|
{JSON.stringify(error)}
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -7,5 +7,5 @@ import "./styles.css";
|
||||||
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</React.StrictMode>
|
</React.StrictMode>,
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
export default function HomePane() {
|
export default function HomePane() {
|
||||||
return <>
|
return <>hellosu</>;
|
||||||
hellosu
|
}
|
||||||
</>
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,4 +7,4 @@
|
||||||
.kanji-link {
|
.kanji-link {
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
border: 1px solid lightgray;
|
border: 1px solid lightgray;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,24 +14,30 @@ export interface GetKanjiResult {
|
||||||
|
|
||||||
interface KanjiListProps {
|
interface KanjiListProps {
|
||||||
data: GetKanjiResult;
|
data: GetKanjiResult;
|
||||||
selectedCharacter: string;
|
selectedCharacter?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function KanjiList({ data, selectedCharacter }: KanjiListProps) {
|
function KanjiList({ data, selectedCharacter }: KanjiListProps) {
|
||||||
return <>
|
return (
|
||||||
Displaying {data.kanji.length} of {data.count} results.
|
<>
|
||||||
|
Displaying {data.kanji.length} of {data.count} results.
|
||||||
{data.kanji.map(kanji => <Link key={kanji.character} className={styles['kanji-link']} to={`/kanji/${kanji.character}`}>
|
{data.kanji.map((kanji) => (
|
||||||
<Grid
|
<Link
|
||||||
templateRows='repeat(2, 1fr)'
|
key={kanji.character}
|
||||||
templateColumns='1fr 3fr'>
|
className={styles["kanji-link"]}
|
||||||
<GridItem rowSpan={2} style={{ fontSize: '24px', textAlign: 'center' }}>{kanji.character}</GridItem>
|
to={`/kanji/${kanji.character}`}
|
||||||
<GridItem>{kanji.meaning}</GridItem>
|
>
|
||||||
<GridItem>#{kanji.most_used_rank} most used</GridItem>
|
<Grid templateRows="repeat(2, 1fr)" templateColumns="1fr 3fr">
|
||||||
</Grid>
|
<GridItem rowSpan={2} style={{ fontSize: "24px", textAlign: "center" }}>
|
||||||
</Link>)}
|
{kanji.character}
|
||||||
</>
|
</GridItem>
|
||||||
|
<GridItem>{kanji.meaning}</GridItem>
|
||||||
|
<GridItem>#{kanji.most_used_rank} most used</GridItem>
|
||||||
|
</Grid>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function KanjiPane() {
|
export default function KanjiPane() {
|
||||||
|
@ -43,15 +49,15 @@ export default function KanjiPane() {
|
||||||
{JSON.stringify(error)}
|
{JSON.stringify(error)}
|
||||||
{JSON.stringify(selectedKanji)}
|
{JSON.stringify(selectedKanji)}
|
||||||
|
|
||||||
<Stack spacing={7} direction='row'>
|
<Stack spacing={7} direction="row">
|
||||||
<Box p={2} className={styles['kanji-list']}>
|
<Box p={2} className={styles["kanji-list"]}>
|
||||||
{data && <KanjiList data={data} selectedCharacter={selectedKanji} />}
|
{data && <KanjiList data={data} selectedCharacter={selectedKanji} />}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box p={5}>
|
<Box p={5}>
|
||||||
{selectedKanji ? <KanjiDisplay kanjiCharacter={selectedKanji} /> : "nothing selected"}
|
{selectedKanji ? <KanjiDisplay kanjiCharacter={selectedKanji} /> : "nothing selected"}
|
||||||
</Box>
|
</Box>
|
||||||
</Stack >
|
</Stack>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
export default function SettingsPane() {
|
export default function SettingsPane() {
|
||||||
return <></>
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
export default function SrsPane() {
|
export default function SrsPane() {
|
||||||
return <></>
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
export default function VocabPane() {
|
export default function VocabPane() {
|
||||||
return <></>
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
|
@ -2,4 +2,4 @@ export interface Kanji {
|
||||||
character: string;
|
character: string;
|
||||||
meaning: string;
|
meaning: string;
|
||||||
most_used_rank: number;
|
most_used_rank: number;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
export default {
|
export default {
|
||||||
content: ["./src/**/*.{html,js,jsx,ts,tsx}",
|
content: ["./src/**/*.{html,js,jsx,ts,tsx}", "./node_modules/flowbite/**/*.js"],
|
||||||
"./node_modules/flowbite/**/*.js"],
|
|
||||||
|
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
|
|
||||||
plugins: [require("flowbite/plugin")],
|
plugins: [require("flowbite/plugin")],
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue