73 lines
2.3 KiB
TypeScript
73 lines
2.3 KiB
TypeScript
import { create } from 'zustand'
|
|
|
|
export type NotificationType = 'info' | 'warning' | 'success' | 'error'
|
|
|
|
export interface Notification {
|
|
id: string
|
|
tag?: string
|
|
message: string
|
|
type: NotificationType
|
|
durationMs?: number
|
|
}
|
|
|
|
interface NotificationState {
|
|
notifications: Notification[]
|
|
timers: Record<string, ReturnType<typeof setTimeout>>
|
|
add: (msg: string, type?: NotificationType, durationMs?: number, tag?: string) => void
|
|
remove: (id: string) => void
|
|
}
|
|
|
|
export const useNotificationStore = create<NotificationState>((set, get) => ({
|
|
notifications: [],
|
|
timers: {},
|
|
|
|
add(msg, type = 'info', durationMs = 5000, tag) {
|
|
const { timers } = get()
|
|
|
|
// Se tem tag, remove a notificação anterior com a mesma tag (sem animação de saída)
|
|
// e cancela o timer dela, para não empilhar
|
|
if (tag) {
|
|
const existing = get().notifications.find((n) => n.tag === tag)
|
|
if (existing) {
|
|
if (timers[existing.id]) {
|
|
clearTimeout(timers[existing.id])
|
|
set((s) => {
|
|
const { [existing.id]: _, ...rest } = s.timers
|
|
return { timers: rest }
|
|
})
|
|
}
|
|
set((s) => ({ notifications: s.notifications.filter((n) => n.tag !== tag) }))
|
|
}
|
|
}
|
|
|
|
const id = tag ?? `${Date.now()}-${Math.random().toString(36).slice(2, 7)}`
|
|
set((s) => ({ notifications: [...s.notifications, { id, tag, message: msg, type, durationMs }] }))
|
|
|
|
if (durationMs > 0) {
|
|
const timer = setTimeout(() => {
|
|
set((s) => ({
|
|
notifications: s.notifications.filter((n) => n.id !== id),
|
|
timers: Object.fromEntries(Object.entries(s.timers).filter(([k]) => k !== id)),
|
|
}))
|
|
}, durationMs)
|
|
set((s) => ({ timers: { ...s.timers, [id]: timer } }))
|
|
}
|
|
},
|
|
|
|
remove(id) {
|
|
const { timers } = get()
|
|
if (timers[id]) {
|
|
clearTimeout(timers[id])
|
|
set((s) => {
|
|
const { [id]: _, ...rest } = s.timers
|
|
return { timers: rest }
|
|
})
|
|
}
|
|
set((s) => ({ notifications: s.notifications.filter((n) => n.id !== id) }))
|
|
},
|
|
}))
|
|
|
|
/** Atalho para usar fora de componentes React (stores, services, etc.) */
|
|
export const notify = (msg: string, type?: NotificationType, durationMs?: number, tag?: string) =>
|
|
useNotificationStore.getState().add(msg, type, durationMs, tag)
|