Files
suwayomi-material-you-webui/src/components/navbar/action/CategorySelect.tsx

208 lines
7.2 KiB
TypeScript
Raw Normal View History

/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
2021-02-21 04:27:41 +03:30
* 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/.
*/
2021-02-21 04:27:41 +03:30
import { useEffect, useMemo } from 'react';
2021-09-09 17:51:22 +04:30
import Button from '@mui/material/Button';
import DialogTitle from '@mui/material/DialogTitle';
import DialogContent from '@mui/material/DialogContent';
import DialogActions from '@mui/material/DialogActions';
import Dialog from '@mui/material/Dialog';
import FormGroup from '@mui/material/FormGroup';
import { useTranslation } from 'react-i18next';
2023-10-28 00:32:02 +02:00
import { requestManager } from '@/lib/requests/RequestManager.ts';
import { Mangas } from '@/lib/data/Mangas.ts';
import { useSelectableCollection } from '@/components/collection/useSelectableCollection.ts';
import { ThreeStateCheckboxInput } from '@/components/atoms/ThreeStateCheckboxInput.tsx';
import { TCategory } from '@/typings.ts';
2021-02-21 04:27:41 +03:30
type BaseProps = {
open: boolean;
onClose: (didUpdateCategories: boolean) => void;
};
type SingleMangaModeProps = {
mangaId: number;
addToLibrary?: boolean;
};
type MultiMangaModeProps = {
mangaIds: number[];
};
type Props =
| (BaseProps & SingleMangaModeProps & PropertiesNever<MultiMangaModeProps>)
| (BaseProps & PropertiesNever<SingleMangaModeProps> & MultiMangaModeProps);
const useGetMangaCategoryIds = (mangaId: number | undefined): number[] => {
const { data: mangaResult } = requestManager.useGetManga(mangaId ?? -1, { skip: mangaId === undefined });
return useMemo(() => {
if (mangaId === undefined || !mangaResult) {
return [];
}
return mangaResult.manga.categories.nodes.map((category) => category.id);
}, [mangaResult?.manga.categories.nodes, mangaId]);
};
const getCategoryCheckedState = (
categoryId: number,
categoriesToAdd: number[],
categoriesToRemove: number[],
isSingleSelectionMode: boolean,
): boolean | undefined => {
if (categoriesToAdd.includes(categoryId)) {
return true;
}
if (isSingleSelectionMode) {
return undefined;
}
if (categoriesToRemove.includes(categoryId)) {
return false;
}
2021-02-21 04:27:41 +03:30
return undefined;
};
const getDefaultCategoryIds = (categories: TCategory[]) =>
categories.filter(({ default: isDefault }) => isDefault).map(({ id }) => id);
export function CategorySelect(props: Props) {
const { t } = useTranslation();
const { open, onClose, mangaId, mangaIds: passedMangaIds, addToLibrary = false } = props;
const isSingleSelectionMode = mangaId !== undefined;
const mangaIds = passedMangaIds ?? [mangaId];
2021-02-21 04:27:41 +03:30
const mangaCategoryIds = useGetMangaCategoryIds(mangaId);
2023-09-01 20:39:30 +02:00
const { data } = requestManager.useGetCategories();
const categoriesData = data?.categories.nodes;
const allCategories = useMemo(() => {
const cats = [...(categoriesData ?? [])]; // make copy
if (cats.length > 0 && cats[0].name === 'Default') {
cats.shift(); // remove first category if it is 'Default'
}
return cats;
}, [categoriesData]);
2021-02-21 04:27:41 +03:30
const defaultCategoryIds = useMemo(
() => (addToLibrary ? getDefaultCategoryIds(allCategories) : []),
[allCategories],
);
const { handleSelection, setSelectionForKey, getSelectionForKey } = useSelectableCollection<
number,
'categoriesToAdd' | 'categoriesToRemove'
>(allCategories.length, {
currentKey: 'categoriesToAdd',
initialState: {
categoriesToAdd: [...mangaCategoryIds, ...defaultCategoryIds],
categoriesToRemove: [],
},
});
useEffect(() => {
setSelectionForKey('categoriesToAdd', [...mangaCategoryIds, ...defaultCategoryIds]);
setSelectionForKey('categoriesToRemove', []);
}, [mangaCategoryIds]);
const categoriesToAdd = getSelectionForKey('categoriesToAdd');
const categoriesToRemove = getSelectionForKey('categoriesToRemove');
2021-02-21 04:27:41 +03:30
const handleCancel = () => {
setSelectionForKey('categoriesToAdd', mangaCategoryIds);
setSelectionForKey('categoriesToRemove', []);
onClose(false);
2021-02-21 04:27:41 +03:30
};
const handleOk = () => {
onClose(true);
2021-02-21 04:27:41 +03:30
const addToCategories = isSingleSelectionMode
? categoriesToAdd.filter((categoryId) => !mangaCategoryIds.includes(categoryId))
: categoriesToAdd;
const removeFromCategories = isSingleSelectionMode
? mangaCategoryIds.filter((categoryId) => !categoriesToAdd.includes(categoryId))
: categoriesToRemove;
const isUpdateRequired = !!addToCategories.length || !!removeFromCategories.length;
if (!isUpdateRequired) {
return;
}
Mangas.performAction('change_categories', mangaIds, {
changeCategoriesPatch: {
addToCategories,
removeFromCategories,
2023-09-01 20:33:35 +02:00
},
});
2021-02-21 04:27:41 +03:30
};
return (
<Dialog
2021-11-15 19:32:25 +03:30
sx={{
'.MuiDialog-paper': {
maxHeight: 435,
width: '80%',
},
}}
2021-02-21 04:27:41 +03:30
maxWidth="xs"
open={open}
>
<DialogTitle>{t('category.title.set_categories')}</DialogTitle>
2021-02-21 04:27:41 +03:30
<DialogContent dividers>
<FormGroup>
{allCategories.length === 0 && (
<span>
{t('category.error.no_categories_found.label.info')}
<br />
{t('category.error.no_categories_found.label.hint')}
</span>
)}
{allCategories.map((category) => (
<ThreeStateCheckboxInput
checked={getCategoryCheckedState(
category.id,
categoriesToAdd,
categoriesToRemove,
isSingleSelectionMode,
)}
onChange={(checked) => {
handleSelection(category.id, false, 'categoriesToAdd');
handleSelection(category.id, false, 'categoriesToRemove');
if (checked) {
handleSelection(category.id, true, 'categoriesToAdd');
}
if (checked === false) {
handleSelection(category.id, true, 'categoriesToRemove');
}
}}
label={category.name}
key={category.id}
2021-02-21 04:27:41 +03:30
/>
))}
</FormGroup>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={handleCancel} color="primary">
{t('global.button.cancel')}
2021-02-21 04:27:41 +03:30
</Button>
<Button onClick={handleOk} color="primary">
{t('global.button.ok')}
2021-02-21 04:27:41 +03:30
</Button>
</DialogActions>
</Dialog>
);
}