'use client'; import { useEffect, useState } from 'react'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { fetchApi } from '@/lib/utils/client'; import { formatNoteCounterparty } from '@/lib/utils/note'; import Loading from '@/app/component/Loading'; import { useMemberContext } from '@/contexts/memberProvider'; import type { NoteDetail } from '@/types/response/note/inbox'; import type { RecipientItem } from '@/types/response/note/searchRecipient'; import { Reply, Trash2 } from 'lucide-react'; type Props = { noteId: number|null; onClose: () => void; onReply?: (sender: RecipientItem, originalTitle: string) => void; onDeleted?: () => void; }; function formatDate(dateStr: string): string { const d = new Date(dateStr); return `${d.getFullYear()}-${(d.getMonth() + 1).toString().padStart(2, '0')}-${d.getDate().toString().padStart(2, '0')} ${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}`; } export default function NoteDetailModal({ noteId, onClose, onReply, onDeleted }: Props) { const { member } = useMemberContext(); const [note, setNote] = useState(null); const [loading, setLoading] = useState(false); const [deleting, setDeleting] = useState(false); const [error, setError] = useState(null); useEffect(() => { if (noteId === null) { setNote(null); setError(null); return; } let cancelled = false; setLoading(true); setError(null); fetchApi(`/api/note/${noteId}`, { silent: true }).then((res) => { if (cancelled) { return; } if (res.success && res.data) { setNote(res.data); } else { setError(res.message || '쪽지를 불러올 수 없습니다.'); } }).catch((e: unknown) => { if (cancelled) { return; } const msg = e instanceof Error ? e.message : '쪽지를 불러올 수 없습니다.'; setError(msg); }).finally(() => { if (!cancelled) { setLoading(false); } }); return () => { cancelled = true; }; }, [noteId]); // 본인이 sender 인 경우 보낸쪽지 모드 — 답장 버튼 숨기고 상대방으로 receiver 표시 const isSenderView = note !== null && member !== null && note.senderMemberID === member.id; const counterpartyThumb = isSenderView ? note?.receiverThumb : note?.senderThumb; const counterpartyLabel = isSenderView ? '받는 사람' : '보낸 사람'; const counterparty = note ? formatNoteCounterparty(isSenderView ? { name: note.receiverName, sid: note.receiverSID, channelName: note.receiverChannelName, channelHandle: note.receiverChannelHandle } : { isSystem: note.isSystem, name: note.senderName, sid: note.senderSID, channelName: note.senderChannelName, channelHandle: note.senderChannelHandle }) : { primary: '', secondary: null }; const counterpartyName = counterparty.primary; const handleReply = () => { if (!note || note.isSystem || !note.senderMemberID) { return; } const sender: RecipientItem = { memberID: note.senderMemberID, displayName: note.senderName ?? '회원', displaySubtitle: null, thumbnailUrl: note.senderThumb }; onReply?.(sender, note.title); }; const handleDelete = async () => { if (!note) { return; } if (!confirm('이 쪽지를 삭제하시겠습니까?')) { return; } setDeleting(true); try { const res = await fetchApi(`/api/note/${note.id}`, { method: 'DELETE' }); if (res.success) { onDeleted?.(); onClose(); return; } setError(res.message || '삭제에 실패했습니다.'); } catch (e: unknown) { const msg = e instanceof Error ? e.message : '삭제에 실패했습니다.'; setError(msg); } finally { setDeleting(false); } }; const open = noteId !== null; const canReply = !isSenderView && note !== null && !note.isSystem && note.senderMemberID !== null; return ( { if (!o) { onClose(); } }}> {note?.title ?? '쪽지'}
{loading && } {error && (
{error}
)} {note && !loading && ( <>
{counterpartyThumb ? ( // eslint-disable-next-line @next/next/no-img-element {counterpartyName} ) : (
{(counterpartyName ?? '?')[0]?.toUpperCase()}
)}
{counterpartyLabel}: {counterpartyName} {counterparty.secondary && ( {counterparty.secondary} )} {note.isSystem && ( 시스템 )} {isSenderView && ( {note.isRead ? '읽음' : '안읽음'} )}
{formatDate(note.createdAt)}
{note.content}
{canReply && ( )}
)}
); }