Add a delete page article action, add keyboard ctrls for confirm dialogs

This commit is contained in:
Jackson Harper
2022-08-22 15:53:22 +08:00
parent 965ffb3013
commit 7673814fd3
7 changed files with 124 additions and 20 deletions

View File

@ -29,6 +29,9 @@ export const Button = styled('button', {
color: '$omnivoreGray',
bg: '$omnivoreCtaYellow',
p: '10px 13px',
'&:focus': {
outline: '5px auto -webkit-focus-ring-color',
},
},
ctaOutlineYellow: {
boxSizing: 'border-box',
@ -45,6 +48,9 @@ export const Button = styled('button', {
color: '$utilityTextDefault',
bg: 'transparent',
p: '9px 12px',
'&:focus': {
outline: '5px auto -webkit-focus-ring-color',
},
},
ctaLightGray: {
border: 0,

View File

@ -85,7 +85,7 @@ export function CardMenu(props: CardMenuProps): JSX.Element {
onSelect={() => {
props.actionHandler('delete')
}}
title="Remove"
title="Delete"
/>
{!!props.item.subscription && (
<DropdownOption

View File

@ -6,6 +6,8 @@ import {
import { VStack, HStack } from '../elements/LayoutPrimitives'
import { Button } from '../elements/Button'
import { StyledText } from '../elements/StyledText'
import { useConfirmListener } from '../../lib/keyboardShortcuts/useKeyboardShortcuts'
import { useEffect, useRef } from 'react'
type ConfirmationModalProps = {
message?: string
@ -25,13 +27,28 @@ export function ConfirmationModal(props: ConfirmationModalProps): JSX.Element {
<StyledText>{props.message}</StyledText>
<HStack distribution="center" css={{ pt: '$2' }}>
<Button
style="ctaPill"
style="ctaOutlineYellow"
css={{ mr: '$2' }}
onClick={() => props.onOpenChange(false)}
onKeyDown={(event) => {
if (event.key === 'Enter') {
event.preventDefault()
props.onOpenChange(false)
}
}}
>
Cancel
</Button>
<Button style="ctaPill" onClick={props.onAccept}>
<Button
style="ctaDarkYellow"
onClick={props.onAccept}
onKeyDown={(event) => {
if (event.key === 'Enter') {
event.preventDefault()
props.onAccept()
}
}}
>
{props.acceptButtonLabel ?? 'Confirm'}
</Button>
</HStack>

View File

@ -1,5 +1,5 @@
import { Separator } from "@radix-ui/react-separator"
import { ArchiveBox, DotsThree, HighlighterCircle, TagSimple, TextAa, Tray } from "phosphor-react"
import { ArchiveBox, DotsThree, HighlighterCircle, TagSimple, TextAa, Trash, Tray } from "phosphor-react"
import { ArticleAttributes } from "../../../lib/networking/queries/useGetArticleQuery"
import { Button } from "../../elements/Button"
import { Dropdown } from "../../elements/DropdownElements"
@ -143,6 +143,23 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
<MenuSeparator layout={props.layout} />
<Button
style="articleActionIcon"
onClick={() => {
props.articleActionHandler('delete')
}}
>
<TooltipWrapped
tooltipContent="Delete"
tooltipSide={props.layout == 'side' ? 'right' : 'bottom'}
>
<Trash
size={24}
color={theme.colors.readerFont.toString()}
/>
</TooltipWrapped>
</Button>
{!props.article?.isArchived ? (
<Button
style="articleActionIcon"

View File

@ -110,6 +110,9 @@ export function HomeFeedContainer(): JSX.Element {
const [showAddLinkModal, setShowAddLinkModal] = useState(false)
const [showEditTitleModal, setShowEditTitleModal] = useState(false)
const [linkToRemove, setLinkToRemove] = useState<LibraryItem>()
const [linkToEdit, setLinkToEdit] = useState<LibraryItem>()
const [linkToUnsubscribe, setLinkToUnsubscribe] = useState<LibraryItem>()
const [queryInputs, setQueryInputs] =
useState<LibraryItemsQueryInput>(defaultQuery)
@ -342,6 +345,10 @@ export function HomeFeedContainer(): JSX.Element {
}
}
const modalTargetItem = useMemo(() => {
return (labelsTarget || snoozeTarget || shareTarget || linkToEdit || linkToRemove || linkToUnsubscribe)
}, [labelsTarget, snoozeTarget, shareTarget, linkToEdit, linkToRemove, linkToUnsubscribe])
useKeyboardShortcuts(
libraryListCommands((action) => {
const columnCount = (container: HTMLDivElement) => {
@ -353,7 +360,7 @@ export function HomeFeedContainer(): JSX.Element {
}
// If any of the modals are open we disable handling keyboard shortcuts
if (labelsTarget || snoozeTarget || shareTarget) {
if (modalTargetItem) {
return
}
@ -577,6 +584,12 @@ export function HomeFeedContainer(): JSX.Element {
setActiveItem={(item: LibraryItem) => {
activateCard(item.node.id)
}}
linkToRemove={linkToRemove}
setLinkToRemove={setLinkToRemove}
linkToEdit={linkToEdit}
setLinkToEdit={setLinkToEdit}
linkToUnsubscribe={linkToUnsubscribe}
setLinkToUnsubscribe={setLinkToUnsubscribe}
/>
)
}
@ -602,6 +615,14 @@ type HomeFeedContentProps = {
showEditTitleModal: boolean
setShowEditTitleModal: (show: boolean) => void
setActiveItem: (item: LibraryItem) => void
linkToRemove: LibraryItem | undefined
setLinkToRemove: (set: LibraryItem | undefined) => void
linkToEdit: LibraryItem | undefined
setLinkToEdit: (set: LibraryItem | undefined) => void
linkToUnsubscribe: LibraryItem | undefined
setLinkToUnsubscribe: (set: LibraryItem | undefined) => void
actionHandler: (
action: LinkedItemCardAction,
item: LibraryItem | undefined
@ -618,9 +639,6 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
useState(false)
const [showUnsubscribeConfirmation, setShowUnsubscribeConfirmation] =
useState(false)
const [linkToRemove, setLinkToRemove] = useState<LibraryItem>()
const [linkToEdit, setLinkToEdit] = useState<LibraryItem>()
const [linkToUnsubscribe, setLinkToUnsubscribe] = useState<LibraryItem>()
const updateLayout = useCallback(
async (newLayout: LayoutType) => {
@ -649,21 +667,21 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
})
const removeItem = () => {
if (!linkToRemove) {
if (!props.linkToRemove) {
return
}
props.actionHandler('delete', linkToRemove)
setLinkToRemove(undefined)
props.actionHandler('delete', props.linkToRemove)
props.setLinkToRemove(undefined)
setShowRemoveLinkConfirmation(false)
}
const unsubscribe = () => {
if (!linkToUnsubscribe) {
if (!props.linkToUnsubscribe) {
return
}
props.actionHandler('unsubscribe', linkToUnsubscribe)
setLinkToUnsubscribe(undefined)
props.actionHandler('unsubscribe', props.linkToUnsubscribe)
props.setLinkToUnsubscribe(undefined)
setShowUnsubscribeConfirmation(false)
}
@ -847,13 +865,13 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
handleAction={(action: LinkedItemCardAction) => {
if (action === 'delete') {
setShowRemoveLinkConfirmation(true)
setLinkToRemove(linkedItem)
props.setLinkToRemove(linkedItem)
} else if (action === 'editTitle') {
props.setShowEditTitleModal(true)
setLinkToEdit(linkedItem)
props.setLinkToEdit(linkedItem)
} else if (action == 'unsubscribe') {
setShowUnsubscribeConfirmation(true)
setLinkToUnsubscribe(linkedItem)
props.setLinkToUnsubscribe(linkedItem)
} else {
props.actionHandler(action, linkedItem)
}
@ -893,7 +911,7 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
props.actionHandler('update-item', item)
}
onOpenChange={() => props.setShowEditTitleModal(false)}
item={linkToEdit as LibraryItem}
item={props.linkToEdit as LibraryItem}
/>
)}
{props.shareTarget && viewerData?.me?.profile.username && (

View File

@ -17,9 +17,11 @@ export type ReaderSettings = {
setMarginWidth: (newMarginWidth: number) => void
showSetLabelsModal: boolean
showDeleteConfirmation: boolean
showEditDisplaySettingsModal: boolean
setShowSetLabelsModal: (showSetLabelsModal: boolean) => void
setShowDeleteConfirmation: (showDeleteConfirmation: boolean) => void
setShowEditDisplaySettingsModal: (showEditDisplaySettingsModal: boolean) => void
actionHandler: (action: string, arg?: unknown) => void
@ -36,6 +38,7 @@ export const useReaderSettings = (): ReaderSettings => {
const [fontFamily, setFontFamily] = usePersistedState({ key: 'fontFamily', initialValue: DEFAULT_FONT })
const [showSetLabelsModal, setShowSetLabelsModal] = useState(false)
const [showEditDisplaySettingsModal, setShowEditDisplaySettingsModal] = useState(false)
const [showDeleteConfirmation, setShowDeleteConfirmation] = useState(false)
const updateFontSize = async (newFontSize: number) => {
setFontSize(newFontSize)
@ -136,8 +139,8 @@ export const useReaderSettings = (): ReaderSettings => {
preferencesData,
fontSize, lineHeight, marginWidth,
setFontSize, setLineHeight, setMarginWidth,
showSetLabelsModal, showEditDisplaySettingsModal,
setShowSetLabelsModal, setShowEditDisplaySettingsModal,
showDeleteConfirmation, showSetLabelsModal, showEditDisplaySettingsModal,
setShowSetLabelsModal, setShowEditDisplaySettingsModal, setShowDeleteConfirmation,
actionHandler, setFontFamily, fontFamily,
}
}

View File

@ -29,6 +29,8 @@ import { DisplaySettingsModal } from '../../../components/templates/article/Disp
import { useReaderSettings } from '../../../lib/hooks/useReaderSettings'
import { SkeletonArticleContainer } from '../../../components/templates/article/SkeletonArticleContainer'
import { useRegisterActions } from 'kbar'
import { deleteLinkMutation } from '../../../lib/networking/mutations/deleteLinkMutation'
import { ConfirmationModal } from '../../../components/patterns/ConfirmationModal'
const PdfArticleContainerNoSSR = dynamic<PdfArticleContainerProps>(
@ -61,6 +63,8 @@ export default function Home(): JSX.Element {
useKeyboardShortcuts(navigationCommands(router))
const actionHandler = useCallback(async(action: string, arg?: unknown) => {
console.log('handling action: ', action, article)
switch (action) {
case 'unarchive':
if (article) {
@ -103,6 +107,9 @@ export default function Home(): JSX.Element {
router.push(`/home`)
}
break
case 'delete':
readerSettings.setShowDeleteConfirmation(true)
break
case 'openOriginalArticle':
const url = article?.url
if (url) {
@ -128,8 +135,12 @@ export default function Home(): JSX.Element {
const openOriginalArticle = () => {
actionHandler('openOriginalArticle')
}
const deletePage = () => {
actionHandler('delete')
}
document.addEventListener('archive', archive)
document.addEventListener('delete', deletePage)
document.addEventListener('openOriginalArticle', openOriginalArticle)
return () => {
@ -155,6 +166,22 @@ export default function Home(): JSX.Element {
}
}, [article, viewerData])
const deleteCurrentItem = useCallback(async () => {
if (article) {
removeItemFromCache(cache, mutate, article.id)
await deleteLinkMutation(article.id)
.then((res) => {
if (res) {
showSuccessToast('Page deleted', { position: 'bottom-right' })
} else {
// todo: revalidate or put back in cache?
showErrorToast('Error deleting page', { position: 'bottom-right' })
}
})
router.push(`/home`)
}
}, [article])
useRegisterActions([
{
id: 'open',
@ -181,6 +208,15 @@ export default function Home(): JSX.Element {
document.dispatchEvent(new Event('archive'));
}
},
{
id: 'delete',
section: 'Article',
name: 'Delete current item',
shortcut: ['#'],
perform: () => {
document.dispatchEvent(new Event('delete'));
}
},
{
id: 'highlight',
section: 'Article',
@ -319,6 +355,13 @@ export default function Home(): JSX.Element {
onOpenChange={() => readerSettings.setShowEditDisplaySettingsModal(false)}
/>
)}
{readerSettings.showDeleteConfirmation && (
<ConfirmationModal
message={'Are you sure you want to delete this page?'}
onAccept={deleteCurrentItem}
onOpenChange={() => readerSettings.setShowDeleteConfirmation(false)}
/>
)}
</PrimaryLayout>
)
}