Files
2026-06-24 15:34:09 +02:00

105 lines
4.0 KiB
TypeScript

'use client'
import { DeletePostDocument, DownvoteDocument, MeDocument, PostDocument, Post as PostType, RemoveDownvoteDocument, RemoveUpvoteDocument, UpvoteDocument } from '@/generated/graphql/graphql'
import { useMutation, useQuery } from '@apollo/client/react'
import { ArrowDownIcon, ArrowUpIcon, DeleteIcon, EditIcon } from '@chakra-ui/icons'
import { Box, Flex, Heading, IconButton, Text } from '@chakra-ui/react'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
interface Props {
post: PostType
}
export const Post: React.FC<Props> = ({ post: p }) => {
const { data } = useQuery(MeDocument)
const router = useRouter()
const { refetch } = useQuery(PostDocument, { returnPartialData: false, variables: { id: p.id } })
const [upvote, { loading: upvoting }] = useMutation(UpvoteDocument)
const [downvote, { loading: downvoting }] = useMutation(DownvoteDocument)
const [removeUpvote, { loading: removingUpvote }] = useMutation(RemoveUpvoteDocument)
const [removeDownvote, { loading: removingDownvote }] = useMutation(RemoveDownvoteDocument)
const [deletePost, { loading: deletingPost }] = useMutation(DeletePostDocument)
return (
<Flex key={p.id} p={5} borderWidth='1px'>
<Flex flexDir='column' mr='4' justifyContent='space-between' alignItems='center'>
<IconButton
aria-label='upvote-button'
icon={<ArrowUpIcon />}
color={p.upvoted ? 'green' : ''}
isLoading={upvoting || removingUpvote}
onClick={async () => {
if (!data?.me) {
router.push('/login')
}
if (p.upvoted) {
await removeUpvote({ variables: { postID: p.id } })
// Apollo Client automatically refetches one single post and merge it with the cache of previous posts and updating the result of useQuery(PostsDocument), so we don't need to refetch every post.
await refetch()
}
else {
await upvote({ variables: { postID: p.id } })
await refetch()
}
}}
/>
<Text marginY='2'>{p.points}</Text>
<IconButton
aria-label='downvote-button'
icon={<ArrowDownIcon />}
color={p.downvoted ? 'red' : ''}
isLoading={downvoting || removingDownvote}
onClick={async () => {
if (!data?.me) {
router.push('/login')
}
if (p.downvoted) {
await removeDownvote({ variables: { postID: p.id } })
await refetch()
}
else {
await downvote({ variables: { postID: p.id } })
await refetch()
}
}}
/>
</Flex>
<Box>
<Link href={`/post/${p.id}`}>
<Heading as='h3' size='md'>{p.title}</Heading>
</Link>
<Text>Posted by {p.author.username}</Text>
<Text mt={4}>{p.snippet}</Text>
</Box>
{
p.authorID == data?.me?.id &&
<Flex flexDir='column' ml='auto' justifyContent='space-between' alignItems='center'>
<Link href={`/edit-post/${p.id}`}>
<IconButton
aria-label='edit-button'
icon={<EditIcon />}
/>
</Link>
<IconButton
aria-label='delete-button'
icon={<DeleteIcon />}
colorScheme='red'
isLoading={deletingPost}
onClick={async () => {
await deletePost({
variables: { id: p.id },
// https://stackoverflow.com/questions/63192774/apollo-client-delete-item-from-cache
// https://www.apollographql.com/docs/react/caching/garbage-collection/#cacheevict
update: cache => {
const normalizedId = cache.identify({ id: p.id, __typename: 'Post' })
cache.evict({ id: normalizedId })
cache.gc()
}
})
}}
/>
</Flex>
}
</Flex>
)
}