/* eslint-disable react/no-children-prop */ import { ChangeEvent, useCallback, useEffect, useMemo, useRef, useState, } from 'react' import { formattedShortTime } from '../../lib/dateFormatting' import { Box, HStack, SpanBox, VStack } from '../elements/LayoutPrimitives' import MarkdownIt from 'markdown-it' import MdEditor, { Plugins } from 'react-markdown-editor-lite' import 'react-markdown-editor-lite/lib/index.css' import ReactMarkdown from 'react-markdown' import throttle from 'lodash/throttle' import { updateHighlightMutation } from '../../lib/networking/mutations/updateHighlightMutation' import { Highlight } from '../../lib/networking/fragments/highlightFragment' import { Button } from '../elements/Button' import remarkGfm from 'remark-gfm' import { RcEditorStyles } from './RcEditorStyles' import { isDarkTheme } from '../../lib/themeUpdater' import { showErrorToast, showSuccessToast } from '../../lib/toastHelpers' const mdParser = new MarkdownIt() MdEditor.use(Plugins.TabInsert, { tabMapValue: 1, // note that 1 means a '\t' instead of ' '. }) type HighlightViewNoteProps = { targetId: string placeHolder: string mode: 'edit' | 'preview' highlight: Highlight setEditMode: (set: 'edit' | 'preview') => void text: string | undefined updateHighlight: (highlight: Highlight) => void } export function HighlightViewNote(props: HighlightViewNoteProps): JSX.Element { const [lastSaved, setLastSaved] = useState(undefined) const [errorSaving, setErrorSaving] = useState(undefined) const saveText = useCallback( (text: string, updateTime: Date, interactive: boolean) => { ;(async () => { console.log('updating highlight text') const success = await updateHighlightMutation({ annotation: text, libraryItemId: props.targetId, highlightId: props.highlight?.id, }) if (success) { setLastSaved(updateTime) props.highlight.annotation = text props.updateHighlight(props.highlight) if (interactive) { showSuccessToast('Note saved', { position: 'bottom-right', }) } } else { setErrorSaving('Error saving note.') } })() }, [props] ) return ( ) } type MarkdownNote = { targetId: string placeHolder: string mode: 'edit' | 'preview' setEditMode: (set: 'edit' | 'preview') => void text: string | undefined fillBackground: boolean | undefined lastSaved: Date | undefined errorSaving: string | undefined saveText: (text: string, updateTime: Date, interactive: boolean) => void } export function MarkdownNote(props: MarkdownNote): JSX.Element { const editorRef = useRef(null) const isDark = isDarkTheme() const [lastChanged, setLastChanged] = useState(undefined) const saveRef = useRef(props.saveText) useEffect(() => { saveRef.current = props.saveText }, [props]) const debouncedSave = useMemo< (text: string, updateTime: Date) => void >(() => { const func = (text: string, updateTime: Date) => { saveRef.current?.(text, updateTime, false) } return throttle(func, 3000) }, []) const handleEditorChange = useCallback( ( data: { text: string; html: string }, event?: ChangeEvent | undefined ) => { if (event) { event.preventDefault() } const updateTime = new Date() setLastChanged(updateTime) debouncedSave(data.text, updateTime) }, [] ) return ( <> {props.mode == 'edit' ? ( ) => { if (event.code.toLowerCase() === 'escape') { props.setEditMode('preview') event.preventDefault() event.stopPropagation() } }} > mdParser.render(text)} onChange={handleEditorChange} /> {props.errorSaving && ( {props.errorSaving} )} {props.lastSaved !== undefined ? ( <> {lastChanged === props.lastSaved ? 'Saved' : `Last saved ${formattedShortTime( props.lastSaved.toISOString() )}`} ) : null} ) : ( <> *': { m: '0px', }, }} onClick={() => props.setEditMode('edit')} > )} ) }