Filtering on the library view

This commit is contained in:
Jackson Harper 2024-06-25 09:11:15 +08:00
parent b39bffdc1a
commit 613dd39aba
8 changed files with 131 additions and 87 deletions

View file

@ -65,6 +65,7 @@ export function LibraryGridCard(props: LinkedItemCardProps): JSX.Element {
pl: '0px',
padding: '0px',
width: '100%',
maxWidth: '400px',
height: '100%',
minHeight: '270px',
borderRadius: '5px',

View file

@ -13,6 +13,9 @@ import { TrashIcon } from '../../elements/icons/TrashIcon'
import { LabelIcon } from '../../elements/icons/LabelIcon'
import { UnarchiveIcon } from '../../elements/icons/UnarchiveIcon'
import { BrowserIcon } from '../../elements/icons/BrowserIcon'
import { LibraryIcon } from '../../elements/icons/LibraryIcon'
import useLibraryItemActions from '../../../lib/hooks/useLibraryItemActions'
import { MoveToInboxIcon } from '../../elements/icons/MoveToInboxIcon'
type LibraryHoverActionsProps = {
viewer: UserBasicData
@ -25,6 +28,7 @@ type LibraryHoverActionsProps = {
export const LibraryHoverActions = (props: LibraryHoverActionsProps) => {
const [menuOpen, setMenuOpen] = useState(false)
const { moveItem } = useLibraryItemActions()
return (
<Box
@ -71,28 +75,47 @@ export const LibraryHoverActions = (props: LibraryHoverActionsProps) => {
color={theme.colors.thNotebookSubtle.toString()}
/>
</Button>
<Button
title={props.item.isArchived ? 'Unarchive (e)' : 'Archive (e)'}
style="hoverActionIcon"
onClick={(event) => {
const action = props.item.isArchived ? 'unarchive' : 'archive'
props.handleAction(action)
event.preventDefault()
event.stopPropagation()
}}
>
{props.item.isArchived ? (
<UnarchiveIcon
{props.item.folder == 'following' ? (
<Button
title="Move to library"
style="hoverActionIcon"
onClick={async (event) => {
event.preventDefault()
event.stopPropagation()
if (await moveItem(props.item.id)) {
props.handleAction('update-item')
}
}}
>
<MoveToInboxIcon
size={21}
color={theme.colors.thNotebookSubtle.toString()}
/>
) : (
<ArchiveIcon
size={21}
color={theme.colors.thNotebookSubtle.toString()}
/>
)}
</Button>
</Button>
) : (
<Button
title={props.item.isArchived ? 'Unarchive (e)' : 'Archive (e)'}
style="hoverActionIcon"
onClick={(event) => {
const action = props.item.isArchived ? 'unarchive' : 'archive'
props.handleAction(action)
event.preventDefault()
event.stopPropagation()
}}
>
{props.item.isArchived ? (
<UnarchiveIcon
size={21}
color={theme.colors.thNotebookSubtle.toString()}
/>
) : (
<ArchiveIcon
size={21}
color={theme.colors.thNotebookSubtle.toString()}
/>
)}
</Button>
)}
<Button
title="Remove (#)"
style="hoverActionIcon"

View file

@ -19,6 +19,7 @@ import {
} from '../../../lib/networking/queries/typeaheadSearch'
import type {
LibraryItem,
LibraryItemNode,
LibraryItemsQueryInput,
} from '../../../lib/networking/queries/useGetLibraryItemsQuery'
import { useGetLibraryItemsQuery } from '../../../lib/networking/queries/useGetLibraryItemsQuery'
@ -75,6 +76,7 @@ const TIMEOUT_DELAYS = [2000, 3500, 5000]
type LibraryContainerProps = {
folder: string
filterFunc: (item: LibraryItemNode) => boolean
}
export function LibraryContainer(props: LibraryContainerProps): JSX.Element {
@ -92,13 +94,11 @@ export function LibraryContainer(props: LibraryContainerProps): JSX.Element {
const gridContainerRef = useRef<HTMLDivElement>(null)
const [labelsTarget, setLabelsTarget] = useState<LibraryItem | undefined>(
undefined
)
const [labelsTarget, setLabelsTarget] =
useState<LibraryItem | undefined>(undefined)
const [notebookTarget, setNotebookTarget] = useState<LibraryItem | undefined>(
undefined
)
const [notebookTarget, setNotebookTarget] =
useState<LibraryItem | undefined>(undefined)
const [showAddLinkModal, setShowAddLinkModal] = useState(false)
const [showEditTitleModal, setShowEditTitleModal] = useState(false)
@ -151,12 +151,14 @@ export function LibraryContainer(props: LibraryContainerProps): JSX.Element {
const libraryItems = useMemo(() => {
const items =
itemsPages?.flatMap((ad) => {
return ad.search.edges.map((it) => ({
...it,
isLoading: it.node.state === 'PROCESSING',
}))
}) || []
itemsPages
?.flatMap((ad) => {
return ad.search.edges.map((it) => ({
...it,
isLoading: it.node.state === 'PROCESSING',
}))
})
.filter((item) => props.filterFunc(item.node)) || []
return items
}, [itemsPages, performActionOnItem])

View file

@ -2,15 +2,15 @@ import { Allotment } from 'allotment'
import 'allotment/dist/style.css'
import { LibraryContainer } from './LibraryContainer'
export function LibraryItemsContainer(): JSX.Element {
return (
<Allotment>
<Allotment.Pane minSize={200}>
<LibraryContainer folder="inbox" />
</Allotment.Pane>
{/* <Allotment.Pane snap maxSize={400}>
<HighlightsList />
</Allotment.Pane> */}
</Allotment>
)
}
// export function LibraryItemsContainer(): JSX.Element {
// return (
// <Allotment>
// {/* <Allotment.Pane minSize={200}>
// <LibraryContainer folder="inbox" />
// </Allotment.Pane> */}
// {/* <Allotment.Pane snap maxSize={400}>
// <HighlightsList />
// </Allotment.Pane> */}
// </Allotment>
// )
// }

View file

@ -1,4 +1,4 @@
import { useState, useEffect, useCallback } from 'react'
import { useCallback } from 'react'
import { setLinkArchivedMutation } from '../networking/mutations/setLinkArchivedMutation'
import {
showErrorToast,
@ -8,6 +8,7 @@ import {
import { deleteLinkMutation } from '../networking/mutations/deleteLinkMutation'
import { updatePageMutation } from '../networking/mutations/updatePageMutation'
import { State } from '../networking/fragments/articleFragment'
import { moveToFolderMutation } from '../networking/mutations/moveToLibraryMutation'
export default function useLibraryItemActions() {
const archiveItem = useCallback(async (itemId: string) => {
@ -51,15 +52,11 @@ export default function useLibraryItemActions() {
}, [])
const moveItem = useCallback(async (itemId: string) => {
const result = await setLinkArchivedMutation({
linkId: itemId,
archived: true,
})
const result = await moveToFolderMutation(itemId, 'inbox')
if (result) {
showSuccessToast('Link archived', { position: 'bottom-right' })
showSuccessToast('Moved to library', { position: 'bottom-right' })
} else {
showErrorToast('Error archiving link', { position: 'bottom-right' })
showErrorToast('Error moving item', { position: 'bottom-right' })
}
return !!result

View file

@ -31,6 +31,8 @@ export enum State {
SUCCEEDED = 'SUCCEEDED',
PROCESSING = 'PROCESSING',
FAILED = 'FAILED',
DELETED = 'DELETED',
ARCHIVED = 'ARCHIVED',
}
export enum PageType {

View file

@ -82,6 +82,7 @@ export type LibraryItemNode = {
readingProgressTopPercent?: number
readingProgressAnchorIndex: number
slug: string
folder?: string
isArchived: boolean
description: string
ownedByViewer: boolean
@ -168,6 +169,7 @@ export function useGetLibraryItemsQuery(
title
slug
url
folder
pageType
contentReader
createdAt
@ -284,6 +286,7 @@ export function useGetLibraryItemsQuery(
action: LibraryItemAction,
item: LibraryItem
) => {
console.log('performing action on items: ', action)
if (!responsePages) {
return
}
@ -295,11 +298,16 @@ export function useGetLibraryItemsQuery(
for (const searchResults of responsePages) {
const itemIndex = getIndexOf(searchResults.search, item)
console.log(' --- item index', itemIndex)
if (itemIndex !== -1) {
if (typeof mutatedItem === 'undefined') {
searchResults.search.edges.splice(itemIndex, 1)
} else {
searchResults.search.edges.splice(itemIndex, 1, mutatedItem)
console.log(
'earchResults.search.edges:',
searchResults.search.edges[itemIndex]
)
}
break
}
@ -309,17 +317,14 @@ export function useGetLibraryItemsQuery(
switch (action) {
case 'archive':
if (/in:all/.test(query)) {
updateData({
cursor: item.cursor,
node: {
...item.node,
isArchived: true,
},
})
} else {
updateData(undefined)
}
console.log('setting item archived')
updateData({
cursor: item.cursor,
node: {
...item.node,
isArchived: true,
},
})
setLinkArchivedMutation({
linkId: item.node.id,
@ -332,6 +337,8 @@ export function useGetLibraryItemsQuery(
}
})
mutate()
break
case 'unarchive':
if (/in:all/.test(query)) {
@ -421,27 +428,6 @@ export function useGetLibraryItemsQuery(
readingProgressAnchorIndex: 0,
})
break
// case 'unsubscribe':
// if (!!item.node.subscription) {
// updateData({
// cursor: item.cursor,
// node: {
// ...item.node,
// subscription: undefined,
// },
// })
// unsubscribeMutation(item.node.subscription).then((res) => {
// if (res) {
// showSuccessToast('Unsubscribed successfully', {
// position: 'bottom-right',
// })
// } else {
// showErrorToast('Error unsubscribing', {
// position: 'bottom-right',
// })
// }
// })
// }
case 'update-item':
updateData(item)
break

View file

@ -34,13 +34,46 @@ export default function Home(): JSX.Element {
case 'highlights':
return <HighlightsContainer />
case 'library':
return <LibraryContainer folder="inbox" />
return (
<LibraryContainer
folder="inbox"
filterFunc={(item) => {
console.log('filtering: ', item)
return (
item.state != 'DELETED' &&
!item.isArchived &&
item.folder == 'inbox'
)
}}
/>
)
case 'subscriptions':
return <LibraryContainer folder="following" />
return (
<LibraryContainer
folder="following"
filterFunc={(item) => {
return (
item.state != 'DELETED' &&
!item.isArchived &&
item.folder == 'following'
)
}}
/>
)
case 'archive':
return <LibraryContainer folder="archive" />
return (
<LibraryContainer
folder="archive"
filterFunc={(item) => item.state != 'DELETED' && item.isArchived}
/>
)
case 'trash':
return <LibraryContainer folder="trash" />
return (
<LibraryContainer
folder="trash"
filterFunc={(item) => item.state == 'DELETED'}
/>
)
default:
return <></>