Files
omnivore/packages/web/components/patterns/HighlightViewNote.tsx
Jackson Harper 0b04cbef28 Udpated boxes for notes
Needs to be refactored a bit, but using two different components
right now because some of the small differences are hard to
abstract while developing.
2023-03-24 10:18:36 +08:00

294 lines
8.2 KiB
TypeScript

import {
ChangeEvent,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react'
import { formattedShortTime } from '../../lib/dateFormatting'
import { createHighlightMutation } from '../../lib/networking/mutations/createHighlightMutation'
import { updateHighlightMutation } from '../../lib/networking/mutations/updateHighlightMutation'
import { Box, HStack, SpanBox, VStack } from '../elements/LayoutPrimitives'
import MarkdownIt from 'markdown-it'
import MdEditor from 'react-markdown-editor-lite'
import 'react-markdown-editor-lite/lib/index.css'
import ReactMarkdown from 'react-markdown'
import { v4 as uuidv4 } from 'uuid'
import { nanoid } from 'nanoid'
import throttle from 'lodash/throttle'
import { Highlight } from '../../lib/networking/fragments/highlightFragment'
import { StyledText } from '../elements/StyledText'
const mdParser = new MarkdownIt()
type HighlightViewNoteProps = {
placeHolder: string
mode: 'edit' | 'preview'
highlight?: Highlight
sizeMode: 'normal' | 'maximized'
setEditMode: (set: 'edit' | 'preview') => void
}
export function HighlightViewNote(props: HighlightViewNoteProps): JSX.Element {
const [noteText, setNoteText] = useState('')
const [lastSaved, setLastSaved] = useState<Date | undefined>(undefined)
const [lastChanged, setLastChanged] = useState<Date | undefined>(undefined)
const [errorSaving, setErrorSaving] = useState<string | undefined>(undefined)
const [createStartTime, setCreateStartTime] = useState<Date | undefined>(
undefined
)
useEffect(() => {
setNoteText(props.highlight?.annotation ?? '')
}, [props.highlight?.annotation])
const highlightId = useMemo(() => {
console.log(' -- highlightId: ', props.highlight)
if (props.highlight) {
return { id: props.highlight.id, shortId: props.highlight.shortId }
}
return { id: uuidv4(), shortId: nanoid(8) }
}, [props.highlight])
const saveText = useCallback(
(text, updateTime) => {
;(async () => {
console.log('calling save: ', props.highlight)
if (props.highlight) {
const success = await updateHighlightMutation({
annotation: text,
highlightId: props.highlight?.id,
})
if (success) {
setLastSaved(updateTime)
} else {
setErrorSaving('Error saving highlight.')
}
} else {
console.log('creating note highlight: ', highlightId)
if (!createStartTime) {
setCreateStartTime(new Date())
const created = await createHighlightMutation({
type: 'NOTE',
id: highlightId.id,
articleId: props.pageId,
shortId: highlightId.shortId,
annotation: text,
})
console.log('created highlight: ', created)
if (created) {
setLastSaved(updateTime)
// props.dispatchList({
// type: 'CREATE_NOTE',
// highlight: created,
// })
} else {
console.log('unable to create note highlight')
}
}
}
})()
},
[lastSaved, lastChanged, createStartTime]
)
const saveRef = useRef(saveText)
useEffect(() => {
saveRef.current = saveText
}, [lastSaved, lastChanged, createStartTime])
const debouncedSave = useMemo<
(text: string, updateTime: Date) => void
>(() => {
const func = (text: string, updateTime: Date) => {
saveRef.current?.(text, updateTime)
}
return throttle(func, 3000)
}, [])
const handleEditorChange = useCallback(
(
data: { text: string; html: string },
event?: ChangeEvent<HTMLTextAreaElement> | undefined
) => {
if (event) {
event.preventDefault()
}
setNoteText(data.text)
const updateTime = new Date()
setLastChanged(updateTime)
debouncedSave(data.text, updateTime)
},
[lastSaved, lastChanged, createStartTime]
)
console.log(' props: ', props)
return (
<>
{props.mode == 'edit' ? (
<VStack
css={{
width: '100%',
mt: '15px',
'.rc-md-editor': {
borderRadius: '5px',
},
'.rc-md-navigation': {
borderRadius: '5px',
borderBottomLeftRadius: '0px',
borderBottomRightRadius: '0px',
},
'.rc-md-editor .editor-container >.section': {
borderRight: 'unset',
},
'.rc-md-editor .editor-container .sec-md .input': {
padding: '10px',
borderRadius: '5px',
},
}}
>
<MdEditor
value={noteText}
autoFocus={true}
placeholder={props.placeHolder}
view={{ menu: true, md: true, html: false }}
onBlur={() => {
console.log(' BLURRING ')
props.setEditMode('preview')
}}
canView={{
menu: props.mode == 'edit',
md: true,
html: true,
both: false,
fullScreen: false,
hideMenu: false,
}}
plugins={[
'header',
'font-bold',
'font-italic',
'font-underline',
'font-strikethrough',
'list-unordered',
'list-ordered',
'block-quote',
'link',
'auto-resize',
]}
style={{
width: '100%',
height: props.sizeMode == 'normal' ? '160px' : '320px',
}}
renderHTML={(text: string) => mdParser.render(text)}
onChange={handleEditorChange}
/>
<HStack
css={{
minHeight: '15px',
width: '100%',
fontSize: '9px',
mt: '1px',
color: '$thTextSubtle',
}}
alignment="start"
distribution="start"
>
{errorSaving && (
<SpanBox
css={{
width: '100%',
fontSize: '9px',
mt: '1px',
color: 'red',
}}
>
{errorSaving}
</SpanBox>
)}
{lastSaved !== undefined ? (
<>
{lastChanged === lastSaved
? 'Saved'
: `Last saved ${formattedShortTime(lastSaved.toISOString())}`}
</>
) : null}
{/* <SpanBox
css={{
marginLeft: 'auto',
justifyContent: 'end',
lineHeight: '1',
alignSelf: 'end',
padding: '2px',
cursor: 'pointer',
borderRadius: '1000px',
'&:hover': {
background: '#EBEBEB',
},
}}
onClick={(event) => {
props.setEditMode(!editMode)
event.preventDefault()
}}
>
{editMode ? <a href="">Preview</a> : <a href="">Preview</a>}
</SpanBox> */}
</HStack>
</VStack>
) : (
<>
<StyledText
css={{
borderRadius: '5px',
p: '10px',
width: '100%',
marginTop: '15px',
color: '$thHighContrast',
bg: '#F5F5F5',
'> *': {
m: '0px',
},
}}
onClick={() => props.setEditMode('edit')}
>
<ReactMarkdown
children={
props.highlight?.annotation ?? 'Add notes to this highlight...'
}
/>
</StyledText>
</>
)}
</>
)
}
// {!isEditing ? (
// <StyledText
// css={{
// borderRadius: '5px',
// p: '10px',
// width: '100%',
// marginTop: '15px',
// color: '$thHighContrast',
// border: '1px solid #EBEBEB',
// }}
// onClick={() => setIsEditing(true)}
// >
// {props.highlight.annotation
// ? props.highlight.annotation
// : 'Add notes to this highlight...'}
// </StyledText>
// ) : null}
// {isEditing && (