diff --git a/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryHighlightGridCard.tsx index c7eaa004d..fb5b4cd1b 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 } 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' @@ -20,6 +20,8 @@ export const GridSeparator = styled(Box, { type LibraryHighlightGridCardProps = { viewer: UserBasicData item: LibraryItemNode + + deleteHighlight: (item: LibraryItemNode, highlight: Highlight) => void } export function LibraryHighlightGridCard( @@ -128,6 +130,7 @@ export function LibraryHighlightGridCard( viewer={props.viewer} item={props.item} highlight={highlight} + deleteHighlight={props.deleteHighlight} /> ))} diff --git a/packages/web/components/templates/homeFeed/EmptyHighlights.tsx b/packages/web/components/templates/homeFeed/EmptyHighlights.tsx new file mode 100644 index 000000000..204904acc --- /dev/null +++ b/packages/web/components/templates/homeFeed/EmptyHighlights.tsx @@ -0,0 +1,23 @@ +import { Book } from 'phosphor-react' +import { VStack } from '../../elements/LayoutPrimitives' +import { StyledText } from '../../elements/StyledText' +import { theme } from '../../tokens/stitches.config' + +export function EmptyHighlights(): JSX.Element { + return ( + + + + No results found. + + + ) +} diff --git a/packages/web/components/templates/homeFeed/EmptyLibrary.tsx b/packages/web/components/templates/homeFeed/EmptyLibrary.tsx index eadc62608..f7965adc2 100644 --- a/packages/web/components/templates/homeFeed/EmptyLibrary.tsx +++ b/packages/web/components/templates/homeFeed/EmptyLibrary.tsx @@ -18,6 +18,7 @@ export function EmptyLibrary(props: EmptyLibraryProps): JSX.Element { color: '$grayTextContrast', textAlign: 'center', paddingTop: '88px', + flex: '1', }} > diff --git a/packages/web/components/templates/homeFeed/HighlightItem.tsx b/packages/web/components/templates/homeFeed/HighlightItem.tsx index 7955f11dd..43fdc60ed 100644 --- a/packages/web/components/templates/homeFeed/HighlightItem.tsx +++ b/packages/web/components/templates/homeFeed/HighlightItem.tsx @@ -21,12 +21,15 @@ import { } from '../../elements/LayoutPrimitives' import { StyledText } from '../../elements/StyledText' import { ConfirmationModal } from '../../patterns/ConfirmationModal' +import { theme } from '../../tokens/stitches.config' import { SetLabelsModal } from '../article/SetLabelsModal' type HighlightItemProps = { highlight: Highlight viewer: UserBasicData | undefined item: LibraryItemNode + + deleteHighlight: (item: LibraryItemNode, highlight: Highlight) => void } const StyledQuote = styled(Blockquote, { @@ -140,10 +143,6 @@ export function HighlightItem(props: HighlightItemProps): JSX.Element { css={{ marginLeft: 'auto', width: '20px', - visibility: hover ? 'unset' : 'hidden', - '@media (hover: none)': { - visibility: 'unset', - }, }} > { - ;(async () => { - const markdown = highlightAsMarkdown(props.highlight) - await navigator.clipboard.writeText(markdown) - showSuccessToast('Highlight copied') - })() - }, [props.highlight]) - return ( - + } > @@ -249,7 +245,7 @@ export function HighlightsMenu(props: HighlightsMenuProps): JSX.Element { /> { - exportHighlight() + props.setShowConfirmDeleteHighlightId(props.highlight.id) }} title="Delete" /> diff --git a/packages/web/components/templates/homeFeed/HighlightsLayout.tsx b/packages/web/components/templates/homeFeed/HighlightsLayout.tsx index 129d0f8f7..9d445f05d 100644 --- a/packages/web/components/templates/homeFeed/HighlightsLayout.tsx +++ b/packages/web/components/templates/homeFeed/HighlightsLayout.tsx @@ -1,7 +1,11 @@ import { HighlighterCircle } from 'phosphor-react' -import { useCallback, useEffect, useState } from 'react' +import { useCallback, useEffect, useReducer, useState } from 'react' import { Toaster } from 'react-hot-toast' -import { LibraryItem } from '../../../lib/networking/queries/useGetLibraryItemsQuery' +import { Highlight } from '../../../lib/networking/fragments/highlightFragment' +import { + LibraryItem, + LibraryItemNode, +} from '../../../lib/networking/queries/useGetLibraryItemsQuery' import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery' import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers' import { Dropdown, DropdownOption } from '../../elements/DropdownElements' @@ -14,6 +18,8 @@ import { timeAgo, } from '../../patterns/LibraryCards/LibraryCardStyles' import { LibraryHighlightGridCard } from '../../patterns/LibraryCards/LibraryHighlightGridCard' +import { EmptyHighlights } from './EmptyHighlights' +import { HEADER_HEIGHT, MOBILE_HEADER_HEIGHT } from './HeaderSpacer' import { HighlightItem, highlightsAsMarkdown } from './HighlightItem' type HighlightItemsLayoutProps = { @@ -30,99 +36,152 @@ export function HighlightItemsLayout( undefined ) + const listReducer = ( + state: LibraryItem[], + action: { + type: string + itemId?: string + highlightId?: string + items?: LibraryItem[] + } + ) => { + switch (action.type) { + case 'RESET': + return action.items ?? [] + case 'REMOVE_HIGHLIGHT': + const item = state.find((li) => li.node.id === action.itemId) + if (item && item.node.highlights) { + item.node.highlights = item.node.highlights.filter( + (h) => h.id !== action.highlightId + ) + } + const result = state.filter( + (item) => item.node.highlights && item.node.highlights.length > 0 + ) + if ( + item && + item == currentItem && + (item.node.highlights?.length ?? 0) < 1 + ) { + setCurrentItem(result.length > 0 ? result[0] : undefined) + } + return result + default: + throw new Error() + } + } + + const [items, dispatchList] = useReducer(listReducer, []) + + function handleDelete(item: LibraryItemNode, highlight: Highlight) { + dispatchList({ + type: 'REMOVE_HIGHLIGHT', + itemId: item.id, + highlightId: highlight.id, + }) + } + + useEffect(() => { + dispatchList({ + type: 'RESET', + items: props.items, + }) + }, [props.items]) + useEffect(() => { // Only set the current item on larger screens if (window.innerWidth >= 992 /* lgDown */) { - if (!currentItem && props.items.length > 0) { - setCurrentItem(props.items[0]) + if (!currentItem && items.length > 0) { + setCurrentItem(items[0]) } } - }, [currentItem, setCurrentItem, props.items]) + }, [currentItem, setCurrentItem, items]) + + if (items.length < 1) { + return ( + + + + ) + } return ( <> - - - {/* - - */} - - {props.items.map((linkedItem) => ( - { - setCurrentItem(linkedItem) - event.preventDefault() - }} - > - {props.viewer && ( - - )} - - ))} - + alignment="center" + distribution="start" + > + + + + {currentItem && ( <> - + + + )} @@ -139,10 +213,56 @@ export function HighlightItemsLayout( ) } +type LibraryItemsListProps = { + items: LibraryItem[] + viewer: UserBasicData | undefined + + currentItem: LibraryItem | undefined + setCurrentItem: (item: LibraryItem | undefined) => void + + deleteHighlight: (item: LibraryItemNode, highlight: Highlight) => void +} + +function LibraryItemsList(props: LibraryItemsListProps): JSX.Element { + return ( + <> + {props.items.map((linkedItem) => ( + { + props.setCurrentItem(linkedItem) + event.preventDefault() + }} + > + {props.viewer && ( + + )} + + ))} + + ) +} + type HighlightTitleCardProps = { item: LibraryItem viewer: UserBasicData selected: boolean + deleteHighlight: (item: LibraryItemNode, highlight: Highlight) => void } function LibraryItemCard(props: HighlightTitleCardProps): JSX.Element { @@ -152,6 +272,7 @@ function LibraryItemCard(props: HighlightTitleCardProps): JSX.Element { @@ -238,6 +359,8 @@ function HighlightTitleCard(props: HighlightTitleCardProps): JSX.Element { type HighlightListProps = { item: LibraryItem viewer: UserBasicData | undefined + + deleteHighlight: (item: LibraryItemNode, highlight: Highlight) => void } function HighlightList(props: HighlightListProps): JSX.Element { @@ -266,7 +389,6 @@ function HighlightList(props: HighlightListProps): JSX.Element { HIGHLIGHTS @@ -302,15 +423,17 @@ function HighlightList(props: HighlightListProps): JSX.Element { /> - + {(props.item.node.highlights ?? []).map((highlight) => ( ))} + diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index a5e1c827c..10fa7bed3 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -686,7 +686,7 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { setShowFilterMenu={setShowFilterMenu} /> - {props.mode == 'highlights' && ( + {!props.isValidating && props.mode == 'highlights' && (