Optionally migrate track bindings

This commit is contained in:
schroda
2024-04-20 23:51:47 +02:00
parent 0d8bb789a0
commit 2066851793
9 changed files with 116 additions and 21 deletions

View File

@@ -35,7 +35,7 @@ export const MigrateDialog = ({ mangaIdToMigrateTo, onClose }: { mangaIdToMigrat
const mangaId = Number(mangaIdAsString);
const {
settings: { migrateChapters, migrateCategories, deleteChapters },
settings: { migrateChapters, migrateCategories, migrateTracking, deleteChapters },
} = useMetadataServerSettings();
const [isMigrationInProcess, setIsMigrationInProcess] = useState(false);
@@ -58,6 +58,7 @@ export const MigrateDialog = ({ mangaIdToMigrateTo, onClose }: { mangaIdToMigrat
mode,
migrateChapters,
migrateCategories,
migrateTracking,
deleteChapters,
});
@@ -84,6 +85,12 @@ export const MigrateDialog = ({ mangaIdToMigrateTo, onClose }: { mangaIdToMigrat
checked={migrateCategories}
onChange={(_, checked) => setMigrationFlag('migrateCategories', checked)}
/>
<CheckboxInput
disabled={isMigrationInProcess}
label={t('tracking.title')}
checked={migrateTracking}
onChange={(_, checked) => setMigrationFlag('migrateTracking', checked)}
/>
<CheckboxInput
disabled={isMigrationInProcess}
label={t('migrate.dialog.label.delete_downloaded')}

View File

@@ -368,22 +368,66 @@ export class Mangas {
throw new Error('Categories are missing');
}
requestManager.updateMangasCategories([mangaToMigrateTo.id], {
await requestManager.updateMangasCategories([mangaToMigrateTo.id], {
addToCategories: mangaToMigrate.categories.nodes.map((category) => category.id),
});
}).response;
}
private static async migrateTracking(
mode: MigrateMode,
mangaToMigrate: GetMangaToMigrateQuery['manga'],
mangaToMigrateTo: GetMangaToMigrateToFetchMutation['fetchManga']['manga'],
): Promise<void> {
if (!mangaToMigrate.trackRecords) {
throw new Error('TrackRecords of manga to migrate are missing');
}
if (!mangaToMigrateTo.trackRecords) {
throw new Error('TrackRecords of manga to migrate to are missing');
}
const trackBindingsToAdd = mangaToMigrate.trackRecords.nodes.filter((trackRecordToMigrate) =>
mangaToMigrateTo.trackRecords!.nodes.every(
(trackRecord) => trackRecordToMigrate.remoteId !== trackRecord.remoteId,
),
);
await Promise.all([
...(mode === 'migrate'
? mangaToMigrate.trackRecords.nodes.map(
(trackRecord) => requestManager.unbindTracker(trackRecord.id).response,
)
: []),
...trackBindingsToAdd.map((trackRecord) =>
requestManager.bindTracker(mangaToMigrateTo.id, trackRecord.trackerId, trackRecord.remoteId),
),
]);
}
static async migrate(
mangaId: number,
mangaIdToMigrateTo: number,
{ mode, migrateChapters, migrateCategories, deleteChapters }: Omit<MigrateOptions, 'mangaIdToMigrateTo'>,
{
mode,
migrateChapters,
migrateCategories,
migrateTracking,
deleteChapters,
}: Omit<MigrateOptions, 'mangaIdToMigrateTo'>,
): Promise<void> {
return Mangas.executeAction('migrate', 1, async () => {
const [{ data: mangaToMigrateData }, { data: mangaToMigrateToData }] = await Promise.all([
requestManager.getMangaToMigrate(mangaId, { migrateChapters, migrateCategories, deleteChapters })
.response,
requestManager.getMangaToMigrateToFetch(mangaIdToMigrateTo, { migrateChapters, migrateCategories })
.response,
requestManager.getMangaToMigrate(mangaId, {
migrateChapters,
migrateCategories,
migrateTracking,
deleteChapters,
}).response,
requestManager.getMangaToMigrateToFetch(mangaIdToMigrateTo, {
migrateChapters,
migrateCategories,
migrateTracking,
}).response,
]);
if (!mangaToMigrateData.manga || !mangaToMigrateToData?.fetchManga.manga) {
@@ -407,6 +451,9 @@ export class Mangas {
migrateCategories
? Mangas.migrateCategories(mangaToMigrateData.manga, mangaToMigrateToData.fetchManga.manga)
: undefined,
migrateTracking
? Mangas.migrateTracking(mode, mangaToMigrateData.manga, mangaToMigrateToData.fetchManga.manga)
: undefined,
!mangaToMigrateToData.fetchManga.manga.inLibrary
? requestManager.updateManga(mangaIdToMigrateTo, { updateManga: { inLibrary: true } }).response
: undefined,

View File

@@ -2890,10 +2890,11 @@ export type GetMangaToMigrateToFetchMutationVariables = Exact<{
id: Scalars['Int']['input'];
migrateChapters: Scalars['Boolean']['input'];
migrateCategories: Scalars['Boolean']['input'];
migrateTracking: Scalars['Boolean']['input'];
}>;
export type GetMangaToMigrateToFetchMutation = { __typename?: 'Mutation', fetchManga: { __typename?: 'FetchMangaPayload', clientMutationId?: string | null, manga: { __typename?: 'MangaType', id: number, title: string, inLibrary: boolean, categories?: { __typename?: 'CategoryNodeList', nodes: Array<{ __typename?: 'CategoryType', id: number }> } } }, fetchChapters?: { __typename?: 'FetchChaptersPayload', chapters: Array<{ __typename?: 'ChapterType', id: number, chapterNumber: number, isRead: boolean, isDownloaded: boolean, isBookmarked: boolean, manga: { __typename?: 'MangaType', id: number } }> } };
export type GetMangaToMigrateToFetchMutation = { __typename?: 'Mutation', fetchManga: { __typename?: 'FetchMangaPayload', clientMutationId?: string | null, manga: { __typename?: 'MangaType', id: number, title: string, inLibrary: boolean, categories?: { __typename?: 'CategoryNodeList', nodes: Array<{ __typename?: 'CategoryType', id: number }> }, trackRecords?: { __typename?: 'TrackRecordNodeList', nodes: Array<{ __typename?: 'TrackRecordType', id: number, remoteId: string, trackerId: number }> } } }, fetchChapters?: { __typename?: 'FetchChaptersPayload', chapters: Array<{ __typename?: 'ChapterType', id: number, chapterNumber: number, isRead: boolean, isDownloaded: boolean, isBookmarked: boolean, manga: { __typename?: 'MangaType', id: number } }> } };
export type SetMangaMetadataMutationVariables = Exact<{
input: SetMangaMetaInput;
@@ -3181,10 +3182,11 @@ export type GetMangaToMigrateQueryVariables = Exact<{
id: Scalars['Int']['input'];
getChapterData: Scalars['Boolean']['input'];
migrateCategories: Scalars['Boolean']['input'];
migrateTracking: Scalars['Boolean']['input'];
}>;
export type GetMangaToMigrateQuery = { __typename?: 'Query', manga: { __typename?: 'MangaType', id: number, inLibrary: boolean, title: string, chapters?: { __typename?: 'ChapterNodeList', totalCount: number, nodes: Array<{ __typename?: 'ChapterType', id: number, chapterNumber: number, isRead: boolean, isDownloaded: boolean, isBookmarked: boolean, manga: { __typename?: 'MangaType', id: number } }> }, categories?: { __typename?: 'CategoryNodeList', nodes: Array<{ __typename?: 'CategoryType', id: number }> } } };
export type GetMangaToMigrateQuery = { __typename?: 'Query', manga: { __typename?: 'MangaType', id: number, inLibrary: boolean, title: string, chapters?: { __typename?: 'ChapterNodeList', totalCount: number, nodes: Array<{ __typename?: 'ChapterType', id: number, chapterNumber: number, isRead: boolean, isDownloaded: boolean, isBookmarked: boolean, manga: { __typename?: 'MangaType', id: number } }> }, categories?: { __typename?: 'CategoryNodeList', nodes: Array<{ __typename?: 'CategoryType', id: number }> }, trackRecords?: { __typename?: 'TrackRecordNodeList', nodes: Array<{ __typename?: 'TrackRecordType', id: number, remoteId: string, trackerId: number }> } } };
export type GetMangasQueryVariables = Exact<{
after?: InputMaybe<Scalars['Cursor']['input']>;

View File

@@ -50,7 +50,12 @@ export const GET_MANGA_FETCH = gql`
// makes the server fetch and return the manga
export const GET_MANGA_TO_MIGRATE_TO_FETCH = gql`
mutation GET_MANGA_TO_MIGRATE_TO_FETCH($id: Int!, $migrateChapters: Boolean!, $migrateCategories: Boolean!) {
mutation GET_MANGA_TO_MIGRATE_TO_FETCH(
$id: Int!
$migrateChapters: Boolean!
$migrateCategories: Boolean!
$migrateTracking: Boolean!
) {
fetchManga(input: { id: $id }) {
clientMutationId
manga {
@@ -62,6 +67,13 @@ export const GET_MANGA_TO_MIGRATE_TO_FETCH = gql`
id
}
}
trackRecords @include(if: $migrateTracking) {
nodes {
id
remoteId
trackerId
}
}
}
}
fetchChapters(input: { mangaId: $id }) @include(if: $migrateChapters) {

View File

@@ -22,7 +22,12 @@ export const GET_MANGA = gql`
// returns the current manga from the database
export const GET_MANGA_TO_MIGRATE = gql`
query GET_MANGA_TO_MIGRATE($id: Int!, $getChapterData: Boolean!, $migrateCategories: Boolean!) {
query GET_MANGA_TO_MIGRATE(
$id: Int!
$getChapterData: Boolean!
$migrateCategories: Boolean!
$migrateTracking: Boolean!
) {
manga(id: $id) {
id
inLibrary
@@ -45,6 +50,13 @@ export const GET_MANGA_TO_MIGRATE = gql`
id
}
}
trackRecords @include(if: $migrateTracking) {
nodes {
id
remoteId
trackerId
}
}
}
}
`;

View File

@@ -41,6 +41,7 @@ const GLOBAL_METADATA_KEYS: AppMetadataKeys[] = [
// migration
'migrateChapters',
'migrateCategories',
'migrateTracking',
'deleteChapters',
// browse

View File

@@ -37,6 +37,7 @@ export const getDefaultSettings = (): MetadataServerSettings => ({
// migration
migrateChapters: true,
migrateCategories: true,
migrateTracking: true,
deleteChapters: true,
// browse

View File

@@ -287,7 +287,7 @@ import {
TRACKER_UPDATE_BIND,
} from '@/lib/graphql/mutations/TrackerMutation.ts';
import { ControlledPromise } from '@/lib/ControlledPromise.ts';
import { TManga } from '@/typings.ts';
import { MetadataMigrationSettings, TManga } from '@/typings.ts';
enum GQLMethod {
QUERY = 'QUERY',
@@ -1549,19 +1549,22 @@ export class RequestManager {
{
migrateChapters = false,
migrateCategories = false,
migrateTracking = false,
deleteChapters = false,
apolloOptions: options,
}: {
migrateChapters?: boolean;
migrateCategories?: boolean;
deleteChapters?: boolean;
}: Partial<MetadataMigrationSettings> & {
apolloOptions?: QueryOptions<GetMangaToMigrateQueryVariables, GetMangaToMigrateQuery>;
} = {},
): AbortabaleApolloQueryResponse<GetMangaToMigrateQuery> {
return this.doRequest(
GQLMethod.QUERY,
GET_MANGA_TO_MIGRATE,
{ id: Number(mangaId), getChapterData: migrateChapters || deleteChapters, migrateCategories },
{
id: Number(mangaId),
getChapterData: migrateChapters || deleteChapters,
migrateCategories,
migrateTracking,
},
options,
);
}
@@ -1587,10 +1590,9 @@ export class RequestManager {
{
migrateChapters = false,
migrateCategories = false,
migrateTracking = false,
apolloOptions: options,
}: {
migrateChapters?: boolean;
migrateCategories?: boolean;
}: Partial<Omit<MetadataMigrationSettings, 'deleteChapters'>> & {
apolloOptions?: MutationOptions<
GetMangaToMigrateToFetchMutation,
GetMangaToMigrateToFetchMutationVariables
@@ -1604,6 +1606,7 @@ export class RequestManager {
id: Number(mangaId),
migrateChapters,
migrateCategories,
migrateTracking,
},
options,
);
@@ -2490,6 +2493,15 @@ export class RequestManager {
return this.doRequest(GQLMethod.USE_MUTATION, TRACKER_BIND, undefined, options);
}
public bindTracker(
mangaId: number,
trackerId: number,
remoteId: string,
options?: MutationOptions<TrackerBindMutation, TrackerBindMutationVariables>,
): AbortableApolloMutationResponse<TrackerBindMutation> {
return this.doRequest(GQLMethod.MUTATION, TRACKER_BIND, { mangaId, remoteId, trackerId }, options);
}
public unbindTracker(
recordId: number,
deleteRemoteTrack?: boolean,

View File

@@ -242,6 +242,7 @@ export type MetadataClientSettings = {
export type MetadataMigrationSettings = {
migrateChapters: boolean;
migrateCategories: boolean;
migrateTracking: boolean;
deleteChapters: boolean;
};