2026-03-13 22:41:28 +01:00
|
|
|
/*
|
|
|
|
|
* 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 Stack from '@mui/material/Stack';
|
|
|
|
|
import { MigrationProgressBar } from '@/features/migration/components/MigrationProgressBar.tsx';
|
|
|
|
|
import { useLingui } from '@lingui/react/macro';
|
|
|
|
|
import { useAppTitleAndAction } from '@/features/navigation-bar/hooks/useAppTitleAndAction.ts';
|
|
|
|
|
import { MigrationManager } from '@/features/migration/MigrationManager.ts';
|
|
|
|
|
import { MigrationEntryStatus } from '@/features/migration/Migration.types.ts';
|
|
|
|
|
import { useMemo } from 'react';
|
|
|
|
|
import { DEFAULT_FULL_FAB_HEIGHT } from '@/base/components/buttons/StyledFab.tsx';
|
|
|
|
|
import { MigrationContinueButton } from '@/features/migration/components/MigrationContinueButton.tsx';
|
|
|
|
|
import { plural } from '@lingui/core/macro';
|
|
|
|
|
import { MigrationEntryGroup } from '@/features/migration/components/MIgrationEntryGroup.tsx';
|
|
|
|
|
|
|
|
|
|
export const MigrationExecute = () => {
|
|
|
|
|
const { t } = useLingui();
|
|
|
|
|
const entries = MigrationManager.useEntries();
|
|
|
|
|
const progress = MigrationManager.useMigrationProgress();
|
|
|
|
|
|
2026-05-13 01:36:33 +02:00
|
|
|
useAppTitleAndAction(
|
|
|
|
|
(() => {
|
|
|
|
|
if (MigrationManager.getState().isAborted) {
|
|
|
|
|
return t`Aborted`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (MigrationManager.isPhaseComplete()) {
|
|
|
|
|
return t`Migration complete`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return t`Migrating`;
|
|
|
|
|
})(),
|
|
|
|
|
undefined,
|
|
|
|
|
[MigrationManager.isPhaseComplete()],
|
|
|
|
|
);
|
2026-03-13 22:41:28 +01:00
|
|
|
|
|
|
|
|
const entryList = useMemo(() => Object.values(entries), [entries]);
|
|
|
|
|
const migratingEntries = useMemo(
|
|
|
|
|
() =>
|
2026-04-23 20:23:29 +02:00
|
|
|
entryList
|
|
|
|
|
.filter((entry) =>
|
|
|
|
|
[MigrationEntryStatus.SEARCH_COMPLETE, MigrationEntryStatus.MIGRATING].includes(entry.status),
|
|
|
|
|
)
|
|
|
|
|
.toSorted((a, b) => {
|
|
|
|
|
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);
|
|
|
|
|
}),
|
2026-03-13 22:41:28 +01:00
|
|
|
[entryList],
|
|
|
|
|
);
|
|
|
|
|
const migratedEntries = useMemo(
|
|
|
|
|
() => entryList.filter((entry) => entry.status === MigrationEntryStatus.MIGRATION_COMPLETE),
|
|
|
|
|
[entryList],
|
|
|
|
|
);
|
|
|
|
|
const failedEntries = useMemo(
|
|
|
|
|
() => entryList.filter((entry) => entry.status === MigrationEntryStatus.MIGRATION_FAILED),
|
|
|
|
|
[entryList],
|
|
|
|
|
);
|
|
|
|
|
const excludedEntries = useMemo(() => entryList.filter((entry) => entry.isExcluded), [entryList]);
|
|
|
|
|
const noMatchEntries = useMemo(
|
|
|
|
|
() => entryList.filter((entry) => entry.status === MigrationEntryStatus.NO_MATCH),
|
|
|
|
|
[entryList],
|
|
|
|
|
);
|
2026-05-08 15:19:26 +02:00
|
|
|
const outdatedEntries = useMemo(
|
|
|
|
|
() => entryList.filter((entry) => entry.status === MigrationEntryStatus.OUTDATED),
|
|
|
|
|
[entryList],
|
|
|
|
|
);
|
2026-03-13 22:41:28 +01:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<MigrationProgressBar
|
|
|
|
|
{...progress}
|
|
|
|
|
label={t`${progress.completed} / ${progress.total}${progress.failed > 0 ? ` (${progress.failed} failed)` : ''}`}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<Stack
|
|
|
|
|
direction="row"
|
|
|
|
|
sx={{ p: 2, pb: DEFAULT_FULL_FAB_HEIGHT, gap: 4, flexWrap: 'wrap', justifyContent: 'center' }}
|
|
|
|
|
>
|
|
|
|
|
<MigrationEntryGroup
|
|
|
|
|
status={MigrationEntryStatus.MIGRATING}
|
|
|
|
|
title={plural(migratingEntries.length, {
|
|
|
|
|
one: '1 migrating entry',
|
|
|
|
|
other: '# migrating entries',
|
|
|
|
|
})}
|
|
|
|
|
entries={migratingEntries}
|
|
|
|
|
isMigrating
|
2026-04-23 15:18:35 +02:00
|
|
|
color="info"
|
2026-03-13 22:41:28 +01:00
|
|
|
/>
|
|
|
|
|
<MigrationEntryGroup
|
|
|
|
|
status={MigrationEntryStatus.MIGRATION_FAILED}
|
|
|
|
|
title={plural(failedEntries.length, {
|
|
|
|
|
one: '1 failed entry',
|
|
|
|
|
other: '# failed entries',
|
|
|
|
|
})}
|
|
|
|
|
entries={failedEntries}
|
|
|
|
|
isMigrating
|
|
|
|
|
color="error"
|
|
|
|
|
/>
|
|
|
|
|
<MigrationEntryGroup
|
|
|
|
|
status={MigrationEntryStatus.NO_MATCH}
|
|
|
|
|
title={plural(noMatchEntries.length, {
|
|
|
|
|
one: '1 entry with no match',
|
|
|
|
|
other: '# entries with no match',
|
|
|
|
|
})}
|
|
|
|
|
entries={noMatchEntries}
|
|
|
|
|
color="warning"
|
|
|
|
|
isMigrating
|
|
|
|
|
/>
|
2026-05-08 15:19:26 +02:00
|
|
|
<MigrationEntryGroup
|
|
|
|
|
status={MigrationEntryStatus.OUTDATED}
|
|
|
|
|
title={plural(outdatedEntries.length, {
|
|
|
|
|
one: '1 entry with only outdated matches',
|
|
|
|
|
other: '# entries with only outdated matches',
|
|
|
|
|
})}
|
|
|
|
|
entries={outdatedEntries}
|
|
|
|
|
color="warning"
|
|
|
|
|
/>
|
2026-03-13 22:41:28 +01:00
|
|
|
<MigrationEntryGroup
|
|
|
|
|
status={MigrationEntryStatus.EXCLUDED}
|
|
|
|
|
title={plural(excludedEntries.length, {
|
|
|
|
|
one: '1 excluded entry',
|
|
|
|
|
other: '# excluded entries',
|
|
|
|
|
})}
|
|
|
|
|
entries={excludedEntries}
|
|
|
|
|
color="info"
|
|
|
|
|
isMigrating
|
|
|
|
|
/>
|
|
|
|
|
<MigrationEntryGroup
|
|
|
|
|
status={MigrationEntryStatus.MIGRATION_COMPLETE}
|
|
|
|
|
title={plural(migratedEntries.length, {
|
|
|
|
|
one: '1 migrated entry',
|
|
|
|
|
other: '# migrated entries',
|
|
|
|
|
})}
|
|
|
|
|
entries={migratedEntries}
|
|
|
|
|
color="success"
|
|
|
|
|
isMigrating
|
|
|
|
|
/>
|
|
|
|
|
</Stack>
|
|
|
|
|
|
|
|
|
|
<MigrationContinueButton
|
|
|
|
|
title={MigrationManager.isPhaseComplete() ? t`Done` : t`Abort`}
|
|
|
|
|
onClick={() =>
|
2026-05-13 01:36:33 +02:00
|
|
|
MigrationManager.isPhaseComplete() ? MigrationManager.reset() : MigrationManager.stop()
|
2026-03-13 22:41:28 +01:00
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
};
|