Move bulk action to react-query
This commit is contained in:
@ -1,7 +1,7 @@
|
||||
import type { LibraryItem } from '../../../lib/networking/library_items/useLibraryItems'
|
||||
import { LinkedItemCardAction } from '../../patterns/LibraryCards/CardTypes'
|
||||
import { MultiSelectMode } from './LibraryHeader'
|
||||
import { BulkAction } from '../../../lib/networking/mutations/bulkActionMutation'
|
||||
import { BulkAction } from '../../../lib/networking/library_items/useLibraryItems'
|
||||
|
||||
export type LayoutType = 'LIST_LAYOUT' | 'GRID_LAYOUT'
|
||||
export type LibraryMode = 'reads' | 'highlights' | 'tldr'
|
||||
|
||||
@ -10,7 +10,7 @@ import { LayoutType, LibraryMode } from './HomeFeedContainer'
|
||||
import { OmnivoreSmallLogo } from '../../elements/images/OmnivoreNameLogo'
|
||||
import { DEFAULT_HEADER_HEIGHT, HeaderSpacer } from './HeaderSpacer'
|
||||
import { LIBRARY_LEFT_MENU_WIDTH } from '../navMenu/LibraryMenu'
|
||||
import { BulkAction } from '../../../lib/networking/mutations/bulkActionMutation'
|
||||
import { BulkAction } from '../../../lib/networking/library_items/useLibraryItems'
|
||||
import { HeaderToggleGridIcon } from '../../elements/icons/HeaderToggleGridIcon'
|
||||
import { HeaderToggleListIcon } from '../../elements/icons/HeaderToggleListIcon'
|
||||
import { HeaderToggleTLDRIcon } from '../../elements/icons/HeaderToggleTLDRIcon'
|
||||
|
||||
@ -2,7 +2,7 @@ import { useState } from 'react'
|
||||
import { theme } from '../../tokens/stitches.config'
|
||||
import { Box, HStack, SpanBox } from '../../elements/LayoutPrimitives'
|
||||
import { Button } from '../../elements/Button'
|
||||
import { BulkAction } from '../../../lib/networking/mutations/bulkActionMutation'
|
||||
import { BulkAction } from '../../../lib/networking/library_items/useLibraryItems'
|
||||
import { ArchiveIcon } from '../../elements/icons/ArchiveIcon'
|
||||
import { LabelIcon } from '../../elements/icons/LabelIcon'
|
||||
import { TrashIcon } from '../../elements/icons/TrashIcon'
|
||||
@ -116,7 +116,7 @@ export const MultiSelectControls = (props: MultiSelectProps): JSX.Element => {
|
||||
<SpanBox
|
||||
css={{
|
||||
display: 'none',
|
||||
fontSize: '14px',
|
||||
fontSize: '11px',
|
||||
fontFamily: '$display',
|
||||
marginRight: 'auto',
|
||||
'@mdDown': {
|
||||
|
||||
@ -19,6 +19,7 @@ import {
|
||||
LibraryItems,
|
||||
LibraryItemsQueryInput,
|
||||
useArchiveItem,
|
||||
useBulkActions,
|
||||
useDeleteItem,
|
||||
useGetLibraryItems,
|
||||
useMoveItemToFolder,
|
||||
@ -39,8 +40,7 @@ import { EditLibraryItemModal } from '../homeFeed/EditItemModals'
|
||||
import { EmptyLibrary } from '../homeFeed/EmptyLibrary'
|
||||
import { MultiSelectMode } from '../homeFeed/LibraryHeader'
|
||||
import { UploadModal } from '../UploadModal'
|
||||
import { BulkAction } from '../../../lib/networking/mutations/bulkActionMutation'
|
||||
import { bulkActionMutation } from '../../../lib/networking/mutations/bulkActionMutation'
|
||||
import { BulkAction } from '../../../lib/networking/library_items/useLibraryItems'
|
||||
import {
|
||||
showErrorToast,
|
||||
showSuccessToast,
|
||||
@ -117,6 +117,7 @@ export function LibraryContainer(props: LibraryContainerProps): JSX.Element {
|
||||
const archiveItem = useArchiveItem()
|
||||
const deleteItem = useDeleteItem()
|
||||
const moveToFolder = useMoveItemToFolder()
|
||||
const bulkAction = useBulkActions()
|
||||
const updateItemReadStatus = useUpdateItemReadStatus()
|
||||
|
||||
const [queryInputs, setQueryInputs] =
|
||||
@ -801,12 +802,12 @@ export function LibraryContainer(props: LibraryContainerProps): JSX.Element {
|
||||
const expectedCount = checkedItems.length
|
||||
|
||||
try {
|
||||
const res = await bulkActionMutation(
|
||||
const res = await bulkAction.mutateAsync({
|
||||
action,
|
||||
query,
|
||||
expectedCount,
|
||||
labelIds
|
||||
)
|
||||
labelIds,
|
||||
})
|
||||
if (res) {
|
||||
let successMessage: string | undefined = undefined
|
||||
switch (action) {
|
||||
|
||||
@ -9,7 +9,7 @@ import { FunnelSimple, X } from '@phosphor-icons/react'
|
||||
import { LayoutType } from '../homeFeed/HomeFeedContainer'
|
||||
import { OmnivoreSmallLogo } from '../../elements/images/OmnivoreNameLogo'
|
||||
import { LIBRARY_LEFT_MENU_WIDTH } from '../navMenu/LibraryMenu'
|
||||
import { BulkAction } from '../../../lib/networking/mutations/bulkActionMutation'
|
||||
import { BulkAction } from '../../../lib/networking/library_items/useLibraryItems'
|
||||
import { HeaderToggleGridIcon } from '../../elements/icons/HeaderToggleGridIcon'
|
||||
import { HeaderToggleListIcon } from '../../elements/icons/HeaderToggleListIcon'
|
||||
import { UserBasicData } from '../../../lib/networking/queries/useGetViewerQuery'
|
||||
|
||||
@ -1,56 +1,4 @@
|
||||
import { Action, createAction, useKBar, useRegisterActions } from 'kbar'
|
||||
import debounce from 'lodash/debounce'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Toaster } from 'react-hot-toast'
|
||||
import TopBarProgress from 'react-topbar-progress-indicator'
|
||||
import { useFetchMore } from '../../../lib/hooks/useFetchMoreScroll'
|
||||
import { usePersistedState } from '../../../lib/hooks/usePersistedState'
|
||||
import { libraryListCommands } from '../../../lib/keyboardShortcuts/navigationShortcuts'
|
||||
import { useKeyboardShortcuts } from '../../../lib/keyboardShortcuts/useKeyboardShortcuts'
|
||||
import {
|
||||
PageType,
|
||||
State,
|
||||
} from '../../../lib/networking/fragments/articleFragment'
|
||||
import {
|
||||
SearchItem,
|
||||
TypeaheadSearchItemsData,
|
||||
typeaheadSearchQuery,
|
||||
} from '../../../lib/networking/queries/typeaheadSearch'
|
||||
import type {
|
||||
LibraryItem,
|
||||
LibraryItemsQueryInput,
|
||||
} from '../../../lib/networking/library_items/useLibraryItems'
|
||||
import {
|
||||
useGetViewerQuery,
|
||||
UserBasicData,
|
||||
} from '../../../lib/networking/queries/useGetViewerQuery'
|
||||
import { Button } from '../../elements/Button'
|
||||
import { StyledText } from '../../elements/StyledText'
|
||||
import { ConfirmationModal } from '../../patterns/ConfirmationModal'
|
||||
import { LinkedItemCardAction } from '../../patterns/LibraryCards/CardTypes'
|
||||
import { LinkedItemCard } from '../../patterns/LibraryCards/LinkedItemCard'
|
||||
import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
|
||||
import { AddLinkModal } from '../AddLinkModal'
|
||||
import { EditLibraryItemModal } from '../homeFeed/EditItemModals'
|
||||
import { EmptyLibrary } from '../homeFeed/EmptyLibrary'
|
||||
import { LegacyLibraryHeader, MultiSelectMode } from '../homeFeed/LibraryHeader'
|
||||
import { UploadModal } from '../UploadModal'
|
||||
import { BulkAction } from '../../../lib/networking/mutations/bulkActionMutation'
|
||||
import { bulkActionMutation } from '../../../lib/networking/mutations/bulkActionMutation'
|
||||
import {
|
||||
showErrorToast,
|
||||
showSuccessToast,
|
||||
showSuccessToastWithAction,
|
||||
} from '../../../lib/toastHelpers'
|
||||
import { SetPageLabelsModalPresenter } from '../article/SetLabelsModalPresenter'
|
||||
import { NotebookPresenter } from '../article/NotebookPresenter'
|
||||
import { saveUrlMutation } from '../../../lib/networking/mutations/saveUrlMutation'
|
||||
import { articleQuery } from '../../../lib/networking/queries/useGetArticleQuery'
|
||||
import { PinnedButtons } from '../homeFeed/PinnedButtons'
|
||||
import { PinnedSearch } from '../../../pages/settings/pinned-searches'
|
||||
import { FetchItemsError } from '../homeFeed/FetchItemsError'
|
||||
import { LibraryHeader } from './LibraryHeader'
|
||||
import { VStack } from '../../elements/LayoutPrimitives'
|
||||
|
||||
type LibrarySideBarProps = {
|
||||
text: string
|
||||
|
||||
@ -235,3 +235,26 @@ export const GQL_GET_LIBRARY_ITEM_CONTENT = gql`
|
||||
${labelFragment}
|
||||
${recommendationFragment}
|
||||
`
|
||||
|
||||
export const GQL_BULK_ACTION = gql`
|
||||
mutation BulkAction(
|
||||
$action: BulkActionType!
|
||||
$query: String!
|
||||
$expectedCount: Int
|
||||
$labelIds: [ID!]
|
||||
) {
|
||||
bulkAction(
|
||||
query: $query
|
||||
action: $action
|
||||
labelIds: $labelIds
|
||||
expectedCount: $expectedCount
|
||||
) {
|
||||
... on BulkActionSuccess {
|
||||
success
|
||||
}
|
||||
... on BulkActionError {
|
||||
errorCodes
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
@ -12,6 +12,7 @@ import { Highlight, highlightFragment } from '../fragments/highlightFragment'
|
||||
import { makeGqlFetcher, requestHeaders } from '../networkHelpers'
|
||||
import { Label } from '../fragments/labelFragment'
|
||||
import {
|
||||
GQL_BULK_ACTION,
|
||||
GQL_DELETE_LIBRARY_ITEM,
|
||||
GQL_GET_LIBRARY_ITEM_CONTENT,
|
||||
GQL_MOVE_ITEM_TO_FOLDER,
|
||||
@ -621,6 +622,56 @@ export const useSetItemLabels = () => {
|
||||
})
|
||||
}
|
||||
|
||||
export const useBulkActions = () => {
|
||||
const queryClient = useQueryClient()
|
||||
const bulkAction = async (variables: {
|
||||
action: BulkAction
|
||||
query: string
|
||||
expectedCount: number
|
||||
labelIds?: string[]
|
||||
}) => {
|
||||
const result = (await gqlFetcher(GQL_BULK_ACTION, {
|
||||
...variables,
|
||||
})) as BulkActionData
|
||||
if (result.bulkAction?.errorCodes?.length) {
|
||||
throw new Error(result.bulkAction.errorCodes[0])
|
||||
}
|
||||
return result.bulkAction.success
|
||||
}
|
||||
return useMutation({
|
||||
mutationFn: bulkAction,
|
||||
onMutate: async (variables: {
|
||||
action: BulkAction
|
||||
query: string
|
||||
expectedCount: number
|
||||
labelIds?: string[]
|
||||
}) => {
|
||||
await queryClient.cancelQueries({ queryKey: ['libraryItems'] })
|
||||
},
|
||||
onSettled: async (newLabels, variables) => {
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: ['libraryItems'],
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export enum BulkAction {
|
||||
ARCHIVE = 'ARCHIVE',
|
||||
DELETE = 'DELETE',
|
||||
ADD_LABELS = 'ADD_LABELS',
|
||||
MARK_AS_READ = 'MARK_AS_READ',
|
||||
}
|
||||
|
||||
type BulkActionResult = {
|
||||
success?: boolean
|
||||
errorCodes?: string[]
|
||||
}
|
||||
|
||||
type BulkActionData = {
|
||||
bulkAction: BulkActionResult
|
||||
}
|
||||
|
||||
type UpdateLibraryItemInput = {
|
||||
pageId: string
|
||||
title?: string
|
||||
|
||||
@ -1,62 +1,2 @@
|
||||
import { gql } from 'graphql-request'
|
||||
import { gqlFetcher } from '../networkHelpers'
|
||||
|
||||
export enum BulkAction {
|
||||
ARCHIVE = 'ARCHIVE',
|
||||
DELETE = 'DELETE',
|
||||
ADD_LABELS = 'ADD_LABELS',
|
||||
MARK_AS_READ = 'MARK_AS_READ',
|
||||
}
|
||||
|
||||
type BulkActionResponseData = {
|
||||
success: boolean
|
||||
}
|
||||
|
||||
type BulkActionResponse = {
|
||||
errorCodes?: string[]
|
||||
bulkAction?: BulkActionResponseData
|
||||
}
|
||||
|
||||
export async function bulkActionMutation(
|
||||
action: BulkAction,
|
||||
query: string,
|
||||
expectedCount: number,
|
||||
labelIds?: string[]
|
||||
): Promise<boolean> {
|
||||
const mutation = gql`
|
||||
mutation BulkAction(
|
||||
$action: BulkActionType!
|
||||
$query: String!
|
||||
$expectedCount: Int
|
||||
$labelIds: [ID!]
|
||||
) {
|
||||
bulkAction(
|
||||
query: $query
|
||||
action: $action
|
||||
labelIds: $labelIds
|
||||
expectedCount: $expectedCount
|
||||
) {
|
||||
... on BulkActionSuccess {
|
||||
success
|
||||
}
|
||||
... on BulkActionError {
|
||||
errorCodes
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
try {
|
||||
const response = await gqlFetcher(mutation, {
|
||||
action,
|
||||
query,
|
||||
labelIds,
|
||||
expectedCount,
|
||||
})
|
||||
const data = response as BulkActionResponse | undefined
|
||||
return data?.bulkAction?.success ?? false
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,16 +5,16 @@ import { VStack } from '../../components/elements/LayoutPrimitives'
|
||||
|
||||
import { StyledText } from '../../components/elements/StyledText'
|
||||
import { ProfileLayout } from '../../components/templates/ProfileLayout'
|
||||
import {
|
||||
BulkAction,
|
||||
bulkActionMutation,
|
||||
} from '../../lib/networking/mutations/bulkActionMutation'
|
||||
import { BulkAction } from '../../lib/networking/library_items/useLibraryItems'
|
||||
import { Button } from '../../components/elements/Button'
|
||||
import { theme } from '../../components/tokens/stitches.config'
|
||||
import { ConfirmationModal } from '../../components/patterns/ConfirmationModal'
|
||||
import { showErrorToast, showSuccessToast } from '../../lib/toastHelpers'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useGetLibraryItems } from '../../lib/networking/library_items/useLibraryItems'
|
||||
import {
|
||||
useBulkActions,
|
||||
useGetLibraryItems,
|
||||
} from '../../lib/networking/library_items/useLibraryItems'
|
||||
import {
|
||||
BorderedFormInput,
|
||||
FormLabel,
|
||||
@ -33,6 +33,7 @@ export default function BulkPerformer(): JSX.Element {
|
||||
const [expectedCount, setExpectedCount] = useState<number | undefined>()
|
||||
const [errorMessage, setErrorMessage] = useState<string | undefined>()
|
||||
const [runningState, setRunningState] = useState<RunningState>('none')
|
||||
const bulkAction = useBulkActions()
|
||||
|
||||
const { data: itemsPages, isLoading } = useGetLibraryItems(undefined, {
|
||||
searchQuery: query,
|
||||
@ -64,7 +65,11 @@ export default function BulkPerformer(): JSX.Element {
|
||||
return
|
||||
}
|
||||
try {
|
||||
const success = await bulkActionMutation(action, query, expectedCount)
|
||||
const success = await bulkAction.mutateAsync({
|
||||
action,
|
||||
query,
|
||||
expectedCount,
|
||||
})
|
||||
if (!success) {
|
||||
throw 'Success not returned'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user