265 lines
6.8 KiB
TypeScript
265 lines
6.8 KiB
TypeScript
/**
|
|
* Project Store
|
|
* Manages project state and operations
|
|
*/
|
|
|
|
import { create } from 'zustand'
|
|
import { ProjectInfo, VideoTrack, AudioTrack } from '../services/tauri'
|
|
import { ProjectService } from '../services/tauri'
|
|
|
|
interface ProjectState {
|
|
// Current project
|
|
currentProject: ProjectInfo | null
|
|
|
|
// Project list
|
|
projects: ProjectInfo[]
|
|
|
|
// Loading states
|
|
isLoading: boolean
|
|
isSaving: boolean
|
|
|
|
// Error state
|
|
error: string | null
|
|
|
|
// Actions
|
|
createProject: (name: string, description?: string) => Promise<void>
|
|
loadProject: (projectPath: string) => Promise<void>
|
|
saveProject: () => Promise<void>
|
|
closeProject: () => void
|
|
|
|
// Track management
|
|
addVideoTrack: (track: VideoTrack) => void
|
|
addAudioTrack: (track: AudioTrack) => void
|
|
removeVideoTrack: (trackId: string) => void
|
|
removeAudioTrack: (trackId: string) => void
|
|
updateVideoTrack: (trackId: string, updates: Partial<VideoTrack>) => void
|
|
updateAudioTrack: (trackId: string, updates: Partial<AudioTrack>) => void
|
|
|
|
// Timeline management
|
|
setTimelinePosition: (position: number) => void
|
|
setTimelineZoom: (zoom: number) => void
|
|
setTimelineDuration: (duration: number) => void
|
|
|
|
// Utility actions
|
|
setError: (error: string | null) => void
|
|
clearError: () => void
|
|
}
|
|
|
|
export const useProjectStore = create<ProjectState>((set, get) => ({
|
|
// Initial state
|
|
currentProject: null,
|
|
projects: [],
|
|
isLoading: false,
|
|
isSaving: false,
|
|
error: null,
|
|
|
|
// Project operations
|
|
createProject: async (name: string, description = '') => {
|
|
set({ isLoading: true, error: null })
|
|
|
|
try {
|
|
const project = await ProjectService.createProject(name, description)
|
|
set({
|
|
currentProject: project,
|
|
isLoading: false
|
|
})
|
|
} catch (error) {
|
|
set({
|
|
error: error instanceof Error ? error.message : 'Failed to create project',
|
|
isLoading: false
|
|
})
|
|
}
|
|
},
|
|
|
|
loadProject: async (projectPath: string) => {
|
|
set({ isLoading: true, error: null })
|
|
|
|
try {
|
|
const project = await ProjectService.loadProject(projectPath)
|
|
set({
|
|
currentProject: project,
|
|
isLoading: false
|
|
})
|
|
} catch (error) {
|
|
set({
|
|
error: error instanceof Error ? error.message : 'Failed to load project',
|
|
isLoading: false
|
|
})
|
|
}
|
|
},
|
|
|
|
saveProject: async () => {
|
|
const { currentProject } = get()
|
|
if (!currentProject) return
|
|
|
|
set({ isSaving: true, error: null })
|
|
|
|
try {
|
|
await ProjectService.saveProject(currentProject)
|
|
set({
|
|
currentProject: {
|
|
...currentProject,
|
|
modified_at: new Date().toISOString()
|
|
},
|
|
isSaving: false
|
|
})
|
|
} catch (error) {
|
|
set({
|
|
error: error instanceof Error ? error.message : 'Failed to save project',
|
|
isSaving: false
|
|
})
|
|
}
|
|
},
|
|
|
|
closeProject: () => {
|
|
set({ currentProject: null })
|
|
},
|
|
|
|
// Track management
|
|
addVideoTrack: (track: VideoTrack) => {
|
|
const { currentProject } = get()
|
|
if (!currentProject) return
|
|
|
|
const updatedProject = {
|
|
...currentProject,
|
|
video_tracks: [...currentProject.video_tracks, track],
|
|
modified_at: new Date().toISOString()
|
|
}
|
|
|
|
set({ currentProject: updatedProject })
|
|
},
|
|
|
|
addAudioTrack: (track: AudioTrack) => {
|
|
const { currentProject } = get()
|
|
if (!currentProject) return
|
|
|
|
const updatedProject = {
|
|
...currentProject,
|
|
audio_tracks: [...currentProject.audio_tracks, track],
|
|
modified_at: new Date().toISOString()
|
|
}
|
|
|
|
set({ currentProject: updatedProject })
|
|
},
|
|
|
|
removeVideoTrack: (trackId: string) => {
|
|
const { currentProject } = get()
|
|
if (!currentProject) return
|
|
|
|
const updatedProject = {
|
|
...currentProject,
|
|
video_tracks: currentProject.video_tracks.filter(track => track.id !== trackId),
|
|
modified_at: new Date().toISOString()
|
|
}
|
|
|
|
set({ currentProject: updatedProject })
|
|
},
|
|
|
|
removeAudioTrack: (trackId: string) => {
|
|
const { currentProject } = get()
|
|
if (!currentProject) return
|
|
|
|
const updatedProject = {
|
|
...currentProject,
|
|
audio_tracks: currentProject.audio_tracks.filter(track => track.id !== trackId),
|
|
modified_at: new Date().toISOString()
|
|
}
|
|
|
|
set({ currentProject: updatedProject })
|
|
},
|
|
|
|
updateVideoTrack: (trackId: string, updates: Partial<VideoTrack>) => {
|
|
const { currentProject } = get()
|
|
if (!currentProject) return
|
|
|
|
const updatedProject = {
|
|
...currentProject,
|
|
video_tracks: currentProject.video_tracks.map(track =>
|
|
track.id === trackId ? { ...track, ...updates } : track
|
|
),
|
|
modified_at: new Date().toISOString()
|
|
}
|
|
|
|
set({ currentProject: updatedProject })
|
|
},
|
|
|
|
updateAudioTrack: (trackId: string, updates: Partial<AudioTrack>) => {
|
|
const { currentProject } = get()
|
|
if (!currentProject) return
|
|
|
|
const updatedProject = {
|
|
...currentProject,
|
|
audio_tracks: currentProject.audio_tracks.map(track =>
|
|
track.id === trackId ? { ...track, ...updates } : track
|
|
),
|
|
modified_at: new Date().toISOString()
|
|
}
|
|
|
|
set({ currentProject: updatedProject })
|
|
},
|
|
|
|
// Timeline management
|
|
setTimelinePosition: (position: number) => {
|
|
const { currentProject } = get()
|
|
if (!currentProject) return
|
|
|
|
const updatedProject = {
|
|
...currentProject,
|
|
timeline: {
|
|
...currentProject.timeline,
|
|
position
|
|
}
|
|
}
|
|
|
|
set({ currentProject: updatedProject })
|
|
},
|
|
|
|
setTimelineZoom: (zoom: number) => {
|
|
const { currentProject } = get()
|
|
if (!currentProject) return
|
|
|
|
const updatedProject = {
|
|
...currentProject,
|
|
timeline: {
|
|
...currentProject.timeline,
|
|
zoom
|
|
}
|
|
}
|
|
|
|
set({ currentProject: updatedProject })
|
|
},
|
|
|
|
setTimelineDuration: (duration: number) => {
|
|
const { currentProject } = get()
|
|
if (!currentProject) return
|
|
|
|
const updatedProject = {
|
|
...currentProject,
|
|
timeline: {
|
|
...currentProject.timeline,
|
|
duration
|
|
}
|
|
}
|
|
|
|
set({ currentProject: updatedProject })
|
|
},
|
|
|
|
// Utility actions
|
|
setError: (error: string | null) => {
|
|
set({ error })
|
|
},
|
|
|
|
clearError: () => {
|
|
set({ error: null })
|
|
}
|
|
}))
|
|
|
|
// Selectors for easier access to specific state
|
|
export const useCurrentProject = () => useProjectStore(state => state.currentProject)
|
|
export const useProjectLoading = () => useProjectStore(state => state.isLoading)
|
|
export const useProjectSaving = () => useProjectStore(state => state.isSaving)
|
|
export const useProjectError = () => useProjectStore(state => state.error)
|
|
export const useVideoTracks = () => useProjectStore(state => state.currentProject?.video_tracks || [])
|
|
export const useAudioTracks = () => useProjectStore(state => state.currentProject?.audio_tracks || [])
|
|
export const useTimeline = () => useProjectStore(state => state.currentProject?.timeline || { duration: 0, zoom: 1, position: 0 })
|