mirror of
				https://github.com/r-freeman/portfolio.git
				synced 2025-11-04 15:51:11 +00:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
			0bd57ee508
			...
			cd5aae039d
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| cd5aae039d | |||
| d09d66d4e5 | 
							
								
								
									
										10
									
								
								components/icons/CrossIcon.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								components/icons/CrossIcon.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
import {Props} from '@/types'
 | 
			
		||||
 | 
			
		||||
export function CrossIcon(props: Props) {
 | 
			
		||||
    return (
 | 
			
		||||
        <svg fill="none" strokeWidth={1.5} stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
             aria-hidden="true" {...props}>
 | 
			
		||||
            <path strokeLinecap="round" strokeLinejoin="round" d="M6 18 18 6M6 6l12 12"/>
 | 
			
		||||
        </svg>
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
@ -3,7 +3,7 @@
 | 
			
		||||
import React, {ReactNode, useRef, useState} from 'react'
 | 
			
		||||
import {CheckIcon} from '@/components/icons/CheckIcon'
 | 
			
		||||
import {CopyIcon} from '@/components/icons/CopyIcon'
 | 
			
		||||
import clsx from 'clsx';
 | 
			
		||||
import clsx from 'clsx'
 | 
			
		||||
 | 
			
		||||
export function Code({children}: { children: ReactNode }) {
 | 
			
		||||
    const [copied, setCopied] = useState<boolean>(false)
 | 
			
		||||
@ -28,7 +28,7 @@ export function Code({children}: { children: ReactNode }) {
 | 
			
		||||
                            className={clsx('absolute text-zinc-400 hover:text-zinc-50 ease-in transform transition', !copied ? 'scale-100' : 'scale-0')}/>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </button>
 | 
			
		||||
            <div className="mt-5 sm:mt-0 pb-5 overflow-auto ">
 | 
			
		||||
            <div className="mt-5 sm:mt-0 pb-5 overflow-auto">
 | 
			
		||||
            {children}
 | 
			
		||||
            </div>
 | 
			
		||||
        </pre>
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
'use client'
 | 
			
		||||
 | 
			
		||||
import React, {ReactNode, useActionState, useEffect, useState} from 'react'
 | 
			
		||||
import React, {useActionState, useEffect, useState} from 'react'
 | 
			
		||||
import {useSession} from 'next-auth/react'
 | 
			
		||||
import Image from 'next/image'
 | 
			
		||||
import clsx from 'clsx'
 | 
			
		||||
@ -8,6 +8,7 @@ import {addComment, loginWithGitHub} from '@/app/actions/comments'
 | 
			
		||||
import {Button} from '@/components/ui/Button'
 | 
			
		||||
import {GitHubIcon} from '@/components/icons/SocialIcons'
 | 
			
		||||
import {ArrowLeftIcon} from '@/components/icons/ArrowLeftIcon'
 | 
			
		||||
import {StatusMessage} from '@/components/ui/StatusMessage'
 | 
			
		||||
import {getShortDurationFromNow} from '@/lib/dateFns'
 | 
			
		||||
import ReplyProvider, {useReplyContext} from '@/app/context/ReplyProvider'
 | 
			
		||||
import CommentFormProvider, {useCommentFormContext} from '@/app/context/CommentFormProvider'
 | 
			
		||||
@ -98,23 +99,6 @@ Comments.List = function List({comments}: CommentsListProps) {
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type CommentsStatusProps = {
 | 
			
		||||
    children: ReactNode
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Comments.Status = function Status({children}: CommentsStatusProps) {
 | 
			
		||||
    const errorConditions = ['error', 'problem']
 | 
			
		||||
    const isError = errorConditions.some(condition => children?.toString().toLowerCase().includes(condition))
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <p aria-live="polite" role="status"
 | 
			
		||||
           className={clsx('text-sm font-semibold',
 | 
			
		||||
               !isError ? 'text-green-800 dark:text-green-600' : 'text-red-800 dark:text-red-600')}>
 | 
			
		||||
            {children}
 | 
			
		||||
        </p>
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type InitialState = {
 | 
			
		||||
    message: string
 | 
			
		||||
}
 | 
			
		||||
@ -130,6 +114,8 @@ Comments.Form = function Form({slug}: { slug: string }) {
 | 
			
		||||
    const replyContext = useReplyContext()
 | 
			
		||||
    const commentFormContext = useCommentFormContext()
 | 
			
		||||
    const commentFormRef = commentFormContext?.commentFormRef
 | 
			
		||||
    const [commentLength, setCommentLength] = useState<number>(0)
 | 
			
		||||
    const commentMaxLength = 300
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        if (replyContext?.replyTo?.parent_id !== null) {
 | 
			
		||||
@ -151,6 +137,11 @@ Comments.Form = function Form({slug}: { slug: string }) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const handleSubmit = () => {
 | 
			
		||||
        replyContext?.setReplyTo(null)
 | 
			
		||||
        setCommentLength(0)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <div className="mt-12">
 | 
			
		||||
            {!session ?
 | 
			
		||||
@ -159,7 +150,7 @@ Comments.Form = function Form({slug}: { slug: string }) {
 | 
			
		||||
                        <GitHubIcon className="w-6 h-6 dark:fill-white"/>Sign in to comment
 | 
			
		||||
                    </Button>
 | 
			
		||||
                </form> :
 | 
			
		||||
                <form action={formAction} onSubmit={() => replyContext?.setReplyTo(null)}>
 | 
			
		||||
                <form action={formAction} onSubmit={handleSubmit}>
 | 
			
		||||
                    <label htmlFor="comment"
 | 
			
		||||
                           className="text-base font-semibold tracking-tight text-zinc-800 dark:text-zinc-100">
 | 
			
		||||
                        {replyContext?.replyTo ? `Reply to ${replyContext?.replyTo.user.name}` : 'Add a comment'}
 | 
			
		||||
@ -171,9 +162,10 @@ Comments.Form = function Form({slug}: { slug: string }) {
 | 
			
		||||
                                rows={4}
 | 
			
		||||
                                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}
 | 
			
		||||
                                onChange={(e) => setCommentLength(e.target.value.length ?? 0)}
 | 
			
		||||
                                disabled={pending}
 | 
			
		||||
                                defaultValue={''}
 | 
			
		||||
                                maxLength={255}
 | 
			
		||||
                                maxLength={commentMaxLength}
 | 
			
		||||
                                ref={commentFormRef}
 | 
			
		||||
                                required
 | 
			
		||||
                            />
 | 
			
		||||
@ -181,9 +173,7 @@ Comments.Form = function Form({slug}: { slug: string }) {
 | 
			
		||||
                    <input type="hidden" name="parent_id" value={parentId ?? ''}/>
 | 
			
		||||
                    <input type="hidden" name="slug" value={slug}/>
 | 
			
		||||
                    <div className="mt-2 flex justify-between items-start gap-x-4">
 | 
			
		||||
                        <Comments.Status>
 | 
			
		||||
                            {state?.message}
 | 
			
		||||
                        </Comments.Status>
 | 
			
		||||
                        <p className="text-sm text-zinc-600 dark:text-zinc-400">{`${commentLength} / ${commentMaxLength}`}</p>
 | 
			
		||||
                        <div className="flex gap-x-4">
 | 
			
		||||
                            {replyContext?.replyTo &&
 | 
			
		||||
                                <button className="text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 hover:dark:text-zinc-50"
 | 
			
		||||
@ -194,6 +184,9 @@ Comments.Form = function Form({slug}: { slug: string }) {
 | 
			
		||||
                            </Button>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <StatusMessage className="mt-2">
 | 
			
		||||
                        {state.message}
 | 
			
		||||
                    </StatusMessage>
 | 
			
		||||
                </form>
 | 
			
		||||
            }
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										29
									
								
								components/ui/StatusMessage.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								components/ui/StatusMessage.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
			
		||||
import clsx from 'clsx'
 | 
			
		||||
import React, {ReactNode} from 'react'
 | 
			
		||||
import {CheckIcon} from '@/components/icons/CheckIcon'
 | 
			
		||||
import {CrossIcon} from '@/components/icons/CrossIcon'
 | 
			
		||||
 | 
			
		||||
type StatusMessageProps = {
 | 
			
		||||
    children: ReactNode
 | 
			
		||||
    className?: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function StatusMessage({children, className}: StatusMessageProps) {
 | 
			
		||||
    const errorConditions = ['error', 'problem']
 | 
			
		||||
    const isError = errorConditions.some(condition => children?.toString().toLowerCase().includes(condition))
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <>
 | 
			
		||||
            {children &&
 | 
			
		||||
                <div className={clsx(`flex items-start sm:items-center ${className ?? ''}`)}>
 | 
			
		||||
                    {!isError ? <CheckIcon className="size-5 mt-0.5 sm:mt-0 mr-1 text-green-800 dark:text-green-600"/>
 | 
			
		||||
                        : <CrossIcon className="size-5 mt-0.5 sm:mt-0 mr-1 text-red-800 dark:text-red-600"/>}
 | 
			
		||||
                    <p aria-live="polite" role="status"
 | 
			
		||||
                       className={clsx('text-sm font-semibold', !isError ? 'text-green-800 dark:text-green-600' : 'text-red-800 dark:text-red-600')}>
 | 
			
		||||
                        {children}
 | 
			
		||||
                    </p>
 | 
			
		||||
                </div>
 | 
			
		||||
            }
 | 
			
		||||
        </>
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user