Labels update
This commit is contained in:
@ -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 (
|
||||
<SpanBox
|
||||
css={{
|
||||
display: 'inline-table',
|
||||
margin: '2px',
|
||||
fontSize: '11px',
|
||||
fontWeight: '500',
|
||||
fontFamily: '$inter',
|
||||
padding: '4px 10px',
|
||||
whiteSpace: 'nowrap',
|
||||
cursor: 'pointer',
|
||||
backgroundClip: 'padding-box',
|
||||
borderRadius: '5px',
|
||||
borderWidth: '1px',
|
||||
borderStyle: 'solid',
|
||||
color: isDark ? '#EBEBEB' : '#2A2A2A',
|
||||
borderColor: props.isSelected ? selectedBorder : unSelectedBorder,
|
||||
backgroundColor: isDark ? '#2A2A2A' : '#F5F5F5',
|
||||
}}
|
||||
>
|
||||
<HStack alignment="center" css={{ gap: '10px' }}>
|
||||
<Circle size={14} color={props.color} weight="fill" />
|
||||
<SpanBox css={{ pt: '1px' }}>{props.text}</SpanBox>
|
||||
{props.xAction && (
|
||||
<Button
|
||||
style="ghost"
|
||||
css={{ display: 'flex', pt: '1px' }}
|
||||
onClick={(event) => {
|
||||
if (props.xAction) {
|
||||
props.xAction()
|
||||
event.preventDefault()
|
||||
}
|
||||
}}
|
||||
>
|
||||
<X
|
||||
size={14}
|
||||
color={
|
||||
props.isSelected
|
||||
? '#FFEA9F'
|
||||
: theme.colors.thBorderSubtle.toString()
|
||||
}
|
||||
/>
|
||||
</Button>
|
||||
)}
|
||||
</HStack>
|
||||
</SpanBox>
|
||||
)
|
||||
}
|
||||
|
||||
// if (props.useAppAppearance) {
|
||||
return (
|
||||
<Button
|
||||
style="plainIcon"
|
||||
onClick={(e) => {
|
||||
router.push(`/home?q=label:"${props.text}"`)
|
||||
e.stopPropagation()
|
||||
<SpanBox
|
||||
css={{
|
||||
display: 'inline-table',
|
||||
margin: '2px',
|
||||
fontSize: '11px',
|
||||
fontWeight: '500',
|
||||
fontFamily: '$inter',
|
||||
padding: '1px 7px',
|
||||
whiteSpace: 'nowrap',
|
||||
cursor: 'pointer',
|
||||
backgroundClip: 'padding-box',
|
||||
borderRadius: '5px',
|
||||
borderWidth: '1px',
|
||||
borderStyle: 'solid',
|
||||
color: isDark ? '#EBEBEB' : '#2A2A2A',
|
||||
borderColor: props.isSelected ? selectedBorder : unSelectedBorder,
|
||||
backgroundColor: isDark ? '#2A2A2A' : '#F5F5F5',
|
||||
}}
|
||||
>
|
||||
<SpanBox
|
||||
css={{
|
||||
display: 'inline-table',
|
||||
margin: '2px',
|
||||
borderRadius: '4px',
|
||||
color: textColor,
|
||||
fontSize: '13px',
|
||||
fontWeight: '500',
|
||||
padding: '3px 6px',
|
||||
whiteSpace: 'nowrap',
|
||||
cursor: 'pointer',
|
||||
backgroundClip: 'padding-box',
|
||||
backgroundColor: props.color,
|
||||
}}
|
||||
>
|
||||
{props.text}
|
||||
</SpanBox>
|
||||
</Button>
|
||||
<HStack alignment="center" css={{ gap: '10px' }}>
|
||||
<Circle size={14} color={props.color} weight="fill" />
|
||||
<SpanBox css={{ pt: '1px' }}>{props.text}</SpanBox>
|
||||
{props.xAction && (
|
||||
<Button
|
||||
style="ghost"
|
||||
css={{ display: 'flex', pt: '1px' }}
|
||||
onClick={(event) => {
|
||||
if (props.xAction) {
|
||||
props.xAction()
|
||||
event.preventDefault()
|
||||
}
|
||||
}}
|
||||
>
|
||||
<X
|
||||
size={14}
|
||||
color={
|
||||
props.isSelected
|
||||
? '#FFEA9F'
|
||||
: theme.colors.thBorderSubtle.toString()
|
||||
}
|
||||
/>
|
||||
</Button>
|
||||
)}
|
||||
</HStack>
|
||||
</SpanBox>
|
||||
)
|
||||
// }
|
||||
|
||||
// return (
|
||||
// <Button
|
||||
// style="plainIcon"
|
||||
// onClick={(e) => {
|
||||
// router.push(`/home?q=label:"${props.text}"`)
|
||||
// e.stopPropagation()
|
||||
// }}
|
||||
// >
|
||||
// <SpanBox
|
||||
// css={{
|
||||
// display: 'inline-table',
|
||||
// margin: '2px',
|
||||
// borderRadius: '4px',
|
||||
// color: textColor,
|
||||
// fontSize: '13px',
|
||||
// fontWeight: '500',
|
||||
// padding: '3px 6px',
|
||||
// whiteSpace: 'nowrap',
|
||||
// cursor: 'pointer',
|
||||
// backgroundClip: 'padding-box',
|
||||
// backgroundColor: props.color,
|
||||
// }}
|
||||
// >
|
||||
// {props.text}
|
||||
// </SpanBox>
|
||||
// </Button>
|
||||
// )
|
||||
}
|
||||
|
||||
@ -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<NoteState>({
|
||||
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',
|
||||
}}
|
||||
>
|
||||
<SectionTitle
|
||||
@ -328,7 +221,7 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element {
|
||||
mode={notesEditMode}
|
||||
targetId={props.item.id}
|
||||
setEditMode={setNotesEditMode}
|
||||
text={noteState.note?.annotation}
|
||||
text={noteState.current.note?.annotation}
|
||||
placeHolder="Add notes to this document..."
|
||||
saveText={handleSaveNoteText}
|
||||
/>
|
||||
|
||||
@ -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,
|
||||
|
||||
Reference in New Issue
Block a user