Files
vtuber-awards/frontend/src/composables/useAdminToast.ts
T
2026-06-26 18:09:29 +02:00

87 lines
1.9 KiB
TypeScript

import { computed, ref, watch, type Ref } from 'vue'
export type AdminToastTone = 'success' | 'error' | 'info'
export interface AdminToast {
id: number
message: string
tone: AdminToastTone
}
const activeToast = ref<AdminToast | null>(null)
let toastId = 0
let hideTimer: number | undefined
export function useAdminToast() {
function dismissAdminToast(id?: number) {
if (id && activeToast.value?.id !== id) return
activeToast.value = null
if (hideTimer !== undefined && typeof window !== 'undefined') {
window.clearTimeout(hideTimer)
hideTimer = undefined
}
}
function showAdminToast(message: string, tone: AdminToastTone = 'info', durationMs = tone === 'error' ? 6200 : 4200) {
const trimmedMessage = message.trim()
if (!trimmedMessage) return
if (hideTimer !== undefined && typeof window !== 'undefined') {
window.clearTimeout(hideTimer)
hideTimer = undefined
}
const id = ++toastId
activeToast.value = { id, message: trimmedMessage, tone }
if (typeof window !== 'undefined') {
hideTimer = window.setTimeout(() => dismissAdminToast(id), durationMs)
}
}
return {
toast: computed(() => activeToast.value),
showAdminToast,
dismissAdminToast,
}
}
export function watchAdminToast(adminMessage: Ref<string>, adminError: Ref<string>) {
const { showAdminToast } = useAdminToast()
watch(
adminMessage,
(message) => {
if (message.trim()) {
showAdminToast(message, 'success')
}
},
{ flush: 'post' },
)
watch(
adminError,
(error) => {
if (error.trim()) {
showAdminToast(error, 'error')
}
},
{ flush: 'post' },
)
}
export function watchAdminErrorToast(adminError: Ref<string>) {
const { showAdminToast } = useAdminToast()
watch(
adminError,
(error) => {
if (error.trim()) {
showAdminToast(error, 'error')
}
},
{ flush: 'post' },
)
}