Better handlding of deleting highlights from the library highlights view

This commit is contained in:
Jackson Harper 2023-03-17 14:11:03 +08:00
parent 49e1947095
commit f61efd2d3e
4 changed files with 157 additions and 74 deletions

View file

@ -18,6 +18,7 @@ export function EmptyLibrary(props: EmptyLibraryProps): JSX.Element {
color: '$grayTextContrast',
textAlign: 'center',
paddingTop: '88px',
flex: '1',
}}
>
<Book size={44} color={theme.colors.grayTextContrast.toString()} />

View file

@ -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',
},
}}
>
<HighlightsMenu
@ -163,6 +162,7 @@ export function HighlightItem(props: HighlightItemProps): JSX.Element {
)
if (result) {
showSuccessToast('Highlight deleted')
props.deleteHighlight(props.item, props.highlight)
} else {
showErrorToast('Error deleting highlight')
}
@ -207,14 +207,6 @@ export function HighlightsMenu(props: HighlightsMenuProps): JSX.Element {
})()
}, [props.highlight])
const exportHighlight = useCallback(() => {
;(async () => {
const markdown = highlightAsMarkdown(props.highlight)
await navigator.clipboard.writeText(markdown)
showSuccessToast('Highlight copied')
})()
}, [props.highlight])
return (
<Dropdown
triggerElement={
@ -231,7 +223,11 @@ export function HighlightsMenu(props: HighlightsMenuProps): JSX.Element {
},
}}
>
<DotsThreeVertical size={20} color="#EBEBEB" weight="bold" />
<DotsThreeVertical
size={20}
color={theme.colors.thTextContrast2.toString()}
weight="bold"
/>
</Box>
}
>
@ -249,7 +245,7 @@ export function HighlightsMenu(props: HighlightsMenuProps): JSX.Element {
/>
<DropdownOption
onSelect={() => {
exportHighlight()
props.setShowConfirmDeleteHighlightId(props.highlight.id)
}}
title="Delete"
/>

View file

@ -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,14 +36,74 @@ 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
)
}
return state.filter(
(item) => item.node.highlights && item.node.highlights.length > 0
)
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 (
<Box
css={{
width: '100%',
height: `calc(100vh - ${HEADER_HEIGHT})`,
'@xlgDown': {
height: `calc(100vh - ${MOBILE_HEADER_HEIGHT})`,
},
}}
>
<EmptyHighlights />
</Box>
)
}
return (
<>
@ -45,6 +111,7 @@ export function HighlightItemsLayout(
css={{
width: '100%',
height: '100%',
position: 'relative',
bg: '$thBackground2',
}}
distribution="start"
@ -63,59 +130,30 @@ export function HighlightItemsLayout(
distribution="start"
alignment="start"
>
<HStack
css={{
width: 'calc(100% - 35px)',
height: '55px',
mx: '20px',
borderBottom: '1px solid $thBorderColor',
}}
alignment="center"
distribution="start"
>
{/* <Box
css={{
display: 'flex',
height: '20px',
width: '20px',
marginLeft: 'auto',
alignItems: 'center',
justifyContent: 'center',
borderRadius: '1000px',
'&:hover': {
bg: '#898989',
},
}}
>
<DotsThreeVertical size={200} color="#898989" weight="bold" />
</Box> */}
</HStack>
{props.items.map((linkedItem) => (
<Box
className="linkedItemCard"
data-testid="linkedItemCard"
id={linkedItem.node.id}
tabIndex={0}
key={linkedItem.node.id}
css={{
width: '100%',
height: '100%',
px: '15px',
}}
onClick={(event) => {
setCurrentItem(linkedItem)
event.preventDefault()
}}
>
{props.viewer && (
<LibraryItemCard
item={linkedItem}
viewer={props.viewer}
selected={currentItem?.node.id == linkedItem.node.id}
/>
)}
{items.length > 0 ? (
<>
<HStack
css={{
width: 'calc(100% - 35px)',
height: '55px',
mx: '20px',
borderBottom: '1px solid $thBorderColor',
}}
alignment="center"
distribution="start"
></HStack>
<LibraryItemsList
items={items}
viewer={props.viewer}
currentItem={currentItem}
setCurrentItem={setCurrentItem}
/>
</>
) : (
<Box css={{ width: '100%', height: '200px' }}>
No highlights found
</Box>
))}
)}
</VStack>
{currentItem && (
<>
@ -130,7 +168,11 @@ export function HighlightItemsLayout(
},
}}
>
<HighlightList item={currentItem} viewer={props.viewer} />
<HighlightList
item={currentItem}
viewer={props.viewer}
deleteHighlight={handleDelete}
/>
</SpanBox>
</>
)}
@ -139,6 +181,47 @@ export function HighlightItemsLayout(
)
}
type LibraryItemsListProps = {
items: LibraryItem[]
viewer: UserBasicData | undefined
currentItem: LibraryItem | undefined
setCurrentItem: (item: LibraryItem | undefined) => void
}
function LibraryItemsList(props: LibraryItemsListProps): JSX.Element {
return (
<>
{props.items.map((linkedItem) => (
<Box
className="linkedItemCard"
data-testid="linkedItemCard"
id={linkedItem.node.id}
tabIndex={0}
key={linkedItem.node.id}
css={{
width: '100%',
height: '100%',
px: '15px',
}}
onClick={(event) => {
props.setCurrentItem(linkedItem)
event.preventDefault()
}}
>
{props.viewer && (
<LibraryItemCard
item={linkedItem}
viewer={props.viewer}
selected={props.currentItem?.node.id == linkedItem.node.id}
/>
)}
</Box>
))}
</>
)
}
type HighlightTitleCardProps = {
item: LibraryItem
viewer: UserBasicData
@ -238,6 +321,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 {
@ -288,7 +373,7 @@ function HighlightList(props: HighlightListProps): JSX.Element {
fontSize: '15px',
fontFamily: '$display',
width: '100%',
color: '$thTextContrast2',
color: 'thTextContrast2',
}}
>
HIGHLIGHTS
@ -309,6 +394,7 @@ function HighlightList(props: HighlightListProps): JSX.Element {
viewer={props.viewer}
item={props.item.node}
highlight={highlight}
deleteHighlight={props.deleteHighlight}
/>
))}
</VStack>

View file

@ -686,7 +686,7 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
setShowFilterMenu={setShowFilterMenu}
/>
{props.mode == 'highlights' && (
{!props.isValidating && props.mode == 'highlights' && (
<HighlightItemsLayout
gridContainerRef={props.gridContainerRef}
items={props.items}