fix: stabilize inbox confirmation retries
This commit is contained in:
@@ -13,6 +13,11 @@ type ProjectInboxProps = {
|
||||
onConfirm: (inboxId: string, suggestionIds: string[]) => Promise<InboxConfirmationOutcome>
|
||||
}
|
||||
|
||||
type UncertainConfirmation = {
|
||||
inboxItemId: string
|
||||
suggestionIds: string[]
|
||||
}
|
||||
|
||||
export function ProjectInbox({ activeWorkspace, onAnalyze, onConfirm }: ProjectInboxProps) {
|
||||
const [selectedItemId, setSelectedItemId] = useState(activeWorkspace.inbox[0]?.id ?? '')
|
||||
const [draftSuggestions, setDraftSuggestions] = useState<InboxSuggestionDTO[]>([])
|
||||
@@ -23,6 +28,7 @@ export function ProjectInbox({ activeWorkspace, onAnalyze, onConfirm }: ProjectI
|
||||
const [success, setSuccess] = useState('')
|
||||
const [refreshWarning, setRefreshWarning] = useState('')
|
||||
const [locallyConfirmedItemIds, setLocallyConfirmedItemIds] = useState<string[]>([])
|
||||
const [confirmationUncertain, setConfirmationUncertain] = useState<UncertainConfirmation | null>(null)
|
||||
const analysisGeneration = useRef(0)
|
||||
const selectedItem = useMemo(
|
||||
() => activeWorkspace.inbox.find((item) => item.id === selectedItemId) ?? activeWorkspace.inbox[0],
|
||||
@@ -30,6 +36,7 @@ export function ProjectInbox({ activeWorkspace, onAnalyze, onConfirm }: ProjectI
|
||||
)
|
||||
|
||||
function selectItem(item: InboxItem) {
|
||||
if (confirmationUncertain) return
|
||||
analysisGeneration.current += 1
|
||||
setSelectedItemId(item.id)
|
||||
setDraftSuggestions([])
|
||||
@@ -41,7 +48,7 @@ export function ProjectInbox({ activeWorkspace, onAnalyze, onConfirm }: ProjectI
|
||||
}
|
||||
|
||||
async function analyzeSelectedItem() {
|
||||
if (!selectedItem || selectedItem.status !== 'open') return
|
||||
if (!selectedItem || selectedItem.status !== 'open' || confirmationUncertain) return
|
||||
setAnalyzing(true)
|
||||
setError('')
|
||||
setSuccess('')
|
||||
@@ -64,7 +71,11 @@ export function ProjectInbox({ activeWorkspace, onAnalyze, onConfirm }: ProjectI
|
||||
|
||||
async function confirmSelectedSuggestions() {
|
||||
if (!selectedItem) return
|
||||
if (selectedSuggestionIds.length === 0) {
|
||||
const confirmation = confirmationUncertain ?? {
|
||||
inboxItemId: selectedItem.id,
|
||||
suggestionIds: [...selectedSuggestionIds],
|
||||
}
|
||||
if (confirmation.suggestionIds.length === 0) {
|
||||
setError('请至少勾选一条建议')
|
||||
return
|
||||
}
|
||||
@@ -72,11 +83,13 @@ export function ProjectInbox({ activeWorkspace, onAnalyze, onConfirm }: ProjectI
|
||||
setError('')
|
||||
setSuccess('')
|
||||
try {
|
||||
const outcome = await onConfirm(selectedItem.id, selectedSuggestionIds)
|
||||
setLocallyConfirmedItemIds((current) => current.includes(selectedItem.id) ? current : [...current, selectedItem.id])
|
||||
const outcome = await onConfirm(confirmation.inboxItemId, confirmation.suggestionIds)
|
||||
setConfirmationUncertain(null)
|
||||
setLocallyConfirmedItemIds((current) => current.includes(confirmation.inboxItemId) ? current : [...current, confirmation.inboxItemId])
|
||||
setSuccess(`已创建 ${outcome.createdCount} 个对象`)
|
||||
setRefreshWarning(outcome.refreshError ?? '')
|
||||
} catch (reason) {
|
||||
setConfirmationUncertain(isUncertainConfirmationError(reason) ? confirmation : null)
|
||||
setError(confirmationErrorMessage(reason))
|
||||
} finally {
|
||||
setConfirming(false)
|
||||
@@ -84,6 +97,7 @@ export function ProjectInbox({ activeWorkspace, onAnalyze, onConfirm }: ProjectI
|
||||
}
|
||||
|
||||
function toggleSuggestion(identity: string, checked: boolean) {
|
||||
if (confirmationUncertain) return
|
||||
setSelectedSuggestionIds((current) => checked
|
||||
? current.includes(identity) ? current : [...current, identity]
|
||||
: current.filter((value) => value !== identity))
|
||||
@@ -113,7 +127,7 @@ export function ProjectInbox({ activeWorkspace, onAnalyze, onConfirm }: ProjectI
|
||||
key={item.id}
|
||||
type="button"
|
||||
className={selectedItem?.id === item.id ? 'mail-item active' : 'mail-item'}
|
||||
disabled={confirming}
|
||||
disabled={confirming || confirmationUncertain !== null}
|
||||
onClick={() => selectItem(item)}
|
||||
>
|
||||
<span>{item.title}</span>
|
||||
@@ -142,7 +156,7 @@ export function ProjectInbox({ activeWorkspace, onAnalyze, onConfirm }: ProjectI
|
||||
<Button
|
||||
icon={<IconRobot />}
|
||||
loading={analyzing}
|
||||
disabled={confirming || isInboxItemProcessed(selectedItem, locallyConfirmedItemIds)}
|
||||
disabled={confirming || confirmationUncertain !== null || isInboxItemProcessed(selectedItem, locallyConfirmedItemIds)}
|
||||
onClick={() => void analyzeSelectedItem()}
|
||||
>
|
||||
分析内容
|
||||
@@ -167,7 +181,7 @@ export function ProjectInbox({ activeWorkspace, onAnalyze, onConfirm }: ProjectI
|
||||
<Checkbox
|
||||
key={suggestion.id}
|
||||
checked={selectedSuggestionIds.includes(suggestion.id)}
|
||||
disabled={confirming || isInboxItemProcessed(selectedItem, locallyConfirmedItemIds)}
|
||||
disabled={confirming || confirmationUncertain !== null || isInboxItemProcessed(selectedItem, locallyConfirmedItemIds)}
|
||||
onChange={(checked) => toggleSuggestion(suggestion.id, checked)}
|
||||
>
|
||||
<span className="inbox-suggestion-copy">
|
||||
@@ -195,7 +209,7 @@ export function ProjectInbox({ activeWorkspace, onAnalyze, onConfirm }: ProjectI
|
||||
disabled={confirming || analyzing || isInboxItemProcessed(selectedItem, locallyConfirmedItemIds) || draftSuggestions.length === 0}
|
||||
onClick={() => void confirmSelectedSuggestions()}
|
||||
>
|
||||
确认创建
|
||||
{confirmationUncertain ? '重试确认' : '确认创建'}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
@@ -207,8 +221,12 @@ export function ProjectInbox({ activeWorkspace, onAnalyze, onConfirm }: ProjectI
|
||||
)
|
||||
}
|
||||
|
||||
function isUncertainConfirmationError(reason: unknown) {
|
||||
return reason instanceof ApiError && (reason.code === 'network_error' || reason.code === 'invalid_response')
|
||||
}
|
||||
|
||||
function confirmationErrorMessage(reason: unknown) {
|
||||
if (reason instanceof ApiError && (reason.code === 'network_error' || reason.code === 'invalid_response')) {
|
||||
if (isUncertainConfirmationError(reason)) {
|
||||
return '确认结果未知,服务端支持幂等处理,可安全重试'
|
||||
}
|
||||
return reason instanceof Error ? reason.message : '确认创建失败,请稍后重试'
|
||||
|
||||
Reference in New Issue
Block a user