From 2a0244b612faad556fc8c83016ede0a1ccd8c0fd Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 27 Mar 2023 14:53:33 +0800 Subject: [PATCH] Refactor for PDF highlights --- .../templates/article/NotebookModal.tsx | 244 ++---------------- .../templates/article/PdfArticleContainer.tsx | 8 + .../templates/homeFeed/HighlightsLayout.tsx | 7 +- 3 files changed, 28 insertions(+), 231 deletions(-) diff --git a/packages/web/components/templates/article/NotebookModal.tsx b/packages/web/components/templates/article/NotebookModal.tsx index 5a5d1ef92..641e6242a 100644 --- a/packages/web/components/templates/article/NotebookModal.tsx +++ b/packages/web/components/templates/article/NotebookModal.tsx @@ -55,244 +55,38 @@ type AnnotationInfo = { } export function NotebookModal(props: NotebookModalProps): JSX.Element { - const [showConfirmDeleteHighlightId, setShowConfirmDeleteHighlightId] = - useState(undefined) - const [labelsTarget, setLabelsTarget] = useState( - undefined - ) const [sizeMode, setSizeMode] = useState<'normal' | 'maximized'>('normal') const [showConfirmDeleteNote, setShowConfirmDeleteNote] = useState(false) - const [notesEditMode, setNotesEditMode] = useState<'edit' | 'preview'>( - 'preview' + const [allAnnotations, setAllAnnotations] = useState( + undefined ) + const [deletedAnnotations, setDeletedAnnotations] = useState< + Highlight[] | undefined + >(undefined) - const [, updateState] = useState({}) + const handleClose = useCallback(() => { + props.onClose(allAnnotations ?? [], deletedAnnotations ?? []) + }, [allAnnotations, deletedAnnotations]) - const annotationsReducer = ( - state: AnnotationInfo, - action: { - type: string - allHighlights?: Highlight[] - note?: Highlight | undefined - - updateHighlight?: Highlight | undefined - deleteHighlightId?: string | undefined - } - ) => { - switch (action.type) { - case 'RESET': { - console.log(' -- reseting highlights: ', action.allHighlights) - const note = action.allHighlights?.find((h) => h.type == 'NOTE') - return { - ...state, - loaded: true, - note: note, - noteId: note?.id ?? state.noteId, - allAnnotations: [...(action.allHighlights ?? [])], - } - } - case 'CREATE_NOTE': { - if (!action.note) { - throw new Error('No note on CREATE_NOTE action') - } - return { - ...state, - note: action.note, - noteId: action.note.id, - allAnnotations: [...state.allAnnotations, action.note], - } - } - case 'DELETE_NOTE': { - // If there is no note to delete, just make sure we have cleared out the note - const noteId = action.note?.id - if (!action.note?.id) { - return { - ...state, - node: undefined, - noteId: uuidv4(), - } - } - const idx = state.allAnnotations.findIndex((h) => h.id === noteId) - return { - ...state, - note: undefined, - noteId: uuidv4(), - allAnnotations: state.allAnnotations.splice(idx, 1), - } - } - case 'DELETE_HIGHLIGHT': { - const highlightId = action.deleteHighlightId - if (!highlightId) { - throw new Error('No highlightId for delete action.') - } - const idx = state.allAnnotations.findIndex((h) => h.id === highlightId) - if (idx < 0) { - return { ...state } - } - const deleted = state.deletedAnnotations - deleted.push(state.allAnnotations[idx]) - - return { - ...state, - deletedAnnotations: deleted, - allAnnotations: state.allAnnotations.splice(idx, 1), - } - } - case 'UPDATE_HIGHLIGHT': { - const highlight = action.updateHighlight - if (!highlight) { - throw new Error('No highlightId for delete action.') - } - const idx = state.allAnnotations.findIndex((h) => h.id === highlight.id) - if (idx !== -1) { - state.allAnnotations[idx] = highlight - } - return { - ...state, - } - } - default: - return state - } - } - - const [annotations, dispatchAnnotations] = useReducer(annotationsReducer, { - loaded: false, - note: undefined, - noteId: uuidv4(), - allAnnotations: [], - deletedAnnotations: [], - }) - - useEffect(() => { - dispatchAnnotations({ - type: 'RESET', - allHighlights: props.highlights, - }) - }, [props.highlights]) + const handleAnnotationsChange = useCallback( + (allAnnotations, deletedAnnotations) => { + setAllAnnotations(allAnnotations) + setDeletedAnnotations(deletedAnnotations) + }, + [] + ) const exportHighlights = useCallback(() => { ;(async () => { - if (!annotations) { + if (!allAnnotations) { showErrorToast('No highlights to export') return } - const markdown = highlightsAsMarkdown(annotations.allAnnotations) + const markdown = highlightsAsMarkdown(allAnnotations) await navigator.clipboard.writeText(markdown) showSuccessToast('Highlight copied') })() - }, [annotations]) - - const deleteDocumentNote = useCallback(() => { - const note = annotations.note - if (!note) { - showErrorToast('No note found') - return - } - ;(async () => { - try { - const result = await deleteHighlightMutation(note.id) - if (!result) { - throw new Error() - } - showSuccessToast('Note deleted') - dispatchAnnotations({ - note, - type: 'DELETE_NOTE', - }) - } catch (err) { - console.log('error deleting note', err) - showErrorToast('Error deleting note') - } - })() - }, [annotations]) - - const sortedHighlights = useMemo(() => { - const sorted = (a: number, b: number) => { - if (a < b) { - return -1 - } - if (a > b) { - return 1 - } - return 0 - } - - return annotations.allAnnotations - .filter((h) => h.type === 'HIGHLIGHT') - .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) - }) - }, [annotations]) - - const handleSaveNoteText = useCallback( - (text, cb: (success: boolean) => void) => { - if (!annotations.loaded) { - // We haven't loaded the user's annotations yet, so we can't - // find or create their highlight note. - return - } - - if (!annotations.note) { - const noteId = annotations.noteId - ;(async () => { - const success = await createHighlightMutation({ - id: noteId, - shortId: nanoid(8), - type: 'NOTE', - articleId: props.pageId, - annotation: text, - }) - console.log('success creating annotation note: ', success) - if (success) { - dispatchAnnotations({ - type: 'CREATE_NOTE', - note: success, - }) - } - cb(!!success) - })() - return - } - - if (annotations.note) { - const note = annotations.note - ;(async () => { - const success = await updateHighlightMutation({ - highlightId: note.id, - annotation: text, - }) - console.log('success updating annotation note: ', success) - if (success) { - note.annotation = text - dispatchAnnotations({ - type: 'UPDATE_NOTE', - note: note, - }) - } - cb(!!success) - })() - return - } - }, - [annotations, props.pageId] - ) - - const handleClose = useCallback(() => { - props.onClose(annotations.allAnnotations, annotations.deletedAnnotations) - }, [annotations]) + }, [allAnnotations]) return ( @@ -352,7 +146,7 @@ export function NotebookModal(props: NotebookModalProps): JSX.Element { - + ) diff --git a/packages/web/components/templates/article/PdfArticleContainer.tsx b/packages/web/components/templates/article/PdfArticleContainer.tsx index 93730bc34..256220c97 100644 --- a/packages/web/components/templates/article/PdfArticleContainer.tsx +++ b/packages/web/components/templates/article/PdfArticleContainer.tsx @@ -424,6 +424,7 @@ export default function PdfArticleContainer( document.addEventListener('deleteHighlightbyId', async (event) => { const annotationId = (event as CustomEvent).detail as string + console.log(' DELETING ANNOTATION BY ID: ', annotationId) for (let pageIdx = 0; pageIdx < instance.totalPageCount; pageIdx++) { const annotations = await instance.getAnnotations(pageIdx) for (let annIdx = 0; annIdx < annotations.size; annIdx++) { @@ -432,6 +433,7 @@ export default function PdfArticleContainer( continue } const storedId = annotationOmnivoreId(annotation) + console.log(' --- storedId:', storedId) if (storedId == annotationId) { await instance.delete(annotation) await deleteHighlightMutation(annotationId) @@ -501,6 +503,12 @@ export default function PdfArticleContainer( updatedHighlights, deletedAnnotations ) + deletedAnnotations.forEach((highlight) => { + const event = new CustomEvent('deleteHighlightbyId', { + detail: highlight.id, + }) + document.dispatchEvent(event) + }) props.setShowHighlightsModal(false) }} /> diff --git a/packages/web/components/templates/homeFeed/HighlightsLayout.tsx b/packages/web/components/templates/homeFeed/HighlightsLayout.tsx index 6e95867c9..5f727a517 100644 --- a/packages/web/components/templates/homeFeed/HighlightsLayout.tsx +++ b/packages/web/components/templates/homeFeed/HighlightsLayout.tsx @@ -403,6 +403,7 @@ function HighlightList(props: HighlightListProps): JSX.Element { width: '100%', color: 'thTextContrast2', m: '0px', + pb: '5px', }} > NOTEBOOK @@ -420,12 +421,6 @@ function HighlightList(props: HighlightListProps): JSX.Element { { - console.log('closing notebook: ', highlights, deletedAnnotations) - }} />