|
8 | 8 | } from "@tanstack/solid-query"; |
9 | 9 | import { Channel } from "@tauri-apps/api/core"; |
10 | 10 | import { CheckMenuItem, Menu } from "@tauri-apps/api/menu"; |
11 | | -import { save as saveDialog } from "@tauri-apps/plugin-dialog"; |
| 11 | +import { ask, save as saveDialog } from "@tauri-apps/plugin-dialog"; |
| 12 | +import { remove } from "@tauri-apps/plugin-fs"; |
12 | 13 | import { cx } from "cva"; |
13 | 14 | import { |
14 | 15 | createEffect, |
@@ -194,6 +195,27 @@ export function ExportDialog() { |
194 | 195 | ); |
195 | 196 |
|
196 | 197 | const [outputPath, setOutputPath] = createSignal<string | null>(null); |
| 198 | + const [isCancelled, setIsCancelled] = createSignal(false); |
| 199 | + |
| 200 | + const handleCancel = async () => { |
| 201 | + if ( |
| 202 | + await ask("Are you sure you want to cancel the export?", { |
| 203 | + title: "Cancel Export", |
| 204 | + kind: "warning", |
| 205 | + }) |
| 206 | + ) { |
| 207 | + setIsCancelled(true); |
| 208 | + setExportState({ type: "idle" }); |
| 209 | + const path = outputPath(); |
| 210 | + if (path) { |
| 211 | + try { |
| 212 | + await remove(path); |
| 213 | + } catch (e) { |
| 214 | + console.error("Failed to delete cancelled file", e); |
| 215 | + } |
| 216 | + } |
| 217 | + } |
| 218 | + }; |
197 | 219 |
|
198 | 220 | const projectPath = editorInstance.path; |
199 | 221 |
|
@@ -222,13 +244,17 @@ export function ExportDialog() { |
222 | 244 |
|
223 | 245 | const copy = createMutation(() => ({ |
224 | 246 | mutationFn: async () => { |
| 247 | + setIsCancelled(false); |
225 | 248 | if (exportState.type !== "idle") return; |
226 | 249 | setExportState(reconcile({ action: "copy", type: "starting" })); |
227 | 250 |
|
228 | 251 | const outputPath = await exportWithSettings((progress) => { |
| 252 | + if (isCancelled()) return; |
229 | 253 | setExportState({ type: "rendering", progress }); |
230 | 254 | }); |
231 | 255 |
|
| 256 | + if (isCancelled()) throw new SilentError("Cancelled"); |
| 257 | + |
232 | 258 | setExportState({ type: "copying" }); |
233 | 259 |
|
234 | 260 | await commands.copyVideoToClipboard(outputPath); |
@@ -265,6 +291,7 @@ export function ExportDialog() { |
265 | 291 |
|
266 | 292 | const save = createMutation(() => ({ |
267 | 293 | mutationFn: async () => { |
| 294 | + setIsCancelled(false); |
268 | 295 | if (exportState.type !== "idle") return; |
269 | 296 |
|
270 | 297 | const extension = settings.format === "Gif" ? "gif" : "mp4"; |
@@ -293,9 +320,12 @@ export function ExportDialog() { |
293 | 320 | }); |
294 | 321 |
|
295 | 322 | const videoPath = await exportWithSettings((progress) => { |
| 323 | + if (isCancelled()) return; |
296 | 324 | setExportState({ type: "rendering", progress }); |
297 | 325 | }); |
298 | 326 |
|
| 327 | + if (isCancelled()) throw new SilentError("Cancelled"); |
| 328 | + |
299 | 329 | setExportState({ type: "copying" }); |
300 | 330 |
|
301 | 331 | await commands.copyFileToPath(videoPath, savePath); |
@@ -332,6 +362,7 @@ export function ExportDialog() { |
332 | 362 |
|
333 | 363 | const upload = createMutation(() => ({ |
334 | 364 | mutationFn: async () => { |
| 365 | + setIsCancelled(false); |
335 | 366 | if (exportState.type !== "idle") return; |
336 | 367 | setExportState(reconcile({ action: "upload", type: "starting" })); |
337 | 368 |
|
@@ -371,9 +402,12 @@ export function ExportDialog() { |
371 | 402 | ); |
372 | 403 | }); |
373 | 404 |
|
374 | | - await exportWithSettings((progress) => |
375 | | - setExportState({ type: "rendering", progress }), |
376 | | - ); |
| 405 | + await exportWithSettings((progress) => { |
| 406 | + if (isCancelled()) return; |
| 407 | + setExportState({ type: "rendering", progress }); |
| 408 | + }); |
| 409 | + |
| 410 | + if (isCancelled()) throw new SilentError("Cancelled"); |
377 | 411 |
|
378 | 412 | setExportState({ type: "uploading", progress: 0 }); |
379 | 413 |
|
@@ -853,10 +887,20 @@ export function ExportDialog() { |
853 | 887 | keyed |
854 | 888 | > |
855 | 889 | {(copyState) => ( |
856 | | - <RenderProgress |
857 | | - state={copyState} |
858 | | - format={settings.format} |
859 | | - /> |
| 890 | + <> |
| 891 | + <RenderProgress |
| 892 | + state={copyState} |
| 893 | + format={settings.format} |
| 894 | + /> |
| 895 | + <Button |
| 896 | + variant="ghost" |
| 897 | + size="sm" |
| 898 | + onClick={handleCancel} |
| 899 | + class="mt-4 hover:bg-red-9 hover:text-white" |
| 900 | + > |
| 901 | + Cancel |
| 902 | + </Button> |
| 903 | + </> |
860 | 904 | )} |
861 | 905 | </Show> |
862 | 906 | </div> |
@@ -895,10 +939,20 @@ export function ExportDialog() { |
895 | 939 | keyed |
896 | 940 | > |
897 | 941 | {(copyState) => ( |
898 | | - <RenderProgress |
899 | | - state={copyState} |
900 | | - format={settings.format} |
901 | | - /> |
| 942 | + <> |
| 943 | + <RenderProgress |
| 944 | + state={copyState} |
| 945 | + format={settings.format} |
| 946 | + /> |
| 947 | + <Button |
| 948 | + variant="ghost" |
| 949 | + size="sm" |
| 950 | + onClick={handleCancel} |
| 951 | + class="mt-4 hover:bg-red-9 hover:text-white" |
| 952 | + > |
| 953 | + Cancel |
| 954 | + </Button> |
| 955 | + </> |
902 | 956 | )} |
903 | 957 | </Show> |
904 | 958 | </> |
@@ -967,10 +1021,20 @@ export function ExportDialog() { |
967 | 1021 | keyed |
968 | 1022 | > |
969 | 1023 | {(renderState) => ( |
970 | | - <RenderProgress |
971 | | - state={renderState} |
972 | | - format={settings.format} |
973 | | - /> |
| 1024 | + <> |
| 1025 | + <RenderProgress |
| 1026 | + state={renderState} |
| 1027 | + format={settings.format} |
| 1028 | + /> |
| 1029 | + <Button |
| 1030 | + variant="ghost" |
| 1031 | + size="sm" |
| 1032 | + onClick={handleCancel} |
| 1033 | + class="mt-4 hover:bg-red-9 hover:text-white" |
| 1034 | + > |
| 1035 | + Cancel |
| 1036 | + </Button> |
| 1037 | + </> |
974 | 1038 | )} |
975 | 1039 | </Match> |
976 | 1040 | </Switch> |
|
0 commit comments