From 4d127a038bf768eded68aae3cfa5c1b12241bdac Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Fri, 27 Mar 2026 00:55:15 +0100 Subject: chore: normalize line endings --- .../storage/migrations/transformers/v10-to-v11.ts | 226 ++++++------ .../storage/migrations/transformers/v11-to-v12.ts | 392 ++++++++++----------- .../storage/migrations/transformers/v12-to-v13.ts | 204 +++++------ .../storage/migrations/transformers/v13-to-v14.ts | 212 +++++------ .../storage/migrations/transformers/v14-to-v15.ts | 134 +++---- .../storage/migrations/transformers/v15-to-v16.ts | 120 +++---- .../storage/migrations/transformers/v16-to-v17.ts | 214 +++++------ .../storage/migrations/transformers/v17-to-v18.ts | 210 +++++------ .../storage/migrations/transformers/v18-to-v19.ts | 92 ++--- .../storage/migrations/transformers/v3-to-v4.ts | 378 ++++++++++---------- .../storage/migrations/transformers/v4-to-v5.ts | 354 +++++++++---------- .../storage/migrations/transformers/v5-to-v6.ts | 176 ++++----- .../storage/migrations/transformers/v6-to-v7.ts | 212 +++++------ .../storage/migrations/transformers/v7-to-v8.ts | 192 +++++----- .../storage/migrations/transformers/v8-to-v9.ts | 198 +++++------ .../storage/migrations/transformers/v9-to-v10.ts | 42 +-- 16 files changed, 1678 insertions(+), 1678 deletions(-) (limited to 'apps/web/src/services/storage/migrations/transformers') diff --git a/apps/web/src/services/storage/migrations/transformers/v10-to-v11.ts b/apps/web/src/services/storage/migrations/transformers/v10-to-v11.ts index b48f5e88..2f803d67 100644 --- a/apps/web/src/services/storage/migrations/transformers/v10-to-v11.ts +++ b/apps/web/src/services/storage/migrations/transformers/v10-to-v11.ts @@ -1,113 +1,113 @@ -import type { MigrationResult, ProjectRecord } from "./types"; -import { getProjectId, isRecord } from "./utils"; - -export function transformProjectV10ToV11({ - project, -}: { - project: ProjectRecord; -}): MigrationResult { - if (!getProjectId({ project })) { - return { project, skipped: true, reason: "no project id" }; - } - - if (typeof project.version === "number" && project.version >= 11) { - return { project, skipped: true, reason: "already v11" }; - } - - const migratedProject = migrateProjectScale({ project }); - - return { - project: { ...migratedProject, version: 11 }, - skipped: false, - }; -} - -function migrateProjectScale({ - project, -}: { - project: ProjectRecord; -}): ProjectRecord { - const scenesValue = project.scenes; - if (!Array.isArray(scenesValue)) return project; - - const migratedScenes = scenesValue.map((scene) => - migrateSceneScale({ scene }), - ); - - return { ...project, scenes: migratedScenes }; -} - -function migrateSceneScale({ scene }: { scene: unknown }): unknown { - if (!isRecord(scene)) return scene; - - const tracksValue = scene.tracks; - if (!Array.isArray(tracksValue)) return scene; - - const migratedTracks = tracksValue.map((track) => - migrateTrackScale({ track }), - ); - - return { ...scene, tracks: migratedTracks }; -} - -function migrateTrackScale({ track }: { track: unknown }): unknown { - if (!isRecord(track)) return track; - - const elementsValue = track.elements; - if (!Array.isArray(elementsValue)) return track; - - const migratedElements = elementsValue.map((element) => - migrateElementScale({ element }), - ); - - return { ...track, elements: migratedElements }; -} - -function migrateElementScale({ element }: { element: unknown }): unknown { - if (!isRecord(element)) return element; - - const transform = element.transform; - if (!isRecord(transform)) return element; - - const scale = transform.scale; - if (typeof scale !== "number") return element; - - const migratedTransform = { - ...transform, - scaleX: scale, - scaleY: scale, - }; - delete (migratedTransform as Record).scale; - - let migratedElement: ProjectRecord = { - ...element, - transform: migratedTransform, - }; - - const animations = element.animations; - if (isRecord(animations) && isRecord(animations.channels)) { - const channels = animations.channels as Record; - const scaleChannel = channels["transform.scale"]; - if (scaleChannel && isRecord(scaleChannel)) { - const keyframes = (scaleChannel as { keyframes?: unknown[] }).keyframes; - if (Array.isArray(keyframes)) { - const newChannels = { ...channels }; - delete newChannels["transform.scale"]; - newChannels["transform.scaleX"] = { - ...scaleChannel, - keyframes: [...keyframes], - }; - newChannels["transform.scaleY"] = { - ...scaleChannel, - keyframes: keyframes.map((kf) => (isRecord(kf) ? { ...kf } : kf)), - }; - migratedElement = { - ...migratedElement, - animations: { ...animations, channels: newChannels }, - }; - } - } - } - - return migratedElement; -} +import type { MigrationResult, ProjectRecord } from "./types"; +import { getProjectId, isRecord } from "./utils"; + +export function transformProjectV10ToV11({ + project, +}: { + project: ProjectRecord; +}): MigrationResult { + if (!getProjectId({ project })) { + return { project, skipped: true, reason: "no project id" }; + } + + if (typeof project.version === "number" && project.version >= 11) { + return { project, skipped: true, reason: "already v11" }; + } + + const migratedProject = migrateProjectScale({ project }); + + return { + project: { ...migratedProject, version: 11 }, + skipped: false, + }; +} + +function migrateProjectScale({ + project, +}: { + project: ProjectRecord; +}): ProjectRecord { + const scenesValue = project.scenes; + if (!Array.isArray(scenesValue)) return project; + + const migratedScenes = scenesValue.map((scene) => + migrateSceneScale({ scene }), + ); + + return { ...project, scenes: migratedScenes }; +} + +function migrateSceneScale({ scene }: { scene: unknown }): unknown { + if (!isRecord(scene)) return scene; + + const tracksValue = scene.tracks; + if (!Array.isArray(tracksValue)) return scene; + + const migratedTracks = tracksValue.map((track) => + migrateTrackScale({ track }), + ); + + return { ...scene, tracks: migratedTracks }; +} + +function migrateTrackScale({ track }: { track: unknown }): unknown { + if (!isRecord(track)) return track; + + const elementsValue = track.elements; + if (!Array.isArray(elementsValue)) return track; + + const migratedElements = elementsValue.map((element) => + migrateElementScale({ element }), + ); + + return { ...track, elements: migratedElements }; +} + +function migrateElementScale({ element }: { element: unknown }): unknown { + if (!isRecord(element)) return element; + + const transform = element.transform; + if (!isRecord(transform)) return element; + + const scale = transform.scale; + if (typeof scale !== "number") return element; + + const migratedTransform = { + ...transform, + scaleX: scale, + scaleY: scale, + }; + delete (migratedTransform as Record).scale; + + let migratedElement: ProjectRecord = { + ...element, + transform: migratedTransform, + }; + + const animations = element.animations; + if (isRecord(animations) && isRecord(animations.channels)) { + const channels = animations.channels as Record; + const scaleChannel = channels["transform.scale"]; + if (scaleChannel && isRecord(scaleChannel)) { + const keyframes = (scaleChannel as { keyframes?: unknown[] }).keyframes; + if (Array.isArray(keyframes)) { + const newChannels = { ...channels }; + delete newChannels["transform.scale"]; + newChannels["transform.scaleX"] = { + ...scaleChannel, + keyframes: [...keyframes], + }; + newChannels["transform.scaleY"] = { + ...scaleChannel, + keyframes: keyframes.map((kf) => (isRecord(kf) ? { ...kf } : kf)), + }; + migratedElement = { + ...migratedElement, + animations: { ...animations, channels: newChannels }, + }; + } + } + } + + return migratedElement; +} diff --git a/apps/web/src/services/storage/migrations/transformers/v11-to-v12.ts b/apps/web/src/services/storage/migrations/transformers/v11-to-v12.ts index fa1bd371..4efc5b25 100644 --- a/apps/web/src/services/storage/migrations/transformers/v11-to-v12.ts +++ b/apps/web/src/services/storage/migrations/transformers/v11-to-v12.ts @@ -1,196 +1,196 @@ -import type { MigrationResult, ProjectRecord } from "./types"; -import { getProjectId, isRecord } from "./utils"; - -export function transformProjectV11ToV12({ - project, -}: { - project: ProjectRecord; -}): MigrationResult { - if (!getProjectId({ project })) { - return { project, skipped: true, reason: "no project id" }; - } - - if (typeof project.version === "number" && project.version >= 12) { - return { project, skipped: true, reason: "already v12" }; - } - - const migratedProject = migrateProjectPosition({ project }); - - return { - project: { ...migratedProject, version: 12 }, - skipped: false, - }; -} - -function migrateProjectPosition({ - project, -}: { - project: ProjectRecord; -}): ProjectRecord { - const scenesValue = project.scenes; - if (!Array.isArray(scenesValue)) return project; - - const migratedScenes = scenesValue.map((scene) => - migrateScenePosition({ scene }), - ); - - return { ...project, scenes: migratedScenes }; -} - -function migrateScenePosition({ scene }: { scene: unknown }): unknown { - if (!isRecord(scene)) return scene; - - const tracksValue = scene.tracks; - if (!Array.isArray(tracksValue)) return scene; - - const migratedTracks = tracksValue.map((track) => - migrateTrackPosition({ track }), - ); - - return { ...scene, tracks: migratedTracks }; -} - -function migrateTrackPosition({ track }: { track: unknown }): unknown { - if (!isRecord(track)) return track; - - const elementsValue = track.elements; - if (!Array.isArray(elementsValue)) return track; - - const migratedElements = elementsValue.map((element) => - migrateElementPosition({ element }), - ); - - return { ...track, elements: migratedElements }; -} - -function migrateElementPosition({ element }: { element: unknown }): unknown { - if (!isRecord(element)) return element; - - const animations = element.animations; - if (!isRecord(animations) || !isRecord(animations.channels)) return element; - - const channels = animations.channels as Record; - const xChannel = channels["transform.position.x"]; - const yChannel = channels["transform.position.y"]; - - if (!xChannel && !yChannel) return element; - - const baseTransform = isRecord(element.transform) ? element.transform : {}; - const basePosition = isRecord(baseTransform.position) - ? baseTransform.position - : {}; - const baseX = typeof basePosition.x === "number" ? basePosition.x : 0; - const baseY = typeof basePosition.y === "number" ? basePosition.y : 0; - - const xKeyframes = getKeyframes({ channel: xChannel }); - const yKeyframes = getKeyframes({ channel: yChannel }); - - const allTimes = Array.from( - new Set([ - ...xKeyframes.map((kf) => kf.time), - ...yKeyframes.map((kf) => kf.time), - ]), - ).sort((a, b) => a - b); - - const vectorKeyframes = allTimes.map((time) => { - const xKf = xKeyframes.find((kf) => Math.abs(kf.time - time) < 0.001); - const yKf = yKeyframes.find((kf) => Math.abs(kf.time - time) < 0.001); - const x = - typeof xKf?.value === "number" - ? xKf.value - : interpolateScalarAtTime({ - keyframes: xKeyframes, - time, - fallback: baseX, - }); - const y = - typeof yKf?.value === "number" - ? yKf.value - : interpolateScalarAtTime({ - keyframes: yKeyframes, - time, - fallback: baseY, - }); - const interpolation = xKf?.interpolation ?? yKf?.interpolation ?? "linear"; - return { - id: xKf?.id ?? yKf?.id ?? crypto.randomUUID(), - time, - value: { x, y }, - interpolation, - }; - }); - - const newChannels = { ...channels }; - delete newChannels["transform.position.x"]; - delete newChannels["transform.position.y"]; - newChannels["transform.position"] = { - valueKind: "vector", - keyframes: vectorKeyframes, - }; - - return { - ...element, - animations: { ...animations, channels: newChannels }, - }; -} - -interface ScalarKeyframe { - id: string; - time: number; - value: number; - interpolation: string; -} - -function getKeyframes({ channel }: { channel: unknown }): ScalarKeyframe[] { - if (!isRecord(channel)) return []; - if (!Array.isArray(channel.keyframes)) return []; - return channel.keyframes.flatMap((kf) => { - if (!isRecord(kf)) return []; - if ( - typeof kf.id !== "string" || - typeof kf.time !== "number" || - typeof kf.value !== "number" - ) - return []; - return [ - { - id: kf.id, - time: kf.time, - value: kf.value, - interpolation: - typeof kf.interpolation === "string" ? kf.interpolation : "linear", - }, - ]; - }); -} - -function interpolateScalarAtTime({ - keyframes, - time, - fallback, -}: { - keyframes: ScalarKeyframe[]; - time: number; - fallback: number; -}): number { - if (keyframes.length === 0) return fallback; - - const sorted = [...keyframes].sort((a, b) => a.time - b.time); - const first = sorted[0]; - const last = sorted[sorted.length - 1]; - - if (!first || !last) return fallback; - if (time <= first.time) return first.value; - if (time >= last.time) return last.value; - - for (let i = 0; i < sorted.length - 1; i++) { - const left = sorted[i]; - const right = sorted[i + 1]; - if (time < left.time || time > right.time) continue; - if (left.interpolation === "hold") return left.value; - const t = (time - left.time) / (right.time - left.time); - return left.value + (right.value - left.value) * t; - } - - return last.value; -} +import type { MigrationResult, ProjectRecord } from "./types"; +import { getProjectId, isRecord } from "./utils"; + +export function transformProjectV11ToV12({ + project, +}: { + project: ProjectRecord; +}): MigrationResult { + if (!getProjectId({ project })) { + return { project, skipped: true, reason: "no project id" }; + } + + if (typeof project.version === "number" && project.version >= 12) { + return { project, skipped: true, reason: "already v12" }; + } + + const migratedProject = migrateProjectPosition({ project }); + + return { + project: { ...migratedProject, version: 12 }, + skipped: false, + }; +} + +function migrateProjectPosition({ + project, +}: { + project: ProjectRecord; +}): ProjectRecord { + const scenesValue = project.scenes; + if (!Array.isArray(scenesValue)) return project; + + const migratedScenes = scenesValue.map((scene) => + migrateScenePosition({ scene }), + ); + + return { ...project, scenes: migratedScenes }; +} + +function migrateScenePosition({ scene }: { scene: unknown }): unknown { + if (!isRecord(scene)) return scene; + + const tracksValue = scene.tracks; + if (!Array.isArray(tracksValue)) return scene; + + const migratedTracks = tracksValue.map((track) => + migrateTrackPosition({ track }), + ); + + return { ...scene, tracks: migratedTracks }; +} + +function migrateTrackPosition({ track }: { track: unknown }): unknown { + if (!isRecord(track)) return track; + + const elementsValue = track.elements; + if (!Array.isArray(elementsValue)) return track; + + const migratedElements = elementsValue.map((element) => + migrateElementPosition({ element }), + ); + + return { ...track, elements: migratedElements }; +} + +function migrateElementPosition({ element }: { element: unknown }): unknown { + if (!isRecord(element)) return element; + + const animations = element.animations; + if (!isRecord(animations) || !isRecord(animations.channels)) return element; + + const channels = animations.channels as Record; + const xChannel = channels["transform.position.x"]; + const yChannel = channels["transform.position.y"]; + + if (!xChannel && !yChannel) return element; + + const baseTransform = isRecord(element.transform) ? element.transform : {}; + const basePosition = isRecord(baseTransform.position) + ? baseTransform.position + : {}; + const baseX = typeof basePosition.x === "number" ? basePosition.x : 0; + const baseY = typeof basePosition.y === "number" ? basePosition.y : 0; + + const xKeyframes = getKeyframes({ channel: xChannel }); + const yKeyframes = getKeyframes({ channel: yChannel }); + + const allTimes = Array.from( + new Set([ + ...xKeyframes.map((kf) => kf.time), + ...yKeyframes.map((kf) => kf.time), + ]), + ).sort((a, b) => a - b); + + const vectorKeyframes = allTimes.map((time) => { + const xKf = xKeyframes.find((kf) => Math.abs(kf.time - time) < 0.001); + const yKf = yKeyframes.find((kf) => Math.abs(kf.time - time) < 0.001); + const x = + typeof xKf?.value === "number" + ? xKf.value + : interpolateScalarAtTime({ + keyframes: xKeyframes, + time, + fallback: baseX, + }); + const y = + typeof yKf?.value === "number" + ? yKf.value + : interpolateScalarAtTime({ + keyframes: yKeyframes, + time, + fallback: baseY, + }); + const interpolation = xKf?.interpolation ?? yKf?.interpolation ?? "linear"; + return { + id: xKf?.id ?? yKf?.id ?? crypto.randomUUID(), + time, + value: { x, y }, + interpolation, + }; + }); + + const newChannels = { ...channels }; + delete newChannels["transform.position.x"]; + delete newChannels["transform.position.y"]; + newChannels["transform.position"] = { + valueKind: "vector", + keyframes: vectorKeyframes, + }; + + return { + ...element, + animations: { ...animations, channels: newChannels }, + }; +} + +interface ScalarKeyframe { + id: string; + time: number; + value: number; + interpolation: string; +} + +function getKeyframes({ channel }: { channel: unknown }): ScalarKeyframe[] { + if (!isRecord(channel)) return []; + if (!Array.isArray(channel.keyframes)) return []; + return channel.keyframes.flatMap((kf) => { + if (!isRecord(kf)) return []; + if ( + typeof kf.id !== "string" || + typeof kf.time !== "number" || + typeof kf.value !== "number" + ) + return []; + return [ + { + id: kf.id, + time: kf.time, + value: kf.value, + interpolation: + typeof kf.interpolation === "string" ? kf.interpolation : "linear", + }, + ]; + }); +} + +function interpolateScalarAtTime({ + keyframes, + time, + fallback, +}: { + keyframes: ScalarKeyframe[]; + time: number; + fallback: number; +}): number { + if (keyframes.length === 0) return fallback; + + const sorted = [...keyframes].sort((a, b) => a.time - b.time); + const first = sorted[0]; + const last = sorted[sorted.length - 1]; + + if (!first || !last) return fallback; + if (time <= first.time) return first.value; + if (time >= last.time) return last.value; + + for (let i = 0; i < sorted.length - 1; i++) { + const left = sorted[i]; + const right = sorted[i + 1]; + if (time < left.time || time > right.time) continue; + if (left.interpolation === "hold") return left.value; + const t = (time - left.time) / (right.time - left.time); + return left.value + (right.value - left.value) * t; + } + + return last.value; +} diff --git a/apps/web/src/services/storage/migrations/transformers/v12-to-v13.ts b/apps/web/src/services/storage/migrations/transformers/v12-to-v13.ts index 59b29732..518de59a 100644 --- a/apps/web/src/services/storage/migrations/transformers/v12-to-v13.ts +++ b/apps/web/src/services/storage/migrations/transformers/v12-to-v13.ts @@ -1,102 +1,102 @@ -import type { MigrationResult, ProjectRecord } from "./types"; -import { getProjectId, isRecord } from "./utils"; - -export function transformProjectV12ToV13({ - project, -}: { - project: ProjectRecord; -}): MigrationResult { - if (!getProjectId({ project })) { - return { project, skipped: true, reason: "no project id" }; - } - - if (typeof project.version === "number" && project.version >= 13) { - return { project, skipped: true, reason: "already v13" }; - } - - const migratedProject = migrateProjectMasks({ project }); - - return { - project: { ...migratedProject, version: 13 }, - skipped: false, - }; -} - -function migrateProjectMasks({ - project, -}: { - project: ProjectRecord; -}): ProjectRecord { - const scenesValue = project.scenes; - if (!Array.isArray(scenesValue)) return project; - - const migratedScenes = scenesValue.map((scene) => - migrateSceneMasks({ scene }), - ); - - return { ...project, scenes: migratedScenes }; -} - -function migrateSceneMasks({ scene }: { scene: unknown }): unknown { - if (!isRecord(scene)) return scene; - - const tracksValue = scene.tracks; - if (!Array.isArray(tracksValue)) return scene; - - const migratedTracks = tracksValue.map((track) => - migrateTrackMasks({ track }), - ); - - return { ...scene, tracks: migratedTracks }; -} - -function migrateTrackMasks({ track }: { track: unknown }): unknown { - if (!isRecord(track)) return track; - - const elementsValue = track.elements; - if (!Array.isArray(elementsValue)) return track; - - const migratedElements = elementsValue.map((element) => - migrateElementMasks({ element }), - ); - - return { ...track, elements: migratedElements }; -} - -function migrateElementMasks({ element }: { element: unknown }): unknown { - if (!isRecord(element)) return element; - - const masksValue = element.masks; - if (!Array.isArray(masksValue)) return element; - - const migratedMasks = masksValue.map((mask) => migrateMask({ mask })); - - return { ...element, masks: migratedMasks }; -} - -function migrateMask({ mask }: { mask: unknown }): unknown { - if (!isRecord(mask)) return mask; - - const params = isRecord(mask.params) ? { ...mask.params } : {}; - const result = { ...mask }; - - result.params = { - ...params, - feather: typeof mask.feather === "number" ? mask.feather : 0, - inverted: typeof mask.inverted === "boolean" ? mask.inverted : false, - strokeColor: - isRecord(mask.stroke) && typeof mask.stroke.color === "string" - ? mask.stroke.color - : "#ffffff", - strokeWidth: - isRecord(mask.stroke) && typeof mask.stroke.width === "number" - ? mask.stroke.width - : 0, - }; - - delete (result as Record).feather; - delete (result as Record).inverted; - delete (result as Record).stroke; - - return result; -} +import type { MigrationResult, ProjectRecord } from "./types"; +import { getProjectId, isRecord } from "./utils"; + +export function transformProjectV12ToV13({ + project, +}: { + project: ProjectRecord; +}): MigrationResult { + if (!getProjectId({ project })) { + return { project, skipped: true, reason: "no project id" }; + } + + if (typeof project.version === "number" && project.version >= 13) { + return { project, skipped: true, reason: "already v13" }; + } + + const migratedProject = migrateProjectMasks({ project }); + + return { + project: { ...migratedProject, version: 13 }, + skipped: false, + }; +} + +function migrateProjectMasks({ + project, +}: { + project: ProjectRecord; +}): ProjectRecord { + const scenesValue = project.scenes; + if (!Array.isArray(scenesValue)) return project; + + const migratedScenes = scenesValue.map((scene) => + migrateSceneMasks({ scene }), + ); + + return { ...project, scenes: migratedScenes }; +} + +function migrateSceneMasks({ scene }: { scene: unknown }): unknown { + if (!isRecord(scene)) return scene; + + const tracksValue = scene.tracks; + if (!Array.isArray(tracksValue)) return scene; + + const migratedTracks = tracksValue.map((track) => + migrateTrackMasks({ track }), + ); + + return { ...scene, tracks: migratedTracks }; +} + +function migrateTrackMasks({ track }: { track: unknown }): unknown { + if (!isRecord(track)) return track; + + const elementsValue = track.elements; + if (!Array.isArray(elementsValue)) return track; + + const migratedElements = elementsValue.map((element) => + migrateElementMasks({ element }), + ); + + return { ...track, elements: migratedElements }; +} + +function migrateElementMasks({ element }: { element: unknown }): unknown { + if (!isRecord(element)) return element; + + const masksValue = element.masks; + if (!Array.isArray(masksValue)) return element; + + const migratedMasks = masksValue.map((mask) => migrateMask({ mask })); + + return { ...element, masks: migratedMasks }; +} + +function migrateMask({ mask }: { mask: unknown }): unknown { + if (!isRecord(mask)) return mask; + + const params = isRecord(mask.params) ? { ...mask.params } : {}; + const result = { ...mask }; + + result.params = { + ...params, + feather: typeof mask.feather === "number" ? mask.feather : 0, + inverted: typeof mask.inverted === "boolean" ? mask.inverted : false, + strokeColor: + isRecord(mask.stroke) && typeof mask.stroke.color === "string" + ? mask.stroke.color + : "#ffffff", + strokeWidth: + isRecord(mask.stroke) && typeof mask.stroke.width === "number" + ? mask.stroke.width + : 0, + }; + + delete (result as Record).feather; + delete (result as Record).inverted; + delete (result as Record).stroke; + + return result; +} diff --git a/apps/web/src/services/storage/migrations/transformers/v13-to-v14.ts b/apps/web/src/services/storage/migrations/transformers/v13-to-v14.ts index 01a3cc3e..89dde38e 100644 --- a/apps/web/src/services/storage/migrations/transformers/v13-to-v14.ts +++ b/apps/web/src/services/storage/migrations/transformers/v13-to-v14.ts @@ -1,106 +1,106 @@ -import type { MigrationResult, ProjectRecord } from "./types"; -import { getProjectId, isRecord } from "./utils"; - -export function transformProjectV13ToV14({ - project, -}: { - project: ProjectRecord; -}): MigrationResult { - if (!getProjectId({ project })) { - return { project, skipped: true, reason: "no project id" }; - } - - if (typeof project.version === "number" && project.version >= 14) { - return { project, skipped: true, reason: "already v14" }; - } - - const migratedProject = migrateProjectSplitMasks({ project }); - - return { - project: { ...migratedProject, version: 14 }, - skipped: false, - }; -} - -function migrateProjectSplitMasks({ - project, -}: { - project: ProjectRecord; -}): ProjectRecord { - const scenesValue = project.scenes; - if (!Array.isArray(scenesValue)) return project; - - const migratedScenes = scenesValue.map((scene) => - migrateSceneSplitMasks({ scene }), - ); - - return { ...project, scenes: migratedScenes }; -} - -function migrateSceneSplitMasks({ scene }: { scene: unknown }): unknown { - if (!isRecord(scene)) return scene; - - const tracksValue = scene.tracks; - if (!Array.isArray(tracksValue)) return scene; - - const migratedTracks = tracksValue.map((track) => - migrateTrackSplitMasks({ track }), - ); - - return { ...scene, tracks: migratedTracks }; -} - -function migrateTrackSplitMasks({ track }: { track: unknown }): unknown { - if (!isRecord(track)) return track; - - const elementsValue = track.elements; - if (!Array.isArray(elementsValue)) return track; - - const migratedElements = elementsValue.map((element) => - migrateElementSplitMasks({ element }), - ); - - return { ...track, elements: migratedElements }; -} - -function migrateElementSplitMasks({ element }: { element: unknown }): unknown { - if (!isRecord(element)) return element; - - const masksValue = element.masks; - if (!Array.isArray(masksValue)) return element; - - const migratedMasks = masksValue.map((mask) => migrateSplitMask({ mask })); - - return { ...element, masks: migratedMasks }; -} - -/** - * Converts split mask params from position-along-normal to explicit (x, y) anchor. - * - * Old: center = (cx + (position-0.5)*W*cos(rot), cy + (position-0.5)*W*sin(rot)) - * New: center = (cx + x*W, cy + y*W) - * - * Conversion: x = (position-0.5)*cos(rot), y = (position-0.5)*sin(rot) - */ -function migrateSplitMask({ mask }: { mask: unknown }): unknown { - if (!isRecord(mask)) return mask; - if (mask.type !== "split") return mask; - - const params = isRecord(mask.params) ? mask.params : {}; - const position = typeof params.position === "number" ? params.position : 0.5; - const rotation = typeof params.rotation === "number" ? params.rotation : 0; - - const angleRad = (rotation * Math.PI) / 180; - const x = (position - 0.5) * Math.cos(angleRad); - const y = (position - 0.5) * Math.sin(angleRad); - - const { position: _removed, ...restParams } = params as Record< - string, - unknown - > & { position: unknown }; - - return { - ...mask, - params: { ...restParams, x, y }, - }; -} +import type { MigrationResult, ProjectRecord } from "./types"; +import { getProjectId, isRecord } from "./utils"; + +export function transformProjectV13ToV14({ + project, +}: { + project: ProjectRecord; +}): MigrationResult { + if (!getProjectId({ project })) { + return { project, skipped: true, reason: "no project id" }; + } + + if (typeof project.version === "number" && project.version >= 14) { + return { project, skipped: true, reason: "already v14" }; + } + + const migratedProject = migrateProjectSplitMasks({ project }); + + return { + project: { ...migratedProject, version: 14 }, + skipped: false, + }; +} + +function migrateProjectSplitMasks({ + project, +}: { + project: ProjectRecord; +}): ProjectRecord { + const scenesValue = project.scenes; + if (!Array.isArray(scenesValue)) return project; + + const migratedScenes = scenesValue.map((scene) => + migrateSceneSplitMasks({ scene }), + ); + + return { ...project, scenes: migratedScenes }; +} + +function migrateSceneSplitMasks({ scene }: { scene: unknown }): unknown { + if (!isRecord(scene)) return scene; + + const tracksValue = scene.tracks; + if (!Array.isArray(tracksValue)) return scene; + + const migratedTracks = tracksValue.map((track) => + migrateTrackSplitMasks({ track }), + ); + + return { ...scene, tracks: migratedTracks }; +} + +function migrateTrackSplitMasks({ track }: { track: unknown }): unknown { + if (!isRecord(track)) return track; + + const elementsValue = track.elements; + if (!Array.isArray(elementsValue)) return track; + + const migratedElements = elementsValue.map((element) => + migrateElementSplitMasks({ element }), + ); + + return { ...track, elements: migratedElements }; +} + +function migrateElementSplitMasks({ element }: { element: unknown }): unknown { + if (!isRecord(element)) return element; + + const masksValue = element.masks; + if (!Array.isArray(masksValue)) return element; + + const migratedMasks = masksValue.map((mask) => migrateSplitMask({ mask })); + + return { ...element, masks: migratedMasks }; +} + +/** + * Converts split mask params from position-along-normal to explicit (x, y) anchor. + * + * Old: center = (cx + (position-0.5)*W*cos(rot), cy + (position-0.5)*W*sin(rot)) + * New: center = (cx + x*W, cy + y*W) + * + * Conversion: x = (position-0.5)*cos(rot), y = (position-0.5)*sin(rot) + */ +function migrateSplitMask({ mask }: { mask: unknown }): unknown { + if (!isRecord(mask)) return mask; + if (mask.type !== "split") return mask; + + const params = isRecord(mask.params) ? mask.params : {}; + const position = typeof params.position === "number" ? params.position : 0.5; + const rotation = typeof params.rotation === "number" ? params.rotation : 0; + + const angleRad = (rotation * Math.PI) / 180; + const x = (position - 0.5) * Math.cos(angleRad); + const y = (position - 0.5) * Math.sin(angleRad); + + const { position: _removed, ...restParams } = params as Record< + string, + unknown + > & { position: unknown }; + + return { + ...mask, + params: { ...restParams, x, y }, + }; +} diff --git a/apps/web/src/services/storage/migrations/transformers/v14-to-v15.ts b/apps/web/src/services/storage/migrations/transformers/v14-to-v15.ts index beef05d4..e75c541b 100644 --- a/apps/web/src/services/storage/migrations/transformers/v14-to-v15.ts +++ b/apps/web/src/services/storage/migrations/transformers/v14-to-v15.ts @@ -1,67 +1,67 @@ -import { STICKER_INTRINSIC_SIZE_FALLBACK } from "@/constants/sticker-constants"; -import type { MigrationResult, ProjectRecord } from "./types"; -import { getProjectId, isRecord } from "./utils"; - -export function transformProjectV14ToV15({ - project, -}: { - project: ProjectRecord; -}): MigrationResult { - if (!getProjectId({ project })) { - return { project, skipped: true, reason: "no project id" }; - } - - if (typeof project.version === "number" && project.version >= 15) { - return { project, skipped: true, reason: "already v15" }; - } - - const migratedProject = backfillStickerIntrinsicDimensions({ project }); - - return { - project: { ...migratedProject, version: 15 }, - skipped: false, - }; -} - -function backfillStickerIntrinsicDimensions({ - project, -}: { - project: ProjectRecord; -}): ProjectRecord { - const scenesValue = project.scenes; - if (!Array.isArray(scenesValue)) return project; - - const migratedScenes = scenesValue.map((scene) => { - if (!isRecord(scene)) return scene; - const tracksValue = scene.tracks; - if (!Array.isArray(tracksValue)) return scene; - - const migratedTracks = tracksValue.map((track) => { - if (!isRecord(track)) return track; - const elementsValue = track.elements; - if (!Array.isArray(elementsValue)) return track; - - const migratedElements = elementsValue.map((element) => { - if (!isRecord(element)) return element; - if (element.type !== "sticker") return element; - if ( - typeof element.intrinsicWidth === "number" && - typeof element.intrinsicHeight === "number" - ) { - return element; - } - return { - ...element, - intrinsicWidth: STICKER_INTRINSIC_SIZE_FALLBACK, - intrinsicHeight: STICKER_INTRINSIC_SIZE_FALLBACK, - }; - }); - - return { ...track, elements: migratedElements }; - }); - - return { ...scene, tracks: migratedTracks }; - }); - - return { ...project, scenes: migratedScenes }; -} +import { STICKER_INTRINSIC_SIZE_FALLBACK } from "@/constants/sticker-constants"; +import type { MigrationResult, ProjectRecord } from "./types"; +import { getProjectId, isRecord } from "./utils"; + +export function transformProjectV14ToV15({ + project, +}: { + project: ProjectRecord; +}): MigrationResult { + if (!getProjectId({ project })) { + return { project, skipped: true, reason: "no project id" }; + } + + if (typeof project.version === "number" && project.version >= 15) { + return { project, skipped: true, reason: "already v15" }; + } + + const migratedProject = backfillStickerIntrinsicDimensions({ project }); + + return { + project: { ...migratedProject, version: 15 }, + skipped: false, + }; +} + +function backfillStickerIntrinsicDimensions({ + project, +}: { + project: ProjectRecord; +}): ProjectRecord { + const scenesValue = project.scenes; + if (!Array.isArray(scenesValue)) return project; + + const migratedScenes = scenesValue.map((scene) => { + if (!isRecord(scene)) return scene; + const tracksValue = scene.tracks; + if (!Array.isArray(tracksValue)) return scene; + + const migratedTracks = tracksValue.map((track) => { + if (!isRecord(track)) return track; + const elementsValue = track.elements; + if (!Array.isArray(elementsValue)) return track; + + const migratedElements = elementsValue.map((element) => { + if (!isRecord(element)) return element; + if (element.type !== "sticker") return element; + if ( + typeof element.intrinsicWidth === "number" && + typeof element.intrinsicHeight === "number" + ) { + return element; + } + return { + ...element, + intrinsicWidth: STICKER_INTRINSIC_SIZE_FALLBACK, + intrinsicHeight: STICKER_INTRINSIC_SIZE_FALLBACK, + }; + }); + + return { ...track, elements: migratedElements }; + }); + + return { ...scene, tracks: migratedTracks }; + }); + + return { ...project, scenes: migratedScenes }; +} diff --git a/apps/web/src/services/storage/migrations/transformers/v15-to-v16.ts b/apps/web/src/services/storage/migrations/transformers/v15-to-v16.ts index 15e2f3bb..746938d0 100644 --- a/apps/web/src/services/storage/migrations/transformers/v15-to-v16.ts +++ b/apps/web/src/services/storage/migrations/transformers/v15-to-v16.ts @@ -1,60 +1,60 @@ -import type { MigrationResult, ProjectRecord } from "./types"; -import { getProjectId, isRecord } from "./utils"; - -export function transformProjectV15ToV16({ - project, -}: { - project: ProjectRecord; -}): MigrationResult { - if (!getProjectId({ project })) { - return { project, skipped: true, reason: "no project id" }; - } - - if (typeof project.version === "number" && project.version >= 16) { - return { project, skipped: true, reason: "already v16" }; - } - - return { - project: { - ...renameStickerTracksToGraphic({ project }), - version: 16, - }, - skipped: false, - }; -} - -function renameStickerTracksToGraphic({ - project, -}: { - project: ProjectRecord; -}): ProjectRecord { - const scenesValue = project.scenes; - if (!Array.isArray(scenesValue)) { - return project; - } - - const migratedScenes = scenesValue.map((scene) => { - if (!isRecord(scene) || !Array.isArray(scene.tracks)) { - return scene; - } - - return { - ...scene, - tracks: scene.tracks.map((track) => { - if (!isRecord(track) || track.type !== "sticker") { - return track; - } - - return { - ...track, - type: "graphic", - }; - }), - }; - }); - - return { - ...project, - scenes: migratedScenes, - }; -} +import type { MigrationResult, ProjectRecord } from "./types"; +import { getProjectId, isRecord } from "./utils"; + +export function transformProjectV15ToV16({ + project, +}: { + project: ProjectRecord; +}): MigrationResult { + if (!getProjectId({ project })) { + return { project, skipped: true, reason: "no project id" }; + } + + if (typeof project.version === "number" && project.version >= 16) { + return { project, skipped: true, reason: "already v16" }; + } + + return { + project: { + ...renameStickerTracksToGraphic({ project }), + version: 16, + }, + skipped: false, + }; +} + +function renameStickerTracksToGraphic({ + project, +}: { + project: ProjectRecord; +}): ProjectRecord { + const scenesValue = project.scenes; + if (!Array.isArray(scenesValue)) { + return project; + } + + const migratedScenes = scenesValue.map((scene) => { + if (!isRecord(scene) || !Array.isArray(scene.tracks)) { + return scene; + } + + return { + ...scene, + tracks: scene.tracks.map((track) => { + if (!isRecord(track) || track.type !== "sticker") { + return track; + } + + return { + ...track, + type: "graphic", + }; + }), + }; + }); + + return { + ...project, + scenes: migratedScenes, + }; +} diff --git a/apps/web/src/services/storage/migrations/transformers/v16-to-v17.ts b/apps/web/src/services/storage/migrations/transformers/v16-to-v17.ts index 7fec70e9..00474188 100644 --- a/apps/web/src/services/storage/migrations/transformers/v16-to-v17.ts +++ b/apps/web/src/services/storage/migrations/transformers/v16-to-v17.ts @@ -1,107 +1,107 @@ -import type { MigrationResult, ProjectRecord } from "./types"; -import { getProjectId, isRecord } from "./utils"; - -export function transformProjectV16ToV17({ - project, -}: { - project: ProjectRecord; -}): MigrationResult { - if (!getProjectId({ project })) { - return { project, skipped: true, reason: "no project id" }; - } - - if (typeof project.version === "number" && project.version >= 17) { - return { project, skipped: true, reason: "already v17" }; - } - - return { - project: { - ...backfillMaskStrokeAlign({ project }), - version: 17, - }, - skipped: false, - }; -} - -function backfillMaskStrokeAlign({ - project, -}: { - project: ProjectRecord; -}): ProjectRecord { - const scenesValue = project.scenes; - if (!Array.isArray(scenesValue)) { - return project; - } - - const migratedScenes = scenesValue.map((scene) => { - if (!isRecord(scene)) { - return scene; - } - - const tracksValue = scene.tracks; - if (!Array.isArray(tracksValue)) { - return scene; - } - - const migratedTracks = tracksValue.map((track) => { - if (!isRecord(track)) { - return track; - } - - const elementsValue = track.elements; - if (!Array.isArray(elementsValue)) { - return track; - } - - const migratedElements = elementsValue.map((element) => { - if (!isRecord(element)) { - return element; - } - - const masksValue = element.masks; - if (!Array.isArray(masksValue)) { - return element; - } - - const migratedMasks = masksValue.map((mask) => { - if (!isRecord(mask)) { - return mask; - } - - const paramsValue = mask.params; - if (!isRecord(paramsValue) || typeof paramsValue.strokeAlign === "string") { - return mask; - } - - return { - ...mask, - params: { - ...paramsValue, - strokeAlign: "center", - }, - }; - }); - - return { - ...element, - masks: migratedMasks, - }; - }); - - return { - ...track, - elements: migratedElements, - }; - }); - - return { - ...scene, - tracks: migratedTracks, - }; - }); - - return { - ...project, - scenes: migratedScenes, - }; -} +import type { MigrationResult, ProjectRecord } from "./types"; +import { getProjectId, isRecord } from "./utils"; + +export function transformProjectV16ToV17({ + project, +}: { + project: ProjectRecord; +}): MigrationResult { + if (!getProjectId({ project })) { + return { project, skipped: true, reason: "no project id" }; + } + + if (typeof project.version === "number" && project.version >= 17) { + return { project, skipped: true, reason: "already v17" }; + } + + return { + project: { + ...backfillMaskStrokeAlign({ project }), + version: 17, + }, + skipped: false, + }; +} + +function backfillMaskStrokeAlign({ + project, +}: { + project: ProjectRecord; +}): ProjectRecord { + const scenesValue = project.scenes; + if (!Array.isArray(scenesValue)) { + return project; + } + + const migratedScenes = scenesValue.map((scene) => { + if (!isRecord(scene)) { + return scene; + } + + const tracksValue = scene.tracks; + if (!Array.isArray(tracksValue)) { + return scene; + } + + const migratedTracks = tracksValue.map((track) => { + if (!isRecord(track)) { + return track; + } + + const elementsValue = track.elements; + if (!Array.isArray(elementsValue)) { + return track; + } + + const migratedElements = elementsValue.map((element) => { + if (!isRecord(element)) { + return element; + } + + const masksValue = element.masks; + if (!Array.isArray(masksValue)) { + return element; + } + + const migratedMasks = masksValue.map((mask) => { + if (!isRecord(mask)) { + return mask; + } + + const paramsValue = mask.params; + if (!isRecord(paramsValue) || typeof paramsValue.strokeAlign === "string") { + return mask; + } + + return { + ...mask, + params: { + ...paramsValue, + strokeAlign: "center", + }, + }; + }); + + return { + ...element, + masks: migratedMasks, + }; + }); + + return { + ...track, + elements: migratedElements, + }; + }); + + return { + ...scene, + tracks: migratedTracks, + }; + }); + + return { + ...project, + scenes: migratedScenes, + }; +} diff --git a/apps/web/src/services/storage/migrations/transformers/v17-to-v18.ts b/apps/web/src/services/storage/migrations/transformers/v17-to-v18.ts index 31a60121..5dcf6feb 100644 --- a/apps/web/src/services/storage/migrations/transformers/v17-to-v18.ts +++ b/apps/web/src/services/storage/migrations/transformers/v17-to-v18.ts @@ -1,105 +1,105 @@ -import type { MigrationResult, ProjectRecord } from "./types"; -import { VOLUME_DB_MIN } from "@/lib/timeline/audio-constants"; -import { clampDb } from "@/lib/timeline/audio-state"; -import { getProjectId, isRecord } from "./utils"; - -export function transformProjectV17ToV18({ - project, -}: { - project: ProjectRecord; -}): MigrationResult { - if (!getProjectId({ project })) { - return { project, skipped: true, reason: "no project id" }; - } - - if (typeof project.version === "number" && project.version >= 18) { - return { project, skipped: true, reason: "already v18" }; - } - - return { - project: { - ...migrateElementVolumes({ project }), - version: 18, - }, - skipped: false, - }; -} - -function migrateElementVolumes({ - project, -}: { - project: ProjectRecord; -}): ProjectRecord { - const scenesValue = project.scenes; - if (!Array.isArray(scenesValue)) { - return project; - } - - const migratedScenes = scenesValue.map((scene) => { - if (!isRecord(scene)) { - return scene; - } - - const tracksValue = scene.tracks; - if (!Array.isArray(tracksValue)) { - return scene; - } - - const migratedTracks = tracksValue.map((track) => { - if (!isRecord(track)) { - return track; - } - - const elementsValue = track.elements; - if (!Array.isArray(elementsValue)) { - return track; - } - - const migratedElements = elementsValue.map((element) => { - if (!isRecord(element)) { - return element; - } - - if (element.type !== "audio" && element.type !== "video") { - return element; - } - - const legacyVolume = element.volume; - return { - ...element, - volume: - typeof legacyVolume === "number" - ? linearGainToDb(legacyVolume) - : 0, - }; - }); - - return { - ...track, - elements: migratedElements, - }; - }); - - return { - ...scene, - tracks: migratedTracks, - }; - }); - - return { - ...project, - scenes: migratedScenes, - }; -} - -function linearGainToDb(gain: number): number { - if (!Number.isFinite(gain)) { - return 0; - } - - if (gain <= 0) { - return VOLUME_DB_MIN; - } - - return clampDb(20 * Math.log10(gain)); -} +import type { MigrationResult, ProjectRecord } from "./types"; +import { VOLUME_DB_MIN } from "@/lib/timeline/audio-constants"; +import { clampDb } from "@/lib/timeline/audio-state"; +import { getProjectId, isRecord } from "./utils"; + +export function transformProjectV17ToV18({ + project, +}: { + project: ProjectRecord; +}): MigrationResult { + if (!getProjectId({ project })) { + return { project, skipped: true, reason: "no project id" }; + } + + if (typeof project.version === "number" && project.version >= 18) { + return { project, skipped: true, reason: "already v18" }; + } + + return { + project: { + ...migrateElementVolumes({ project }), + version: 18, + }, + skipped: false, + }; +} + +function migrateElementVolumes({ + project, +}: { + project: ProjectRecord; +}): ProjectRecord { + const scenesValue = project.scenes; + if (!Array.isArray(scenesValue)) { + return project; + } + + const migratedScenes = scenesValue.map((scene) => { + if (!isRecord(scene)) { + return scene; + } + + const tracksValue = scene.tracks; + if (!Array.isArray(tracksValue)) { + return scene; + } + + const migratedTracks = tracksValue.map((track) => { + if (!isRecord(track)) { + return track; + } + + const elementsValue = track.elements; + if (!Array.isArray(elementsValue)) { + return track; + } + + const migratedElements = elementsValue.map((element) => { + if (!isRecord(element)) { + return element; + } + + if (element.type !== "audio" && element.type !== "video") { + return element; + } + + const legacyVolume = element.volume; + return { + ...element, + volume: + typeof legacyVolume === "number" + ? linearGainToDb(legacyVolume) + : 0, + }; + }); + + return { + ...track, + elements: migratedElements, + }; + }); + + return { + ...scene, + tracks: migratedTracks, + }; + }); + + return { + ...project, + scenes: migratedScenes, + }; +} + +function linearGainToDb(gain: number): number { + if (!Number.isFinite(gain)) { + return 0; + } + + if (gain <= 0) { + return VOLUME_DB_MIN; + } + + return clampDb(20 * Math.log10(gain)); +} diff --git a/apps/web/src/services/storage/migrations/transformers/v18-to-v19.ts b/apps/web/src/services/storage/migrations/transformers/v18-to-v19.ts index 1946b829..3afa5203 100644 --- a/apps/web/src/services/storage/migrations/transformers/v18-to-v19.ts +++ b/apps/web/src/services/storage/migrations/transformers/v18-to-v19.ts @@ -1,46 +1,46 @@ -import type { MigrationResult, ProjectRecord } from "./types"; -import { getProjectId, isRecord } from "./utils"; - -export function transformProjectV18ToV19({ - project, -}: { - project: ProjectRecord; -}): MigrationResult { - if (!getProjectId({ project })) { - return { project, skipped: true, reason: "no project id" }; - } - - if (typeof project.version === "number" && project.version >= 19) { - return { project, skipped: true, reason: "already v19" }; - } - - return { - project: { - ...migrateCanvasSizeSettings({ project }), - version: 19, - }, - skipped: false, - }; -} - -function migrateCanvasSizeSettings({ - project, -}: { - project: ProjectRecord; -}): ProjectRecord { - const settingsValue = project.settings; - if (!isRecord(settingsValue)) { - return project; - } - - const migratedSettings: Record = { - ...settingsValue, - canvasSizeMode: "preset", - lastCustomCanvasSize: null, - }; - - return { - ...project, - settings: migratedSettings, - }; -} +import type { MigrationResult, ProjectRecord } from "./types"; +import { getProjectId, isRecord } from "./utils"; + +export function transformProjectV18ToV19({ + project, +}: { + project: ProjectRecord; +}): MigrationResult { + if (!getProjectId({ project })) { + return { project, skipped: true, reason: "no project id" }; + } + + if (typeof project.version === "number" && project.version >= 19) { + return { project, skipped: true, reason: "already v19" }; + } + + return { + project: { + ...migrateCanvasSizeSettings({ project }), + version: 19, + }, + skipped: false, + }; +} + +function migrateCanvasSizeSettings({ + project, +}: { + project: ProjectRecord; +}): ProjectRecord { + const settingsValue = project.settings; + if (!isRecord(settingsValue)) { + return project; + } + + const migratedSettings: Record = { + ...settingsValue, + canvasSizeMode: "preset", + lastCustomCanvasSize: null, + }; + + return { + ...project, + settings: migratedSettings, + }; +} diff --git a/apps/web/src/services/storage/migrations/transformers/v3-to-v4.ts b/apps/web/src/services/storage/migrations/transformers/v3-to-v4.ts index d6e1efee..84d97fd8 100644 --- a/apps/web/src/services/storage/migrations/transformers/v3-to-v4.ts +++ b/apps/web/src/services/storage/migrations/transformers/v3-to-v4.ts @@ -1,189 +1,189 @@ -import type { MigrationResult, ProjectRecord } from "./types"; -import { getProjectId, isRecord } from "./utils"; - -const LEGACY_FONT_WEIGHT_MAP = { - normal: "400", - bold: "700", -} as const; - -const VALID_NUMERIC_FONT_WEIGHTS = new Set([ - "100", - "200", - "300", - "400", - "500", - "600", - "700", - "800", - "900", -]); - -export function transformProjectV3ToV4({ - project, -}: { - project: ProjectRecord; -}): MigrationResult { - const projectId = getProjectId({ project }); - if (!projectId) { - return { project, skipped: true, reason: "no project id" }; - } - - if (isV4Project({ project })) { - return { project, skipped: true, reason: "already v4" }; - } - - const migratedProject = normalizeProjectTextFontWeights({ project }); - - return { - project: { - ...migratedProject, - version: 4, - }, - skipped: false, - }; -} - -function normalizeProjectTextFontWeights({ - project, -}: { - project: ProjectRecord; -}): ProjectRecord { - const scenesValue = project.scenes; - if (!Array.isArray(scenesValue)) { - return project; - } - - let hasSceneChanges = false; - const normalizedScenes = scenesValue.map((scene) => { - const normalizedScene = normalizeSceneTextFontWeights({ scene }); - if (normalizedScene !== scene) { - hasSceneChanges = true; - } - return normalizedScene; - }); - - if (!hasSceneChanges) { - return project; - } - - return { - ...project, - scenes: normalizedScenes, - }; -} - -function normalizeSceneTextFontWeights({ scene }: { scene: unknown }): unknown { - if (!isRecord(scene)) { - return scene; - } - - const tracksValue = scene.tracks; - if (!Array.isArray(tracksValue)) { - return scene; - } - - let hasTrackChanges = false; - const normalizedTracks = tracksValue.map((track) => { - const normalizedTrack = normalizeTrackTextFontWeights({ track }); - if (normalizedTrack !== track) { - hasTrackChanges = true; - } - return normalizedTrack; - }); - - if (!hasTrackChanges) { - return scene; - } - - return { - ...scene, - tracks: normalizedTracks, - }; -} - -function normalizeTrackTextFontWeights({ track }: { track: unknown }): unknown { - if (!isRecord(track)) { - return track; - } - - if (track.type !== "text") { - return track; - } - - const elementsValue = track.elements; - if (!Array.isArray(elementsValue)) { - return track; - } - - let hasElementChanges = false; - const normalizedElements = elementsValue.map((element) => { - const normalizedElement = normalizeTextElementFontWeight({ element }); - if (normalizedElement !== element) { - hasElementChanges = true; - } - return normalizedElement; - }); - - if (!hasElementChanges) { - return track; - } - - return { - ...track, - elements: normalizedElements, - }; -} - -function normalizeTextElementFontWeight({ - element, -}: { - element: unknown; -}): unknown { - if (!isRecord(element) || element.type !== "text") { - return element; - } - - const normalizedWeight = normalizeFontWeight({ value: element.fontWeight }); - if (normalizedWeight === element.fontWeight) { - return element; - } - - return { - ...element, - fontWeight: normalizedWeight, - }; -} - -function normalizeFontWeight({ value }: { value: unknown }): unknown { - if (typeof value === "number") { - const numericWeight = String(value); - if (VALID_NUMERIC_FONT_WEIGHTS.has(numericWeight)) { - return numericWeight; - } - return value; - } - - if (typeof value !== "string") { - return value; - } - - const normalized = value.trim().toLowerCase(); - if (normalized in LEGACY_FONT_WEIGHT_MAP) { - return LEGACY_FONT_WEIGHT_MAP[ - normalized as keyof typeof LEGACY_FONT_WEIGHT_MAP - ]; - } - - if (VALID_NUMERIC_FONT_WEIGHTS.has(normalized)) { - return normalized; - } - - return value; -} - -export { getProjectId } from "./utils"; - -function isV4Project({ project }: { project: ProjectRecord }): boolean { - const versionValue = project.version; - return typeof versionValue === "number" && versionValue >= 4; -} +import type { MigrationResult, ProjectRecord } from "./types"; +import { getProjectId, isRecord } from "./utils"; + +const LEGACY_FONT_WEIGHT_MAP = { + normal: "400", + bold: "700", +} as const; + +const VALID_NUMERIC_FONT_WEIGHTS = new Set([ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900", +]); + +export function transformProjectV3ToV4({ + project, +}: { + project: ProjectRecord; +}): MigrationResult { + const projectId = getProjectId({ project }); + if (!projectId) { + return { project, skipped: true, reason: "no project id" }; + } + + if (isV4Project({ project })) { + return { project, skipped: true, reason: "already v4" }; + } + + const migratedProject = normalizeProjectTextFontWeights({ project }); + + return { + project: { + ...migratedProject, + version: 4, + }, + skipped: false, + }; +} + +function normalizeProjectTextFontWeights({ + project, +}: { + project: ProjectRecord; +}): ProjectRecord { + const scenesValue = project.scenes; + if (!Array.isArray(scenesValue)) { + return project; + } + + let hasSceneChanges = false; + const normalizedScenes = scenesValue.map((scene) => { + const normalizedScene = normalizeSceneTextFontWeights({ scene }); + if (normalizedScene !== scene) { + hasSceneChanges = true; + } + return normalizedScene; + }); + + if (!hasSceneChanges) { + return project; + } + + return { + ...project, + scenes: normalizedScenes, + }; +} + +function normalizeSceneTextFontWeights({ scene }: { scene: unknown }): unknown { + if (!isRecord(scene)) { + return scene; + } + + const tracksValue = scene.tracks; + if (!Array.isArray(tracksValue)) { + return scene; + } + + let hasTrackChanges = false; + const normalizedTracks = tracksValue.map((track) => { + const normalizedTrack = normalizeTrackTextFontWeights({ track }); + if (normalizedTrack !== track) { + hasTrackChanges = true; + } + return normalizedTrack; + }); + + if (!hasTrackChanges) { + return scene; + } + + return { + ...scene, + tracks: normalizedTracks, + }; +} + +function normalizeTrackTextFontWeights({ track }: { track: unknown }): unknown { + if (!isRecord(track)) { + return track; + } + + if (track.type !== "text") { + return track; + } + + const elementsValue = track.elements; + if (!Array.isArray(elementsValue)) { + return track; + } + + let hasElementChanges = false; + const normalizedElements = elementsValue.map((element) => { + const normalizedElement = normalizeTextElementFontWeight({ element }); + if (normalizedElement !== element) { + hasElementChanges = true; + } + return normalizedElement; + }); + + if (!hasElementChanges) { + return track; + } + + return { + ...track, + elements: normalizedElements, + }; +} + +function normalizeTextElementFontWeight({ + element, +}: { + element: unknown; +}): unknown { + if (!isRecord(element) || element.type !== "text") { + return element; + } + + const normalizedWeight = normalizeFontWeight({ value: element.fontWeight }); + if (normalizedWeight === element.fontWeight) { + return element; + } + + return { + ...element, + fontWeight: normalizedWeight, + }; +} + +function normalizeFontWeight({ value }: { value: unknown }): unknown { + if (typeof value === "number") { + const numericWeight = String(value); + if (VALID_NUMERIC_FONT_WEIGHTS.has(numericWeight)) { + return numericWeight; + } + return value; + } + + if (typeof value !== "string") { + return value; + } + + const normalized = value.trim().toLowerCase(); + if (normalized in LEGACY_FONT_WEIGHT_MAP) { + return LEGACY_FONT_WEIGHT_MAP[ + normalized as keyof typeof LEGACY_FONT_WEIGHT_MAP + ]; + } + + if (VALID_NUMERIC_FONT_WEIGHTS.has(normalized)) { + return normalized; + } + + return value; +} + +export { getProjectId } from "./utils"; + +function isV4Project({ project }: { project: ProjectRecord }): boolean { + const versionValue = project.version; + return typeof versionValue === "number" && versionValue >= 4; +} diff --git a/apps/web/src/services/storage/migrations/transformers/v4-to-v5.ts b/apps/web/src/services/storage/migrations/transformers/v4-to-v5.ts index 971e3510..243b130c 100644 --- a/apps/web/src/services/storage/migrations/transformers/v4-to-v5.ts +++ b/apps/web/src/services/storage/migrations/transformers/v4-to-v5.ts @@ -1,177 +1,177 @@ -import type { MigrationResult, ProjectRecord } from "./types"; -import { getProjectId, isRecord } from "./utils"; - -const KNOWN_STICKER_PROVIDER_IDS = new Set([ - "icons", - "emoji", - "flags", - "shapes", -]); - -export function transformProjectV4ToV5({ - project, -}: { - project: ProjectRecord; -}): MigrationResult { - const projectId = getProjectId({ project }); - if (!projectId) { - return { project, skipped: true, reason: "no project id" }; - } - - if (isV5Project({ project })) { - return { project, skipped: true, reason: "already v5" }; - } - - const migratedProject = migrateProjectStickerElements({ project }); - - return { - project: { - ...migratedProject, - version: 5, - }, - skipped: false, - }; -} - -function migrateProjectStickerElements({ - project, -}: { - project: ProjectRecord; -}): ProjectRecord { - const scenesValue = project.scenes; - if (!Array.isArray(scenesValue)) { - return project; - } - - let hasSceneChanges = false; - const migratedScenes = scenesValue.map((scene) => { - const migratedScene = migrateSceneStickerElements({ scene }); - if (migratedScene !== scene) { - hasSceneChanges = true; - } - return migratedScene; - }); - - if (!hasSceneChanges) { - return project; - } - - return { - ...project, - scenes: migratedScenes, - }; -} - -function migrateSceneStickerElements({ scene }: { scene: unknown }): unknown { - if (!isRecord(scene)) { - return scene; - } - - const tracksValue = scene.tracks; - if (!Array.isArray(tracksValue)) { - return scene; - } - - let hasTrackChanges = false; - const migratedTracks = tracksValue.map((track) => { - const migratedTrack = migrateTrackStickerElements({ track }); - if (migratedTrack !== track) { - hasTrackChanges = true; - } - return migratedTrack; - }); - - if (!hasTrackChanges) { - return scene; - } - - return { - ...scene, - tracks: migratedTracks, - }; -} - -function migrateTrackStickerElements({ track }: { track: unknown }): unknown { - if (!isRecord(track)) { - return track; - } - - const elementsValue = track.elements; - if (!Array.isArray(elementsValue)) { - return track; - } - - let hasElementChanges = false; - const migratedElements = elementsValue.map((element) => { - const migratedElement = migrateStickerElement({ element }); - if (migratedElement !== element) { - hasElementChanges = true; - } - return migratedElement; - }); - - if (!hasElementChanges) { - return track; - } - - return { - ...track, - elements: migratedElements, - }; -} - -function migrateStickerElement({ element }: { element: unknown }): unknown { - if (!isRecord(element) || element.type !== "sticker") { - return element; - } - - const existingStickerId = - typeof element.stickerId === "string" ? element.stickerId : null; - const legacyIconName = - typeof element.iconName === "string" ? element.iconName : null; - - const normalizedStickerId = - normalizeStickerId({ - value: existingStickerId ?? legacyIconName, - }) ?? null; - - const hasLegacyIconName = "iconName" in element; - const hasLegacyColor = "color" in element; - const shouldUpdateStickerId = normalizedStickerId !== existingStickerId; - - if (!hasLegacyIconName && !hasLegacyColor && !shouldUpdateStickerId) { - return element; - } - - const { - iconName: _legacyIconName, - color: _legacyColor, - ...remaining - } = element; - return normalizedStickerId - ? { ...remaining, stickerId: normalizedStickerId } - : remaining; -} - -function normalizeStickerId({ value }: { value: unknown }): string | null { - if (typeof value !== "string" || value.length === 0) { - return null; - } - - const separatorIndex = value.indexOf(":"); - if (separatorIndex === -1) { - return `icons:${value}`; - } - - const maybeProvider = value.slice(0, separatorIndex); - if (KNOWN_STICKER_PROVIDER_IDS.has(maybeProvider)) { - return value; - } - - return `icons:${value}`; -} - -function isV5Project({ project }: { project: ProjectRecord }): boolean { - const versionValue = project.version; - return typeof versionValue === "number" && versionValue >= 5; -} +import type { MigrationResult, ProjectRecord } from "./types"; +import { getProjectId, isRecord } from "./utils"; + +const KNOWN_STICKER_PROVIDER_IDS = new Set([ + "icons", + "emoji", + "flags", + "shapes", +]); + +export function transformProjectV4ToV5({ + project, +}: { + project: ProjectRecord; +}): MigrationResult { + const projectId = getProjectId({ project }); + if (!projectId) { + return { project, skipped: true, reason: "no project id" }; + } + + if (isV5Project({ project })) { + return { project, skipped: true, reason: "already v5" }; + } + + const migratedProject = migrateProjectStickerElements({ project }); + + return { + project: { + ...migratedProject, + version: 5, + }, + skipped: false, + }; +} + +function migrateProjectStickerElements({ + project, +}: { + project: ProjectRecord; +}): ProjectRecord { + const scenesValue = project.scenes; + if (!Array.isArray(scenesValue)) { + return project; + } + + let hasSceneChanges = false; + const migratedScenes = scenesValue.map((scene) => { + const migratedScene = migrateSceneStickerElements({ scene }); + if (migratedScene !== scene) { + hasSceneChanges = true; + } + return migratedScene; + }); + + if (!hasSceneChanges) { + return project; + } + + return { + ...project, + scenes: migratedScenes, + }; +} + +function migrateSceneStickerElements({ scene }: { scene: unknown }): unknown { + if (!isRecord(scene)) { + return scene; + } + + const tracksValue = scene.tracks; + if (!Array.isArray(tracksValue)) { + return scene; + } + + let hasTrackChanges = false; + const migratedTracks = tracksValue.map((track) => { + const migratedTrack = migrateTrackStickerElements({ track }); + if (migratedTrack !== track) { + hasTrackChanges = true; + } + return migratedTrack; + }); + + if (!hasTrackChanges) { + return scene; + } + + return { + ...scene, + tracks: migratedTracks, + }; +} + +function migrateTrackStickerElements({ track }: { track: unknown }): unknown { + if (!isRecord(track)) { + return track; + } + + const elementsValue = track.elements; + if (!Array.isArray(elementsValue)) { + return track; + } + + let hasElementChanges = false; + const migratedElements = elementsValue.map((element) => { + const migratedElement = migrateStickerElement({ element }); + if (migratedElement !== element) { + hasElementChanges = true; + } + return migratedElement; + }); + + if (!hasElementChanges) { + return track; + } + + return { + ...track, + elements: migratedElements, + }; +} + +function migrateStickerElement({ element }: { element: unknown }): unknown { + if (!isRecord(element) || element.type !== "sticker") { + return element; + } + + const existingStickerId = + typeof element.stickerId === "string" ? element.stickerId : null; + const legacyIconName = + typeof element.iconName === "string" ? element.iconName : null; + + const normalizedStickerId = + normalizeStickerId({ + value: existingStickerId ?? legacyIconName, + }) ?? null; + + const hasLegacyIconName = "iconName" in element; + const hasLegacyColor = "color" in element; + const shouldUpdateStickerId = normalizedStickerId !== existingStickerId; + + if (!hasLegacyIconName && !hasLegacyColor && !shouldUpdateStickerId) { + return element; + } + + const { + iconName: _legacyIconName, + color: _legacyColor, + ...remaining + } = element; + return normalizedStickerId + ? { ...remaining, stickerId: normalizedStickerId } + : remaining; +} + +function normalizeStickerId({ value }: { value: unknown }): string | null { + if (typeof value !== "string" || value.length === 0) { + return null; + } + + const separatorIndex = value.indexOf(":"); + if (separatorIndex === -1) { + return `icons:${value}`; + } + + const maybeProvider = value.slice(0, separatorIndex); + if (KNOWN_STICKER_PROVIDER_IDS.has(maybeProvider)) { + return value; + } + + return `icons:${value}`; +} + +function isV5Project({ project }: { project: ProjectRecord }): boolean { + const versionValue = project.version; + return typeof versionValue === "number" && versionValue >= 5; +} diff --git a/apps/web/src/services/storage/migrations/transformers/v5-to-v6.ts b/apps/web/src/services/storage/migrations/transformers/v5-to-v6.ts index ca855149..f3890f24 100644 --- a/apps/web/src/services/storage/migrations/transformers/v5-to-v6.ts +++ b/apps/web/src/services/storage/migrations/transformers/v5-to-v6.ts @@ -1,88 +1,88 @@ -import type { MigrationResult, ProjectRecord } from "./types"; -import { getProjectId, isRecord } from "./utils"; - -export function transformProjectV5ToV6({ - project, -}: { - project: ProjectRecord; -}): MigrationResult { - const projectId = getProjectId({ project }); - if (!projectId) { - return { project, skipped: true, reason: "no project id" }; - } - - if (isV6Project({ project })) { - return { project, skipped: true, reason: "already v6" }; - } - - const migratedProject = migrateProjectBookmarks({ project }); - - return { - project: { - ...migratedProject, - version: 6, - }, - skipped: false, - }; -} - -function migrateProjectBookmarks({ - project, -}: { - project: ProjectRecord; -}): ProjectRecord { - const scenesValue = project.scenes; - if (!Array.isArray(scenesValue)) { - return project; - } - - let hasSceneChanges = false; - const migratedScenes = scenesValue.map((scene) => { - const migratedScene = migrateSceneBookmarks({ scene }); - if (migratedScene !== scene) { - hasSceneChanges = true; - } - return migratedScene; - }); - - if (!hasSceneChanges) { - return project; - } - - return { - ...project, - scenes: migratedScenes, - }; -} - -function migrateSceneBookmarks({ scene }: { scene: unknown }): unknown { - if (!isRecord(scene)) { - return scene; - } - - const bookmarksValue = scene.bookmarks; - if (!Array.isArray(bookmarksValue)) { - return scene; - } - - const needsMigration = bookmarksValue.some( - (bookmark) => typeof bookmark === "number", - ); - if (!needsMigration) { - return scene; - } - - const migratedBookmarks = bookmarksValue.map((bookmark) => - typeof bookmark === "number" ? { time: bookmark } : bookmark, - ); - - return { - ...scene, - bookmarks: migratedBookmarks, - }; -} - -function isV6Project({ project }: { project: ProjectRecord }): boolean { - const versionValue = project.version; - return typeof versionValue === "number" && versionValue >= 6; -} +import type { MigrationResult, ProjectRecord } from "./types"; +import { getProjectId, isRecord } from "./utils"; + +export function transformProjectV5ToV6({ + project, +}: { + project: ProjectRecord; +}): MigrationResult { + const projectId = getProjectId({ project }); + if (!projectId) { + return { project, skipped: true, reason: "no project id" }; + } + + if (isV6Project({ project })) { + return { project, skipped: true, reason: "already v6" }; + } + + const migratedProject = migrateProjectBookmarks({ project }); + + return { + project: { + ...migratedProject, + version: 6, + }, + skipped: false, + }; +} + +function migrateProjectBookmarks({ + project, +}: { + project: ProjectRecord; +}): ProjectRecord { + const scenesValue = project.scenes; + if (!Array.isArray(scenesValue)) { + return project; + } + + let hasSceneChanges = false; + const migratedScenes = scenesValue.map((scene) => { + const migratedScene = migrateSceneBookmarks({ scene }); + if (migratedScene !== scene) { + hasSceneChanges = true; + } + return migratedScene; + }); + + if (!hasSceneChanges) { + return project; + } + + return { + ...project, + scenes: migratedScenes, + }; +} + +function migrateSceneBookmarks({ scene }: { scene: unknown }): unknown { + if (!isRecord(scene)) { + return scene; + } + + const bookmarksValue = scene.bookmarks; + if (!Array.isArray(bookmarksValue)) { + return scene; + } + + const needsMigration = bookmarksValue.some( + (bookmark) => typeof bookmark === "number", + ); + if (!needsMigration) { + return scene; + } + + const migratedBookmarks = bookmarksValue.map((bookmark) => + typeof bookmark === "number" ? { time: bookmark } : bookmark, + ); + + return { + ...scene, + bookmarks: migratedBookmarks, + }; +} + +function isV6Project({ project }: { project: ProjectRecord }): boolean { + const versionValue = project.version; + return typeof versionValue === "number" && versionValue >= 6; +} diff --git a/apps/web/src/services/storage/migrations/transformers/v6-to-v7.ts b/apps/web/src/services/storage/migrations/transformers/v6-to-v7.ts index 6f4848d4..fe3b75fd 100644 --- a/apps/web/src/services/storage/migrations/transformers/v6-to-v7.ts +++ b/apps/web/src/services/storage/migrations/transformers/v6-to-v7.ts @@ -1,106 +1,106 @@ -import type { MigrationResult, ProjectRecord } from "./types"; -import { getProjectId, isRecord } from "./utils"; - -export function transformProjectV6ToV7({ - project, -}: { - project: ProjectRecord; -}): MigrationResult { - const projectId = getProjectId({ project }); - if (!projectId) { - return { project, skipped: true, reason: "no project id" }; - } - - if (isV7Project({ project })) { - return { project, skipped: true, reason: "already v7" }; - } - - const migratedProject = migrateProjectTextElements({ project }); - - return { - project: { ...migratedProject, version: 7 }, - skipped: false, - }; -} - -function migrateProjectTextElements({ - project, -}: { - project: ProjectRecord; -}): ProjectRecord { - const scenesValue = project.scenes; - if (!Array.isArray(scenesValue)) return project; - - let hasChanges = false; - const migratedScenes = scenesValue.map((scene) => { - const migrated = migrateSceneTextElements({ scene }); - if (migrated !== scene) hasChanges = true; - return migrated; - }); - - if (!hasChanges) return project; - return { ...project, scenes: migratedScenes }; -} - -function migrateSceneTextElements({ scene }: { scene: unknown }): unknown { - if (!isRecord(scene)) return scene; - - const tracksValue = scene.tracks; - if (!Array.isArray(tracksValue)) return scene; - - let hasChanges = false; - const migratedTracks = tracksValue.map((track) => { - const migrated = migrateTrackTextElements({ track }); - if (migrated !== track) hasChanges = true; - return migrated; - }); - - if (!hasChanges) return scene; - return { ...scene, tracks: migratedTracks }; -} - -function migrateTrackTextElements({ track }: { track: unknown }): unknown { - if (!isRecord(track)) return track; - - const elementsValue = track.elements; - if (!Array.isArray(elementsValue)) return track; - - let hasChanges = false; - const migratedElements = elementsValue.map((element) => { - const migrated = migrateTextElement({ element }); - if (migrated !== element) hasChanges = true; - return migrated; - }); - - if (!hasChanges) return track; - return { ...track, elements: migratedElements }; -} - -function migrateTextElement({ element }: { element: unknown }): unknown { - if (!isRecord(element)) return element; - if (element.type !== "text") return element; - if (isRecord(element.background)) return element; - - const backgroundColor = - typeof element.backgroundColor === "string" - ? element.backgroundColor - : "transparent"; - - const { backgroundColor: _removed, ...rest } = element; - - return { - ...rest, - background: { - color: backgroundColor, - cornerRadius: 0, - paddingX: 8, - paddingY: 4, - offsetX: 0, - offsetY: 0, - }, - }; -} - -function isV7Project({ project }: { project: ProjectRecord }): boolean { - return typeof project.version === "number" && project.version >= 7; -} +import type { MigrationResult, ProjectRecord } from "./types"; +import { getProjectId, isRecord } from "./utils"; + +export function transformProjectV6ToV7({ + project, +}: { + project: ProjectRecord; +}): MigrationResult { + const projectId = getProjectId({ project }); + if (!projectId) { + return { project, skipped: true, reason: "no project id" }; + } + + if (isV7Project({ project })) { + return { project, skipped: true, reason: "already v7" }; + } + + const migratedProject = migrateProjectTextElements({ project }); + + return { + project: { ...migratedProject, version: 7 }, + skipped: false, + }; +} + +function migrateProjectTextElements({ + project, +}: { + project: ProjectRecord; +}): ProjectRecord { + const scenesValue = project.scenes; + if (!Array.isArray(scenesValue)) return project; + + let hasChanges = false; + const migratedScenes = scenesValue.map((scene) => { + const migrated = migrateSceneTextElements({ scene }); + if (migrated !== scene) hasChanges = true; + return migrated; + }); + + if (!hasChanges) return project; + return { ...project, scenes: migratedScenes }; +} + +function migrateSceneTextElements({ scene }: { scene: unknown }): unknown { + if (!isRecord(scene)) return scene; + + const tracksValue = scene.tracks; + if (!Array.isArray(tracksValue)) return scene; + + let hasChanges = false; + const migratedTracks = tracksValue.map((track) => { + const migrated = migrateTrackTextElements({ track }); + if (migrated !== track) hasChanges = true; + return migrated; + }); + + if (!hasChanges) return scene; + return { ...scene, tracks: migratedTracks }; +} + +function migrateTrackTextElements({ track }: { track: unknown }): unknown { + if (!isRecord(track)) return track; + + const elementsValue = track.elements; + if (!Array.isArray(elementsValue)) return track; + + let hasChanges = false; + const migratedElements = elementsValue.map((element) => { + const migrated = migrateTextElement({ element }); + if (migrated !== element) hasChanges = true; + return migrated; + }); + + if (!hasChanges) return track; + return { ...track, elements: migratedElements }; +} + +function migrateTextElement({ element }: { element: unknown }): unknown { + if (!isRecord(element)) return element; + if (element.type !== "text") return element; + if (isRecord(element.background)) return element; + + const backgroundColor = + typeof element.backgroundColor === "string" + ? element.backgroundColor + : "transparent"; + + const { backgroundColor: _removed, ...rest } = element; + + return { + ...rest, + background: { + color: backgroundColor, + cornerRadius: 0, + paddingX: 8, + paddingY: 4, + offsetX: 0, + offsetY: 0, + }, + }; +} + +function isV7Project({ project }: { project: ProjectRecord }): boolean { + return typeof project.version === "number" && project.version >= 7; +} diff --git a/apps/web/src/services/storage/migrations/transformers/v7-to-v8.ts b/apps/web/src/services/storage/migrations/transformers/v7-to-v8.ts index 7393f776..e592cde9 100644 --- a/apps/web/src/services/storage/migrations/transformers/v7-to-v8.ts +++ b/apps/web/src/services/storage/migrations/transformers/v7-to-v8.ts @@ -1,96 +1,96 @@ -import type { MigrationResult, ProjectRecord } from "./types"; -import { getProjectId, isRecord } from "./utils"; - -export function transformProjectV7ToV8({ - project, -}: { - project: ProjectRecord; -}): MigrationResult { - const projectId = getProjectId({ project }); - if (!projectId) { - return { project, skipped: true, reason: "no project id" }; - } - - if (isV8Project({ project })) { - return { project, skipped: true, reason: "already v8" }; - } - - const migratedProject = migrateProjectElements({ project }); - - return { - project: { ...migratedProject, version: 8 }, - skipped: false, - }; -} - -function migrateProjectElements({ - project, -}: { - project: ProjectRecord; -}): ProjectRecord { - const scenesValue = project.scenes; - if (!Array.isArray(scenesValue)) return project; - - let hasChanges = false; - const migratedScenes = scenesValue.map((scene) => { - const migrated = migrateSceneElements({ scene }); - if (migrated !== scene) hasChanges = true; - return migrated; - }); - - if (!hasChanges) return project; - return { ...project, scenes: migratedScenes }; -} - -function migrateSceneElements({ scene }: { scene: unknown }): unknown { - if (!isRecord(scene)) return scene; - - const tracksValue = scene.tracks; - if (!Array.isArray(tracksValue)) return scene; - - let hasChanges = false; - const migratedTracks = tracksValue.map((track) => { - const migrated = migrateTrackElements({ track }); - if (migrated !== track) hasChanges = true; - return migrated; - }); - - if (!hasChanges) return scene; - return { ...scene, tracks: migratedTracks }; -} - -function migrateTrackElements({ track }: { track: unknown }): unknown { - if (!isRecord(track)) return track; - - const elementsValue = track.elements; - if (!Array.isArray(elementsValue)) return track; - - let hasChanges = false; - const migratedElements = elementsValue.map((element) => { - const migrated = migrateElement({ element }); - if (migrated !== element) hasChanges = true; - return migrated; - }); - - if (!hasChanges) return track; - return { ...track, elements: migratedElements }; -} - -function migrateElement({ element }: { element: unknown }): unknown { - if (!isRecord(element)) return element; - if (element.type !== "video" && element.type !== "audio") return element; - if (typeof element.sourceDuration === "number") return element; - - const trimStart = typeof element.trimStart === "number" ? element.trimStart : 0; - const duration = typeof element.duration === "number" ? element.duration : 0; - const trimEnd = typeof element.trimEnd === "number" ? element.trimEnd : 0; - - return { - ...element, - sourceDuration: trimStart + duration + trimEnd, - }; -} - -function isV8Project({ project }: { project: ProjectRecord }): boolean { - return typeof project.version === "number" && project.version >= 8; -} +import type { MigrationResult, ProjectRecord } from "./types"; +import { getProjectId, isRecord } from "./utils"; + +export function transformProjectV7ToV8({ + project, +}: { + project: ProjectRecord; +}): MigrationResult { + const projectId = getProjectId({ project }); + if (!projectId) { + return { project, skipped: true, reason: "no project id" }; + } + + if (isV8Project({ project })) { + return { project, skipped: true, reason: "already v8" }; + } + + const migratedProject = migrateProjectElements({ project }); + + return { + project: { ...migratedProject, version: 8 }, + skipped: false, + }; +} + +function migrateProjectElements({ + project, +}: { + project: ProjectRecord; +}): ProjectRecord { + const scenesValue = project.scenes; + if (!Array.isArray(scenesValue)) return project; + + let hasChanges = false; + const migratedScenes = scenesValue.map((scene) => { + const migrated = migrateSceneElements({ scene }); + if (migrated !== scene) hasChanges = true; + return migrated; + }); + + if (!hasChanges) return project; + return { ...project, scenes: migratedScenes }; +} + +function migrateSceneElements({ scene }: { scene: unknown }): unknown { + if (!isRecord(scene)) return scene; + + const tracksValue = scene.tracks; + if (!Array.isArray(tracksValue)) return scene; + + let hasChanges = false; + const migratedTracks = tracksValue.map((track) => { + const migrated = migrateTrackElements({ track }); + if (migrated !== track) hasChanges = true; + return migrated; + }); + + if (!hasChanges) return scene; + return { ...scene, tracks: migratedTracks }; +} + +function migrateTrackElements({ track }: { track: unknown }): unknown { + if (!isRecord(track)) return track; + + const elementsValue = track.elements; + if (!Array.isArray(elementsValue)) return track; + + let hasChanges = false; + const migratedElements = elementsValue.map((element) => { + const migrated = migrateElement({ element }); + if (migrated !== element) hasChanges = true; + return migrated; + }); + + if (!hasChanges) return track; + return { ...track, elements: migratedElements }; +} + +function migrateElement({ element }: { element: unknown }): unknown { + if (!isRecord(element)) return element; + if (element.type !== "video" && element.type !== "audio") return element; + if (typeof element.sourceDuration === "number") return element; + + const trimStart = typeof element.trimStart === "number" ? element.trimStart : 0; + const duration = typeof element.duration === "number" ? element.duration : 0; + const trimEnd = typeof element.trimEnd === "number" ? element.trimEnd : 0; + + return { + ...element, + sourceDuration: trimStart + duration + trimEnd, + }; +} + +function isV8Project({ project }: { project: ProjectRecord }): boolean { + return typeof project.version === "number" && project.version >= 8; +} diff --git a/apps/web/src/services/storage/migrations/transformers/v8-to-v9.ts b/apps/web/src/services/storage/migrations/transformers/v8-to-v9.ts index 1dc895bc..628eebb6 100644 --- a/apps/web/src/services/storage/migrations/transformers/v8-to-v9.ts +++ b/apps/web/src/services/storage/migrations/transformers/v8-to-v9.ts @@ -1,99 +1,99 @@ -import type { MigrationResult, ProjectRecord } from "./types"; -import { getProjectId, isRecord } from "./utils"; - -export function transformProjectV8ToV9({ - project, -}: { - project: ProjectRecord; -}): MigrationResult { - const projectId = getProjectId({ project }); - if (!projectId) { - return { project, skipped: true, reason: "no project id" }; - } - - if (isV9Project({ project })) { - return { project, skipped: true, reason: "already v9" }; - } - - const migratedProject = migrateProjectTextElements({ project }); - - return { - project: { ...migratedProject, version: 9 }, - skipped: false, - }; -} - -function migrateProjectTextElements({ - project, -}: { - project: ProjectRecord; -}): ProjectRecord { - const scenesValue = project.scenes; - if (!Array.isArray(scenesValue)) return project; - - let hasChanges = false; - const migratedScenes = scenesValue.map((scene) => { - const migrated = migrateSceneTextElements({ scene }); - if (migrated !== scene) hasChanges = true; - return migrated; - }); - - if (!hasChanges) return project; - return { ...project, scenes: migratedScenes }; -} - -function migrateSceneTextElements({ scene }: { scene: unknown }): unknown { - if (!isRecord(scene)) return scene; - - const tracksValue = scene.tracks; - if (!Array.isArray(tracksValue)) return scene; - - let hasChanges = false; - const migratedTracks = tracksValue.map((track) => { - const migrated = migrateTrackTextElements({ track }); - if (migrated !== track) hasChanges = true; - return migrated; - }); - - if (!hasChanges) return scene; - return { ...scene, tracks: migratedTracks }; -} - -function migrateTrackTextElements({ track }: { track: unknown }): unknown { - if (!isRecord(track)) return track; - if (track.type !== "text") return track; - - const elementsValue = track.elements; - if (!Array.isArray(elementsValue)) return track; - - let hasChanges = false; - const migratedElements = elementsValue.map((element) => { - const migrated = migrateTextElement({ element }); - if (migrated !== element) hasChanges = true; - return migrated; - }); - - if (!hasChanges) return track; - return { ...track, elements: migratedElements }; -} - -function migrateTextElement({ element }: { element: unknown }): unknown { - if (!isRecord(element)) return element; - if (element.type !== "text") return element; - - const bg = element.background; - if (!isRecord(bg)) return element; - if (typeof bg.enabled === "boolean") return element; - - const color = typeof bg.color === "string" ? bg.color : "transparent"; - const enabled = color !== "transparent"; - - return { - ...element, - background: { ...bg, enabled }, - }; -} - -function isV9Project({ project }: { project: ProjectRecord }): boolean { - return typeof project.version === "number" && project.version >= 9; -} +import type { MigrationResult, ProjectRecord } from "./types"; +import { getProjectId, isRecord } from "./utils"; + +export function transformProjectV8ToV9({ + project, +}: { + project: ProjectRecord; +}): MigrationResult { + const projectId = getProjectId({ project }); + if (!projectId) { + return { project, skipped: true, reason: "no project id" }; + } + + if (isV9Project({ project })) { + return { project, skipped: true, reason: "already v9" }; + } + + const migratedProject = migrateProjectTextElements({ project }); + + return { + project: { ...migratedProject, version: 9 }, + skipped: false, + }; +} + +function migrateProjectTextElements({ + project, +}: { + project: ProjectRecord; +}): ProjectRecord { + const scenesValue = project.scenes; + if (!Array.isArray(scenesValue)) return project; + + let hasChanges = false; + const migratedScenes = scenesValue.map((scene) => { + const migrated = migrateSceneTextElements({ scene }); + if (migrated !== scene) hasChanges = true; + return migrated; + }); + + if (!hasChanges) return project; + return { ...project, scenes: migratedScenes }; +} + +function migrateSceneTextElements({ scene }: { scene: unknown }): unknown { + if (!isRecord(scene)) return scene; + + const tracksValue = scene.tracks; + if (!Array.isArray(tracksValue)) return scene; + + let hasChanges = false; + const migratedTracks = tracksValue.map((track) => { + const migrated = migrateTrackTextElements({ track }); + if (migrated !== track) hasChanges = true; + return migrated; + }); + + if (!hasChanges) return scene; + return { ...scene, tracks: migratedTracks }; +} + +function migrateTrackTextElements({ track }: { track: unknown }): unknown { + if (!isRecord(track)) return track; + if (track.type !== "text") return track; + + const elementsValue = track.elements; + if (!Array.isArray(elementsValue)) return track; + + let hasChanges = false; + const migratedElements = elementsValue.map((element) => { + const migrated = migrateTextElement({ element }); + if (migrated !== element) hasChanges = true; + return migrated; + }); + + if (!hasChanges) return track; + return { ...track, elements: migratedElements }; +} + +function migrateTextElement({ element }: { element: unknown }): unknown { + if (!isRecord(element)) return element; + if (element.type !== "text") return element; + + const bg = element.background; + if (!isRecord(bg)) return element; + if (typeof bg.enabled === "boolean") return element; + + const color = typeof bg.color === "string" ? bg.color : "transparent"; + const enabled = color !== "transparent"; + + return { + ...element, + background: { ...bg, enabled }, + }; +} + +function isV9Project({ project }: { project: ProjectRecord }): boolean { + return typeof project.version === "number" && project.version >= 9; +} diff --git a/apps/web/src/services/storage/migrations/transformers/v9-to-v10.ts b/apps/web/src/services/storage/migrations/transformers/v9-to-v10.ts index 52d13295..18885b62 100644 --- a/apps/web/src/services/storage/migrations/transformers/v9-to-v10.ts +++ b/apps/web/src/services/storage/migrations/transformers/v9-to-v10.ts @@ -1,21 +1,21 @@ -import type { MigrationResult, ProjectRecord } from "./types"; -import { getProjectId } from "./utils"; - -export function transformProjectV9ToV10({ - project, -}: { - project: ProjectRecord; -}): MigrationResult { - if (!getProjectId({ project })) { - return { project, skipped: true, reason: "no project id" }; - } - - if (typeof project.version === "number" && project.version >= 10) { - return { project, skipped: true, reason: "already v10" }; - } - - return { - project: { ...project, version: 10 }, - skipped: false, - }; -} +import type { MigrationResult, ProjectRecord } from "./types"; +import { getProjectId } from "./utils"; + +export function transformProjectV9ToV10({ + project, +}: { + project: ProjectRecord; +}): MigrationResult { + if (!getProjectId({ project })) { + return { project, skipped: true, reason: "no project id" }; + } + + if (typeof project.version === "number" && project.version >= 10) { + return { project, skipped: true, reason: "already v10" }; + } + + return { + project: { ...project, version: 10 }, + skipped: false, + }; +} -- cgit v1.3.1