From fa0efe779470724ee17ac2c6991c13a54bd23e51 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 22 Jun 2023 11:04:45 +0800 Subject: [PATCH 01/44] First pass at hover actions on the library items --- packages/web/components/elements/Button.tsx | 18 +++++- .../components/patterns/HighlightNotes.tsx | 6 +- .../web/components/patterns/HighlightView.tsx | 42 +++++++++----- .../LibraryCards/LibraryCardStyles.tsx | 6 +- .../patterns/LibraryCards/LibraryGridCard.tsx | 57 ++++++++++++++++++- .../patterns/LibraryCards/LibraryListCard.tsx | 51 ++++++++++++++++- .../components/templates/article/Notebook.tsx | 46 +++++++++------ .../templates/article/NotebookModal.tsx | 1 + .../web/components/tokens/stitches.config.ts | 4 ++ 9 files changed, 187 insertions(+), 44 deletions(-) diff --git a/packages/web/components/elements/Button.tsx b/packages/web/components/elements/Button.tsx index 89a7aa59d..635e357fb 100644 --- a/packages/web/components/elements/Button.tsx +++ b/packages/web/components/elements/Button.tsx @@ -242,15 +242,29 @@ export const Button = styled('button', { border: 'none', cursor: 'pointer', '&:hover': { - opacity: 0.8, + opacity: 0.7, }, }, articleActionIcon: { bg: 'transparent', border: 'none', cursor: 'pointer', + padding: '4px', + borderRadius: '5px', + '&:hover': { - opacity: 0.8, + opacity: 0.7, + }, + }, + hoverActionIcon: { + bg: 'transparent', + border: 'none', + cursor: 'pointer', + padding: '4px', + borderRadius: '5px', + + '&:hover': { + opacity: 0.7, }, }, ghost: { diff --git a/packages/web/components/patterns/HighlightNotes.tsx b/packages/web/components/patterns/HighlightNotes.tsx index 97a395cd9..98ee1c35d 100644 --- a/packages/web/components/patterns/HighlightNotes.tsx +++ b/packages/web/components/patterns/HighlightNotes.tsx @@ -8,7 +8,7 @@ import { useState, } from 'react' import { formattedShortTime } from '../../lib/dateFormatting' -import { HStack, SpanBox, VStack } from '../elements/LayoutPrimitives' +import { Box, HStack, SpanBox, VStack } from '../elements/LayoutPrimitives' import MarkdownIt from 'markdown-it' import MdEditor, { Plugins } from 'react-markdown-editor-lite' @@ -307,7 +307,7 @@ export function MarkdownNote(props: MarkdownNote): JSX.Element { p: '5px', width: '100%', fontSize: '15px', - borderRadius: '3px', + // borderRadius: '3px', marginTop: props.fillBackground || !props.text ? '10px' : '0px', paddingLeft: @@ -323,7 +323,7 @@ export function MarkdownNote(props: MarkdownNote): JSX.Element { ? '5px' : '0px', color: props.text ? '$thHighContrast' : '#898989', - border: props.text ? 'unset' : '1px solid $thBorderColor', + // border: props.text ? 'unset' : '1px solid $thBorderColor', background: props.text && props.fillBackground ? '$thBackground5' : 'unset', '> *': { diff --git a/packages/web/components/patterns/HighlightView.tsx b/packages/web/components/patterns/HighlightView.tsx index b9d55ea50..0673fb96a 100644 --- a/packages/web/components/patterns/HighlightView.tsx +++ b/packages/web/components/patterns/HighlightView.tsx @@ -1,5 +1,5 @@ /* eslint-disable react/no-children-prop */ -import { BookOpen, PencilLine } from 'phosphor-react' +import { BookOpen, CaretDown, PencilLine } from 'phosphor-react' import { useState } from 'react' import type { Highlight } from '../../lib/networking/fragments/highlightFragment' import { LabelChip } from '../elements/LabelChip' @@ -37,24 +37,38 @@ export function HighlightView(props: HighlightViewProps): JSX.Element { width: '100%', height: '100%', alignItems: 'stretch', + + background: '$thBackground', + borderRadius: '6px', + boxShadow: '0px 4px 4px rgba(33, 33, 33, 0.1)', }} + // > - - + + diff --git a/packages/web/components/patterns/LibraryCards/LibraryCardStyles.tsx b/packages/web/components/patterns/LibraryCards/LibraryCardStyles.tsx index 1775c6143..faa441bc5 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryCardStyles.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryCardStyles.tsx @@ -18,16 +18,12 @@ export const MenuStyle = { display: 'flex', marginLeft: 'auto', height: '30px', - width: '30px', + width: '150px', mt: '-5px', mr: '-5px', pt: '2px', alignItems: 'center', justifyContent: 'center', - borderRadius: '1000px', - '&:hover': { - bg: '$thBackground4', - }, } export const TitleStyle = { diff --git a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx index b2fd95ac7..2ae6986bb 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx @@ -5,7 +5,14 @@ import { CoverImage } from '../../elements/CoverImage' import dayjs from 'dayjs' import relativeTime from 'dayjs/plugin/relativeTime' import { useCallback, useState } from 'react' -import { DotsThreeVertical } from 'phosphor-react' +import { + ArchiveBox, + DotsThree, + DotsThreeVertical, + Tag, + Trash, + Tray, +} from 'phosphor-react' import Link from 'next/link' import { CardMenu } from '../CardMenu' import { @@ -19,6 +26,8 @@ import { TitleStyle, } from './LibraryCardStyles' import { sortedLabels } from '../../../lib/labelsSort' +import { Button } from '../../elements/Button' +import { theme } from '../../tokens/stitches.config' dayjs.extend(relativeTime) @@ -133,19 +142,63 @@ const LibraryGridCardContent = (props: LinkedItemCardProps): JSX.Element => { + + + setMenuOpen(open)} actionHandler={props.handleAction} triggerElement={ - + } /> diff --git a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx index 205e65f93..a4fb3ae38 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx @@ -2,7 +2,14 @@ import { Box, VStack, HStack, SpanBox } from '../../elements/LayoutPrimitives' import { LabelChip } from '../../elements/LabelChip' import type { LinkedItemCardProps } from './CardTypes' import { useCallback, useState } from 'react' -import { DotsThree } from 'phosphor-react' +import { + Archive, + ArchiveBox, + DotsThree, + Tag, + Trash, + Tray, +} from 'phosphor-react' import Link from 'next/link' import { CardMenu } from '../CardMenu' import { @@ -16,6 +23,8 @@ import { } from './LibraryCardStyles' import { sortedLabels } from '../../../lib/labelsSort' import { LIBRARY_LEFT_MENU_WIDTH } from '../../templates/homeFeed/LibraryFilterMenu' +import { Button } from '../../elements/Button' +import { theme } from '../../tokens/stitches.config' export function LibraryListCard(props: LinkedItemCardProps): JSX.Element { const [isHovered, setIsHovered] = useState(false) @@ -99,12 +108,52 @@ export function LibraryListCardContent( + + + setNotesEditMode(edit ? 'edit' : 'preview')} /> - + + + - + {sortedHighlights.map((highlight) => ( + {/* */} {props.title} diff --git a/packages/web/components/templates/article/NotebookModal.tsx b/packages/web/components/templates/article/NotebookModal.tsx index 25281f854..a563c7059 100644 --- a/packages/web/components/templates/article/NotebookModal.tsx +++ b/packages/web/components/templates/article/NotebookModal.tsx @@ -88,6 +88,7 @@ export function NotebookModal(props: NotebookModalProps): JSX.Element { }} css={{ overflow: 'auto', + bg: '$thLibraryBackground', height: sizeMode === 'normal' ? 'unset' : '100%', maxWidth: sizeMode === 'normal' ? '640px' : '100%', minHeight: sizeMode === 'normal' ? '525px' : 'unset', diff --git a/packages/web/components/tokens/stitches.config.ts b/packages/web/components/tokens/stitches.config.ts index d37381370..237002bf3 100644 --- a/packages/web/components/tokens/stitches.config.ts +++ b/packages/web/components/tokens/stitches.config.ts @@ -180,6 +180,8 @@ export const { styled, css, theme, getCssText, globalCss, keyframes, config } = thLibraryMenuUnselected: '#898989', thLibrarySelectionColor: '#FFEA9F', + thNotebookSubtle: '#6A6968', + thTextContrast: '#1E1E1E', thTextContrast2: '#3D3D3D', @@ -274,6 +276,8 @@ const darkThemeSpec = { thLibraryMenuUnselected: '#898989', thLibrarySelectionColor: '#3D3D3D', + thNotebookSubtle: '#898989', + thTextContrast: '#FFFFFF', thTextContrast2: '#EBEBEB', From e294ed4b4b88a5d8be626840213bb658ab15126c Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 22 Jun 2023 17:16:54 +0800 Subject: [PATCH 02/44] Clean up notebooks design and saving --- .../components/patterns/HighlightNotes.tsx | 141 ++++------- .../web/components/patterns/HighlightView.tsx | 78 +++--- .../LibraryCards/LibraryCardStyles.tsx | 6 +- .../patterns/LibraryCards/LibraryGridCard.tsx | 3 - .../patterns/LibraryCards/LibraryListCard.tsx | 32 ++- .../templates/article/HighlightViewItem.tsx | 37 ++- .../components/templates/article/Notebook.tsx | 222 ++++++++++-------- .../templates/article/NotebookModal.tsx | 7 +- .../templates/homeFeed/HighlightItem.tsx | 128 +++++----- .../web/components/tokens/stitches.config.ts | 1 + packages/web/lib/themeUpdater.tsx | 1 + 11 files changed, 324 insertions(+), 332 deletions(-) diff --git a/packages/web/components/patterns/HighlightNotes.tsx b/packages/web/components/patterns/HighlightNotes.tsx index 98ee1c35d..e0d3644c5 100644 --- a/packages/web/components/patterns/HighlightNotes.tsx +++ b/packages/web/components/patterns/HighlightNotes.tsx @@ -26,6 +26,9 @@ import { import { CloseButton } from '../elements/CloseButton' import { StyledText } from '../elements/StyledText' import remarkGfm from 'remark-gfm' +import { RcEditorStyles } from './RcEditorStyles' +import { isDarkTheme } from '../../lib/themeUpdater' +import { showErrorToast, showSuccessToast } from '../../lib/toastHelpers' const mdParser = new MarkdownIt() @@ -34,6 +37,8 @@ MdEditor.use(Plugins.TabInsert, { }) type NoteSectionProps = { + targetId: string + placeHolder: string mode: 'edit' | 'preview' @@ -60,6 +65,7 @@ export function HighlightNoteBox(props: NoteSectionProps): JSX.Element { return ( (null) const [lastChanged, setLastChanged] = useState(undefined) const [errorSaving, setErrorSaving] = useState(undefined) - - const saveRef = useRef(props.saveText) - - useEffect(() => { - saveRef.current = props.saveText - }, [props.lastSaved, lastChanged]) - - const debouncedSave = useMemo< - (text: string, updateTime: Date) => void - >(() => { - const func = (text: string, updateTime: Date) => { - saveRef.current?.(text, updateTime) - } - return throttle(func, 3000) - }, []) - - const handleEditorChange = useCallback( - ( - data: { text: string; html: string }, - event?: ChangeEvent | undefined - ) => { - if (event) { - event.preventDefault() - } - - const updateTime = new Date() - setLastChanged(updateTime) - debouncedSave(data.text, updateTime) - }, - [props.lastSaved, lastChanged] - ) + const isDark = isDarkTheme() return ( <> @@ -175,28 +163,13 @@ export function MarkdownNote(props: MarkdownNote): JSX.Element { .section': { - borderRight: 'unset', - }, - '.rc-md-editor .editor-container .sec-md .input': { - padding: '10px', - borderRadius: '5px', - fontSize: '16px', - }, + ...RcEditorStyles(isDark), }} onKeyDown={(event: React.KeyboardEvent) => { if (event.code.toLowerCase() === 'escape') { props.setEditMode('preview') event.preventDefault() + event.stopPropagation() } }} > @@ -233,7 +206,6 @@ export function MarkdownNote(props: MarkdownNote): JSX.Element { height: props.sizeMode == 'normal' ? '160px' : '320px', }} renderHTML={(text: string) => mdParser.render(text)} - onChange={handleEditorChange} /> )} - {props.lastSaved !== undefined ? ( - <> - {lastChanged === props.lastSaved - ? 'Saved' - : `Last saved ${formattedShortTime( - props.lastSaved.toISOString() - )}`} - - ) : null} - {lastChanged !== props.lastSaved && ( - + - - )} + Save + + ) : ( @@ -306,9 +264,9 @@ export function MarkdownNote(props: MarkdownNote): JSX.Element { css={{ p: '5px', width: '100%', - fontSize: '15px', - // borderRadius: '3px', - marginTop: props.fillBackground || !props.text ? '10px' : '0px', + fontSize: '12px', + marginTop: '0px', + paddingTop: '0px', paddingLeft: props.fillBackground && props.text @@ -344,6 +302,8 @@ export function MarkdownNote(props: MarkdownNote): JSX.Element { } type MarkdownModalProps = { + targetId: string + placeHolder: string mode: 'edit' | 'preview' @@ -435,6 +395,7 @@ export function MarkdownModal(props: MarkdownModalProps): JSX.Element { ('preview') return ( > - + {/* */} + @@ -89,7 +98,7 @@ export function HighlightView(props: HighlightViewProps): JSX.Element { }, fontSize: '15px', lineHeight: 1.5, - color: '$grayText', + color: '$thTextSubtle2', img: { display: 'block', margin: '0.5em auto !important', @@ -110,11 +119,18 @@ export function HighlightView(props: HighlightViewProps): JSX.Element { ))} - { - setNoteMode(noteMode == 'preview' ? 'edit' : 'preview') - event.preventDefault() - }} - > - {noteMode === 'edit' ? ( - - ) : ( - - )} - diff --git a/packages/web/components/patterns/LibraryCards/LibraryCardStyles.tsx b/packages/web/components/patterns/LibraryCards/LibraryCardStyles.tsx index faa441bc5..954211043 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryCardStyles.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryCardStyles.tsx @@ -18,9 +18,9 @@ export const MenuStyle = { display: 'flex', marginLeft: 'auto', height: '30px', - width: '150px', - mt: '-5px', - mr: '-5px', + width: '180px', + // mt: '-5px', + // mr: '-5px', pt: '2px', alignItems: 'center', justifyContent: 'center', diff --git a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx index 2ae6986bb..dc73339d0 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx @@ -145,9 +145,6 @@ const LibraryGridCardContent = (props: LinkedItemCardProps): JSX.Element => { gap: '10px', px: '20px', borderRadius: '1000px', - - bg: 'red', - visibility: props.isHovered || menuOpen ? 'unset' : 'hidden', '@media (hover: none)': { visibility: 'unset', diff --git a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx index a4fb3ae38..fe3956871 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx @@ -6,6 +6,8 @@ import { Archive, ArchiveBox, DotsThree, + Note, + Notebook, Tag, Trash, Tray, @@ -116,6 +118,27 @@ export function LibraryListCardContent( }, }} > + + - setHover(true)} onMouseLeave={() => setHover(false)} > - + - - - + ) } diff --git a/packages/web/components/templates/article/Notebook.tsx b/packages/web/components/templates/article/Notebook.tsx index 420568cfd..ac991d2b4 100644 --- a/packages/web/components/templates/article/Notebook.tsx +++ b/packages/web/components/templates/article/Notebook.tsx @@ -3,7 +3,15 @@ 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 { BookOpen, CaretDown, PencilLine, X } from 'phosphor-react' +import { + BookOpen, + CaretDown, + CaretRight, + DotsThree, + Pencil, + PencilLine, + X, +} from 'phosphor-react' import { updateHighlightMutation } from '../../../lib/networking/mutations/updateHighlightMutation' import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers' import { diff_match_patch } from 'diff-match-patch' @@ -20,6 +28,8 @@ import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery import { ReadableItem } from '../../../lib/networking/queries/useGetLibraryItemsQuery' import { SetHighlightLabelsModalPresenter } from './SetLabelsModalPresenter' import { Button } from '../../elements/Button' +import { Dropdown, DropdownOption } from '../../elements/DropdownElements' +import { ArticleNoteBox } from '../../patterns/ArticleNotes' type NotebookProps = { viewer: UserBasicData @@ -93,6 +103,7 @@ export function Notebook(props: NotebookProps): JSX.Element { if (!action.note) { throw new Error('No note on CREATE_NOTE action') } + console.log(' - CREATE_NOTE', action.note) return { ...state, note: action.note, @@ -234,6 +245,14 @@ export function Notebook(props: NotebookProps): JSX.Element { const handleSaveNoteText = useCallback( (text, cb: (success: boolean) => void) => { + console.log( + 'saving note text: ', + text, + 'annotations.loaded: ', + annotations.loaded, + 'annotations.note: ', + annotations.note + ) if (!annotations.loaded) { // We haven't loaded the user's annotations yet, so we can't // find or create their highlight note. @@ -285,72 +304,89 @@ export function Notebook(props: NotebookProps): JSX.Element { [annotations, props.item] ) + const [articleNotesCollapsed, setArticleNotesCollapsed] = useState(false) + const [highlightsCollapsed, setHighlightsCollapsed] = useState(false) + return ( - setNotesEditMode(edit ? 'edit' : 'preview')} + collapsed={articleNotesCollapsed} + setCollapsed={setArticleNotesCollapsed} /> - - - + {!articleNotesCollapsed && ( + + + + )} + - + - {sortedHighlights.map((highlight) => ( - { - dispatchAnnotations({ - type: 'UPDATE_HIGHLIGHT', - updateHighlight: highlight, - }) - }} - /> - ))} - {sortedHighlights.length === 0 && ( - - You have not added any highlights to this document. - + {!highlightsCollapsed && ( + <> + {sortedHighlights.map((highlight) => ( + { + dispatchAnnotations({ + type: 'UPDATE_HIGHLIGHT', + updateHighlight: highlight, + }) + }} + /> + ))} + {sortedHighlights.length === 0 && ( + + You have not added any highlights to this document. + + )} + )} - + /> */} {showConfirmDeleteHighlightId && ( @@ -419,62 +455,52 @@ export function Notebook(props: NotebookProps): JSX.Element { ) } -type TitledSectionProps = { +type SectionTitleProps = { title: string - editMode?: boolean - setEditMode?: (set: boolean) => void + collapsed: boolean + setCollapsed: (set: boolean) => void } -function TitledSection(props: TitledSectionProps): JSX.Element { +function SectionTitle(props: SectionTitleProps): JSX.Element { return ( <> - { + props.setCollapsed(!props.collapsed) + event.stopPropagation() + }} > - {/* */} + {props.collapsed ? ( + + ) : ( + + )} {props.title} - {props.setEditMode && ( - { - if (props.setEditMode) { - props.setEditMode(!props.editMode) - } - event.preventDefault() - }} - > - {props.editMode ? ( - - ) : ( - - )} - - )} - + ) } diff --git a/packages/web/components/templates/article/NotebookModal.tsx b/packages/web/components/templates/article/NotebookModal.tsx index a563c7059..7cf0d877a 100644 --- a/packages/web/components/templates/article/NotebookModal.tsx +++ b/packages/web/components/templates/article/NotebookModal.tsx @@ -89,8 +89,9 @@ export function NotebookModal(props: NotebookModalProps): JSX.Element { css={{ overflow: 'auto', bg: '$thLibraryBackground', + width: '100%', height: sizeMode === 'normal' ? 'unset' : '100%', - maxWidth: sizeMode === 'normal' ? '640px' : '100%', + maxWidth: sizeMode === 'normal' ? '748px' : '1050px', minHeight: sizeMode === 'normal' ? '525px' : 'unset', '@mdDown': { top: '20px', @@ -102,7 +103,7 @@ export function NotebookModal(props: NotebookModalProps): JSX.Element { }} > - + {/* */} }> { diff --git a/packages/web/components/templates/homeFeed/HighlightItem.tsx b/packages/web/components/templates/homeFeed/HighlightItem.tsx index eea578052..2588de8b6 100644 --- a/packages/web/components/templates/homeFeed/HighlightItem.tsx +++ b/packages/web/components/templates/homeFeed/HighlightItem.tsx @@ -10,7 +10,7 @@ import { DropdownOption, DropdownSeparator, } from '../../elements/DropdownElements' -import { Box } from '../../elements/LayoutPrimitives' +import { Box, VStack } from '../../elements/LayoutPrimitives' import { styled, theme } from '../../tokens/stitches.config' @@ -57,70 +57,76 @@ export function HighlightsMenu(props: HighlightsMenuProps): JSX.Element { }, [props.highlight]) return ( - - - - } + - { - copyHighlight() - }} - title="Copy" - /> - { - props.setLabelsTarget(props.highlight) - }} - title="Labels" - /> - { - props.setShowConfirmDeleteHighlightId(props.highlight.id) - }} - title="Delete" - /> - - + + + } > - { - console.log('event.ctrlKey: ', event.ctrlKey, event.metaKey) - if (event.ctrlKey || event.metaKey) { - window.open( - `/${props.viewer.profile.username}/${props.item.slug}#${props.highlight.id}`, - '_blank' - ) - return - } - props.viewInReader(props.highlight.id) - event.preventDefault() - event.stopPropagation() + { + copyHighlight() }} + title="Copy" + /> + { + props.setLabelsTarget(props.highlight) + }} + title="Labels" + /> + { + props.setShowConfirmDeleteHighlightId(props.highlight.id) + }} + title="Delete" + /> + + - View In Reader - - - + { + console.log('event.ctrlKey: ', event.ctrlKey, event.metaKey) + if (event.ctrlKey || event.metaKey) { + window.open( + `/${props.viewer.profile.username}/${props.item.slug}#${props.highlight.id}`, + '_blank' + ) + return + } + props.viewInReader(props.highlight.id) + event.preventDefault() + event.stopPropagation() + }} + > + View In Reader + + + + ) } diff --git a/packages/web/components/tokens/stitches.config.ts b/packages/web/components/tokens/stitches.config.ts index 237002bf3..1b0eae447 100644 --- a/packages/web/components/tokens/stitches.config.ts +++ b/packages/web/components/tokens/stitches.config.ts @@ -277,6 +277,7 @@ const darkThemeSpec = { thLibrarySelectionColor: '#3D3D3D', thNotebookSubtle: '#898989', + thNotebookHighContrast: '#2A2A2A', thTextContrast: '#FFFFFF', thTextContrast2: '#EBEBEB', diff --git a/packages/web/lib/themeUpdater.tsx b/packages/web/lib/themeUpdater.tsx index 07b860c5c..6c57109a6 100644 --- a/packages/web/lib/themeUpdater.tsx +++ b/packages/web/lib/themeUpdater.tsx @@ -107,6 +107,7 @@ export function isDarkTheme(): boolean { return ( currentTheme === 'Dark' || currentTheme === 'Darker' || + currentTheme === 'Apollo' || currentTheme == 'Black' ) } From b1a7fc8488f6f76280bca33d8e4965c65807dd0b Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 22 Jun 2023 17:28:02 +0800 Subject: [PATCH 03/44] Improve styling of notes --- packages/web/components/patterns/HighlightNotes.tsx | 5 +++-- packages/web/components/templates/article/Notebook.tsx | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/web/components/patterns/HighlightNotes.tsx b/packages/web/components/patterns/HighlightNotes.tsx index e0d3644c5..3464970fc 100644 --- a/packages/web/components/patterns/HighlightNotes.tsx +++ b/packages/web/components/patterns/HighlightNotes.tsx @@ -163,7 +163,7 @@ export function MarkdownNote(props: MarkdownNote): JSX.Element { ) => { if (event.code.toLowerCase() === 'escape') { @@ -266,7 +266,7 @@ export function MarkdownNote(props: MarkdownNote): JSX.Element { width: '100%', fontSize: '12px', marginTop: '0px', - paddingTop: '0px', + paddingTop: '5px', paddingLeft: props.fillBackground && props.text @@ -282,6 +282,7 @@ export function MarkdownNote(props: MarkdownNote): JSX.Element { : '0px', color: props.text ? '$thHighContrast' : '#898989', // border: props.text ? 'unset' : '1px solid $thBorderColor', + borderRadius: '5px', background: props.text && props.fillBackground ? '$thBackground5' : 'unset', '> *': { diff --git a/packages/web/components/templates/article/Notebook.tsx b/packages/web/components/templates/article/Notebook.tsx index ac991d2b4..1c2add16f 100644 --- a/packages/web/components/templates/article/Notebook.tsx +++ b/packages/web/components/templates/article/Notebook.tsx @@ -314,7 +314,7 @@ export function Notebook(props: NotebookProps): JSX.Element { height: '100%', width: '100%', p: '40px', - '@mdDown': { p: '10px' }, + '@mdDown': { p: '15px' }, }} > Date: Thu, 22 Jun 2023 17:40:29 +0800 Subject: [PATCH 04/44] Update notebook styling --- .../web/components/patterns/ArticleNotes.tsx | 283 ++++++++++++++++++ .../patterns/MDEditorSavePlugin.tsx | 33 ++ .../components/patterns/RcEditorStyles.tsx | 34 +++ .../components/templates/article/Notebook.tsx | 4 +- 4 files changed, 352 insertions(+), 2 deletions(-) create mode 100644 packages/web/components/patterns/ArticleNotes.tsx create mode 100644 packages/web/components/patterns/MDEditorSavePlugin.tsx create mode 100644 packages/web/components/patterns/RcEditorStyles.tsx diff --git a/packages/web/components/patterns/ArticleNotes.tsx b/packages/web/components/patterns/ArticleNotes.tsx new file mode 100644 index 000000000..b8da7c9ef --- /dev/null +++ b/packages/web/components/patterns/ArticleNotes.tsx @@ -0,0 +1,283 @@ +/* eslint-disable react/no-children-prop */ +import { + ChangeEvent, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react' +import { formattedShortTime } from '../../lib/dateFormatting' +import { Box, HStack, SpanBox, VStack } from '../elements/LayoutPrimitives' + +import MarkdownIt from 'markdown-it' +import MdEditor, { Plugins } from 'react-markdown-editor-lite' +import 'react-markdown-editor-lite/lib/index.css' +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' +import { + ModalContent, + ModalOverlay, + ModalRoot, +} from '../elements/ModalPrimitives' +import { CloseButton } from '../elements/CloseButton' +import { StyledText } from '../elements/StyledText' +import remarkGfm from 'remark-gfm' +import MDEditorSavePlugin from './MDEditorSavePlugin' +import HandleFullScreen from './MDEditorSavePlugin' +import Counter from './MDEditorSavePlugin' +import { isDarkTheme } from '../../lib/themeUpdater' +import { RcEditorStyles } from './RcEditorStyles' + +const mdParser = new MarkdownIt() + +MdEditor.use(Plugins.TabInsert, { + tabMapValue: 1, // note that 1 means a '\t' instead of ' '. +}) + +console.log() +MdEditor.use(Counter) + +type NoteSectionProps = { + targetId: string + + placeHolder: string + mode: 'edit' | 'preview' + + sizeMode: 'normal' | 'maximized' + setEditMode: (set: 'edit' | 'preview') => void + + text: string | undefined + saveText: (text: string, completed: (success: boolean) => void) => void +} + +export function ArticleNotes(props: NoteSectionProps): JSX.Element { + const [lastSaved, setLastSaved] = useState(undefined) + + const saveText = useCallback( + (text, updateTime) => { + props.saveText(text, (success) => { + if (success) { + setLastSaved(updateTime) + } + }) + }, + [props] + ) + + return ( + + ) +} + +type HighlightViewNoteProps = { + targetId: string + + placeHolder: string + mode: 'edit' | 'preview' + + highlight: Highlight + + sizeMode: 'normal' | 'maximized' + setEditMode: (set: 'edit' | 'preview') => void + + text: string | undefined + updateHighlight: (highlight: Highlight) => void +} + +export function HighlightViewNote(props: HighlightViewNoteProps): JSX.Element { + const [lastSaved, setLastSaved] = useState(undefined) + + const saveText = useCallback( + (text, updateTime) => { + ;(async () => { + const success = await updateHighlightMutation({ + annotation: text, + highlightId: props.highlight?.id, + }) + if (success) { + setLastSaved(updateTime) + props.highlight.annotation = text + props.updateHighlight(props.highlight) + } + })() + }, + [props] + ) + + return ( + + ) +} + +type MarkdownNote = { + targetId: string + + placeHolder: string + + sizeMode: 'normal' | 'maximized' + + text: string | undefined + fillBackground: boolean | undefined + + lastSaved: Date | undefined + saveText: (text: string, updateTime: Date) => void +} + +export function MarkdownNote(props: MarkdownNote): JSX.Element { + const editorRef = useRef(null) + const [lastChanged, setLastChanged] = useState(undefined) + const [errorSaving, setErrorSaving] = useState(undefined) + const isDark = isDarkTheme() + + const saveRef = useRef(props.saveText) + + useEffect(() => { + saveRef.current = props.saveText + }, [props.lastSaved, lastChanged]) + + const debouncedSave = useMemo< + (text: string, updateTime: Date) => void + >(() => { + const func = (text: string, updateTime: Date) => { + saveRef.current?.(text, updateTime) + } + return throttle(func, 3000) + }, []) + + const handleEditorChange = useCallback( + ( + data: { text: string; html: string }, + event?: ChangeEvent | undefined + ) => { + if (event) { + event.preventDefault() + } + + const updateTime = new Date() + setLastChanged(updateTime) + localStorage.setItem(`note-${props.targetId}`, JSON.stringify(data)) + debouncedSave(data.text, updateTime) + }, + [props.lastSaved, lastChanged] + ) + + useEffect(() => { + const saveMarkdownNote = () => { + const md = editorRef.current?.getMdValue() + if (md) { + props.saveText(md, new Date()) + } + } + document.addEventListener('saveMarkdownNote', saveMarkdownNote) + return () => { + document.removeEventListener('saveMarkdownNote', saveMarkdownNote) + } + }, [props, editorRef]) + + console.log('isDark: ', isDark) + + return ( + ) => { + if (event.code.toLowerCase() === 'escape') { + event.preventDefault() + event.stopPropagation() + } + }} + > + mdParser.render(text)} + onChange={handleEditorChange} + /> + + {errorSaving && ( + + {errorSaving} + + )} + {props.lastSaved !== undefined ? ( + <> + {lastChanged === props.lastSaved + ? 'Saved' + : `Last saved ${formattedShortTime( + props.lastSaved.toISOString() + )}`} + + ) : null} + + + ) +} diff --git a/packages/web/components/patterns/MDEditorSavePlugin.tsx b/packages/web/components/patterns/MDEditorSavePlugin.tsx new file mode 100644 index 000000000..7d9605542 --- /dev/null +++ b/packages/web/components/patterns/MDEditorSavePlugin.tsx @@ -0,0 +1,33 @@ +import { Disc, FloppyDisk } from 'phosphor-react' +import { PluginComponent, PluginProps } from 'react-markdown-editor-lite' +import { SpanBox } from '../elements/LayoutPrimitives' +import { Button } from '../elements/Button' + +export default class MDEditorSavePlugin extends PluginComponent { + static pluginName = 'save' + + static align = 'right' + + constructor(props: any) { + super(props) + + this.handleClick = this.handleClick.bind(this) + } + + private handleClick() {} + + render() { + return ( + + ) + } +} diff --git a/packages/web/components/patterns/RcEditorStyles.tsx b/packages/web/components/patterns/RcEditorStyles.tsx new file mode 100644 index 000000000..058cc6bbe --- /dev/null +++ b/packages/web/components/patterns/RcEditorStyles.tsx @@ -0,0 +1,34 @@ +export const RcEditorStyles = (isDark: boolean, shadow: boolean) => { + return { + '.rc-md-editor .rc-md-navigation': { + background: 'var(--colors-grayBg)', + borderBottom: '1px solid $thBorderSubtle', + }, + '.rc-md-editor': { + borderRadius: '5px', + backgroundColor: isDark ? '#2A2A2A' : 'white', + border: '1px solid $thBorderSubtle', + boxShadow: shadow ? '0px 4px 4px rgba(33, 33, 33, 0.1)' : 'unset', + }, + '.rc-md-navigation': { + borderRadius: '5px', + borderBottomLeftRadius: '0px', + borderBottomRightRadius: '0px', + background: 'var(--colors-grayBg)', + }, + '.rc-md-editor .editor-container >.section': { + borderRight: 'unset', + }, + '.rc-md-editor .editor-container .sec-md .input': { + padding: '10px', + borderRadius: '5px', + fontSize: '16px', + color: isDark ? '#EBEBEB' : 'black', + backgroundColor: isDark ? '#2A2A2A' : 'white', + }, + '.rc-md-editor .drop-wrap': { + border: '1px solid $thBorderSubtle', + backgroundColor: isDark ? '#2A2A2A' : 'white', + }, + } +} diff --git a/packages/web/components/templates/article/Notebook.tsx b/packages/web/components/templates/article/Notebook.tsx index 1c2add16f..cde685879 100644 --- a/packages/web/components/templates/article/Notebook.tsx +++ b/packages/web/components/templates/article/Notebook.tsx @@ -29,7 +29,7 @@ import { ReadableItem } from '../../../lib/networking/queries/useGetLibraryItems import { SetHighlightLabelsModalPresenter } from './SetLabelsModalPresenter' import { Button } from '../../elements/Button' import { Dropdown, DropdownOption } from '../../elements/DropdownElements' -import { ArticleNoteBox } from '../../patterns/ArticleNotes' +import { ArticleNotes } from '../../patterns/ArticleNotes' type NotebookProps = { viewer: UserBasicData @@ -328,7 +328,7 @@ export function Notebook(props: NotebookProps): JSX.Element { distribution="start" css={{ width: '100%', mt: '10px', gap: '10px' }} > - Date: Thu, 22 Jun 2023 17:48:34 +0800 Subject: [PATCH 05/44] Remove hover actions on grid view --- .../components/patterns/LibraryCards/LibraryGridCard.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx index dc73339d0..6a6884cf2 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx @@ -142,6 +142,9 @@ const LibraryGridCardContent = (props: LinkedItemCardProps): JSX.Element => { { }, }} > - + */} Date: Thu, 22 Jun 2023 18:51:10 +0800 Subject: [PATCH 06/44] Clean up note saving, linting fixes --- .../patterns/MDEditorSavePlugin.tsx | 11 +++---- .../components/templates/article/Notebook.tsx | 30 +++++++++---------- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/packages/web/components/patterns/MDEditorSavePlugin.tsx b/packages/web/components/patterns/MDEditorSavePlugin.tsx index 7d9605542..e25110376 100644 --- a/packages/web/components/patterns/MDEditorSavePlugin.tsx +++ b/packages/web/components/patterns/MDEditorSavePlugin.tsx @@ -1,6 +1,7 @@ -import { Disc, FloppyDisk } from 'phosphor-react' -import { PluginComponent, PluginProps } from 'react-markdown-editor-lite' -import { SpanBox } from '../elements/LayoutPrimitives' +/* eslint-disable functional/no-class */ + +import { FloppyDisk } from 'phosphor-react' +import { PluginComponent } from 'react-markdown-editor-lite' import { Button } from '../elements/Button' export default class MDEditorSavePlugin extends PluginComponent { @@ -10,12 +11,8 @@ export default class MDEditorSavePlugin extends PluginComponent { constructor(props: any) { super(props) - - this.handleClick = this.handleClick.bind(this) } - private handleClick() {} - render() { return ( - )} - - - ) - } - + // 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, From f1e435ff0b598de9cde58a0b1e1281faebcaef4c Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Fri, 23 Jun 2023 17:53:24 +0800 Subject: [PATCH 09/44] Passing error messages for notebooks --- .../web/components/patterns/ArticleNotes.tsx | 77 ++--------- .../components/patterns/HighlightNotes.tsx | 120 +++++++++--------- .../components/patterns/RcEditorStyles.tsx | 6 + .../components/templates/article/Notebook.tsx | 114 ++++++++++++----- 4 files changed, 161 insertions(+), 156 deletions(-) diff --git a/packages/web/components/patterns/ArticleNotes.tsx b/packages/web/components/patterns/ArticleNotes.tsx index 1ee3cd4c1..c6c86f617 100644 --- a/packages/web/components/patterns/ArticleNotes.tsx +++ b/packages/web/components/patterns/ArticleNotes.tsx @@ -45,24 +45,15 @@ type NoteSectionProps = { targetId: string placeHolder: string - mode: 'edit' | 'preview' - - setEditMode: (set: 'edit' | 'preview') => void text: string | undefined - saveText: (text: string, completed: (success: boolean) => void) => void + saveText: (text: string) => void } export function ArticleNotes(props: NoteSectionProps): JSX.Element { - const [lastSaved, setLastSaved] = useState(undefined) - const saveText = useCallback( - (text, updateTime) => { - props.saveText(text, (success) => { - if (success) { - setLastSaved(updateTime) - } - }) + (text) => { + props.saveText(text) }, [props] ) @@ -73,7 +64,6 @@ export function ArticleNotes(props: NoteSectionProps): JSX.Element { placeHolder={props.placeHolder} text={props.text} saveText={saveText} - lastSaved={lastSaved} fillBackground={false} /> ) @@ -97,14 +87,14 @@ export function HighlightViewNote(props: HighlightViewNoteProps): JSX.Element { const [lastSaved, setLastSaved] = useState(undefined) const saveText = useCallback( - (text, updateTime) => { + (text) => { ;(async () => { const success = await updateHighlightMutation({ annotation: text, highlightId: props.highlight?.id, }) if (success) { - setLastSaved(updateTime) + // setLastSaved(updateTime) props.highlight.annotation = text props.updateHighlight(props.highlight) } @@ -119,7 +109,6 @@ export function HighlightViewNote(props: HighlightViewNoteProps): JSX.Element { placeHolder={props.placeHolder} text={props.text} saveText={saveText} - lastSaved={lastSaved} fillBackground={true} /> ) @@ -133,27 +122,22 @@ type MarkdownNote = { text: string | undefined fillBackground: boolean | undefined - lastSaved: Date | undefined - saveText: (text: string, updateTime: Date) => void + saveText: (text: string) => void } export function MarkdownNote(props: MarkdownNote): JSX.Element { const editorRef = useRef(null) - const [lastChanged, setLastChanged] = useState(undefined) - const [errorSaving, setErrorSaving] = useState(undefined) const isDark = isDarkTheme() const saveRef = useRef(props.saveText) useEffect(() => { saveRef.current = props.saveText - }, [props.lastSaved, lastChanged]) + }, [props]) - const debouncedSave = useMemo< - (text: string, updateTime: Date) => void - >(() => { - const func = (text: string, updateTime: Date) => { - saveRef.current?.(text, updateTime) + const debouncedSave = useMemo<(text: string) => void>(() => { + const func = (text: string) => { + saveRef.current?.(text) } return throttle(func, 3000) }, []) @@ -167,19 +151,16 @@ export function MarkdownNote(props: MarkdownNote): JSX.Element { event.preventDefault() } - const updateTime = new Date() - setLastChanged(updateTime) - localStorage.setItem(`note-${props.targetId}`, JSON.stringify(data)) - debouncedSave(data.text, updateTime) + debouncedSave(data.text) }, - [props.lastSaved, lastChanged] + [] ) useEffect(() => { const saveMarkdownNote = () => { const md = editorRef.current?.getMdValue() if (md) { - props.saveText(md, new Date()) + props.saveText(md) } } document.addEventListener('saveMarkdownNote', saveMarkdownNote) @@ -238,38 +219,6 @@ export function MarkdownNote(props: MarkdownNote): JSX.Element { renderHTML={(text: string) => mdParser.render(text)} onChange={handleEditorChange} /> - - {errorSaving && ( - - {errorSaving} - - )} - {props.lastSaved !== undefined ? ( - <> - {lastChanged === props.lastSaved - ? 'Saved' - : `Last saved ${formattedShortTime( - props.lastSaved.toISOString() - )}`} - - ) : null} - ) } diff --git a/packages/web/components/patterns/HighlightNotes.tsx b/packages/web/components/patterns/HighlightNotes.tsx index c8ba4809d..f2194ed5c 100644 --- a/packages/web/components/patterns/HighlightNotes.tsx +++ b/packages/web/components/patterns/HighlightNotes.tsx @@ -18,13 +18,6 @@ import throttle from 'lodash/throttle' import { updateHighlightMutation } from '../../lib/networking/mutations/updateHighlightMutation' import { Highlight } from '../../lib/networking/fragments/highlightFragment' import { Button } from '../elements/Button' -import { - ModalContent, - ModalOverlay, - ModalRoot, -} from '../elements/ModalPrimitives' -import { CloseButton } from '../elements/CloseButton' -import { StyledText } from '../elements/StyledText' import remarkGfm from 'remark-gfm' import { RcEditorStyles } from './RcEditorStyles' import { isDarkTheme } from '../../lib/themeUpdater' @@ -36,46 +29,6 @@ MdEditor.use(Plugins.TabInsert, { tabMapValue: 1, // note that 1 means a '\t' instead of ' '. }) -type NoteSectionProps = { - targetId: string - - placeHolder: string - mode: 'edit' | 'preview' - - setEditMode: (set: 'edit' | 'preview') => void - - text: string | undefined - saveText: (text: string, completed: (success: boolean) => void) => void -} - -export function HighlightNoteBox(props: NoteSectionProps): JSX.Element { - const [lastSaved, setLastSaved] = useState(undefined) - - const saveText = useCallback( - (text, updateTime) => { - props.saveText(text, (success) => { - if (success) { - setLastSaved(updateTime) - } - }) - }, - [props] - ) - - return ( - - ) -} - type HighlightViewNoteProps = { targetId: string @@ -92,9 +45,10 @@ type HighlightViewNoteProps = { export function HighlightViewNote(props: HighlightViewNoteProps): JSX.Element { const [lastSaved, setLastSaved] = useState(undefined) + const [errorSaving, setErrorSaving] = useState(undefined) const saveText = useCallback( - (text, updateTime) => { + (text, updateTime, interactive) => { ;(async () => { const success = await updateHighlightMutation({ annotation: text, @@ -104,13 +58,13 @@ export function HighlightViewNote(props: HighlightViewNoteProps): JSX.Element { setLastSaved(updateTime) props.highlight.annotation = text props.updateHighlight(props.highlight) - showSuccessToast('Note saved.', { - position: 'bottom-right', - }) + if (interactive) { + showSuccessToast('Note saved', { + position: 'bottom-right', + }) + } } else { - showErrorToast('Error saving note.', { - position: 'bottom-right', - }) + setErrorSaving('Error saving note.') } })() }, @@ -126,6 +80,7 @@ export function HighlightViewNote(props: HighlightViewNoteProps): JSX.Element { text={props.text} saveText={saveText} lastSaved={lastSaved} + errorSaving={errorSaving} fillBackground={true} /> ) @@ -143,14 +98,47 @@ type MarkdownNote = { fillBackground: boolean | undefined lastSaved: Date | undefined - saveText: (text: string, updateTime: Date) => void + errorSaving: string | undefined + + saveText: (text: string, updateTime: Date, interactive: boolean) => void } export function MarkdownNote(props: MarkdownNote): JSX.Element { const editorRef = useRef(null) - const [lastChanged, setLastChanged] = useState(undefined) - const [errorSaving, setErrorSaving] = useState(undefined) const isDark = isDarkTheme() + const [lastChanged, setLastChanged] = useState(undefined) + + const saveRef = useRef(props.saveText) + + useEffect(() => { + saveRef.current = props.saveText + }, [props]) + + const debouncedSave = useMemo< + (text: string, updateTime: Date) => void + >(() => { + const func = (text: string, updateTime: Date) => { + saveRef.current?.(text, updateTime, false) + } + return throttle(func, 3000) + }, []) + + const handleEditorChange = useCallback( + ( + data: { text: string; html: string }, + event?: ChangeEvent | undefined + ) => { + if (event) { + event.preventDefault() + } + + const updateTime = new Date() + setLastChanged(updateTime) + + debouncedSave(data.text, updateTime) + }, + [] + ) return ( <> @@ -201,6 +189,7 @@ export function MarkdownNote(props: MarkdownNote): JSX.Element { height: '160px', }} renderHTML={(text: string) => mdParser.render(text)} + onChange={handleEditorChange} /> - {errorSaving && ( + {props.errorSaving && ( - {errorSaving} + {props.errorSaving} )} + {props.lastSaved !== undefined ? ( + <> + {lastChanged === props.lastSaved + ? 'Saved' + : `Last saved ${formattedShortTime( + props.lastSaved.toISOString() + )}`} + + ) : null} { const value = editorRef.current?.getMdValue() if (value) { - props.saveText(value, new Date()) + const updateTime = new Date() + setLastChanged(updateTime) + props.saveText(value, updateTime, true) props.setEditMode('preview') } else { showErrorToast('Error saving note.', { diff --git a/packages/web/components/patterns/RcEditorStyles.tsx b/packages/web/components/patterns/RcEditorStyles.tsx index 058cc6bbe..fcda55fa9 100644 --- a/packages/web/components/patterns/RcEditorStyles.tsx +++ b/packages/web/components/patterns/RcEditorStyles.tsx @@ -30,5 +30,11 @@ export const RcEditorStyles = (isDark: boolean, shadow: boolean) => { border: '1px solid $thBorderSubtle', backgroundColor: isDark ? '#2A2A2A' : 'white', }, + '.rc-md-editor:focus-within': { + outline: '2px solid $omnivoreCtaYellow', + borderRadius: '5px', + border: 'unset', + boxShadow: 'unset', + }, } } diff --git a/packages/web/components/templates/article/Notebook.tsx b/packages/web/components/templates/article/Notebook.tsx index 775fdcd11..bc13ba303 100644 --- a/packages/web/components/templates/article/Notebook.tsx +++ b/packages/web/components/templates/article/Notebook.tsx @@ -36,6 +36,8 @@ import { SetHighlightLabelsModalPresenter } from './SetLabelsModalPresenter' import { Button } from '../../elements/Button' import { ArticleNotes } from '../../patterns/ArticleNotes' import { useGetArticleQuery } from '../../../lib/networking/queries/useGetArticleQuery' +import { formattedShortTime } from '../../../lib/dateFormatting' +import { isDarkTheme } from '../../../lib/themeUpdater' type NotebookContentProps = { viewer: UserBasicData @@ -64,6 +66,8 @@ type NoteState = { } export function NotebookContent(props: NotebookContentProps): JSX.Element { + const isDark = isDarkTheme() + const { articleData, mutate } = useGetArticleQuery({ slug: props.item.slug, username: props.viewer.profile.username, @@ -74,9 +78,6 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { const [labelsTarget, setLabelsTarget] = useState( undefined ) - const [notesEditMode, setNotesEditMode] = useState<'edit' | 'preview'>( - 'preview' - ) const noteState = useRef({ isCreating: false, note: undefined, @@ -87,14 +88,22 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { return uuidv4() }, []) - const updateNote = useCallback((note: Highlight, text: string) => { - ;(async () => { - const result = await updateHighlightMutation({ - highlightId: note.id, - annotation: text, - }) - })() - }, []) + const updateNote = useCallback( + (note: Highlight, text: string, startTime: Date) => { + ;(async () => { + const result = await updateHighlightMutation({ + highlightId: note.id, + annotation: text, + }) + if (result) { + setLastSaved(startTime) + } else { + setErrorSaving('Error saving') + } + })() + }, + [] + ) const createNote = useCallback((text: string) => { console.log('creating note: ', newNoteId, noteState.current.isCreating) @@ -112,10 +121,13 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { if (success) { noteState.current.note = success noteState.current.isCreating = false + } else { + setErrorSaving('Error creating note') } } catch (error) { console.error('error creating note: ', error) noteState.current.isCreating = false + setErrorSaving('Error creating note') } })() }, []) @@ -167,10 +179,12 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { }, [highlights]) const handleSaveNoteText = useCallback( - (text, cb: (success: boolean) => void) => { - console.log('handleSaveNoteText', noteState.current) + (text) => { + const changeTime = new Date() + + setLastChanged(changeTime) if (noteState.current.note) { - updateNote(noteState.current.note, text) + updateNote(noteState.current.note, text, changeTime) return } if (noteState.current.isCreating) { @@ -195,6 +209,9 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { const [articleNotesCollapsed, setArticleNotesCollapsed] = useState(false) const [highlightsCollapsed, setHighlightsCollapsed] = useState(false) + const [errorSaving, setErrorSaving] = useState(undefined) + const [lastChanged, setLastChanged] = useState(undefined) + const [lastSaved, setLastSaved] = useState(undefined) return ( @@ -212,20 +230,50 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { setCollapsed={setArticleNotesCollapsed} /> {!articleNotesCollapsed && ( - - - + <> + + + + + {errorSaving && ( + + {errorSaving} + + )} + {lastSaved !== undefined ? ( + <> + {lastChanged === lastSaved + ? 'Saved' + : `Last saved ${formattedShortTime(lastSaved.toISOString())}`} + + ) : null} + + )} @@ -250,16 +298,14 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { setShowConfirmDeleteHighlightId } updateHighlight={() => { - // dispatchAnnotations({ - // type: 'UPDATE_HIGHLIGHT', - // updateHighlight: highlight, - // }) + mutate() }} /> ))} {sortedHighlights.length === 0 && ( You have not added any highlights to this document. From ff06f817fe52f98bf40e4171eb1c03811bc7bb0d Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Fri, 23 Jun 2023 18:54:34 +0800 Subject: [PATCH 10/44] Better handling of state changes on notes --- .../web/components/patterns/ArticleNotes.tsx | 15 ++++-- .../components/patterns/RcEditorStyles.tsx | 6 --- .../templates/article/EpubContainer.tsx | 1 - .../templates/article/HighlightsLayer.tsx | 1 - .../components/templates/article/Notebook.tsx | 47 ++++++++++--------- .../templates/article/NotebookModal.tsx | 41 +++------------- .../templates/article/NotebookPresenter.tsx | 2 - .../templates/article/PdfArticleContainer.tsx | 7 ++- .../templates/homeFeed/HighlightsLayout.tsx | 1 - 9 files changed, 44 insertions(+), 77 deletions(-) diff --git a/packages/web/components/patterns/ArticleNotes.tsx b/packages/web/components/patterns/ArticleNotes.tsx index c6c86f617..81da54d14 100644 --- a/packages/web/components/patterns/ArticleNotes.tsx +++ b/packages/web/components/patterns/ArticleNotes.tsx @@ -46,7 +46,9 @@ type NoteSectionProps = { placeHolder: string - text: string | undefined + text: string + setText: (text: string) => void + saveText: (text: string) => void } @@ -63,6 +65,7 @@ export function ArticleNotes(props: NoteSectionProps): JSX.Element { targetId={props.targetId} placeHolder={props.placeHolder} text={props.text} + setText={props.setText} saveText={saveText} fillBackground={false} /> @@ -79,7 +82,9 @@ type HighlightViewNoteProps = { setEditMode: (set: 'edit' | 'preview') => void - text: string | undefined + text: string + setText: (text: string) => void + updateHighlight: (highlight: Highlight) => void } @@ -108,6 +113,7 @@ export function HighlightViewNote(props: HighlightViewNoteProps): JSX.Element { targetId={props.targetId} placeHolder={props.placeHolder} text={props.text} + setText={props.setText} saveText={saveText} fillBackground={true} /> @@ -120,6 +126,7 @@ type MarkdownNote = { placeHolder: string text: string | undefined + setText: (text: string) => void fillBackground: boolean | undefined saveText: (text: string) => void @@ -147,6 +154,7 @@ export function MarkdownNote(props: MarkdownNote): JSX.Element { data: { text: string; html: string }, event?: ChangeEvent | undefined ) => { + props.setText(data.text) if (event) { event.preventDefault() } @@ -186,8 +194,7 @@ export function MarkdownNote(props: MarkdownNote): JSX.Element { { border: '1px solid $thBorderSubtle', backgroundColor: isDark ? '#2A2A2A' : 'white', }, - '.rc-md-editor:focus-within': { - outline: '2px solid $omnivoreCtaYellow', - borderRadius: '5px', - border: 'unset', - boxShadow: 'unset', - }, } } diff --git a/packages/web/components/templates/article/EpubContainer.tsx b/packages/web/components/templates/article/EpubContainer.tsx index e59e0a5aa..85a21ad84 100644 --- a/packages/web/components/templates/article/EpubContainer.tsx +++ b/packages/web/components/templates/article/EpubContainer.tsx @@ -333,7 +333,6 @@ export default function EpubContainer(props: EpubContainerProps): JSX.Element { key={notebookKey} viewer={props.viewer} item={props.article} - highlights={highlightsRef.current} onClose={(updatedHighlights, deletedAnnotations) => { console.log( 'closed PDF notebook: ', diff --git a/packages/web/components/templates/article/HighlightsLayer.tsx b/packages/web/components/templates/article/HighlightsLayer.tsx index 12f4a7fc1..8d147f362 100644 --- a/packages/web/components/templates/article/HighlightsLayer.tsx +++ b/packages/web/components/templates/article/HighlightsLayer.tsx @@ -749,7 +749,6 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { { // The timeout here is a bit of a hack to work around rerendering diff --git a/packages/web/components/templates/article/Notebook.tsx b/packages/web/components/templates/article/Notebook.tsx index bc13ba303..42b00e668 100644 --- a/packages/web/components/templates/article/Notebook.tsx +++ b/packages/web/components/templates/article/Notebook.tsx @@ -2,23 +2,8 @@ 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, - useRef, - useState, -} from 'react' -import { - BookOpen, - CaretDown, - CaretRight, - DotsThree, - Pencil, - PencilLine, - X, -} from 'phosphor-react' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { CaretDown, CaretRight } from 'phosphor-react' import { updateHighlightMutation } from '../../../lib/networking/mutations/updateHighlightMutation' import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers' import { diff_match_patch } from 'diff-match-patch' @@ -43,7 +28,6 @@ type NotebookContentProps = { viewer: UserBasicData item: ReadableItem - highlights: Highlight[] viewInReader: (highlightId: string) => void @@ -73,6 +57,7 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { username: props.viewer.profile.username, includeFriendsHighlights: false, }) + const [noteText, setNoteText] = useState('') const [showConfirmDeleteHighlightId, setShowConfirmDeleteHighlightId] = useState(undefined) const [labelsTarget, setLabelsTarget] = useState( @@ -138,6 +123,7 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { if (note) { noteState.current.note = note noteState.current.isCreating = false + setNoteText(note.annotation || '') } return result }, [articleData]) @@ -204,9 +190,24 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { } createNote(text) }, - [noteState, createNote, updateNote] + [noteText, noteState, createNote, updateNote, highlights] ) + const deleteDocumentNote = useCallback(() => { + ;(async () => { + highlights + ?.filter((h) => h.type === 'NOTE') + .forEach(async (h) => { + const result = await deleteHighlightMutation(h.id) + if (!result) { + showErrorToast('Error deleting note') + } + }) + noteState.current.note = undefined + })() + setNoteText('') + }, [noteState, highlights]) + const [articleNotesCollapsed, setArticleNotesCollapsed] = useState(false) const [highlightsCollapsed, setHighlightsCollapsed] = useState(false) const [errorSaving, setErrorSaving] = useState(undefined) @@ -238,7 +239,8 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { > @@ -308,7 +310,7 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { p: '10px', mt: '15px', width: '100%', - fontSize: '9px', + fontSize: '13px', color: '$thTextSubtle', alignItems: 'center', justifyContent: 'center', @@ -373,7 +375,7 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { message="Are you sure you want to delete the note from this document?" acceptButtonLabel="Delete" onAccept={() => { - // deleteDocumentNote() + deleteDocumentNote() if (props.setShowConfirmDeleteNote) { props.setShowConfirmDeleteNote(false) } @@ -403,7 +405,6 @@ function SectionTitle(props: SectionTitleProps): JSX.Element { css={{ display: 'flex', alignItems: 'center', - width: '100%', gap: '5px', }} onClick={(event) => { diff --git a/packages/web/components/templates/article/NotebookModal.tsx b/packages/web/components/templates/article/NotebookModal.tsx index 260b93e52..55205f6be 100644 --- a/packages/web/components/templates/article/NotebookModal.tsx +++ b/packages/web/components/templates/article/NotebookModal.tsx @@ -24,10 +24,9 @@ type NotebookModalProps = { viewer: UserBasicData item: ReadableItem - highlights: Highlight[] viewHighlightInReader: (arg: string) => void - onClose: (highlights: Highlight[]) => void + onClose: (highlights: Highlight[], deletedHighlights: Highlight[]) => void } export const getHighlightLocation = (patch: string): number | undefined => { @@ -42,13 +41,15 @@ export function NotebookModal(props: NotebookModalProps): JSX.Element { undefined ) + const [deletedHighlights, setDeletedAnnotations] = useState< + Highlight[] | undefined + >(undefined) + const handleClose = useCallback(() => { - console.log('closing: ', allAnnotations) - props.onClose(allAnnotations ?? []) + props.onClose(allAnnotations ?? [], deletedHighlights ?? []) }, [props, allAnnotations]) const handleAnnotationsChange = useCallback((allAnnotations) => { - console.log('all annotation: ', allAnnotations) setAllAnnotations(allAnnotations) }, []) @@ -191,33 +192,3 @@ function CloseButton(props: { close: () => void }): JSX.Element { ) } - -function SizeToggle(props: SizeToggleProps): JSX.Element { - return ( - - ) -} diff --git a/packages/web/components/templates/article/NotebookPresenter.tsx b/packages/web/components/templates/article/NotebookPresenter.tsx index 5676e5736..58380fdad 100644 --- a/packages/web/components/templates/article/NotebookPresenter.tsx +++ b/packages/web/components/templates/article/NotebookPresenter.tsx @@ -10,7 +10,6 @@ type NotebookPresenterProps = { viewer: UserBasicData item: ReadableItem - highlights: Highlight[] onClose: (highlights: Highlight[]) => void } @@ -20,7 +19,6 @@ export const NotebookPresenter = (props: NotebookPresenterProps) => { { console.log('NotebookModal: ', highlights) props.onClose(highlights) diff --git a/packages/web/components/templates/article/PdfArticleContainer.tsx b/packages/web/components/templates/article/PdfArticleContainer.tsx index cb4809a32..be65a9583 100644 --- a/packages/web/components/templates/article/PdfArticleContainer.tsx +++ b/packages/web/components/templates/article/PdfArticleContainer.tsx @@ -510,14 +510,13 @@ export default function PdfArticleContainer( key={notebookKey} viewer={props.viewer} item={props.article} - highlights={highlightsRef.current} - onClose={(updatedHighlights, deletedAnnotations) => { + onClose={(updatedHighlights, deletedHighlights) => { console.log( 'closed PDF notebook: ', updatedHighlights, - deletedAnnotations + deletedHighlights ) - deletedAnnotations.forEach((highlight) => { + deletedHighlights.forEach((highlight) => { const event = new CustomEvent('deleteHighlightbyId', { detail: highlight.id, }) diff --git a/packages/web/components/templates/homeFeed/HighlightsLayout.tsx b/packages/web/components/templates/homeFeed/HighlightsLayout.tsx index 04455fbb6..8184f40da 100644 --- a/packages/web/components/templates/homeFeed/HighlightsLayout.tsx +++ b/packages/web/components/templates/homeFeed/HighlightsLayout.tsx @@ -447,7 +447,6 @@ function HighlightList(props: HighlightListProps): JSX.Element { )} From 630a11e8782d7389a4e9711c6b6e380e2759ba50 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Fri, 23 Jun 2023 19:31:23 +0800 Subject: [PATCH 11/44] Remove unused props --- packages/web/components/templates/homeFeed/HomeFeedContainer.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index 831510bc9..e824a5b6c 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -1051,7 +1051,6 @@ function LibraryItemsLayout(props: LibraryItemsLayoutProps): JSX.Element { { if (props.notebookTarget?.node.highlights) { props.notebookTarget.node.highlights = highlights From cdaa0c28764739496eb822768ea3c1bfcfd9bbcc Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Fri, 23 Jun 2023 20:37:31 +0800 Subject: [PATCH 12/44] Put the highlight menu in a container --- .../web/components/patterns/ArticleNotes.tsx | 1 - .../web/components/patterns/HighlightView.tsx | 41 ++++++++++++++++--- .../LibraryCards/LibraryHighlightGridCard.tsx | 2 + .../templates/article/HighlightViewItem.tsx | 17 ++++---- .../components/templates/article/Notebook.tsx | 5 ++- .../templates/homeFeed/HighlightItem.tsx | 1 + 6 files changed, 51 insertions(+), 16 deletions(-) diff --git a/packages/web/components/patterns/ArticleNotes.tsx b/packages/web/components/patterns/ArticleNotes.tsx index 81da54d14..8ed2a25bc 100644 --- a/packages/web/components/patterns/ArticleNotes.tsx +++ b/packages/web/components/patterns/ArticleNotes.tsx @@ -182,7 +182,6 @@ export function MarkdownNote(props: MarkdownNote): JSX.Element { css={{ width: '100%', ...RcEditorStyles(isDark, true), - pr: '25px', }} onKeyDown={(event: React.KeyboardEvent) => { if (event.code.toLowerCase() === 'escape') { diff --git a/packages/web/components/patterns/HighlightView.tsx b/packages/web/components/patterns/HighlightView.tsx index 3a581f978..cca4aec7b 100644 --- a/packages/web/components/patterns/HighlightView.tsx +++ b/packages/web/components/patterns/HighlightView.tsx @@ -15,12 +15,22 @@ import { HighlightViewNote } from './HighlightNotes' import ReactMarkdown from 'react-markdown' import remarkGfm from 'remark-gfm' import { isDarkTheme } from '../../lib/themeUpdater' +import { HighlightsMenu } from '../templates/homeFeed/HighlightItem' +import { ReadableItem } from '../../lib/networking/queries/useGetLibraryItemsQuery' +import { UserBasicData } from '../../lib/networking/queries/useGetViewerQuery' type HighlightViewProps = { + item: ReadableItem + viewer: UserBasicData highlight: Highlight author?: string title?: string updateHighlight: (highlight: Highlight) => void + + viewInReader: (highlightId: string) => void + + setLabelsTarget: (target: Highlight) => void + setShowConfirmDeleteHighlightId: (set: string) => void } const StyledQuote = styled(Blockquote, { @@ -38,13 +48,14 @@ export function HighlightView(props: HighlightViewProps): JSX.Element { const [noteMode, setNoteMode] = useState<'preview' | 'edit'>('preview') return ( - + + + {/* - {/* */} - + */} - + ) } diff --git a/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx index 0d2de7109..63ab46060 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx @@ -130,6 +130,8 @@ export function LibraryHighlightGridCard( { console.log('updated highlight: ', highlight) diff --git a/packages/web/components/templates/article/HighlightViewItem.tsx b/packages/web/components/templates/article/HighlightViewItem.tsx index f94b45194..ac3cfab5c 100644 --- a/packages/web/components/templates/article/HighlightViewItem.tsx +++ b/packages/web/components/templates/article/HighlightViewItem.tsx @@ -2,7 +2,7 @@ import { useState } from 'react' import { Highlight } from '../../../lib/networking/fragments/highlightFragment' import { ReadableItem } from '../../../lib/networking/queries/useGetLibraryItemsQuery' import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery' -import { HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives' +import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives' import { HighlightView } from '../../patterns/HighlightView' import { HighlightsMenu } from '../homeFeed/HighlightItem' @@ -35,19 +35,18 @@ export function HighlightViewItem(props: HighlightViewItemProps): JSX.Element { > - ) } diff --git a/packages/web/components/templates/article/Notebook.tsx b/packages/web/components/templates/article/Notebook.tsx index 42b00e668..75f703d82 100644 --- a/packages/web/components/templates/article/Notebook.tsx +++ b/packages/web/components/templates/article/Notebook.tsx @@ -367,7 +367,10 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { setLabelsTarget(undefined)} + onOpenChange={() => { + mutate() + setLabelsTarget(undefined) + }} /> )} {props.showConfirmDeleteNote && ( diff --git a/packages/web/components/templates/homeFeed/HighlightItem.tsx b/packages/web/components/templates/homeFeed/HighlightItem.tsx index 2588de8b6..69af42634 100644 --- a/packages/web/components/templates/homeFeed/HighlightItem.tsx +++ b/packages/web/components/templates/homeFeed/HighlightItem.tsx @@ -66,6 +66,7 @@ export function HighlightsMenu(props: HighlightsMenuProps): JSX.Element { triggerElement={ Date: Fri, 23 Jun 2023 21:02:59 +0800 Subject: [PATCH 13/44] Add a header to the highglight view --- .../web/components/patterns/HighlightView.tsx | 48 ++++++++++++------- .../LibraryCards/LibraryHighlightGridCard.tsx | 8 ++-- .../components/templates/article/Notebook.tsx | 1 - 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/packages/web/components/patterns/HighlightView.tsx b/packages/web/components/patterns/HighlightView.tsx index cca4aec7b..f982d15f3 100644 --- a/packages/web/components/patterns/HighlightView.tsx +++ b/packages/web/components/patterns/HighlightView.tsx @@ -1,5 +1,11 @@ /* eslint-disable react/no-children-prop */ -import { BookOpen, CaretDown, PencilLine } from 'phosphor-react' +import { + BookOpen, + CaretDown, + HighlighterCircle, + Notebook, + PencilLine, +} from 'phosphor-react' import { useState } from 'react' import type { Highlight } from '../../lib/networking/fragments/highlightFragment' import { LabelChip } from '../elements/LabelChip' @@ -10,7 +16,7 @@ import { SpanBox, HStack, } from '../elements/LayoutPrimitives' -import { styled } from '../tokens/stitches.config' +import { styled, theme } from '../tokens/stitches.config' import { HighlightViewNote } from './HighlightNotes' import ReactMarkdown from 'react-markdown' import remarkGfm from 'remark-gfm' @@ -62,27 +68,35 @@ export function HighlightView(props: HighlightViewProps): JSX.Element { }, }} > - - - + {props.highlight.annotation ? ( + + ) : ( + + )} + + + + + {/* { @@ -123,16 +122,19 @@ export function LibraryHighlightGridCard( <> {sortedHighlights.map((highlight) => ( - + {}} + setLabelsTarget={() => {}} + setShowConfirmDeleteHighlightId={() => {}} updateHighlight={(highlight) => { console.log('updated highlight: ', highlight) }} diff --git a/packages/web/components/templates/article/Notebook.tsx b/packages/web/components/templates/article/Notebook.tsx index 75f703d82..118b1d050 100644 --- a/packages/web/components/templates/article/Notebook.tsx +++ b/packages/web/components/templates/article/Notebook.tsx @@ -221,7 +221,6 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { height: '100%', width: '100%', p: '20px', - bg: '$readerMargin', '@mdDown': { p: '15px' }, }} > From 300f27a83ea7b0ca253e60ebb6108b9f51a59e32 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Fri, 23 Jun 2023 21:15:23 +0800 Subject: [PATCH 14/44] view in reader for the small screen highlights view --- .../LibraryCards/LibraryHighlightGridCard.tsx | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx index 7f4179ae6..72e554f38 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx @@ -1,5 +1,5 @@ import { Box, VStack, HStack, SpanBox } from '../../elements/LayoutPrimitives' -import { useMemo, useState } from 'react' +import { useCallback, useMemo, useState } from 'react' import { CaretDown, CaretUp } from 'phosphor-react' import { MetaStyle, timeAgo, TitleStyle } from './LibraryCardStyles' import { styled } from '@stitches/react' @@ -10,6 +10,8 @@ import { theme } from '../../tokens/stitches.config' import { getHighlightLocation } from '../../templates/article/NotebookModal' import { Highlight } from '../../../lib/networking/fragments/highlightFragment' import { HighlightView } from '../HighlightView' +import { useRouter } from 'next/router' +import { showErrorToast } from '../../../lib/toastHelpers' export const GridSeparator = styled(Box, { height: '1px', @@ -29,6 +31,31 @@ export function LibraryHighlightGridCard( ): JSX.Element { const [expanded, setExpanded] = useState(false) const higlightCount = props.item.highlights?.length ?? 0 + const router = useRouter() + const viewInReader = useCallback( + (highlightId) => { + if (!router || !router.isReady || !props.viewer) { + showErrorToast('Error navigating to highlight') + return + } + console.log('pushing user: ', props.viewer, 'slug: ', props.item.slug) + router.push( + { + pathname: '/[username]/[slug]', + query: { + username: props.viewer.profile.username, + slug: props.item.slug, + }, + hash: highlightId, + }, + `${props.viewer.profile.username}/${props.item.slug}#${highlightId}`, + { + scroll: false, + } + ) + }, + [router, props] + ) const sortedHighlights = useMemo(() => { const sorted = (a: number, b: number) => { @@ -132,7 +159,7 @@ export function LibraryHighlightGridCard( viewer={props.viewer} item={props.item} highlight={highlight} - viewInReader={() => {}} + viewInReader={viewInReader} setLabelsTarget={() => {}} setShowConfirmDeleteHighlightId={() => {}} updateHighlight={(highlight) => { From 83012946dca9daefb471d61e80a6910c33a12a11 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Fri, 23 Jun 2023 21:24:32 +0800 Subject: [PATCH 15/44] fix empty arrow funcs --- .../patterns/LibraryCards/LibraryHighlightGridCard.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx index 72e554f38..26aecc3e7 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx @@ -160,8 +160,12 @@ export function LibraryHighlightGridCard( item={props.item} highlight={highlight} viewInReader={viewInReader} - setLabelsTarget={() => {}} - setShowConfirmDeleteHighlightId={() => {}} + setLabelsTarget={() => { + console.log('TODO: set labels') + }} + setShowConfirmDeleteHighlightId={() => { + console.log('TODO: confirm delete') + }} updateHighlight={(highlight) => { console.log('updated highlight: ', highlight) }} From b916b4a065acc76dab495e62fc980bceb3538c5d Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 26 Jun 2023 10:14:23 +0800 Subject: [PATCH 16/44] Fix bug preventing setting a highlight note to an empty string --- packages/web/components/patterns/HighlightNotes.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/web/components/patterns/HighlightNotes.tsx b/packages/web/components/patterns/HighlightNotes.tsx index f2194ed5c..298437d16 100644 --- a/packages/web/components/patterns/HighlightNotes.tsx +++ b/packages/web/components/patterns/HighlightNotes.tsx @@ -234,8 +234,8 @@ export function MarkdownNote(props: MarkdownNote): JSX.Element { - - */} - setMenuOpen(open)} - actionHandler={props.handleAction} - triggerElement={ - - } - /> - )} + void +} + +export const LibraryHoverActions = (props: LibraryHoverActionsProps) => { + const [menuOpen, setMenuOpen] = useState(false) + + return ( + + + + + + + setMenuOpen(open)} + actionHandler={props.handleAction} + triggerElement={ + + + + } + /> + + + ) +} diff --git a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx index fe3956871..594a48725 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx @@ -27,6 +27,7 @@ import { sortedLabels } from '../../../lib/labelsSort' import { LIBRARY_LEFT_MENU_WIDTH } from '../../templates/homeFeed/LibraryFilterMenu' import { Button } from '../../elements/Button' import { theme } from '../../tokens/stitches.config' +import { LibraryHoverActions } from './LibraryHoverActions' export function LibraryListCard(props: LinkedItemCardProps): JSX.Element { const [isHovered, setIsHovered] = useState(false) @@ -67,18 +68,26 @@ export function LibraryListCard(props: LinkedItemCardProps): JSX.Element { {props.inMultiSelect ? ( ) : ( - - + + - - - + + + + + )} ) @@ -99,85 +108,13 @@ export function LibraryListCardContent( <> - {props.inMultiSelect ? ( + {props.inMultiSelect && ( - ) : ( - - - - - - setMenuOpen(open)} - actionHandler={props.handleAction} - triggerElement={ - - } - /> - )} void +} + +export const NotebookHeader = (props: NotebookHeaderProps) => { + const handleClose = useCallback(() => { + props.setShowNotebook(false) + }, [props]) + + return ( + + + Notebook + + + }> + {/* { + exportHighlights() + }} + title="Export Notebook" + /> + { + setShowConfirmDeleteNote(true) + }} + title="Delete Article Note" + /> */} + + + + + ) +} From d4ccde4cd293dae8e8d254e09ddb45d949f626f5 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 26 Jun 2023 14:26:05 +0800 Subject: [PATCH 18/44] Improve hover actions on library cards, start to add spaced list view --- .../patterns/LibraryCards/LibraryGridCard.tsx | 3 ++ .../LibraryCards/LibraryHoverActions.tsx | 33 ++++++++++----- .../patterns/LibraryCards/LibraryListCard.tsx | 10 +++-- .../templates/homeFeed/HomeFeedContainer.tsx | 2 +- packages/web/package.json | 1 + yarn.lock | 40 +++++++++++++++++++ 6 files changed, 74 insertions(+), 15 deletions(-) diff --git a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx index 76335c5ff..25c514a6c 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx @@ -64,9 +64,11 @@ export function ProgressBar(props: ProgressBarProps): JSX.Element { export function LibraryGridCard(props: LinkedItemCardProps): JSX.Element { const [isHovered, setIsHovered] = useState(false) + const [anchor, setAnchor] = useState(null) return ( { const [menuOpen, setMenuOpen] = useState(false) + const { refs, floatingStyles } = useFloating({ + elements: { + reference: props.anchor, + }, + middleware: [ + offset({ + mainAxis: -44, + crossAxis: -10, + }), + ], + placement: 'top-end', + }) return ( - + (null) return ( { diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index e824a5b6c..6df99d810 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -1100,7 +1100,7 @@ function LibraryItems(props: LibraryItemsProps): JSX.Element { width: '100%', gridAutoRows: 'auto', borderRadius: '6px', - gridGap: props.layout == 'LIST_LAYOUT' ? '0' : '20px', + gridGap: props.layout == 'LIST_LAYOUT' ? '10px' : '20px', marginTop: '10px', marginBottom: '0px', paddingTop: '0', diff --git a/packages/web/package.json b/packages/web/package.json index 5e7657691..bbce82d83 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -18,6 +18,7 @@ "build-storybook": "build-storybook -s public" }, "dependencies": { + "@floating-ui/react": "^0.24.3", "@radix-ui/react-avatar": "^0.1.1", "@radix-ui/react-checkbox": "^0.1.5", "@radix-ui/react-dialog": "^0.1.1", diff --git a/yarn.lock b/yarn.lock index 025cd3b97..36db67d18 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2504,6 +2504,34 @@ dependencies: tslib "^2.1.0" +"@floating-ui/core@^1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.3.1.tgz#4d795b649cc3b1cbb760d191c80dcb4353c9a366" + integrity sha512-Bu+AMaXNjrpjh41znzHqaz3r2Nr8hHuHZT6V2LBKMhyMl0FgKA62PNYbqnfgmzOhoWZj70Zecisbo4H1rotP5g== + +"@floating-ui/dom@^1.3.0": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.4.2.tgz#eb3a37f7506c4f95ef735967dc3496b5012e11cb" + integrity sha512-VKmvHVatWnewmGGy+7Mdy4cTJX71Pli6v/Wjb5RQBuq5wjUYx+Ef+kRThi8qggZqDgD8CogCpqhRoVp3+yQk+g== + dependencies: + "@floating-ui/core" "^1.3.1" + +"@floating-ui/react-dom@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.0.1.tgz#7972a4fc488a8c746cded3cfe603b6057c308a91" + integrity sha512-rZtAmSht4Lry6gdhAJDrCp/6rKN7++JnL1/Anbr/DdeyYXQPxvg/ivrbYvJulbRf4vL8b212suwMM2lxbv+RQA== + dependencies: + "@floating-ui/dom" "^1.3.0" + +"@floating-ui/react@^0.24.3": + version "0.24.3" + resolved "https://registry.yarnpkg.com/@floating-ui/react/-/react-0.24.3.tgz#4f11f09c7245555724f5167dd6925133457db89c" + integrity sha512-wWC9duiog4HmbgKSKObDRuXqMjZR/6m75MIG+slm5CVWbridAjK9STcnCsGYmdpK78H/GmzYj4ADVP8paZVLYQ== + dependencies: + "@floating-ui/react-dom" "^2.0.1" + aria-hidden "^1.1.3" + tabbable "^6.0.1" + "@google-cloud/common@^3.8.1": version "3.9.0" resolved "https://registry.yarnpkg.com/@google-cloud/common/-/common-3.9.0.tgz#d93e62d13e66edacfad1cd25b20fdbbc11d9f6dd" @@ -10070,6 +10098,13 @@ aria-hidden@^1.1.1: dependencies: tslib "^1.0.0" +aria-hidden@^1.1.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.3.tgz#14aeb7fb692bbb72d69bebfa47279c1fd725e954" + integrity sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ== + dependencies: + tslib "^2.0.0" + aria-query@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b" @@ -26195,6 +26230,11 @@ synchronous-promise@^2.0.15: resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.15.tgz#07ca1822b9de0001f5ff73595f3d08c4f720eb8e" integrity sha512-k8uzYIkIVwmT+TcglpdN50pS2y1BDcUnBPK9iJeGu0Pl1lOI8pD6wtzgw91Pjpe+RxtTncw32tLxs/R0yNL2Mg== +tabbable@^6.0.1: + version "6.1.2" + resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.1.2.tgz#b0d3ca81d582d48a80f71b267d1434b1469a3703" + integrity sha512-qCN98uP7i9z0fIS4amQ5zbGBOq+OSigYeGvPy7NDk8Y9yncqDZ9pRPgfsc2PJIVM9RrJj7GIfuRgmjoUU9zTHQ== + tapable@^1.0.0, tapable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" From 92b4f0e52360b7f8899487e9f8da58f0951659b7 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 26 Jun 2023 15:38:28 +0800 Subject: [PATCH 19/44] Better hover action positioning --- .../patterns/LibraryCards/LibraryGridCard.tsx | 50 ++++- .../LibraryCards/LibraryHoverActions.tsx | 192 ++++++++---------- .../patterns/LibraryCards/LibraryListCard.tsx | 77 ++++--- .../templates/homeFeed/HomeFeedContainer.tsx | 8 +- 4 files changed, 178 insertions(+), 149 deletions(-) diff --git a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx index 25c514a6c..5fbad4e89 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx @@ -20,7 +20,6 @@ import { CardCheckbox, DescriptionStyle, LibraryItemMetadata, - MenuStyle, MetaStyle, siteName, TitleStyle, @@ -29,6 +28,14 @@ import { sortedLabels } from '../../../lib/labelsSort' import { Button } from '../../elements/Button' import { theme } from '../../tokens/stitches.config' import { LibraryHoverActions } from './LibraryHoverActions' +import { + useHover, + useFloating, + useInteractions, + size, + offset, + autoUpdate, +} from '@floating-ui/react' dayjs.extend(relativeTime) @@ -64,11 +71,29 @@ export function ProgressBar(props: ProgressBarProps): JSX.Element { export function LibraryGridCard(props: LinkedItemCardProps): JSX.Element { const [isHovered, setIsHovered] = useState(false) - const [anchor, setAnchor] = useState(null) + const [isOpen, setIsOpen] = useState(false) + + const { refs, floatingStyles, context } = useFloating({ + open: isOpen, + onOpenChange: setIsOpen, + middleware: [ + offset({ + mainAxis: -25, + }), + size(), + ], + placement: 'top-end', + whileElementsMounted: autoUpdate, + }) + + const hover = useHover(context) + + const { getReferenceProps, getFloatingProps } = useInteractions([hover]) return ( ) : ( <> - + + + { const [menuOpen, setMenuOpen] = useState(false) - const { refs, floatingStyles } = useFloating({ - elements: { - reference: props.anchor, - }, - middleware: [ - offset({ - mainAxis: -44, - crossAxis: -10, - }), - ], - placement: 'top-end', - }) return ( - - + + - + + setMenuOpen(open)} + actionHandler={props.handleAction} + triggerElement={ + + - )} - - - - setMenuOpen(open)} - actionHandler={props.handleAction} - triggerElement={ - - - - } - /> - + + } + /> ) } diff --git a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx index cd0bf8490..3b69d39b7 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx @@ -1,41 +1,55 @@ import { Box, VStack, HStack, SpanBox } from '../../elements/LayoutPrimitives' import { LabelChip } from '../../elements/LabelChip' import type { LinkedItemCardProps } from './CardTypes' -import { useCallback, useState } from 'react' -import { - Archive, - ArchiveBox, - DotsThree, - Note, - Notebook, - Tag, - Trash, - Tray, -} from 'phosphor-react' +import { useCallback, useEffect, useState } from 'react' import Link from 'next/link' -import { CardMenu } from '../CardMenu' import { AuthorInfoStyle, CardCheckbox, LibraryItemMetadata, - MenuStyle, MetaStyle, siteName, TitleStyle, } from './LibraryCardStyles' import { sortedLabels } from '../../../lib/labelsSort' import { LIBRARY_LEFT_MENU_WIDTH } from '../../templates/homeFeed/LibraryFilterMenu' -import { Button } from '../../elements/Button' -import { theme } from '../../tokens/stitches.config' import { LibraryHoverActions } from './LibraryHoverActions' +import { + useHover, + useFloating, + useInteractions, + size, + offset, + autoUpdate, +} from '@floating-ui/react' export function LibraryListCard(props: LinkedItemCardProps): JSX.Element { const [isHovered, setIsHovered] = useState(false) - const [anchor, setAnchor] = useState(null) + + const [isOpen, setIsOpen] = useState(false) + + const { refs, floatingStyles, context } = useFloating({ + open: isOpen, + onOpenChange: setIsOpen, + middleware: [ + offset({ + mainAxis: -25, + // crossAxis: -10, + }), + size(), + ], + placement: 'top-end', + whileElementsMounted: autoUpdate, + }) + + const hover = useHover(context) + + const { getReferenceProps, getFloatingProps } = useInteractions([hover]) return ( ) : ( <> - + + + - {props.item.title} + + {props.item.title} + Date: Mon, 26 Jun 2023 15:58:53 +0800 Subject: [PATCH 20/44] Linting cleanup --- .../patterns/LibraryCards/LibraryGridCard.tsx | 11 ----------- .../patterns/LibraryCards/LibraryHoverActions.tsx | 1 - .../patterns/LibraryCards/LibraryListCard.tsx | 2 +- 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx index 5fbad4e89..faafe6cf7 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx @@ -5,16 +5,7 @@ import { CoverImage } from '../../elements/CoverImage' import dayjs from 'dayjs' import relativeTime from 'dayjs/plugin/relativeTime' import { useCallback, useState } from 'react' -import { - ArchiveBox, - DotsThree, - DotsThreeVertical, - Tag, - Trash, - Tray, -} from 'phosphor-react' import Link from 'next/link' -import { CardMenu } from '../CardMenu' import { AuthorInfoStyle, CardCheckbox, @@ -25,8 +16,6 @@ import { TitleStyle, } from './LibraryCardStyles' import { sortedLabels } from '../../../lib/labelsSort' -import { Button } from '../../elements/Button' -import { theme } from '../../tokens/stitches.config' import { LibraryHoverActions } from './LibraryHoverActions' import { useHover, diff --git a/packages/web/components/patterns/LibraryCards/LibraryHoverActions.tsx b/packages/web/components/patterns/LibraryCards/LibraryHoverActions.tsx index bdcb580d2..d17c39bca 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryHoverActions.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryHoverActions.tsx @@ -14,7 +14,6 @@ import { } from 'phosphor-react' import { CardMenu } from '../CardMenu' import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery' -import { useFloating, shift, offset } from '@floating-ui/react' type LibraryHoverActionsProps = { viewer: UserBasicData diff --git a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx index 3b69d39b7..3fa3968e4 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx @@ -1,7 +1,7 @@ import { Box, VStack, HStack, SpanBox } from '../../elements/LayoutPrimitives' import { LabelChip } from '../../elements/LabelChip' import type { LinkedItemCardProps } from './CardTypes' -import { useCallback, useEffect, useState } from 'react' +import { useCallback, useState } from 'react' import Link from 'next/link' import { AuthorInfoStyle, From 3f17538cb8889ee021c152523a0145afd4f82f48 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 26 Jun 2023 16:27:31 +0800 Subject: [PATCH 21/44] Always display the dropdown menu on touch devices --- .../LibraryCards/LibraryCardStyles.tsx | 16 +++++++++++ .../patterns/LibraryCards/LibraryGridCard.tsx | 26 +++++++++++++++++- .../patterns/LibraryCards/LibraryListCard.tsx | 27 +++++++++++++++++-- 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/packages/web/components/patterns/LibraryCards/LibraryCardStyles.tsx b/packages/web/components/patterns/LibraryCards/LibraryCardStyles.tsx index d04bcc083..259d6592c 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryCardStyles.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryCardStyles.tsx @@ -6,6 +6,22 @@ import { Box, SpanBox } from '../../elements/LayoutPrimitives' dayjs.extend(relativeTime) +export const MenuStyle = { + display: 'flex', + marginLeft: 'auto', + height: '30px', + width: '30px', + mt: '-5px', + mr: '-5px', + pt: '2px', + alignItems: 'center', + justifyContent: 'center', + borderRadius: '1000px', + '&:hover': { + bg: '$thBackground4', + }, +} + export const MetaStyle = { width: '100%', color: '$thTextSubtle3', diff --git a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx index faafe6cf7..263461044 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx @@ -14,6 +14,7 @@ import { MetaStyle, siteName, TitleStyle, + MenuStyle, } from './LibraryCardStyles' import { sortedLabels } from '../../../lib/labelsSort' import { LibraryHoverActions } from './LibraryHoverActions' @@ -25,6 +26,8 @@ import { offset, autoUpdate, } from '@floating-ui/react' +import { CardMenu } from '../CardMenu' +import { DotsThree } from 'phosphor-react' dayjs.extend(relativeTime) @@ -145,6 +148,7 @@ export function LibraryGridCard(props: LinkedItemCardProps): JSX.Element { const LibraryGridCardContent = (props: LinkedItemCardProps): JSX.Element => { const { isChecked, setIsChecked, item } = props + const [menuOpen, setMenuOpen] = useState(false) const originText = siteName(props.item.originalArticleUrl, props.item.url) const handleCheckChanged = useCallback(() => { @@ -161,13 +165,33 @@ const LibraryGridCardContent = (props: LinkedItemCardProps): JSX.Element => { distribution="start" > - {props.inMultiSelect && ( + {props.inMultiSelect ? ( + ) : ( + + setMenuOpen(open)} + actionHandler={props.handleAction} + triggerElement={ + + } + /> + )} diff --git a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx index 3fa3968e4..99470edea 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx @@ -10,6 +10,7 @@ import { MetaStyle, siteName, TitleStyle, + MenuStyle, } from './LibraryCardStyles' import { sortedLabels } from '../../../lib/labelsSort' import { LIBRARY_LEFT_MENU_WIDTH } from '../../templates/homeFeed/LibraryFilterMenu' @@ -22,6 +23,8 @@ import { offset, autoUpdate, } from '@floating-ui/react' +import { CardMenu } from '../CardMenu' +import { DotsThree } from 'phosphor-react' export function LibraryListCard(props: LinkedItemCardProps): JSX.Element { const [isHovered, setIsHovered] = useState(false) @@ -34,7 +37,6 @@ export function LibraryListCard(props: LinkedItemCardProps): JSX.Element { middleware: [ offset({ mainAxis: -25, - // crossAxis: -10, }), size(), ], @@ -126,6 +128,7 @@ export function LibraryListCard(props: LinkedItemCardProps): JSX.Element { export function LibraryListCardContent( props: LinkedItemCardProps ): JSX.Element { + const [menuOpen, setMenuOpen] = useState(false) const { isChecked, setIsChecked, item } = props const originText = siteName(props.item.originalArticleUrl, props.item.url) @@ -137,13 +140,33 @@ export function LibraryListCardContent( <> - {props.inMultiSelect && ( + {props.inMultiSelect ? ( + ) : ( + + setMenuOpen(open)} + actionHandler={props.handleAction} + triggerElement={ + + } + /> + )} Date: Mon, 26 Jun 2023 16:30:02 +0800 Subject: [PATCH 22/44] Clean up label styling --- .../web/components/elements/LabelChip.tsx | 28 +------------------ 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/packages/web/components/elements/LabelChip.tsx b/packages/web/components/elements/LabelChip.tsx index ee4544b8c..89b36569b 100644 --- a/packages/web/components/elements/LabelChip.tsx +++ b/packages/web/components/elements/LabelChip.tsx @@ -11,19 +11,14 @@ type LabelChipProps = { color: string // expected to be a RGB hex color string isSelected?: boolean useAppAppearance?: boolean - xAction?: () => void } export function LabelChip(props: LabelChipProps): JSX.Element { - const router = useRouter() const isDark = isDarkTheme() - const luminance = getLuminance(props.color) - const textColor = luminance > 0.5 ? '#000000' : '#ffffff' const selectedBorder = isDark ? '#FFEA9F' : 'black' const unSelectedBorder = isDark ? '#6A6968' : '#D9D9D9' - // if (props.useAppAppearance) { return ( - + {props.text} - {props.xAction && ( - - )} ) From aab893584e13ff67d2061233a723e640f969f865 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 27 Jun 2023 11:51:12 +0800 Subject: [PATCH 23/44] MOre work on notebook side pane --- packages/web/components/elements/Button.tsx | 1 + .../patterns/HighlightHoverActions.tsx | 113 ++++++++ .../components/patterns/HighlightNotes.tsx | 18 +- .../web/components/patterns/HighlightView.tsx | 135 +++++----- .../components/patterns/RcEditorStyles.tsx | 1 - .../templates/article/HighlightViewItem.tsx | 5 +- .../templates/article/HighlightsLayer.tsx | 61 ++++- .../components/templates/article/Notebook.tsx | 245 +++++++++--------- .../templates/article/NotebookHeader.tsx | 12 +- .../templates/article/SetLabelsModal.tsx | 84 +++--- packages/web/package.json | 1 + packages/web/styles/globals.css | 30 ++- yarn.lock | 30 ++- 13 files changed, 446 insertions(+), 290 deletions(-) create mode 100644 packages/web/components/patterns/HighlightHoverActions.tsx diff --git a/packages/web/components/elements/Button.tsx b/packages/web/components/elements/Button.tsx index dbd64c75a..e9085ad81 100644 --- a/packages/web/components/elements/Button.tsx +++ b/packages/web/components/elements/Button.tsx @@ -263,6 +263,7 @@ export const Button = styled('button', { padding: '4px', height: '100%', pt: '6px', + minWidth: '25px', '&:hover': { bg: '$grayBgHover', diff --git a/packages/web/components/patterns/HighlightHoverActions.tsx b/packages/web/components/patterns/HighlightHoverActions.tsx new file mode 100644 index 000000000..4d3c9ae5e --- /dev/null +++ b/packages/web/components/patterns/HighlightHoverActions.tsx @@ -0,0 +1,113 @@ +import { useState } from 'react' +import { Box, SpanBox } from '../elements/LayoutPrimitives' +import { LibraryItemNode } from '../../lib/networking/queries/useGetLibraryItemsQuery' +import { Button } from '../elements/Button' +import { theme } from '../tokens/stitches.config' +import { + ArchiveBox, + Book, + BookOpen, + Copy, + DotsThree, + Notebook, + Tag, + Trash, + Tray, +} from 'phosphor-react' +//import { CardMenu } from '../CardMenu' +import { UserBasicData } from '../../lib/networking/queries/useGetViewerQuery' +import { Highlight } from '../../lib/networking/fragments/highlightFragment' +import { showErrorToast, showSuccessToast } from '../../lib/toastHelpers' + +type HighlightHoverActionsProps = { + viewer: UserBasicData + highlight: Highlight + + isHovered: boolean + + viewInReader: (highlightId: string) => void + + setLabelsTarget: (target: Highlight) => void + setShowConfirmDeleteHighlightId: (set: string) => void +} + +export const HighlightHoverActions = (props: HighlightHoverActionsProps) => { + const [menuOpen, setMenuOpen] = useState(false) + + return ( + + + + + + + ) +} diff --git a/packages/web/components/patterns/HighlightNotes.tsx b/packages/web/components/patterns/HighlightNotes.tsx index 298437d16..c24df1193 100644 --- a/packages/web/components/patterns/HighlightNotes.tsx +++ b/packages/web/components/patterns/HighlightNotes.tsx @@ -145,6 +145,7 @@ export function MarkdownNote(props: MarkdownNote): JSX.Element { {props.mode == 'edit' ? ( ('preview') + const [isHovered, setIsHovered] = useState(false) + const [isOpen, setIsOpen] = useState(false) + + const { refs, floatingStyles, context } = useFloating({ + open: isOpen, + onOpenChange: setIsOpen, + middleware: [ + offset({ + mainAxis: -25, + }), + size(), + ], + placement: 'top-end', + whileElementsMounted: autoUpdate, + }) + + const hover = useHover(context) + + const { getReferenceProps, getFloatingProps } = useInteractions([hover]) + + console.log( + 'ref={refs.setFloating, style={floatingStyles}', + refs.setFloating, + floatingStyles + ) return ( - - {props.highlight.annotation ? ( - - ) : ( - - )} - - - - - - {/* - - */} - + *': { m: '0px', + display: 'inline', + padding: '2px', + backgroundColor: + 'rgba(var(--colors-highlightBackground), 0.35)', + boxShadow: + '1px 0 0 rgba(var(--colors-highlightBackground), 0.35), -1px 0 0 rgba(var(--colors-highlightBackground), 0.35)', + boxDecorationBreak: 'clone', + borderRadius: '2px', }, fontSize: '15px', lineHeight: 1.5, diff --git a/packages/web/components/patterns/RcEditorStyles.tsx b/packages/web/components/patterns/RcEditorStyles.tsx index 058cc6bbe..b53cba22a 100644 --- a/packages/web/components/patterns/RcEditorStyles.tsx +++ b/packages/web/components/patterns/RcEditorStyles.tsx @@ -8,7 +8,6 @@ export const RcEditorStyles = (isDark: boolean, shadow: boolean) => { borderRadius: '5px', backgroundColor: isDark ? '#2A2A2A' : 'white', border: '1px solid $thBorderSubtle', - boxShadow: shadow ? '0px 4px 4px rgba(33, 33, 33, 0.1)' : 'unset', }, '.rc-md-navigation': { borderRadius: '5px', diff --git a/packages/web/components/templates/article/HighlightViewItem.tsx b/packages/web/components/templates/article/HighlightViewItem.tsx index ac3cfab5c..df2440929 100644 --- a/packages/web/components/templates/article/HighlightViewItem.tsx +++ b/packages/web/components/templates/article/HighlightViewItem.tsx @@ -4,7 +4,6 @@ import { ReadableItem } from '../../../lib/networking/queries/useGetLibraryItems import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery' import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives' import { HighlightView } from '../../patterns/HighlightView' -import { HighlightsMenu } from '../homeFeed/HighlightItem' type HighlightViewItemProps = { viewer: UserBasicData @@ -27,8 +26,8 @@ export function HighlightViewItem(props: HighlightViewItemProps): JSX.Element { setHover(true)} onMouseLeave={() => setHover(false)} diff --git a/packages/web/components/templates/article/HighlightsLayer.tsx b/packages/web/components/templates/article/HighlightsLayer.tsx index 8d147f362..2914a08c0 100644 --- a/packages/web/components/templates/article/HighlightsLayer.tsx +++ b/packages/web/components/templates/article/HighlightsLayer.tsx @@ -26,6 +26,10 @@ import { isTouchScreenDevice } from '../../../lib/deviceType' import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery' import { ReadableItem } from '../../../lib/networking/queries/useGetLibraryItemsQuery' import { SetHighlightLabelsModalPresenter } from './SetLabelsModalPresenter' +import SlidingPane from 'react-sliding-pane' +import 'react-sliding-pane/dist/react-sliding-pane.css' +import { NotebookContent } from './Notebook' +import { NotebookHeader } from './NotebookHeader' type HighlightsLayerProps = { viewer: UserBasicData @@ -74,15 +78,13 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { const focusedHighlightMousePos = useRef({ pageX: 0, pageY: 0 }) const [currentHighlightIdx, setCurrentHighlightIdx] = useState(0) - const [focusedHighlight, setFocusedHighlight] = useState< - Highlight | undefined - >(undefined) + const [focusedHighlight, setFocusedHighlight] = + useState(undefined) const [selectionData, setSelectionData] = useSelection(highlightLocations) - const [labelsTarget, setLabelsTarget] = useState( - undefined - ) + const [labelsTarget, setLabelsTarget] = + useState(undefined) const createHighlightFromSelection = useCallback( async ( @@ -746,6 +748,50 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { if (props.showHighlightsModal) { return ( + { + props.setShowHighlightsModal(false) + }} + > + <> + + { + // The timeout here is a bit of a hack to work around rerendering + setTimeout(() => { + const target = document.querySelector( + `[omnivore-highlight-id="${highlightId}"]` + ) + target?.scrollIntoView({ + block: 'center', + behavior: 'auto', + }) + }, 1) + history.replaceState( + undefined, + window.location.href, + `#${highlightId}` + ) + + // props.setShowHighlightsModal(false) + }} + /> + + + ) + + { + /* return ( - ) + ) */ + } } return <> diff --git a/packages/web/components/templates/article/Notebook.tsx b/packages/web/components/templates/article/Notebook.tsx index 118b1d050..86cf96573 100644 --- a/packages/web/components/templates/article/Notebook.tsx +++ b/packages/web/components/templates/article/Notebook.tsx @@ -60,9 +60,8 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { const [noteText, setNoteText] = useState('') const [showConfirmDeleteHighlightId, setShowConfirmDeleteHighlightId] = useState(undefined) - const [labelsTarget, setLabelsTarget] = useState( - undefined - ) + const [labelsTarget, setLabelsTarget] = + useState(undefined) const noteState = useRef({ isCreating: false, note: undefined, @@ -174,12 +173,9 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { 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) @@ -208,8 +204,8 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { setNoteText('') }, [noteState, highlights]) - const [articleNotesCollapsed, setArticleNotesCollapsed] = useState(false) - const [highlightsCollapsed, setHighlightsCollapsed] = useState(false) + const [tabSelected, setTabSelected] = useState<'note' | 'highlights'>('note') + const [errorSaving, setErrorSaving] = useState(undefined) const [lastChanged, setLastChanged] = useState(undefined) const [lastSaved, setLastSaved] = useState(undefined) @@ -221,120 +217,118 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { height: '100%', width: '100%', p: '20px', + + bg: '$thLibrarySearchbox', '@mdDown': { p: '15px' }, }} > - - {!articleNotesCollapsed && ( - <> - - - - - {errorSaving && ( - - {errorSaving} - - )} - {lastSaved !== undefined ? ( - <> - {lastChanged === lastSaved - ? 'Saved' - : `Last saved ${formattedShortTime(lastSaved.toISOString())}`} - - ) : null} - - - )} - - - + {/* + setTabSelected('note')} + /> setTabSelected('highlights')} /> + */} - {!highlightsCollapsed && ( - <> - {sortedHighlights.map((highlight) => ( - { - mutate() - }} - /> - ))} - {sortedHighlights.length === 0 && ( - - You have not added any highlights to this document. - - )} - - )} - {/* + + + + */} - + alignment="start" + distribution="start" + > + {errorSaving && ( + + {errorSaving} + + )} + {lastSaved !== undefined ? ( + <> + {lastChanged === lastSaved + ? 'Saved' + : `Last saved ${formattedShortTime(lastSaved.toISOString())}`} + + ) : null} + + + {/* )} */} + + {/* {tabSelected == 'highlights' && ( */} + + {sortedHighlights.map((highlight) => ( + { + mutate() + }} + /> + ))} + {sortedHighlights.length === 0 && ( + + You have not added any highlights to this document. + + )} + + {/* )} */} {showConfirmDeleteHighlightId && ( void + selected: boolean + setSelected: (set: boolean) => void } function SectionTitle(props: SectionTitleProps): JSX.Element { @@ -408,30 +402,25 @@ function SectionTitle(props: SectionTitleProps): JSX.Element { display: 'flex', alignItems: 'center', gap: '5px', + color: props.selected ? '$thTextContrast' : '$thTextSubtle', + borderBottom: props.selected + ? '1px solid $thTextContrast' + : '1px solid transparent', }} onClick={(event) => { - props.setCollapsed(!props.collapsed) + props.setSelected(true) event.stopPropagation() }} > - {props.collapsed ? ( - - ) : ( - - )} diff --git a/packages/web/components/templates/article/NotebookHeader.tsx b/packages/web/components/templates/article/NotebookHeader.tsx index 0b19bbf3d..187664f5a 100644 --- a/packages/web/components/templates/article/NotebookHeader.tsx +++ b/packages/web/components/templates/article/NotebookHeader.tsx @@ -31,7 +31,9 @@ export const NotebookHeader = (props: NotebookHeaderProps) => { top: '0px', height: '50px', p: '20px', - background: '#F8FAFB', + borderTopLeftRadius: '10px', + overflow: 'clip', + background: '$thLibrarySearchbox', zIndex: 10, }} > @@ -49,18 +51,18 @@ export const NotebookHeader = (props: NotebookHeaderProps) => { alignment="center" > }> - {/* { - exportHighlights() + // exportHighlights() }} title="Export Notebook" /> { - setShowConfirmDeleteNote(true) + // setShowConfirmDeleteNote(true) }} title="Delete Article Note" - /> */} + /> diff --git a/packages/web/components/templates/article/SetLabelsModal.tsx b/packages/web/components/templates/article/SetLabelsModal.tsx index 9b484e015..06f021591 100644 --- a/packages/web/components/templates/article/SetLabelsModal.tsx +++ b/packages/web/components/templates/article/SetLabelsModal.tsx @@ -14,6 +14,7 @@ import { useGetLabelsQuery } from '../../../lib/networking/queries/useGetLabelsQ import { v4 as uuidv4 } from 'uuid' import { randomLabelColorHex } from '../../../utils/settings-page/labels/labelColorObjects' import { LabelsDispatcher } from '../../../lib/hooks/useSetPageLabels' +import * as Dialog from '@radix-ui/react-dialog' type SetLabelsModalProps = { provider: LabelsProvider @@ -30,9 +31,8 @@ export function SetLabelsModal(props: SetLabelsModalProps): JSX.Element { const availableLabels = useGetLabelsQuery() const [tabCount, setTabCount] = useState(-1) const [tabStartValue, setTabStartValue] = useState('') - const [errorMessage, setErrorMessage] = useState( - undefined - ) + const [errorMessage, setErrorMessage] = + useState(undefined) const errorTimeoutRef = useRef() const [highlightLastLabel, setHighlightLastLabel] = useState(false) @@ -171,44 +171,46 @@ export function SetLabelsModal(props: SetLabelsModalProps): JSX.Element { return ( - - { - event.preventDefault() - props.onOpenChange(false) - }} - onEscapeKeyDown={(event) => { - props.onOpenChange(false) - event.preventDefault() - }} - > - - - - - - - + + + { + event.preventDefault() + props.onOpenChange(false) + }} + onEscapeKeyDown={(event) => { + props.onOpenChange(false) + event.preventDefault() + }} + > + + + + + + + + ) } diff --git a/packages/web/package.json b/packages/web/package.json index bbce82d83..92050b88b 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -62,6 +62,7 @@ "react-markdown-editor-lite": "^1.3.4", "react-masonry-css": "^1.0.16", "react-pro-sidebar": "^0.7.1", + "react-sliding-pane": "^7.3.0", "react-spinners": "^0.13.7", "react-super-responsive-table": "^5.2.1", "react-topbar-progress-indicator": "^4.1.1", diff --git a/packages/web/styles/globals.css b/packages/web/styles/globals.css index 569bddf90..df1934426 100644 --- a/packages/web/styles/globals.css +++ b/packages/web/styles/globals.css @@ -419,20 +419,22 @@ button { margin: 0px; } -.omnivore-masonry-grid { - display: -webkit-box; /* Not needed if autoprefixing */ - display: -ms-flexbox; /* Not needed if autoprefixing */ - display: flex; - margin-left: -16px; /* gutter size offset */ - margin-right: 14px; - width: auto; +.slide-panel-overlay { + z-index: 100 !important; + background: transparent !important; + pointer-events: none; } -.omnivore-masonry-grid_column { - padding-left: 16px; /* gutter size */ - background-clip: padding-box; + +.slide-pane__content { + padding: 0px !important; + pointer-events: all; + border-top-left-radius: 10px; + border-bottom-right-radius: 10px; + border-left: 1px solid var(--colors-thNotebookSubtle); + background: var(--colors-thLibrarySearchbox); } -/* .omnivore-masonry-grid_column > div { - background: grey; - margin-bottom: 16px; -} */ +.slide-pane { + background: transparent !important; + box-shadow: 0px 4px 4px rgba(33, 33, 33, 0.1) !important; +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 36db67d18..eb21f53dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14467,6 +14467,11 @@ executable@^4.1.1: dependencies: pify "^2.2.0" +exenv@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d" + integrity sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw== + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -23841,6 +23846,11 @@ react-is@^18.0.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== +react-lifecycles-compat@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== + react-markdown-editor-lite@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/react-markdown-editor-lite/-/react-markdown-editor-lite-1.3.4.tgz#77992d2389b9427a06595c63d95f52be66e5fea9" @@ -23877,6 +23887,16 @@ react-masonry-css@^1.0.16: resolved "https://registry.yarnpkg.com/react-masonry-css/-/react-masonry-css-1.0.16.tgz#72b28b4ae3484e250534700860597553a10f1a2c" integrity sha512-KSW0hR2VQmltt/qAa3eXOctQDyOu7+ZBevtKgpNDSzT7k5LA/0XntNa9z9HKCdz3QlxmJHglTZ18e4sX4V8zZQ== +react-modal@^3.14.3: + version "3.16.1" + resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.16.1.tgz#34018528fc206561b1a5467fc3beeaddafb39b2b" + integrity sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg== + dependencies: + exenv "^1.2.0" + prop-types "^15.7.2" + react-lifecycles-compat "^3.0.0" + warning "^4.0.3" + react-popper-tooltip@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/react-popper-tooltip/-/react-popper-tooltip-3.1.1.tgz#329569eb7b287008f04fcbddb6370452ad3f9eac" @@ -23960,6 +23980,14 @@ react-slidedown@^2.4.5: dependencies: tslib "^2.0.0" +react-sliding-pane@^7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/react-sliding-pane/-/react-sliding-pane-7.3.0.tgz#a6a03b90db216e7ec6f746c7e649d19ba03ff4e0" + integrity sha512-KCyxw2BBvXjwYm1UX83Vk67D4kxec2icJxrSPidNus8voh1yB1K6bluwShAe3OvN5zk8H9InL22jGomTUOOudw== + dependencies: + prop-types "^15.7.2" + react-modal "^3.14.3" + react-spinners@^0.13.7: version "0.13.7" resolved "https://registry.yarnpkg.com/react-spinners/-/react-spinners-0.13.7.tgz#0f423c415bfa56765ce9fb36ff604e52a92b37a9" @@ -27779,7 +27807,7 @@ walker@~1.0.5: dependencies: makeerror "1.0.12" -warning@^4.0.2: +warning@^4.0.2, warning@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== From 64051f3b3fa7a01a34d580d338a85e166651353b Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 27 Jun 2023 12:11:49 +0800 Subject: [PATCH 24/44] Use full width for highlights pane on smaller screens --- .../templates/article/HighlightsLayer.tsx | 33 +++---------------- 1 file changed, 4 insertions(+), 29 deletions(-) diff --git a/packages/web/components/templates/article/HighlightsLayer.tsx b/packages/web/components/templates/article/HighlightsLayer.tsx index 2914a08c0..b2fa49ce7 100644 --- a/packages/web/components/templates/article/HighlightsLayer.tsx +++ b/packages/web/components/templates/article/HighlightsLayer.tsx @@ -30,6 +30,7 @@ import SlidingPane from 'react-sliding-pane' import 'react-sliding-pane/dist/react-sliding-pane.css' import { NotebookContent } from './Notebook' import { NotebookHeader } from './NotebookHeader' +import useGetWindowDimensions from '../../../lib/hooks/useGetWindowDimensions' type HighlightsLayerProps = { viewer: UserBasicData @@ -86,6 +87,8 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { const [labelsTarget, setLabelsTarget] = useState(undefined) + const windowDimensions = useGetWindowDimensions() + const createHighlightFromSelection = useCallback( async ( selection: SelectionAttributes, @@ -751,7 +754,7 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { ) - - { - /* return ( - { - // The timeout here is a bit of a hack to work around rerendering - setTimeout(() => { - const target = document.querySelector( - `[omnivore-highlight-id="${highlightId}"]` - ) - target?.scrollIntoView({ - block: 'center', - behavior: 'auto', - }) - }, 1) - history.replaceState( - undefined, - window.location.href, - `#${highlightId}` - ) - props.setShowHighlightsModal(false) - }} - /> - ) */ - } } return <> From b719f5df2ae08d4c860880661ccf184d56f8159a Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 27 Jun 2023 12:18:07 +0800 Subject: [PATCH 25/44] Allow the highlights bars and modals to open while the side pane is open --- .../templates/article/HighlightsLayer.tsx | 177 +++++++++--------- 1 file changed, 84 insertions(+), 93 deletions(-) diff --git a/packages/web/components/templates/article/HighlightsLayer.tsx b/packages/web/components/templates/article/HighlightsLayer.tsx index b2fa49ce7..8c2058b41 100644 --- a/packages/web/components/templates/article/HighlightsLayer.tsx +++ b/packages/web/components/templates/article/HighlightsLayer.tsx @@ -695,104 +695,95 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { } }) - if (highlightModalAction?.highlightModalAction == 'addComment') { - return ( - - setHighlightModalAction({ highlightModalAction: 'none' }) - } - createHighlightForNote={highlightModalAction?.createHighlightForNote} - /> - ) - } - - if (labelsTarget) { - return ( - setLabelsTarget(undefined)} - /> - ) - } - - // Display the button bar if we are not in the native app and there - // is a focused highlight or selection data - if (!props.highlightBarDisabled && (focusedHighlight || selectionData)) { - const anchorCoordinates = () => { - return { - pageX: - selectionData?.focusPosition.x ?? - focusedHighlightMousePos.current?.pageX ?? - 0, - pageY: - selectionData?.focusPosition.y ?? - focusedHighlightMousePos.current?.pageY ?? - 0, - } + const anchorCoordinates = () => { + return { + pageX: + selectionData?.focusPosition.x ?? + focusedHighlightMousePos.current?.pageX ?? + 0, + pageY: + selectionData?.focusPosition.y ?? + focusedHighlightMousePos.current?.pageY ?? + 0, } - - return ( - <> - - - ) } - if (props.showHighlightsModal) { - return ( - { - props.setShowHighlightsModal(false) - }} - > + return ( + <> + {highlightModalAction?.highlightModalAction == 'addComment' && ( + + setHighlightModalAction({ highlightModalAction: 'none' }) + } + createHighlightForNote={highlightModalAction?.createHighlightForNote} + /> + )} + {labelsTarget && ( + setLabelsTarget(undefined)} + /> + )} + // Display the button bar if we are not in the native app and there // is + a focused highlight or selection data + {!props.highlightBarDisabled && (focusedHighlight || selectionData) && ( <> - - { - // The timeout here is a bit of a hack to work around rerendering - setTimeout(() => { - const target = document.querySelector( - `[omnivore-highlight-id="${highlightId}"]` - ) - target?.scrollIntoView({ - block: 'center', - behavior: 'auto', - }) - }, 1) - history.replaceState( - undefined, - window.location.href, - `#${highlightId}` - ) - - // props.setShowHighlightsModal(false) - }} + - - ) - } + )} + {props.showHighlightsModal && ( + { + props.setShowHighlightsModal(false) + }} + > + <> + + { + // The timeout here is a bit of a hack to work around rerendering + setTimeout(() => { + const target = document.querySelector( + `[omnivore-highlight-id="${highlightId}"]` + ) + target?.scrollIntoView({ + block: 'center', + behavior: 'auto', + }) + }, 1) + history.replaceState( + undefined, + window.location.href, + `#${highlightId}` + ) - return <> + // props.setShowHighlightsModal(false) + }} + /> + + + )} + + ) } From f5792cfc3ab77f909794ef85f3b9408ff4d8cae7 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 27 Jun 2023 12:24:52 +0800 Subject: [PATCH 26/44] Add padding on bottom of notebooks, add cancel button to note editor --- packages/web/components/patterns/HighlightNotes.tsx | 10 ++++++++++ packages/web/components/patterns/HighlightView.tsx | 6 ------ packages/web/components/templates/article/Notebook.tsx | 9 ++++++--- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/packages/web/components/patterns/HighlightNotes.tsx b/packages/web/components/patterns/HighlightNotes.tsx index c24df1193..19daf58d1 100644 --- a/packages/web/components/patterns/HighlightNotes.tsx +++ b/packages/web/components/patterns/HighlightNotes.tsx @@ -232,6 +232,16 @@ export function MarkdownNote(props: MarkdownNote): JSX.Element { marginLeft: 'auto', }} > + ) diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index b119c2c39..8c886fc91 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -350,7 +350,11 @@ export function HomeFeedContainer(): JSX.Element { setLabelsTarget(item) break case 'open-notebook': - setNotebookTarget(item) + if (!notebookTarget) { + setNotebookTarget(item) + } else { + setNotebookTarget(undefined) + } break case 'unsubscribe': performActionOnItem('unsubscribe', item) @@ -489,6 +493,7 @@ export function HomeFeedContainer(): JSX.Element { handleCardAction('set-labels', activeItem) break case 'openNotebook': + console.log('openNotebook: ', notebookTarget) handleCardAction('open-notebook', activeItem) break case 'sortDescending': diff --git a/packages/web/components/tokens/stitches.config.ts b/packages/web/components/tokens/stitches.config.ts index 5067c66d8..6e57310c7 100644 --- a/packages/web/components/tokens/stitches.config.ts +++ b/packages/web/components/tokens/stitches.config.ts @@ -183,6 +183,7 @@ export const { styled, css, theme, getCssText, globalCss, keyframes, config } = thNotebookSubtle: '#6A6968', thNotebookBorder: '#D9D9D9', thNotebookBackground: '#FCFCFC', + thNotebookTextBackground: '#EBEBEB', thTextContrast: '#1E1E1E', thTextContrast2: '#3D3D3D', @@ -280,7 +281,8 @@ const darkThemeSpec = { thNotebookSubtle: '#898989', thNotebookBorder: '#898989', - thNotebookBackground: '#3D3D3D', + thNotebookBackground: '#3B3938', + thNotebookTextBackground: '#3D3D3D', thNotebookHighContrast: '#2A2A2A', thTextContrast: '#FFFFFF', diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx index 4ba683215..2d1babc43 100644 --- a/packages/web/pages/[username]/[slug]/index.tsx +++ b/packages/web/pages/[username]/[slug]/index.tsx @@ -350,7 +350,7 @@ export default function Home(): JSX.Element { name: 'Notebook', shortcut: ['t'], perform: () => { - setShowHighlightsModal(true) + setShowHighlightsModal(!showHighlightsModal) }, }, { From e8948ef14c7c2c2142a89ba24228eb4baf8c782c Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 27 Jun 2023 14:31:14 +0800 Subject: [PATCH 34/44] Dont show hover actions on touch devices --- .../patterns/LibraryCards/LibraryGridCard.tsx | 26 +++++++++--------- .../patterns/LibraryCards/LibraryListCard.tsx | 27 ++++++++++--------- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx index 263461044..81fa74f61 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx @@ -116,18 +116,20 @@ export function LibraryGridCard(props: LinkedItemCardProps): JSX.Element { ) : ( <> - - - + {!isTouchScreenDevice() && ( + + + + )} ) : ( <> - - - + {!isTouchScreenDevice() && ( + + + + )} Date: Tue, 27 Jun 2023 14:35:45 +0800 Subject: [PATCH 35/44] Add import --- .../web/components/patterns/LibraryCards/LibraryGridCard.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx index 81fa74f61..a269a006b 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx @@ -28,6 +28,7 @@ import { } from '@floating-ui/react' import { CardMenu } from '../CardMenu' import { DotsThree } from 'phosphor-react' +import { isTouchScreenDevice } from '../../../lib/deviceType' dayjs.extend(relativeTime) From 88b38037dea5c87364727029827db4fedc139030 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 27 Jun 2023 14:54:33 +0800 Subject: [PATCH 36/44] Allow escape to close highlights view --- packages/web/pages/[username]/[slug]/index.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx index 2d1babc43..415f161f5 100644 --- a/packages/web/pages/[username]/[slug]/index.tsx +++ b/packages/web/pages/[username]/[slug]/index.tsx @@ -259,6 +259,10 @@ export default function Home(): JSX.Element { ) { return } + if (showHighlightsModal) { + setShowHighlightsModal(false) + return + } const query = window.sessionStorage.getItem('q') if (query) { router.push(`/home?${query}`) @@ -361,7 +365,7 @@ export default function Home(): JSX.Element { perform: () => setShowEditModal(true), }, ], - [readerSettings] + [readerSettings, showHighlightsModal] ) const [labels, dispatchLabels] = useSetPageLabels(article?.id) From 289405364b90145bf8571ccdad2c47a8136029c0 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 27 Jun 2023 14:56:59 +0800 Subject: [PATCH 37/44] fix comments --- packages/web/components/templates/article/HighlightsLayer.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/web/components/templates/article/HighlightsLayer.tsx b/packages/web/components/templates/article/HighlightsLayer.tsx index 26f77b028..e3b48dd9f 100644 --- a/packages/web/components/templates/article/HighlightsLayer.tsx +++ b/packages/web/components/templates/article/HighlightsLayer.tsx @@ -729,8 +729,8 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { onOpenChange={() => setLabelsTarget(undefined)} /> )} - // Display the button bar if we are not in the native app and there // is - a focused highlight or selection data + {/* // Display the button bar if we are not in the native app and there // is + a focused highlight or selection data */} {!props.highlightBarDisabled && (focusedHighlight || selectionData) && ( <> Date: Tue, 27 Jun 2023 15:22:00 +0800 Subject: [PATCH 38/44] Handle deleting highlights in the notebook modal on PDFs --- .../web/components/templates/article/Notebook.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/web/components/templates/article/Notebook.tsx b/packages/web/components/templates/article/Notebook.tsx index 8565cfc6f..ab15b7afc 100644 --- a/packages/web/components/templates/article/Notebook.tsx +++ b/packages/web/components/templates/article/Notebook.tsx @@ -316,14 +316,23 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { message={'Are you sure you want to delete this highlight?'} onAccept={() => { ;(async () => { + const highlightId = showConfirmDeleteHighlightId const success = await deleteHighlightMutation( showConfirmDeleteHighlightId ) mutate() if (success) { - showSuccessToast('Highlight deleted.') + showSuccessToast('Highlight deleted.', { + position: 'bottom-right', + }) + const event = new CustomEvent('deleteHighlightbyId', { + detail: highlightId, + }) + document.dispatchEvent(event) } else { - showErrorToast('Error deleting highlight') + showErrorToast('Error deleting highlight', { + position: 'bottom-right', + }) } })() setShowConfirmDeleteHighlightId(undefined) From 52ee03f2d2bafcb479968748367e5b82ce7882aa Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 27 Jun 2023 15:27:07 +0800 Subject: [PATCH 39/44] Use notebook pane for PDFs --- .../components/templates/article/Notebook.tsx | 2 - .../templates/article/PdfArticleContainer.tsx | 67 ++++++++++--------- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/packages/web/components/templates/article/Notebook.tsx b/packages/web/components/templates/article/Notebook.tsx index ab15b7afc..8c3ac0ff8 100644 --- a/packages/web/components/templates/article/Notebook.tsx +++ b/packages/web/components/templates/article/Notebook.tsx @@ -204,8 +204,6 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { setNoteText('') }, [noteState, highlights]) - const [tabSelected, setTabSelected] = useState<'note' | 'highlights'>('note') - const [errorSaving, setErrorSaving] = useState(undefined) const [lastChanged, setLastChanged] = useState(undefined) const [lastSaved, setLastSaved] = useState(undefined) diff --git a/packages/web/components/templates/article/PdfArticleContainer.tsx b/packages/web/components/templates/article/PdfArticleContainer.tsx index be65a9583..9beff0387 100644 --- a/packages/web/components/templates/article/PdfArticleContainer.tsx +++ b/packages/web/components/templates/article/PdfArticleContainer.tsx @@ -13,11 +13,15 @@ import { articleReadingProgressMutation } from '../../../lib/networking/mutation import { mergeHighlightMutation } from '../../../lib/networking/mutations/mergeHighlightMutation' import { useCanShareNative } from '../../../lib/hooks/useCanShareNative' import { pspdfKitKey } from '../../../lib/appConfig' -import { NotebookModal } from './NotebookModal' import { HighlightNoteModal } from './HighlightNoteModal' import { showErrorToast } from '../../../lib/toastHelpers' import { HEADER_HEIGHT } from '../homeFeed/HeaderSpacer' import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery' +import SlidingPane from 'react-sliding-pane' +import 'react-sliding-pane/dist/react-sliding-pane.css' +import { NotebookContent } from './Notebook' +import { NotebookHeader } from './NotebookHeader' +import useWindowDimensions from '../../../lib/hooks/useGetWindowDimensions' export type PdfArticleContainerProps = { viewer: UserBasicData @@ -30,14 +34,12 @@ export default function PdfArticleContainer( props: PdfArticleContainerProps ): JSX.Element { const containerRef = useRef(null) - const [shareTarget, setShareTarget] = useState( - undefined - ) + const [shareTarget, setShareTarget] = + useState(undefined) const [notebookKey, setNotebookKey] = useState(uuidv4()) const [noteTarget, setNoteTarget] = useState(undefined) - const [noteTargetPageIndex, setNoteTargetPageIndex] = useState< - number | undefined - >(undefined) + const [noteTargetPageIndex, setNoteTargetPageIndex] = + useState(undefined) const highlightsRef = useRef([]) const canShareNative = useCanShareNative() @@ -475,6 +477,8 @@ export default function PdfArticleContainer( // the PSPDFKit instance if the theme, article URL, or page URL changes. Everything else // should be handled by the PSPDFKit instance callbacks. + const windowDimensions = useWindowDimensions() + return ( )} - {props.showHighlightsModal && ( - { - console.log( - 'closed PDF notebook: ', - updatedHighlights, - deletedHighlights - ) - deletedHighlights.forEach((highlight) => { - const event = new CustomEvent('deleteHighlightbyId', { - detail: highlight.id, + { + props.setShowHighlightsModal(false) + }} + > + <> + + { + const event = new CustomEvent('scrollToHighlightId', { + detail: highlightId, }) document.dispatchEvent(event) - }) - props.setShowHighlightsModal(false) - }} - viewHighlightInReader={(highlightId) => { - const event = new CustomEvent('scrollToHighlightId', { - detail: highlightId, - }) - document.dispatchEvent(event) - props.setShowHighlightsModal(false) - }} - /> - )} + }} + /> + + ) } From 73498383bacf9a3a14ae7b9806791249af024433 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 27 Jun 2023 15:41:40 +0800 Subject: [PATCH 40/44] Sync highlight changes from page to notebook --- .../components/templates/article/HighlightsLayer.tsx | 1 + .../web/components/templates/article/Notebook.tsx | 12 ++++++++++++ packages/web/lib/highlights/createHighlight.ts | 2 ++ 3 files changed, 15 insertions(+) diff --git a/packages/web/components/templates/article/HighlightsLayer.tsx b/packages/web/components/templates/article/HighlightsLayer.tsx index e3b48dd9f..a5705c2d5 100644 --- a/packages/web/components/templates/article/HighlightsLayer.tsx +++ b/packages/web/components/templates/article/HighlightsLayer.tsx @@ -188,6 +188,7 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { ) setHighlights(highlights.filter(($0) => $0.id !== highlightId)) setFocusedHighlight(undefined) + document.dispatchEvent(new Event('highlightsUpdated')) } else { console.error('Failed to delete highlight') } diff --git a/packages/web/components/templates/article/Notebook.tsx b/packages/web/components/templates/article/Notebook.tsx index 8c3ac0ff8..e70dbc56c 100644 --- a/packages/web/components/templates/article/Notebook.tsx +++ b/packages/web/components/templates/article/Notebook.tsx @@ -208,6 +208,18 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { const [lastChanged, setLastChanged] = useState(undefined) const [lastSaved, setLastSaved] = useState(undefined) + useEffect(() => { + const highlightsUpdated = () => { + mutate() + } + document.addEventListener('highlightsUpdated', highlightsUpdated) + return () => { + document.removeEventListener('highlightsUpdated', highlightsUpdated) + } + }, [forceUpdate]) + + console.log('articleData: ', articleData) + return ( Date: Tue, 27 Jun 2023 15:48:55 +0800 Subject: [PATCH 41/44] Use mutate as effect hook --- packages/web/components/templates/article/HighlightsLayer.tsx | 1 - packages/web/components/templates/article/Notebook.tsx | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/web/components/templates/article/HighlightsLayer.tsx b/packages/web/components/templates/article/HighlightsLayer.tsx index a5705c2d5..29a4b5594 100644 --- a/packages/web/components/templates/article/HighlightsLayer.tsx +++ b/packages/web/components/templates/article/HighlightsLayer.tsx @@ -19,7 +19,6 @@ import { HighlightBar, HighlightAction } from '../../patterns/HighlightBar' import { removeHighlights } from '../../../lib/highlights/deleteHighlight' import { createHighlight } from '../../../lib/highlights/createHighlight' import { HighlightNoteModal } from './HighlightNoteModal' -import { NotebookModal } from './NotebookModal' import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers' import { ArticleMutations } from '../../../lib/articleActions' import { isTouchScreenDevice } from '../../../lib/deviceType' diff --git a/packages/web/components/templates/article/Notebook.tsx b/packages/web/components/templates/article/Notebook.tsx index e70dbc56c..f511c6e8a 100644 --- a/packages/web/components/templates/article/Notebook.tsx +++ b/packages/web/components/templates/article/Notebook.tsx @@ -216,9 +216,7 @@ export function NotebookContent(props: NotebookContentProps): JSX.Element { return () => { document.removeEventListener('highlightsUpdated', highlightsUpdated) } - }, [forceUpdate]) - - console.log('articleData: ', articleData) + }, [mutate]) return ( Date: Tue, 27 Jun 2023 16:05:46 +0800 Subject: [PATCH 42/44] Allow list author infos more room if needed --- .../web/components/patterns/LibraryCards/LibraryListCard.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx index 7e8f2a791..6c8277c5a 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx @@ -184,6 +184,7 @@ export function LibraryListCardContent( css={{ mt: '5px', ...AuthorInfoStyle, + maxWidth: '90%', }} > {props.item.author} From add720d523b663c2e75c9ea863abaa4401fef1b8 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 27 Jun 2023 16:40:38 +0800 Subject: [PATCH 43/44] Remove test text when creating highlights --- packages/web/components/templates/article/HighlightsLayer.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/web/components/templates/article/HighlightsLayer.tsx b/packages/web/components/templates/article/HighlightsLayer.tsx index 29a4b5594..db05aa5aa 100644 --- a/packages/web/components/templates/article/HighlightsLayer.tsx +++ b/packages/web/components/templates/article/HighlightsLayer.tsx @@ -444,7 +444,7 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { await removeHighlightCallback() break case 'create': - await createHighlightCallback('none') + await createHighlightCallback() break case 'comment': if (props.highlightBarDisabled || focusedHighlight) { @@ -657,7 +657,7 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { dispatchHighlightMessage('noteCreated') } else { try { - await createHighlightCallback('none') + await createHighlightCallback() dispatchHighlightMessage('noteCreated') } catch (error) { dispatchHighlightError('saveAnnotation', error) From 0c219ee789a94361428e9331af4f2a31dc62beaa Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 27 Jun 2023 17:10:47 +0800 Subject: [PATCH 44/44] Sync deletes of highlights --- .../templates/article/HighlightsLayer.tsx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/web/components/templates/article/HighlightsLayer.tsx b/packages/web/components/templates/article/HighlightsLayer.tsx index db05aa5aa..4aa3f7ee0 100644 --- a/packages/web/components/templates/article/HighlightsLayer.tsx +++ b/packages/web/components/templates/article/HighlightsLayer.tsx @@ -546,6 +546,21 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { } } + const deleteHighlightById = useCallback( + (event: Event) => { + const annotationId = (event as CustomEvent).detail as string + if (annotationId) { + removeHighlights( + highlights.map((h) => h.id), + highlightLocations + ) + const keptHighlights = highlights.filter(($0) => $0.id !== annotationId) + setHighlights([...keptHighlights]) + } + }, + [highlights, highlightLocations] + ) + useEffect(() => { const safeHandleAction = async (action: HighlightAction) => { try { @@ -676,6 +691,7 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { document.addEventListener('setHighlightLabels', setHighlightLabels) document.addEventListener('scrollToNextHighlight', goToNextHighlight) document.addEventListener('scrollToPrevHighlight', goToPreviousHighlight) + document.addEventListener('deleteHighlightbyId', deleteHighlightById) return () => { document.removeEventListener('annotate', annotate) @@ -692,6 +708,7 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { 'scrollToPrevHighlight', goToPreviousHighlight ) + document.removeEventListener('deleteHighlightbyId', deleteHighlightById) } })