ChatGPTを使用したアプリ開発記【投稿詳細画面でコメント表示・編集するように改修】

## Firestoreにコメントのサブコレクションのルールを追加
```js
    match /posts/{postId} {
      allow read: if true;
      allow write: if request.auth != null;

      // 🔽 コメントのサブコレクションのルール
      match /comments/{commentId} {
        allow read, write: if request.auth != null;
      }
    }
```
## Firestoreの構成

![スクリーンショット 2025-06-01 10.30.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1058366/d088009d-7e8e-4a78-9958-75e8f34b3ada.png)


## 投稿一覧画面ではコメント数のみ表示
```Home.js
// Firestore の posts コレクションの中にある、ある投稿 (docSnap.id) に対応するサブコレクション comments を指定しています。
const commentsSnapshot = await getDocs(collection(db, "posts", docSnap.id, "comments"));
// 取得した commentsSnapshot の中に含まれるコメント数（ドキュメント数）を返します
const commentCount = commentsSnapshot.size;

//コメント数の表示
 <div style={{ marginTop: '1rem' }}>
   <p className="text-sm text-gray-600">
     コメント数: {post.commentCount || 0}
   </p>
 </div>
```

## 投稿一覧画面UI

![スクリーンショット 2025-06-01 10.24.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1058366/82b1c2e2-ff95-4877-a85c-843cb9b6943b.png)

## 投稿詳細画面のコード
•	目的: 投稿に紐づくすべてのコメントをFirebase Firestoreから取得。
•	コレクション構造:
Firestoreでは、投稿（posts）の中に、サブコレクションとしてcommentsがある構造。
```PostDetail.js
const commentsSnapshot = await getDocs(collection(db, 'posts', postId, 'comments'));
const commentsList = commentsSnapshot.docs.map(doc => doc.data());
setComments(commentsList);
```

•	目的: 取得したcommentsを画面に表示。
•	userNameとtextをリスト形式で表示。
•	comments.length === 0 のときは「コメントはまだありません」と表示。
```PostDetail.js
{comments.length === 0 ? (
  <p className="text-gray-500">コメントはまだありません。</p>
) : (
  <ul className="list-disc list-inside mt-2 mb-4">
    {comments.map((c, i) => (
      <li key={i}>
        <span className="font-bold">{c.userName || '匿名'}:</span> {c.text}
      </li>
    ))}
  </ul>
)}
```
•	目的: 投稿に新しいコメントを追加する。
•	commentText（テキストエリアの入力内容）が空でなく、ログイン済みなら処理実行。
•	usersコレクションからユーザー名を取得。
•	新しいコメントデータ（text, userId, userName, createdAt）を構築。
•	addDocでFirestoreのcommentsサブコレクションに追加。
•	成功後はコメントをローカル状態にも追加し、入力欄を空にする。
```PostDetail.js
const handleAddComment = async () => {
  if (!commentText.trim() || !currentUser) return;

  const userDoc = await getDoc(doc(db, 'users', currentUser.uid));
  const userName = userDoc.exists() ? (userDoc.data().name || "匿名") : "匿名";

  const newComment = {
    text: commentText.trim(),
    userId: currentUser.uid,
    userName,
    createdAt: serverTimestamp(),
  };

  try {
    await addDoc(collection(db, 'posts', postId, 'comments'), newComment);
    setComments(prev => [...prev, newComment]); // 即座にUIにも追加
    setCommentText(""); // 入力欄を空にする
  } catch (err) {
    console.error("コメントの送信に失敗:", err);
  }
};
```
•	ログイン状態によって表示内容が切り替わる。
•	ログイン済み: テキストエリアと送信ボタンを表示。
•	未ログイン: ログインが必要という案内メッセージを表示。
```PostDetail.js
{currentUser ? (
  <div className="mt-4">
    <textarea
      rows={2}
      value={commentText}
      onChange={(e) => setCommentText(e.target.value)}
      placeholder="コメントを入力..."
      className="w-full p-2 border rounded focus:outline-none focus:ring"
    />
    <button
      onClick={handleAddComment}
      className="mt-2 bg-blue-500 text-white px-4 py-2 rounded-2xl hover:bg-blue-600 transition duration-200 shadow-md"
    >
      コメントする
    </button>
  </div>
) : (
  <p className="text-gray-500">コメントするにはログインが必要です。</p>
)}
```
## 投稿詳細UI

![スクリーンショット 2025-06-01 10.26.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1058366/16e28a89-3ad3-4b2e-902c-21b8df3bfadf.png)