From eb4cb16bbded26db857be97372e6a7768f708962 Mon Sep 17 00:00:00 2001 From: Subhamoy Biswas Date: Tue, 26 May 2026 11:20:05 +0530 Subject: [PATCH] feat: added quit-on-close option #18 #33 --- src-tauri/capabilities/default.json | 2 ++ src/App.tsx | 32 +++++++++++++++-- .../pages/settings/applicationSettings.tsx | 10 ++++++ src/components/titlebar.tsx | 36 ++++++++++++++++++- src/services/store.ts | 2 ++ src/types/settings.ts | 1 + 6 files changed, 79 insertions(+), 4 deletions(-) diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 1561820..b8e1df8 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -13,6 +13,8 @@ "core:window:allow-minimize", "core:window:allow-maximize", "core:window:allow-unmaximize", + "core:window:allow-close", + "core:window:allow-destroy", "core:window:allow-start-dragging", "opener:default", "shell:default", diff --git a/src/App.tsx b/src/App.tsx index 1fb15a9..33c82bd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,7 +4,7 @@ import { AppContext } from "@/providers/appContextProvider"; import { useEffect, useRef, useState } from "react"; import { arch, exeExtension } from "@tauri-apps/plugin-os"; import { downloadDir, join, resourceDir, tempDir, dataDir } from "@tauri-apps/api/path"; -import { useBasePathsStore, useCurrentVideoMetadataStore, useDownloaderPageStatesStore, useDownloadStatesStore, useEnvironmentStore, useKvPairsStatesStore, useSettingsPageStatesStore } from "@/services/store"; +import { useBasePathsStore, useCurrentVideoMetadataStore, useDownloadActionStatesStore, useDownloaderPageStatesStore, useDownloadStatesStore, useEnvironmentStore, useKvPairsStatesStore, useSettingsPageStatesStore } from "@/services/store"; import { isObjEmpty} from "@/utils"; import { Command } from "@tauri-apps/plugin-shell"; import { useUpdateDownloadStatus } from "@/services/mutations"; @@ -60,6 +60,7 @@ export default function App({ children }: { children: React.ReactNode }) { theme: APP_THEME, color_scheme: APP_COLOR_SCHEME, use_potoken: USE_POTOKEN, + quit_on_close: QUIT_ON_CLOSE } = useSettingsPageStatesStore(state => state.settings); const erroredDownloadIds = useDownloaderPageStatesStore((state) => state.erroredDownloadIds); @@ -96,6 +97,24 @@ export default function App({ children }: { children: React.ReactNode }) { const { fetchVideoMetadata, startDownload, pauseDownload, resumeDownload, cancelDownload, processQueuedDownloads } = useDownloader(); + const ongoingDownloadsCloseable = globalDownloadStates.filter(state => ['starting', 'downloading', 'queued'].includes(state.download_status)); + const setIsPausingDownload = useDownloadActionStatesStore(state => state.setIsPausingDownload); + + const stopOngoingDownloads = async () => { + if (ongoingDownloadsCloseable.length > 0) { + for (const state of ongoingDownloadsCloseable) { + setIsPausingDownload(state.download_id, true); + try { + await pauseDownload(state); + } catch (e) { + console.error(e); + } finally { + setIsPausingDownload(state.download_id, false); + } + } + } + } + // Prevent right click context menu in production if (!import.meta.env.DEV) { document.oncontextmenu = (event) => { @@ -105,9 +124,16 @@ export default function App({ children }: { children: React.ReactNode }) { // Prevent app from closing useEffect(() => { - const handleCloseRequested = (event: any) => { + const handleCloseRequested = async (event: any) => { event.preventDefault(); - appWindow.hide(); + if (QUIT_ON_CLOSE) { + if (ongoingDownloads.length > 0) { + await stopOngoingDownloads() + } + await appWindow.destroy(); + } else { + await appWindow.hide(); + } }; appWindow.onCloseRequested(handleCloseRequested); diff --git a/src/components/pages/settings/applicationSettings.tsx b/src/components/pages/settings/applicationSettings.tsx index 5639a09..ba77367 100644 --- a/src/components/pages/settings/applicationSettings.tsx +++ b/src/components/pages/settings/applicationSettings.tsx @@ -130,6 +130,7 @@ function AppGeneralSettings() { const maxRetries = useSettingsPageStatesStore(state => state.settings.max_retries); const preferVideoOverPlaylist = useSettingsPageStatesStore(state => state.settings.prefer_video_over_playlist); const strictDownloadabilityCheck = useSettingsPageStatesStore(state => state.settings.strict_downloadablity_check); + const quitOnClose = useSettingsPageStatesStore(state => state.settings.quit_on_close); const useAria2 = useSettingsPageStatesStore(state => state.settings.use_aria2); const useCustomCommands = useSettingsPageStatesStore(state => state.settings.use_custom_commands); @@ -179,6 +180,15 @@ function AppGeneralSettings() { /> +
+

Quit on Close

+

Wheather to quit the app when the main window is closed, if disabled the app will keep running in the background and you can open the main window again from the system tray

+ saveSettingsKey('quit_on_close', checked)} + /> +

Aria2

Use aria2c as external downloader (recommended only if you are experiancing too slow download speeds with native downloader, you need to install aria2 via homebrew if you are on macos to use this feature)

diff --git a/src/components/titlebar.tsx b/src/components/titlebar.tsx index e0e626f..993fd37 100644 --- a/src/components/titlebar.tsx +++ b/src/components/titlebar.tsx @@ -4,10 +4,35 @@ import { MaximizeIcon } from "@/components/icons/maximize"; import { MinimizeIcon } from "@/components/icons/minimize"; import { CloseIcon } from "@/components/icons/close"; import { UnmaximizeIcon } from "@/components/icons/unmaximize"; +import { useDownloadActionStatesStore, useDownloadStatesStore, useSettingsPageStatesStore } from "@/services/store"; +import { useAppContext } from "@/providers/appContextProvider"; export default function TitleBar() { const [maximized, setMaximized] = useState(false); const appWindow = getCurrentWebviewWindow(); + const quitOnClose = useSettingsPageStatesStore(state => state.settings.quit_on_close); + + const downloadStates = useDownloadStatesStore(state => state.downloadStates); + const ongoingDownloads = downloadStates.filter(state => + ['starting', 'downloading', 'queued'].includes(state.download_status) + ); + const setIsPausingDownload = useDownloadActionStatesStore(state => state.setIsPausingDownload); + const { pauseDownload } = useAppContext(); + + const stopOngoingDownloads = async () => { + if (ongoingDownloads.length > 0) { + for (const state of ongoingDownloads) { + setIsPausingDownload(state.download_id, true); + try { + await pauseDownload(state); + } catch (e) { + console.error(e); + } finally { + setIsPausingDownload(state.download_id, false); + } + } + } + } return (
@@ -48,7 +73,16 @@ export default function TitleBar() { className="px-4 py-3 hover:bg-destructive" id="titlebar-close" title="Close" - onClick={() => appWindow.hide()} + onClick={async () => { + if (quitOnClose) { + if (ongoingDownloads.length > 0) { + await stopOngoingDownloads(); + } + await appWindow.destroy(); + } else { + await appWindow.hide() + } + }} > diff --git a/src/services/store.ts b/src/services/store.ts index 74170ca..5f0f89a 100644 --- a/src/services/store.ts +++ b/src/services/store.ts @@ -223,6 +223,7 @@ export const useSettingsPageStatesStore = create((set) pot_server_port: 4416, windows_filenames: true, restrict_filenames: true, + quit_on_close: false, // extension settings websocket_port: 53511 }, @@ -310,6 +311,7 @@ export const useSettingsPageStatesStore = create((set) pot_server_port: 4416, windows_filenames: true, restrict_filenames: true, + quit_on_close: false, // extension settings websocket_port: 53511 }, diff --git a/src/types/settings.ts b/src/types/settings.ts index 900fc2c..f050f7b 100644 --- a/src/types/settings.ts +++ b/src/types/settings.ts @@ -66,6 +66,7 @@ export interface Settings { pot_server_port: number; windows_filenames: boolean; restrict_filenames: boolean; + quit_on_close: boolean; // extension settings websocket_port: number; }