import { useEffect, useMemo, useState } from 'react'; import { BlockNoteEditor } from '@blocknote/core'; import { getDefaultReactSlashMenuItems, useBlockNote } from '@blocknote/react'; import styled from '@emotion/styled'; import { isNonEmptyString } from '@sniptt/guards'; import debounce from 'lodash.debounce'; import { Activity } from '@/activities/types/Activity'; import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; import { BlockEditor } from '@/ui/input/editor/components/BlockEditor'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { REACT_APP_SERVER_BASE_URL } from '~/config'; import { FileFolder, useUploadFileMutation } from '~/generated/graphql'; const StyledBlockNoteStyledContainer = styled.div` width: 100%; `; type ActivityBodyEditorProps = { activity: Pick; onChange?: (activityBody: string) => void; }; export const ActivityBodyEditor = ({ activity, onChange, }: ActivityBodyEditorProps) => { const [body, setBody] = useState(null); const { updateOneRecord } = useUpdateOneRecord({ objectNameSingular: 'activity', }); useEffect(() => { if (body) { onChange?.(body); } }, [body, onChange]); const debounceOnChange = useMemo(() => { const onInternalChange = (activityBody: string) => { setBody(activityBody); updateOneRecord?.({ idToUpdate: activity.id, updateOneRecordInput: { body: activityBody, }, }); }; return debounce(onInternalChange, 200); }, [updateOneRecord, activity.id]); let slashMenuItems = [...getDefaultReactSlashMenuItems()]; const imagesActivated = useIsFeatureEnabled('IS_NOTE_CREATE_IMAGES_ENABLED'); if (!imagesActivated) { slashMenuItems = slashMenuItems.filter((x) => x.name != 'Image'); } const [uploadFile] = useUploadFileMutation(); const handleUploadAttachment = async (file: File): Promise => { if (!file) { return ''; } const result = await uploadFile({ variables: { file, fileFolder: FileFolder.Attachment, }, }); if (!result?.data?.uploadFile) { throw new Error("Couldn't upload Image"); } const imageUrl = REACT_APP_SERVER_BASE_URL + '/files/' + result?.data?.uploadFile; return imageUrl; }; const editor: BlockNoteEditor | null = useBlockNote({ initialContent: isNonEmptyString(activity.body) && activity.body !== '{}' ? JSON.parse(activity.body) : undefined, domAttributes: { editor: { class: 'editor' } }, onEditorContentChange: (editor: BlockNoteEditor) => { debounceOnChange(JSON.stringify(editor.topLevelBlocks) ?? ''); }, slashMenuItems, uploadFile: imagesActivated ? handleUploadAttachment : undefined, onEditorReady: (editor: BlockNoteEditor) => { editor.domElement.addEventListener('paste', handleImagePaste); }, }); const handleImagePaste = async (event: ClipboardEvent) => { const clipboardItems = event.clipboardData?.items; if (clipboardItems) { for (let i = 0; i < clipboardItems.length; i++) { if ( clipboardItems[i].kind === 'file' && clipboardItems[i].type.match('^image/') ) { const pastedFile = clipboardItems[i].getAsFile(); if (!pastedFile) { return; } const imageUrl = await handleUploadAttachment(pastedFile); if (imageUrl) { editor?.insertBlocks( [ { type: 'image', props: { url: imageUrl, }, }, ], editor?.getTextCursorPosition().block, 'after', ); } } } } }; return ( ); };