refactor: move to a typed-tracks system and add support for text
This commit is contained in:
@ -1,16 +1,25 @@
|
||||
import { create } from "zustand";
|
||||
import { storageService } from "@/lib/storage/storage-service";
|
||||
|
||||
export type MediaType = "image" | "video" | "audio";
|
||||
|
||||
export interface MediaItem {
|
||||
id: string;
|
||||
name: string;
|
||||
type: "image" | "video" | "audio";
|
||||
type: MediaType;
|
||||
file: File;
|
||||
url: string; // Object URL for preview
|
||||
url?: string; // Object URL for preview
|
||||
thumbnailUrl?: string; // For video thumbnails
|
||||
duration?: number; // For video/audio duration
|
||||
width?: number; // For video/image width
|
||||
height?: number; // For video/image height
|
||||
// Text-specific properties
|
||||
content?: string; // Text content
|
||||
fontSize?: number; // Font size
|
||||
fontFamily?: string; // Font family
|
||||
color?: string; // Text color
|
||||
backgroundColor?: string; // Background color
|
||||
textAlign?: "left" | "center" | "right"; // Text alignment
|
||||
}
|
||||
|
||||
interface MediaStore {
|
||||
@ -25,7 +34,7 @@ interface MediaStore {
|
||||
}
|
||||
|
||||
// Helper function to determine file type
|
||||
export const getFileType = (file: File): "image" | "video" | "audio" | null => {
|
||||
export const getFileType = (file: File): MediaType | null => {
|
||||
const { type } = file;
|
||||
|
||||
if (type.startsWith("image/")) {
|
||||
@ -46,7 +55,7 @@ export const getImageDimensions = (
|
||||
file: File
|
||||
): Promise<{ width: number; height: number }> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
const img = new window.Image();
|
||||
|
||||
img.addEventListener("load", () => {
|
||||
const width = img.naturalWidth;
|
||||
@ -69,8 +78,8 @@ export const generateVideoThumbnail = (
|
||||
file: File
|
||||
): Promise<{ thumbnailUrl: string; width: number; height: number }> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const video = document.createElement("video");
|
||||
const canvas = document.createElement("canvas");
|
||||
const video = document.createElement("video") as HTMLVideoElement;
|
||||
const canvas = document.createElement("canvas") as HTMLCanvasElement;
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
if (!ctx) {
|
||||
@ -115,7 +124,7 @@ export const getMediaDuration = (file: File): Promise<number> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const element = document.createElement(
|
||||
file.type.startsWith("video/") ? "video" : "audio"
|
||||
) as HTMLVideoElement | HTMLAudioElement;
|
||||
) as HTMLVideoElement;
|
||||
|
||||
element.addEventListener("loadedmetadata", () => {
|
||||
resolve(element.duration);
|
||||
@ -162,17 +171,17 @@ export const useMediaStore = create<MediaStore>((set, get) => ({
|
||||
console.error("Failed to save media item:", error);
|
||||
// Remove from local state if save failed
|
||||
set((state) => ({
|
||||
mediaItems: state.mediaItems.filter((item) => item.id !== newItem.id),
|
||||
mediaItems: state.mediaItems.filter((media) => media.id !== newItem.id),
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
removeMediaItem: async (id) => {
|
||||
removeMediaItem: async (id: string) => {
|
||||
const state = get();
|
||||
const item = state.mediaItems.find((item) => item.id === id);
|
||||
const item = state.mediaItems.find((media) => media.id === id);
|
||||
|
||||
// Cleanup object URLs to prevent memory leaks
|
||||
if (item) {
|
||||
if (item && item.url) {
|
||||
URL.revokeObjectURL(item.url);
|
||||
if (item.thumbnailUrl) {
|
||||
URL.revokeObjectURL(item.thumbnailUrl);
|
||||
@ -181,7 +190,7 @@ export const useMediaStore = create<MediaStore>((set, get) => ({
|
||||
|
||||
// Remove from local state immediately
|
||||
set((state) => ({
|
||||
mediaItems: state.mediaItems.filter((item) => item.id !== id),
|
||||
mediaItems: state.mediaItems.filter((media) => media.id !== id),
|
||||
}));
|
||||
|
||||
// Remove from persistent storage
|
||||
@ -189,7 +198,6 @@ export const useMediaStore = create<MediaStore>((set, get) => ({
|
||||
await storageService.deleteMediaItem(id);
|
||||
} catch (error) {
|
||||
console.error("Failed to delete media item:", error);
|
||||
// Could re-add to local state here if needed
|
||||
}
|
||||
},
|
||||
|
||||
@ -211,7 +219,9 @@ export const useMediaStore = create<MediaStore>((set, get) => ({
|
||||
|
||||
// Cleanup all object URLs
|
||||
state.mediaItems.forEach((item) => {
|
||||
URL.revokeObjectURL(item.url);
|
||||
if (item.url) {
|
||||
URL.revokeObjectURL(item.url);
|
||||
}
|
||||
if (item.thumbnailUrl) {
|
||||
URL.revokeObjectURL(item.thumbnailUrl);
|
||||
}
|
||||
|
Reference in New Issue
Block a user