More comment improvements
All checks were successful
Build And Publish / BuildAndPublish (push) Successful in 3m11s

This commit is contained in:
Ryan Freeman 2025-04-26 13:41:29 +01:00
parent ed815cf9a3
commit b6c7dbdd71
7 changed files with 23 additions and 24 deletions

View File

@ -3,6 +3,7 @@ SPOTIFY_CLIENT_SECRET=
SPOTIFY_REFRESH_TOKEN=
NEXT_PUBLIC_SITE_URL=
GITHUB_ACCESS_TOKEN=
GITHUB_USER_ID=
GITHUB_USERNAME=
GITHUB_CLIENT_ID=
GITHUB_SECRET=

BIN
.env.gpg

Binary file not shown.

View File

@ -4,6 +4,7 @@ import {auth, signIn} from '@/auth'
import {createClient} from '@/lib/supabase/server'
import {z} from 'zod'
import {sendNotification} from '@/lib/ntfy'
import {extractUserId} from '@/lib/github'
export async function loginWithGitHub() {
await signIn('github')
@ -22,15 +23,6 @@ const notificationBody = (comment: { id: number, content: string }, user: { name
headers: {
Authorization: `Bearer ${process.env.NTFY_TOKEN}`
}
},
{
action: 'http',
label: 'Delete comment',
url: `${process.env.NEXT_PUBLIC_SITE_URL}/api/comments/moderate/${comment.id}`,
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.NTFY_TOKEN}`
}
}
]
}
@ -43,7 +35,7 @@ export async function addComment(prevState: { message: string }, formData: FormD
const success_message = 'Your comment was submitted and is awaiting approval.'
const schema = z.object({
comment: z.string().min(1).max(300),
comment: z.string().min(1).max(500),
slug: z.string(),
parent_id: z.string().optional()
})
@ -54,7 +46,7 @@ export async function addComment(prevState: { message: string }, formData: FormD
parent_id: formData.get('parent_id')
}
const parse = schema.safeParse({comment, slug, parent_id});
const parse = schema.safeParse({comment, slug, parent_id})
if (!parse.success) {
return {message: validation_error}
}
@ -64,6 +56,7 @@ export async function addComment(prevState: { message: string }, formData: FormD
try {
const supabase = await createClient()
const session = await auth()
const isMe = process.env.GITHUB_USER_ID === extractUserId(session?.user?.image ?? '')
if (!session?.user) {
return {message: authorisation_error}
@ -78,7 +71,7 @@ export async function addComment(prevState: { message: string }, formData: FormD
const {data: newComment, error} = await supabase
.from('comments')
.insert({content: comment, article_id: article?.id, user_id: user?.id, parent_id: parent_id})
.insert({content: comment, article_id: article?.id, user_id: user?.id, parent_id: parent_id, published: isMe})
.select('*')
.single()
@ -86,7 +79,7 @@ export async function addComment(prevState: { message: string }, formData: FormD
return {message: server_error}
}
if (process.env.NODE_ENV === 'production') {
if (process.env.NODE_ENV === 'production' && !isMe) {
await sendNotification(notificationBody(newComment, user, article))
}

View File

@ -19,7 +19,7 @@ export default function CommentFormProvider({children}: { children: ReactNode })
const [replyTo, setReplyTo] = useState<Comment | null>(null)
const [commentLength, setCommentLength] = useState<number>(0)
const commentFormRef = useRef<HTMLTextAreaElement>(null)
const commentMaxLength = 300
const commentMaxLength = 500
const focusCommentForm = () => {
commentFormRef.current?.focus()

View File

@ -44,14 +44,15 @@ Comments.ReplyButton = function ReplyButton({comment}: ReplyButton) {
)
}
Comments.Comment = function Comment({comment, isReply = false}: {
Comments.Comment = function Comment({comment, isReply = false, className}: {
comment: Comment,
isReply?: boolean
className?: string
}) {
return (
<>
<article
className={clsx('flex gap-x-4 py-5', isReply && 'ml-[66px] border-l border-zinc-100 pl-6 dark:border-zinc-700/40')}>
className={clsx('flex gap-x-4 py-5', `${className ?? ''}`, isReply && 'ml-[66px] border-l border-zinc-100 pl-6 dark:border-zinc-700/40')}>
<Image src={comment.user.image} alt={comment.user.name} width={64} height={64}
className={clsx('rounded-full', isReply ? 'size-8' : 'size-12')}/>
<div className="flex-auto">
@ -87,8 +88,9 @@ Comments.List = function List({comments}: CommentsListProps) {
<React.Fragment key={comment.id}>
<Comments.Comment comment={comment}/>
{(typeof comment.replies !== 'undefined' && comment.replies.length > 0) ?
comment.replies.map(reply => (
<Comments.Comment key={reply.id} comment={reply} isReply={true}/>
comment.replies.map((reply, i) => (
<Comments.Comment key={reply.id} comment={reply} isReply={true}
className={`${i + 1 === comment.replies?.length ? 'mb-6' : ''}`}/>
)) : null
}
</React.Fragment>

View File

@ -1,5 +1,6 @@
import {createClient} from '@/lib/supabase/client'
import {QueryData} from '@supabase/supabase-js'
import type {Comment} from '@/types'
export async function getComments(slug: string) {
try {
@ -27,23 +28,21 @@ export async function getComments(slug: string) {
// @ts-ignore
acc[comment.id] = {...comment, replies: []}
return acc
}, {});
}, {})
return comments?.reduce<Comment[]>((nested, comment) => {
if (typeof commentMap !== 'undefined') {
if (comment.parent_id !== null) {
const parent = commentMap[comment.parent_id];
const parent: Comment = commentMap[comment.parent_id]
if (parent) {
// @ts-ignore
parent.replies?.push(commentMap[comment.id])
// @ts-ignore
parent.replies?.sort((a, b) => a.id - b.id)
}
} else {
nested.push(commentMap[comment.id]);
nested.push(commentMap[comment.id])
}
}
return nested;
return nested
}, [])
} catch (error) {
console.error(error)

View File

@ -46,4 +46,8 @@ export async function getPinnedRepos() {
}) as PinnedReposResponse
return response.data.user.pinnedItems.nodes
}
export function extractUserId(avatarUrl: string) {
return new URL(avatarUrl).pathname.split('/')[2]
}