Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
parkpow
GitHub Repository: parkpow/deep-license-plate-recognition
Path: blob/master/webhooks/webhook_preview/hooks/use-toast.ts
1091 views
1
"use client"
2
3
// Inspired by react-hot-toast library
4
import * as React from "react"
5
6
import type {
7
ToastActionElement,
8
ToastProps,
9
} from "@/components/ui/toast"
10
11
const TOAST_LIMIT = 1
12
const TOAST_REMOVE_DELAY = 1000000
13
14
type ToasterToast = ToastProps & {
15
id: string
16
title?: React.ReactNode
17
description?: React.ReactNode
18
action?: ToastActionElement
19
}
20
21
const actionTypes = {
22
ADD_TOAST: "ADD_TOAST",
23
UPDATE_TOAST: "UPDATE_TOAST",
24
DISMISS_TOAST: "DISMISS_TOAST",
25
REMOVE_TOAST: "REMOVE_TOAST",
26
} as const
27
28
let count = 0
29
30
function genId() {
31
count = (count + 1) % Number.MAX_SAFE_INTEGER
32
return count.toString()
33
}
34
35
type ActionType = typeof actionTypes
36
37
type Action =
38
| {
39
type: ActionType["ADD_TOAST"]
40
toast: ToasterToast
41
}
42
| {
43
type: ActionType["UPDATE_TOAST"]
44
toast: Partial<ToasterToast>
45
}
46
| {
47
type: ActionType["DISMISS_TOAST"]
48
toastId?: ToasterToast["id"]
49
}
50
| {
51
type: ActionType["REMOVE_TOAST"]
52
toastId?: ToasterToast["id"]
53
}
54
55
interface State {
56
toasts: ToasterToast[]
57
}
58
59
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
60
61
const addToRemoveQueue = (toastId: string) => {
62
if (toastTimeouts.has(toastId)) {
63
return
64
}
65
66
const timeout = setTimeout(() => {
67
toastTimeouts.delete(toastId)
68
dispatch({
69
type: "REMOVE_TOAST",
70
toastId: toastId,
71
})
72
}, TOAST_REMOVE_DELAY)
73
74
toastTimeouts.set(toastId, timeout)
75
}
76
77
export const reducer = (state: State, action: Action): State => {
78
switch (action.type) {
79
case "ADD_TOAST":
80
return {
81
...state,
82
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
83
}
84
85
case "UPDATE_TOAST":
86
return {
87
...state,
88
toasts: state.toasts.map((t) =>
89
t.id === action.toast.id ? { ...t, ...action.toast } : t
90
),
91
}
92
93
case "DISMISS_TOAST": {
94
const { toastId } = action
95
96
// ! Side effects ! - This could be extracted into a dismissToast() action,
97
// but I'll keep it here for simplicity
98
if (toastId) {
99
addToRemoveQueue(toastId)
100
} else {
101
state.toasts.forEach((toast) => {
102
addToRemoveQueue(toast.id)
103
})
104
}
105
106
return {
107
...state,
108
toasts: state.toasts.map((t) =>
109
t.id === toastId || toastId === undefined
110
? {
111
...t,
112
open: false,
113
}
114
: t
115
),
116
}
117
}
118
case "REMOVE_TOAST":
119
if (action.toastId === undefined) {
120
return {
121
...state,
122
toasts: [],
123
}
124
}
125
return {
126
...state,
127
toasts: state.toasts.filter((t) => t.id !== action.toastId),
128
}
129
}
130
}
131
132
const listeners: Array<(state: State) => void> = []
133
134
let memoryState: State = { toasts: [] }
135
136
function dispatch(action: Action) {
137
memoryState = reducer(memoryState, action)
138
listeners.forEach((listener) => {
139
listener(memoryState)
140
})
141
}
142
143
type Toast = Omit<ToasterToast, "id">
144
145
function toast({ ...props }: Toast) {
146
const id = genId()
147
148
const update = (props: ToasterToast) =>
149
dispatch({
150
type: "UPDATE_TOAST",
151
toast: { ...props, id },
152
})
153
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
154
155
dispatch({
156
type: "ADD_TOAST",
157
toast: {
158
...props,
159
id,
160
open: true,
161
onOpenChange: (open) => {
162
if (!open) dismiss()
163
},
164
},
165
})
166
167
return {
168
id: id,
169
dismiss,
170
update,
171
}
172
}
173
174
function useToast() {
175
const [state, setState] = React.useState<State>(memoryState)
176
177
React.useEffect(() => {
178
listeners.push(setState)
179
return () => {
180
const index = listeners.indexOf(setState)
181
if (index > -1) {
182
listeners.splice(index, 1)
183
}
184
}
185
}, [state])
186
187
return {
188
...state,
189
toast,
190
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
191
}
192
}
193
194
export { useToast, toast }
195
196