Sort browse migration manga selection by title

This commit is contained in:
schroda
2026-05-16 01:33:37 +02:00
parent c41b2ff64d
commit e944c6711e
5 changed files with 106 additions and 68 deletions

View File

@@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- (**Migration**) Keep the migration page open when aborting during the actual migration execution - (**Migration**) Keep the migration page open when aborting during the actual migration execution
- (**Migration**) Prevent concurrent requests to the same tracker - (**Migration**) Prevent concurrent requests to the same tracker
- (**Migration**) Allow only 2 tracker requests per second - (**Migration**) Allow only 2 tracker requests per second
- (**Migration**) Sort manga to migrate selection by title and by recently added to the library
### Fixed ### Fixed

View File

@@ -20,12 +20,19 @@ import { useLocalStorage } from '@/base/hooks/useStorage.tsx';
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts'; import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
import { MigrationManager } from '@/features/migration/MigrationManager.ts'; import { MigrationManager } from '@/features/migration/MigrationManager.ts';
import { useSelectableCollection } from '@/base/collection/hooks/useSelectableCollection.ts'; import { useSelectableCollection } from '@/base/collection/hooks/useSelectableCollection.ts';
import type { GetSourceMigratableQuery, GetSourceMigratableQueryVariables } from '@/lib/graphql/generated/graphql.ts'; import type {
GetMigratableSourceMangasQuery,
GetMigratableSourceMangasQueryVariables,
GetSourceMigratableQuery,
GetSourceMigratableQueryVariables,
} from '@/lib/graphql/generated/graphql.ts';
import { GET_SOURCE_MIGRATABLE } from '@/lib/graphql/source/SourceQuery.ts'; import { GET_SOURCE_MIGRATABLE } from '@/lib/graphql/source/SourceQuery.ts';
import { SelectableCollectionSelectMode } from '@/base/collection/components/SelectableCollectionSelectMode.tsx'; import { SelectableCollectionSelectMode } from '@/base/collection/components/SelectableCollectionSelectMode.tsx';
import { Mangas } from '@/features/manga/services/Mangas.ts'; import { Mangas } from '@/features/manga/services/Mangas.ts';
import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts'; import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts';
import { MigrationContinueButton } from '@/features/migration/components/MigrationContinueButton.tsx'; import { MigrationContinueButton } from '@/features/migration/components/MigrationContinueButton.tsx';
import { GET_MIGRATABLE_SOURCE_MANGAS } from '@/lib/graphql/manga/MangaQuery.ts';
import { MangaOrderBy, SortOrder } from '@/lib/graphql/generated/graphql-base.types.ts';
const getSourceError = (error: unknown): unknown => { const getSourceError = (error: unknown): unknown => {
const message = getErrorMessage(error); const message = getErrorMessage(error);
@@ -66,9 +73,28 @@ export const MigrationSelectMangas = () => {
loading: areMangasLoading, loading: areMangasLoading,
error: mangasError, error: mangasError,
refetch: refetchMangas, refetch: refetchMangas,
} = requestManager.useGetMigratableSourceMangas(sourceId!, { } = requestManager.useGetMangas<GetMigratableSourceMangasQuery, GetMigratableSourceMangasQueryVariables>(
skip: !sourceIds, GET_MIGRATABLE_SOURCE_MANGAS,
}); {
condition: {
sourceId,
inLibrary: true,
},
order: [
{
by: MangaOrderBy.Title,
byType: SortOrder.Asc,
},
{
by: MangaOrderBy.InLibraryAt,
byType: SortOrder.Desc,
},
],
},
{
skip: !sourceIds,
},
);
const mangas = migratableSourceMangasData?.mangas.nodes ?? STABLE_EMPTY_ARRAY; const mangas = migratableSourceMangasData?.mangas.nodes ?? STABLE_EMPTY_ARRAY;
const mangaIds = useMemo(() => Mangas.getIds(mangas), [mangas]); const mangaIds = useMemo(() => Mangas.getIds(mangas), [mangas]);

View File

@@ -624,7 +624,14 @@ export type GetMangasDuplicatesQueryVariables = Exact<{
export type GetMangasDuplicatesQuery = { __typename: 'Query', mangas: { __typename: 'MangaNodeList', totalCount: number, nodes: Array<{ __typename: 'MangaType', description: string | null, id: number, title: string, thumbnailUrl: string | null, thumbnailUrlLastFetched: string | null, inLibrary: boolean, initialized: boolean, sourceId: string, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, chapters: { __typename: 'ChapterNodeList', totalCount: number } }>, pageInfo: { __typename: 'PageInfo', endCursor: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor: string | null } } }; export type GetMangasDuplicatesQuery = { __typename: 'Query', mangas: { __typename: 'MangaNodeList', totalCount: number, nodes: Array<{ __typename: 'MangaType', description: string | null, id: number, title: string, thumbnailUrl: string | null, thumbnailUrlLastFetched: string | null, inLibrary: boolean, initialized: boolean, sourceId: string, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, chapters: { __typename: 'ChapterNodeList', totalCount: number } }>, pageInfo: { __typename: 'PageInfo', endCursor: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor: string | null } } };
export type GetMigratableSourceMangasQueryVariables = Exact<{ export type GetMigratableSourceMangasQueryVariables = Exact<{
sourceId: string; after?: string | null | undefined;
before?: string | null | undefined;
condition?: Types.MangaConditionInput | null | undefined;
filter?: Types.MangaFilterInput | null | undefined;
first?: number | null | undefined;
last?: number | null | undefined;
offset?: number | null | undefined;
order?: Array<Types.MangaOrderInput> | Types.MangaOrderInput | null | undefined;
}>; }>;

View File

@@ -239,8 +239,26 @@ export const GET_MANGAS_DUPLICATES = gql`
export const GET_MIGRATABLE_SOURCE_MANGAS = gql` export const GET_MIGRATABLE_SOURCE_MANGAS = gql`
${MANGA_MIGRATION_FIELDS} ${MANGA_MIGRATION_FIELDS}
query GET_MIGRATABLE_SOURCE_MANGAS($sourceId: LongString!) { query GET_MIGRATABLE_SOURCE_MANGAS(
mangas(condition: { sourceId: $sourceId, inLibrary: true }) { $after: Cursor
$before: Cursor
$condition: MangaConditionInput
$filter: MangaFilterInput
$first: Int
$last: Int
$offset: Int
$order: [MangaOrderInput!]
) {
mangas(
after: $after
before: $before
condition: $condition
filter: $filter
first: $first
last: $last
offset: $offset
order: $order
) {
nodes { nodes {
...MANGA_MIGRATION_FIELDS ...MANGA_MIGRATION_FIELDS
} }

View File

@@ -31,6 +31,8 @@ import type {
ClearDownloaderMutationVariables, ClearDownloaderMutationVariables,
ClearServerCacheMutation, ClearServerCacheMutation,
ClearServerCacheMutationVariables, ClearServerCacheMutationVariables,
CreateBackupMutation,
CreateBackupMutationVariables,
CreateCategoryMutation, CreateCategoryMutation,
CreateCategoryMutationVariables, CreateCategoryMutationVariables,
DeleteCategoryMutation, DeleteCategoryMutation,
@@ -57,20 +59,24 @@ import type {
GetCategoryMangasQueryVariables, GetCategoryMangasQueryVariables,
GetChapterPagesFetchMutation, GetChapterPagesFetchMutation,
GetChapterPagesFetchMutationVariables, GetChapterPagesFetchMutationVariables,
GetChaptersHistoryQuery,
GetChaptersHistoryQueryVariables,
GetChaptersMangaQuery, GetChaptersMangaQuery,
GetChaptersMangaQueryVariables, GetChaptersMangaQueryVariables,
GetChaptersUpdatesQuery, GetChaptersUpdatesQuery,
GetChaptersUpdatesQueryVariables, GetChaptersUpdatesQueryVariables,
GetChaptersHistoryQuery,
GetChaptersHistoryQueryVariables,
GetDownloadStatusQuery, GetDownloadStatusQuery,
GetDownloadStatusQueryVariables, GetDownloadStatusQueryVariables,
GetExtensionQuery,
GetExtensionQueryVariables,
GetExtensionsFetchMutation, GetExtensionsFetchMutation,
GetExtensionsFetchMutationVariables, GetExtensionsFetchMutationVariables,
GetExtensionsQuery, GetExtensionsQuery,
GetExtensionsQueryVariables, GetExtensionsQueryVariables,
GetGlobalMetadatasQuery, GetGlobalMetadatasQuery,
GetGlobalMetadatasQueryVariables, GetGlobalMetadatasQueryVariables,
GetKoSyncStatusQuery,
GetKoSyncStatusQueryVariables,
GetLastUpdateTimestampQuery, GetLastUpdateTimestampQuery,
GetLastUpdateTimestampQueryVariables, GetLastUpdateTimestampQueryVariables,
GetMangaChaptersFetchMutation, GetMangaChaptersFetchMutation,
@@ -85,8 +91,6 @@ import type {
GetMangaToMigrateQueryVariables, GetMangaToMigrateQueryVariables,
GetMangaToMigrateToFetchMutation, GetMangaToMigrateToFetchMutation,
GetMangaToMigrateToFetchMutationVariables, GetMangaToMigrateToFetchMutationVariables,
GetMigratableSourceMangasQuery,
GetMigratableSourceMangasQueryVariables,
GetMigratableSourcesQuery, GetMigratableSourcesQuery,
GetMigratableSourcesQueryVariables, GetMigratableSourcesQueryVariables,
GetRestoreStatusQuery, GetRestoreStatusQuery,
@@ -103,6 +107,12 @@ import type {
GetWebuiUpdateStatusQueryVariables, GetWebuiUpdateStatusQueryVariables,
InstallExternalExtensionMutation, InstallExternalExtensionMutation,
InstallExternalExtensionMutationVariables, InstallExternalExtensionMutationVariables,
KoSyncLoginMutation,
KoSyncLoginMutationVariables,
KoSyncLogoutMutation,
KoSyncLogoutMutationVariables,
RefreshMangaMutation,
RefreshMangaMutationVariables,
ReorderChapterDownloadMutation, ReorderChapterDownloadMutation,
ReorderChapterDownloadMutationVariables, ReorderChapterDownloadMutationVariables,
ResetWebuiUpdateStatusMutation, ResetWebuiUpdateStatusMutation,
@@ -131,10 +141,14 @@ import type {
TrackerUnbindMutationVariables, TrackerUnbindMutationVariables,
TrackerUpdateBindMutation, TrackerUpdateBindMutation,
TrackerUpdateBindMutationVariables, TrackerUpdateBindMutationVariables,
UpdateCategoryMetadataMutation,
UpdateCategoryMetadataMutationVariables,
UpdateCategoryMutation, UpdateCategoryMutation,
UpdateCategoryMutationVariables, UpdateCategoryMutationVariables,
UpdateCategoryOrderMutation, UpdateCategoryOrderMutation,
UpdateCategoryOrderMutationVariables, UpdateCategoryOrderMutationVariables,
UpdateChapterMetadataMutation,
UpdateChapterMetadataMutationVariables,
UpdateChapterMutation, UpdateChapterMutation,
UpdateChapterMutationVariables, UpdateChapterMutationVariables,
UpdateChaptersMutation, UpdateChaptersMutation,
@@ -143,8 +157,14 @@ import type {
UpdateExtensionMutationVariables, UpdateExtensionMutationVariables,
UpdateExtensionsMutation, UpdateExtensionsMutation,
UpdateExtensionsMutationVariables, UpdateExtensionsMutationVariables,
UpdateGlobalMetadataMutation,
UpdateGlobalMetadataMutationVariables,
UpdateLibraryMutation,
UpdateLibraryMutationVariables,
UpdateMangaCategoriesMutation, UpdateMangaCategoriesMutation,
UpdateMangaCategoriesMutationVariables, UpdateMangaCategoriesMutationVariables,
UpdateMangaMetadataMutation,
UpdateMangaMetadataMutationVariables,
UpdateMangaMutation, UpdateMangaMutation,
UpdateMangaMutationVariables, UpdateMangaMutationVariables,
UpdateMangasCategoriesMutation, UpdateMangasCategoriesMutation,
@@ -155,74 +175,52 @@ import type {
UpdaterSubscriptionVariables, UpdaterSubscriptionVariables,
UpdateServerSettingsMutation, UpdateServerSettingsMutation,
UpdateServerSettingsMutationVariables, UpdateServerSettingsMutationVariables,
UpdateSourceMetadataMutation,
UpdateSourceMetadataMutationVariables,
UpdateSourcePreferencesMutation, UpdateSourcePreferencesMutation,
UpdateSourcePreferencesMutationVariables, UpdateSourcePreferencesMutationVariables,
UpdateWebuiMutation, UpdateWebuiMutation,
UpdateWebuiMutationVariables, UpdateWebuiMutationVariables,
ValidateBackupQuery,
ValidateBackupQueryVariables,
WebuiUpdateSubscription,
UpdateLibraryMutation,
UpdateLibraryMutationVariables,
GetExtensionQuery,
GetExtensionQueryVariables,
UserLoginMutation, UserLoginMutation,
UserLoginMutationVariables, UserLoginMutationVariables,
UserRefreshMutation, UserRefreshMutation,
UserRefreshMutationVariables, UserRefreshMutationVariables,
CreateBackupMutation, ValidateBackupQuery,
CreateBackupMutationVariables, ValidateBackupQueryVariables,
KoSyncLoginMutation, WebuiUpdateSubscription,
KoSyncLoginMutationVariables,
KoSyncLogoutMutation,
KoSyncLogoutMutationVariables,
GetKoSyncStatusQuery,
GetKoSyncStatusQueryVariables,
UpdateGlobalMetadataMutation,
UpdateGlobalMetadataMutationVariables,
UpdateMangaMetadataMutation,
UpdateMangaMetadataMutationVariables,
UpdateChapterMetadataMutation,
UpdateChapterMetadataMutationVariables,
UpdateCategoryMetadataMutation,
UpdateCategoryMetadataMutationVariables,
UpdateSourceMetadataMutation,
UpdateSourceMetadataMutationVariables,
RefreshMangaMutation,
RefreshMangaMutationVariables,
} from '@/lib/graphql/generated/graphql.ts'; } from '@/lib/graphql/generated/graphql.ts';
import type { import type {
ChapterConditionInput,
CreateBackupInput,
CreateCategoryInput,
DeleteCategoryMetasInput,
DeleteChapterMetasInput,
DeleteGlobalMetasInput, DeleteGlobalMetasInput,
SetGlobalMetasInput, DeleteMangaMetasInput,
UpdateExtensionPatchInput,
DeleteSourceMetasInput, DeleteSourceMetasInput,
SetSourceMetasInput,
FetchSourceMangaInput, FetchSourceMangaInput,
SourcePreferenceChangeInput,
FilterChangeInput, FilterChangeInput,
RestoreBackupInput,
SetCategoryMetasInput,
SetChapterMetasInput,
SetGlobalMetasInput,
SetMangaMetasInput,
SetSourceMetasInput,
SourcePreferenceChangeInput,
UpdateCategoryPatchInput,
UpdateChapterPatchInput,
UpdateExtensionPatchInput,
UpdateMangaCategoriesPatchInput, UpdateMangaCategoriesPatchInput,
UpdateMangaPatchInput, UpdateMangaPatchInput,
DeleteMangaMetasInput,
SetMangaMetasInput,
ChapterConditionInput,
UpdateChapterPatchInput,
DeleteChapterMetasInput,
SetChapterMetasInput,
CreateCategoryInput,
UpdateCategoryPatchInput,
DeleteCategoryMetasInput,
SetCategoryMetasInput,
CreateBackupInput,
RestoreBackupInput,
UpdateTrackInput, UpdateTrackInput,
} from '@/lib/graphql/generated/graphql-base.types.ts'; } from '@/lib/graphql/generated/graphql-base.types.ts';
import { import {
CategoryOrderBy, CategoryOrderBy,
ChapterOrderBy, ChapterOrderBy,
DownloaderState,
DownloadUpdateType, DownloadUpdateType,
FetchSourceMangaType, FetchSourceMangaType,
SortOrder, SortOrder,
DownloaderState,
} from '@/lib/graphql/generated/graphql-base.types.ts'; } from '@/lib/graphql/generated/graphql-base.types.ts';
import { GET_GLOBAL_METADATAS } from '@/lib/graphql/metadata/GlobalMetadataQuery.ts'; import { GET_GLOBAL_METADATAS } from '@/lib/graphql/metadata/GlobalMetadataQuery.ts';
import { UPDATE_GLOBAL_METADATA } from '@/lib/graphql/metadata/GlobalMetadataMutation.ts'; import { UPDATE_GLOBAL_METADATA } from '@/lib/graphql/metadata/GlobalMetadataMutation.ts';
@@ -250,12 +248,7 @@ import {
UPDATE_MANGAS, UPDATE_MANGAS,
UPDATE_MANGAS_CATEGORIES, UPDATE_MANGAS_CATEGORIES,
} from '@/lib/graphql/manga/MangaMutation.ts'; } from '@/lib/graphql/manga/MangaMutation.ts';
import { import { GET_MANGA_TO_MIGRATE, GET_MANGA_TRACK_RECORDS, GET_MANGAS_LIBRARY } from '@/lib/graphql/manga/MangaQuery.ts';
GET_MANGA_TO_MIGRATE,
GET_MANGA_TRACK_RECORDS,
GET_MANGAS_LIBRARY,
GET_MIGRATABLE_SOURCE_MANGAS,
} from '@/lib/graphql/manga/MangaQuery.ts';
import { import {
GET_CATEGORIES_BASE, GET_CATEGORIES_BASE,
GET_CATEGORIES_LIBRARY, GET_CATEGORIES_LIBRARY,
@@ -2282,13 +2275,6 @@ export class RequestManager {
return this.doRequest(GQLMethod.QUERY, document, variables, options); return this.doRequest(GQLMethod.QUERY, document, variables, options);
} }
public useGetMigratableSourceMangas(
sourceId: string,
options?: QueryHookOptions<GetMigratableSourceMangasQuery, GetMigratableSourceMangasQueryVariables>,
): AbortableApolloUseQueryResponse<GetMigratableSourceMangasQuery, GetMigratableSourceMangasQueryVariables> {
return this.doRequest(GQLMethod.USE_QUERY, GET_MIGRATABLE_SOURCE_MANGAS, { sourceId }, options);
}
public useUpdateMangaCategories( public useUpdateMangaCategories(
options?: MutationHookOptions<UpdateMangaCategoriesMutation, UpdateMangaCategoriesMutationVariables>, options?: MutationHookOptions<UpdateMangaCategoriesMutation, UpdateMangaCategoriesMutationVariables>,
): AbortableApolloUseMutationResponse<UpdateMangaCategoriesMutation, UpdateMangaCategoriesMutationVariables> { ): AbortableApolloUseMutationResponse<UpdateMangaCategoriesMutation, UpdateMangaCategoriesMutationVariables> {