Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

style: posts #1496

Merged
merged 2 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 0 additions & 90 deletions src/components/LikeButton.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion src/components/copy-resource.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const CopyToClipboard: FunctionComponent<
<button
type="button"
onClick={handleCopyToClipboard}
className={`group flex text-sm items-center space-x-1 rounded-md p-2 bg-gray-50 dark:bg-gray-800 text-black dark:text-white dark:hover:bg-gray-700 hover:bg-blue-100 hover:text-blue-600 transition-colors ease-in-out duration-300 ${className}`}
className={`group flex text-sm items-center space-x-1 rounded-md p-2 bg-gray-50 dark:bg-gray-800 text-black dark:text-white dark:hover:bg-gray-700 hover:bg-blue-100 dark:hover:text-blue-500 hover:text-blue-600 transition-colors ease-in-out duration-300 ${className}`}
aria-label="Copy link"
>
<IconLink className="w-5" />
Expand Down
138 changes: 138 additions & 0 deletions src/components/like-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import {trpc} from '@/app/_trpc/client'
import {useViewer} from '@/context/viewer-context'
import Spinner from '@/spinner'
import {cn} from '@/ui/utils'
import {useEffect, useState} from 'react'

interface LikeButtonProps {
postId: number
className?: string
}

export function LikeButton({postId, className}: LikeButtonProps) {
const utils = trpc.useUtils()
const [mounted, setMounted] = useState(false)
const [optimisticLiked, setOptimisticLiked] = useState(false)
const [optimisticCount, setOptimisticCount] = useState<number | null>(null)
const {authenticated, loading: viewerLoading} = useViewer()

const {
data: likeCount,
isLoading: countLoading,
status: countStatus,
} = trpc.likes.getLikesForPost.useQuery({postId})
const {
data: hasLiked,
isLoading: likedLoading,
status: likedStatus,
} = trpc.likes.hasUserLikedPost.useQuery({postId})

const {
mutate: toggleLike,
isLoading: mutationLoading,
isIdle,
} = trpc.likes.toggleLike.useMutation({
onMutate: () => {
setOptimisticLiked(!optimisticLiked)
setOptimisticCount((prev) =>
prev !== null ? (optimisticLiked ? prev - 1 : prev + 1) : null,
)
},
onSettled: () => {
utils.likes.getLikesForPost.invalidate({postId})
utils.likes.hasUserLikedPost.invalidate({postId})
},
onError: () => {
setOptimisticLiked(!optimisticLiked)
setOptimisticCount((prev) =>
prev !== null ? (optimisticLiked ? prev + 1 : prev - 1) : null,
)
},
})

useEffect(() => {
setMounted(true)
}, [])

useEffect(() => {
if (hasLiked !== undefined) setOptimisticLiked(hasLiked)
}, [hasLiked])

useEffect(() => {
if (likeCount !== undefined) setOptimisticCount(likeCount)
}, [likeCount])

const isLoading =
countStatus !== 'success' || likedStatus !== 'success' || viewerLoading
const isLiked = mounted && optimisticLiked

return (
<button
onClick={() => !viewerLoading && toggleLike({postId})}
disabled={isLoading}
className={cn(
'flex items-center gap-1.5 text-sm transition duration-300 ease-in-out dark:shadow-md px-3 py-2 rounded bg-gradient-to-tr dark:from-gray-800 dark:to-gray-700 from-yellow-300/20 border-pink-400/5 relative border dark:border-0 to-amber-300/20 hover:bg-amber-300/30',
{
'': isLiked,
'dark:text-gray-200 hover:dark:text-white': !isLiked,
'animate-pulse opacity-50': isLoading,
},
className,
)}
aria-label={optimisticLiked ? 'Unlike post' : 'Like post'}
>
{isLiked ? (
<svg
className={cn('h-4 w-4', {
'animate-pulse': isLoading,
})}
xmlns="http://www.w3.org/2000/svg"
width="12"
height="12"
viewBox="0 0 12 12"
aria-label="Unlike post"
>
<g fill="currentColor">
<path
d="M9.956,4.5H6.731v-3A1.314,1.314,0,0,0,5.634,0a1.3,1.3,0,0,0-.4,0,.5.5,0,0,0-.45.375L3,6v6H9.431a2.009,2.009,0,0,0,1.95-1.725l.6-3.45a1.9,1.9,0,0,0-.45-1.575A1.883,1.883,0,0,0,9.956,4.5Z"
fill="currentColor"
/>
<path d="M.5,6H2v6H.5a.5.5,0,0,1-.5-.5v-5A.5.5,0,0,1,.5,6Z" />
</g>
</svg>
) : (
<svg
className={cn('h-4 w-4', {
'animate-pulse': isLoading,
})}
xmlns="http://www.w3.org/2000/svg"
width="12"
height="12"
viewBox="0 0 12 12"
aria-label="Like post"
>
<g
strokeWidth="1"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
>
<polyline
points="2.5 11.5 0.5 11.5 0.5 6.5 2.5 6.5"
stroke="currentColor"
/>
<path d="M2.5,6.5l2-6H5A1.5,1.5,0,0,1,6.5,2V4.5H9.77a1.5,1.5,0,0,1,1.485,1.712l-.571,4A1.5,1.5,0,0,1,9.2,11.5H2.5Z" />
</g>
</svg>
)}
{optimisticCount !== null ? (
<span className="min-w-3 text-center tabular-nums">
{optimisticCount}
</span>
) : (
<Spinner className="w-3" />
)}
</button>
)
}
4 changes: 2 additions & 2 deletions src/components/share-bluesky.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const BlueskyLink: FunctionComponent<React.PropsWithChildren<any>> = ({
}
return get(resource, 'title') && get(resource, 'path') ? (
<a
className={`group flex text-sm items-center space-x-1 rounded-md p-2 bg-gray-50 dark:bg-gray-800 text-black dark:text-white dark:hover:bg-gray-700 hover:bg-blue-100 hover:text-blue-600 transition-colors ease-in-out duration-300 ${className}`}
className={`group flex text-sm items-center space-x-1 rounded-md p-2 bg-gray-50 dark:bg-gray-800 text-black dark:text-white dark:hover:bg-gray-700 hover:bg-blue-100 dark:hover:text-blue-500 hover:text-blue-600 transition-colors ease-in-out duration-300 ${className}`}
target="_blank"
rel="noopener noreferrer"
href={encodeBlueskyUrl()}
Expand All @@ -42,7 +42,7 @@ export const IconBluesky: FunctionComponent<
version="1.1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 64 57"
className={`w-5 transition-colors group-hover:text-[#1185FE] ${className}`}
className={`w-5 ${className}`}
>
<path
fill="currentColor"
Expand Down
4 changes: 2 additions & 2 deletions src/components/tweet-resource.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const TweetResource: FunctionComponent<React.PropsWithChildren<any>> = ({
return get(resource, 'title') && get(resource, 'path') ? (
<a
className={twMerge(
`group flex text-sm items-center space-x-1 rounded-md p-2 bg-gray-50 dark:bg-gray-800 text-black dark:text-white dark:hover:bg-gray-700 hover:bg-blue-100 hover:text-blue-600 transition-colors ease-in-out duration-300`,
`group flex text-sm items-center space-x-1 rounded-md p-2 bg-gray-50 dark:bg-gray-800 text-black dark:text-white dark:hover:bg-gray-700 dark:hover:text-blue-500 hover:bg-blue-100 hover:text-blue-600 transition-colors ease-in-out duration-300`,
className,
)}
target="_blank"
Expand All @@ -46,7 +46,7 @@ export const IconTwitter: FunctionComponent<
<path
className="fill-current"
d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"
></path>
/>
</g>
</svg>
)
Expand Down
Loading
Loading