Sort bulk migration execution page entries

This commit is contained in:
schroda
2026-05-16 02:23:36 +02:00
parent d154e01490
commit 21c16d4ba9
4 changed files with 82 additions and 44 deletions

View File

@@ -14,7 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- (**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 - (**Migration**) Sort manga to migrate selection by title and by recently added to the library
- (**Migration**) Sort destination source selection by same order as browse source page - (**Migration**) Sort destination source selection by same order as browse source page
- (**Migration**) Sort entries in search page by title - (**Migration**) Sort entries in search/execution page by title
### Fixed ### Fixed

View File

@@ -0,0 +1,46 @@
/*
* 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 type { MigrationEntryStatus, TMigrationEntry } from '@/features/migration/Migration.types.ts';
export class MigrationEntries {
public static getExcluded(entries: TMigrationEntry[]): TMigrationEntry[] {
return entries.filter((entry) => entry.isExcluded);
}
public static getHaveStatus(entries: TMigrationEntry[], ...statuses: MigrationEntryStatus[]): TMigrationEntry[] {
return entries.filter((entry) => statuses.includes(entry.status));
}
public static getHaveStatusSorted(
entries: TMigrationEntry[],
...statuses: MigrationEntryStatus[]
): TMigrationEntry[] {
return MigrationEntries.getHaveStatus(entries, ...statuses).toSorted((a, b) =>
a.mangaTitle.localeCompare(b.mangaTitle),
);
}
public static getActiveEntriesSorted(
entries: TMigrationEntry[],
activeStatus: MigrationEntryStatus,
...statuses: MigrationEntryStatus[]
): TMigrationEntry[] {
return MigrationEntries.getHaveStatus(entries, ...statuses).toSorted((a, b) => {
if (a.status === activeStatus && b.status !== activeStatus) {
return -1;
}
if (a.status !== activeStatus && b.status === activeStatus) {
return 1;
}
return a.mangaTitle.localeCompare(b.mangaTitle);
});
}
}

View File

@@ -17,6 +17,7 @@ import { DEFAULT_FULL_FAB_HEIGHT } from '@/base/components/buttons/StyledFab.tsx
import { MigrationContinueButton } from '@/features/migration/components/MigrationContinueButton.tsx'; import { MigrationContinueButton } from '@/features/migration/components/MigrationContinueButton.tsx';
import { plural } from '@lingui/core/macro'; import { plural } from '@lingui/core/macro';
import { MigrationEntryGroup } from '@/features/migration/components/MIgrationEntryGroup.tsx'; import { MigrationEntryGroup } from '@/features/migration/components/MIgrationEntryGroup.tsx';
import { MigrationEntries } from '@/features/migration/MigrationEntries.ts';
export const MigrationExecute = () => { export const MigrationExecute = () => {
const { t } = useLingui(); const { t } = useLingui();
@@ -42,38 +43,29 @@ export const MigrationExecute = () => {
const entryList = useMemo(() => Object.values(entries), [entries]); const entryList = useMemo(() => Object.values(entries), [entries]);
const migratingEntries = useMemo( const migratingEntries = useMemo(
() => () =>
entryList MigrationEntries.getActiveEntriesSorted(
.filter((entry) => entryList,
[MigrationEntryStatus.SEARCH_COMPLETE, MigrationEntryStatus.MIGRATING].includes(entry.status), MigrationEntryStatus.MIGRATING,
) MigrationEntryStatus.SEARCH_COMPLETE,
.toSorted((a, b) => { MigrationEntryStatus.MIGRATING,
if (a.status === MigrationEntryStatus.MIGRATING && b.status !== MigrationEntryStatus.MIGRATING) { ),
return -1;
}
if (a.status !== MigrationEntryStatus.MIGRATING && b.status === MigrationEntryStatus.MIGRATING) {
return 1;
}
return entryList.indexOf(a) - entryList.indexOf(b);
}),
[entryList], [entryList],
); );
const migratedEntries = useMemo( const migratedEntries = useMemo(
() => entryList.filter((entry) => entry.status === MigrationEntryStatus.MIGRATION_COMPLETE), () => MigrationEntries.getHaveStatusSorted(entryList, MigrationEntryStatus.MIGRATION_COMPLETE),
[entryList], [entryList],
); );
const failedEntries = useMemo( const failedEntries = useMemo(
() => entryList.filter((entry) => entry.status === MigrationEntryStatus.MIGRATION_FAILED), () => MigrationEntries.getHaveStatusSorted(entryList, MigrationEntryStatus.MIGRATION_FAILED),
[entryList], [entryList],
); );
const excludedEntries = useMemo(() => entryList.filter((entry) => entry.isExcluded), [entryList]); const excludedEntries = useMemo(() => MigrationEntries.getExcluded(entryList), [entryList]);
const noMatchEntries = useMemo( const noMatchEntries = useMemo(
() => entryList.filter((entry) => entry.status === MigrationEntryStatus.NO_MATCH), () => MigrationEntries.getHaveStatusSorted(entryList, MigrationEntryStatus.NO_MATCH),
[entryList], [entryList],
); );
const outdatedEntries = useMemo( const outdatedEntries = useMemo(
() => entryList.filter((entry) => entry.status === MigrationEntryStatus.OUTDATED), () => MigrationEntries.getHaveStatusSorted(entryList, MigrationEntryStatus.OUTDATED),
[entryList], [entryList],
); );

View File

@@ -17,13 +17,10 @@ import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts'
import { DEFAULT_FULL_FAB_HEIGHT } from '@/base/components/buttons/StyledFab.tsx'; import { DEFAULT_FULL_FAB_HEIGHT } from '@/base/components/buttons/StyledFab.tsx';
import { useMemo } from 'react'; import { useMemo } from 'react';
import Stack from '@mui/material/Stack'; import Stack from '@mui/material/Stack';
import type { TMigrationEntry } from '@/features/migration/Migration.types.ts';
import { MigrationEntryStatus } from '@/features/migration/Migration.types.ts'; import { MigrationEntryStatus } from '@/features/migration/Migration.types.ts';
import { MigrationEntryGroup } from '@/features/migration/components/MIgrationEntryGroup.tsx'; import { MigrationEntryGroup } from '@/features/migration/components/MIgrationEntryGroup.tsx';
import { plural } from '@lingui/core/macro'; import { plural } from '@lingui/core/macro';
import { MigrationEntries } from '@/features/migration/MigrationEntries.ts';
const getEntries = (status: MigrationEntryStatus, entries: TMigrationEntry[]): TMigrationEntry[] =>
entries.filter((entry) => entry.status === status).toSorted((a, b) => a.mangaTitle.localeCompare(b.mangaTitle));
export const MigrationSearch = () => { export const MigrationSearch = () => {
const { t } = useLingui(); const { t } = useLingui();
@@ -37,27 +34,30 @@ export const MigrationSearch = () => {
const entryList = useMemo(() => Object.values(entries), [entries]); const entryList = useMemo(() => Object.values(entries), [entries]);
const searchingEntries = useMemo( const searchingEntries = useMemo(
() => () =>
entryList MigrationEntries.getActiveEntriesSorted(
.filter((entry) => entryList,
[MigrationEntryStatus.PENDING, MigrationEntryStatus.SEARCHING].includes(entry.status), MigrationEntryStatus.SEARCHING,
) MigrationEntryStatus.PENDING,
.toSorted((a, b) => { MigrationEntryStatus.SEARCHING,
if (a.status === MigrationEntryStatus.SEARCHING && b.status !== MigrationEntryStatus.SEARCHING) { ),
return -1; [entryList],
} );
const failedEntries = useMemo(
if (a.status !== MigrationEntryStatus.SEARCHING && b.status === MigrationEntryStatus.SEARCHING) { () => MigrationEntries.getHaveStatusSorted(entryList, MigrationEntryStatus.SEARCH_FAILED),
return 1; [entryList],
} );
const noMatchEntries = useMemo(
return a.mangaTitle.localeCompare(b.mangaTitle); () => MigrationEntries.getHaveStatusSorted(entryList, MigrationEntryStatus.NO_MATCH),
}), [entryList],
);
const outdatedEntries = useMemo(
() => MigrationEntries.getHaveStatusSorted(entryList, MigrationEntryStatus.OUTDATED),
[entryList],
);
const matchedEntries = useMemo(
() => MigrationEntries.getHaveStatusSorted(entryList, MigrationEntryStatus.SEARCH_COMPLETE),
[entryList], [entryList],
); );
const failedEntries = useMemo(() => getEntries(MigrationEntryStatus.SEARCH_FAILED, entryList), [entryList]);
const noMatchEntries = useMemo(() => getEntries(MigrationEntryStatus.NO_MATCH, entryList), [entryList]);
const outdatedEntries = useMemo(() => getEntries(MigrationEntryStatus.OUTDATED, entryList), [entryList]);
const matchedEntries = useMemo(() => getEntries(MigrationEntryStatus.SEARCH_COMPLETE, entryList), [entryList]);
const hasMigratableEntries = useMemo(() => !!MigrationManager.getMigratableEntries().length, [entryList]); const hasMigratableEntries = useMemo(() => !!MigrationManager.getMigratableEntries().length, [entryList]);