Fix selecting migration destination from source browse search page
When going to the source search from the global search, the manga card mode wasn't passed, which prevented a manga from getting selected as the migration destination #1110
This commit is contained in:
@@ -6,7 +6,10 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import type { MangaCardMode } from '@/features/manga/Manga.types.ts';
|
||||
|
||||
export interface RouteStateSourcesSearchAll {
|
||||
title?: string;
|
||||
shouldShowOnlyPinnedSources?: boolean;
|
||||
mode?: MangaCardMode;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import { useLingui } from '@lingui/react/macro';
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import { AppbarSearch } from '@/base/components/AppbarSearch.tsx';
|
||||
import { useDebounce } from '@/base/hooks/useDebounce.ts';
|
||||
import type { MangaCardProps } from '@/features/manga/Manga.types.ts';
|
||||
import type { MangaCardProps, MangaIdInfo } from '@/features/manga/Manga.types.ts';
|
||||
import { EmptyView } from '@/base/components/feedback/EmptyView.tsx';
|
||||
import { STABLE_EMPTY_ARRAY, STABLE_EMPTY_OBJECT } from '@/base/Base.constants.ts';
|
||||
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
|
||||
@@ -59,7 +59,6 @@ import { SearchParam } from '@/base/Base.types.ts';
|
||||
import { MigrationManager } from '@/features/migration/MigrationManager.ts';
|
||||
import { assertIsDefined } from '@/base/Asserts.ts';
|
||||
import { ReactRouter } from '@/lib/react-router/ReactRouter.ts';
|
||||
import { SubpathUtil } from '@/lib/utils/SubpathUtil.ts';
|
||||
import type { RouteStateSourcesSearchAll } from '@/features/global-search/SearchAll.types.ts';
|
||||
|
||||
type SourceLoadingState = { isLoading: boolean; hasResults: boolean; emptySearch: boolean; error: any };
|
||||
@@ -128,11 +127,13 @@ const SourceSearchPreview = React.memo(
|
||||
mode,
|
||||
shouldShowOnlySourcesWithResults,
|
||||
onMigrateSelect,
|
||||
mangaId,
|
||||
}: {
|
||||
source: SourceIdInfo & SourceDisplayNameInfo & SourceNameInfo & SourceLanguageInfo;
|
||||
onSearchRequestFinished: (source: SourceIdInfo, state: SourceLoadingState) => void;
|
||||
searchString: string | null | undefined;
|
||||
emptyQuery: boolean;
|
||||
mangaId?: MangaIdInfo['id'];
|
||||
} & Pick<MangaCardProps, 'mode' | 'onMigrateSelect'> &
|
||||
Pick<MetadataBrowseSettings, 'shouldShowOnlySourcesWithResults'>) => {
|
||||
const { t } = useLingui();
|
||||
@@ -194,6 +195,10 @@ const SourceSearchPreview = React.memo(
|
||||
<CardActionArea
|
||||
component={Link}
|
||||
to={AppRoutes.sources.children.browse.path(id, searchString)}
|
||||
state={AppRoutes.sources.children.browse.state({
|
||||
mode,
|
||||
mangaId,
|
||||
})}
|
||||
sx={{ p: 1, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}
|
||||
>
|
||||
<Box>
|
||||
@@ -254,8 +259,6 @@ export const SearchAll = ({
|
||||
const { state } = useLocation<RouteStateSourcesSearchAll>();
|
||||
const { ref: filterHeaderRef, height: filterHeaderHeight } = useElementSize();
|
||||
|
||||
const isMigrateMode = SubpathUtil.getPathname().startsWith(AppRoutes.migrate.path);
|
||||
|
||||
const { mangaId } = useParams<{ mangaId?: string }>() ?? STABLE_EMPTY_OBJECT;
|
||||
const [query] = useQueryParam(SearchParam.QUERY, StringParam);
|
||||
const searchString = useDebounce(query, TRIGGER_SEARCH_THRESHOLD);
|
||||
@@ -281,7 +284,13 @@ export const SearchAll = ({
|
||||
const sourceLanguages = useMemo(() => Sources.getLanguages(sources, { excludeLocalSource: true }), [sources]);
|
||||
|
||||
const hasPinnedSources = useMemo(() => !!Sources.filter(sources, { pinned: true }).length, [sources]);
|
||||
const shouldShowOnlyPinnedSources = state?.shouldShowOnlyPinnedSources ?? hasPinnedSources;
|
||||
|
||||
const {
|
||||
title = t`Global Search`,
|
||||
shouldShowOnlyPinnedSources = hasPinnedSources,
|
||||
mode = 'source',
|
||||
} = state ?? STABLE_EMPTY_OBJECT;
|
||||
const isMigrateMode = ['migrate.select.single', 'migrate.select.bulk'].includes(mode);
|
||||
|
||||
const filteredSources = useMemo(
|
||||
() =>
|
||||
@@ -319,7 +328,7 @@ export const SearchAll = ({
|
||||
);
|
||||
|
||||
useAppTitleAndAction(
|
||||
isMigrateMode ? state?.title : t`Global Search`,
|
||||
title,
|
||||
<>
|
||||
<AppbarSearch isClosable={false} />
|
||||
<SourceLanguageSelect
|
||||
@@ -429,10 +438,10 @@ export const SearchAll = ({
|
||||
onSearchRequestFinished={updateSourceLoadingState}
|
||||
searchString={searchString}
|
||||
emptyQuery={!query}
|
||||
mode={isMigrateMode ? 'migrate.select' : 'source'}
|
||||
mode={mode}
|
||||
shouldShowOnlySourcesWithResults={shouldShowOnlySourcesWithResults}
|
||||
onMigrateSelect={
|
||||
migrationDestinationSourceIds
|
||||
isMigrateMode
|
||||
? (match) => {
|
||||
assertIsDefined(mangaId);
|
||||
MigrationManager.selectManualMatch(Number(mangaId), {
|
||||
@@ -444,6 +453,7 @@ export const SearchAll = ({
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
mangaId={mangaId ? Number(mangaId) : undefined}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
|
||||
@@ -30,7 +30,7 @@ import type {
|
||||
TrackRecordType,
|
||||
} from '@/lib/graphql/generated/graphql-base.types.ts';
|
||||
|
||||
export type MangaCardMode = 'default' | 'source' | 'migrate.search' | 'migrate.select' | 'duplicate';
|
||||
export type MangaCardMode = 'default' | 'source' | 'migrate.select.bulk' | 'migrate.select.single' | 'duplicate';
|
||||
|
||||
type MangaCardBaseProps = Pick<MangaTypeGql, 'id' | 'title' | 'sourceId'> &
|
||||
Omit<SingleModeProps['manga'], 'downloadCount' | 'unreadCount' | 'chapters'> &
|
||||
|
||||
@@ -31,21 +31,16 @@ import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler';
|
||||
import { MangaMigration } from '@/features/migration/MangaMigration.ts';
|
||||
import { MANGA_ACTION_TO_TRANSLATION } from '@/features/manga/Manga.constants.ts';
|
||||
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
||||
import { assertIsDefined } from '@/base/Asserts.ts';
|
||||
|
||||
const getMangaLinkTo = (
|
||||
mode: MangaCardMode,
|
||||
mangaId: number,
|
||||
sourceId: string | undefined,
|
||||
mangaTitle: string,
|
||||
): string => {
|
||||
const getMangaLinkTo = (mode: MangaCardMode, mangaId: number): string => {
|
||||
switch (mode) {
|
||||
case 'default':
|
||||
case 'source':
|
||||
case 'duplicate':
|
||||
return AppRoutes.manga.path(mangaId);
|
||||
case 'migrate.search':
|
||||
return AppRoutes.migrate.children.singleMangaSearch.path(sourceId ?? '-1', mangaId, mangaTitle);
|
||||
case 'migrate.select':
|
||||
case 'migrate.select.single':
|
||||
case 'migrate.select.bulk':
|
||||
return '';
|
||||
default:
|
||||
throw new Error(`getMangaLinkTo: unexpected MangaCardMode "${mode}"`);
|
||||
@@ -73,13 +68,15 @@ export const MangaCard = memo((props: MangaCardProps) => {
|
||||
|
||||
const { updateLibraryState, isInLibrary } = useManageMangaLibraryState(manga, mode === 'source');
|
||||
|
||||
const mangaLinkTo = getMangaLinkTo(mode, manga.id, manga.sourceId, manga.title);
|
||||
const mangaLinkTo = getMangaLinkTo(mode, manga.id);
|
||||
|
||||
const handleClick = useCallback(
|
||||
(event: React.MouseEvent | React.TouchEvent, openMenu?: () => void) => {
|
||||
const isDefaultMode = mode === 'default';
|
||||
const isSourceMode = mode === 'source';
|
||||
const isMigrateSelectMode = mode === 'migrate.select';
|
||||
const isMigrationSelectSingleMode = mode === 'migrate.select.single';
|
||||
const isMigrationSelectBulkMode = mode === 'migrate.select.bulk';
|
||||
const isMigrateSelectMode = isMigrationSelectSingleMode || isMigrationSelectBulkMode;
|
||||
const isSelectionMode = selected !== null;
|
||||
const isLongPress = !!openMenu;
|
||||
|
||||
@@ -107,8 +104,8 @@ export const MangaCard = memo((props: MangaCardProps) => {
|
||||
}
|
||||
|
||||
if (isMigrateSelectMode) {
|
||||
const isBulkMigrationManualSearch = !!onMigrateSelect;
|
||||
if (isBulkMigrationManualSearch) {
|
||||
if (isMigrationSelectBulkMode) {
|
||||
assertIsDefined(onMigrateSelect);
|
||||
onMigrateSelect({ ...manga, missingChapters: undefined });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -323,6 +323,7 @@ export class MigrationManager {
|
||||
{
|
||||
state: AppRoutes.migrate.children.singleMangaSearch.state({
|
||||
title: t`Migrate "${manga.title}"`,
|
||||
mode: 'migrate.select.single',
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -164,7 +164,7 @@ export const MigrationSelectMangas = () => {
|
||||
return (
|
||||
<>
|
||||
<BaseMangaGrid
|
||||
mode="migrate.select"
|
||||
mode="migrate.select.bulk"
|
||||
hasNextPage={false}
|
||||
loadMore={noOp}
|
||||
isLoading={areMangasLoading}
|
||||
|
||||
@@ -16,7 +16,7 @@ import type {
|
||||
SourcePreferenceChangeInput,
|
||||
SourceType,
|
||||
} from '@/lib/graphql/generated/graphql-base.types.ts';
|
||||
import type { MangaCardMode } from '@/features/manga/Manga.types.ts';
|
||||
import type { MangaCardMode, MangaIdInfo } from '@/features/manga/Manga.types.ts';
|
||||
|
||||
export interface IPos {
|
||||
type: 'selectState' | 'textState' | 'checkBoxState' | 'triState' | 'sortState';
|
||||
@@ -86,4 +86,5 @@ export interface RouteStateSourceBrowse {
|
||||
contentType?: SourceContentType;
|
||||
clearCache?: boolean;
|
||||
mode?: MangaCardMode;
|
||||
mangaId?: MangaIdInfo['id'];
|
||||
}
|
||||
|
||||
@@ -58,6 +58,8 @@ import { useAppTitleAndAction } from '@/features/navigation-bar/hooks/useAppTitl
|
||||
import { useNavBarContext } from '@/features/navigation-bar/NavbarContext.tsx';
|
||||
import { VirtuosoUtil } from '@/lib/virtuoso/Virtuoso.util.tsx';
|
||||
import { IconWebView } from '@/assets/icons/IconWebView.tsx';
|
||||
import { MigrationManager } from '@/features/migration/MigrationManager.ts';
|
||||
import { ReactRouter } from '@/lib/react-router/ReactRouter.ts';
|
||||
|
||||
const DEFAULT_SOURCE: SourceIdInfo = { id: '-1' };
|
||||
|
||||
@@ -216,6 +218,8 @@ export function SourceMangas() {
|
||||
const {
|
||||
contentType: initialContentType = SourceContentType.POPULAR,
|
||||
clearCache = false,
|
||||
mode = 'source',
|
||||
mangaId,
|
||||
} = useLocation<RouteStateSourceBrowse>().state ?? STABLE_EMPTY_OBJECT;
|
||||
|
||||
const {
|
||||
@@ -485,8 +489,20 @@ export function SourceMangas() {
|
||||
messageExtra={messageExtra}
|
||||
isLoading={isLoading}
|
||||
gridLayout={sourceGridLayout}
|
||||
mode="source"
|
||||
mode={mode}
|
||||
inLibraryIndicator
|
||||
onMigrateSelect={
|
||||
mangaId
|
||||
? (match) => {
|
||||
MigrationManager.selectManualMatch(mangaId, {
|
||||
...match,
|
||||
sourceTitle: Sources.getFromCache(match.sourceId)?.displayName,
|
||||
latestChapterNumber: undefined,
|
||||
});
|
||||
ReactRouter.navigate(AppRoutes.migrate.path);
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{error && (
|
||||
|
||||
Reference in New Issue
Block a user