mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Filtering on the library view
This commit is contained in:
parent
b39bffdc1a
commit
613dd39aba
8 changed files with 131 additions and 87 deletions
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
// )
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ export enum State {
|
|||
SUCCEEDED = 'SUCCEEDED',
|
||||
PROCESSING = 'PROCESSING',
|
||||
FAILED = 'FAILED',
|
||||
DELETED = 'DELETED',
|
||||
ARCHIVED = 'ARCHIVED',
|
||||
}
|
||||
|
||||
export enum PageType {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 <></>
|
||||
|
|
|
|||
Loading…
Reference in a new issue