|
1 |
| -import { useMutation } from '@apollo/client' |
| 1 | +import { useMutation, useQuery } from '@apollo/client' |
2 | 2 | import { gql } from 'graphql-tag'
|
3 | 3 | import Dropdown from 'react-bootstrap/Dropdown'
|
4 | 4 | import { useToast } from './toast'
|
| 5 | +import { useCallback, useMemo, useEffect, useState } from 'react' |
| 6 | +import { DndProvider, useDndHandlers } from '@/wallets/client/context/dnd' |
| 7 | +import { ListItem, ItemsSkeleton } from './items' |
| 8 | +import MoreFooter from './more-footer' |
| 9 | +import { useData } from './use-data' |
| 10 | +import { SUB_ITEMS } from '@/fragments/subs' |
| 11 | +import styles from './items.module.css' |
| 12 | +import bookmarkStyles from '@/styles/bookmark.module.css' |
| 13 | +import DragIcon from '@/svgs/draggable.svg' |
5 | 14 |
|
6 | 15 | export default function BookmarkDropdownItem ({ item: { id, meBookmark } }) {
|
7 | 16 | const toaster = useToast()
|
@@ -39,3 +48,85 @@ export default function BookmarkDropdownItem ({ item: { id, meBookmark } }) {
|
39 | 48 | </Dropdown.Item>
|
40 | 49 | )
|
41 | 50 | }
|
| 51 | + |
| 52 | +const REORDER_BOOKMARKS = gql` |
| 53 | + mutation reorderBookmarks($itemIds: [ID!]!) { |
| 54 | + reorderBookmarks(itemIds: $itemIds) |
| 55 | + } |
| 56 | +` |
| 57 | + |
| 58 | +function DraggableBookmarkItem ({ item, index, children, ...props }) { |
| 59 | + const handlers = useDndHandlers(index) |
| 60 | + return ( |
| 61 | + <div |
| 62 | + draggable |
| 63 | + onDragStart={handlers.handleDragStart} |
| 64 | + onDragOver={handlers.handleDragOver} |
| 65 | + onDragEnter={handlers.handleDragEnter} |
| 66 | + onDragLeave={handlers.handleDragLeave} |
| 67 | + onDrop={handlers.handleDrop} |
| 68 | + onDragEnd={handlers.handleDragEnd} |
| 69 | + onTouchStart={handlers.handleTouchStart} |
| 70 | + onTouchMove={handlers.handleTouchMove} |
| 71 | + onTouchEnd={handlers.handleTouchEnd} |
| 72 | + data-index={index} |
| 73 | + className={`${bookmarkStyles.draggableBookmark} ${handlers.isBeingDragged ? bookmarkStyles.dragging : ''} ${handlers.isDragOver ? bookmarkStyles.dragOver : ''}`} |
| 74 | + {...props} |
| 75 | + > |
| 76 | + <DragIcon className={bookmarkStyles.dragHandle} width={14} height={14} /> |
| 77 | + {children} |
| 78 | + </div> |
| 79 | + ) |
| 80 | +} |
| 81 | + |
| 82 | +export function CustomBookmarkList ({ ssrData, variables = {}, query }) { |
| 83 | + const toaster = useToast() |
| 84 | + const [reorderBookmarks] = useMutation(REORDER_BOOKMARKS) |
| 85 | + |
| 86 | + const { data, fetchMore } = useQuery(query || SUB_ITEMS, { variables }) |
| 87 | + const dat = useData(data, ssrData) |
| 88 | + |
| 89 | + const { items, cursor } = useMemo(() => { |
| 90 | + if (!dat) return { items: [], cursor: null } |
| 91 | + return dat?.items || { items: [], cursor: null } |
| 92 | + }, [dat]) |
| 93 | + |
| 94 | + const [orderedItems, setOrderedItems] = useState(items || []) |
| 95 | + useEffect(() => { setOrderedItems(items || []) }, [items]) |
| 96 | + |
| 97 | + const Skeleton = useCallback(() => |
| 98 | + <ItemsSkeleton startRank={items?.length} limit={variables.limit} Footer={MoreFooter} />, [items]) |
| 99 | + |
| 100 | + if (!dat) return <Skeleton /> |
| 101 | + |
| 102 | + const handleReorder = useCallback(async (newItems) => { |
| 103 | + try { |
| 104 | + const itemIds = newItems.map(item => item.id.toString()) |
| 105 | + await reorderBookmarks({ variables: { itemIds } }) |
| 106 | + toaster.success('bookmarks reordered') |
| 107 | + } catch (err) { |
| 108 | + console.error(err) |
| 109 | + toaster.danger('failed to reorder bookmarks') |
| 110 | + setOrderedItems(items || []) |
| 111 | + } |
| 112 | + }, [reorderBookmarks, toaster]) |
| 113 | + |
| 114 | + const visibleItems = useMemo(() => (orderedItems || []).filter(item => item?.meBookmark === true), [orderedItems]) |
| 115 | + |
| 116 | + return ( |
| 117 | + <DndProvider items={visibleItems} onReorder={(newItems) => { setOrderedItems(newItems); handleReorder(newItems) }}> |
| 118 | + <div className={styles.grid}> |
| 119 | + {visibleItems.map((item, i) => ( |
| 120 | + <DraggableBookmarkItem key={item.id} item={item} index={i}> |
| 121 | + <ListItem item={item} itemClassName={variables.includeComments ? 'py-2' : ''} pinnable={false} /> |
| 122 | + </DraggableBookmarkItem> |
| 123 | + ))} |
| 124 | + </div> |
| 125 | + <MoreFooter |
| 126 | + cursor={cursor} fetchMore={fetchMore} |
| 127 | + count={visibleItems.length} |
| 128 | + Skeleton={Skeleton} |
| 129 | + /> |
| 130 | + </DndProvider> |
| 131 | + ) |
| 132 | +} |
0 commit comments