webapp/src/context/snackbar.tsx

80 lines
2.0 KiB
TypeScript
Raw Normal View History

2023-02-09 22:39:52 +00:00
import type { Accessor, JSX } from 'solid-js'
import { createContext, createSignal, useContext } from 'solid-js'
import { delay } from '../utils/delay'
const DEFAULT_DURATION = 3000 // 3 sec
2023-02-09 22:39:52 +00:00
type SnackbarMessage = {
type: 'success' | 'error'
body: string | JSX.Element
duration: number
}
type SnackbarContextType = {
snackbarMessage: Accessor<SnackbarMessage>
actions: {
showSnackbar: (message: {
type?: SnackbarMessage['type']
body: SnackbarMessage['body']
duration?: SnackbarMessage['duration']
}) => Promise<void>
}
}
const SnackbarContext = createContext<SnackbarContextType>()
export function useSnackbar() {
return useContext(SnackbarContext)
}
const messagesToShow: SnackbarMessage[] = []
let currentCheckMessagesPromise = null
export const SnackbarProvider = (props: { children: JSX.Element }) => {
const [snackbarMessage, setSnackbarMessage] = createSignal<SnackbarMessage>(null)
const checkMessages = async () => {
if (messagesToShow.length === 0) {
currentCheckMessagesPromise = null
return
}
setSnackbarMessage(messagesToShow[0])
await delay(messagesToShow[0].duration)
setSnackbarMessage(null)
await delay(1000)
messagesToShow.shift()
await checkMessages()
}
const showSnackbar = async (message: {
type?: SnackbarMessage['type']
body: SnackbarMessage['body']
duration?: SnackbarMessage['duration']
}): Promise<void> => {
const messageToShow = {
type: message.type ?? 'success',
body: message.body,
duration: message.duration ?? DEFAULT_DURATION
}
messagesToShow.push(messageToShow)
if (!currentCheckMessagesPromise) {
currentCheckMessagesPromise = checkMessages()
await currentCheckMessagesPromise
}
return currentCheckMessagesPromise
}
const actions = {
showSnackbar
}
const value: SnackbarContextType = { snackbarMessage, actions }
return <SnackbarContext.Provider value={value}>{props.children}</SnackbarContext.Provider>
}