fix(web): resolve approval card race condition with polling

Race condition between polling (5s interval) and sign/reject operations
caused cards to flicker and reappear after being approved.

Fix:
- Pause polling during sign/reject API calls
- Resume polling after 1 second delay to allow backend state sync
- Apply same pattern to both signApproval and rejectApproval

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
OG T
2026-03-23 09:13:35 +08:00
parent de5796522f
commit 68f4cf51b6

View File

@@ -176,6 +176,14 @@ export const useApprovalStore = create<ApprovalState>()(
// Sign Approval
// ==========================================================================
signApproval: async (id, signerId, signerName, comment) => {
// 🔧 Race Condition 修復: 簽核期間暫停 Polling
const wasPolling = pollingTimer !== null
if (wasPolling) {
clearInterval(pollingTimer!)
pollingTimer = null
console.log('[Approval] Polling paused during sign')
}
set({ signingId: id, error: null })
try {
@@ -219,6 +227,14 @@ export const useApprovalStore = create<ApprovalState>()(
}
})
// 🔧 Race Condition 修復: 延遲 1 秒後恢復 Polling讓後端有時間更新
if (wasPolling) {
setTimeout(() => {
get().startPolling(get().pollingInterval || DEFAULT_POLLING_INTERVAL)
console.log('[Approval] Polling resumed after sign')
}, 1000)
}
return result
} catch (err) {
console.error('[Approval] Sign failed:', err)
@@ -226,6 +242,13 @@ export const useApprovalStore = create<ApprovalState>()(
signingId: null,
error: `Sign failed: ${err}`,
})
// 🔧 失敗也要恢復 Polling
if (wasPolling) {
get().startPolling(get().pollingInterval || DEFAULT_POLLING_INTERVAL)
console.log('[Approval] Polling resumed after sign error')
}
return null
}
},
@@ -234,6 +257,14 @@ export const useApprovalStore = create<ApprovalState>()(
// Reject Approval
// ==========================================================================
rejectApproval: async (id, rejectorId, rejectorName, reason) => {
// 🔧 Race Condition 修復: 拒絕期間暫停 Polling
const wasPolling = pollingTimer !== null
if (wasPolling) {
clearInterval(pollingTimer!)
pollingTimer = null
console.log('[Approval] Polling paused during reject')
}
set({ rejectingId: id, error: null })
try {
@@ -266,6 +297,14 @@ export const useApprovalStore = create<ApprovalState>()(
}
})
// 🔧 Race Condition 修復: 延遲 1 秒後恢復 Polling
if (wasPolling) {
setTimeout(() => {
get().startPolling(get().pollingInterval || DEFAULT_POLLING_INTERVAL)
console.log('[Approval] Polling resumed after reject')
}, 1000)
}
return true
} catch (err) {
console.error('[Approval] Reject failed:', err)
@@ -273,6 +312,13 @@ export const useApprovalStore = create<ApprovalState>()(
rejectingId: null,
error: `Reject failed: ${err}`,
})
// 🔧 失敗也要恢復 Polling
if (wasPolling) {
get().startPolling(get().pollingInterval || DEFAULT_POLLING_INTERVAL)
console.log('[Approval] Polling resumed after reject error')
}
return false
}
},