-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- bump dependencies. - split build into manual chunks. - add ToastProvider to show notifications with different styles and duration. - widgets can use 'useToasts' global to show toasts. - queue notifications, prevent continuous duplicates. - improve implementation and project workflow for EmoteSystem\Admin.
- Loading branch information
1 parent
bf67ea3
commit 6e2c7b2
Showing
15 changed files
with
411 additions
and
248 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import {App} from "./api"; | ||
|
||
declare global { | ||
interface Window { | ||
MUI: typeof import('@mui/material'); | ||
App: App; | ||
} | ||
} | ||
|
||
export {}; |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import {Alert, Snackbar} from "@mui/material"; | ||
import {ActionDispatch, createContext, ReactElement, useContext, useEffect, useReducer, useState} from "react"; | ||
import {equalsToast, Toast, ToastAction, ToastState} from "./ToastReducer.ts"; | ||
|
||
interface ToastProviderProps { | ||
readonly children: ReactElement | Array<ReactElement>; | ||
} | ||
|
||
export function useToasts() { | ||
const dispatch = useContext(ToastDispatchContext); | ||
|
||
return (toast: Toast) => { | ||
dispatch!({ | ||
type: 'open', | ||
toast: { | ||
...toast, | ||
style: toast.style ?? 'info', | ||
duration: toast.duration ?? 4000, | ||
} | ||
}); | ||
}; | ||
} | ||
|
||
const toastReducer = (state: ToastState, action: ToastAction): ToastState => { | ||
if (action.type === 'open') { | ||
return queueToast(state, action.toast); | ||
} else if (action.type === 'close') { | ||
return closeToast(state); | ||
} | ||
throw new Error(`ToastProvider: unknown action "${action.type}".`); | ||
}; | ||
|
||
const queueToast = (state: ToastState, toast?: Toast): ToastState => { | ||
if (!toast) { | ||
return state; | ||
} | ||
const lastToast: Toast | undefined = state.toasts[state.toasts.length - 1] ?? state.toast; | ||
|
||
if (equalsToast(lastToast, toast)) { | ||
return state; | ||
} | ||
if (state.toast) { | ||
return { | ||
toast: state.toast, | ||
toasts: [...state.toasts, toast] | ||
} | ||
} | ||
return { | ||
toast: toast, | ||
toasts: [] | ||
}; | ||
} | ||
|
||
const closeToast = (state: ToastState) => { | ||
let toasts: Toast[] = state.toasts; | ||
let show: Toast | undefined = toasts.splice(0, 1)[0]; | ||
|
||
return { | ||
toast: show, | ||
toasts: [...toasts] | ||
}; | ||
} | ||
|
||
const toastState: ToastState = { | ||
toast: undefined, | ||
toasts: [] | ||
}; | ||
|
||
const ToastContext = createContext<ToastState | null>(null); | ||
const ToastDispatchContext = createContext<ActionDispatch<[action: ToastAction]> | null>(null); | ||
|
||
export default function ToastProvider({children}: ToastProviderProps) { | ||
const [state, dispatch] = useReducer(toastReducer, toastState); | ||
const [show, setShow] = useState<boolean>(false); | ||
const [toast, setToast] = useState<Toast>({message: '', duration: 4000}); | ||
|
||
useEffect(() => { | ||
if (!state.toast) { | ||
return; | ||
} | ||
setToast(state.toast); | ||
setShow(true); | ||
}, [state]); | ||
|
||
const handleClose = () => { | ||
setShow(false); | ||
// NOTE: wait for animation to finish. | ||
setTimeout(() => { | ||
dispatch({type: 'close'}); | ||
}, 500); | ||
}; | ||
|
||
return ( | ||
<ToastContext.Provider value={state}> | ||
<ToastDispatchContext.Provider value={dispatch}> | ||
<Snackbar | ||
open={show} | ||
autoHideDuration={toast.duration} | ||
onClose={handleClose}> | ||
<Alert severity={toast.style} | ||
variant="filled" | ||
sx={{width: '100%'}}> | ||
{toast.message} | ||
</Alert> | ||
</Snackbar> | ||
{children} | ||
</ToastDispatchContext.Provider> | ||
</ToastContext.Provider> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
export type ToastStyle = 'info' | 'success' | 'warning' | 'error'; | ||
|
||
export interface Toast { | ||
readonly message: string; | ||
readonly style?: ToastStyle; | ||
readonly duration?: number; | ||
readonly onClosed?: () => void; | ||
} | ||
|
||
export interface ToastState { | ||
readonly toast?: Toast; | ||
readonly toasts: Toast[]; | ||
} | ||
|
||
export interface ToastAction { | ||
readonly type: 'open' | 'close'; | ||
readonly toast?: Toast; | ||
} | ||
|
||
export function equalsToast(a?: Toast, b?: Toast): boolean { | ||
return a?.style === b?.style && | ||
a?.duration === b?.duration && | ||
a?.message === b?.message && | ||
a?.onClosed === b?.onClosed; | ||
} |
Oops, something went wrong.