diff --git a/packages/web/components/patterns/CardMenu.tsx b/packages/web/components/patterns/CardMenu.tsx index 3a7abb49b..13b2a60ef 100644 --- a/packages/web/components/patterns/CardMenu.tsx +++ b/packages/web/components/patterns/CardMenu.tsx @@ -12,6 +12,7 @@ export type CardMenuDropdownAction = | 'delete' | 'share' | 'snooze' + | 'set-labels' type CardMenuProps = { item: LibraryItemNode @@ -34,6 +35,12 @@ export function CardMenu(props: CardMenuProps): JSX.Element { title="Unarchive" /> )} + { + props.actionHandler('set-labels') + }} + title="Set Labels" + /> {isVipUser(props.viewer) && ( { diff --git a/packages/web/components/patterns/LibraryCards/CardTypes.tsx b/packages/web/components/patterns/LibraryCards/CardTypes.tsx index 6fd207761..4a2b856ac 100644 --- a/packages/web/components/patterns/LibraryCards/CardTypes.tsx +++ b/packages/web/components/patterns/LibraryCards/CardTypes.tsx @@ -12,6 +12,7 @@ export type LinkedItemCardAction = | 'mark-unread' | 'share' | 'snooze' + | 'set-labels' export type LinkedItemCardProps = { item: LibraryItemNode diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx index 9b82691f0..a5121d9c8 100644 --- a/packages/web/components/templates/article/ArticleActionsMenu.tsx +++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx @@ -110,6 +110,8 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element > diff --git a/packages/web/components/templates/article/SetLabelsControl.tsx b/packages/web/components/templates/article/SetLabelsControl.tsx index 1302369d8..2afb1e06e 100644 --- a/packages/web/components/templates/article/SetLabelsControl.tsx +++ b/packages/web/components/templates/article/SetLabelsControl.tsx @@ -7,7 +7,6 @@ import { CrossIcon } from '../../elements/images/CrossIcon' import { styled, theme } from '../../tokens/stitches.config' import { Label } from '../../../lib/networking/fragments/labelFragment' import { useGetLabelsQuery } from '../../../lib/networking/queries/useGetLabelsQuery' -import { ArticleAttributes } from '../../../lib/networking/queries/useGetArticleQuery' import { Check, Circle, PencilSimple, Plus } from 'phosphor-react' import { isTouchScreenDevice } from '../../../lib/deviceType' import { setLabelsMutation } from '../../../lib/networking/mutations/setLabelsMutation' @@ -15,9 +14,12 @@ import { createLabelMutation } from '../../../lib/networking/mutations/createLab import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers' import { randomLabelColorHex } from '../../../utils/settings-page/labels/labelColorObjects' import { useRouter } from 'next/router' +import { ArticleAttributes } from '../../../lib/networking/queries/useGetArticleQuery' type SetLabelsControlProps = { - article: ArticleAttributes + linkId: string + labels: Label[] | undefined + article?: ArticleAttributes articleActionHandler: (action: string, arg?: unknown) => void } @@ -187,7 +189,7 @@ export function SetLabelsControl(props: SetLabelsControlProps): JSX.Element { const router = useRouter() const [filterText, setFilterText] = useState('') const { labels, revalidate } = useGetLabelsQuery() - const [selectedLabels, setSelectedLabels] = useState(props.article.labels || []) + const [selectedLabels, setSelectedLabels] = useState(props.labels || []) useEffect(() => { setFocusedIndex(undefined) @@ -211,11 +213,13 @@ export function SetLabelsControl(props: SetLabelsControlProps): JSX.Element { setSelectedLabels(newSelectedLabels) const result = await setLabelsMutation( - props.article.linkId, + props.linkId, newSelectedLabels.map((label) => label.id) ) - props.article.labels = result + if (props.article) { + props.article.labels = result + } props.articleActionHandler('refreshLabels', result) revalidate() diff --git a/packages/web/components/templates/article/SetLabelsModal.tsx b/packages/web/components/templates/article/SetLabelsModal.tsx index 81696600f..e6ee08795 100644 --- a/packages/web/components/templates/article/SetLabelsModal.tsx +++ b/packages/web/components/templates/article/SetLabelsModal.tsx @@ -1,3 +1,4 @@ +import { Label } from '../../../lib/networking/fragments/labelFragment' import { ArticleAttributes } from '../../../lib/networking/queries/useGetArticleQuery' import { Button } from '../../elements/Button' import { CrossIcon } from '../../elements/images/CrossIcon' @@ -12,7 +13,9 @@ import { theme } from '../../tokens/stitches.config' import { SetLabelsControl } from './SetLabelsControl' type SetLabelsModalProps = { - article: ArticleAttributes + linkId: string + labels: Label[] | undefined + article?: ArticleAttributes onOpenChange: (open: boolean) => void articleActionHandler: (action: string, arg?: unknown) => void } @@ -41,6 +44,7 @@ export function SetLabelsModal(props: SetLabelsModalProps): JSX.Element { onClick={() => { props.onOpenChange(false) }} + tabIndex={-1} > ( + undefined + ) + const [showAddLinkModal, setShowAddLinkModal] = useState(false) const [queryInputs, setQueryInputs] = @@ -230,8 +236,14 @@ export function HomeFeedContainer(props: HomeFeedContainerProps): JSX.Element { return } + // If any of the modals are open we disable handling keyboard shortcuts + if (labelsTarget || snoozeTarget || shareTarget) { + return + } + switch (action) { case 'showDetail': + const username = viewerData?.me?.profile.username if (username) { setActiveCardId(item.node.id) @@ -265,6 +277,9 @@ export function HomeFeedContainer(props: HomeFeedContainerProps): JSX.Element { case 'snooze': setSnoozeTarget(item) break + case 'set-labels': + setLabelsTarget(item) + break } } @@ -355,6 +370,9 @@ export function HomeFeedContainer(props: HomeFeedContainerProps): JSX.Element { case 'markItemAsUnread': handleCardAction('mark-unread', activeItem) break + case 'showEditLabelsModal': + handleCardAction('set-labels', activeItem) + break case 'shareItem': setShareTarget(activeItem) break @@ -408,6 +426,8 @@ export function HomeFeedContainer(props: HomeFeedContainerProps): JSX.Element { setShareTarget={setShareTarget} snoozeTarget={snoozeTarget} setSnoozeTarget={setSnoozeTarget} + labelsTarget={labelsTarget} + setLabelsTarget={setLabelsTarget} showAddLinkModal={showAddLinkModal} setShowAddLinkModal={setShowAddLinkModal} /> @@ -428,6 +448,8 @@ type HomeFeedContentProps = { setShareTarget: (target: LibraryItem | undefined) => void snoozeTarget: LibraryItem | undefined setSnoozeTarget: (target: LibraryItem | undefined) => void + labelsTarget: LibraryItem | undefined + setLabelsTarget: (target: LibraryItem | undefined) => void showAddLinkModal: boolean setShowAddLinkModal: (show: boolean) => void actionHandler: ( @@ -457,6 +479,8 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { [layout, setLayout] ) + const [, updateState] = useState({}) + const StyledToggleButton = styled('button', { p: '0px', backgroundColor: 'transparent', @@ -706,6 +730,25 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { onOpenChange={() => setShowRemoveLinkConfirmation(false)} /> )} + {props.labelsTarget?.node.id && ( + { + switch(action) { + case 'refreshLabels': + if (props.labelsTarget) { + props.labelsTarget.node.labels = value as (Label[] | undefined) + updateState({}) + } + break + } + }} + onOpenChange={() => { + props.setLabelsTarget(undefined) + }} + /> + )} ) } diff --git a/packages/web/lib/keyboardShortcuts/navigationShortcuts.ts b/packages/web/lib/keyboardShortcuts/navigationShortcuts.ts index 98da5cf33..e984a199c 100644 --- a/packages/web/lib/keyboardShortcuts/navigationShortcuts.ts +++ b/packages/web/lib/keyboardShortcuts/navigationShortcuts.ts @@ -86,6 +86,7 @@ type LibraryListKeyboardAction = | 'sortAscending' | 'shareItem' | 'showAddLinkModal' + | 'showEditLabelsModal' export function libraryListCommands( actionHandler: (action: LibraryListKeyboardAction) => void @@ -127,6 +128,12 @@ export function libraryListCommands( shortcutKeyDescription: 'e', callback: () => actionHandler('archiveItem'), }, + { + shortcutKeys: ['l'], + actionDescription: 'Edit item labels', + shortcutKeyDescription: 'l', + callback: () => actionHandler('showEditLabelsModal'), + }, { shortcutKeys: ['shift', 'i'], actionDescription: 'Mark item as read', diff --git a/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx b/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx index a12a19219..f84b22a47 100644 --- a/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx +++ b/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx @@ -2,7 +2,6 @@ import { gql } from 'graphql-request' import useSWRInfinite from 'swr/infinite' import { gqlFetcher } from '../networkHelpers' import type { ArticleFragmentData } from '../fragments/articleFragment' -import { articleFragment } from '../fragments/articleFragment' import { setLinkArchivedMutation } from '../mutations/setLinkArchivedMutation' import { deleteLinkMutation } from '../mutations/deleteLinkMutation' import { articleReadingProgressMutation } from '../mutations/articleReadingProgressMutation' diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx index 2e682da08..eb5d6bd1d 100644 --- a/packages/web/pages/[username]/[slug]/index.tsx +++ b/packages/web/pages/[username]/[slug]/index.tsx @@ -254,6 +254,8 @@ export default function Home(): JSX.Element { {showSetLabelsModal && ( setShowSetLabelsModal(false)} />