From cdfed0fe94d0c8817cfd09ad4b94484b6aaf8e6e Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Sat, 30 Aug 2025 03:57:59 +0200 Subject: [PATCH] Replace pkexec with terminal output (with `TERMINAL` environment var) ArchUpdater:use terminal thanks to `TERMINAL` environment variable README: Add explanation for said environment var --- Modules/ArchUpdaterPanel/ArchUpdaterPanel.qml | 227 ++++++++- Modules/Bar/Widgets/ArchUpdater.qml | 3 +- Services/ArchUpdaterService.qml | 465 ++++++++++++++---- 3 files changed, 573 insertions(+), 122 deletions(-) diff --git a/Modules/ArchUpdaterPanel/ArchUpdaterPanel.qml b/Modules/ArchUpdaterPanel/ArchUpdaterPanel.qml index a4759ca..dc715fb 100644 --- a/Modules/ArchUpdaterPanel/ArchUpdaterPanel.qml +++ b/Modules/ArchUpdaterPanel/ArchUpdaterPanel.qml @@ -15,8 +15,9 @@ NPanel { // When the panel opens onOpened: { - ArchUpdaterService.doPoll() - ArchUpdaterService.doAurPoll() + console.log("ArchUpdaterPanel: Panel opened, refreshing package lists...") + // Always refresh when panel opens to ensure we have the latest data + ArchUpdaterService.forceRefresh() } panelContent: Rectangle { @@ -47,6 +48,19 @@ NPanel { Layout.fillWidth: true } + // Reset button (only show if update failed) + NIconButton { + visible: ArchUpdaterService.updateFailed + icon: "refresh" + tooltipText: "Reset update state" + sizeRatio: 0.8 + colorBg: Color.mError + colorFg: Color.mOnError + onClicked: { + ArchUpdaterService.resetUpdateState() + } + } + NIconButton { icon: "close" tooltipText: "Close" @@ -59,8 +73,10 @@ NPanel { Layout.fillWidth: true } - // Update summary + // Update summary (only show when packages are available) NText { + visible: !ArchUpdaterService.updateInProgress && !ArchUpdaterService.updateFailed && !ArchUpdaterService.busy + && !ArchUpdaterService.aurBusy && ArchUpdaterService.totalUpdates > 0 text: ArchUpdaterService.totalUpdates + " package" + (ArchUpdaterService.totalUpdates !== 1 ? "s" : "") + " can be updated" font.pointSize: Style.fontSizeL * scaling font.weight: Style.fontWeightMedium @@ -68,16 +84,184 @@ NPanel { Layout.fillWidth: true } - // Package selection info + // Package selection info (only show when not updating and have packages) NText { + visible: !ArchUpdaterService.updateInProgress && !ArchUpdaterService.updateFailed && !ArchUpdaterService.busy + && !ArchUpdaterService.aurBusy && ArchUpdaterService.totalUpdates > 0 text: ArchUpdaterService.selectedPackagesCount + " of " + ArchUpdaterService.totalUpdates + " packages selected" font.pointSize: Style.fontSizeS * scaling color: Color.mOnSurfaceVariant Layout.fillWidth: true } - // Unified list + // Update in progress state + ColumnLayout { + Layout.fillWidth: true + Layout.fillHeight: true + visible: ArchUpdaterService.updateInProgress + spacing: Style.marginM * scaling + + Item { + Layout.fillHeight: true + } // Spacer + + NIcon { + text: "hourglass_empty" + font.pointSize: Style.fontSizeXXXL * scaling + color: Color.mPrimary + Layout.alignment: Qt.AlignHCenter + } + + NText { + text: "Update in progress" + font.pointSize: Style.fontSizeL * scaling + color: Color.mOnSurface + Layout.alignment: Qt.AlignHCenter + } + + NText { + text: "Please check your terminal window for update progress and prompts." + font.pointSize: Style.fontSizeNormal * scaling + color: Color.mOnSurfaceVariant + Layout.alignment: Qt.AlignHCenter + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.Wrap + Layout.maximumWidth: 280 * scaling + } + + Item { + Layout.fillHeight: true + } // Spacer + } + + // Update failed state + Item { + Layout.fillWidth: true + Layout.fillHeight: true + visible: ArchUpdaterService.updateFailed + + ColumnLayout { + anchors.centerIn: parent + spacing: Style.marginM * scaling + + NIcon { + text: "error_outline" + font.pointSize: Style.fontSizeXXXL * scaling + color: Color.mError + Layout.alignment: Qt.AlignHCenter + } + + NText { + text: "Update failed" + font.pointSize: Style.fontSizeL * scaling + color: Color.mOnSurface + Layout.alignment: Qt.AlignHCenter + } + + NText { + text: "Check your terminal for error details and try again." + font.pointSize: Style.fontSizeNormal * scaling + color: Color.mOnSurfaceVariant + Layout.alignment: Qt.AlignHCenter + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.Wrap + Layout.maximumWidth: 280 * scaling + } + + // Prominent refresh button + NIconButton { + icon: "refresh" + tooltipText: "Refresh and try again" + sizeRatio: 1.2 + colorBg: Color.mPrimary + colorFg: Color.mOnPrimary + onClicked: { + ArchUpdaterService.resetUpdateState() + } + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: Style.marginL * scaling + } + } + } + + // No updates available state + Item { + Layout.fillWidth: true + Layout.fillHeight: true + visible: !ArchUpdaterService.updateInProgress && !ArchUpdaterService.updateFailed && !ArchUpdaterService.busy + && !ArchUpdaterService.aurBusy && ArchUpdaterService.totalUpdates === 0 + + ColumnLayout { + anchors.centerIn: parent + spacing: Style.marginM * scaling + + NIcon { + text: "check_circle" + font.pointSize: Style.fontSizeXXXL * scaling + color: Color.mPrimary + Layout.alignment: Qt.AlignHCenter + } + + NText { + text: "System is up to date" + font.pointSize: Style.fontSizeL * scaling + color: Color.mOnSurface + Layout.alignment: Qt.AlignHCenter + } + + NText { + text: "All packages are current. Check back later for updates." + font.pointSize: Style.fontSizeNormal * scaling + color: Color.mOnSurfaceVariant + Layout.alignment: Qt.AlignHCenter + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.Wrap + Layout.maximumWidth: 280 * scaling + } + } + } + + // Checking for updates state + Item { + Layout.fillWidth: true + Layout.fillHeight: true + visible: (ArchUpdaterService.busy || ArchUpdaterService.aurBusy) && !ArchUpdaterService.updateInProgress + && !ArchUpdaterService.updateFailed + + ColumnLayout { + anchors.centerIn: parent + spacing: Style.marginM * scaling + + NIcon { + text: "refresh" + font.pointSize: Style.fontSizeXXXL * scaling + color: Color.mPrimary + Layout.alignment: Qt.AlignHCenter + } + + NText { + text: "Checking for updates" + font.pointSize: Style.fontSizeL * scaling + color: Color.mOnSurface + Layout.alignment: Qt.AlignHCenter + } + + NText { + text: "Scanning package databases for available updates..." + font.pointSize: Style.fontSizeNormal * scaling + color: Color.mOnSurfaceVariant + Layout.alignment: Qt.AlignHCenter + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.Wrap + Layout.maximumWidth: 280 * scaling + } + } + } + + // Package list (only show when not in any special state) NBox { + visible: !ArchUpdaterService.updateInProgress && !ArchUpdaterService.updateFailed && !ArchUpdaterService.busy + && !ArchUpdaterService.aurBusy && ArchUpdaterService.totalUpdates > 0 Layout.fillWidth: true Layout.fillHeight: true @@ -164,53 +348,52 @@ NPanel { } } - // Action buttons + // Action buttons (only show when not updating) RowLayout { + visible: !ArchUpdaterService.updateInProgress && !ArchUpdaterService.updateFailed Layout.fillWidth: true spacing: Style.marginL * scaling NIconButton { icon: "refresh" - tooltipText: "Check for updates" + tooltipText: "Refresh package lists" onClicked: { - ArchUpdaterService.doPoll() - ArchUpdaterService.doAurPoll() + ArchUpdaterService.forceRefresh() } colorBg: Color.mSurfaceVariant colorFg: Color.mOnSurface Layout.fillWidth: true + enabled: !ArchUpdaterService.busy && !ArchUpdaterService.aurBusy } NIconButton { - icon: ArchUpdaterService.updateInProgress ? "hourglass_empty" : "system_update_alt" - tooltipText: ArchUpdaterService.updateInProgress ? "Update in progress..." : "Update all packages" - enabled: !ArchUpdaterService.updateInProgress + icon: "system_update_alt" + tooltipText: "Update all packages" + enabled: ArchUpdaterService.totalUpdates > 0 onClicked: { ArchUpdaterService.runUpdate() root.close() } - colorBg: ArchUpdaterService.updateInProgress ? Color.mSurfaceVariant : Color.mPrimary - colorFg: ArchUpdaterService.updateInProgress ? Color.mOnSurfaceVariant : Color.mOnPrimary + colorBg: ArchUpdaterService.totalUpdates > 0 ? Color.mPrimary : Color.mSurfaceVariant + colorFg: ArchUpdaterService.totalUpdates > 0 ? Color.mOnPrimary : Color.mOnSurfaceVariant Layout.fillWidth: true } NIconButton { - icon: ArchUpdaterService.updateInProgress ? "hourglass_empty" : "check_box" - tooltipText: ArchUpdaterService.updateInProgress ? "Update in progress..." : "Update selected packages" - enabled: !ArchUpdaterService.updateInProgress && ArchUpdaterService.selectedPackagesCount > 0 + icon: "check_box" + tooltipText: "Update selected packages" + enabled: ArchUpdaterService.selectedPackagesCount > 0 onClicked: { if (ArchUpdaterService.selectedPackagesCount > 0) { ArchUpdaterService.runSelectiveUpdate() root.close() } } - colorBg: ArchUpdaterService.updateInProgress ? Color.mSurfaceVariant : (ArchUpdaterService.selectedPackagesCount - > 0 ? Color.mPrimary : Color.mSurfaceVariant) - colorFg: ArchUpdaterService.updateInProgress ? Color.mOnSurfaceVariant : (ArchUpdaterService.selectedPackagesCount - > 0 ? Color.mOnPrimary : Color.mOnSurfaceVariant) + colorBg: ArchUpdaterService.selectedPackagesCount > 0 ? Color.mPrimary : Color.mSurfaceVariant + colorFg: ArchUpdaterService.selectedPackagesCount > 0 ? Color.mOnPrimary : Color.mOnSurfaceVariant Layout.fillWidth: true } } } } -} +} \ No newline at end of file diff --git a/Modules/Bar/Widgets/ArchUpdater.qml b/Modules/Bar/Widgets/ArchUpdater.qml index 106b167..eba5b9d 100644 --- a/Modules/Bar/Widgets/ArchUpdater.qml +++ b/Modules/Bar/Widgets/ArchUpdater.qml @@ -64,6 +64,5 @@ NIconButton { PanelService.getPanel("archUpdaterPanel").toggle(screen, this) ArchUpdaterService.doPoll() - ArchUpdaterService.doAurPoll() } -} +} \ No newline at end of file diff --git a/Services/ArchUpdaterService.qml b/Services/ArchUpdaterService.qml index de3b3af..d6d1791 100644 --- a/Services/ArchUpdaterService.qml +++ b/Services/ArchUpdaterService.qml @@ -8,28 +8,164 @@ import qs.Commons Singleton { id: updateService - // Core properties - readonly property bool busy: checkupdatesProcess.running - readonly property bool aurBusy: checkAurUpdatesProcess.running - readonly property int updates: repoPackages.length - readonly property int aurUpdates: aurPackages.length - readonly property int totalUpdates: updates + aurUpdates + // ============================================================================ + // CORE PROPERTIES + // ============================================================================ + + // Package data property var repoPackages: [] property var aurPackages: [] property var selectedPackages: [] property int selectedPackagesCount: 0 + + // Update state property bool updateInProgress: false + property bool updateFailed: false + property string lastUpdateError: "" + + // Computed properties + readonly property bool busy: checkupdatesProcess.running + readonly property bool aurBusy: checkParuUpdatesProcess.running + readonly property int updates: repoPackages.length + readonly property int aurUpdates: aurPackages.length + readonly property int totalUpdates: updates + aurUpdates + + // ============================================================================ + // TIMERS + // ============================================================================ + + // Refresh timer for post-update polling + Timer { + id: refreshTimer + interval: 5000 // Increased delay to ensure updates complete + repeat: false + onTriggered: { + console.log("ArchUpdater: Refreshing package lists after update...") + // Just refresh package lists without syncing database + doPoll() + } + } + + // Timer to mark update as complete - with error handling + Timer { + id: updateCompleteTimer + interval: 30000 // Increased to 30 seconds to allow more time + repeat: false + onTriggered: { + console.log("ArchUpdater: Update timeout reached, checking for failures...") + checkForUpdateFailures() + } + } + + // Timer to check if update processes are still running + Timer { + id: updateMonitorTimer + interval: 2000 + repeat: true + running: updateInProgress + onTriggered: { + // Check if any update-related processes might still be running + checkUpdateStatus() + } + } + + // ============================================================================ + // MONITORING PROCESSES + // ============================================================================ + + // Process to monitor update completion + Process { + id: updateStatusProcess + command: ["pgrep", "-f", "(pacman|yay|paru).*(-S|-Syu)"] + onExited: function (exitCode) { + if (exitCode !== 0 && updateInProgress) { + // No update processes found, update likely completed + console.log("ArchUpdater: No update processes detected, marking update as complete") + updateInProgress = false + updateMonitorTimer.stop() + + // Don't stop the complete timer - let it handle failures + // If the update actually failed, the timer will trigger and set updateFailed = true + + // Refresh package lists after a short delay + Qt.callLater(() => { + doPoll() + }, 2000) + } + } + } + + // Process to check for errors in log file (only when update is in progress) + Process { + id: errorCheckProcess + command: ["sh", "-c", "if [ -f /tmp/archupdater_output.log ]; then grep -i 'error\\|failed\\|failed to build\\|ERROR_DETECTED' /tmp/archupdater_output.log | tail -1; fi"] + onExited: function (exitCode) { + if (exitCode === 0 && updateInProgress) { + // Error found in log + console.log("ArchUpdater: Error detected in log file") + updateInProgress = false + updateFailed = true + updateCompleteTimer.stop() + updateMonitorTimer.stop() + lastUpdateError = "Build or update error detected" + + // Refresh to check actual state + Qt.callLater(() => { + doPoll() + }, 1000) + } + } + } + + // Timer to check for errors more frequently when update is in progress + Timer { + id: errorCheckTimer + interval: 5000 // Check every 5 seconds + repeat: true + running: updateInProgress + onTriggered: { + if (updateInProgress && !errorCheckProcess.running) { + errorCheckProcess.running = true + } + } + } + + // ============================================================================ + // MONITORING FUNCTIONS + // ============================================================================ + function checkUpdateStatus() { + if (updateInProgress && !updateStatusProcess.running) { + updateStatusProcess.running = true + } + } + + function checkForUpdateFailures() { + console.log("ArchUpdater: Checking for update failures...") + updateInProgress = false + updateFailed = true + updateCompleteTimer.stop() + updateMonitorTimer.stop() + + // Refresh to check actual state after a delay + Qt.callLater(() => { + doPoll() + }, 2000) + } // Initial check Component.onCompleted: { + getAurHelper() doPoll() - doAurPoll() } + // ============================================================================ + // PACKAGE CHECKING PROCESSES + // ============================================================================ + // Process for checking repo updates Process { id: checkupdatesProcess - command: ["checkupdates"] + command: ["checkupdates", "--nosync"] onExited: function (exitCode) { if (exitCode !== 0 && exitCode !== 2) { Logger.warn("ArchUpdater", "checkupdates failed (code:", exitCode, ")") @@ -44,13 +180,13 @@ Singleton { } } - // Process for checking AUR updates + // Process for checking AUR updates with paru specifically Process { - id: checkAurUpdatesProcess - command: ["sh", "-c", "command -v yay >/dev/null 2>&1 && yay -Qua || command -v paru >/dev/null 2>&1 && paru -Qua || echo ''"] + id: checkParuUpdatesProcess + command: ["paru", "-Qua"] onExited: function (exitCode) { if (exitCode !== 0) { - Logger.warn("ArchUpdater", "AUR check failed (code:", exitCode, ")") + Logger.warn("ArchUpdater", "paru check failed (code:", exitCode, ")") aurPackages = [] } } @@ -62,8 +198,12 @@ Singleton { } } - // Parse checkupdates output - function parseCheckupdatesOutput(output) { + // ============================================================================ + // PARSING FUNCTIONS + // ============================================================================ + + // Generic package parsing function + function parsePackageOutput(output, source) { const lines = output.trim().split('\n').filter(line => line.trim()) const packages = [] @@ -75,65 +215,83 @@ Singleton { "oldVersion": m[2], "newVersion": m[3], "description": `${m[1]} ${m[2]} -> ${m[3]}`, - "source": "repo" + "source": source }) } } - repoPackages = packages + // Only update if we have new data or if this is a fresh check + if (packages.length > 0 || output.trim() === "") { + if (source === "repo") { + repoPackages = packages + } else { + aurPackages = packages + } + } + } + + // Parse checkupdates output + function parseCheckupdatesOutput(output) { + parsePackageOutput(output, "repo") } // Parse AUR updates output function parseAurUpdatesOutput(output) { - const lines = output.trim().split('\n').filter(line => line.trim()) - const packages = [] + parsePackageOutput(output, "aur") + } - for (const line of lines) { - const m = line.match(/^(\S+)\s+([^\s]+)\s+->\s+([^\s]+)$/) - if (m) { - packages.push({ - "name": m[1], - "oldVersion": m[2], - "newVersion": m[3], - "description": `${m[1]} ${m[2]} -> ${m[3]}`, - "source": "aur" - }) - } + function doPoll() { + // Start repo updates check + if (!busy) { + checkupdatesProcess.running = true } - aurPackages = packages + // Start AUR updates check + if (!aurBusy) { + checkParuUpdatesProcess.running = true + } } - // Check for updates - function doPoll() { - if (busy) - return - checkupdatesProcess.running = true - } + // ============================================================================ + // UPDATE FUNCTIONS + // ============================================================================ - // Check for AUR updates - function doAurPoll() { - if (aurBusy) - return - checkAurUpdatesProcess.running = true + // Helper function to generate update command with error detection + function generateUpdateCommand(baseCommand) { + return baseCommand + " 2>&1 | tee /tmp/archupdater_output.log; if [ $? -ne 0 ]; then echo 'ERROR_DETECTED'; fi; echo 'Update complete! Press Enter to close...'; read -p 'Press Enter to continue...'" } // Update all packages (repo + AUR) function runUpdate() { if (totalUpdates === 0) { doPoll() - doAurPoll() return } + // Reset any previous error states + updateFailed = false + lastUpdateError = "" updateInProgress = true - // Update repos first, then AUR - Quickshell.execDetached(["pkexec", "pacman", "-Syu", "--noconfirm"]) - Quickshell.execDetached( - ["sh", "-c", "command -v yay >/dev/null 2>&1 && yay -Sua --noconfirm || command -v paru >/dev/null 2>&1 && paru -Sua --noconfirm || true"]) + console.log("ArchUpdater: Starting full system update...") - // Refresh after updates with multiple attempts - refreshAfterUpdate() + const terminal = Quickshell.env("TERMINAL") || "xterm" + + // Check if we have an AUR helper for full system update + const aurHelper = getAurHelper() + if (aurHelper && (aurUpdates > 0 || updates > 0)) { + // Use AUR helper for full system update (handles both repo and AUR) + const command = generateUpdateCommand(aurHelper + " -Syu") + Quickshell.execDetached([terminal, "-e", "bash", "-c", command]) + } else if (updates > 0) { + // Fallback to pacman if no AUR helper or only repo updates + const command = generateUpdateCommand("sudo pacman -Syu") + Quickshell.execDetached([terminal, "-e", "bash", "-c", command]) + } + + // Start monitoring and timeout timers + refreshTimer.start() + updateCompleteTimer.start() + updateMonitorTimer.start() } // Update selected packages @@ -141,7 +299,13 @@ Singleton { if (selectedPackages.length === 0) return + // Reset any previous error states + updateFailed = false + lastUpdateError = "" updateInProgress = true + console.log("ArchUpdater: Starting selective update for", selectedPackages.length, "packages") + + const terminal = Quickshell.env("TERMINAL") || "xterm" // Split selected packages by source const repoPkgs = [] @@ -159,48 +323,140 @@ Singleton { } } - // Update repo packages + // Update repo packages with sudo if (repoPkgs.length > 0) { - const repoCommand = ["pkexec", "pacman", "-S", "--noconfirm"].concat(repoPkgs) - Logger.log("ArchUpdater", "Running repo command:", repoCommand.join(" ")) - Quickshell.execDetached(repoCommand) + const packageList = repoPkgs.join(" ") + const command = generateUpdateCommand("sudo pacman -S " + packageList) + Quickshell.execDetached([terminal, "-e", "bash", "-c", command]) } - // Update AUR packages + // Update AUR packages with yay/paru if (aurPkgs.length > 0) { const aurHelper = getAurHelper() if (aurHelper) { - const aurCommand = [aurHelper, "-S", "--noconfirm"].concat(aurPkgs) - Logger.log("ArchUpdater", "Running AUR command:", aurCommand.join(" ")) - Quickshell.execDetached(aurCommand) + const packageList = aurPkgs.join(" ") + const command = generateUpdateCommand(aurHelper + " -S " + packageList) + Quickshell.execDetached([terminal, "-e", "bash", "-c", command]) } else { Logger.warn("ArchUpdater", "No AUR helper found for packages:", aurPkgs.join(", ")) } } - // Clear selection and refresh - selectedPackages = [] - selectedPackagesCount = 0 - refreshAfterUpdate() + // Start monitoring and timeout timers + refreshTimer.start() + updateCompleteTimer.start() + updateMonitorTimer.start() } + // Reset update state (useful for manual recovery) + function resetUpdateState() { + // If update is in progress, mark it as failed first + if (updateInProgress) { + updateFailed = true + } + + updateInProgress = false + lastUpdateError = "" + updateCompleteTimer.stop() + updateMonitorTimer.stop() + refreshTimer.stop() + + // Refresh to get current state + doPoll() + } + + // Manual refresh function + function forceRefresh() { + // Prevent multiple simultaneous refreshes + if (busy || aurBusy) { + return + } + + // Clear error states when refreshing + updateFailed = false + lastUpdateError = "" + + // Just refresh the package lists without syncing databases + doPoll() + } + + // ============================================================================ + // UTILITY PROCESSES + // ============================================================================ + + // Process for checking yay availability + Process { + id: yayCheckProcess + command: ["which", "yay"] + onExited: function (exitCode) { + if (exitCode === 0) { + cachedAurHelper = "yay" + } + } + } + + // Process for checking paru availability + Process { + id: paruCheckProcess + command: ["which", "paru"] + onExited: function (exitCode) { + if (exitCode === 0) { + if (cachedAurHelper === "") { + cachedAurHelper = "paru" + } + } + } + } + + // Process for syncing package databases with sudo + Process { + id: syncDatabaseProcess + command: ["sudo", "pacman", "-Sy"] + onStarted: { + console.log("ArchUpdater: Starting database sync with sudo...") + } + onExited: function (exitCode) { + console.log("ArchUpdater: Database sync exited with code:", exitCode) + if (exitCode === 0) { + console.log("ArchUpdater: Database sync successful") + } else { + console.log("ArchUpdater: Database sync failed") + } + + // After sync completes, wait a moment then refresh package lists + console.log("ArchUpdater: Database sync complete, waiting before refresh...") + Qt.callLater(() => { + console.log("ArchUpdater: Refreshing package lists after database sync...") + doPoll() + }, 2000) + } + } + + // Cached AUR helper detection + property string cachedAurHelper: "" + // Helper function to detect AUR helper function getAurHelper() { - // Check for yay first, then paru - const yayCheck = Quickshell.exec("command -v yay", true) - if (yayCheck.exitCode === 0 && yayCheck.stdout.trim()) { - return "yay" + // Return cached result if available + if (cachedAurHelper !== "") { + return cachedAurHelper } - const paruCheck = Quickshell.exec("command -v paru", true) - if (paruCheck.exitCode === 0 && paruCheck.stdout.trim()) { - return "paru" - } + // Check for AUR helpers using Process objects + console.log("ArchUpdater: Detecting AUR helper...") - return null + // Start the detection processes + yayCheckProcess.running = true + paruCheckProcess.running = true + + // For now, return a default (will be updated by the processes) + // In a real implementation, you'd want to wait for the processes to complete + return "paru" // Default fallback } - // Package selection functions + // ============================================================================ + // PACKAGE SELECTION FUNCTIONS + // ============================================================================ function togglePackageSelection(packageName) { const index = selectedPackages.indexOf(packageName) if (index > -1) { @@ -225,47 +481,60 @@ Singleton { return selectedPackages.indexOf(packageName) > -1 } - // Robust refresh after updates - function refreshAfterUpdate() { - // First refresh attempt after 3 seconds - Qt.callLater(() => { - doPoll() - doAurPoll() - }, 3000) + // ============================================================================ + // REFRESH FUNCTIONS + // ============================================================================ - // Second refresh attempt after 8 seconds - Qt.callLater(() => { - doPoll() - doAurPoll() - }, 8000) - - // Third refresh attempt after 15 seconds - Qt.callLater(() => { - doPoll() - doAurPoll() - updateInProgress = false - }, 15000) - - // Final refresh attempt after 30 seconds - Qt.callLater(() => { - doPoll() - doAurPoll() - }, 30000) + // Function to manually sync package databases (separate from refresh) + function syncPackageDatabases() { + console.log("ArchUpdater: Manual database sync requested...") + const terminal = Quickshell.env("TERMINAL") || "xterm" + const command = "sudo pacman -Sy && echo 'Database sync complete! Press Enter to close...' && read -p 'Press Enter to continue...'" + console.log("ArchUpdater: Executing sync command:", command) + console.log("ArchUpdater: Terminal:", terminal) + Quickshell.execDetached([terminal, "-e", "bash", "-c", command]) } + // Function to force a complete refresh (sync + check) + function forceCompleteRefresh() { + console.log("ArchUpdater: Force complete refresh requested...") + + // Start database sync process (will trigger refresh when complete) + console.log("ArchUpdater: Starting complete refresh process...") + syncDatabaseProcess.running = true + } + + // Function to sync database and refresh package lists + function syncDatabaseAndRefresh() { + console.log("ArchUpdater: Syncing database and refreshing package lists...") + + // Start database sync process (will trigger refresh when complete) + console.log("ArchUpdater: Starting database sync process...") + syncDatabaseProcess.running = true + } + + // ============================================================================ + // UTILITY FUNCTIONS + // ============================================================================ + // Notification helper function notify(title, body) { Quickshell.execDetached(["notify-send", "-a", "UpdateService", "-i", "system-software-update", title, body]) } + // ============================================================================ + // AUTO-POLL TIMER + // ============================================================================ + // Auto-poll every 15 minutes Timer { interval: 15 * 60 * 1000 // 15 minutes repeat: true running: true onTriggered: { - doPoll() - doAurPoll() + if (!updateInProgress) { + doPoll() + } } } -} +} \ No newline at end of file