import { ModalRoot, ModalOverlay, ModalContent, ModalTitleBar, } from '../../elements/ModalPrimitives' import { Box, HStack, VStack, SpanBox } from '../../elements/LayoutPrimitives' import { Button } from '../../elements/Button' import { StyledText } from '../../elements/StyledText' import { TrashIcon } from '../../elements/images/TrashIcon' import { theme } from '../../tokens/stitches.config' import type { Highlight } from '../../../lib/networking/fragments/highlightFragment' import { HighlightView } from '../../patterns/HighlightView' import { useCallback, useMemo, useState } from 'react' import { StyledTextArea } from '../../elements/StyledTextArea' import { ConfirmationModal } from '../../patterns/ConfirmationModal' import { DotsThree } from 'phosphor-react' import { Dropdown, DropdownOption } from '../../elements/DropdownElements' import { SetLabelsModal } from './SetLabelsModal' import { Label } from '../../../lib/networking/fragments/labelFragment' import { setLabelsForHighlight } from '../../../lib/networking/mutations/setLabelsForHighlight' import { updateHighlightMutation } from '../../../lib/networking/mutations/updateHighlightMutation' import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers' import { diff_match_patch } from 'diff-match-patch' import { HighlightNoteTextEditArea } from '../../elements/HighlightNoteTextEditArea' type NotebookModalProps = { highlights: Highlight[] scrollToHighlight?: (arg: string) => void updateHighlight: (highlight: Highlight) => void deleteHighlightAction?: (highlightId: string) => void onOpenChange: (open: boolean) => void } export const getHighlightLocation = (patch: string): number | undefined => { const dmp = new diff_match_patch() const patches = dmp.patch_fromText(patch) return patches[0].start1 || undefined } export function NotebookModal(props: NotebookModalProps): JSX.Element { const [showConfirmDeleteHighlightId, setShowConfirmDeleteHighlightId] = useState(undefined) const [labelsTarget, setLabelsTarget] = useState( undefined ) const [, updateState] = useState({}) const sortedHighlights = useMemo(() => { const sorted = (a: number, b: number) => { if (a < b) { return -1 } if (a > b) { return 1 } return 0 } return props.highlights.sort((a: Highlight, b: Highlight) => { if (a.highlightPositionPercent && b.highlightPositionPercent) { return sorted(a.highlightPositionPercent, b.highlightPositionPercent) } // We do this in a try/catch because it might be an invalid diff // With PDF it will definitely be an invalid diff. try { const aPos = getHighlightLocation(a.patch) const bPos = getHighlightLocation(b.patch) if (aPos && bPos) { return sorted(aPos, bPos) } } catch {} return a.createdAt.localeCompare(b.createdAt) }) }, [props.highlights]) return ( { event.preventDefault() props.onOpenChange(false) }} css={{ overflow: 'auto', px: '24px' }} > {sortedHighlights.map((highlight) => ( { if (props.deleteHighlightAction) { props.deleteHighlightAction(highlight.id) } }} updateHighlight={props.updateHighlight} /> ))} {sortedHighlights.length === 0 && ( You have not added any highlights or notes to this document )} {showConfirmDeleteHighlightId && ( { if (props.deleteHighlightAction) { props.deleteHighlightAction(showConfirmDeleteHighlightId) } setShowConfirmDeleteHighlightId(undefined) }} onOpenChange={() => setShowConfirmDeleteHighlightId(undefined)} icon={ } /> )} {labelsTarget && ( { const result = setLabelsForHighlight( labelsTarget.id, labels.map((label) => label.id) ) return result }} /> )} ) } type ModalHighlightViewProps = { highlight: Highlight showDelete: boolean scrollToHighlight?: (arg: string) => void deleteHighlightAction: () => void updateHighlight: (highlight: Highlight) => void setSetLabelsTarget: (highlight: Highlight) => void setShowConfirmDeleteHighlightId: (id: string | undefined) => void } function ModalHighlightView(props: ModalHighlightViewProps): JSX.Element { const [isEditing, setIsEditing] = useState(false) const copyHighlight = useCallback(async () => { await navigator.clipboard.writeText(props.highlight.quote) }, [props.highlight]) return ( <> } > { await copyHighlight() }} title="Copy" /> { props.setSetLabelsTarget(props.highlight) }} title="Labels" /> { props.setShowConfirmDeleteHighlightId(props.highlight.id) }} title="Delete" /> {!isEditing ? ( setIsEditing(true)} > {props.highlight.annotation ? props.highlight.annotation : 'Add your notes...'} ) : null} {isEditing && ( )} ) }