mirror of
https://github.com/r-freeman/portfolio.git
synced 2025-05-15 21:20:19 +00:00
Update comment component
All checks were successful
Build And Publish / BuildAndPublish (push) Successful in 3m9s
All checks were successful
Build And Publish / BuildAndPublish (push) Successful in 3m9s
This commit is contained in:
parent
90a4bce926
commit
ed5f5a8256
@ -8,22 +8,19 @@ import clsx from 'clsx'
|
|||||||
export function Code({children}: { children: ReactNode }) {
|
export function Code({children}: { children: ReactNode }) {
|
||||||
const [copied, setCopied] = useState<boolean>(false)
|
const [copied, setCopied] = useState<boolean>(false)
|
||||||
const preRef = useRef<HTMLPreElement>(null)
|
const preRef = useRef<HTMLPreElement>(null)
|
||||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
|
||||||
|
|
||||||
const handleCopy = async (e: React.MouseEvent<HTMLButtonElement>) => {
|
const handleCopy = async (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
await navigator.clipboard.writeText(preRef.current?.innerText ?? '')
|
await navigator.clipboard.writeText(preRef.current?.innerText ?? '')
|
||||||
setCopied(true)
|
setCopied(true)
|
||||||
setTimeout(() => setCopied(false), 1000)
|
setTimeout(() => setCopied(false), 1000)
|
||||||
buttonRef.current?.blur()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<pre className="relative group" ref={preRef}>
|
<pre className="relative group" ref={preRef}>
|
||||||
<button
|
<button
|
||||||
className="absolute top-0 right-0 m-5 text-zinc-400 hover:text-zinc-50"
|
className="absolute top-0 right-0 m-5 text-zinc-400"
|
||||||
onClick={handleCopy} aria-label={`${!copied ? 'Copy this code' : 'Copied!'}`}
|
onClick={handleCopy} aria-label={`${!copied ? 'Copy this code' : 'Copied!'}`}>
|
||||||
ref={buttonRef}>
|
|
||||||
<div className="relative size-6">
|
<div className="relative size-6">
|
||||||
<CheckIcon
|
<CheckIcon
|
||||||
className={clsx('absolute text-green-500 ease-in transform transition', !copied ? 'scale-0' : 'scale-100')}/>
|
className={clsx('absolute text-green-500 ease-in transform transition', !copied ? 'scale-0' : 'scale-100')}/>
|
||||||
|
@ -27,9 +27,6 @@ Comments.ReplyButton = function ReplyButton({comment}: ReplyButton) {
|
|||||||
|
|
||||||
const handleReplyButton = async (e: React.MouseEvent<HTMLButtonElement>) => {
|
const handleReplyButton = async (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (!session) {
|
|
||||||
await loginWithGitHub()
|
|
||||||
}
|
|
||||||
commentFormContext?.setCommentLength(0)
|
commentFormContext?.setCommentLength(0)
|
||||||
commentFormContext?.commentFormRef?.current?.form?.reset()
|
commentFormContext?.commentFormRef?.current?.form?.reset()
|
||||||
commentFormContext?.setReplyTo(comment);
|
commentFormContext?.setReplyTo(comment);
|
||||||
@ -52,6 +49,8 @@ Comments.Comment = function Comment({comment, isReply = false, className}: {
|
|||||||
isReply?: boolean
|
isReply?: boolean
|
||||||
className?: string
|
className?: string
|
||||||
}) {
|
}) {
|
||||||
|
const {data: session} = useSession()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<article
|
<article
|
||||||
@ -70,7 +69,9 @@ Comments.Comment = function Comment({comment, isReply = false, className}: {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-1 text-sm text-zinc-600 dark:text-zinc-400 max-w-xl">{comment.content}</p>
|
<p className="mt-1 text-sm text-zinc-600 dark:text-zinc-400 max-w-xl">{comment.content}</p>
|
||||||
|
{session &&
|
||||||
<Comments.ReplyButton comment={comment}/>
|
<Comments.ReplyButton comment={comment}/>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</>
|
</>
|
||||||
@ -82,12 +83,16 @@ type CommentsListProps = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Comments.List = function List({comments}: CommentsListProps) {
|
Comments.List = function List({comments}: CommentsListProps) {
|
||||||
|
const commentCount: number = comments.reduce((acc, comment) => {
|
||||||
|
if (!comment) return acc
|
||||||
|
const replyCount = comment.replies?.length || 0
|
||||||
|
return (acc + 1) + replyCount
|
||||||
|
}, 0)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
{comments.length > 0 &&
|
|
||||||
<section>
|
<section>
|
||||||
<h3 className="text-base font-semibold tracking-tight text-zinc-800 dark:text-zinc-100 mb-4">
|
<h3 className="text-base font-semibold tracking-tight text-zinc-800 dark:text-zinc-100 mb-4">
|
||||||
Comments
|
{commentCount > 0 ? `${commentCount} comment${commentCount > 1 ? 's' : ''}` : 'No comments'}
|
||||||
</h3>
|
</h3>
|
||||||
{comments.map((comment) => (
|
{comments.map((comment) => (
|
||||||
<React.Fragment key={comment.id}>
|
<React.Fragment key={comment.id}>
|
||||||
@ -101,8 +106,6 @@ Comments.List = function List({comments}: CommentsListProps) {
|
|||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
</section>
|
</section>
|
||||||
}
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,7 +144,11 @@ Comments.Form = function Form({slug}: { slug: string }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
|
if (!session) {
|
||||||
|
e.preventDefault()
|
||||||
|
await loginWithGitHub()
|
||||||
|
}
|
||||||
commentFormContext?.setReplyTo(null)
|
commentFormContext?.setReplyTo(null)
|
||||||
commentFormContext?.setCommentLength(0)
|
commentFormContext?.setCommentLength(0)
|
||||||
}
|
}
|
||||||
@ -154,17 +161,12 @@ Comments.Form = function Form({slug}: { slug: string }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mt-16">
|
<div className="mt-16">
|
||||||
{!session ?
|
|
||||||
<form action={async () => await loginWithGitHub()}>
|
|
||||||
<Button variant="secondary">
|
|
||||||
<GitHubIcon className="w-6 h-6 dark:fill-white"/>Sign in to comment
|
|
||||||
</Button>
|
|
||||||
</form> :
|
|
||||||
<form action={formAction} onSubmit={handleSubmit}>
|
<form action={formAction} onSubmit={handleSubmit}>
|
||||||
<div className="flex gap-x-4">
|
<div className="flex gap-x-4">
|
||||||
{session?.user?.image !== null &&
|
{session?.user?.image !== null &&
|
||||||
<Image className="size-12 rounded-full" src={session?.user?.image ?? ''}
|
<Image className="size-12 rounded-full"
|
||||||
alt={session?.user?.name ?? ''}
|
src={session?.user?.image ?? 'https://github.com/identicons/unknown.png'}
|
||||||
|
alt={session?.user?.name ?? 'Unknown user'}
|
||||||
width={64} height={64}/>
|
width={64} height={64}/>
|
||||||
}
|
}
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
@ -175,12 +177,12 @@ Comments.Form = function Form({slug}: { slug: string }) {
|
|||||||
className="resize-none block w-full rounded-md px-3 py-1.5 text-base text-zinc-600 dark:text-zinc-400 bg-[#fafafa] dark:bg-[#121212] border-[1px] dark:border-zinc-700/40 -outline-offset-1 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 focus:dark:outline-indigo-600"
|
className="resize-none block w-full rounded-md px-3 py-1.5 text-base text-zinc-600 dark:text-zinc-400 bg-[#fafafa] dark:bg-[#121212] border-[1px] dark:border-zinc-700/40 -outline-offset-1 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 focus:dark:outline-indigo-600"
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
onChange={(e) => commentFormContext?.setCommentLength(e.target.value.length ?? 0)}
|
onChange={(e) => commentFormContext?.setCommentLength(e.target.value.length ?? 0)}
|
||||||
disabled={pending}
|
disabled={pending || !session}
|
||||||
defaultValue={''}
|
defaultValue={''}
|
||||||
maxLength={commentFormContext?.commentMaxLength}
|
maxLength={commentFormContext?.commentMaxLength}
|
||||||
ref={commentFormRef}
|
ref={commentFormRef}
|
||||||
placeholder={`${commentFormContext?.replyTo ? `Reply to ${commentFormContext.replyTo.user.name}` : 'Add a comment'}`}
|
placeholder={`${!session ? 'Sign in to comment' : commentFormContext?.replyTo ? `Reply to ${commentFormContext.replyTo.user.name}` : 'Add a comment'}`}
|
||||||
required
|
required={!!session}
|
||||||
/>
|
/>
|
||||||
<input type="hidden" name="parent_id" value={parentId ?? ''}/>
|
<input type="hidden" name="parent_id" value={parentId ?? ''}/>
|
||||||
<input type="hidden" name="slug" value={slug}/>
|
<input type="hidden" name="slug" value={slug}/>
|
||||||
@ -192,9 +194,14 @@ Comments.Form = function Form({slug}: { slug: string }) {
|
|||||||
className="text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 hover:dark:text-zinc-50"
|
className="text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 hover:dark:text-zinc-50"
|
||||||
onClick={handleCancel}>Cancel</button>
|
onClick={handleCancel}>Cancel</button>
|
||||||
}
|
}
|
||||||
|
{session ?
|
||||||
<Button variant="secondary" disabled={pending}>
|
<Button variant="secondary" disabled={pending}>
|
||||||
Comment
|
Comment
|
||||||
|
</Button> :
|
||||||
|
<Button variant="secondary">
|
||||||
|
<GitHubIcon className="w-6 h-6 dark:fill-white"/>Sign in with GitHub
|
||||||
</Button>
|
</Button>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<StatusMessage className="mt-2">
|
<StatusMessage className="mt-2">
|
||||||
@ -203,7 +210,6 @@ Comments.Form = function Form({slug}: { slug: string }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ export function StatusMessage({children, className, errorConditions}: StatusMess
|
|||||||
<>
|
<>
|
||||||
{children &&
|
{children &&
|
||||||
<div
|
<div
|
||||||
className={clsx(`flex items-start sm:items-center ${className ?? ''}, ${isError ? 'text-red-800 dark:text-red-600' : 'text-green-800 dark:text-green-600'}`)}>
|
className={clsx(`flex items-start sm:items-center ${className ?? ''}, ${isError ? 'text-red-800 dark:text-red-500' : 'text-green-800 dark:text-green-500'}`)}>
|
||||||
{!isError ? <CheckIcon className="size-5 mt-0.5 sm:mt-0 mr-1"/>
|
{!isError ? <CheckIcon className="size-5 mt-0.5 sm:mt-0 mr-1"/>
|
||||||
: <CrossIcon className="size-5 mt-0.5 sm:mt-0 mr-1 "/>}
|
: <CrossIcon className="size-5 mt-0.5 sm:mt-0 mr-1 "/>}
|
||||||
<p aria-live="polite" role="status"
|
<p aria-live="polite" role="status"
|
||||||
|
@ -16,6 +16,10 @@ const nextConfig = {
|
|||||||
{
|
{
|
||||||
protocol: 'https',
|
protocol: 'https',
|
||||||
hostname: 'avatars.githubusercontent.com'
|
hostname: 'avatars.githubusercontent.com'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
protocol: 'https',
|
||||||
|
hostname: 'github.com'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user