Change to useState to match the rest of the codebase

This commit is contained in:
Roo Code 2025-02-07 20:21:15 -05:00
parent 85dacb03a3
commit b85ebbc89a

View file

@ -1,5 +1,5 @@
import { VSCodeButton, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
import { memo, useEffect, useReducer, useRef } from "react"
import { memo, useEffect, useRef, useState } from "react"
import { ApiConfigMeta } from "../../../../src/shared/ExtensionMessage"
import { Dropdown } from "vscrui"
import type { DropdownOption } from "vscrui"
@ -14,86 +14,6 @@ interface ApiConfigManagerProps {
onUpsertConfig: (configName: string) => void
}
type State = {
isRenaming: boolean
isCreating: boolean
inputValue: string
newProfileName: string
error: string | null
}
type Action =
| { type: "START_RENAME"; payload: string }
| { type: "CANCEL_EDIT" }
| { type: "SET_INPUT"; payload: string }
| { type: "SET_NEW_NAME"; payload: string }
| { type: "START_CREATE" }
| { type: "CANCEL_CREATE" }
| { type: "SET_ERROR"; payload: string | null }
| { type: "RESET_STATE" }
const initialState: State = {
isRenaming: false,
isCreating: false,
inputValue: "",
newProfileName: "",
error: null,
}
const reducer = (state: State, action: Action): State => {
switch (action.type) {
case "START_RENAME":
return {
...state,
isRenaming: true,
inputValue: action.payload,
error: null,
}
case "CANCEL_EDIT":
return {
...state,
isRenaming: false,
inputValue: "",
error: null,
}
case "SET_INPUT":
return {
...state,
inputValue: action.payload,
error: null,
}
case "SET_NEW_NAME":
return {
...state,
newProfileName: action.payload,
error: null,
}
case "START_CREATE":
return {
...state,
isCreating: true,
newProfileName: "",
error: null,
}
case "CANCEL_CREATE":
return {
...state,
isCreating: false,
newProfileName: "",
error: null,
}
case "SET_ERROR":
return {
...state,
error: action.payload,
}
case "RESET_STATE":
return initialState
default:
return state
}
}
const ApiConfigManager = ({
currentApiConfigName = "",
listApiConfigMeta = [],
@ -102,7 +22,11 @@ const ApiConfigManager = ({
onRenameConfig,
onUpsertConfig,
}: ApiConfigManagerProps) => {
const [state, dispatch] = useReducer(reducer, initialState)
const [isRenaming, setIsRenaming] = useState(false)
const [isCreating, setIsCreating] = useState(false)
const [inputValue, setInputValue] = useState("")
const [newProfileName, setNewProfileName] = useState("")
const [error, setError] = useState<string | null>(null)
const inputRef = useRef<any>(null)
const newProfileInputRef = useRef<any>(null)
@ -127,68 +51,84 @@ const ApiConfigManager = ({
// Focus input when entering rename mode
useEffect(() => {
if (state.isRenaming) {
if (isRenaming) {
const timeoutId = setTimeout(() => inputRef.current?.focus(), 0)
return () => clearTimeout(timeoutId)
}
}, [state.isRenaming])
}, [isRenaming])
// Focus input when opening new dialog
useEffect(() => {
if (state.isCreating) {
if (isCreating) {
const timeoutId = setTimeout(() => newProfileInputRef.current?.focus(), 0)
return () => clearTimeout(timeoutId)
}
}, [state.isCreating])
}, [isCreating])
// Reset state when current profile changes
useEffect(() => {
dispatch({ type: "RESET_STATE" })
setIsRenaming(false)
setIsCreating(false)
setInputValue("")
setNewProfileName("")
setError(null)
}, [currentApiConfigName])
const handleAdd = () => {
dispatch({ type: "START_CREATE" })
setIsCreating(true)
setNewProfileName("")
setError(null)
}
const handleStartRename = () => {
dispatch({ type: "START_RENAME", payload: currentApiConfigName || "" })
setIsRenaming(true)
setInputValue(currentApiConfigName || "")
setError(null)
}
const handleCancel = () => {
dispatch({ type: "CANCEL_EDIT" })
setIsRenaming(false)
setInputValue("")
setError(null)
}
const handleSave = () => {
const trimmedValue = state.inputValue.trim()
const trimmedValue = inputValue.trim()
const error = validateName(trimmedValue, false)
if (error) {
dispatch({ type: "SET_ERROR", payload: error })
setError(error)
return
}
if (state.isRenaming && currentApiConfigName) {
if (isRenaming && currentApiConfigName) {
if (currentApiConfigName === trimmedValue) {
dispatch({ type: "CANCEL_EDIT" })
setIsRenaming(false)
setInputValue("")
setError(null)
return
}
onRenameConfig(currentApiConfigName, trimmedValue)
}
dispatch({ type: "CANCEL_EDIT" })
setIsRenaming(false)
setInputValue("")
setError(null)
}
const handleNewProfileSave = () => {
const trimmedValue = state.newProfileName.trim()
const trimmedValue = newProfileName.trim()
const error = validateName(trimmedValue, true)
if (error) {
dispatch({ type: "SET_ERROR", payload: error })
setError(error)
return
}
onUpsertConfig(trimmedValue)
dispatch({ type: "CANCEL_CREATE" })
setIsCreating(false)
setNewProfileName("")
setError(null)
}
const handleDelete = () => {
@ -212,23 +152,24 @@ const ApiConfigManager = ({
<span style={{ fontWeight: "500" }}>Configuration Profile</span>
</label>
{state.isRenaming ? (
{isRenaming ? (
<div
data-testid="rename-form"
style={{ display: "flex", gap: "4px", alignItems: "center", flexDirection: "column" }}>
<div style={{ display: "flex", gap: "4px", alignItems: "center", width: "100%" }}>
<VSCodeTextField
ref={inputRef}
value={state.inputValue}
value={inputValue}
onInput={(e: unknown) => {
const target = e as { target: { value: string } }
dispatch({ type: "SET_INPUT", payload: target.target.value })
setInputValue(target.target.value)
setError(null)
}}
placeholder="Enter new name"
style={{ flexGrow: 1 }}
onKeyDown={(e: unknown) => {
const event = e as { key: string }
if (event.key === "Enter" && state.inputValue.trim()) {
if (event.key === "Enter" && inputValue.trim()) {
handleSave()
} else if (event.key === "Escape") {
handleCancel()
@ -237,7 +178,7 @@ const ApiConfigManager = ({
/>
<VSCodeButton
appearance="icon"
disabled={!state.inputValue.trim()}
disabled={!inputValue.trim()}
onClick={handleSave}
title="Save"
style={{
@ -263,9 +204,9 @@ const ApiConfigManager = ({
<span className="codicon codicon-close" />
</VSCodeButton>
</div>
{state.error && (
{error && (
<p className="text-red-500 text-sm mt-2" data-testid="error-message">
{state.error}
{error}
</p>
)}
</div>
@ -345,8 +286,18 @@ const ApiConfigManager = ({
)}
<Dialog
open={state.isCreating}
onOpenChange={(open: boolean) => dispatch({ type: open ? "START_CREATE" : "CANCEL_CREATE" })}
open={isCreating}
onOpenChange={(open: boolean) => {
if (open) {
setIsCreating(true)
setNewProfileName("")
setError(null)
} else {
setIsCreating(false)
setNewProfileName("")
setError(null)
}
}}
aria-labelledby="new-profile-title">
<DialogContent className="p-4 max-w-sm">
<h2 id="new-profile-title" className="text-lg font-semibold mb-4">
@ -355,39 +306,52 @@ const ApiConfigManager = ({
<button
className="absolute right-4 top-4"
aria-label="Close dialog"
onClick={() => dispatch({ type: "CANCEL_CREATE" })}>
onClick={() => {
setIsCreating(false)
setNewProfileName("")
setError(null)
}}>
<span className="codicon codicon-close" />
</button>
<VSCodeTextField
ref={newProfileInputRef}
value={state.newProfileName}
value={newProfileName}
onInput={(e: unknown) => {
const target = e as { target: { value: string } }
dispatch({ type: "SET_NEW_NAME", payload: target.target.value })
setNewProfileName(target.target.value)
setError(null)
}}
placeholder="Enter profile name"
style={{ width: "100%" }}
onKeyDown={(e: unknown) => {
const event = e as { key: string }
if (event.key === "Enter" && state.newProfileName.trim()) {
if (event.key === "Enter" && newProfileName.trim()) {
handleNewProfileSave()
} else if (event.key === "Escape") {
dispatch({ type: "CANCEL_CREATE" })
setIsCreating(false)
setNewProfileName("")
setError(null)
}
}}
/>
{state.error && (
{error && (
<p className="text-red-500 text-sm mt-2" data-testid="error-message">
{state.error}
{error}
</p>
)}
<div className="flex justify-end gap-2 mt-4">
<VSCodeButton appearance="secondary" onClick={() => dispatch({ type: "CANCEL_CREATE" })}>
<VSCodeButton
appearance="secondary"
onClick={() => {
setIsCreating(false)
setNewProfileName("")
setError(null)
}}>
Cancel
</VSCodeButton>
<VSCodeButton
appearance="primary"
disabled={!state.newProfileName.trim()}
disabled={!newProfileName.trim()}
onClick={handleNewProfileSave}>
Create Profile
</VSCodeButton>