fix rendering issue from v1 to current

This commit is contained in:
Maze Winther 2026-02-05 14:44:31 +01:00
parent d0b4b1a5fc
commit 464a6e83b9
5 changed files with 62 additions and 42 deletions

View File

@ -862,7 +862,7 @@ function ProjectsSkeleton() {
);
return (
<div className="xs:grid-cols-2 grid grid-cols-1 gap-6 sm:grid-cols-3 lg:grid-cols-4">
<div className="px-4 xs:grid-cols-2 grid grid-cols-1 gap-6 sm:grid-cols-3 lg:grid-cols-4">
{skeletonIds.map((skeletonId) => (
<Card
key={skeletonId}

View File

@ -29,10 +29,9 @@ export function buildScene(params: BuildSceneParams) {
(track) => !("hidden" in track && track.hidden),
);
const mainTrack = visibleTracks.find((track) => isMainTrack(track)) ?? null;
const orderedTracksTopToBottom = [
...visibleTracks.filter((track) => !isMainTrack(track)),
...(mainTrack ? [mainTrack] : []),
...visibleTracks.filter((track) => isMainTrack(track)),
];
const orderedTracksBottomToTop = orderedTracksTopToBottom.slice().reverse();

View File

@ -19,6 +19,8 @@ export interface MigrationProgress {
let hasCleanedUpMetaDb = false;
const MIN_MIGRATION_DISPLAY_MS = 1000;
export async function runStorageMigrations({
migrations,
onProgress,
@ -45,6 +47,7 @@ export async function runStorageMigrations({
const orderedMigrations = [...migrations].sort((a, b) => a.from - b.from);
let migratedCount = 0;
let migrationStartTime: number | null = null;
for (const project of projects) {
if (typeof project !== "object" || project === null) {
@ -59,6 +62,11 @@ export async function runStorageMigrations({
continue;
}
// Track when we first showed the migration dialog
if (migrationStartTime === null) {
migrationStartTime = Date.now();
}
const projectName = getProjectName({ project: projectRecord });
onProgress?.({
isMigrating: true,
@ -90,6 +98,16 @@ export async function runStorageMigrations({
}
}
// Ensure dialog is visible for minimum time so users can see it
if (migrationStartTime !== null) {
const elapsed = Date.now() - migrationStartTime;
if (elapsed < MIN_MIGRATION_DISPLAY_MS) {
await new Promise((resolve) =>
setTimeout(resolve, MIN_MIGRATION_DISPLAY_MS - elapsed),
);
}
}
onProgress?.({
isMigrating: false,
fromVersion: null,

View File

@ -26,7 +26,7 @@ interface LegacyMediaElement {
type: "media";
mediaId: string;
muted?: boolean;
[id: string]: unknown;
[key: string]: unknown;
}
interface LegacyTextElement {
@ -35,19 +35,19 @@ interface LegacyTextElement {
y: number;
rotation: number;
opacity: number;
[id: string]: unknown;
[key: string]: unknown;
}
interface LegacyAudioElement {
type: "audio";
mediaId: string;
[id: string]: unknown;
[key: string]: unknown;
}
interface LegacyMediaTrack {
type: "media";
elements: unknown[];
[id: string]: unknown;
[key: string]: unknown;
}
export interface TransformV1ToV2Options {
@ -265,35 +265,38 @@ async function transformTracks({
}
let isFirstVideoTrackFound = false;
const transformedTracks: (TimelineTrack | null)[] = [];
const transformedTracks = await Promise.all(
tracks.map(async (track) => {
if (!isRecord(track)) {
return null;
}
for (const track of tracks) {
if (!isRecord(track)) {
transformedTracks.push(null);
continue;
}
const trackType = track.type;
if (trackType === "media") {
const videoTrack = await transformMediaTrack({
track: track as LegacyMediaTrack,
loadMediaAsset,
isMain: !isFirstVideoTrackFound,
});
isFirstVideoTrackFound = true;
return videoTrack;
}
const trackType = track.type;
if (trackType === "media") {
const videoTrack = await transformMediaTrack({
track: track as LegacyMediaTrack,
loadMediaAsset,
isMain: !isFirstVideoTrackFound,
});
isFirstVideoTrackFound = true;
transformedTracks.push(videoTrack);
continue;
}
if (trackType === "text") {
return transformTextTrack({ track });
}
if (trackType === "text") {
transformedTracks.push(transformTextTrack({ track }));
continue;
}
if (trackType === "audio") {
return transformAudioTrack({ track });
}
if (trackType === "audio") {
transformedTracks.push(transformAudioTrack({ track }));
continue;
}
return null;
}),
);
transformedTracks.push(null);
}
return transformedTracks.filter(
(track): track is TimelineTrack => track !== null,

View File

@ -33,8 +33,7 @@ export class V1toV2Migration extends StorageMigration {
});
if (!result.skipped) {
// cleanup legacy timeline DBs after successful transformation
await cleanupLegacyTimelineDBs({
void cleanupLegacyTimelineDBs({
projectId,
project: result.project,
});
@ -60,13 +59,13 @@ function createMediaAssetLoader({
};
}
async function cleanupLegacyTimelineDBs({
function cleanupLegacyTimelineDBs({
projectId,
project,
}: {
projectId: string;
project: ProjectRecord;
}): Promise<void> {
}): void {
const scenes = project.scenes;
if (!Array.isArray(scenes)) {
return;
@ -89,11 +88,12 @@ async function cleanupLegacyTimelineDBs({
const projectDbName = `video-editor-timelines-${projectId}`;
dbNamesToDelete.push(projectDbName);
for (const dbName of dbNamesToDelete) {
try {
await deleteDatabase({ dbName });
} catch {
// ignore errors, DB might not exist or already deleted
}
}
// Fire-and-forget: delete in parallel, don't block migration
void Promise.all(
dbNamesToDelete.map((dbName) =>
deleteDatabase({ dbName }).catch(() => {
// ignore errors, DB might not exist or already deleted
}),
),
);
}