Refactor extension state into ExtensionStateContext

This commit is contained in:
Saoud Rizwan
2024-08-27 04:44:36 -04:00
parent e95bebacda
commit 3b5e018a60
14 changed files with 155 additions and 258 deletions

View File

@@ -1,51 +1,27 @@
import React, { useCallback, useEffect, useMemo, useState } from "react"
import { useCallback, useMemo, useState } from "react"
import { useEvent } from "react-use"
import { ApiConfiguration } from "../../src/shared/api"
import { ClaudeMessage, ExtensionMessage } from "../../src/shared/ExtensionMessage"
import { HistoryItem } from "../../src/shared/HistoryItem"
import "./App.css"
import { ExtensionMessage } from "../../src/shared/ExtensionMessage"
import { normalizeApiConfiguration } from "./components/ApiOptions"
import ChatView from "./components/ChatView"
import HistoryView from "./components/HistoryView"
import SettingsView from "./components/SettingsView"
import WelcomeView from "./components/WelcomeView"
import { ExtensionStateContextProvider, useExtensionState } from "./context/ExtensionStateContext"
import { vscode } from "./utils/vscode"
/*
The contents of webviews however are created when the webview becomes visible and destroyed when the webview is moved into the background. Any state inside the webview will be lost when the webview is moved to a background tab.
The best way to solve this is to make your webview stateless. Use message passing to save off the webview's state and then restore the state when the webview becomes visible again.
*/
const App: React.FC = () => {
const [didHydrateState, setDidHydrateState] = useState(false)
const AppContent = () => {
const { apiConfiguration } = useExtensionState()
const [showSettings, setShowSettings] = useState(false)
const [showHistory, setShowHistory] = useState(false)
const [showWelcome, setShowWelcome] = useState<boolean>(false)
const [version, setVersion] = useState<string>("")
const [apiConfiguration, setApiConfiguration] = useState<ApiConfiguration | undefined>(undefined)
const [maxRequestsPerTask, setMaxRequestsPerTask] = useState<string>("")
const [customInstructions, setCustomInstructions] = useState<string>("")
const [alwaysAllowReadOnly, setAlwaysAllowReadOnly] = useState<boolean>(false)
const [vscodeThemeName, setVscodeThemeName] = useState<string | undefined>(undefined)
const [vscodeUriScheme, setVscodeUriScheme] = useState<string | undefined>(undefined)
const [claudeMessages, setClaudeMessages] = useState<ClaudeMessage[]>([])
const [taskHistory, setTaskHistory] = useState<HistoryItem[]>([])
const [showAnnouncement, setShowAnnouncement] = useState(false)
const [koduCredits, setKoduCredits] = useState<number | undefined>(undefined)
const [shouldShowKoduPromo, setShouldShowKoduPromo] = useState(true)
const [didAuthKoduFromWelcome, setDidAuthKoduFromWelcome] = useState<boolean>(false)
useEffect(() => {
vscode.postMessage({ type: "webviewDidLaunch" })
}, [])
const handleMessage = useCallback(
(e: MessageEvent) => {
const message: ExtensionMessage = e.data
switch (message.type) {
case "state":
setVersion(message.state!.version)
const hasKey =
message.state!.apiConfiguration?.apiKey !== undefined ||
message.state!.apiConfiguration?.openRouterApiKey !== undefined ||
@@ -55,25 +31,10 @@ const App: React.FC = () => {
if (!hasKey) {
setDidAuthKoduFromWelcome(false)
}
setApiConfiguration(message.state!.apiConfiguration)
setMaxRequestsPerTask(
message.state!.maxRequestsPerTask !== undefined
? message.state!.maxRequestsPerTask.toString()
: ""
)
setCustomInstructions(message.state!.customInstructions || "")
setAlwaysAllowReadOnly(message.state!.alwaysAllowReadOnly || false)
setVscodeThemeName(message.state!.themeName)
setVscodeUriScheme(message.state!.uriScheme)
setClaudeMessages(message.state!.claudeMessages)
setTaskHistory(message.state!.taskHistory)
setKoduCredits(message.state!.koduCredits)
// don't update showAnnouncement to false if shouldShowAnnouncement is false
if (message.state!.shouldShowAnnouncement) {
setShowAnnouncement(true)
}
setShouldShowKoduPromo(message.state!.shouldShowKoduPromo)
setDidHydrateState(true)
break
case "action":
switch (message.action!) {
@@ -89,9 +50,6 @@ const App: React.FC = () => {
setShowSettings(false)
setShowHistory(false)
break
case "koduCreditsFetched":
setKoduCredits(message.state!.koduCredits)
break
case "koduAuthenticated":
if (!didAuthKoduFromWelcome) {
setShowSettings(true)
@@ -112,49 +70,21 @@ const App: React.FC = () => {
return normalizeApiConfiguration(apiConfiguration)
}, [apiConfiguration])
if (!didHydrateState) {
return null
}
return (
<>
{showWelcome ? (
<WelcomeView
apiConfiguration={apiConfiguration}
setApiConfiguration={setApiConfiguration}
vscodeUriScheme={vscodeUriScheme}
setDidAuthKoduFromWelcome={setDidAuthKoduFromWelcome}
/>
<WelcomeView setDidAuthKoduFromWelcome={setDidAuthKoduFromWelcome} />
) : (
<>
{showSettings && (
<SettingsView
version={version}
apiConfiguration={apiConfiguration}
setApiConfiguration={setApiConfiguration}
koduCredits={koduCredits}
maxRequestsPerTask={maxRequestsPerTask}
setMaxRequestsPerTask={setMaxRequestsPerTask}
customInstructions={customInstructions}
setCustomInstructions={setCustomInstructions}
alwaysAllowReadOnly={alwaysAllowReadOnly}
setAlwaysAllowReadOnly={setAlwaysAllowReadOnly}
onDone={() => setShowSettings(false)}
vscodeUriScheme={vscodeUriScheme}
/>
)}
{showHistory && <HistoryView taskHistory={taskHistory} onDone={() => setShowHistory(false)} />}
{showSettings && <SettingsView onDone={() => setShowSettings(false)} />}
{showHistory && <HistoryView onDone={() => setShowHistory(false)} />}
{/* Do not conditionally load ChatView, it's expensive and there's state we don't want to lose (user input, disableInput, askResponse promise, etc.) */}
<ChatView
version={version}
messages={claudeMessages}
taskHistory={taskHistory}
showHistoryView={() => {
setShowSettings(false)
setShowHistory(true)
}}
isHidden={showSettings || showHistory}
vscodeThemeName={vscodeThemeName}
showAnnouncement={showAnnouncement}
selectedModelSupportsImages={selectedModelInfo.supportsImages}
selectedModelSupportsPromptCache={selectedModelInfo.supportsPromptCache}
@@ -162,10 +92,6 @@ const App: React.FC = () => {
vscode.postMessage({ type: "didCloseAnnouncement" })
setShowAnnouncement(false)
}}
apiConfiguration={apiConfiguration}
vscodeUriScheme={vscodeUriScheme}
shouldShowKoduPromo={shouldShowKoduPromo}
koduCredits={koduCredits}
/>
</>
)}
@@ -173,4 +99,12 @@ const App: React.FC = () => {
)
}
const App = () => {
return (
<ExtensionStateContextProvider>
<AppContent />
</ExtensionStateContextProvider>
)
}
export default App