Skip to content

Commit be9db9d

Browse files
authored
チャットメッセージ削除・編集 (#291)
* チャットメッセージ削除機能実装 * make node_modules ignored * 未稼働の関数の消去
1 parent f89b3b8 commit be9db9d

File tree

3 files changed

+89
-2
lines changed

3 files changed

+89
-2
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ scripts/seed/node_modules
44
/.env
55

66
# web
7+
web/node_modules
78
web/dist
89
web/.next
910
web/next-env.d.ts
1011
web/.vercel
1112
web/.dev.vars
1213
# server
14+
server/node_modules
1315
server/target
1416

1517
# ?

server/routes/chat.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ const router = new Hono()
417417
},
418418
)
419419
.delete(
420-
"/messages/:message",
420+
"/messages/:message/:room",
421421
zValidator(
422422
"param",
423423
z.object({

web/src/app/[locale]/(auth)/chat/[id]/page.tsx

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ function MessageList({
139139
room: string;
140140
}) {
141141
const [messages, setMessages] = useState(data);
142+
const { idToken: Authorization } = useAuthContext();
142143

143144
useEffect(() => {
144145
handlers.onCreate = (message) => {
@@ -188,12 +189,62 @@ function MessageList({
188189
bottomRef.current?.scrollIntoView({ behavior: "auto" });
189190
}, [messages]);
190191

192+
const [selectedMessageId, setSelectedMessageId] = useState<string | null>(null);
193+
const [showConfirmModal, setShowConfirmModal] = useState(false);
194+
const [deletingMessageId, setDeletingMessageId] = useState<string | null>(null);
195+
const longPressTimer = useRef<NodeJS.Timer | null>(null);
196+
197+
const handleRequestDelete = (id: string) => {
198+
setDeletingMessageId(id);
199+
setShowConfirmModal(true);
200+
};
201+
202+
const handleDelete = async () => {
203+
if (!deletingMessageId) return;
204+
205+
try {
206+
await client.chat.messages[":message"][":room"].$delete({
207+
header: { Authorization },
208+
param: { message: deletingMessageId, room: room },
209+
});
210+
211+
setMessages((prev) => prev.filter((m) => m.id !== deletingMessageId));
212+
213+
setDeletingMessageId(null);
214+
setShowConfirmModal(false);
215+
} catch (error) {
216+
alert("削除に失敗しました");
217+
}
218+
};
219+
220+
const handleEdit = (id: string) => {
221+
console.log("編集", id);
222+
setSelectedMessageId(null);
223+
};
224+
225+
const handleLongPressStart = (id: string) => {
226+
longPressTimer.current = setTimeout(() => {
227+
setSelectedMessageId(id);
228+
}, 600); // 600ms 長押しで発動
229+
};
230+
231+
const handleLongPressEnd = () => {
232+
if (longPressTimer.current) clearTimeout(longPressTimer.current);
233+
};
234+
191235
return (
192236
<ul className="mx-3 mt-[56px] mb-[76px] grow overflow-y-scroll sm:pb-0" id="scroll-bottom">
193237
{messages.map((m) => (
194238
// TODO: handle pictures
195239
<li key={m.id}>
196-
<div className={`chat ${m.senderId === me.id ? "chat-end" : "chat-start"}`}>
240+
<div
241+
className={`chat ${m.senderId === me.id ? "chat-end" : "chat-start"}`}
242+
onTouchStart={() => handleLongPressStart(m.id)}
243+
onTouchEnd={handleLongPressEnd}
244+
onMouseDown={() => handleLongPressStart(m.id)}
245+
onMouseUp={handleLongPressEnd}
246+
onMouseLeave={handleLongPressEnd}
247+
>
197248
<div className="chat-header">
198249
<time className="text-xs opacity-50">{m.createdAt.toLocaleString()}</time>
199250
</div>
@@ -204,8 +255,42 @@ function MessageList({
204255
<br />
205256
</div>
206257
))}
258+
259+
{selectedMessageId === m.id && (
260+
<div className="absolute top-0 right-0 z-10 flex gap-1 rounded border bg-white p-1 shadow">
261+
<button
262+
type="button"
263+
className="text-blue-600 text-sm hover:underline"
264+
onClick={() => handleEdit(m.id)}
265+
>
266+
編集
267+
</button>
268+
<button type="button" onClick={() => handleRequestDelete(m.id)} className="text-red-600">
269+
削除
270+
</button>
271+
</div>
272+
)}
207273
</div>
208274
{/* <div className="chat-footer opacity-50">Seen</div> */}
275+
{showConfirmModal && (
276+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-40">
277+
<div className="rounded-lg bg-white p-4 shadow-lg">
278+
<p className="mb-4">このメッセージを削除しますか?</p>
279+
<div className="flex justify-end gap-3">
280+
<button
281+
type="button"
282+
className="rounded bg-gray-300 px-4 py-2"
283+
onClick={() => setShowConfirmModal(false)}
284+
>
285+
キャンセル
286+
</button>
287+
<button type="button" className="rounded bg-red-500 px-4 py-2 text-white" onClick={handleDelete}>
288+
削除
289+
</button>
290+
</div>
291+
</div>
292+
</div>
293+
)}
209294
</div>
210295
</li>
211296
))}

0 commit comments

Comments
 (0)