import { Box, Skeleton, Tile, Option } from '@rocket.chat/fuselage'; import { Random } from '@rocket.chat/random'; import { useEndpoint } from '@rocket.chat/ui-contexts'; import type { ForwardedRef, ReactNode } from 'react'; import { forwardRef, useEffect, useId, useImperativeHandle } from 'react'; import type { ComposerBoxPopupProps } from './ComposerBoxPopup'; import { useChat } from '../contexts/ChatContext'; type ComposerBoxPopupPreviewItem = { _id: string; type: 'image' | 'video' | 'audio' | 'text' | 'other'; value: string; sort?: number }; export type ComposerBoxPopupPreviewProps = ComposerBoxPopupProps & { title?: ReactNode; rid: string; tmid?: string; suspended: boolean; }; const ComposerBoxPopupPreview = forwardRef(function ComposerBoxPopupPreview( { focused, items, title, rid, tmid, select, suspended }: ComposerBoxPopupPreviewProps, ref: ForwardedRef< | { getFilter?: () => unknown; select?: (s: ComposerBoxPopupPreviewItem) => void; } | undefined >, ) { const id = useId(); const chat = useChat(); const executeSlashCommandPreviewEndpoint = useEndpoint('POST', '/v1/commands.preview'); useImperativeHandle( ref, () => ({ getFilter: () => { const value = chat?.composer?.substring(0, chat?.composer?.selection.start); if (!value) { throw new Error('No value'); } const matches = value.match(/(\/[\w\d\S]+ )([^]*)$/); if (!matches) { throw new Error('No matches'); } const cmd = matches[1].replace('/', '').trim().toLowerCase(); const params = matches[2]; return { cmd, params, msg: { rid, tmid } }; }, ...(!suspended && { select: (item) => { const value = chat?.composer?.substring(0, chat?.composer?.selection.start); if (!value) { throw new Error('No value'); } const matches = value.match(/(\/[\w\d\S]+ )([^]*)$/); if (!matches) { throw new Error('No matches'); } const cmd = matches[1].replace('/', '').trim().toLowerCase(); const params = matches[2]; void executeSlashCommandPreviewEndpoint({ command: cmd, params, roomId: rid, ...(tmid && { tmid }), triggerId: Random.id(), previewItem: { id: item._id, type: item.type, value: item.value }, }); chat?.composer?.setText(''); }, }), }), [chat?.composer, executeSlashCommandPreviewEndpoint, rid, tmid, suspended], ); const itemsFlat = items .flatMap((item) => { if (item.isSuccess) { return item.data; } return []; }) .sort((a, b) => (('sort' in a && a.sort) || 0) - (('sort' in b && b.sort) || 0)); const isLoading = items.some((item) => item.isLoading && item.fetchStatus !== 'idle'); useEffect(() => { if (focused) { const element = document.getElementById(`popup-item-${focused._id}`); if (element) { element.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } } }, [focused]); if (suspended) { return null; } return ( {title && ( {title} )} {isLoading && Array(5) .fill(5) .map((_, index) => )} {!isLoading && itemsFlat.map((item) => ( select(item)} role='option' className={['popup-item', item === focused && 'selected'].filter(Boolean).join(' ')} id={`popup-item-${item._id}`} key={item._id} backgroundColor={item === focused ? 'selected' : undefined} borderColor={item === focused ? 'highlight' : 'transparent'} tabIndex={item === focused ? 0 : -1} aria-selected={item === focused} margin={2} borderWidth='default' borderRadius='x4' > {item.type === 'image' && {item._id}} {item.type === 'audio' && ( )} {item.type === 'video' && ( )} {item.type === 'text' && } {item.type === 'other' && {item.value}} ))} ); }); export default ComposerBoxPopupPreview;