Files
suwayomi-material-you-webui/src/features/category/services/CategoryMetadata.ts
2026-03-11 19:42:02 +01:00

119 lines
4.5 KiB
TypeScript

/*
* 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 { useEffect, useMemo } from 'react';
import {
requestCategoryMetadataUpdate,
requestBatchCategoryMetadataUpdate,
} from '@/features/metadata/services/MetadataUpdater.ts';
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
import type { LibraryOptions } from '@/features/library/Library.types.ts';
import type { CategoryIdInfo, CategoryMetadataKeys, ICategoryMetadata } from '@/features/category/Category.types.ts';
import { convertFromGqlMeta } from '@/features/metadata/services/MetadataConverter.ts';
import { getMetadataFrom } from '@/features/metadata/services/MetadataReader.ts';
import type {
AllowedMetadataValueTypes,
GqlMetaHolder,
Metadata,
MetadataHolder,
} from '@/features/metadata/Metadata.types.ts';
export const DEFAULT_CATEGORY_METADATA: ICategoryMetadata = {
// sort options
sortDesc: undefined,
sortBy: undefined,
// filter options
hasDownloadedChapters: undefined,
hasBookmarkedChapters: undefined,
hasUnreadChapters: undefined,
hasReadChapters: undefined,
hasDuplicateChapters: undefined,
hasTrackerBinding: {},
hasStatus: {} as LibraryOptions['hasStatus'],
};
const convertAppMetadataToGqlMetadata = (
metadata: Partial<ICategoryMetadata>,
): Metadata<string, AllowedMetadataValueTypes> => ({
...metadata,
hasTrackerBinding: metadata.hasTrackerBinding ? JSON.stringify(metadata.hasTrackerBinding) : undefined,
hasStatus: metadata.hasStatus ? JSON.stringify(metadata.hasStatus) : undefined,
});
const getCategoryMetadataWithDefaultValueFallback = (
meta: CategoryIdInfo & MetadataHolder,
defaultMetadata: ICategoryMetadata = DEFAULT_CATEGORY_METADATA,
useEffectFn?: typeof useEffect,
): ICategoryMetadata => getMetadataFrom('category', meta, defaultMetadata, undefined, useEffectFn);
const getMetadata = (
metaHolder: CategoryIdInfo & GqlMetaHolder,
defaultMetadata?: ICategoryMetadata,
useEffectFn?: typeof useEffect,
) =>
getCategoryMetadataWithDefaultValueFallback(
{ ...metaHolder, meta: convertFromGqlMeta(metaHolder.meta) },
defaultMetadata,
useEffectFn,
);
export const getCategoryMetadata = (
metaHolder: CategoryIdInfo & GqlMetaHolder,
defaultMetadata?: ICategoryMetadata,
): ICategoryMetadata => getMetadata(metaHolder, defaultMetadata);
export const useGetCategoryMetadata = (
metaHolder: CategoryIdInfo & GqlMetaHolder,
defaultMetadata?: ICategoryMetadata,
): ICategoryMetadata => {
const metadata = getMetadata(metaHolder, defaultMetadata, useEffect);
return useMemo(() => metadata, [metaHolder, defaultMetadata]);
};
export const updateCategoryMetadata = async <
MetadataKeys extends CategoryMetadataKeys = CategoryMetadataKeys,
MetadataKey extends MetadataKeys = MetadataKeys,
>(
category: CategoryIdInfo & GqlMetaHolder,
metadataKey: MetadataKey,
value: ICategoryMetadata[MetadataKey],
): Promise<void> =>
requestCategoryMetadataUpdate(category, {
update: [[metadataKey, convertAppMetadataToGqlMetadata({ [metadataKey]: value })[metadataKey]]],
});
export const batchUpdateCategoryMetadata = async <
MetadataKeys extends CategoryMetadataKeys = CategoryMetadataKeys,
MetadataKey extends MetadataKeys = MetadataKeys,
>(
updates: Array<{
categories: (CategoryIdInfo & GqlMetaHolder)[];
entries: Array<{ metadataKey: MetadataKey; value: ICategoryMetadata[MetadataKey] }>;
}>,
): Promise<void> =>
requestBatchCategoryMetadataUpdate(
updates.map(({ categories, entries }) => ({
categories,
options: {
update: entries.map(({ metadataKey, value }) => [
metadataKey,
convertAppMetadataToGqlMetadata({ [metadataKey]: value })[metadataKey],
]),
},
})),
);
export const createUpdateCategoryMetadata =
<Settings extends CategoryMetadataKeys>(
category: CategoryIdInfo & GqlMetaHolder,
handleError: (error: any) => void = defaultPromiseErrorHandler('createUpdateCategoryMetadata'),
): ((...args: OmitFirst<Parameters<typeof updateCategoryMetadata<Settings>>>) => Promise<void>) =>
(metadataKey, value) =>
updateCategoryMetadata(category, metadataKey, value).catch(handleError);