mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Merge pull request #451 from omnivore-app/fix/labels-on-library
Allow editing labels from the library view
This commit is contained in:
commit
c99466c21e
9 changed files with 76 additions and 7 deletions
|
|
@ -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"
|
||||
/>
|
||||
)}
|
||||
<DropdownOption
|
||||
onSelect={() => {
|
||||
props.actionHandler('set-labels')
|
||||
}}
|
||||
title="Set Labels"
|
||||
/>
|
||||
{isVipUser(props.viewer) && (
|
||||
<DropdownOption
|
||||
onSelect={() => {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ export type LinkedItemCardAction =
|
|||
| 'mark-unread'
|
||||
| 'share'
|
||||
| 'snooze'
|
||||
| 'set-labels'
|
||||
|
||||
export type LinkedItemCardProps = {
|
||||
item: LibraryItemNode
|
||||
|
|
|
|||
|
|
@ -110,6 +110,8 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
|
|||
>
|
||||
<SetLabelsControl
|
||||
article={props.article}
|
||||
linkId={props.article.linkId}
|
||||
labels={props.article.labels}
|
||||
articleActionHandler={props.articleActionHandler}
|
||||
/>
|
||||
</ActionDropdown>
|
||||
|
|
|
|||
|
|
@ -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<Label[]>(props.article.labels || [])
|
||||
const [selectedLabels, setSelectedLabels] = useState<Label[]>(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()
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
>
|
||||
<CrossIcon
|
||||
size={14}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ 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'
|
||||
import { Label } from '../../../lib/networking/fragments/labelFragment'
|
||||
|
||||
export type LayoutType = 'LIST_LAYOUT' | 'GRID_LAYOUT'
|
||||
|
||||
|
|
@ -61,6 +63,10 @@ export function HomeFeedContainer(props: HomeFeedContainerProps): JSX.Element {
|
|||
undefined
|
||||
)
|
||||
|
||||
const [labelsTarget, setLabelsTarget] = useState<LibraryItem | undefined>(
|
||||
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 && (
|
||||
<SetLabelsModal
|
||||
linkId={props.labelsTarget.node.id}
|
||||
labels={props.labelsTarget.node.labels}
|
||||
articleActionHandler={(action, value) => {
|
||||
switch(action) {
|
||||
case 'refreshLabels':
|
||||
if (props.labelsTarget) {
|
||||
props.labelsTarget.node.labels = value as (Label[] | undefined)
|
||||
updateState({})
|
||||
}
|
||||
break
|
||||
}
|
||||
}}
|
||||
onOpenChange={() => {
|
||||
props.setLabelsTarget(undefined)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -254,6 +254,8 @@ export default function Home(): JSX.Element {
|
|||
{showSetLabelsModal && (
|
||||
<SetLabelsModal
|
||||
article={article}
|
||||
linkId={article.id}
|
||||
labels={article.labels}
|
||||
articleActionHandler={actionHandler}
|
||||
onOpenChange={() => setShowSetLabelsModal(false)}
|
||||
/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue