Add a delete page article action, add keyboard ctrls for confirm dialogs
This commit is contained in:
@ -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,
|
||||
|
||||
@ -85,7 +85,7 @@ export function CardMenu(props: CardMenuProps): JSX.Element {
|
||||
onSelect={() => {
|
||||
props.actionHandler('delete')
|
||||
}}
|
||||
title="Remove"
|
||||
title="Delete"
|
||||
/>
|
||||
{!!props.item.subscription && (
|
||||
<DropdownOption
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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 && (
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user