| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 |
- import { OLToast, OLToastProps } from '@/shared/components/ol/ol-toast'
- import useEventListener from '@/shared/hooks/use-event-listener'
- import { Fragment, memo, ReactElement, useCallback, useState } from 'react'
- import { debugConsole } from '@/utils/debugging'
- import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
- import { OLToastContainer } from '@/shared/components/ol/ol-toast-container'
- import clipboardToastGenerators from '@/features/source-editor/components/clipboard-toasts'
- import importDocxFeedbackToastGenerators from '@/features/project-list/components/new-project-button/import-docx-feedback-toast'
- import exportDocumentToastGenerators from '@/features/ide-react/components/toolbar/export-document-toasts'
- const moduleGeneratorsImport = importOverleafModules('toastGenerators') as {
- import: { default: GlobalToastGeneratorEntry[] }
- }[]
- const moduleGenerators = moduleGeneratorsImport.map(
- ({ import: { default: listEntry } }) => listEntry
- )
- export type GlobalToastGeneratorEntry = {
- key: string
- generator: GlobalToastGenerator
- }
- type GlobalToastGenerator = (
- args: Record<string, any>
- ) => Omit<OLToastProps, 'onDismiss'>
- const GENERATOR_LIST: GlobalToastGeneratorEntry[] = [
- ...moduleGenerators.flat(),
- ...clipboardToastGenerators,
- ...importDocxFeedbackToastGenerators,
- ...exportDocumentToastGenerators,
- ]
- const GENERATOR_MAP: Map<string, GlobalToastGenerator> = new Map(
- GENERATOR_LIST.map(({ key, generator }) => [key, generator])
- )
- let toastCounter = 1
- export const GlobalToasts = memo(function GlobalToasts() {
- const [toasts, setToasts] = useState<
- { component: ReactElement; id: string }[]
- >([])
- const removeToast = useCallback((id: string) => {
- setToasts(current => current.filter(toast => toast.id !== id))
- }, [])
- const createToast = useCallback(
- (id: string, key: string, data: any): ReactElement | null => {
- const generator = GENERATOR_MAP.get(key)
- if (!generator) {
- debugConsole.error('No toast generator found for key:', key)
- return null
- }
- const props = generator(data)
- if (!props.autoHide && !props.isDismissible) {
- // We don't want any toasts that are not dismissible and don't auto-hide
- props.isDismissible = true
- }
- if (props.autoHide && !props.isDismissible && props.delay !== undefined) {
- // If the toast is auto-hiding but not dismissible, we need to make sure the delay is not too long
- props.delay = Math.min(props.delay, 60_000)
- }
- return <OLToast {...props} onDismiss={() => removeToast(id)} />
- },
- [removeToast]
- )
- const addToast = useCallback(
- (key: string, data?: any) => {
- const id = `toast-${toastCounter++}`
- const component = createToast(id, key, data)
- if (!component) {
- return
- }
- setToasts(current => [...current, { id, component }])
- },
- [createToast]
- )
- const showToastListener = useCallback(
- (event: CustomEvent) => {
- if (!event.detail?.key) {
- debugConsole.error('No key provided for toast')
- return
- }
- const { key, ...rest } = event.detail
- addToast(key, rest)
- },
- [addToast]
- )
- useEventListener('ide:show-toast', showToastListener)
- return (
- <OLToastContainer className="global-toasts">
- {toasts.map(({ component, id }) => (
- <Fragment key={id}>{component}</Fragment>
- ))}
- </OLToastContainer>
- )
- })
|