Dont require confirmation on delete, show undo action
This commit is contained in:
@ -77,9 +77,9 @@ export const Button = styled('button', {
|
||||
fontFamily: 'Inter',
|
||||
borderRadius: '8px',
|
||||
cursor: 'pointer',
|
||||
color: '$grayTextContrast',
|
||||
color: 'white',
|
||||
p: '10px 12px',
|
||||
bg: 'rgb(125, 125, 125, 0.1)',
|
||||
bg: 'rgb(125, 125, 125, 0.3)',
|
||||
'&:hover': {
|
||||
bg: 'rgb(47, 47, 47, 0.1)',
|
||||
'.ctaButtonIcon': {
|
||||
|
||||
@ -1,14 +1,7 @@
|
||||
import { Action, createAction, useKBar, useRegisterActions } from 'kbar'
|
||||
import debounce from 'lodash/debounce'
|
||||
import { useRouter } from 'next/router'
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useReducer,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
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'
|
||||
@ -48,10 +41,13 @@ import { LibraryHeader, MultiSelectMode } from './LibraryHeader'
|
||||
import { UploadModal } from '../UploadModal'
|
||||
import { BulkAction } from '../../../lib/networking/mutations/bulkActionMutation'
|
||||
import { bulkActionMutation } from '../../../lib/networking/mutations/bulkActionMutation'
|
||||
import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers'
|
||||
import {
|
||||
showErrorToast,
|
||||
showSuccessToast,
|
||||
showSuccessToastWithUndo,
|
||||
} from '../../../lib/toastHelpers'
|
||||
import { SetPageLabelsModalPresenter } from '../article/SetLabelsModalPresenter'
|
||||
import { NotebookPresenter } from '../article/NotebookPresenter'
|
||||
import { Highlight } from '../../../lib/networking/fragments/highlightFragment'
|
||||
|
||||
export type LayoutType = 'LIST_LAYOUT' | 'GRID_LAYOUT'
|
||||
export type LibraryMode = 'reads' | 'highlights'
|
||||
@ -110,6 +106,19 @@ export function HomeFeedContainer(): JSX.Element {
|
||||
mutate,
|
||||
} = useGetLibraryItemsQuery(queryInputs)
|
||||
|
||||
useEffect(() => {
|
||||
const handleRevalidate = () => {
|
||||
;(async () => {
|
||||
console.log('revalidating library')
|
||||
await mutate()
|
||||
})()
|
||||
}
|
||||
document.addEventListener('revalidateLibrary', handleRevalidate)
|
||||
return () => {
|
||||
document.removeEventListener('revalidateLibrary', handleRevalidate)
|
||||
}
|
||||
}, [mutate])
|
||||
|
||||
useEffect(() => {
|
||||
if (queryValue.startsWith('#')) {
|
||||
debouncedFetchSearchResults(
|
||||
@ -1199,10 +1208,7 @@ function LibraryItems(props: LibraryItemsProps): JSX.Element {
|
||||
setIsChecked={props.setIsChecked}
|
||||
multiSelectMode={props.multiSelectMode}
|
||||
handleAction={(action: LinkedItemCardAction) => {
|
||||
if (action === 'delete') {
|
||||
props.setShowRemoveLinkConfirmation(true)
|
||||
props.setLinkToRemove(linkedItem)
|
||||
} else if (action === 'editTitle') {
|
||||
if (action === 'editTitle') {
|
||||
props.setShowEditTitleModal(true)
|
||||
props.setLinkToEdit(linkedItem)
|
||||
} else if (action == 'unsubscribe') {
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
import { gql } from 'graphql-request'
|
||||
import { gqlFetcher } from '../networkHelpers'
|
||||
import { State } from '../fragments/articleFragment'
|
||||
|
||||
export type UpdatePageInput = {
|
||||
pageId: string
|
||||
title: string
|
||||
title?: string
|
||||
byline?: string | undefined
|
||||
description: string
|
||||
description?: string
|
||||
savedAt?: string
|
||||
publishedAt?: string
|
||||
state?: State
|
||||
}
|
||||
|
||||
export async function updatePageMutation(
|
||||
|
||||
@ -1,15 +1,20 @@
|
||||
import { gql } from 'graphql-request'
|
||||
import useSWRInfinite from 'swr/infinite'
|
||||
import { gqlFetcher } from '../networkHelpers'
|
||||
import type { PageType, State } from '../fragments/articleFragment'
|
||||
import { PageType, State } from '../fragments/articleFragment'
|
||||
import { ContentReader } from '../fragments/articleFragment'
|
||||
import { setLinkArchivedMutation } from '../mutations/setLinkArchivedMutation'
|
||||
import { deleteLinkMutation } from '../mutations/deleteLinkMutation'
|
||||
import { unsubscribeMutation } from '../mutations/unsubscribeMutation'
|
||||
import { articleReadingProgressMutation } from '../mutations/articleReadingProgressMutation'
|
||||
import { Label } from './../fragments/labelFragment'
|
||||
import { showErrorToast, showSuccessToast } from '../../toastHelpers'
|
||||
import {
|
||||
showErrorToast,
|
||||
showSuccessToast,
|
||||
showSuccessToastWithUndo,
|
||||
} from '../../toastHelpers'
|
||||
import { Highlight, highlightFragment } from '../fragments/highlightFragment'
|
||||
import { updatePageMutation } from '../mutations/updatePageMutation'
|
||||
|
||||
export interface ReadableItem {
|
||||
id: string
|
||||
@ -343,9 +348,26 @@ export function useGetLibraryItemsQuery({
|
||||
break
|
||||
case 'delete':
|
||||
updateData(undefined)
|
||||
deleteLinkMutation(item.node.id).then((res) => {
|
||||
|
||||
const pageId = item.node.id
|
||||
deleteLinkMutation(pageId).then((res) => {
|
||||
if (res) {
|
||||
showSuccessToast('Link removed', { position: 'bottom-right' })
|
||||
showSuccessToastWithUndo('Page deleted', async () => {
|
||||
const result = await updatePageMutation({
|
||||
pageId: pageId,
|
||||
state: State.SUCCEEDED,
|
||||
})
|
||||
|
||||
mutate()
|
||||
|
||||
if (result) {
|
||||
showSuccessToast('Page recovered')
|
||||
} else {
|
||||
showErrorToast(
|
||||
'Error recovering page, check your deleted items'
|
||||
)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
showErrorToast('Error removing link', { position: 'bottom-right' })
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import { toast, ToastOptions } from 'react-hot-toast'
|
||||
import { CheckCircle, WarningCircle, X } from 'phosphor-react'
|
||||
import { Box, HStack } from '../components/elements/LayoutPrimitives'
|
||||
import { styled } from '@stitches/react'
|
||||
import { Button } from '../components/elements/Button'
|
||||
|
||||
const toastStyles = {
|
||||
minWidth: 265,
|
||||
@ -67,10 +68,64 @@ const showToast = (
|
||||
)
|
||||
}
|
||||
|
||||
const showToastWithUndo = (
|
||||
message: string,
|
||||
background: string,
|
||||
undoAction: () => Promise<void>,
|
||||
options?: ToastOptions
|
||||
) => {
|
||||
return toast(
|
||||
({ id }) => (
|
||||
<FullWidthContainer alignment="center">
|
||||
<CheckCircle size={24} color="white" />
|
||||
<MessageContainer>{message}</MessageContainer>
|
||||
<HStack distribution="end" css={{ marginLeft: 16 }}>
|
||||
<Button
|
||||
style="ctaLightGray"
|
||||
onClick={(event) => {
|
||||
event.preventDefault()
|
||||
|
||||
toast.dismiss(id)
|
||||
;(async () => {
|
||||
await undoAction()
|
||||
})()
|
||||
}}
|
||||
>
|
||||
Undo
|
||||
</Button>
|
||||
</HStack>
|
||||
</FullWidthContainer>
|
||||
),
|
||||
{
|
||||
style: {
|
||||
...toastStyles,
|
||||
background: background,
|
||||
},
|
||||
duration: 3500,
|
||||
...options,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export const showSuccessToast = (message: string, options?: ToastOptions) => {
|
||||
return showToast(message, '#55B938', 'success', options)
|
||||
return showToast(message, '#55B938', 'success', {
|
||||
position: 'bottom-right',
|
||||
...options,
|
||||
})
|
||||
}
|
||||
|
||||
export const showErrorToast = (message: string, options?: ToastOptions) => {
|
||||
return showToast(message, '#cc0000', 'error', options)
|
||||
return showToast(message, '#cc0000', 'error', {
|
||||
position: 'bottom-right',
|
||||
...options,
|
||||
})
|
||||
}
|
||||
|
||||
export const showSuccessToastWithUndo = (
|
||||
message: string,
|
||||
undoAction: () => Promise<void>
|
||||
) => {
|
||||
return showToastWithUndo(message, '#55B938', undoAction, {
|
||||
position: 'bottom-right',
|
||||
})
|
||||
}
|
||||
|
||||
@ -27,7 +27,11 @@ import { ArticleActionsMenu } from '../../../components/templates/article/Articl
|
||||
import { setLinkArchivedMutation } from '../../../lib/networking/mutations/setLinkArchivedMutation'
|
||||
import { Label } from '../../../lib/networking/fragments/labelFragment'
|
||||
import { useSWRConfig } from 'swr'
|
||||
import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers'
|
||||
import {
|
||||
showErrorToast,
|
||||
showSuccessToast,
|
||||
showSuccessToastWithUndo,
|
||||
} from '../../../lib/toastHelpers'
|
||||
import { SetLabelsModal } from '../../../components/templates/article/SetLabelsModal'
|
||||
import { DisplaySettingsModal } from '../../../components/templates/article/DisplaySettingsModal'
|
||||
import { useReaderSettings } from '../../../lib/hooks/useReaderSettings'
|
||||
@ -41,6 +45,8 @@ import { VerticalArticleActionsMenu } from '../../../components/templates/articl
|
||||
import { PdfHeaderSpacer } from '../../../components/templates/article/PdfHeaderSpacer'
|
||||
import { EpubContainerProps } from '../../../components/templates/article/EpubContainer'
|
||||
import { useSetPageLabels } from '../../../lib/hooks/useSetPageLabels'
|
||||
import { updatePageMutation } from '../../../lib/networking/mutations/updatePageMutation'
|
||||
import { State } from '../../../lib/networking/fragments/articleFragment'
|
||||
|
||||
const PdfArticleContainerNoSSR = dynamic<PdfArticleContainerProps>(
|
||||
() => import('./../../../components/templates/article/PdfArticleContainer'),
|
||||
@ -138,7 +144,7 @@ export default function Home(): JSX.Element {
|
||||
}
|
||||
break
|
||||
case 'delete':
|
||||
readerSettings.setShowDeleteConfirmation(true)
|
||||
await deleteCurrentItem()
|
||||
break
|
||||
case 'openOriginalArticle':
|
||||
const url = article?.url
|
||||
@ -206,10 +212,23 @@ export default function Home(): JSX.Element {
|
||||
|
||||
const deleteCurrentItem = useCallback(async () => {
|
||||
if (article) {
|
||||
removeItemFromCache(cache, mutate, article.id)
|
||||
await deleteLinkMutation(article.id).then((res) => {
|
||||
const pageId = article.id
|
||||
|
||||
removeItemFromCache(cache, mutate, pageId)
|
||||
await deleteLinkMutation(pageId).then((res) => {
|
||||
if (res) {
|
||||
showSuccessToast('Page deleted', { position: 'bottom-right' })
|
||||
showSuccessToastWithUndo('Page deleted', async () => {
|
||||
const result = await updatePageMutation({
|
||||
pageId: pageId,
|
||||
state: State.SUCCEEDED,
|
||||
})
|
||||
document.dispatchEvent(new Event('revalidateLibrary'))
|
||||
if (result) {
|
||||
showSuccessToast('Page recovered')
|
||||
} else {
|
||||
showErrorToast('Error recovering page, check your deleted items')
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// todo: revalidate or put back in cache?
|
||||
showErrorToast('Error deleting page', { position: 'bottom-right' })
|
||||
|
||||
Reference in New Issue
Block a user