Optionally migrate track bindings
This commit is contained in:
@@ -35,7 +35,7 @@ export const MigrateDialog = ({ mangaIdToMigrateTo, onClose }: { mangaIdToMigrat
|
|||||||
const mangaId = Number(mangaIdAsString);
|
const mangaId = Number(mangaIdAsString);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
settings: { migrateChapters, migrateCategories, deleteChapters },
|
settings: { migrateChapters, migrateCategories, migrateTracking, deleteChapters },
|
||||||
} = useMetadataServerSettings();
|
} = useMetadataServerSettings();
|
||||||
|
|
||||||
const [isMigrationInProcess, setIsMigrationInProcess] = useState(false);
|
const [isMigrationInProcess, setIsMigrationInProcess] = useState(false);
|
||||||
@@ -58,6 +58,7 @@ export const MigrateDialog = ({ mangaIdToMigrateTo, onClose }: { mangaIdToMigrat
|
|||||||
mode,
|
mode,
|
||||||
migrateChapters,
|
migrateChapters,
|
||||||
migrateCategories,
|
migrateCategories,
|
||||||
|
migrateTracking,
|
||||||
deleteChapters,
|
deleteChapters,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -84,6 +85,12 @@ export const MigrateDialog = ({ mangaIdToMigrateTo, onClose }: { mangaIdToMigrat
|
|||||||
checked={migrateCategories}
|
checked={migrateCategories}
|
||||||
onChange={(_, checked) => setMigrationFlag('migrateCategories', checked)}
|
onChange={(_, checked) => setMigrationFlag('migrateCategories', checked)}
|
||||||
/>
|
/>
|
||||||
|
<CheckboxInput
|
||||||
|
disabled={isMigrationInProcess}
|
||||||
|
label={t('tracking.title')}
|
||||||
|
checked={migrateTracking}
|
||||||
|
onChange={(_, checked) => setMigrationFlag('migrateTracking', checked)}
|
||||||
|
/>
|
||||||
<CheckboxInput
|
<CheckboxInput
|
||||||
disabled={isMigrationInProcess}
|
disabled={isMigrationInProcess}
|
||||||
label={t('migrate.dialog.label.delete_downloaded')}
|
label={t('migrate.dialog.label.delete_downloaded')}
|
||||||
|
|||||||
@@ -368,22 +368,66 @@ export class Mangas {
|
|||||||
throw new Error('Categories are missing');
|
throw new Error('Categories are missing');
|
||||||
}
|
}
|
||||||
|
|
||||||
requestManager.updateMangasCategories([mangaToMigrateTo.id], {
|
await requestManager.updateMangasCategories([mangaToMigrateTo.id], {
|
||||||
addToCategories: mangaToMigrate.categories.nodes.map((category) => category.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(
|
static async migrate(
|
||||||
mangaId: number,
|
mangaId: number,
|
||||||
mangaIdToMigrateTo: number,
|
mangaIdToMigrateTo: number,
|
||||||
{ mode, migrateChapters, migrateCategories, deleteChapters }: Omit<MigrateOptions, 'mangaIdToMigrateTo'>,
|
{
|
||||||
|
mode,
|
||||||
|
migrateChapters,
|
||||||
|
migrateCategories,
|
||||||
|
migrateTracking,
|
||||||
|
deleteChapters,
|
||||||
|
}: Omit<MigrateOptions, 'mangaIdToMigrateTo'>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
return Mangas.executeAction('migrate', 1, async () => {
|
return Mangas.executeAction('migrate', 1, async () => {
|
||||||
const [{ data: mangaToMigrateData }, { data: mangaToMigrateToData }] = await Promise.all([
|
const [{ data: mangaToMigrateData }, { data: mangaToMigrateToData }] = await Promise.all([
|
||||||
requestManager.getMangaToMigrate(mangaId, { migrateChapters, migrateCategories, deleteChapters })
|
requestManager.getMangaToMigrate(mangaId, {
|
||||||
.response,
|
migrateChapters,
|
||||||
requestManager.getMangaToMigrateToFetch(mangaIdToMigrateTo, { migrateChapters, migrateCategories })
|
migrateCategories,
|
||||||
.response,
|
migrateTracking,
|
||||||
|
deleteChapters,
|
||||||
|
}).response,
|
||||||
|
requestManager.getMangaToMigrateToFetch(mangaIdToMigrateTo, {
|
||||||
|
migrateChapters,
|
||||||
|
migrateCategories,
|
||||||
|
migrateTracking,
|
||||||
|
}).response,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!mangaToMigrateData.manga || !mangaToMigrateToData?.fetchManga.manga) {
|
if (!mangaToMigrateData.manga || !mangaToMigrateToData?.fetchManga.manga) {
|
||||||
@@ -407,6 +451,9 @@ export class Mangas {
|
|||||||
migrateCategories
|
migrateCategories
|
||||||
? Mangas.migrateCategories(mangaToMigrateData.manga, mangaToMigrateToData.fetchManga.manga)
|
? Mangas.migrateCategories(mangaToMigrateData.manga, mangaToMigrateToData.fetchManga.manga)
|
||||||
: undefined,
|
: undefined,
|
||||||
|
migrateTracking
|
||||||
|
? Mangas.migrateTracking(mode, mangaToMigrateData.manga, mangaToMigrateToData.fetchManga.manga)
|
||||||
|
: undefined,
|
||||||
!mangaToMigrateToData.fetchManga.manga.inLibrary
|
!mangaToMigrateToData.fetchManga.manga.inLibrary
|
||||||
? requestManager.updateManga(mangaIdToMigrateTo, { updateManga: { inLibrary: true } }).response
|
? requestManager.updateManga(mangaIdToMigrateTo, { updateManga: { inLibrary: true } }).response
|
||||||
: undefined,
|
: undefined,
|
||||||
|
|||||||
@@ -2890,10 +2890,11 @@ export type GetMangaToMigrateToFetchMutationVariables = Exact<{
|
|||||||
id: Scalars['Int']['input'];
|
id: Scalars['Int']['input'];
|
||||||
migrateChapters: Scalars['Boolean']['input'];
|
migrateChapters: Scalars['Boolean']['input'];
|
||||||
migrateCategories: 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<{
|
export type SetMangaMetadataMutationVariables = Exact<{
|
||||||
input: SetMangaMetaInput;
|
input: SetMangaMetaInput;
|
||||||
@@ -3181,10 +3182,11 @@ export type GetMangaToMigrateQueryVariables = Exact<{
|
|||||||
id: Scalars['Int']['input'];
|
id: Scalars['Int']['input'];
|
||||||
getChapterData: Scalars['Boolean']['input'];
|
getChapterData: Scalars['Boolean']['input'];
|
||||||
migrateCategories: 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<{
|
export type GetMangasQueryVariables = Exact<{
|
||||||
after?: InputMaybe<Scalars['Cursor']['input']>;
|
after?: InputMaybe<Scalars['Cursor']['input']>;
|
||||||
|
|||||||
@@ -50,7 +50,12 @@ export const GET_MANGA_FETCH = gql`
|
|||||||
|
|
||||||
// makes the server fetch and return the manga
|
// makes the server fetch and return the manga
|
||||||
export const GET_MANGA_TO_MIGRATE_TO_FETCH = gql`
|
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 }) {
|
fetchManga(input: { id: $id }) {
|
||||||
clientMutationId
|
clientMutationId
|
||||||
manga {
|
manga {
|
||||||
@@ -62,6 +67,13 @@ export const GET_MANGA_TO_MIGRATE_TO_FETCH = gql`
|
|||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
trackRecords @include(if: $migrateTracking) {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
remoteId
|
||||||
|
trackerId
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fetchChapters(input: { mangaId: $id }) @include(if: $migrateChapters) {
|
fetchChapters(input: { mangaId: $id }) @include(if: $migrateChapters) {
|
||||||
|
|||||||
@@ -22,7 +22,12 @@ export const GET_MANGA = gql`
|
|||||||
|
|
||||||
// returns the current manga from the database
|
// returns the current manga from the database
|
||||||
export const GET_MANGA_TO_MIGRATE = gql`
|
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) {
|
manga(id: $id) {
|
||||||
id
|
id
|
||||||
inLibrary
|
inLibrary
|
||||||
@@ -45,6 +50,13 @@ export const GET_MANGA_TO_MIGRATE = gql`
|
|||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
trackRecords @include(if: $migrateTracking) {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
remoteId
|
||||||
|
trackerId
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ const GLOBAL_METADATA_KEYS: AppMetadataKeys[] = [
|
|||||||
// migration
|
// migration
|
||||||
'migrateChapters',
|
'migrateChapters',
|
||||||
'migrateCategories',
|
'migrateCategories',
|
||||||
|
'migrateTracking',
|
||||||
'deleteChapters',
|
'deleteChapters',
|
||||||
|
|
||||||
// browse
|
// browse
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export const getDefaultSettings = (): MetadataServerSettings => ({
|
|||||||
// migration
|
// migration
|
||||||
migrateChapters: true,
|
migrateChapters: true,
|
||||||
migrateCategories: true,
|
migrateCategories: true,
|
||||||
|
migrateTracking: true,
|
||||||
deleteChapters: true,
|
deleteChapters: true,
|
||||||
|
|
||||||
// browse
|
// browse
|
||||||
|
|||||||
@@ -287,7 +287,7 @@ import {
|
|||||||
TRACKER_UPDATE_BIND,
|
TRACKER_UPDATE_BIND,
|
||||||
} from '@/lib/graphql/mutations/TrackerMutation.ts';
|
} from '@/lib/graphql/mutations/TrackerMutation.ts';
|
||||||
import { ControlledPromise } from '@/lib/ControlledPromise.ts';
|
import { ControlledPromise } from '@/lib/ControlledPromise.ts';
|
||||||
import { TManga } from '@/typings.ts';
|
import { MetadataMigrationSettings, TManga } from '@/typings.ts';
|
||||||
|
|
||||||
enum GQLMethod {
|
enum GQLMethod {
|
||||||
QUERY = 'QUERY',
|
QUERY = 'QUERY',
|
||||||
@@ -1549,19 +1549,22 @@ export class RequestManager {
|
|||||||
{
|
{
|
||||||
migrateChapters = false,
|
migrateChapters = false,
|
||||||
migrateCategories = false,
|
migrateCategories = false,
|
||||||
|
migrateTracking = false,
|
||||||
deleteChapters = false,
|
deleteChapters = false,
|
||||||
apolloOptions: options,
|
apolloOptions: options,
|
||||||
}: {
|
}: Partial<MetadataMigrationSettings> & {
|
||||||
migrateChapters?: boolean;
|
|
||||||
migrateCategories?: boolean;
|
|
||||||
deleteChapters?: boolean;
|
|
||||||
apolloOptions?: QueryOptions<GetMangaToMigrateQueryVariables, GetMangaToMigrateQuery>;
|
apolloOptions?: QueryOptions<GetMangaToMigrateQueryVariables, GetMangaToMigrateQuery>;
|
||||||
} = {},
|
} = {},
|
||||||
): AbortabaleApolloQueryResponse<GetMangaToMigrateQuery> {
|
): AbortabaleApolloQueryResponse<GetMangaToMigrateQuery> {
|
||||||
return this.doRequest(
|
return this.doRequest(
|
||||||
GQLMethod.QUERY,
|
GQLMethod.QUERY,
|
||||||
GET_MANGA_TO_MIGRATE,
|
GET_MANGA_TO_MIGRATE,
|
||||||
{ id: Number(mangaId), getChapterData: migrateChapters || deleteChapters, migrateCategories },
|
{
|
||||||
|
id: Number(mangaId),
|
||||||
|
getChapterData: migrateChapters || deleteChapters,
|
||||||
|
migrateCategories,
|
||||||
|
migrateTracking,
|
||||||
|
},
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1587,10 +1590,9 @@ export class RequestManager {
|
|||||||
{
|
{
|
||||||
migrateChapters = false,
|
migrateChapters = false,
|
||||||
migrateCategories = false,
|
migrateCategories = false,
|
||||||
|
migrateTracking = false,
|
||||||
apolloOptions: options,
|
apolloOptions: options,
|
||||||
}: {
|
}: Partial<Omit<MetadataMigrationSettings, 'deleteChapters'>> & {
|
||||||
migrateChapters?: boolean;
|
|
||||||
migrateCategories?: boolean;
|
|
||||||
apolloOptions?: MutationOptions<
|
apolloOptions?: MutationOptions<
|
||||||
GetMangaToMigrateToFetchMutation,
|
GetMangaToMigrateToFetchMutation,
|
||||||
GetMangaToMigrateToFetchMutationVariables
|
GetMangaToMigrateToFetchMutationVariables
|
||||||
@@ -1604,6 +1606,7 @@ export class RequestManager {
|
|||||||
id: Number(mangaId),
|
id: Number(mangaId),
|
||||||
migrateChapters,
|
migrateChapters,
|
||||||
migrateCategories,
|
migrateCategories,
|
||||||
|
migrateTracking,
|
||||||
},
|
},
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
@@ -2490,6 +2493,15 @@ export class RequestManager {
|
|||||||
return this.doRequest(GQLMethod.USE_MUTATION, TRACKER_BIND, undefined, options);
|
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(
|
public unbindTracker(
|
||||||
recordId: number,
|
recordId: number,
|
||||||
deleteRemoteTrack?: boolean,
|
deleteRemoteTrack?: boolean,
|
||||||
|
|||||||
@@ -242,6 +242,7 @@ export type MetadataClientSettings = {
|
|||||||
export type MetadataMigrationSettings = {
|
export type MetadataMigrationSettings = {
|
||||||
migrateChapters: boolean;
|
migrateChapters: boolean;
|
||||||
migrateCategories: boolean;
|
migrateCategories: boolean;
|
||||||
|
migrateTracking: boolean;
|
||||||
deleteChapters: boolean;
|
deleteChapters: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user