The credential issuance and verification handlers used Handle::block_on() directly inside the tokio runtime, causing a deadlock. Wrapped with block_in_place() to properly yield the runtime thread. Also completed full feature verification across all 25 test groups (~175 checks) on live server. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
48 lines
1.1 KiB
TypeScript
48 lines
1.1 KiB
TypeScript
import { ref, readonly } from 'vue'
|
|
|
|
export type ToastVariant = 'success' | 'error' | 'info'
|
|
|
|
export interface ToastItem {
|
|
id: number
|
|
message: string
|
|
variant: ToastVariant
|
|
dismissing: boolean
|
|
}
|
|
|
|
const toasts = ref<ToastItem[]>([])
|
|
let nextId = 0
|
|
|
|
function addToast(message: string, variant: ToastVariant = 'info', duration = 3000) {
|
|
const id = nextId++
|
|
toasts.value.push({ id, message, variant, dismissing: false })
|
|
|
|
// Auto-dismiss
|
|
if (duration > 0) {
|
|
setTimeout(() => dismissToast(id), duration)
|
|
}
|
|
|
|
// Cap at 5 visible toasts
|
|
if (toasts.value.length > 5) {
|
|
toasts.value.shift()
|
|
}
|
|
}
|
|
|
|
function dismissToast(id: number) {
|
|
const idx = toasts.value.findIndex(t => t.id === id)
|
|
if (idx === -1) return
|
|
toasts.value[idx]!.dismissing = true
|
|
setTimeout(() => {
|
|
toasts.value = toasts.value.filter(t => t.id !== id)
|
|
}, 300)
|
|
}
|
|
|
|
export function useToast() {
|
|
return {
|
|
toasts: readonly(toasts),
|
|
success: (msg: string) => addToast(msg, 'success'),
|
|
error: (msg: string) => addToast(msg, 'error'),
|
|
info: (msg: string) => addToast(msg, 'info'),
|
|
dismiss: dismissToast,
|
|
}
|
|
}
|