From 6ca16b94494b7c793b992f6ff3ab02e42920fb35 Mon Sep 17 00:00:00 2001 From: schroda <50052685+schroda@users.noreply.github.com> Date: Mon, 30 Sep 2024 04:01:23 +0200 Subject: [PATCH] Add sort options to migration list --- public/locales/en.json | 8 +- src/lib/metadata/metadata.ts | 1 + src/lib/metadata/metadataServerSettings.ts | 8 ++ src/screens/Browse.tsx | 2 +- src/screens/Migration.constants.ts | 25 +++++ src/screens/Migration.tsx | 123 +++++++++++++++++---- src/screens/Migration.types.ts | 26 +++++ src/typings.ts | 2 + 8 files changed, 174 insertions(+), 21 deletions(-) create mode 100644 src/screens/Migration.constants.ts create mode 100644 src/screens/Migration.types.ts diff --git a/public/locales/en.json b/public/locales/en.json index 65345ac4..d0d41b68 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -423,10 +423,12 @@ }, "sort": { "label": { + "asc": "Ascending", "by_chapter_number": "By chapter number", "by_fetch_date": "By date fetched", "by_source": "By source", - "by_upload_date": "By upload date" + "by_upload_date": "By upload date", + "desc": "Descending" } }, "time": { @@ -735,6 +737,10 @@ "search": { "title": "$t(migrate.title) \"{{title}}\"" }, + "sort": { + "by_manga_count": "By manga count", + "by_source_name": "By source name" + }, "title": "Migrate" }, "reader": { diff --git a/src/lib/metadata/metadata.ts b/src/lib/metadata/metadata.ts index 6f578e82..df1601a5 100644 --- a/src/lib/metadata/metadata.ts +++ b/src/lib/metadata/metadata.ts @@ -61,6 +61,7 @@ const GLOBAL_METADATA_KEYS: AppMetadataKeys[] = [ 'migrateCategories', 'migrateTracking', 'deleteChapters', + 'migrateSortSettings', // browse 'hideLibraryEntries', diff --git a/src/lib/metadata/metadataServerSettings.ts b/src/lib/metadata/metadataServerSettings.ts index 8aa49bb2..a2c5d9e5 100644 --- a/src/lib/metadata/metadataServerSettings.ts +++ b/src/lib/metadata/metadataServerSettings.ts @@ -10,6 +10,7 @@ import { AllowedMetadataValueTypes, AppMetadataKeys, Metadata, + MetadataMigrationSettings, MetadataServerSettingKeys, MetadataServerSettings, MetadataThemeSettings, @@ -19,6 +20,7 @@ import { convertFromGqlMeta, getMetadataFrom, requestUpdateServerMetadata } from import { jsonSaveParse } from '@/util/HelperFunctions.ts'; import { DEFAULT_DEVICE } from '@/util/device.ts'; import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; +import { DEFAULT_SORT_SETTINGS } from '@/screens/Migration.constants.ts'; export const getDefaultSettings = (): MetadataServerSettings => ({ // downloads @@ -41,6 +43,7 @@ export const getDefaultSettings = (): MetadataServerSettings => ({ migrateCategories: true, migrateTracking: true, deleteChapters: true, + migrateSortSettings: DEFAULT_SORT_SETTINGS, // browse hideLibraryEntries: false, @@ -64,6 +67,7 @@ export const convertSettingsToMetadata = ( ...settings, devices: JSON.stringify(settings.devices), customThemes: JSON.stringify(settings.customThemes), + migrateSortSettings: JSON.stringify(settings.migrateSortSettings), }); export const convertMetadataToSettings = ( @@ -76,6 +80,10 @@ export const convertMetadataToSettings = ( customThemes: jsonSaveParse((metadata.customThemes as string) ?? '') ?? getDefaultSettings().customThemes, + migrateSortSettings: + jsonSaveParse( + (metadata.migrateSortSettings as string) ?? '', + ) ?? getDefaultSettings().migrateSortSettings, }) satisfies MetadataServerSettings; const getMetadataServerSettingsWithDefaultFallback = ( diff --git a/src/screens/Browse.tsx b/src/screens/Browse.tsx index cd531fc1..10527234 100644 --- a/src/screens/Browse.tsx +++ b/src/screens/Browse.tsx @@ -55,7 +55,7 @@ export function Browse() { - + ); diff --git a/src/screens/Migration.constants.ts b/src/screens/Migration.constants.ts new file mode 100644 index 00000000..4f3b0402 --- /dev/null +++ b/src/screens/Migration.constants.ts @@ -0,0 +1,25 @@ +/* + * 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 { SortBy, SortOrder } from '@/screens/Migration.types.ts'; +import { TranslationKey } from '@/typings.ts'; + +export const sortByToTranslationKey: Record = { + [SortBy.SOURCE_NAME]: 'migrate.sort.by_source_name', + [SortBy.MANGA_COUNT]: 'migrate.sort.by_manga_count', +}; + +export const sortOrderToTranslationKey: Record = { + [SortOrder.ASC]: 'global.sort.label.asc', + [SortOrder.DESC]: 'global.sort.label.desc', +}; + +export const DEFAULT_SORT_SETTINGS = { + sortBy: SortBy.SOURCE_NAME, + sortOrder: SortOrder.ASC, +}; diff --git a/src/screens/Migration.tsx b/src/screens/Migration.tsx index 48877c60..5405e8c6 100644 --- a/src/screens/Migration.tsx +++ b/src/screens/Migration.tsx @@ -9,45 +9,89 @@ import { useTranslation } from 'react-i18next'; import { useMemo } from 'react'; import List from '@mui/material/List'; +import Stack from '@mui/material/Stack'; +import IconButton from '@mui/material/IconButton'; +import SortByAlphaIcon from '@mui/icons-material/SortByAlpha'; +import TagIcon from '@mui/icons-material/Tag'; +import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'; +import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'; +import Tooltip from '@mui/material/Tooltip'; import { requestManager } from '@/lib/requests/RequestManager.ts'; import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder.tsx'; import { EmptyViewAbsoluteCentered } from '@/components/util/EmptyViewAbsoluteCentered.tsx'; -import { GetMigratableSourcesQuery } from '@/lib/graphql/generated/graphql.ts'; import { MigrationCard, TMigratableSource } from '@/components/MigrationCard.tsx'; import { StyledGroupItemWrapper } from '@/components/virtuoso/StyledGroupItemWrapper.tsx'; import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; +import { SortBy, SortOrder, SortSettings, TMigratableSourcesResult } from '@/screens/Migration.types.ts'; +import { sortByToTranslationKey, sortOrderToTranslationKey } from '@/screens/Migration.constants'; +import { + createUpdateMetadataServerSettings, + useMetadataServerSettings, +} from '@/lib/metadata/metadataServerSettings.ts'; +import { makeToast } from '@/components/util/Toast.tsx'; +import { useNavBarContext } from '@/components/context/NavbarContext.tsx'; -type TMigratableSourcesResult = GetMigratableSourcesQuery['mangas']['nodes']; -type TMigratableSources = Record; - -const getMigratableSources = (mangas?: TMigratableSourcesResult): TMigratableSources => { +const getMigratableSources = ( + mangas: TMigratableSourcesResult | undefined, + { sortBy, sortOrder }: SortSettings, +): TMigratableSource[] => { if (!mangas) { - return {}; + return []; } - const uniqueSources: TMigratableSources = {}; + const sourceBySourceId: Record = {}; mangas.forEach(({ sourceId, source }) => { - const uniqueSource = uniqueSources[sourceId] ?? { + const uniqueSource = sourceBySourceId[sourceId] ?? { ...{ id: sourceId, name: sourceId, lang: 'unknown', iconUrl: null, mangaCount: 0, ...source }, }; - uniqueSources[sourceId] = { + sourceBySourceId[sourceId] = { ...uniqueSource, mangaCount: uniqueSource.mangaCount + 1, }; }); - return uniqueSources; + const sourcesSortedBy = Object.values(sourceBySourceId).toSorted((a, b) => { + switch (sortBy) { + case SortBy.SOURCE_NAME: + return a.name.localeCompare(b.name); + case SortBy.MANGA_COUNT: + return a.mangaCount - b.mangaCount; + default: + throw new Error(`Unexpected "sortBy" "${sortBy}"`); + } + }); + + switch (sortOrder) { + case SortOrder.ASC: + return sourcesSortedBy; + case SortOrder.DESC: + return sourcesSortedBy.toReversed(); + default: + throw new Error(`Unexpected "sortOrder" "${sortOrder}"`); + } }; -export const Migration = () => { +export const Migration = ({ tabsMenuHeight }: { tabsMenuHeight: number }) => { const { t } = useTranslation(); + const { appBarHeight } = useNavBarContext(); + + const { + settings: { migrateSortSettings }, + } = useMetadataServerSettings(); + const updateMetadataServerSettings = createUpdateMetadataServerSettings<'migrateSortSettings'>(() => + makeToast(t('global.error.label.failed_to_save_changes'), 'error'), + ); + const { sortBy, sortOrder } = migrateSortSettings; const { data, loading, error, refetch } = requestManager.useGetMigratableSources({ notifyOnNetworkStatusChange: true, }); - const migratableSources = useMemo(() => getMigratableSources(data?.mangas.nodes), [data?.mangas.nodes]); + const migratableSources = useMemo( + () => getMigratableSources(data?.mangas.nodes, migrateSortSettings), + [data?.mangas.nodes, migrateSortSettings], + ); if (loading) { return ; @@ -64,12 +108,53 @@ export const Migration = () => { } return ( - - {Object.values(migratableSources).map((migratableSource) => ( - - - - ))} - + <> + + + + updateMetadataServerSettings('migrateSortSettings', { sortBy: (sortBy + 1) % 2, sortOrder }) + } + > + {sortBy ? : } + + + + + updateMetadataServerSettings('migrateSortSettings', { + sortBy, + sortOrder: (sortOrder + 1) % 2, + }) + } + > + {sortOrder ? : } + + + + + {migratableSources.map((migratableSource) => ( + + + + ))} + + ); }; diff --git a/src/screens/Migration.types.ts b/src/screens/Migration.types.ts new file mode 100644 index 00000000..77cbb895 --- /dev/null +++ b/src/screens/Migration.types.ts @@ -0,0 +1,26 @@ +/* + * 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 { GetMigratableSourcesQuery } from '@/lib/graphql/generated/graphql.ts'; + +export enum SortBy { + SOURCE_NAME, + MANGA_COUNT, +} + +export enum SortOrder { + ASC, + DESC, +} + +export interface SortSettings { + sortBy: SortBy; + sortOrder: SortOrder; +} + +export type TMigratableSourcesResult = GetMigratableSourcesQuery['mangas']['nodes']; diff --git a/src/typings.ts b/src/typings.ts index 95a04baa..4fae201c 100644 --- a/src/typings.ts +++ b/src/typings.ts @@ -22,6 +22,7 @@ import { TrackerType, } from '@/lib/graphql/generated/graphql.ts'; import { AppTheme } from '@/lib/ui/AppThemes.ts'; +import { SortSettings } from '@/screens/Migration.types.ts'; type GenericLocation = Omit & { state?: State }; @@ -152,6 +153,7 @@ export type MetadataMigrationSettings = { migrateCategories: boolean; migrateTracking: boolean; deleteChapters: boolean; + migrateSortSettings: SortSettings; }; export type MetadataBrowseSettings = {