Feature/add option to remove manga from categories when removing from library (#633)
* Optionally remove manga categories when removing them from library * Use "Mangas::removeFromLibrary" * Handle uncatched promises
This commit is contained in:
@@ -30,6 +30,7 @@ import { MenuItem } from '@/components/menu/MenuItem.tsx';
|
|||||||
import { IChapterWithMeta } from '@/components/chapter/ChapterList.tsx';
|
import { IChapterWithMeta } from '@/components/chapter/ChapterList.tsx';
|
||||||
import { ChaptersWithMeta } from '@/lib/data/ChaptersWithMeta.ts';
|
import { ChaptersWithMeta } from '@/lib/data/ChaptersWithMeta.ts';
|
||||||
import { createGetMenuItemTitle, createIsMenuItemDisabled, createShouldShowMenuItem } from '@/components/menu/util.ts';
|
import { createGetMenuItemTitle, createIsMenuItemDisabled, createShouldShowMenuItem } from '@/components/menu/util.ts';
|
||||||
|
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
|
||||||
|
|
||||||
type BaseProps = { onClose: () => void };
|
type BaseProps = { onClose: () => void };
|
||||||
|
|
||||||
@@ -116,7 +117,7 @@ export const ChapterActionMenuItems = ({
|
|||||||
Chapters.performAction(actualAction, chapter ? [chapter.id] : ChaptersWithMeta.getIds(chaptersWithMeta), {
|
Chapters.performAction(actualAction, chapter ? [chapter.id] : ChaptersWithMeta.getIds(chaptersWithMeta), {
|
||||||
chapters: getChapters(),
|
chapters: getChapters(),
|
||||||
wasManuallyMarkedAsRead: true,
|
wasManuallyMarkedAsRead: true,
|
||||||
});
|
}).catch(defaultPromiseErrorHandler('ChapterActionMenuItems::performAction'));
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import { SelectableCollectionSelectAll } from '@/components/collection/Selectabl
|
|||||||
import { Chapters } from '@/lib/data/Chapters.ts';
|
import { Chapters } from '@/lib/data/Chapters.ts';
|
||||||
import { ChaptersWithMeta } from '@/lib/data/ChaptersWithMeta.ts';
|
import { ChaptersWithMeta } from '@/lib/data/ChaptersWithMeta.ts';
|
||||||
import { ChapterActionMenuItems } from '@/components/chapter/ChapterActionMenuItems.tsx';
|
import { ChapterActionMenuItems } from '@/components/chapter/ChapterActionMenuItems.tsx';
|
||||||
|
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
|
||||||
|
|
||||||
const ChapterListHeader = styled(Stack)(({ theme }) => ({
|
const ChapterListHeader = styled(Stack)(({ theme }) => ({
|
||||||
margin: 8,
|
margin: 8,
|
||||||
@@ -167,7 +168,7 @@ export const ChapterList: React.FC<IProps> = ({ manga, isRefreshing }) => {
|
|||||||
onClick={() =>
|
onClick={() =>
|
||||||
Chapters.download(
|
Chapters.download(
|
||||||
ChaptersWithMeta.getIds(ChaptersWithMeta.getNonDownloaded(chaptersWithMeta)),
|
ChaptersWithMeta.getIds(ChaptersWithMeta.getNonDownloaded(chaptersWithMeta)),
|
||||||
)
|
).catch(defaultPromiseErrorHandler('ChapterList::download'))
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<DownloadIcon />
|
<DownloadIcon />
|
||||||
|
|||||||
@@ -229,11 +229,7 @@ export const MangaDetails: React.FC<IProps> = ({ manga }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const removeFromLibrary = () => {
|
const removeFromLibrary = () => {
|
||||||
Promise.all([requestManager.updateManga(manga.id, { updateManga: { inLibrary: false } }).response])
|
Mangas.removeFromLibrary([manga.id]).catch(defaultPromiseErrorHandler('MangaDetails::removeFromLibrary'));
|
||||||
.then(() => makeToast(t('library.info.label.removed_from_library'), 'success'))
|
|
||||||
.catch(() => {
|
|
||||||
makeToast(t('library.error.label.remove_from_library'), 'error');
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import { CheckboxInput } from '@/components/atoms/CheckboxInput.tsx';
|
|||||||
import { useMetadataServerSettings } from '@/util/metadataServerSettings.ts';
|
import { useMetadataServerSettings } from '@/util/metadataServerSettings.ts';
|
||||||
import { convertToGqlMeta, requestUpdateServerMetadata } from '@/util/metadata.ts';
|
import { convertToGqlMeta, requestUpdateServerMetadata } from '@/util/metadata.ts';
|
||||||
import { makeToast } from '@/components/util/Toast.tsx';
|
import { makeToast } from '@/components/util/Toast.tsx';
|
||||||
|
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
|
||||||
|
|
||||||
type BaseProps = {
|
type BaseProps = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -155,7 +156,7 @@ export function CategorySelect(props: Props) {
|
|||||||
addToCategories,
|
addToCategories,
|
||||||
removeFromCategories,
|
removeFromCategories,
|
||||||
},
|
},
|
||||||
});
|
}).catch(defaultPromiseErrorHandler('CategorySelect::handleOk'));
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -492,6 +492,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"remove_from_library": {
|
||||||
|
"remove_from_categories": {
|
||||||
|
"label": {
|
||||||
|
"description": "Remove manga from categories when removing them from the library",
|
||||||
|
"title": "Forget manga categories"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"ignore_filters": {
|
"ignore_filters": {
|
||||||
"label": {
|
"label": {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
} from '@/lib/graphql/generated/graphql.ts';
|
} from '@/lib/graphql/generated/graphql.ts';
|
||||||
import { Chapters } from '@/lib/data/Chapters.ts';
|
import { Chapters } from '@/lib/data/Chapters.ts';
|
||||||
import { makeToast } from '@/components/util/Toast.tsx';
|
import { makeToast } from '@/components/util/Toast.tsx';
|
||||||
|
import { getMetadataServerSettings } from '@/util/metadataServerSettings.ts';
|
||||||
|
|
||||||
export type MangaAction =
|
export type MangaAction =
|
||||||
| 'download'
|
| 'download'
|
||||||
@@ -208,10 +209,15 @@ export class Mangas {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static async removeFromLibrary(mangaIds: number[]): Promise<void> {
|
static async removeFromLibrary(mangaIds: number[]): Promise<void> {
|
||||||
|
const { removeMangaFromCategories } = await getMetadataServerSettings();
|
||||||
return Mangas.executeAction(
|
return Mangas.executeAction(
|
||||||
'remove_from_library',
|
'remove_from_library',
|
||||||
mangaIds.length,
|
mangaIds.length,
|
||||||
() => requestManager.updateMangas(mangaIds, { inLibrary: false }).response,
|
() =>
|
||||||
|
requestManager.updateMangas(mangaIds, {
|
||||||
|
updateMangas: { inLibrary: false },
|
||||||
|
updateMangasCategories: removeMangaFromCategories ? { clearCategories: true } : undefined,
|
||||||
|
}).response,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2842,10 +2842,12 @@ export type UpdateMangaMutation = { __typename?: 'Mutation', updateManga: { __ty
|
|||||||
|
|
||||||
export type UpdateMangasMutationVariables = Exact<{
|
export type UpdateMangasMutationVariables = Exact<{
|
||||||
input: UpdateMangasInput;
|
input: UpdateMangasInput;
|
||||||
|
updateCategoryInput: UpdateMangasCategoriesInput;
|
||||||
|
updateCategories: Scalars['Boolean']['input'];
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
export type UpdateMangasMutation = { __typename?: 'Mutation', updateMangas: { __typename?: 'UpdateMangasPayload', clientMutationId?: string | null, mangas: Array<{ __typename?: 'MangaType', id: number, inLibrary: boolean, inLibraryAt: any, categories: { __typename?: 'CategoryNodeList', totalCount: number, nodes: Array<{ __typename?: 'CategoryType', id: number, mangas: { __typename?: 'MangaNodeList', totalCount: number } }> } }> } };
|
export type UpdateMangasMutation = { __typename?: 'Mutation', updateMangas: { __typename?: 'UpdateMangasPayload', clientMutationId?: string | null, mangas: Array<{ __typename?: 'MangaType', id: number, inLibrary: boolean, inLibraryAt: any, categories: { __typename?: 'CategoryNodeList', totalCount: number, nodes: Array<{ __typename?: 'CategoryType', id: number, mangas: { __typename?: 'MangaNodeList', totalCount: number } }> } }> }, updateMangasCategories?: { __typename?: 'UpdateMangasCategoriesPayload', mangas: Array<{ __typename?: 'MangaType', id: number, categories: { __typename?: 'CategoryNodeList', totalCount: number, nodes: Array<{ __typename?: 'CategoryType', id: number, mangas: { __typename?: 'MangaNodeList', totalCount: number } }> } }> } };
|
||||||
|
|
||||||
export type UpdateMangaCategoriesMutationVariables = Exact<{
|
export type UpdateMangaCategoriesMutationVariables = Exact<{
|
||||||
input: UpdateMangaCategoriesInput;
|
input: UpdateMangaCategoriesInput;
|
||||||
|
|||||||
@@ -130,7 +130,11 @@ export const UPDATE_MANGA = gql`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const UPDATE_MANGAS = gql`
|
export const UPDATE_MANGAS = gql`
|
||||||
mutation UPDATE_MANGAS($input: UpdateMangasInput!) {
|
mutation UPDATE_MANGAS(
|
||||||
|
$input: UpdateMangasInput!
|
||||||
|
$updateCategoryInput: UpdateMangasCategoriesInput!
|
||||||
|
$updateCategories: Boolean!
|
||||||
|
) {
|
||||||
updateMangas(input: $input) {
|
updateMangas(input: $input) {
|
||||||
clientMutationId
|
clientMutationId
|
||||||
mangas {
|
mangas {
|
||||||
@@ -148,6 +152,20 @@ export const UPDATE_MANGAS = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
updateMangasCategories(input: $updateCategoryInput) @include(if: $updateCategories) {
|
||||||
|
mangas {
|
||||||
|
id
|
||||||
|
categories {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
mangas {
|
||||||
|
totalCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
totalCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -1584,13 +1584,17 @@ export class RequestManager {
|
|||||||
|
|
||||||
public updateMangas(
|
public updateMangas(
|
||||||
ids: number[],
|
ids: number[],
|
||||||
patch: UpdateMangaPatchInput,
|
patch: { updateMangas: UpdateMangaPatchInput; updateMangasCategories?: UpdateMangaCategoriesPatchInput },
|
||||||
options?: MutationOptions<UpdateMangasMutation, UpdateMangasMutationVariables>,
|
options?: MutationOptions<UpdateMangasMutation, UpdateMangasMutationVariables>,
|
||||||
): AbortableApolloMutationResponse<UpdateMangasMutation> {
|
): AbortableApolloMutationResponse<UpdateMangasMutation> {
|
||||||
const result = this.doRequest<UpdateMangasMutation, UpdateMangasMutationVariables>(
|
const result = this.doRequest<UpdateMangasMutation, UpdateMangasMutationVariables>(
|
||||||
GQLMethod.MUTATION,
|
GQLMethod.MUTATION,
|
||||||
UPDATE_MANGAS,
|
UPDATE_MANGAS,
|
||||||
{ input: { ids, patch } },
|
{
|
||||||
|
input: { ids, patch: patch.updateMangas },
|
||||||
|
updateCategoryInput: { ids, patch: patch.updateMangasCategories ?? {} },
|
||||||
|
updateCategories: !!patch.updateMangasCategories,
|
||||||
|
},
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -97,6 +97,19 @@ export function LibrarySettings() {
|
|||||||
onChange={(e) => setSettingValue('showAddToLibraryCategorySelectDialog', e.target.checked)}
|
onChange={(e) => setSettingValue('showAddToLibraryCategorySelectDialog', e.target.checked)}
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
<ListItem>
|
||||||
|
<ListItemText
|
||||||
|
primary={t('library.settings.general.remove_from_library.remove_from_categories.label.title')}
|
||||||
|
secondary={t(
|
||||||
|
'library.settings.general.remove_from_library.remove_from_categories.label.description',
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Switch
|
||||||
|
edge="end"
|
||||||
|
checked={settings.removeMangaFromCategories}
|
||||||
|
onChange={(e) => setSettingValue('removeMangaFromCategories', e.target.checked)}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
</List>
|
</List>
|
||||||
<GlobalUpdateSettings />
|
<GlobalUpdateSettings />
|
||||||
<List
|
<List
|
||||||
|
|||||||
@@ -228,6 +228,7 @@ export type MetadataServerSettings = {
|
|||||||
// library
|
// library
|
||||||
showAddToLibraryCategorySelectDialog: boolean;
|
showAddToLibraryCategorySelectDialog: boolean;
|
||||||
ignoreFilters: boolean;
|
ignoreFilters: boolean;
|
||||||
|
removeMangaFromCategories: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface ISearchSettings {
|
export interface ISearchSettings {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export const getDefaultSettings = (): MetadataServerSettings => ({
|
|||||||
// library
|
// library
|
||||||
showAddToLibraryCategorySelectDialog: true,
|
showAddToLibraryCategorySelectDialog: true,
|
||||||
ignoreFilters: false,
|
ignoreFilters: false,
|
||||||
|
removeMangaFromCategories: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const getMetadataServerSettingsWithDefaultFallback = (
|
const getMetadataServerSettingsWithDefaultFallback = (
|
||||||
|
|||||||
Reference in New Issue
Block a user