diff --git a/webview-ui/src/components/history/HistoryView.tsx b/webview-ui/src/components/history/HistoryView.tsx index fb621f5ec0..eba11ce581 100644 --- a/webview-ui/src/components/history/HistoryView.tsx +++ b/webview-ui/src/components/history/HistoryView.tsx @@ -1,5 +1,5 @@ -import React, { memo, useState, useMemo } from "react" -import { ArrowLeft, Settings } from "lucide-react" +import React, { memo, useState, useMemo, useCallback, useEffect } from "react" +import { ArrowLeft, Settings, FolderOpen, RefreshCw, Loader2 } from "lucide-react" import { DeleteTaskDialog } from "./DeleteTaskDialog" import { BatchDeleteTaskDialog } from "./BatchDeleteTaskDialog" import { Virtuoso } from "react-virtuoso" @@ -11,6 +11,14 @@ import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" import { vscode } from "@/utils/vscode" import { useExtensionState } from "@/context/ExtensionStateContext" import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, Button, Checkbox, Popover, @@ -48,7 +56,7 @@ const HistoryView = ({ onDone }: HistoryViewProps) => { showAllWorkspaces, setShowAllWorkspaces, } = useTaskSearch() - const { taskHistoryRetention } = useExtensionState() + const { taskHistoryRetention, taskHistorySize } = useExtensionState() const { t } = useAppTranslation() // Use grouped tasks hook @@ -60,6 +68,39 @@ const HistoryView = ({ onDone }: HistoryViewProps) => { const [selectedTaskIds, setSelectedTaskIds] = useState([]) const [showBatchDeleteDialog, setShowBatchDeleteDialog] = useState(false) const [isRetentionPopoverOpen, setIsRetentionPopoverOpen] = useState(false) + const [pendingRetention, setPendingRetention] = useState(null) + const [showRetentionConfirmDialog, setShowRetentionConfirmDialog] = useState(false) + const [isRefreshingTaskCount, setIsRefreshingTaskCount] = useState(false) + const [cachedTaskCount, setCachedTaskCount] = useState(taskHistorySize?.taskCount) + + // Update cached task count when taskHistorySize changes + useEffect(() => { + if (taskHistorySize) { + setCachedTaskCount(taskHistorySize.taskCount) + setIsRefreshingTaskCount(false) + } + }, [taskHistorySize]) + + // Handle refresh task count + const handleRefreshTaskCount = useCallback(() => { + setIsRefreshingTaskCount(true) + vscode.postMessage({ type: "refreshTaskHistorySize" }) + }, []) + + // Get task count display text + const getTaskCountDisplayText = (): string => { + const count = taskHistorySize?.taskCount ?? cachedTaskCount + if (count === undefined) { + return t("settings:taskHistoryStorage.clickToCount") + } + if (count === 0) { + return t("settings:taskHistoryStorage.empty") + } + if (count === 1) { + return t("settings:taskHistoryStorage.countSingular") + } + return t("settings:taskHistoryStorage.count", { count }) + } // Normalize retention setting to ensure it's valid const normalizedRetention: TaskHistoryRetentionSetting = TASK_HISTORY_RETENTION_OPTIONS.includes( @@ -68,9 +109,31 @@ const HistoryView = ({ onDone }: HistoryViewProps) => { ? (taskHistoryRetention as TaskHistoryRetentionSetting) : "never" - // Handle retention setting change + // Handle retention setting change - show confirmation dialog first const handleRetentionChange = (value: TaskHistoryRetentionSetting) => { - vscode.postMessage({ type: "updateSettings", updatedSettings: { taskHistoryRetention: value } }) + // If selecting the same value, do nothing + if (value === normalizedRetention) { + return + } + // Show confirmation dialog for any change + setPendingRetention(value) + setShowRetentionConfirmDialog(true) + } + + // Confirm retention change + const confirmRetentionChange = () => { + if (pendingRetention !== null) { + vscode.postMessage({ type: "updateSettings", updatedSettings: { taskHistoryRetention: pendingRetention } }) + } + setShowRetentionConfirmDialog(false) + setPendingRetention(null) + setIsRetentionPopoverOpen(false) + } + + // Cancel retention change + const cancelRetentionChange = () => { + setShowRetentionConfirmDialog(false) + setPendingRetention(null) } // Get subtask count for a task @@ -148,7 +211,28 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
-

{t("settings:aboutRetention.label")}

+ {/* Task count display */} +
+ + {getTaskCountDisplayText()} + +
+ +
+

{t("settings:aboutRetention.label")}

+

- {t("settings:aboutRetention.description")} + {t("settings:aboutRetention.warning")}

-

{t("settings:aboutRetention.warning")}

@@ -421,6 +504,32 @@ const HistoryView = ({ onDone }: HistoryViewProps) => { }} /> )} + + {/* Retention change confirmation dialog */} + + + + {t("settings:aboutRetention.confirmDialog.title")} + + {pendingRetention === "never" + ? t("settings:aboutRetention.confirmDialog.descriptionNever") + : t("settings:aboutRetention.confirmDialog.description", { + period: pendingRetention, + })} + + + + + {t("settings:aboutRetention.confirmDialog.cancel")} + + + {pendingRetention === "never" + ? t("settings:aboutRetention.confirmDialog.confirmNever") + : t("settings:aboutRetention.confirmDialog.confirm")} + + + + ) } diff --git a/webview-ui/src/components/settings/About.tsx b/webview-ui/src/components/settings/About.tsx index c5345f8da1..17e3d6cfa9 100644 --- a/webview-ui/src/components/settings/About.tsx +++ b/webview-ui/src/components/settings/About.tsx @@ -1,19 +1,7 @@ -import { HTMLAttributes, useState, useCallback, useEffect } from "react" +import { HTMLAttributes } from "react" import { useAppTranslation } from "@/i18n/TranslationContext" import { Trans } from "react-i18next" -import { - Download, - Upload, - TriangleAlert, - Bug, - Lightbulb, - Shield, - MessageCircle, - MessagesSquare, - RefreshCw, - FolderOpen, - Loader2, -} from "lucide-react" +import { Download, Upload, TriangleAlert, Bug, Lightbulb, Shield, MessageCircle, MessagesSquare } from "lucide-react" import { VSCodeCheckbox, VSCodeLink } from "@vscode/webview-ui-toolkit/react" import type { TelemetrySetting } from "@roo-code/types" @@ -28,63 +16,15 @@ import { SectionHeader } from "./SectionHeader" import { Section } from "./Section" import { SearchableSetting } from "./SearchableSetting" -type TaskHistorySize = { - taskCount: number -} - type AboutProps = HTMLAttributes & { telemetrySetting: TelemetrySetting setTelemetrySetting: (setting: TelemetrySetting) => void debug?: boolean setDebug?: (debug: boolean) => void - taskHistorySize?: TaskHistorySize } -export const About = ({ - telemetrySetting, - setTelemetrySetting, - debug, - setDebug, - taskHistorySize, - className, - ...props -}: AboutProps) => { +export const About = ({ telemetrySetting, setTelemetrySetting, debug, setDebug, className, ...props }: AboutProps) => { const { t } = useAppTranslation() - const [isRefreshing, setIsRefreshing] = useState(false) - const [cachedSize, setCachedSize] = useState(taskHistorySize) - - // Update cached size when taskHistorySize changes and reset refreshing state - useEffect(() => { - if (taskHistorySize) { - setCachedSize(taskHistorySize) - setIsRefreshing(false) - } - }, [taskHistorySize]) - - // NOTE: No auto-trigger on mount - user must click refresh button - // This is intentional for performance with large task counts (e.g., 9000+ tasks) - - const handleRefreshTaskCount = useCallback(() => { - setIsRefreshing(true) - vscode.postMessage({ type: "refreshTaskHistorySize" }) - }, []) - - const getTaskCountDisplayText = (): string => { - // Use cached size if available, otherwise prompt user to click refresh - const displaySize = taskHistorySize || cachedSize - if (!displaySize) { - return t("settings:taskHistoryStorage.clickToCount") - } - if (displaySize.taskCount === 0) { - return t("settings:taskHistoryStorage.empty") - } - if (displaySize.taskCount === 1) { - return t("settings:taskHistoryStorage.countSingular") - } - return t("settings:taskHistoryStorage.count", { - count: displaySize.taskCount, - }) - } return (
@@ -191,37 +131,10 @@ export const About = ({
- -
- - - {t("settings:taskHistoryStorage.label")}: {getTaskCountDisplayText()} - - -
-
- + label={t("settings:about.manageSettings")}>

{t("settings:about.manageSettings")}