mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-14 04:16:06 +00:00
feat(desktop): add cancel-summon button in SummoningModal
Add Cancel button in SummoningModal after 60s threshold. Call POST
/v1/agents/{id}/cancel-summon via getApiClient(). Update agent status
to summon_failed. Add i18n strings for en/vi/zh in desktop locale.
This commit is contained in:
1 parent
2a7a8f9a84
commit
f2325f6d98
8 files changed
+56
-6
No files matched your search
@@ -11,7 +11,7 @@ import type { AgentData, AgentInput } from '../../types/agent'
|
||||
|
||||
export function AgentList() {
|
||||
const { t } = useTranslation(['agents', 'common'])
|
||||
const { agents, loading, atLimit, createAgent, updateAgent, deleteAgent, resummonAgent, fetchAgents } = useAgentCrud()
|
||||
const { agents, loading, atLimit, createAgent, updateAgent, deleteAgent, resummonAgent, cancelSummonAgent, fetchAgents } = useAgentCrud()
|
||||
const setStoreAgents = useAgentStore((s) => s.setAgents)
|
||||
|
||||
const [formOpen, setFormOpen] = useState(false)
|
||||
@@ -140,6 +140,7 @@ export function AgentList() {
|
||||
agentId={summoningAgent.id}
|
||||
agentName={summoningAgent.name}
|
||||
onContinue={handleSummoningComplete}
|
||||
onCancel={cancelSummonAgent}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useMemo, useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { getApiClient } from '../../lib/api'
|
||||
import { agentService } from '../../services/agent-service'
|
||||
|
||||
import { SummoningModal } from './SummoningModal'
|
||||
import type { ProviderData } from '../../types/provider'
|
||||
@@ -109,6 +110,7 @@ export function AgentStep({ provider, model, onBack, onComplete }: AgentStepProp
|
||||
agentId={createdAgent.id}
|
||||
agentName={createdAgent.name}
|
||||
onContinue={onComplete}
|
||||
onCancel={(id) => agentService.cancelSummon(id)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -11,14 +11,18 @@ interface SummoningModalProps {
|
||||
agentId: string
|
||||
agentName: string
|
||||
onContinue: () => void
|
||||
onCancel?: (agentId: string) => Promise<void>
|
||||
}
|
||||
|
||||
export function SummoningModal({ agentId, agentName, onContinue }: SummoningModalProps) {
|
||||
const CANCEL_THRESHOLD_SEC = 60
|
||||
|
||||
export function SummoningModal({ agentId, agentName, onContinue, onCancel }: SummoningModalProps) {
|
||||
const { t } = useTranslation(['desktop', 'common'])
|
||||
const [generatedFiles, setGeneratedFiles] = useState<string[]>([])
|
||||
const [status, setStatus] = useState<'summoning' | 'completed' | 'failed'>('summoning')
|
||||
const [errorMsg, setErrorMsg] = useState('')
|
||||
const [retrying, setRetrying] = useState(false)
|
||||
const [cancelling, setCancelling] = useState(false)
|
||||
const [elapsed, setElapsed] = useState(0)
|
||||
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null)
|
||||
|
||||
@@ -84,6 +88,19 @@ export function SummoningModal({ agentId, agentName, onContinue }: SummoningModa
|
||||
}
|
||||
}
|
||||
|
||||
const handleCancel = async () => {
|
||||
if (!onCancel) return
|
||||
setCancelling(true)
|
||||
try {
|
||||
await onCancel(agentId)
|
||||
// BE emits WS type=failed → listener above switches to failed state
|
||||
} catch {
|
||||
// keep modal open
|
||||
} finally {
|
||||
setCancelling(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-[70] flex items-center justify-center bg-black/50 backdrop-blur-sm">
|
||||
<div className="bg-surface-secondary border border-border rounded-2xl shadow-xl max-w-md w-full mx-4">
|
||||
@@ -164,6 +181,19 @@ export function SummoningModal({ agentId, agentName, onContinue }: SummoningModa
|
||||
</p>
|
||||
)}
|
||||
|
||||
{status === 'summoning' && elapsed >= CANCEL_THRESHOLD_SEC && onCancel && (
|
||||
<div className="flex flex-col items-center gap-1">
|
||||
<p className="text-xs text-text-muted">{t('desktop:summoning.takingTooLong')}</p>
|
||||
<button
|
||||
onClick={handleCancel}
|
||||
disabled={cancelling}
|
||||
className="px-3 py-1.5 text-sm text-text-secondary hover:bg-surface-tertiary rounded-md transition-colors disabled:opacity-50"
|
||||
>
|
||||
{cancelling ? t('desktop:summoning.cancelling') : t('desktop:summoning.cancel')}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{status === 'completed' && (
|
||||
<button
|
||||
onClick={onContinue}
|
||||
|
||||
@@ -63,7 +63,11 @@ export function useAgentCrud() {
|
||||
setAgents((prev) => prev.map((a) => a.id === id ? { ...a, status: 'summoning' } : a))
|
||||
}, [])
|
||||
|
||||
const cancelSummonAgent = useCallback(async (id: string) => {
|
||||
await agentService.cancelSummon(id)
|
||||
}, [])
|
||||
|
||||
const atLimit = agents.length >= MAX_AGENTS_LITE
|
||||
|
||||
return { agents, loading, atLimit, fetchAgents, createAgent, updateAgent, deleteAgent, resummonAgent }
|
||||
return { agents, loading, atLimit, fetchAgents, createAgent, updateAgent, deleteAgent, resummonAgent, cancelSummonAgent }
|
||||
}
|
||||
@@ -26,7 +26,10 @@
|
||||
"fileLabelSOUL": "Core personality",
|
||||
"fileLabelIDENTITY": "Identity & knowledge",
|
||||
"agentReady": "{{name}} is ready!",
|
||||
"retrying": "Retrying..."
|
||||
"retrying": "Retrying...",
|
||||
"takingTooLong": "Taking too long?",
|
||||
"cancel": "Cancel",
|
||||
"cancelling": "Cancelling..."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
|
||||
@@ -26,7 +26,10 @@
|
||||
"fileLabelSOUL": "Tính cách cốt lõi",
|
||||
"fileLabelIDENTITY": "Danh tính & kiến thức",
|
||||
"agentReady": "{{name}} đã sẵn sàng!",
|
||||
"retrying": "Đang thử lại..."
|
||||
"retrying": "Đang thử lại...",
|
||||
"takingTooLong": "Mất quá nhiều thời gian?",
|
||||
"cancel": "Huỷ",
|
||||
"cancelling": "Đang huỷ..."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
|
||||
@@ -26,7 +26,10 @@
|
||||
"fileLabelSOUL": "核心个性",
|
||||
"fileLabelIDENTITY": "身份与知识",
|
||||
"agentReady": "{{name}} 已就绪!",
|
||||
"retrying": "重试中..."
|
||||
"retrying": "重试中...",
|
||||
"takingTooLong": "耗时过长?",
|
||||
"cancel": "取消",
|
||||
"cancelling": "正在取消..."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
|
||||
@@ -38,6 +38,10 @@ export const agentService = {
|
||||
return getApiClient().post<void>(`/v1/agents/${id}/resummon`, {})
|
||||
},
|
||||
|
||||
cancelSummon(id: string): Promise<void> {
|
||||
return getApiClient().post<void>(`/v1/agents/${id}/cancel-summon`, {})
|
||||
},
|
||||
|
||||
resummonWs(agentId: string): Promise<unknown> {
|
||||
return getWsClient().call('agents.resummon', { agentId })
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user