Skip to content

Commit

Permalink
feat: Add comment feature
Browse files Browse the repository at this point in the history
  • Loading branch information
paulcjy committed Sep 1, 2024
1 parent f49b442 commit e4328a0
Show file tree
Hide file tree
Showing 6 changed files with 899 additions and 4 deletions.
12 changes: 8 additions & 4 deletions app/[day]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Comments from '@/components/Comments'
import { Bible, allBibles } from 'contentlayer/generated'
import { notFound } from 'next/navigation'

Expand All @@ -11,9 +12,12 @@ export default function BiblePage({ params }: { params: { day: string } }) {
if (!bible) notFound()

return (
<div
className="markdown pr-4 pl-6 mb-16 -mt-4"
dangerouslySetInnerHTML={{ __html: bible.body.html }}
/>
<>
<div
className="markdown pr-4 pl-6 mb-16 -mt-4"
dangerouslySetInnerHTML={{ __html: bible.body.html }}
/>
<Comments pageId={params.day} />
</>
)
}
121 changes: 121 additions & 0 deletions components/Comments.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
'use client'
import React, { useState, useEffect } from 'react'
import { db, auth } from '../firebase'
import { ref, push, onValue, remove } from 'firebase/database'
import { signInAnonymously } from 'firebase/auth'
import { MessageSquare, Send, Trash2 } from 'lucide-react'

export default function Comments2({ pageId }) {
const [comments, setComments] = useState([])
const [newComment, setNewComment] = useState('')
const [authorName, setAuthorName] = useState('')
const [isLoading, setIsLoading] = useState(false)

useEffect(() => {
const commentsRef = ref(db, `comments/${pageId}`)
onValue(commentsRef, (snapshot) => {
const data = snapshot.val()
if (data) {
const commentList = Object.entries(data).map(([key, value]) => ({
id: key,
...value,
}))
setComments(commentList.sort((a, b) => b.timestamp - a.timestamp))
} else {
setComments([])
}
})
}, [pageId])

const handleSubmit = async (e) => {
e.preventDefault()
if (!newComment.trim() || !authorName.trim()) return

setIsLoading(true)
try {
await signInAnonymously(auth)
const commentsRef = ref(db, `comments/${pageId}`)
await push(commentsRef, {
author: authorName.trim(),
content: newComment.trim(),
timestamp: Date.now(),
})
setNewComment('')
// 작성자 이름은 유지합니다.
} catch (error) {
console.error('Error posting comment:', error)
} finally {
setIsLoading(false)
}
}

const handleDelete = async (commentId) => {
if (window.confirm('Are you sure you want to delete this comment?')) {
try {
const commentRef = ref(db, `comments/${pageId}/${commentId}`)
await remove(commentRef)
} catch (error) {
console.error('Error deleting comment:', error)
}
}
}

return (
<div className="mt-8 bg-white shadow-lg rounded-lg p-6">
<h2 className="text-xl font-bold mb-6 flex items-center">
<MessageSquare className="mr-2" />
댓글
</h2>
<form onSubmit={handleSubmit} className="mb-6 space-y-4">
<div>
<input
type="text"
value={authorName}
onChange={(e) => setAuthorName(e.target.value)}
className="w-full p-3 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="이름"
required
/>
</div>
<div className="flex items-center">
<textarea
value={newComment}
onChange={(e) => setNewComment(e.target.value)}
className="flex-grow p-3 border rounded-l-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="댓글을 입력하세요."
rows={3}
required
/>
<button
type="submit"
className="bg-blue-500 text-white p-3 rounded-r-lg hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50"
disabled={isLoading || !newComment.trim() || !authorName.trim()}
>
{isLoading ? (
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-white"></div>
) : (
<Send className="h-6 w-6" />
)}
</button>
</div>
</form>
<div className="space-y-4">
{comments.map((comment) => (
<div key={comment.id} className="bg-gray-100 p-4 rounded-lg">
<div className="font-bold mb-2">{comment.author}</div>
<p className="mb-2">{comment.content}</p>
<div className="flex justify-between items-center text-sm text-gray-500">
<span>{new Date(comment.timestamp).toLocaleString()}</span>
{/* <button
onClick={() => handleDelete(comment.id)}
className="text-red-500 hover:text-red-700 focus:outline-none"
>
<Trash2 className="h-4 w-4" />
</button> */}
</div>
</div>
))}
</div>
</div>
)
}
71 changes: 71 additions & 0 deletions components/Comments2.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
'use client'
import React, { useState, useEffect } from 'react'
import { db, auth } from '../firebase'
import { ref, push, onValue, remove } from 'firebase/database'
import { signInAnonymously } from 'firebase/auth'

export default function Comments({ pageId }) {
const [comments, setComments] = useState([])
const [newComment, setNewComment] = useState('')

useEffect(() => {
const commentsRef = ref(db, `comments/${pageId}`)
onValue(commentsRef, (snapshot) => {
const data = snapshot.val()
if (data) {
const commentList = Object.entries(data).map(([key, value]) => ({
id: key,
...value,
}))
setComments(commentList)
} else {
setComments([])
}
})
}, [pageId])

const handleSubmit = async (e) => {
e.preventDefault()
if (!newComment.trim()) return

try {
await signInAnonymously(auth)
const commentsRef = ref(db, `comments/${pageId}`)
await push(commentsRef, {
text: newComment,
timestamp: Date.now(),
})
setNewComment('')
} catch (error) {
console.error('Error posting comment:', error)
}
}

return (
<div className="mt-8">
<h2 className="text-xl font-bold mb-4">Comments</h2>
<form onSubmit={handleSubmit} className="mb-4">
<textarea
value={newComment}
onChange={(e) => setNewComment(e.target.value)}
className="w-full p-2 border rounded"
placeholder="Add a comment..."
/>
<button
type="submit"
className="mt-2 px-4 py-2 bg-blue-500 text-white rounded"
>
Post Comment
</button>
</form>
<div>
{comments.map((comment) => (
<div key={comment.id} className="mb-2 p-2 bg-gray-100 rounded">
<p>{comment.text}</p>
<small>{new Date(comment.timestamp).toLocaleString()}</small>
</div>
))}
</div>
</div>
)
}
26 changes: 26 additions & 0 deletions firebase.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Import the functions you need from the SDKs you need
import { initializeApp } from 'firebase/app'
import { getAnalytics, isSupported } from 'firebase/analytics'
import { getDatabase } from 'firebase/database'
import { getAuth } from 'firebase/auth'
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: 'AIzaSyDpYyv3Dl_9W3GHuqiRxJsAOBh52KcMWIQ',
authDomain: 'daily-bible-11e5a.firebaseapp.com',
projectId: 'daily-bible-11e5a',
storageBucket: 'daily-bible-11e5a.appspot.com',
messagingSenderId: '783764023979',
appId: '1:783764023979:web:36d8826e6e538ce608a5ee',
measurementId: 'G-9G8S03QQP4',
databaseURL: 'https://daily-bible-11e5a-default-rtdb.firebaseio.com',
}

// Initialize Firebase
const app = initializeApp(firebaseConfig)
const analytics = isSupported(app)
export const db = getDatabase(app)
export const auth = getAuth(app)
Loading

0 comments on commit e4328a0

Please sign in to comment.