=> {
- if (thumbnailCache[projectId] !== undefined) {
- return thumbnailCache[projectId];
- }
-
- setLoadingThumbnails((prev) => new Set(prev).add(projectId));
-
- try {
- const thumbnail = await getProjectThumbnail(projectId);
- setThumbnailCache((prev) => ({ ...prev, [projectId]: thumbnail }));
- return thumbnail;
- } finally {
- setLoadingThumbnails((prev) => {
- const newSet = new Set(prev);
- newSet.delete(projectId);
- return newSet;
- });
- }
- },
- [],
- );
+ const editor = EditorCore.getInstance();
const handleCreateProject = async () => {
- const projectId = await createNewProject("New Project");
- console.log("projectId", projectId);
- router.push(`/editor/${projectId}`);
+ try {
+ const projectId = await editor.project.createNewProject({
+ name: "New project",
+ });
+ router.push(`/editor/${projectId}`);
+ } catch (error) {
+ toast.error("Failed to create project", {
+ description:
+ error instanceof Error ? error.message : "Please try again",
+ });
+ }
};
const handleSelectProject = (projectId: string, checked: boolean) => {
@@ -118,15 +90,28 @@ export default function ProjectsPage() {
};
const handleBulkDelete = async () => {
- await Promise.all(
- Array.from(selectedProjects).map((projectId) => deleteProject(projectId)),
- );
- setSelectedProjects(new Set());
- setIsSelectionMode(false);
- setIsBulkDeleteDialogOpen(false);
+ try {
+ await Promise.all(
+ Array.from(selectedProjects).map((projectId) =>
+ editor.project.deleteProject({ id: projectId }),
+ ),
+ );
+ } catch (error) {
+ toast.error("Failed to delete projects", {
+ description:
+ error instanceof Error ? error.message : "Please try again",
+ });
+ } finally {
+ setSelectedProjects(new Set());
+ setIsSelectionMode(false);
+ setIsBulkDeleteDialogOpen(false);
+ }
};
- const sortedProjects = getFilteredAndSortedProjects(searchQuery, sortOption);
+ const sortedProjects = editor.project.getFilteredAndSortedProjects({
+ searchQuery,
+ sortOption,
+ });
const allSelected =
sortedProjects.length > 0 &&
@@ -178,8 +163,8 @@ export default function ProjectsPage() {
Your Projects
- {savedProjects.length}{" "}
- {savedProjects.length === 1 ? "project" : "projects"}
+ {sortedProjects.length}{" "}
+ {sortedProjects.length === 1 ? "project" : "projects"}
{isSelectionMode && selectedProjects.size > 0 && (
• {selectedProjects.size} selected
@@ -209,9 +194,9 @@ export default function ProjectsPage() {
@@ -317,7 +302,7 @@ export default function ProjectsPage() {
)}
- {isLoading || !isInitialized ? (
+ {editor.project.isLoading || !editor.project.isInitialized ? (
{Array.from({ length: 8 }, (_, index) => (
))}
- ) : savedProjects.length === 0 ? (
+ ) : sortedProjects.length === 0 ? (
) : sortedProjects.length === 0 ? (
))}
@@ -372,7 +356,6 @@ interface ProjectCardProps {
isSelectionMode?: boolean;
isSelected?: boolean;
onSelect?: (projectId: string, checked: boolean) => void;
- getProjectThumbnail: (projectId: string) => Promise;
}
function ProjectCard({
@@ -380,28 +363,12 @@ function ProjectCard({
isSelectionMode = false,
isSelected = false,
onSelect,
- getProjectThumbnail,
}: ProjectCardProps) {
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
const [isRenameDialogOpen, setIsRenameDialogOpen] = useState(false);
- const [dynamicThumbnail, setDynamicThumbnail] = useState(null);
- const [isLoadingThumbnail, setIsLoadingThumbnail] = useState(true);
const { deleteProject, renameProject, duplicateProject } = useProjectStore();
- useEffect(() => {
- const loadThumbnail = async () => {
- setIsLoadingThumbnail(true);
- try {
- const thumbnail = await getProjectThumbnail(project.id);
- setDynamicThumbnail(thumbnail);
- } finally {
- setIsLoadingThumbnail(false);
- }
- };
- loadThumbnail();
- }, [project.id, getProjectThumbnail]);
-
const formatDate = (date: Date): string => {
return date.toLocaleDateString("en-US", {
month: "short",
@@ -466,13 +433,9 @@ function ProjectCard({
)}
- {isLoadingThumbnail ? (
-
-
-
- ) : dynamicThumbnail ? (
+ {project.thumbnail ? (
{
- console.log("handleNameSave", newName);
+ const handleExit = async () => {
+ if (isExiting) return;
+ setIsExiting(true);
+
+ try {
+ await editor.project.prepareExit();
+ editor.project.closeProject();
+ } catch (error) {
+ console.error("Failed to prepare project exit:", error);
+ } finally {
+ editor.project.closeProject();
+ router.push("/projects");
+ }
+ };
+
+ const handleSaveProjectName = async (newName: string) => {
if (activeProject && newName.trim() && newName !== activeProject.name) {
try {
- await renameProject(activeProject.id, newName.trim());
- setIsRenameDialogOpen(false);
+ await editor.project.renameProject({
+ id: activeProject.id,
+ name: newName.trim(),
+ });
} catch (error) {
- console.error("Failed to rename project:", error);
+ toast.error("Failed to rename project", {
+ description:
+ error instanceof Error ? error.message : "Please try again",
+ });
+ } finally {
+ setIsRenameDialogOpen(false);
}
}
};
- const handleDelete = () => {
+ const handleDeleteProject = async () => {
if (activeProject) {
- deleteProject(activeProject.id);
- setIsDeleteDialogOpen(false);
- router.push("/projects");
+ try {
+ await editor.project.deleteProject({ id: activeProject.id });
+ router.push("/projects");
+ } catch (error) {
+ toast.error("Failed to delete project", {
+ description:
+ error instanceof Error ? error.message : "Please try again",
+ });
+ } finally {
+ setIsDeleteDialogOpen(false);
+ }
}
};
@@ -77,12 +115,18 @@ function ProjectDropdown() {
-
-
+
+ {isExiting ? (
+
+ ) : (
- Projects
-
-
+ )}
+ Projects
+
setIsRenameDialogOpen(true)}
@@ -115,13 +159,13 @@ function ProjectDropdown() {
handleSaveProjectName(newName)}
projectName={activeProject?.name || ""}
/>
>
diff --git a/apps/web/src/components/editor/export-button.tsx b/apps/web/src/components/editor/export-button.tsx
index 21a9fd39..57a49fb0 100644
--- a/apps/web/src/components/editor/export-button.tsx
+++ b/apps/web/src/components/editor/export-button.tsx
@@ -1,7 +1,7 @@
"use client";
import { useState } from "react";
-import { TransitionUpIcon } from "../icons";
+import { TransitionUpIcon } from "@opencut/ui/icons";
import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
import { Button } from "../ui/button";
import { Label } from "../ui/label";
@@ -15,20 +15,20 @@ import {
getExportFileExtension,
DEFAULT_EXPORT_OPTIONS,
} from "@/lib/export-utils";
-import { useProjectStore } from "@/stores/project-store";
import { Check, Copy, Download, RotateCcw, X } from "lucide-react";
import { ExportFormat, ExportQuality, ExportResult } from "@/types/export";
import { PropertyGroup } from "./properties-panel/property-item";
+import { useEditor } from "@/hooks/use-editor";
export function ExportButton() {
const [isExportPopoverOpen, setIsExportPopoverOpen] = useState(false);
- const { activeProject } = useProjectStore();
+ const editor = useEditor();
const handleExport = () => {
setIsExportPopoverOpen(true);
};
- const hasProject = !!activeProject;
+ const hasProject = !!editor.project.activeProject;
return (
@@ -36,10 +36,10 @@ export function ExportButton() {