feat(export): allow re-saving exported video on dialog cancel

This commit is contained in:
Siddharth
2026-03-21 17:06:25 -07:00
parent ece93683b8
commit c322825969
2 changed files with 47 additions and 0 deletions
@@ -130,6 +130,8 @@ interface SettingsPanelProps {
onSaveProject?: () => void;
onLoadProject?: () => void;
onExport?: () => void;
unsavedExport?: { arrayBuffer: ArrayBuffer; fileName: string; format: string } | null;
onSaveUnsavedExport?: () => void;
selectedAnnotationId?: string | null;
annotationRegions?: AnnotationRegion[];
onAnnotationContentChange?: (id: string, content: string) => void;
@@ -198,6 +200,8 @@ export function SettingsPanel({
onSaveProject,
onLoadProject,
onExport,
unsavedExport,
onSaveUnsavedExport,
selectedAnnotationId,
annotationRegions = [],
onAnnotationContentChange,
@@ -1150,6 +1154,17 @@ export function SettingsPanel({
</Button>
</div>
{unsavedExport && (
<Button
type="button"
size="lg"
onClick={onSaveUnsavedExport}
className="w-full mb-2 py-5 text-sm font-semibold flex items-center justify-center gap-2 bg-indigo-500 text-white rounded-xl shadow-lg shadow-indigo-500/20 hover:bg-indigo-500/90 hover:scale-[1.02] active:scale-[0.98] transition-all duration-200"
>
<Download className="w-4 h-4" />
Choose Save Location
</Button>
)}
<Button
data-testid={getTestId("export-button")}
type="button"
@@ -105,6 +105,11 @@ export default function VideoEditor() {
const [gifSizePreset, setGifSizePreset] = useState<GifSizePreset>("medium");
const [exportedFilePath, setExportedFilePath] = useState<string | null>(null);
const [lastSavedSnapshot, setLastSavedSnapshot] = useState<string | null>(null);
const [unsavedExport, setUnsavedExport] = useState<{
arrayBuffer: ArrayBuffer;
fileName: string;
format: string;
} | null>(null);
const videoPlaybackRef = useRef<VideoPlaybackRef>(null);
const nextZoomIdRef = useRef(1);
@@ -966,6 +971,27 @@ export default function VideoEditor() {
[handleShowExportedFile],
);
const handleSaveUnsavedExport = useCallback(async () => {
if (!unsavedExport) return;
try {
const saveResult = await window.electronAPI.saveExportedVideo(
unsavedExport.arrayBuffer,
unsavedExport.fileName,
);
if (saveResult.canceled) {
toast.info("Export canceled");
} else if (saveResult.success && saveResult.path) {
setUnsavedExport(null);
handleExportSaved(unsavedExport.format === "gif" ? "GIF" : "Video", saveResult.path);
} else {
toast.error(saveResult.message || "Failed to save export");
}
} catch (error) {
console.error("Error saving unsaved export:", error);
toast.error("Failed to save exported video");
}
}, [unsavedExport, handleExportSaved]);
const handleExport = useCallback(
async (settings: ExportSettings) => {
if (!videoPath) {
@@ -1045,8 +1071,10 @@ export default function VideoEditor() {
const saveResult = await window.electronAPI.saveExportedVideo(arrayBuffer, fileName);
if (saveResult.canceled) {
setUnsavedExport({ arrayBuffer, fileName, format: "gif" });
toast.info("Export canceled");
} else if (saveResult.success && saveResult.path) {
setUnsavedExport(null);
handleExportSaved("GIF", saveResult.path);
} else {
setExportError(saveResult.message || "Failed to save GIF");
@@ -1173,8 +1201,10 @@ export default function VideoEditor() {
const saveResult = await window.electronAPI.saveExportedVideo(arrayBuffer, fileName);
if (saveResult.canceled) {
setUnsavedExport({ arrayBuffer, fileName, format: "mp4" });
toast.info("Export canceled");
} else if (saveResult.success && saveResult.path) {
setUnsavedExport(null);
handleExportSaved("Video", saveResult.path);
} else {
setExportError(saveResult.message || "Failed to save video");
@@ -1528,6 +1558,8 @@ export default function VideoEditor() {
}
onSpeedChange={handleSpeedChange}
onSpeedDelete={handleSpeedDelete}
unsavedExport={unsavedExport}
onSaveUnsavedExport={handleSaveUnsavedExport}
/>
</Panel>
</PanelGroup>