mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
Warn when switching tabs with unsaved content
This commit is contained in:
parent
e6f3bb7051
commit
e9e8c748e8
2 changed files with 57 additions and 29 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { useCallback, useEffect, useState } from "react"
|
||||
import { useCallback, useEffect, useRef, useState } from "react"
|
||||
import { useEvent } from "react-use"
|
||||
|
||||
import { ExtensionMessage } from "../../src/shared/ExtensionMessage"
|
||||
|
|
@ -7,7 +7,7 @@ import { vscode } from "./utils/vscode"
|
|||
import { ExtensionStateContextProvider, useExtensionState } from "./context/ExtensionStateContext"
|
||||
import ChatView from "./components/chat/ChatView"
|
||||
import HistoryView from "./components/history/HistoryView"
|
||||
import SettingsView from "./components/settings/SettingsView"
|
||||
import SettingsView, { SettingsViewRef } from "./components/settings/SettingsView"
|
||||
import WelcomeView from "./components/welcome/WelcomeView"
|
||||
import McpView from "./components/mcp/McpView"
|
||||
import PromptsView from "./components/prompts/PromptsView"
|
||||
|
|
@ -26,18 +26,33 @@ const App = () => {
|
|||
const { didHydrateState, showWelcome, shouldShowAnnouncement } = useExtensionState()
|
||||
const [showAnnouncement, setShowAnnouncement] = useState(false)
|
||||
const [tab, setTab] = useState<Tab>("chat")
|
||||
const settingsRef = useRef<SettingsViewRef>(null)
|
||||
|
||||
const onMessage = useCallback((e: MessageEvent) => {
|
||||
const message: ExtensionMessage = e.data
|
||||
|
||||
if (message.type === "action" && message.action) {
|
||||
const newTab = tabsByMessageAction[message.action]
|
||||
|
||||
if (newTab) {
|
||||
const switchTab = useCallback(
|
||||
(newTab: Tab) => {
|
||||
if (tab === "settings" && settingsRef.current?.checkUnsaveChanges) {
|
||||
settingsRef.current.checkUnsaveChanges(() => setTab(newTab))
|
||||
} else {
|
||||
setTab(newTab)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
},
|
||||
[tab],
|
||||
)
|
||||
|
||||
const onMessage = useCallback(
|
||||
(e: MessageEvent) => {
|
||||
const message: ExtensionMessage = e.data
|
||||
|
||||
if (message.type === "action" && message.action) {
|
||||
const newTab = tabsByMessageAction[message.action]
|
||||
|
||||
if (newTab) {
|
||||
switchTab(newTab)
|
||||
}
|
||||
}
|
||||
},
|
||||
[switchTab],
|
||||
)
|
||||
|
||||
useEvent("message", onMessage)
|
||||
|
||||
|
|
@ -58,15 +73,15 @@ const App = () => {
|
|||
<WelcomeView />
|
||||
) : (
|
||||
<>
|
||||
{tab === "settings" && <SettingsView onDone={() => setTab("chat")} />}
|
||||
{tab === "history" && <HistoryView onDone={() => setTab("chat")} />}
|
||||
{tab === "mcp" && <McpView onDone={() => setTab("chat")} />}
|
||||
{tab === "prompts" && <PromptsView onDone={() => setTab("chat")} />}
|
||||
{tab === "settings" && <SettingsView ref={settingsRef} onDone={() => switchTab("chat")} />}
|
||||
{tab === "history" && <HistoryView onDone={() => switchTab("chat")} />}
|
||||
{tab === "mcp" && <McpView onDone={() => switchTab("chat")} />}
|
||||
{tab === "prompts" && <PromptsView onDone={() => switchTab("chat")} />}
|
||||
<ChatView
|
||||
isHidden={tab !== "chat"}
|
||||
showAnnouncement={showAnnouncement}
|
||||
hideAnnouncement={() => setShowAnnouncement(false)}
|
||||
showHistoryView={() => setTab("history")}
|
||||
showHistoryView={() => switchTab("history")}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { VSCodeButton, VSCodeCheckbox, VSCodeLink, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
||||
import { memo, useCallback, useEffect, useRef, useState } from "react"
|
||||
import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useRef, useState } from "react"
|
||||
import { ExtensionStateContextType, useExtensionState } from "../../context/ExtensionStateContext"
|
||||
import { validateApiConfiguration, validateModelId } from "../../utils/validate"
|
||||
import { vscode } from "../../utils/vscode"
|
||||
|
|
@ -25,17 +25,22 @@ type SettingsViewProps = {
|
|||
onDone: () => void
|
||||
}
|
||||
|
||||
const SettingsView = ({ onDone }: SettingsViewProps) => {
|
||||
export interface SettingsViewRef {
|
||||
checkUnsaveChanges: (then: () => void) => void
|
||||
}
|
||||
|
||||
const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone }, ref) => {
|
||||
const extensionState = useExtensionState()
|
||||
const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
|
||||
const [modelIdErrorMessage, setModelIdErrorMessage] = useState<string | undefined>(undefined)
|
||||
const [commandInput, setCommandInput] = useState("")
|
||||
const prevApiConfigName = useRef(extensionState.currentApiConfigName)
|
||||
const [isDiscardDialogShow, setDiscardDialogShow] = useState(false)
|
||||
|
||||
// TODO: Reduce WebviewMessage/ExtensionState complexity
|
||||
const [cachedState, setCachedState] = useState(extensionState)
|
||||
const [isChangeDetected, setChangeDetected] = useState(false)
|
||||
const prevApiConfigName = useRef(extensionState.currentApiConfigName)
|
||||
const confirmDialogHandler = useRef<() => void>()
|
||||
|
||||
// TODO: Reduce WebviewMessage/ExtensionState complexity
|
||||
const { currentApiConfigName } = extensionState
|
||||
const {
|
||||
apiConfiguration,
|
||||
|
|
@ -190,12 +195,6 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
|||
setModelIdErrorMessage(modelIdValidationResult)
|
||||
}, [apiConfiguration, extensionState.glamaModels, extensionState.openRouterModels])
|
||||
|
||||
const confirmDialogHandler = useRef<() => void>()
|
||||
const onConfirmDialogResult = useCallback((confirm: boolean) => {
|
||||
if (confirm) {
|
||||
confirmDialogHandler.current?.()
|
||||
}
|
||||
}, [])
|
||||
const checkUnsaveChanges = useCallback(
|
||||
(then: () => void) => {
|
||||
if (isChangeDetected) {
|
||||
|
|
@ -208,6 +207,20 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
|||
[isChangeDetected],
|
||||
)
|
||||
|
||||
useImperativeHandle(
|
||||
ref,
|
||||
() => ({
|
||||
checkUnsaveChanges,
|
||||
}),
|
||||
[checkUnsaveChanges],
|
||||
)
|
||||
|
||||
const onConfirmDialogResult = useCallback((confirm: boolean) => {
|
||||
if (confirm) {
|
||||
confirmDialogHandler.current?.()
|
||||
}
|
||||
}, [])
|
||||
|
||||
const handleResetState = () => {
|
||||
vscode.postMessage({ type: "resetState" })
|
||||
}
|
||||
|
|
@ -250,7 +263,7 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
|||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Unsaved changes</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
<span style={{ fontSize: "2em" }} className={`codicon codicon-warning align-middle mr-1`} />
|
||||
<span className={`codicon codicon-warning align-middle mr-1`} />
|
||||
Do you want to discard changes and continue?
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
|
|
@ -890,6 +903,6 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
|||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
export default memo(SettingsView)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue