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