Optionally ignore missing chapter matches during bulk migration
This commit is contained in:
@@ -109,7 +109,7 @@ export const MangaCard = memo((props: MangaCardProps) => {
|
||||
if (isMigrateSelectMode) {
|
||||
const isBulkMigrationManualSearch = !!onMigrateSelect;
|
||||
if (isBulkMigrationManualSearch) {
|
||||
onMigrateSelect(manga);
|
||||
onMigrateSelect({ ...manga, missingChapters: undefined });
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@ export interface MigrationBulkSearchSettings {
|
||||
selectHighestChapterNumberSource: boolean;
|
||||
ignoreOutdatedMatches: boolean;
|
||||
requireAdditionalChapters: boolean;
|
||||
ignoreWithMissingChapters: boolean;
|
||||
performAdvancedSearch: boolean;
|
||||
}
|
||||
|
||||
@@ -102,6 +103,7 @@ export interface MigrationMatch
|
||||
extends MangaIdInfo, MangaTitleInfo, MangaThumbnailInfo, MangaSourceIdInfo, MangaArtistInfo, MangaAuthorInfo {
|
||||
sourceTitle: SourceDisplayNameInfo['displayName'] | undefined;
|
||||
latestChapterNumber: ChapterNumberInfo['chapterNumber'] | undefined;
|
||||
missingChapters: number | undefined;
|
||||
}
|
||||
|
||||
export interface TMigrationEntry {
|
||||
@@ -110,6 +112,7 @@ export interface TMigrationEntry {
|
||||
mangaArtist: MangaArtistInfo['artist'];
|
||||
mangaAuthor: MangaAuthorInfo['author'];
|
||||
latestChapterNumber: ChapterNumberInfo['chapterNumber'] | undefined;
|
||||
missingChapters: number | undefined;
|
||||
mangaThumbnailUrl: MangaThumbnailInfo['thumbnailUrl'] | undefined;
|
||||
sourceId: SourceIdInfo['id'];
|
||||
sourceTitle: SourceDisplayNameInfo['displayName'] | undefined;
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import { GET_MIGRATION_SOURCE_MANGAS_FETCH } from '@/lib/graphql/source/SourceMutation.ts';
|
||||
import type {
|
||||
ChapterListFieldsFragment,
|
||||
GetMigrationSourceMangasFetchMutation,
|
||||
GetMigrationSourceMangasFetchMutationVariables,
|
||||
GetServerSettingsQuery,
|
||||
@@ -67,6 +68,7 @@ import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
||||
import isEqual from 'lodash/fp/isEqual';
|
||||
import uniqBy from 'lodash/fp/uniqBy';
|
||||
import { MigrationEntries } from '@/features/migration/MigrationEntries.ts';
|
||||
import { Chapters } from '@/features/chapter/services/Chapters.ts';
|
||||
|
||||
const RESUMABLE_PHASES: readonly MigrationPhase[] = [MigrationPhase.SEARCHING, MigrationPhase.MIGRATING];
|
||||
|
||||
@@ -246,6 +248,7 @@ export class MigrationManager {
|
||||
author: cachedEntry?.author ?? searchMatch.author,
|
||||
sourceTitle: cachedEntry?.source?.displayName ?? searchMatch.sourceTitle,
|
||||
latestChapterNumber: cachedEntry?.highestNumberedChapter?.chapterNumber ?? searchMatch.latestChapterNumber,
|
||||
missingChapters: searchMatch.missingChapters,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -262,6 +265,7 @@ export class MigrationManager {
|
||||
mangaArtist: cachedEntry?.artist ?? entry.mangaArtist,
|
||||
mangaAuthor: cachedEntry?.author ?? entry.mangaAuthor,
|
||||
latestChapterNumber: cachedEntry?.highestNumberedChapter?.chapterNumber ?? entry.latestChapterNumber,
|
||||
missingChapters: entry.missingChapters,
|
||||
mangaThumbnailUrl: cachedEntry?.thumbnailUrl ?? entry.mangaThumbnailUrl,
|
||||
sourceId: cachedEntry?.sourceId ?? entry.sourceId,
|
||||
sourceTitle: cachedEntry?.source?.displayName ?? entry.sourceTitle,
|
||||
@@ -336,6 +340,7 @@ export class MigrationManager {
|
||||
mangaArtist: manga.artist,
|
||||
mangaAuthor: manga.author,
|
||||
latestChapterNumber: manga.highestNumberedChapter?.chapterNumber,
|
||||
missingChapters: undefined,
|
||||
mangaThumbnailUrl: manga.thumbnailUrl,
|
||||
sourceId: manga.sourceId,
|
||||
sourceTitle: manga.source?.displayName,
|
||||
@@ -710,7 +715,7 @@ export class MigrationManager {
|
||||
sourceId: SourceIdInfo['id'],
|
||||
signal: AbortSignal,
|
||||
{ selectHighestChapterNumberSource, performAdvancedSearch }: MigrationBulkSearchSettings,
|
||||
): Promise<MangaMigrationFieldsFragment[]> {
|
||||
): Promise<{ manga: MangaMigrationFieldsFragment; chapters: ChapterListFieldsFragment[] | null }[]> {
|
||||
if (signal.aborted) {
|
||||
throw new Error(signal.reason);
|
||||
}
|
||||
@@ -781,12 +786,20 @@ export class MigrationManager {
|
||||
}).response,
|
||||
);
|
||||
|
||||
return updatedMatch.data?.fetchManga?.manga ?? match;
|
||||
if (updatedMatch.data?.fetchManga?.manga) {
|
||||
return {
|
||||
manga: updatedMatch.data.fetchManga.manga,
|
||||
chapters: updatedMatch.data.fetchChapters?.chapters ?? null,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
return match;
|
||||
return {
|
||||
manga: match,
|
||||
chapters: null,
|
||||
};
|
||||
})();
|
||||
});
|
||||
|
||||
@@ -801,7 +814,12 @@ export class MigrationManager {
|
||||
mainSignal: AbortSignal,
|
||||
options: MigrationBulkSearchSettings,
|
||||
): Promise<void> {
|
||||
const { selectHighestChapterNumberSource, ignoreOutdatedMatches, requireAdditionalChapters } = options;
|
||||
const {
|
||||
selectHighestChapterNumberSource,
|
||||
ignoreOutdatedMatches,
|
||||
requireAdditionalChapters,
|
||||
ignoreWithMissingChapters,
|
||||
} = options;
|
||||
|
||||
const state = MigrationManager.getState();
|
||||
const entry = state.entries[mangaId];
|
||||
@@ -873,9 +891,11 @@ export class MigrationManager {
|
||||
: null;
|
||||
|
||||
const newMatches = foundMatches.filter((newMatch) =>
|
||||
draftEntry.searchMatches.every((existingMatch) => newMatch.id !== existingMatch.id),
|
||||
draftEntry.searchMatches.every(
|
||||
(existingMatch) => newMatch.manga.id !== existingMatch.id,
|
||||
),
|
||||
);
|
||||
const matches = newMatches.map((manga) => ({
|
||||
const matches = newMatches.map(({ manga, chapters }) => ({
|
||||
id: manga.id,
|
||||
title: manga.title,
|
||||
artist: manga.artist,
|
||||
@@ -884,6 +904,7 @@ export class MigrationManager {
|
||||
thumbnailUrl: manga.thumbnailUrl,
|
||||
sourceId: manga.sourceId,
|
||||
sourceTitle: manga.source?.displayName,
|
||||
missingChapters: chapters ? Chapters.getMissingCount(chapters) : undefined,
|
||||
}));
|
||||
|
||||
draftEntry.destSourceIdToSearchState[destSourceId] = true;
|
||||
@@ -921,6 +942,8 @@ export class MigrationManager {
|
||||
|
||||
const ignoreOutdatedMatch = ignoreOutdatedMatches && isOutdated;
|
||||
const satisfiesRequireAdditionalChapters = !requireAdditionalChapters || hasNewerChapter;
|
||||
const ignoreBecauseMissingChapters =
|
||||
ignoreWithMissingChapters && !!bestMatch.missingChapters;
|
||||
const isPreferredSourcePriorityMatch =
|
||||
hasHigherSourcePriority && !selectHighestChapterNumberSource;
|
||||
const isPreferredChapterNumberMatch =
|
||||
@@ -933,6 +956,7 @@ export class MigrationManager {
|
||||
!draftEntry.isManualSelection &&
|
||||
!ignoreOutdatedMatch &&
|
||||
satisfiesRequireAdditionalChapters &&
|
||||
!ignoreBecauseMissingChapters &&
|
||||
(isPreferredSourcePriorityMatch || isPreferredChapterNumberMatch);
|
||||
if (isPreferredMatch) {
|
||||
draftEntry.selectedMatchMangaId = bestMatch.id;
|
||||
|
||||
@@ -30,6 +30,7 @@ export const MigrationBulkSearchOptionsDialog = ({
|
||||
|
||||
const [selectHighestChapterNumberSource, setSelectHighestChapterNumberSource] = useState(false);
|
||||
const [ignoreOutdatedMatches, setIgnoreOutdatedMatches] = useState(false);
|
||||
const [ignoreWithMissingChapters, setIgnoreWithMissingChapters] = useState(false);
|
||||
const [requireAdditionalChapters, setRequireAdditionalChapters] = useState(false);
|
||||
const [performAdvancedSearch, setPerformAdvancedSearch] = useState(false);
|
||||
|
||||
@@ -49,7 +50,7 @@ export const MigrationBulkSearchOptionsDialog = ({
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="textSecondary"
|
||||
>{t`Only automatically select matches if they have at least the same latest chapter. They will still be shown in the found matches`}</Typography>
|
||||
>{t`Automatically select matches if they have at least the same latest chapter`}</Typography>
|
||||
</Stack>
|
||||
}
|
||||
sx={{
|
||||
@@ -70,7 +71,7 @@ export const MigrationBulkSearchOptionsDialog = ({
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="textSecondary"
|
||||
>{t`Only automatically select matches if they have additional chapters. They will still be shown in the found matches`}</Typography>
|
||||
>{t`Automatically select matches if they have additional chapters`}</Typography>
|
||||
</Stack>
|
||||
}
|
||||
sx={{
|
||||
@@ -79,6 +80,27 @@ export const MigrationBulkSearchOptionsDialog = ({
|
||||
checked={requireAdditionalChapters}
|
||||
onChange={(_, checked) => setRequireAdditionalChapters(checked)}
|
||||
/>
|
||||
<CheckboxInput
|
||||
label={
|
||||
<Stack
|
||||
sx={{
|
||||
// Padding comes from the MUI Checkbox component
|
||||
pt: '9px',
|
||||
}}
|
||||
>
|
||||
<Typography>{t`Ignore matches with missing chapters`}</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="textSecondary"
|
||||
>{t`Automatically select matches if they have no missing chapters`}</Typography>
|
||||
</Stack>
|
||||
}
|
||||
sx={{
|
||||
alignItems: 'start',
|
||||
}}
|
||||
checked={ignoreWithMissingChapters}
|
||||
onChange={(_, checked) => setIgnoreWithMissingChapters(checked)}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogContent dividers>
|
||||
<Stack
|
||||
@@ -152,6 +174,7 @@ export const MigrationBulkSearchOptionsDialog = ({
|
||||
selectHighestChapterNumberSource,
|
||||
ignoreOutdatedMatches,
|
||||
requireAdditionalChapters,
|
||||
ignoreWithMissingChapters,
|
||||
performAdvancedSearch,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,22 +11,44 @@ import Typography from '@mui/material/Typography';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
|
||||
export const MigrationEntryMetadataText = (
|
||||
entry: Pick<MigrationMatch, 'artist' | 'author' | 'latestChapterNumber'>,
|
||||
entry: Pick<MigrationMatch, 'artist' | 'author' | 'latestChapterNumber' | 'missingChapters'>,
|
||||
) => {
|
||||
const { t } = useLingui();
|
||||
|
||||
const latestChapterNumber = (entry.latestChapterNumber ?? 0) > 1 ? entry.latestChapterNumber : t`Unknown`;
|
||||
const latestChapter = t`Latest: ${latestChapterNumber}`;
|
||||
const isSameArtistAuthor = entry.artist === entry.author;
|
||||
|
||||
const artist = entry.artist ? `${entry.artist} - ` : '';
|
||||
const author = entry.author ? `${entry.author} - ` : '';
|
||||
const isSameArtistAuthor = artist === author;
|
||||
const artistAuthor = isSameArtistAuthor ? artist : `${artist}${author}`;
|
||||
const nodes = [
|
||||
entry.author && (
|
||||
<Typography sx={{ display: 'inline-flex' }} key="author" variant="inherit">
|
||||
{entry.author}
|
||||
</Typography>
|
||||
),
|
||||
!isSameArtistAuthor && entry.artist ? (
|
||||
<Typography sx={{ display: 'inline-flex' }} key="artist" variant="inherit">
|
||||
{entry.artist}
|
||||
</Typography>
|
||||
) : null,
|
||||
<Typography sx={{ display: 'inline-flex' }} key="latest-chapter-number" variant="inherit">
|
||||
{t`Latest: ${latestChapterNumber}`}
|
||||
</Typography>,
|
||||
entry.missingChapters ? (
|
||||
<Typography sx={{ display: 'inline-flex' }} key="missing-chapters" variant="inherit" color="warning">
|
||||
{t`Missing: ${entry.missingChapters}`}
|
||||
</Typography>
|
||||
) : null,
|
||||
]
|
||||
.filter((node) => node !== null)
|
||||
.map((node, index, array) => (
|
||||
<>
|
||||
{node}
|
||||
{index < array.length - 1 && ' - '}
|
||||
</>
|
||||
));
|
||||
|
||||
return (
|
||||
<Typography variant="body2" color="textSecondary">
|
||||
{artistAuthor}
|
||||
{latestChapter}
|
||||
{nodes}
|
||||
</Typography>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -31,6 +31,7 @@ export const MigrationSourceEntry = memo((entry: TMigrationEntry) => {
|
||||
mangaArtist,
|
||||
mangaAuthor,
|
||||
latestChapterNumber,
|
||||
missingChapters,
|
||||
searchMatches,
|
||||
status,
|
||||
} = entry;
|
||||
@@ -50,6 +51,7 @@ export const MigrationSourceEntry = memo((entry: TMigrationEntry) => {
|
||||
artist={mangaArtist}
|
||||
author={mangaAuthor}
|
||||
latestChapterNumber={latestChapterNumber}
|
||||
missingChapters={missingChapters}
|
||||
/>
|
||||
</Stack>
|
||||
<Box sx={{ display: 'flex', gap: 4, alignItems: 'center' }}>
|
||||
@@ -91,6 +93,7 @@ export const MigrationSourceEntry = memo((entry: TMigrationEntry) => {
|
||||
artist={mangaArtist}
|
||||
author={mangaAuthor}
|
||||
latestChapterNumber={latestChapterNumber}
|
||||
missingChapters={missingChapters}
|
||||
/>
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user