From 464a6e83b96a400614bea9973aeb0278d03daa0b Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Thu, 5 Feb 2026 14:44:31 +0100 Subject: [PATCH] fix rendering issue from v1 to current --- apps/web/src/app/projects/page.tsx | 2 +- .../src/services/renderer/scene-builder.ts | 3 +- .../src/services/storage/migrations/runner.ts | 18 ++++++ .../migrations/transformers/v1-to-v2.ts | 59 ++++++++++--------- .../services/storage/migrations/v1-to-v2.ts | 22 +++---- 5 files changed, 62 insertions(+), 42 deletions(-) diff --git a/apps/web/src/app/projects/page.tsx b/apps/web/src/app/projects/page.tsx index 793b5309..5d17a46c 100644 --- a/apps/web/src/app/projects/page.tsx +++ b/apps/web/src/app/projects/page.tsx @@ -862,7 +862,7 @@ function ProjectsSkeleton() { ); return ( -
+
{skeletonIds.map((skeletonId) => ( !("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(); diff --git a/apps/web/src/services/storage/migrations/runner.ts b/apps/web/src/services/storage/migrations/runner.ts index bd34c661..b261252b 100644 --- a/apps/web/src/services/storage/migrations/runner.ts +++ b/apps/web/src/services/storage/migrations/runner.ts @@ -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, diff --git a/apps/web/src/services/storage/migrations/transformers/v1-to-v2.ts b/apps/web/src/services/storage/migrations/transformers/v1-to-v2.ts index 6242f8c3..098101d6 100644 --- a/apps/web/src/services/storage/migrations/transformers/v1-to-v2.ts +++ b/apps/web/src/services/storage/migrations/transformers/v1-to-v2.ts @@ -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, diff --git a/apps/web/src/services/storage/migrations/v1-to-v2.ts b/apps/web/src/services/storage/migrations/v1-to-v2.ts index eff4397e..ef381541 100644 --- a/apps/web/src/services/storage/migrations/v1-to-v2.ts +++ b/apps/web/src/services/storage/migrations/v1-to-v2.ts @@ -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 { 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 + }), + ), + ); }