Skip to content

Commit

Permalink
refactor: language and new book forms
Browse files Browse the repository at this point in the history
  • Loading branch information
webofpies committed Feb 6, 2025
1 parent b01bcc8 commit 8493572
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 148 deletions.
86 changes: 5 additions & 81 deletions frontend/src/features/book/components/NewBookForm/NewBookForm.jsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
import { useNavigate, useSearchParams } from "react-router-dom";
import { useMutation, useQuery } from "@tanstack/react-query";
import { useForm } from "@mantine/form";
import { notifications } from "@mantine/notifications";
import {
ActionIcon,
Button,
Fieldset,
FileInput,
Group,
NumberInput,
Paper,
Popover,
rem,
Select,
Stack,
TagsInput,
Text,
Textarea,
TextInput,
} from "@mantine/core";
Expand All @@ -26,21 +20,19 @@ import {
IconHeading,
IconHeadphones,
IconLink,
IconQuestionMark,
IconSquareRoundedPlusFilled,
IconTags,
IconWorldWww,
} from "@tabler/icons-react";
import LanguageCards from "@language/components/LanguageCards/LanguageCards";
import FormButtons from "@common/FormButtons/FormButtons";
import { userLanguageQuery } from "@language/api/query";
import { initialQuery } from "@settings/api/settings";
import { errorMessage } from "@resources/notifications";
import { createBook, getBookDataFromUrl } from "../../api/api";
import { getFormDataFromObj } from "@actions/utils";
import useNewBookForm from "@book/hooks/useNewBookForm";
import ImportURLInfoPopup from "./components/ImportURLInfoPopup";
import classes from "./NewBookForm.module.css";

function NewBookForm({ openDrawer }) {
function NewBookForm() {
const navigate = useNavigate();
const [params] = useSearchParams();
const langId = params.get("langId");
Expand All @@ -49,43 +41,7 @@ function NewBookForm({ openDrawer }) {
const { data: initial } = useQuery(initialQuery);
const dir = language?.right_to_left ? "rtl" : "ltr";

const form = useForm({
initialValues: {
language_id: "",
title: "",
text: "",
importurl: "",
text_file: undefined,
audio_file: undefined,
threshold_page_tokens: 250,
split_by: "paragraphs",
source_uri: "",
book_tags: [],
},
transformValues: (values) => {
const data = {
...values,
language_id: Number(langId),
};

return getFormDataFromObj(data);
},
});

const cardsRadioLabel = (
<Group wrap="nowrap" gap={5} align="center">
<Text component="span" fw={500} fz="sm">
Language
</Text>
<ActionIcon
variant="transparent"
color="green.6"
onClick={openDrawer}
size="sm">
<IconSquareRoundedPlusFilled />
</ActionIcon>
</Group>
);
const form = useNewBookForm(langId);

const createBookMutation = useMutation({
mutationFn: createBook,
Expand All @@ -108,14 +64,6 @@ function NewBookForm({ openDrawer }) {
<form
className={classes.container}
onSubmit={form.onSubmit(createBookMutation.mutate)}>
{initial.haveLanguages ? (
<LanguageCards
label={cardsRadioLabel}
description="Choose language for your book or create new"
/>
) : (
cardsRadioLabel
)}
<TextInput
wrapperProps={{ dir: dir }}
disabled={isLangSelected ? false : true}
Expand Down Expand Up @@ -182,7 +130,7 @@ function NewBookForm({ openDrawer }) {
flex={1}
label="Import from URL"
leftSection={<IconWorldWww />}
rightSection={<ImportURLInfo />}
rightSection={<ImportURLInfoPopup />}
key={form.key("importurl")}
{...form.getInputProps("importurl")}
/>
Expand Down Expand Up @@ -254,28 +202,4 @@ function NewBookForm({ openDrawer }) {
);
}

function ImportURLInfo() {
return (
<Popover position="top" withArrow shadow="sm">
<Popover.Target>
<ActionIcon variant="transparent">
<IconQuestionMark />
</ActionIcon>
</Popover.Target>
<Popover.Dropdown>
<Paper maw={500} fz="sm">
<p style={{ marginBottom: rem(5) }}>
This import is very primitive -- it grabs <em>all</em> the headings
and text from an HTML page.
</p>
<p>
This will likely include stuff you don&apos;t want. You are able to
edit the resulting text
</p>
</Paper>
</Popover.Dropdown>
</Popover>
);
}

export default NewBookForm;
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { ActionIcon, Paper, Popover, rem } from "@mantine/core";
import { IconQuestionMark } from "@tabler/icons-react";

function ImportURLInfoPopup() {
return (
<Popover position="top" withArrow shadow="sm">
<Popover.Target>
<ActionIcon variant="transparent">
<IconQuestionMark />
</ActionIcon>
</Popover.Target>
<Popover.Dropdown>
<Paper maw={500} fz="sm">
<p style={{ marginBottom: rem(5) }}>
This import is very primitive -- it grabs <em>all</em> the headings
and text from an HTML page.
</p>
<p>
This will likely include stuff you don&apos;t want. You are able to
edit the resulting text
</p>
</Paper>
</Popover.Dropdown>
</Popover>
);
}

export default ImportURLInfoPopup;
31 changes: 31 additions & 0 deletions frontend/src/features/book/hooks/useNewBookForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useForm } from "@mantine/form";
import { getFormDataFromObj } from "@actions/utils";

function useNewBookForm(langId) {
const form = useForm({
initialValues: {
language_id: "",
title: "",
text: "",
importurl: "",
text_file: undefined,
audio_file: undefined,
threshold_page_tokens: 250,
split_by: "paragraphs",
source_uri: "",
book_tags: [],
},
transformValues: (values) => {
const data = {
...values,
language_id: Number(langId),
};

return getFormDataFromObj(data);
},
});

return form;
}

export default useNewBookForm;
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { useEffect } from "react";
import { useQuery } from "@tanstack/react-query";
import { useLocation, useSearchParams } from "react-router-dom";
import { useForm } from "@mantine/form";
import { useSearchParams } from "react-router-dom";
import { randomId } from "@mantine/hooks";
import {
Box,
Expand All @@ -21,76 +20,28 @@ import {
} from "@tabler/icons-react";
import FormButtons from "@common/FormButtons/FormButtons";
import LanguageSelect from "./LanguageSelect";
import LanguageCards from "../LanguageCards/LanguageCards";
import DictionaryBars from "./components/DictionaryBars";
import LanguageRadioLabel from "./components/LanguageRadioLabel";
import InsertDictionaryButton from "./components/InsertDictionaryButton";
import {
parsersQuery,
userLanguageQuery,
predefinedLanguageQuery,
} from "../../api/query";
import { initialQuery } from "@settings/api/settings";
import useSelectedLanguage from "@language/hooks/useSelectedLanguage";
import useLanguageForm from "@language/hooks/useLanguageForm";
import { parsersQuery } from "../../api/query";
import classes from "./LanguageForm.module.css";

function LanguageForm() {
const { pathname } = useLocation();
const [params] = useSearchParams();
const openedFromLanguages = pathname === "/languages";
const langId = params.get("langId");
const predefinedSelected = langId === "0";
const predefSettingsQuery = useQuery(
predefinedLanguageQuery(params.get("name", null))
);
const defSettingsQuery = useQuery(userLanguageQuery(langId));
const { data: parsers } = useQuery(parsersQuery);
const { data: initial } = useQuery(initialQuery);

const form = useForm({
mode: "uncontrolled",
initialValues: {
character_substitutions: "´='|`='|’='|‘='|...=…|..=‥",
split_sentences: ".!?",
split_sentence_exceptions: "Mr.|Mrs.|Dr.|[A-Z].|Vd.|Vds.",
word_chars: "a-zA-ZÀ-ÖØ-öø-ȳáéíóúÁÉÍÓÚñÑ",
right_to_left: false,
show_romanization: false,
parser_type: "spacedel",
// minimum dictionaries should be defined on backend with other settings
dictionaries: [
{
for: "terms",
type: "embedded",
url: "",
active: true,
key: randomId(),
},
{
for: "sentences",
type: "popup",
url: "",
active: true,
key: randomId(),
},
],
},
});
const { language, isSuccess } = useSelectedLanguage();

useEffect(() => {
if (predefSettingsQuery.isSuccess && params.get("name", null)) {
const { dictionaries, ...rest } = predefSettingsQuery.data;
setFormValues(rest, dictionaries);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [predefSettingsQuery.data, predefSettingsQuery.isSuccess]);
const form = useLanguageForm();

useEffect(() => {
if (defSettingsQuery.isSuccess && openedFromLanguages) {
const { dictionaries, ...rest } = defSettingsQuery.data;
if (isSuccess && language) {
const { dictionaries, ...rest } = language;
setFormValues(rest, dictionaries);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [defSettingsQuery.data, defSettingsQuery.isSuccess]);
}, [language, isSuccess]);

useEffect(() => {
if (!langId) {
Expand All @@ -101,19 +52,13 @@ function LanguageForm() {

return (
<form>
{openedFromLanguages && initial.haveLanguages && (
<LanguageCards
label={<LanguageRadioLabel langId={langId} />}
description="Edit existing language"
/>
)}
<LanguageSelect form={form} />

<Divider mt="md" mb="xs" />

<Box pos="relative" className={classes.container}>
<LoadingOverlay
visible={predefinedSelected ? !predefSettingsQuery.isSuccess : false}
visible={!isSuccess && langId}
zIndex={1000}
overlayProps={{ radius: "sm", blur: 2 }}
/>
Expand Down
38 changes: 38 additions & 0 deletions frontend/src/features/language/hooks/useLanguageForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { useForm } from "@mantine/form";
import { randomId } from "@mantine/hooks";

function useLanguageForm() {
const form = useForm({
mode: "uncontrolled",
initialValues: {
character_substitutions: "´='|`='|’='|‘='|...=…|..=‥",
split_sentences: ".!?",
split_sentence_exceptions: "Mr.|Mrs.|Dr.|[A-Z].|Vd.|Vds.",
word_chars: "a-zA-ZÀ-ÖØ-öø-ȳáéíóúÁÉÍÓÚñÑ",
right_to_left: false,
show_romanization: false,
parser_type: "spacedel",
// minimum dictionaries should be defined on backend with other settings
dictionaries: [
{
for: "terms",
type: "embedded",
url: "",
active: true,
key: randomId(),
},
{
for: "sentences",
type: "popup",
url: "",
active: true,
key: randomId(),
},
],
},
});

return form;
}

export default useLanguageForm;
36 changes: 36 additions & 0 deletions frontend/src/features/language/hooks/useSelectedLanguage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useQuery } from "@tanstack/react-query";
import { useSearchParams } from "react-router-dom";
import {
predefinedLanguageQuery,
userLanguageQuery,
} from "@language/api/query";

function useSelectedLanguage() {
const [params] = useSearchParams();
const langId = params.get("langId");
const predefinedSelected = langId === "0";
const userSelected = langId && langId !== "0";

const { data: predefinedLang, isSuccess: predefSuccess } = useQuery(
predefinedLanguageQuery(params.get("name", null))
);
const { data: userLang, isSuccess: userSuccess } = useQuery(
userLanguageQuery(langId)
);

const language = predefinedSelected
? predefinedLang
: userSelected
? userLang
: null;

const isSuccess = predefinedSelected
? predefSuccess
: userSelected
? userSuccess
: false;

return { language, isSuccess };
}

export default useSelectedLanguage;
Loading

0 comments on commit 8493572

Please sign in to comment.