From fca3a4c2ea354cd05afefac643b5281291cd3e5a Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Fri, 23 Jun 2023 17:11:29 +0800 Subject: [PATCH] Labels update --- .../web/components/elements/LabelChip.tsx | 146 +++++----- .../components/templates/article/Notebook.tsx | 263 ++++++------------ .../networking/queries/useGetArticleQuery.tsx | 6 +- 3 files changed, 156 insertions(+), 259 deletions(-) diff --git a/packages/web/components/elements/LabelChip.tsx b/packages/web/components/elements/LabelChip.tsx index 22b10f510..ee4544b8c 100644 --- a/packages/web/components/elements/LabelChip.tsx +++ b/packages/web/components/elements/LabelChip.tsx @@ -23,81 +23,81 @@ export function LabelChip(props: LabelChipProps): JSX.Element { const selectedBorder = isDark ? '#FFEA9F' : 'black' const unSelectedBorder = isDark ? '#6A6968' : '#D9D9D9' - if (props.useAppAppearance) { - return ( - - - - {props.text} - {props.xAction && ( - - )} - - - ) - } - + // if (props.useAppAppearance) { return ( - + + + {props.text} + {props.xAction && ( + + )} + + ) + // } + + // return ( + // + // ) } diff --git a/packages/web/components/templates/article/Notebook.tsx b/packages/web/components/templates/article/Notebook.tsx index d17596eee..775fdcd11 100644 --- a/packages/web/components/templates/article/Notebook.tsx +++ b/packages/web/components/templates/article/Notebook.tsx @@ -2,7 +2,14 @@ import { Box, HStack, VStack, SpanBox } from '../../elements/LayoutPrimitives' import { StyledText } from '../../elements/StyledText' import { theme } from '../../tokens/stitches.config' import type { Highlight } from '../../../lib/networking/fragments/highlightFragment' -import { useCallback, useEffect, useMemo, useReducer, useState } from 'react' +import { + useCallback, + useEffect, + useMemo, + useReducer, + useRef, + useState, +} from 'react' import { BookOpen, CaretDown, @@ -50,14 +57,10 @@ export const getHighlightLocation = (patch: string): number | undefined => { return patches[0].start1 || undefined } -type AnnotationInfo = { - loaded: boolean - +type NoteState = { + isCreating: boolean note: Highlight | undefined - noteId: string - creatingNote: boolean - - allAnnotations: Highlight[] + createStarted: Date | undefined } export function NotebookContent(props: NotebookContentProps): JSX.Element { @@ -74,190 +77,59 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { const [notesEditMode, setNotesEditMode] = useState<'edit' | 'preview'>( 'preview' ) - - // const annotationsReducer = ( - // state: AnnotationInfo, - // action: { - // type: string - // allHighlights?: Highlight[] - // note?: Highlight | undefined - - // updateHighlight?: Highlight | undefined - // deleteHighlightId?: string | undefined - // } - // ) => { - // switch (action.type) { - // case 'RESET': { - // 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, - // creatingNote: false, - // allAnnotations: [...state.allAnnotations, action.note], - // } - // } - // case 'CREATING_NOTE': { - // return { - // ...state, - // creatingNote: true, - // } - // } - // 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, - // creatingNote: false, - // noteId: uuidv4(), - // allAnnotations: [], - // deletedAnnotations: [], - // }) - - // useEffect(() => { - // dispatchAnnotations({ - // type: 'RESET', - // allHighlights: props.highlights, - // }) - // }, [props.highlights]) - - // 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 noteReducer = ( - state: { - note?: Highlight - isCreating: boolean - }, - action: { - type: string - note?: Highlight - } - ) => { - switch (action.type) { - case 'SET_NOTE': { - if (!action.note) { - console.error( - 'invalidate SET_NOTE action, no note provider', - action, - state - ) - } - return { - ...state, - note: action.note, - } - } - } - return state - } - - const [noteState, dispatchNote] = useReducer(noteReducer, { - note: undefined, + const noteState = useRef({ isCreating: false, + note: undefined, + createStarted: undefined, }) + const newNoteId = useMemo(() => { + return uuidv4() + }, []) + + const updateNote = useCallback((note: Highlight, text: string) => { + ;(async () => { + const result = await updateHighlightMutation({ + highlightId: note.id, + annotation: text, + }) + })() + }, []) + + const createNote = useCallback((text: string) => { + console.log('creating note: ', newNoteId, noteState.current.isCreating) + noteState.current.isCreating = true + noteState.current.createStarted = new Date() + ;(async () => { + try { + const success = await createHighlightMutation({ + id: newNoteId, + shortId: nanoid(8), + type: 'NOTE', + articleId: props.item.id, + annotation: text, + }) + if (success) { + noteState.current.note = success + noteState.current.isCreating = false + } + } catch (error) { + console.error('error creating note: ', error) + noteState.current.isCreating = false + } + })() + }, []) + const highlights = useMemo(() => { const result = articleData?.article.article.highlights const note = result?.find((h) => h.type === 'NOTE') if (note) { - dispatchNote({ - type: 'SET_NOTE', - note: note, - }) + noteState.current.note = note + noteState.current.isCreating = false } return result }, [articleData]) - // const note = useMemo(() => { - // return highlights?.find((h) => h.type === 'NOTE') - // }, [highlights]) - useEffect(() => { if (highlights && props.onAnnotationsChanged) { props.onAnnotationsChanged(highlights) @@ -295,8 +167,30 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { }, [highlights]) const handleSaveNoteText = useCallback( - (text, cb: (success: boolean) => void) => {}, - [highlights, props.item] + (text, cb: (success: boolean) => void) => { + console.log('handleSaveNoteText', noteState.current) + if (noteState.current.note) { + updateNote(noteState.current.note, text) + return + } + if (noteState.current.isCreating) { + console.log('note is being created, deferring') + + if (noteState.current.createStarted) { + const timeSinceStart = + new Date().getTime() - noteState.current.createStarted.getTime() + console.log(' -- timeSinceStart: ', timeSinceStart) + + if (timeSinceStart > 4000) { + createNote(text) + return + } + } + return + } + createNote(text) + }, + [noteState, createNote, updateNote] ) const [articleNotesCollapsed, setArticleNotesCollapsed] = useState(false) @@ -310,7 +204,6 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { width: '100%', p: '20px', '@mdDown': { p: '15px' }, - background: '#F8FAFB', }} > diff --git a/packages/web/lib/networking/queries/useGetArticleQuery.tsx b/packages/web/lib/networking/queries/useGetArticleQuery.tsx index 0924f6e74..f9f9df7cf 100644 --- a/packages/web/lib/networking/queries/useGetArticleQuery.tsx +++ b/packages/web/lib/networking/queries/useGetArticleQuery.tsx @@ -14,6 +14,7 @@ import { Recommendation, recommendationFragment, } from './useGetLibraryItemsQuery' +import useSWR from 'swr' type ArticleQueryInput = { username?: string @@ -25,6 +26,8 @@ type ArticleQueryOutput = { articleData?: ArticleData isLoading: boolean articleFetchError: string[] | null + + mutate: () => void } type ArticleData = { @@ -107,7 +110,7 @@ export function useGetArticleQuery({ includeFriendsHighlights, } - const { data, error } = useSWRImmutable( + const { data, error, mutate } = useSWR( slug ? [query, username, slug, includeFriendsHighlights] : null, makeGqlFetcher(variables) ) @@ -124,6 +127,7 @@ export function useGetArticleQuery({ } return { + mutate: mutate, articleData: resultData, isLoading: !error && !data, articleFetchError: resultError ? (resultError as string[]) : null,