Files
omnivore/packages/web/components/templates/homeFeed/EditItemModals.tsx
2024-08-12 11:09:56 +08:00

350 lines
10 KiB
TypeScript

import dayjs, { Dayjs } from 'dayjs'
import { useCallback, useState } from 'react'
import { updatePageMutation } from '../../../lib/networking/mutations/updatePageMutation'
import { ArticleAttributes } from '../../../lib/networking/library_items/useLibraryItems'
import { LibraryItem } from '../../../lib/networking/library_items/useLibraryItems'
import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers'
import { CloseButton } from '../../elements/CloseButton'
import { FormInput } from '../../elements/FormElements'
import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
import {
ModalButtonBar,
ModalContent,
ModalOverlay,
ModalRoot,
} from '../../elements/ModalPrimitives'
import { StyledText } from '../../elements/StyledText'
import { StyledTextArea } from '../../elements/StyledTextArea'
type EditLibraryItemModalProps = {
onOpenChange: (open: boolean) => void
item: LibraryItem
updateItem: (item: LibraryItem) => Promise<void>
}
export function EditLibraryItemModal(
props: EditLibraryItemModalProps
): JSX.Element {
const onSave = useCallback(
(
title: string,
author: string | undefined,
description: string,
savedAt: Dayjs,
publishedAt: Dayjs | undefined
) => {
;(async () => {
if (title !== '') {
const res = await updatePageMutation({
pageId: props.item.node.id,
title,
description,
byline: author,
savedAt: savedAt.toISOString(),
publishedAt: publishedAt ? publishedAt.toISOString() : undefined,
})
if (res) {
await props.updateItem({
cursor: props.item.cursor,
node: {
...props.item.node,
title: title,
author: author,
description: description,
},
})
showSuccessToast('Link updated succesfully', {
position: 'bottom-right',
})
props.onOpenChange(false)
} else {
showErrorToast('There was an error updating your link', {
position: 'bottom-right',
})
}
} else {
showErrorToast('Title must be a non-empty value', {
position: 'bottom-right',
})
}
})()
},
[props]
)
return (
<EditItemModal
title={props.item.node.title}
author={props.item.node.author}
description={props.item.node.description}
savedAt={dayjs(props.item.node.savedAt)}
publishedAt={
props.item.node.publishedAt
? dayjs(props.item.node.publishedAt)
: undefined
}
onOpenChange={props.onOpenChange}
onSave={onSave}
/>
)
}
type EditArticleModalProps = {
onOpenChange: (open: boolean) => void
article: ArticleAttributes
updateArticle: (
title: string,
author: string | undefined,
description: string,
savedAt: string,
publishedAt: string | undefined
) => void
}
export function EditArticleModal(props: EditArticleModalProps): JSX.Element {
const onSave = useCallback(
(
title: string,
author: string | undefined,
description: string,
savedAt: Dayjs,
publishedAt: Dayjs | undefined
) => {
;(async () => {
if (title !== '') {
const res = await updatePageMutation({
pageId: props.article.id,
title,
description,
byline: author,
savedAt: savedAt.toISOString(),
publishedAt: publishedAt ? publishedAt.toISOString() : undefined,
})
if (res) {
props.updateArticle(
title,
author,
description,
savedAt.toISOString(),
publishedAt ? publishedAt.toISOString() : undefined
)
showSuccessToast('Link updated succesfully', {
position: 'bottom-right',
})
props.onOpenChange(false)
} else {
showErrorToast('There was an error updating your link', {
position: 'bottom-right',
})
}
} else {
showErrorToast('Title must be a non-empty value', {
position: 'bottom-right',
})
}
})()
},
[props]
)
return (
<EditItemModal
title={props.article.title}
author={props.article.author}
description={props.article.description || ''}
savedAt={dayjs(props.article.savedAt)}
publishedAt={
props.article.publishedAt ? dayjs(props.article.publishedAt) : undefined
}
onOpenChange={props.onOpenChange}
onSave={onSave}
/>
)
}
type EditItemModalProps = {
title: string
author: string | undefined
description: string
savedAt: Dayjs
publishedAt: Dayjs | undefined
onOpenChange: (open: boolean) => void
onSave: (
title: string,
author: string | undefined,
description: string,
savedAt: Dayjs,
publishedAt: Dayjs | undefined
) => void
}
function EditItemModal(props: EditItemModalProps): JSX.Element {
const [title, setTitle] = useState(props.title)
const [author, setAuthor] = useState(props.author)
const [savedAt, setSavedAt] = useState(props.savedAt)
const [publishedAt, setPublishedAt] = useState(props.publishedAt)
const [description, setDescription] = useState(props.description)
const titleStyle = {
mt: '22px',
mb: '2px',
fontFamily: '$display',
fontWeight: '600',
fontSize: '11px',
color: '#898989',
}
const inputStyle = {
mt: '1px',
borderRadius: '5px',
border: '1px solid $thBorderColor',
fontFamily: 'Inter',
fontWeight: '500',
fontSize: '16px',
height: '38px',
p: '5px',
color: '$thTextContrast2',
'&:focus': {
outline: 'none !important',
border: '1px solid $omnivoreCtaYellow',
},
}
return (
<ModalRoot
defaultOpen
modal={true}
onOpenChange={() => {
props.onOpenChange(false)
}}
css={{}}
>
<ModalOverlay />
<ModalContent
css={{ bg: '$grayBg', p: '20px', maxWidth: '480px' }}
onInteractOutside={(event) => {
event.preventDefault()
}}
onEscapeKeyDown={(event) => {
props.onOpenChange(false)
event.preventDefault()
event.stopPropagation()
}}
>
<VStack distribution="start" css={{ p: '0px' }}>
<Header onOpenChange={props.onOpenChange} />
<Box css={{ width: '100%' }}>
<form
onSubmit={(event) => {
event.preventDefault()
props.onSave(title, author, description, savedAt, publishedAt)
}}
>
<HStack distribution="start" css={{ width: '100%' }}>
<VStack css={{ width: '45%' }}>
<StyledText css={titleStyle}>SAVED AT</StyledText>
<FormInput
type="datetime-local"
value={savedAt.format('YYYY-MM-DDTHH:mm')}
placeholder="Edit Date"
onChange={(event) => {
const dateStr = event.target.value
setSavedAt(dayjs(dateStr))
}}
css={{
...inputStyle,
fontSize: '14px',
textIndent: '0px',
}}
/>
</VStack>
<VStack css={{ width: '45%', marginLeft: 'auto' }}>
<StyledText css={titleStyle}>PUBLISHED AT</StyledText>
<FormInput
type="datetime-local"
value={
publishedAt
? publishedAt.format('YYYY-MM-DDTHH:mm')
: undefined
}
placeholder="Edit Published Date"
onChange={(event) => {
const dateStr = event.target.value
setPublishedAt(dayjs(dateStr))
}}
css={{
...inputStyle,
fontSize: '14px',
textIndent: '0px',
}}
/>
</VStack>
</HStack>
<StyledText css={titleStyle}>TITLE</StyledText>
<FormInput
type="text"
value={title}
autoFocus
placeholder="Edit Title"
onChange={(event) => setTitle(event.target.value)}
onFocus={(event) => {
event.target.select()
}}
css={inputStyle}
/>
<StyledText css={titleStyle}>AUTHOR</StyledText>
<FormInput
type="author"
value={author}
placeholder="Edit Author"
onChange={(event) => setAuthor(event.target.value)}
onFocus={(event) => {
event.target.select()
}}
css={inputStyle}
/>
<StyledText css={titleStyle}>DESCRIPTION</StyledText>
<StyledTextArea
css={{
...inputStyle,
mt: '2px',
width: '100%',
height: '120px',
}}
placeholder="Edit Description"
value={description}
onChange={(event) => setDescription(event.target.value)}
onFocus={(event) => {
event.target.select()
}}
maxLength={4000}
/>
<ModalButtonBar
onOpenChange={props.onOpenChange}
acceptButtonLabel="Save Changes"
/>
</form>
</Box>
</VStack>
</ModalContent>
</ModalRoot>
)
}
type HeaderProps = {
onOpenChange: (open: boolean) => void
}
function Header(props: HeaderProps): JSX.Element {
return (
<HStack distribution="start" alignment="center" css={{ width: '100%' }}>
<StyledText style="modalHeadline">Edit Title & Description</StyledText>
<SpanBox css={{ marginLeft: 'auto' }}>
<CloseButton close={() => props.onOpenChange(false)} />
</SpanBox>
</HStack>
)
}