fix(service): explain sidecar failures and clear recovered notices

This commit is contained in:
wonfen
2026-10-03 10:21:54 +08:00
parent 8c1fb3ac56
commit f958dccb25
24 changed files with 284 additions and 84 deletions
+9
View File
@@ -467,6 +467,15 @@ export const getPendingFailures = async () => {
return invoke<PendingFailure[]>('get_pending_failures')
}
export interface SidecarFailureSnapshot {
revision: number
detail: string | null
}
export const getSidecarFailure = async () => {
return invoke<SidecarFailureSnapshot>('get_sidecar_failure')
}
export const getAppUptime = async () => {
return invoke<number>('get_app_uptime')
}
+27 -2
View File
@@ -1,7 +1,7 @@
import i18n from 'i18next'
import { ReactNode, isValidElement } from 'react'
import type { FailedOperation } from '@/services/cmds'
import type { FailedOperation, SidecarFailureSnapshot } from '@/services/cmds'
import type { TranslationKey } from '@/types/generated/i18n-keys'
type NoticeType = 'success' | 'error' | 'info' | 'warning'
@@ -97,6 +97,7 @@ const CODED_ERROR_TRANSLATION_KEYS: Readonly<Record<string, TranslationKey>> = {
let nextId = 0
let notices: NoticeItem[] = []
let sidecarFailureRevision = -1
const subscribers: Set<NoticeSubscriber> = new Set()
function notifySubscribers() {
@@ -461,7 +462,8 @@ const baseShowNotice = (
const code = failureCode(raw) ?? failureCode(message) ?? nestedCode
const operation = failureOperation(raw) ?? failureOperation(message)
// Suppress duplicate toasts while preserving the caller's id contract.
if (isReportedByDialog(code)) return id
// Sidecar failures come only from versioned backend state, never delayed command rejections.
if (isReportedByDialog(code) || code === 'SERVICE_SIDECAR_FAILED') return id
const effectiveDuration = resolveDuration(type, duration)
const timerId =
effectiveDuration > 0
@@ -524,6 +526,29 @@ export function hideNotice(id: number) {
notifySubscribers()
}
export function hideNoticesByCode(code: string) {
notices
.filter((notice) => notice.code === code)
.forEach(({ id }) => hideNotice(id))
}
export function syncSidecarFailure(snapshot: SidecarFailureSnapshot) {
if (snapshot.revision <= sidecarFailureRevision) return
sidecarFailureRevision = snapshot.revision
hideNoticesByCode('SERVICE_SIDECAR_FAILED')
if (snapshot.detail === null) return
const failure = { code: 'SERVICE_SIDECAR_FAILED', detail: snapshot.detail }
notices = [
...notices,
buildNotice(nextId++, 'error', 0, {
...normalizeNoticeMessage(failure),
code: failure.code,
}),
]
notifySubscribers()
}
export function subscribeNotices(subscriber: NoticeSubscriber) {
subscribers.add(subscriber)
return () => {