diff --git a/src/modules/category/Category.constants.ts b/src/modules/category/Category.constants.ts new file mode 100644 index 00000000..c35cbe89 --- /dev/null +++ b/src/modules/category/Category.constants.ts @@ -0,0 +1,9 @@ +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +export const CREATE_NEW_CATEGORY_ID = -1; diff --git a/src/modules/category/components/CreateOrEditCategoryDialog.tsx b/src/modules/category/components/CreateOrEditCategoryDialog.tsx new file mode 100644 index 00000000..fa68dc9a --- /dev/null +++ b/src/modules/category/components/CreateOrEditCategoryDialog.tsx @@ -0,0 +1,87 @@ +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +import { useState } from 'react'; +import Button from '@mui/material/Button'; +import TextField from '@mui/material/TextField'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogTitle from '@mui/material/DialogTitle'; +import Checkbox from '@mui/material/Checkbox'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import { useTranslation } from 'react-i18next'; +import { requestManager } from '@/lib/requests/RequestManager.ts'; +import { CategoryDefaultInfo, CategoryIdInfo, CategoryNameInfo } from '@/modules/category/Category.types.ts'; +import { getErrorMessage } from '@/lib/HelperFunctions.ts'; +import { makeToast } from '@/modules/core/utils/Toast.ts'; + +export const CreateOrEditCategoryDialog = ({ + category, + onClose, +}: { + category: (CategoryIdInfo & CategoryNameInfo & CategoryDefaultInfo) | undefined; + onClose: () => void; +}) => { + const isEditMode = !!category; + + const { t } = useTranslation(); + + const [dialogName, setDialogName] = useState(category?.name ?? ''); + const [dialogDefault, setDialogDefault] = useState(!!category?.default); + + const handleDialogSubmit = () => { + onClose(); + + if (isEditMode) { + requestManager + .updateCategory(category.id, { name: dialogName, default: dialogDefault }) + .response.catch((e) => + makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)), + ); + + return; + } + + requestManager + .createCategory({ name: dialogName, default: dialogDefault }) + .response.catch((e) => makeToast(t('category.error.label.create_failure'), 'error', getErrorMessage(e))); + }; + + return ( + + + {isEditMode ? t('category.dialog.title.edit_category_one') : t('category.dialog.title.new_category')} + + + setDialogName(e.target.value)} + /> + setDialogDefault(e.target.checked)} />} + label={t('category.label.use_as_default_category')} + /> + + + + + + + ); +}; diff --git a/src/modules/category/screens/CategorySettings.tsx b/src/modules/category/screens/CategorySettings.tsx index 953b3b06..2609b57f 100644 --- a/src/modules/category/screens/CategorySettings.tsx +++ b/src/modules/category/screens/CategorySettings.tsx @@ -7,17 +7,8 @@ */ import { ComponentProps, useMemo, useState } from 'react'; -import { useTheme } from '@mui/material/styles'; import Fab from '@mui/material/Fab'; import AddIcon from '@mui/icons-material/Add'; -import Button from '@mui/material/Button'; -import TextField from '@mui/material/TextField'; -import Dialog from '@mui/material/Dialog'; -import DialogActions from '@mui/material/DialogActions'; -import DialogContent from '@mui/material/DialogContent'; -import DialogTitle from '@mui/material/DialogTitle'; -import Checkbox from '@mui/material/Checkbox'; -import FormControlLabel from '@mui/material/FormControlLabel'; import { useTranslation } from 'react-i18next'; import Box from '@mui/material/Box'; import { closestCenter, DndContext, DragEndEvent } from '@dnd-kit/core'; @@ -32,14 +23,16 @@ import { GET_CATEGORIES_SETTINGS } from '@/lib/graphql/queries/CategoryQuery.ts' import { CategorySettingsCard } from '@/modules/category/components/CategorySettingsCard.tsx'; import { CategoryIdInfo } from '@/modules/category/Category.types.ts'; import { getErrorMessage, noOp } from '@/lib/HelperFunctions.ts'; -import { makeToast } from '@/modules/core/utils/Toast.ts'; import { DndSortableItem } from '@/lib/dnd-kit/DndSortableItem.tsx'; import { DndKitUtil } from '@/lib/dnd-kit/DndKitUtil.ts'; import { DndOverlayItem } from '@/lib/dnd-kit/DndOverlayItem.tsx'; import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; +import { CREATE_NEW_CATEGORY_ID } from '@/modules/category/Category.constants.ts'; +import { CreateOrEditCategoryDialog } from '@/modules/category/components/CreateOrEditCategoryDialog.tsx'; export function CategorySettings() { const { t } = useTranslation(); + const dndSensors = DndKitUtil.useSensorsForDevice(); useAppTitle(t('category.dialog.title.edit_category_other')); @@ -47,6 +40,14 @@ export function CategorySettings() { GetCategoriesSettingsQuery, GetCategoriesSettingsQueryVariables >(GET_CATEGORIES_SETTINGS, { notifyOnNetworkStatusChange: true }); + const [reorderCategory, { reset: revertReorder }] = requestManager.useReorderCategory(); + + const [categoryToEdit, setCategoryToEdit] = useState(CREATE_NEW_CATEGORY_ID); + const [dialogOpen, setDialogOpen] = useState(false); + const [dndActiveCategory, setDndActiveCategory] = useState< + ComponentProps['category'] | null + >(null); + const categories = useMemo(() => { const res = [...(data?.categories.nodes ?? [])]; if (res.length > 0 && res[0].name === 'Default') { @@ -55,18 +56,6 @@ export function CategorySettings() { return res; }, [data]); - const [categoryToEdit, setCategoryToEdit] = useState(-1); // -1 means new category - const [dialogOpen, setDialogOpen] = useState(false); - const [dialogName, setDialogName] = useState(''); - const [dialogDefault, setDialogDefault] = useState(false); - const [reorderCategory, { reset: revertReorder }] = requestManager.useReorderCategory(); - const theme = useTheme(); - - const dndSensors = DndKitUtil.useSensorsForDevice(); - const [dndActiveCategory, setDndActiveCategory] = useState< - ComponentProps['category'] | null - >(null); - const categoryReorder = (list: CategoryIdInfo[], from: number, to: number) => { const reorderedCategory = list[from]; @@ -90,47 +79,16 @@ export function CategorySettings() { categoryReorder(categories, oldIndex, newIndex); }; - const resetDialog = () => { - setDialogName(''); - setDialogDefault(false); - setCategoryToEdit(-1); - }; - - const handleDialogOpen = () => { - resetDialog(); - setDialogOpen(true); - }; - - const handleEditDialogOpen = (index: number) => { - setDialogName(categories[index].name); - setDialogDefault(categories[index].default); - setCategoryToEdit(index); + const handleDialogOpen = (categoryId?: CategoryIdInfo['id']) => { + setCategoryToEdit(categoryId ?? CREATE_NEW_CATEGORY_ID); setDialogOpen(true); }; const handleDialogCancel = () => { + setCategoryToEdit(CREATE_NEW_CATEGORY_ID); setDialogOpen(false); }; - const handleDialogSubmit = () => { - setDialogOpen(false); - - if (categoryToEdit === -1) { - requestManager - .createCategory({ name: dialogName, default: dialogDefault }) - .response.catch((e) => - makeToast(t('category.error.label.create_failure'), 'error', getErrorMessage(e)), - ); - } else { - const category = categories[categoryToEdit]; - requestManager - .updateCategory(category.id, { name: dialogName, default: dialogDefault }) - .response.catch((e) => - makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)), - ); - } - }; - if (loading) { return ; } @@ -165,7 +123,7 @@ export function CategorySettings() { id={category.id} isDragging={category.id === dndActiveCategory?.id} > - handleEditDialogOpen(index)} /> + handleDialogOpen(index)} /> ))} @@ -177,48 +135,19 @@ export function CategorySettings() { theme.spacing(2), + right: (theme) => theme.spacing(2), }} - onClick={handleDialogOpen} + onClick={() => handleDialogOpen()} > - - - {categoryToEdit === -1 - ? t('category.dialog.title.new_category') - : t('category.dialog.title.edit_category_one')} - - - setDialogName(e.target.value)} - /> - setDialogDefault(e.target.checked)} /> - } - label={t('category.label.use_as_default_category')} - /> - - - - - - + + {dialogOpen && ( + + )} ); }