mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
add loading and set labels ui
This commit is contained in:
parent
276205d52a
commit
6482d280f7
5 changed files with 94 additions and 48 deletions
|
|
@ -1,9 +1,9 @@
|
|||
import {
|
||||
ModalRoot,
|
||||
ModalContent,
|
||||
ModalOverlay,
|
||||
ModalRoot,
|
||||
} from '../../elements/ModalPrimitives'
|
||||
import { HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
|
||||
import { HStack, VStack } from '../../elements/LayoutPrimitives'
|
||||
import { Button } from '../../elements/Button'
|
||||
import { StyledText } from '../../elements/StyledText'
|
||||
import { CrossIcon } from '../../elements/images/CrossIcon'
|
||||
|
|
@ -15,36 +15,33 @@ import { ArticleAttributes } from '../../../lib/networking/queries/useGetArticle
|
|||
import { setLabelsMutation } from '../../../lib/networking/mutations/setLabelsMutation'
|
||||
|
||||
type EditLabelsModalProps = {
|
||||
labels: string[]
|
||||
article: ArticleAttributes
|
||||
onOpenChange: (open: boolean) => void
|
||||
}
|
||||
|
||||
export function EditLabelsModal(
|
||||
props: EditLabelsModalProps
|
||||
): JSX.Element {
|
||||
const [selectedLabels, setSelectedLabels] = useState(props.labels)
|
||||
export function EditLabelsModal(props: EditLabelsModalProps): JSX.Element {
|
||||
const [selectedLabels, setSelectedLabels] = useState(
|
||||
props.article.labels?.map((l) => l.id) || []
|
||||
)
|
||||
const { labels, revalidate, isValidating } = useGetLabelsQuery()
|
||||
|
||||
const saveAndExit = useCallback(async () => {
|
||||
if (selectedLabels.length > 0) {
|
||||
const result = await setLabelsMutation(
|
||||
props.article.id,
|
||||
selectedLabels,
|
||||
)
|
||||
console.log('result of setting labels', result)
|
||||
}
|
||||
const result = await setLabelsMutation(props.article.linkId, selectedLabels)
|
||||
console.log('result of setting labels', result)
|
||||
props.onOpenChange(false)
|
||||
}, [selectedLabels, props.onOpenChange])
|
||||
|
||||
const handleChange = useCallback((event: ChangeEvent<HTMLInputElement>) => {
|
||||
const label = event.target.value
|
||||
if (event.target.checked) {
|
||||
setSelectedLabels([...selectedLabels, label])
|
||||
} else {
|
||||
setSelectedLabels(selectedLabels.filter((l) => l !== label))
|
||||
}
|
||||
}, [selectedLabels, setSelectedLabels])
|
||||
const handleChange = useCallback(
|
||||
(event: ChangeEvent<HTMLInputElement>) => {
|
||||
const label = event.target.value
|
||||
if (event.target.checked) {
|
||||
setSelectedLabels([...selectedLabels, label])
|
||||
} else {
|
||||
setSelectedLabels(selectedLabels.filter((l) => l !== label))
|
||||
}
|
||||
},
|
||||
[selectedLabels, setSelectedLabels]
|
||||
)
|
||||
|
||||
return (
|
||||
<ModalRoot defaultOpen onOpenChange={saveAndExit}>
|
||||
|
|
@ -61,7 +58,9 @@ export function EditLabelsModal(
|
|||
alignment="center"
|
||||
css={{ width: '100%' }}
|
||||
>
|
||||
<StyledText style="modalHeadline" css={{ p: '16px' }}>Edit Labels</StyledText>
|
||||
<StyledText style="modalHeadline" css={{ p: '16px' }}>
|
||||
Edit Labels
|
||||
</StyledText>
|
||||
<Button
|
||||
css={{ pt: '16px', pr: '16px' }}
|
||||
style="ghost"
|
||||
|
|
@ -75,25 +74,34 @@ export function EditLabelsModal(
|
|||
/>
|
||||
</Button>
|
||||
</HStack>
|
||||
{labels && labels.map((label) => (
|
||||
<HStack key={label.id} css={{ height: '50px', verticalAlign: 'middle' }} onClick={() => {
|
||||
if (selectedLabels.includes(label.id)) {
|
||||
setSelectedLabels(selectedLabels.filter((id) => id !== label.id))
|
||||
} else {
|
||||
setSelectedLabels([...selectedLabels, label.id])
|
||||
}
|
||||
}}>
|
||||
<Label color={label.color} text={label.name} />
|
||||
<input
|
||||
type="checkbox"
|
||||
value={label.id}
|
||||
onChange={handleChange}
|
||||
checked={selectedLabels.includes(label.id)}
|
||||
/>
|
||||
</HStack>
|
||||
))}
|
||||
{labels &&
|
||||
labels.map((label) => (
|
||||
<HStack
|
||||
key={label.id}
|
||||
css={{ height: '50px', verticalAlign: 'middle' }}
|
||||
onClick={() => {
|
||||
if (selectedLabels.includes(label.id)) {
|
||||
setSelectedLabels(
|
||||
selectedLabels.filter((id) => id !== label.id)
|
||||
)
|
||||
} else {
|
||||
setSelectedLabels([...selectedLabels, label.id])
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Label color={label.color} text={label.name} />
|
||||
<input
|
||||
type="checkbox"
|
||||
value={label.id}
|
||||
onChange={handleChange}
|
||||
checked={selectedLabels.includes(label.id)}
|
||||
/>
|
||||
</HStack>
|
||||
))}
|
||||
<HStack css={{ width: '100%', mb: '16px' }} alignment="center">
|
||||
<Button style="ctaDarkYellow" onClick={saveAndExit}>Save</Button>
|
||||
<Button style="ctaDarkYellow" onClick={saveAndExit}>
|
||||
Save
|
||||
</Button>
|
||||
</HStack>
|
||||
</VStack>
|
||||
</ModalContent>
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ export const articleFragment = gql`
|
|||
slug
|
||||
isArchived
|
||||
description
|
||||
linkId
|
||||
}
|
||||
`
|
||||
|
||||
|
|
@ -38,4 +39,5 @@ export type ArticleFragmentData = {
|
|||
slug: string
|
||||
isArchived: boolean
|
||||
description: string
|
||||
linkId?: string
|
||||
}
|
||||
|
|
|
|||
18
packages/web/lib/networking/fragments/labelFragment.ts
Normal file
18
packages/web/lib/networking/fragments/labelFragment.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import { gql } from 'graphql-request'
|
||||
|
||||
export const labelFragment = gql`
|
||||
fragment LabelFields on Label {
|
||||
id
|
||||
name
|
||||
color
|
||||
description
|
||||
}
|
||||
`
|
||||
|
||||
export type Label = {
|
||||
id: string
|
||||
name: string
|
||||
color: string
|
||||
description?: string
|
||||
createdAt: string
|
||||
}
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
import { gql } from 'graphql-request'
|
||||
import useSWRImmutable, { useSWRConfig } from 'swr'
|
||||
import useSWRImmutable from 'swr'
|
||||
import { makeGqlFetcher, RequestContext, ssrFetcher } from '../networkHelpers'
|
||||
import { articleFragment, ContentReader } from '../fragments/articleFragment'
|
||||
import { highlightFragment, Highlight } from '../fragments/highlightFragment'
|
||||
import { Highlight, highlightFragment } from '../fragments/highlightFragment'
|
||||
import { ScopedMutator } from 'swr/dist/types'
|
||||
import { Label, labelFragment } from '../fragments/labelFragment'
|
||||
|
||||
type ArticleQueryInput = {
|
||||
username?: string
|
||||
|
|
@ -46,6 +47,8 @@ export type ArticleAttributes = {
|
|||
content: string
|
||||
shareInfo?: ArticleShareInfo
|
||||
highlights: Highlight[]
|
||||
linkId: string
|
||||
labels?: Label[]
|
||||
}
|
||||
|
||||
type ArticleShareInfo = {
|
||||
|
|
@ -73,6 +76,9 @@ const query = gql`
|
|||
highlights(input: { includeFriends: $includeFriendsHighlights }) {
|
||||
...HighlightFields
|
||||
}
|
||||
labels {
|
||||
...LabelFields
|
||||
}
|
||||
}
|
||||
}
|
||||
... on ArticleError {
|
||||
|
|
@ -82,10 +88,16 @@ const query = gql`
|
|||
}
|
||||
${articleFragment}
|
||||
${highlightFragment}
|
||||
${labelFragment}
|
||||
`
|
||||
export const cacheArticle = (mutate: ScopedMutator, username: string, article: ArticleAttributes, includeFriendsHighlights = false) => {
|
||||
export const cacheArticle = (
|
||||
mutate: ScopedMutator,
|
||||
username: string,
|
||||
article: ArticleAttributes,
|
||||
includeFriendsHighlights = false
|
||||
) => {
|
||||
mutate([query, username, article.slug, includeFriendsHighlights], {
|
||||
article: { article: {...article, cached: true} }
|
||||
article: { article: { ...article, cached: true } },
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -134,4 +146,3 @@ export async function articleQuery(
|
|||
|
||||
return Promise.reject()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import { gql } from 'graphql-request'
|
||||
import useSWRInfinite from 'swr/infinite'
|
||||
import { gqlFetcher } from '../networkHelpers'
|
||||
import { articleFragment } from '../fragments/articleFragment'
|
||||
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'
|
||||
import { labelFragment } from '../fragments/labelFragment'
|
||||
|
||||
export type LibraryItemsQueryInput = {
|
||||
limit: number
|
||||
|
|
@ -89,6 +90,9 @@ export function useGetLibraryItemsQuery({
|
|||
cursor
|
||||
node {
|
||||
...ArticleFields
|
||||
labels {
|
||||
...LabelFields
|
||||
}
|
||||
originalArticleUrl
|
||||
}
|
||||
}
|
||||
|
|
@ -106,6 +110,7 @@ export function useGetLibraryItemsQuery({
|
|||
}
|
||||
}
|
||||
${articleFragment}
|
||||
${labelFragment}
|
||||
`
|
||||
|
||||
const variables = {
|
||||
|
|
@ -132,7 +137,9 @@ export function useGetLibraryItemsQuery({
|
|||
limit,
|
||||
sortDescending,
|
||||
searchQuery,
|
||||
pageIndex === 0 ? undefined : previousResult.articles.pageInfo.endCursor,
|
||||
pageIndex === 0
|
||||
? undefined
|
||||
: previousResult.articles.pageInfo.endCursor,
|
||||
]
|
||||
},
|
||||
(query, _l, _s, _sq, cursor: string) => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue