Use same notebook across layouts

This commit is contained in:
Jackson Harper
2023-03-27 14:04:34 +08:00
parent 040e446167
commit bf768293aa
4 changed files with 260 additions and 351 deletions

View File

@ -17,6 +17,7 @@ import ReactMarkdown from 'react-markdown'
import throttle from 'lodash/throttle'
import { updateHighlightMutation } from '../../lib/networking/mutations/updateHighlightMutation'
import { Highlight } from '../../lib/networking/fragments/highlightFragment'
import { Button } from '../elements/Button'
const mdParser = new MarkdownIt()
@ -118,6 +119,7 @@ type MarkdownNote = {
}
export function MarkdownNote(props: MarkdownNote): JSX.Element {
const editorRef = useRef<MdEditor | null>(null)
const [lastChanged, setLastChanged] = useState<Date | undefined>(undefined)
const [errorSaving, setErrorSaving] = useState<string | undefined>(undefined)
@ -183,6 +185,7 @@ export function MarkdownNote(props: MarkdownNote): JSX.Element {
}}
>
<MdEditor
ref={editorRef}
autoFocus={true}
defaultValue={props.text}
placeholder={props.placeHolder}
@ -220,7 +223,6 @@ export function MarkdownNote(props: MarkdownNote): JSX.Element {
width: '100%',
fontSize: '9px',
mt: '1px',
bg: 'red',
color: '$thTextSubtle',
}}
alignment="start"
@ -247,17 +249,37 @@ export function MarkdownNote(props: MarkdownNote): JSX.Element {
)}`}
</>
) : null}
<SpanBox
css={{
width: '100%',
fontSize: '9px',
mt: '1px',
color: 'red',
marginLeft: 'auto',
}}
>
Save
</SpanBox>
{lastChanged !== props.lastSaved && (
<SpanBox
css={{
fontSize: '9px',
mt: '1px',
color: 'green',
marginLeft: 'auto',
}}
>
<Button
css={{
textDecoration: 'underline',
border: 'unset',
background: 'unset',
'&:hover': {
border: 'unset',
background: 'unset',
},
}}
onClick={(event) => {
const value = editorRef.current?.getMdValue()
if (value) {
props.saveText(value, new Date())
}
event.preventDefault()
}}
>
Save
</Button>
</SpanBox>
)}
</HStack>
</VStack>
) : (

View File

@ -1,3 +1,4 @@
import { BookOpen, PencilLine } from 'phosphor-react'
import { Fragment, useMemo, useState } from 'react'
import type { Highlight } from '../../lib/networking/fragments/highlightFragment'
import { LabelChip } from '../elements/LabelChip'
@ -63,7 +64,7 @@ export function HighlightView(props: HighlightViewProps): JSX.Element {
}}
/>
</VStack>
<VStack css={{ width: '100%', padding: '0px 15px' }}>
<VStack css={{ width: '100%', padding: '0px', paddingLeft: '15px' }}>
<StyledQuote
onClick={() => {
if (props.scrollToHighlight) {
@ -95,15 +96,43 @@ export function HighlightView(props: HighlightViewProps): JSX.Element {
<LabelChip key={index} text={name || ''} color={color} />
))}
</Box>
<HighlightViewNote
text={props.highlight.annotation}
placeHolder="Add notes to this highlight..."
highlight={props.highlight}
sizeMode={'normal'}
mode={noteMode}
setEditMode={setNoteMode}
updateHighlight={props.updateHighlight}
/>
<HStack
css={{ width: '100%', height: '100%' }}
alignment="start"
distribution="start"
>
<HighlightViewNote
text={props.highlight.annotation}
placeHolder="Add notes to this highlight..."
highlight={props.highlight}
sizeMode={'normal'}
mode={noteMode}
setEditMode={setNoteMode}
updateHighlight={props.updateHighlight}
/>
<SpanBox
css={{
lineHeight: '1',
marginLeft: '20px',
marginTop: '20px',
cursor: 'pointer',
borderRadius: '1000px',
'&:hover': {
background: '#EBEBEB',
},
}}
onClick={(event) => {
setNoteMode(noteMode == 'preview' ? 'edit' : 'preview')
event.preventDefault()
}}
>
{noteMode === 'edit' ? (
<BookOpen size={15} color="#898989" />
) : (
<PencilLine size={15} color="#898989" />
)}
</SpanBox>
</HStack>
</VStack>
</HStack>
)

View File

@ -29,6 +29,7 @@ import { nanoid } from 'nanoid'
import { deleteHighlightMutation } from '../../../lib/networking/mutations/deleteHighlightMutation'
import { HighlightNoteBox } from '../../patterns/HighlightNotes'
import { HighlightViewItem } from './HighlightViewItem'
import { Notebook } from './Notebook'
type NotebookModalProps = {
pageId: string
@ -351,196 +352,12 @@ export function NotebookModal(props: NotebookModalProps): JSX.Element {
<CloseButton close={handleClose} />
</HStack>
</HStack>
<VStack distribution="start" css={{ height: '100%', p: '20px' }}>
<TitledSection
title="ARTICLE NOTES"
editMode={notesEditMode == 'edit'}
setEditMode={(edit) => setNotesEditMode(edit ? 'edit' : 'preview')}
/>
<HighlightNoteBox
sizeMode={sizeMode}
mode={notesEditMode}
setEditMode={setNotesEditMode}
text={annotations.note?.annotation}
placeHolder="Add notes to this document..."
saveText={handleSaveNoteText}
/>
<SpanBox css={{ mt: '10px', mb: '25px' }} />
{/* {annotations.allAnnotations.map((highlight) => (
<Box key={`hn-${highlight.id}`} css={{ color: 'black' }}>
{highlight.annotation}
<Button
onClick={() => {
deleteHighlightMutation(highlight.id)
}}
>
DELETE
</Button>
</Box>
))} */}
<Box css={{ width: '100%' }}>
<TitledSection title="HIGHLIGHTS" />
{sortedHighlights.map((highlight) => (
<HighlightViewItem
key={highlight.id}
highlight={highlight}
scrollToHighlight={props.scrollToHighlight}
setSetLabelsTarget={setLabelsTarget}
setShowConfirmDeleteHighlightId={
setShowConfirmDeleteHighlightId
}
deleteHighlightAction={() => {
dispatchAnnotations({
type: 'DELETE_HIGHLIGHT',
deleteHighlightId: highlight.id,
})
}}
updateHighlight={() => {
dispatchAnnotations({
type: 'UPDATE_HIGHLIGHT',
updateHighlight: highlight,
})
}}
/>
))}
{sortedHighlights.length === 0 && (
<Box
css={{
mt: '15px',
width: '100%',
fontSize: '9px',
color: '$thTextSubtle',
alignItems: 'center',
justifyContent: 'center',
mb: '100px',
}}
>
You have not added any highlights to this document.
</Box>
)}
</Box>
</VStack>
<Notebook {...props} />
</ModalContent>
{showConfirmDeleteHighlightId && (
<ConfirmationModal
message={'Are you sure you want to delete this highlight?'}
onAccept={() => {
;(async () => {
const success = await deleteHighlightMutation(
showConfirmDeleteHighlightId
)
if (success) {
dispatchAnnotations({
type: 'DELETE_HIGHLIGHT',
deleteHighlightId: showConfirmDeleteHighlightId,
})
showSuccessToast('Highlight deleted.')
} else {
showErrorToast('Error deleting highlight')
}
})()
setShowConfirmDeleteHighlightId(undefined)
}}
onOpenChange={() => setShowConfirmDeleteHighlightId(undefined)}
icon={
<TrashIcon
size={40}
strokeColor={theme.colors.grayTextContrast.toString()}
/>
}
/>
)}
{labelsTarget && (
<SetLabelsModal
provider={labelsTarget}
onOpenChange={function (open: boolean): void {
setLabelsTarget(undefined)
}}
onLabelsUpdated={function (labels: Label[]): void {
updateState({})
}}
save={function (labels: Label[]): Promise<Label[] | undefined> {
const result = setLabelsForHighlight(
labelsTarget.id,
labels.map((label) => label.id)
)
return result
}}
/>
)}
{showConfirmDeleteNote && (
<ConfirmationModal
message="Are you sure you want to delete the note from this document?"
acceptButtonLabel="Delete"
onAccept={() => {
deleteDocumentNote()
setShowConfirmDeleteNote(false)
}}
onOpenChange={() => setShowConfirmDeleteNote(false)}
/>
)}
</ModalRoot>
)
}
type TitledSectionProps = {
title: string
editMode?: boolean
setEditMode?: (set: boolean) => void
}
function TitledSection(props: TitledSectionProps): JSX.Element {
return (
<>
<HStack css={{ width: '100%' }} alignment="start" distribution="start">
<StyledText
css={{
fontFamily: '$display',
fontStyle: 'normal',
fontWeight: '700',
fontSize: '12px',
lineHeight: '20px',
color: '#898989',
marginBottom: '1px',
}}
>
{props.title}
</StyledText>
{props.setEditMode && (
<SpanBox
css={{
marginLeft: 'auto',
justifyContent: 'end',
lineHeight: '1',
alignSelf: 'end',
padding: '2px',
cursor: 'pointer',
borderRadius: '1000px',
'&:hover': {
background: '#EBEBEB',
},
}}
onClick={(event) => {
if (props.setEditMode) {
props.setEditMode(!props.editMode)
}
event.preventDefault()
}}
>
{props.editMode ? (
<BookOpen size={15} color="#898989" />
) : (
<PencilLine size={15} color="#898989" />
)}
</SpanBox>
)}
</HStack>
<Box css={{ width: '100%', height: '1px', bg: '#EBEBEB', mb: '10px' }} />
</>
)
}
type SizeToggleProps = {
mode: 'normal' | 'maximized'
setMode: (mode: 'normal' | 'maximized') => void

View File

@ -21,6 +21,7 @@ import {
} from '../../patterns/LibraryCards/LibraryCardStyles'
import { LibraryHighlightGridCard } from '../../patterns/LibraryCards/LibraryHighlightGridCard'
import { HighlightViewItem } from '../article/HighlightViewItem'
import { Notebook } from '../article/Notebook'
import { EmptyHighlights } from './EmptyHighlights'
import { HEADER_HEIGHT, MOBILE_HEADER_HEIGHT } from './HeaderSpacer'
import { highlightsAsMarkdown } from './HighlightItem'
@ -188,28 +189,19 @@ export function HighlightItemsLayout(
height: '100%',
width: '100%',
flexGrow: '1',
justifyContent: 'center',
overflowY: 'scroll',
'@lgDown': {
display: 'none',
flexGrow: 'unset',
},
}}
>
<HStack
css={{
flexGrow: '1',
overflowY: 'scroll',
height: '100%',
width: '100%',
}}
distribution="start"
alignment="start"
>
<HighlightList
item={currentItem}
viewer={props.viewer}
deleteHighlight={handleDelete}
/>
</HStack>
<HighlightList
item={currentItem}
viewer={props.viewer}
deleteHighlight={handleDelete}
/>
</SpanBox>
</>
)}
@ -369,9 +361,6 @@ type HighlightListProps = {
}
function HighlightList(props: HighlightListProps): JSX.Element {
const [notesEditMode, setNotesEditMode] = useState<'preview' | 'edit'>(
'preview'
)
const exportHighlights = useCallback(() => {
;(async () => {
if (!props.item.node.highlights) {
@ -384,149 +373,201 @@ function HighlightList(props: HighlightListProps): JSX.Element {
})()
}, [props.item.node.highlights])
const note = useMemo(() => {
const note = (props.item.node.highlights ?? []).find(
(h) => h.type === 'NOTE'
)
console.log('NOTE: ', note)
return note
}, [props.item])
const sortedHighlights = useMemo(() => {
return (props.item.node.highlights ?? []).filter(
(h) => h.type === 'HIGHLIGHT'
)
}, [props.item])
return (
<HStack
<VStack
css={{
m: '20px',
height: '100%',
flexGrow: '1',
height: '100%',
minWidth: '425px',
maxWidth: '625px',
width: '100%',
justifyContent: 'flex-start',
}}
distribution="center"
alignment="start"
distribution="start"
alignment="center"
>
<VStack
<HStack
css={{
width: '425px',
borderRadius: '6px',
width: '100%',
borderBottom: '1px solid $thBorderColor',
}}
alignment="start"
distribution="start"
distribution="center"
>
<HStack
<StyledText
css={{
fontWeight: '600',
fontSize: '15px',
fontFamily: '$display',
width: '100%',
pt: '25px',
borderBottom: '1px solid $thBorderColor',
color: 'thTextContrast2',
m: '0px',
}}
alignment="center"
distribution="center"
>
<StyledText
css={{
fontWeight: '600',
fontSize: '15px',
fontFamily: '$display',
width: '100%',
color: 'thTextContrast2',
NOTEBOOK
</StyledText>
<Dropdown triggerElement={<MenuTrigger />}>
<DropdownOption
onSelect={() => {
exportHighlights()
}}
>
NOTEBOOK
</StyledText>
<Dropdown triggerElement={<MenuTrigger />}>
<DropdownOption
onSelect={() => {
exportHighlights()
}}
title="Export"
/>
</Dropdown>
</HStack>
<HStack
css={{
width: '100%',
pt: '25px',
borderBottom: '1px solid $thBorderColor',
}}
alignment="center"
distribution="center"
>
<StyledText
css={{
fontWeight: '600',
fontSize: '15px',
fontFamily: '$display',
width: '100%',
color: 'thTextContrast2',
}}
>
NOTE
</StyledText>
</HStack>
<HighlightNoteBox
sizeMode="normal"
mode={notesEditMode}
setEditMode={setNotesEditMode}
text={note?.annotation}
placeHolder="Add notes to this document..."
saveText={(highlight) => {
console.log('saving text', highlight)
title="Export"
/>
</Dropdown>
</HStack>
<HStack css={{ width: '100%', height: '100%' }}>
<Notebook
pageId={props.item.node.id}
highlights={props.item.node.highlights ?? []}
onClose={(
highlights: Highlight[],
deletedAnnotations: Highlight[]
) => {
console.log('closing notebook: ', highlights, deletedAnnotations)
}}
/>
<SpanBox css={{ mt: '10px', mb: '25px' }} />
{sortedHighlights && (
<>
<HStack
css={{
width: '100%',
pt: '25px',
borderBottom: '1px solid $thBorderColor',
}}
alignment="center"
distribution="center"
>
<StyledText
css={{
fontWeight: '600',
fontSize: '15px',
fontFamily: '$display',
width: '100%',
color: 'thTextContrast2',
}}
>
HIGHLIGHTS
</StyledText>
</HStack>
<VStack
css={{ width: '100%', mt: '20px' }}
distribution="start"
alignment="start"
>
{sortedHighlights.map((highlight) => (
<>
<HighlightViewItem
key={highlight.id}
highlight={highlight}
updateHighlight={(highlight) => {
console.log('updated highlight: ', highlight)
}}
/>
<SpanBox css={{ mt: '10px', mb: '25px' }} />
</>
))}
<Box css={{ height: '100px' }} />
</VStack>
<SpanBox css={{ mt: '10px', mb: '25px' }} />
</>
)}
</VStack>
</HStack>
</HStack>
</VStack>
)
// return (
// <HStack
// css={{
// m: '20px',
// height: '100%',
// flexGrow: '1',
// }}
// distribution="center"
// alignment="start"
// >
// <VStack
// css={{
// width: '425px',
// borderRadius: '6px',
// }}
// alignment="start"
// distribution="start"
// >
// <HStack
// css={{
// width: '100%',
// pt: '25px',
// borderBottom: '1px solid $thBorderColor',
// }}
// alignment="center"
// distribution="center"
// >
// <StyledText
// css={{
// fontWeight: '600',
// fontSize: '15px',
// fontFamily: '$display',
// width: '100%',
// color: 'thTextContrast2',
// }}
// >
// NOTEBOOK
// </StyledText>
// <Dropdown triggerElement={<MenuTrigger />}>
// <DropdownOption
// onSelect={() => {
// exportHighlights()
// }}
// title="Export"
// />
// </Dropdown>
// </HStack>
// <HStack
// css={{
// width: '100%',
// pt: '25px',
// borderBottom: '1px solid $thBorderColor',
// }}
// alignment="center"
// distribution="center"
// >
// <StyledText
// css={{
// fontWeight: '600',
// fontSize: '15px',
// fontFamily: '$display',
// width: '100%',
// color: 'thTextContrast2',
// }}
// >
// NOTE
// </StyledText>
// </HStack>
// <HighlightNoteBox
// sizeMode="normal"
// mode={notesEditMode}
// setEditMode={setNotesEditMode}
// text={note?.annotation}
// placeHolder="Add notes to this document..."
// saveText={(highlight) => {
// console.log('saving text', highlight)
// }}
// />
// <SpanBox css={{ mt: '10px', mb: '25px' }} />
// {sortedHighlights && (
// <>
// <HStack
// css={{
// width: '100%',
// pt: '25px',
// borderBottom: '1px solid $thBorderColor',
// }}
// alignment="center"
// distribution="center"
// >
// <StyledText
// css={{
// fontWeight: '600',
// fontSize: '15px',
// fontFamily: '$display',
// width: '100%',
// color: 'thTextContrast2',
// }}
// >
// HIGHLIGHTS
// </StyledText>
// </HStack>
// <VStack
// css={{ width: '100%', mt: '20px' }}
// distribution="start"
// alignment="start"
// >
// {sortedHighlights.map((highlight) => (
// <>
// <HighlightViewItem
// key={highlight.id}
// highlight={highlight}
// updateHighlight={(highlight) => {
// console.log('updated highlight: ', highlight)
// }}
// deleteHighlightAction={(highlight) => {
// console.log('deleting: ', highlight)
// }}
// setSetLabelsTarget: (highlight: Highlight) => void
// setShowConfirmDeleteHighlightId: (id: string | undefined) => void
// />
// <SpanBox css={{ mt: '10px', mb: '25px' }} />
// </>
// ))}
// <Box css={{ height: '100px' }} />
// </VStack>
// <SpanBox css={{ mt: '10px', mb: '25px' }} />
// </>
// )}
// </VStack>
// </HStack>
// )
}
type HighlightCountChipProps = {