refactor(agents): extract update agent logic into separate hook

Move update agent functionality from useAgent and useAgents hooks into a dedicated useUpdateAgent hook to improve code organization and maintainability
This commit is contained in:
icarus 2025-09-20 21:31:44 +08:00
parent 043a4fb5ca
commit 7b96900726
6 changed files with 45 additions and 43 deletions

View File

@ -19,6 +19,7 @@ import ClaudeIcon from '@renderer/assets/images/models/claude.png'
import { getModelLogo } from '@renderer/config/models'
import { useAgents } from '@renderer/hooks/agents/useAgents'
import { useModels } from '@renderer/hooks/agents/useModels'
import { useUpdateAgent } from '@renderer/hooks/agents/useUpdateAgent'
import { AddAgentForm, AgentEntity, AgentType, BaseAgentForm, isAgentType, UpdateAgentForm } from '@renderer/types'
import { ChangeEvent, FormEvent, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -78,7 +79,8 @@ export const AgentModal: React.FC<Props> = ({ agent, trigger, isOpen: _isOpen, o
const { t } = useTranslation()
const loadingRef = useRef(false)
// const { setTimeoutTimer } = useTimer()
const { addAgent, updateAgent } = useAgents()
const { addAgent } = useAgents()
const updateAgent = useUpdateAgent()
// hard-coded. We only support anthropic for now.
const { models } = useModels({ providerType: 'anthropic' })
const isEditing = (agent?: AgentEntity) => agent !== undefined
@ -348,7 +350,7 @@ export const AgentModal: React.FC<Props> = ({ agent, trigger, isOpen: _isOpen, o
/>
<div className="space-y-2">
<div className="flex items-center justify-between">
<span className="text-sm font-medium text-foreground">
<span className="font-medium text-foreground text-sm">
{t('agent.session.accessible_paths.label')}
</span>
<Button size="sm" variant="flat" onPress={addAccessiblePath}>
@ -371,7 +373,7 @@ export const AgentModal: React.FC<Props> = ({ agent, trigger, isOpen: _isOpen, o
))}
</div>
) : (
<p className="text-sm text-foreground-400">{t('agent.session.accessible_paths.empty')}</p>
<p className="text-foreground-400 text-sm">{t('agent.session.accessible_paths.empty')}</p>
)}
</div>
<Textarea label={t('common.prompt')} value={form.instructions ?? ''} onValueChange={onInstChange} />

View File

@ -1,13 +1,9 @@
import { UpdateAgentForm } from '@renderer/types'
import { formatErrorMessageWithPrefix } from '@renderer/utils/error'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import useSWR from 'swr'
import { useAgentClient } from './useAgentClient'
export const useAgent = (id: string | null) => {
const { t } = useTranslation()
const client = useAgentClient()
const key = id ? client.agentPaths.withId(id) : null
const fetcher = useCallback(async () => {
@ -17,26 +13,11 @@ export const useAgent = (id: string | null) => {
const result = await client.getAgent(id)
return result
}, [client, id])
const { data, error, isLoading, mutate } = useSWR(key, id ? fetcher : null)
const updateAgent = useCallback(
async (form: UpdateAgentForm) => {
try {
// may change to optimistic update
const result = await client.updateAgent(form)
mutate(result)
window.toast.success(t('common.update_success'))
} catch (error) {
window.toast.error(formatErrorMessageWithPrefix(error, t('agent.update.error.failed')))
}
},
[client, mutate, t]
)
const { data, error, isLoading } = useSWR(key, id ? fetcher : null)
return {
agent: data,
error,
isLoading,
updateAgent
isLoading
}
}

View File

@ -1,4 +1,4 @@
import { AddAgentForm, UpdateAgentForm } from '@renderer/types'
import { AddAgentForm } from '@renderer/types'
import { formatErrorMessageWithPrefix } from '@renderer/utils/error'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
@ -12,6 +12,7 @@ export const useAgents = () => {
const key = client.agentPaths.base
const fetcher = useCallback(async () => {
const result = await client.listAgents()
// NOTE: We only use the array for now. useUpdateAgent depends on this behavior.
return result.data
}, [client])
const { data, error, isLoading, mutate } = useSWR(key, fetcher)
@ -29,20 +30,6 @@ export const useAgents = () => {
[client, mutate, t]
)
const updateAgent = useCallback(
async (form: UpdateAgentForm) => {
try {
// may change to optimistic update
const result = await client.updateAgent(form)
mutate((prev) => prev?.map((a) => (a.id === result.id ? result : a)) ?? [])
window.toast.success(t('common.update_success'))
} catch (error) {
window.toast.error(formatErrorMessageWithPrefix(error, t('agent.update.error.failed')))
}
},
[client, mutate, t]
)
const deleteAgent = useCallback(
async (id: string) => {
try {
@ -69,7 +56,6 @@ export const useAgents = () => {
error,
isLoading,
addAgent,
updateAgent,
deleteAgent,
getAgent
}

View File

@ -0,0 +1,31 @@
import { ListAgentsResponse, UpdateAgentForm } from '@renderer/types'
import { formatErrorMessageWithPrefix } from '@renderer/utils/error'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { mutate } from 'swr'
import { useAgentClient } from './useAgentClient'
export const useUpdateAgent = () => {
const { t } = useTranslation()
const client = useAgentClient()
const listKey = client.agentPaths.base
const updateAgent = useCallback(
async (form: UpdateAgentForm) => {
try {
const itemKey = client.agentPaths.withId(form.id)
// may change to optimistic update
const result = await client.updateAgent(form)
mutate<ListAgentsResponse['data']>(listKey, (prev) => prev?.map((a) => (a.id === result.id ? result : a)) ?? [])
mutate(itemKey, result)
window.toast.success(t('common.update_success'))
} catch (error) {
window.toast.error(formatErrorMessageWithPrefix(error, t('agent.update.error.failed')))
}
},
[client, listKey, t]
)
return updateAgent
}

View File

@ -1,7 +1,7 @@
import CodeEditor from '@renderer/components/CodeEditor'
import { Box, HSpaceBetweenStack, HStack } from '@renderer/components/Layout'
import { RichEditorRef } from '@renderer/components/RichEditor/types'
import { useAgent } from '@renderer/hooks/agents/useAgent'
import { useUpdateAgent } from '@renderer/hooks/agents/useUpdateAgent'
import { usePromptProcessor } from '@renderer/hooks/usePromptProcessor'
import { estimateTextTokens } from '@renderer/services/TokenService'
import { AgentEntity, UpdateAgentForm } from '@renderer/types'
@ -16,7 +16,7 @@ import { SettingDivider } from '..'
interface AgentEssentialSettingsProps {
agent: AgentEntity | undefined | null
update: ReturnType<typeof useAgent>['updateAgent']
update: ReturnType<typeof useUpdateAgent>
}
const AgentEssentialSettings: FC<AgentEssentialSettingsProps> = ({ agent, update }) => {

View File

@ -3,6 +3,7 @@ import { HStack } from '@renderer/components/Layout'
import { TopView } from '@renderer/components/TopView'
import { getAgentAvatar } from '@renderer/config/agent'
import { useAgent } from '@renderer/hooks/agents/useAgent'
import { useUpdateAgent } from '@renderer/hooks/agents/useUpdateAgent'
import { Menu, Modal } from 'antd'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -26,7 +27,8 @@ const AgentSettingPopupContainer: React.FC<AgentSettingPopupParams> = ({ tab, ag
const { t } = useTranslation()
const [menu, setMenu] = useState<AgentSettingPopupTab>(tab || 'essential')
const { agent, updateAgent } = useAgent(agentId)
const { agent } = useAgent(agentId)
const updateAgent = useUpdateAgent()
const onOk = () => {
setOpen(false)