From 73af8ada81f5f096a06ae51957969e269b55d3a0 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 19 Apr 2022 11:10:36 -0700 Subject: [PATCH 1/8] Allow editing labels from the library view --- packages/web/components/patterns/CardMenu.tsx | 7 ++++++ .../patterns/LibraryCards/CardTypes.tsx | 1 + .../templates/article/ArticleActionsMenu.tsx | 3 ++- .../templates/article/SetLabelsControl.tsx | 11 ++++---- .../templates/article/SetLabelsModal.tsx | 5 ++-- .../templates/homeFeed/HomeFeedContainer.tsx | 25 +++++++++++++++++++ .../web/pages/[username]/[slug]/index.tsx | 3 ++- 7 files changed, 46 insertions(+), 9 deletions(-) diff --git a/packages/web/components/patterns/CardMenu.tsx b/packages/web/components/patterns/CardMenu.tsx index 3a7abb49b..a6bd0aa9a 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 @@ -63,6 +64,12 @@ export function CardMenu(props: CardMenuProps): JSX.Element { title="Mark Unread" /> )} + { + props.actionHandler('set-labels') + }} + title="Set Labels" + /> { props.actionHandler('delete') 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..d710a978f 100644 --- a/packages/web/components/templates/article/ArticleActionsMenu.tsx +++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx @@ -109,7 +109,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..afeafd47e 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' @@ -17,7 +16,8 @@ import { randomLabelColorHex } from '../../../utils/settings-page/labels/labelCo import { useRouter } from 'next/router' type SetLabelsControlProps = { - article: ArticleAttributes + linkId: string + labels: Label[] | undefined articleActionHandler: (action: string, arg?: unknown) => void } @@ -187,7 +187,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 +211,11 @@ 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 +// props.article.labels = result props.articleActionHandler('refreshLabels', result) revalidate() @@ -281,6 +281,7 @@ export function SetLabelsControl(props: SetLabelsControlProps): JSX.Element { } } } + event.preventDefault() }, [filterText, filteredLabels, focusedIndex, isSelected, selectedLabels, setSelectedLabels]) const createLabelFromFilterText = useCallback(async () => { diff --git a/packages/web/components/templates/article/SetLabelsModal.tsx b/packages/web/components/templates/article/SetLabelsModal.tsx index 81696600f..2977647be 100644 --- a/packages/web/components/templates/article/SetLabelsModal.tsx +++ b/packages/web/components/templates/article/SetLabelsModal.tsx @@ -1,4 +1,4 @@ -import { ArticleAttributes } from '../../../lib/networking/queries/useGetArticleQuery' +import { Label } from '../../../lib/networking/fragments/labelFragment' import { Button } from '../../elements/Button' import { CrossIcon } from '../../elements/images/CrossIcon' import { HStack, VStack } from '../../elements/LayoutPrimitives' @@ -12,7 +12,8 @@ import { theme } from '../../tokens/stitches.config' import { SetLabelsControl } from './SetLabelsControl' type SetLabelsModalProps = { - article: ArticleAttributes + linkId: string + labels: Label[] | undefined onOpenChange: (open: boolean) => void articleActionHandler: (action: string, arg?: unknown) => void } diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index b25ce4480..d14136dd8 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -35,6 +35,7 @@ import { useFetchMoreScroll } from '../../../lib/hooks/useFetchMoreScroll' import { usePersistedState } from '../../../lib/hooks/usePersistedState' import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers' import { ConfirmationModal } from '../../patterns/ConfirmationModal' +import { SetLabelsModal } from '../article/SetLabelsModal' export type LayoutType = 'LIST_LAYOUT' | 'GRID_LAYOUT' @@ -61,6 +62,10 @@ export function HomeFeedContainer(props: HomeFeedContainerProps): JSX.Element { undefined ) + const [labelsTarget, setLabelsTarget] = useState( + undefined + ) + const [showAddLinkModal, setShowAddLinkModal] = useState(false) const [queryInputs, setQueryInputs] = @@ -265,6 +270,10 @@ export function HomeFeedContainer(props: HomeFeedContainerProps): JSX.Element { case 'snooze': setSnoozeTarget(item) break + case 'set-labels': + console.log('setting labels target', item.node.id) + setLabelsTarget(item) + break } } @@ -408,6 +417,8 @@ export function HomeFeedContainer(props: HomeFeedContainerProps): JSX.Element { setShareTarget={setShareTarget} snoozeTarget={snoozeTarget} setSnoozeTarget={setSnoozeTarget} + labelsTarget={labelsTarget} + setLabelsTarget={setLabelsTarget} showAddLinkModal={showAddLinkModal} setShowAddLinkModal={setShowAddLinkModal} /> @@ -428,6 +439,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: ( @@ -706,6 +719,18 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { onOpenChange={() => setShowRemoveLinkConfirmation(false)} /> )} + {props.labelsTarget?.node.id && ( + { + + }} + onOpenChange={() => { + props.setLabelsTarget(undefined) + }} + /> + )} ) } diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx index 2e682da08..1898fb198 100644 --- a/packages/web/pages/[username]/[slug]/index.tsx +++ b/packages/web/pages/[username]/[slug]/index.tsx @@ -253,7 +253,8 @@ export default function Home(): JSX.Element { {showSetLabelsModal && ( setShowSetLabelsModal(false)} /> From 1afa24d12a8bb386427c343dd6cc2e83613e91bc Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 19 Apr 2022 11:41:29 -0700 Subject: [PATCH 2/8] Keyboard shortcut for editing labels from the library --- packages/web/components/patterns/CardMenu.tsx | 12 ++++++------ .../templates/article/SetLabelsControl.tsx | 1 - .../templates/homeFeed/HomeFeedContainer.tsx | 3 +++ .../web/lib/keyboardShortcuts/navigationShortcuts.ts | 7 +++++++ 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/web/components/patterns/CardMenu.tsx b/packages/web/components/patterns/CardMenu.tsx index a6bd0aa9a..13b2a60ef 100644 --- a/packages/web/components/patterns/CardMenu.tsx +++ b/packages/web/components/patterns/CardMenu.tsx @@ -35,6 +35,12 @@ export function CardMenu(props: CardMenuProps): JSX.Element { title="Unarchive" /> )} + { + props.actionHandler('set-labels') + }} + title="Set Labels" + /> {isVipUser(props.viewer) && ( { @@ -64,12 +70,6 @@ export function CardMenu(props: CardMenuProps): JSX.Element { title="Mark Unread" /> )} - { - props.actionHandler('set-labels') - }} - title="Set Labels" - /> { props.actionHandler('delete') diff --git a/packages/web/components/templates/article/SetLabelsControl.tsx b/packages/web/components/templates/article/SetLabelsControl.tsx index afeafd47e..db15c3583 100644 --- a/packages/web/components/templates/article/SetLabelsControl.tsx +++ b/packages/web/components/templates/article/SetLabelsControl.tsx @@ -281,7 +281,6 @@ export function SetLabelsControl(props: SetLabelsControlProps): JSX.Element { } } } - event.preventDefault() }, [filterText, filteredLabels, focusedIndex, isSelected, selectedLabels, setSelectedLabels]) const createLabelFromFilterText = useCallback(async () => { diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index d14136dd8..c711743b9 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -364,6 +364,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 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', From 0203347c4a587df4d3aa51c264569fe60597f44c Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 19 Apr 2022 11:59:48 -0700 Subject: [PATCH 3/8] Set tab index on the modals close button so it doesnt autofocus --- packages/web/components/templates/article/SetLabelsModal.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/web/components/templates/article/SetLabelsModal.tsx b/packages/web/components/templates/article/SetLabelsModal.tsx index 2977647be..7d32ff49e 100644 --- a/packages/web/components/templates/article/SetLabelsModal.tsx +++ b/packages/web/components/templates/article/SetLabelsModal.tsx @@ -42,6 +42,7 @@ export function SetLabelsModal(props: SetLabelsModalProps): JSX.Element { onClick={() => { props.onOpenChange(false) }} + tabIndex={-1} > Date: Tue, 19 Apr 2022 12:00:15 -0700 Subject: [PATCH 4/8] Refresh labels on the article when edited in the library --- .../templates/homeFeed/HomeFeedContainer.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index c711743b9..e2ca5f184 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -36,6 +36,7 @@ import { usePersistedState } from '../../../lib/hooks/usePersistedState' import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers' import { ConfirmationModal } from '../../patterns/ConfirmationModal' import { SetLabelsModal } from '../article/SetLabelsModal' +import { Label } from '../../../lib/networking/fragments/labelFragment' export type LayoutType = 'LIST_LAYOUT' | 'GRID_LAYOUT' @@ -726,8 +727,13 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { { - + articleActionHandler={(action, value) => { + switch(action) { + case 'refreshLabels': + if (props.labelsTarget) { + props.labelsTarget.node.labels = value as (Label[] | undefined) + } + } }} onOpenChange={() => { props.setLabelsTarget(undefined) From 51d723f3fb8fa6b92504a8772d44cc4b92fd007f Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 19 Apr 2022 14:58:04 -0700 Subject: [PATCH 5/8] Disable keyboard shorcuts when modals are open in the library --- .../components/templates/homeFeed/HomeFeedContainer.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index e2ca5f184..a2099bb2e 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -236,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) @@ -272,7 +278,6 @@ export function HomeFeedContainer(props: HomeFeedContainerProps): JSX.Element { setSnoozeTarget(item) break case 'set-labels': - console.log('setting labels target', item.node.id) setLabelsTarget(item) break } @@ -733,6 +738,7 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { if (props.labelsTarget) { props.labelsTarget.node.labels = value as (Label[] | undefined) } + break } }} onOpenChange={() => { From 5d88b054c8421ec9bb175834633db69da24cf31b Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 19 Apr 2022 15:36:12 -0700 Subject: [PATCH 6/8] Trigger a state change after updating labels --- .../web/components/templates/homeFeed/HomeFeedContainer.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index a2099bb2e..993776ba1 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -479,6 +479,8 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { [layout, setLayout] ) + const [, updateState] = useState({}) + const StyledToggleButton = styled('button', { p: '0px', backgroundColor: 'transparent', @@ -737,6 +739,7 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { case 'refreshLabels': if (props.labelsTarget) { props.labelsTarget.node.labels = value as (Label[] | undefined) + updateState({}) } break } From ab869a79bd5f0e8f36df599c9338f77fb049bd1f Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 19 Apr 2022 15:54:38 -0700 Subject: [PATCH 7/8] Mutate the labels on links when setting them so they are reflected in UI --- .../web/components/templates/article/ArticleActionsMenu.tsx | 1 + .../web/components/templates/article/SetLabelsControl.tsx | 6 +++++- .../web/components/templates/article/SetLabelsModal.tsx | 2 ++ packages/web/pages/[username]/[slug]/index.tsx | 1 + 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx index d710a978f..a5121d9c8 100644 --- a/packages/web/components/templates/article/ArticleActionsMenu.tsx +++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx @@ -109,6 +109,7 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element } > void } @@ -215,7 +217,9 @@ export function SetLabelsControl(props: SetLabelsControlProps): JSX.Element { 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 7d32ff49e..e6ee08795 100644 --- a/packages/web/components/templates/article/SetLabelsModal.tsx +++ b/packages/web/components/templates/article/SetLabelsModal.tsx @@ -1,4 +1,5 @@ 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' import { HStack, VStack } from '../../elements/LayoutPrimitives' @@ -14,6 +15,7 @@ import { SetLabelsControl } from './SetLabelsControl' type SetLabelsModalProps = { linkId: string labels: Label[] | undefined + article?: ArticleAttributes onOpenChange: (open: boolean) => void articleActionHandler: (action: string, arg?: unknown) => void } diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx index 1898fb198..eb5d6bd1d 100644 --- a/packages/web/pages/[username]/[slug]/index.tsx +++ b/packages/web/pages/[username]/[slug]/index.tsx @@ -253,6 +253,7 @@ export default function Home(): JSX.Element { {showSetLabelsModal && ( Date: Tue, 19 Apr 2022 16:13:22 -0700 Subject: [PATCH 8/8] Remove unused import --- packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx | 1 - 1 file changed, 1 deletion(-) 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'