From 7943a076b3f1cf3ac7ec38c767e8e94efa9c6128 Mon Sep 17 00:00:00 2001 From: KaichiManabe <154493010+KaichiManabe@users.noreply.github.com> Date: Sat, 30 Nov 2024 17:57:01 +0900 Subject: [PATCH 1/8] =?UTF-8?q?=E8=87=AA=E5=88=86=E3=81=AB=E3=83=AA?= =?UTF-8?q?=E3=82=AF=E3=82=A8=E3=82=B9=E3=83=88=E3=82=92=E9=80=81=E3=81=A3?= =?UTF-8?q?=E3=81=A6=E3=81=8D=E3=81=9F=E4=BA=BA=E3=81=A8=E3=82=82=E3=83=81?= =?UTF-8?q?=E3=83=A3=E3=83=83=E3=83=88=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/zod/schemas.ts | 1 + server/src/database/chat.ts | 24 ++++++++++++++++++++--- web/components/chat/RoomList.tsx | 27 ++++++++++++++++++++++++++ web/components/human/humanListItem.tsx | 13 +++++++++++-- 4 files changed, 60 insertions(+), 5 deletions(-) diff --git a/common/zod/schemas.ts b/common/zod/schemas.ts index 2c1c9cad..3d44b0c9 100644 --- a/common/zod/schemas.ts +++ b/common/zod/schemas.ts @@ -125,6 +125,7 @@ export const SendMessageSchema = z.object({ export const DMOverviewSchema = z.object({ isDM: z.literal(true), + isFriend: z.boolean(), friendId: UserIDSchema, name: NameSchema, thumbnail: z.string(), diff --git a/server/src/database/chat.ts b/server/src/database/chat.ts index 37f8a7cf..48887899 100644 --- a/server/src/database/chat.ts +++ b/server/src/database/chat.ts @@ -14,9 +14,8 @@ import type { } from "common/types"; import { prisma } from "./client"; import { getRelation } from "./matches"; -import { getMatchedUser } from "./requests"; +import { getMatchedUser, getPendingRequestsToUser } from "./requests"; -// ユーザーの参加しているすべての Room の概要 (Overview) の取得 export async function getOverview( user: UserID, ): Promise> { @@ -24,6 +23,9 @@ export async function getOverview( const matched = await getMatchedUser(user); if (!matched.ok) return Err(matched.error); + const requester = await getPendingRequestsToUser(user); + if (!requester.ok) return Err(requester.error); + const dm = await Promise.all( matched.value.map(async (friend) => { const lastMessageResult = await getLastMessage(user, friend.id); @@ -32,6 +34,7 @@ export async function getOverview( : undefined; const overview: DMOverview = { isDM: true, + isFriend: true, friendId: friend.id, name: friend.name, thumbnail: friend.pictureUrl, @@ -61,7 +64,22 @@ export async function getOverview( }; return overview; }); - return Ok([...shared, ...dm]); + + // リクエスター (友達申請者) のオーバービュー作成 + const requesterOverview = requester.value.map((requester) => { + const overview: DMOverview = { + isDM: true, + isFriend: false, + friendId: requester.id, + name: requester.name, + thumbnail: requester.pictureUrl, + lastMsg: undefined, // リクエストには最後のメッセージはない + }; + return overview; + }); + + // すべてのオーバービューを結合 + return Ok([...shared, ...dm, ...requesterOverview]); } catch (e) { return Err(e); } diff --git a/web/components/chat/RoomList.tsx b/web/components/chat/RoomList.tsx index 1c6b6fad..154c9594 100644 --- a/web/components/chat/RoomList.tsx +++ b/web/components/chat/RoomList.tsx @@ -3,6 +3,7 @@ import { Box, List, Typography } from "@mui/material"; import type { RoomOverview } from "common/types"; import { useRouter } from "next/navigation"; +import request from "~/api/request"; import { HumanListItem } from "../human/humanListItem"; type RoomListProps = { @@ -34,6 +35,32 @@ export function RoomList(props: RoomListProps) {

{roomsData?.map((room) => { if (room.isDM) { + if (!room.isFriend) { + return ( + { + e.stopPropagation(); + navigateToRoom(room); + }} + > + { + request.accept(room.friendId).then(() => location.reload()); + }} + onReject={() => { + request.reject(room.friendId).then(() => location.reload()); + }} + /> + + ); + } return ( )} {onReject && ( // biome-ignore lint/a11y/useButtonType: - )} From 6b80726adbb0432992e1f6f771578ca5fc17a3d5 Mon Sep 17 00:00:00 2001 From: KaichiManabe <154493010+KaichiManabe@users.noreply.github.com> Date: Sat, 30 Nov 2024 18:32:44 +0900 Subject: [PATCH 2/8] =?UTF-8?q?=E3=83=9E=E3=83=83=E3=83=81=E3=81=AF?= =?UTF-8?q?=E3=81=97=E3=81=A6=E3=81=AA=E3=81=84=E4=BA=BA=E3=81=A8=E3=81=AF?= =?UTF-8?q?=E3=80=81=E7=B0=A1=E5=8D=98=E3=81=AA=E3=83=A2=E3=83=BC=E3=83=80?= =?UTF-8?q?=E3=83=AB=E3=81=8C=E8=A1=A8=E7=A4=BA=E3=81=95=E3=82=8C=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/zod/schemas.ts | 1 + server/src/functions/chat.ts | 10 ++++--- web/api/chat/chat.ts | 15 ++++------ web/app/chat/[id]/page.tsx | 19 ++----------- web/components/chat/RoomWindow.tsx | 44 ++++++++++++++++++------------ 5 files changed, 40 insertions(+), 49 deletions(-) diff --git a/common/zod/schemas.ts b/common/zod/schemas.ts index 3d44b0c9..43237158 100644 --- a/common/zod/schemas.ts +++ b/common/zod/schemas.ts @@ -154,6 +154,7 @@ export const DMRoomSchema = z.object({ export const PersonalizedDMRoomSchema = z.object({ name: NameSchema, thumbnail: z.string(), + isFriend: z.boolean(), }); export const SharedRoomSchema = z.object({ diff --git a/server/src/functions/chat.ts b/server/src/functions/chat.ts index 1277e54d..80d3dfdf 100644 --- a/server/src/functions/chat.ts +++ b/server/src/functions/chat.ts @@ -42,8 +42,8 @@ export async function sendDM( send: SendMessage, ): Promise> { const rel = await getRelation(from, to); - if (!rel.ok || rel.value.status !== "MATCHED") - return http.forbidden("cannot send to non-friend"); + if (!rel.ok || rel.value.status === "REJECTED") + return http.forbidden("cannot send to rejected-friend"); // they are now MATCHED const msg: Omit = { @@ -62,8 +62,9 @@ export async function getDM( requester: UserID, _with: UserID, ): Promise> { - if (!areMatched(requester, _with)) - return http.forbidden("cannot DM with a non-friend"); + const rel = await getRelation(requester, _with); + if (!rel.ok || rel.value.status === "REJECTED") + return http.forbidden("cannot send to rejected-friend"); const room = await db.getDMbetween(requester, _with); if (!room.ok) return http.internalError(); @@ -74,6 +75,7 @@ export async function getDM( const personalized: PersonalizedDMRoom & DMRoom = { name: friendData.value.name, thumbnail: friendData.value.pictureUrl, + isFriend: rel.value.status === "MATCHED", ...room.value, }; diff --git a/web/api/chat/chat.ts b/web/api/chat/chat.ts index 63788ea5..83e307d5 100644 --- a/web/api/chat/chat.ts +++ b/web/api/chat/chat.ts @@ -3,6 +3,7 @@ import type { InitRoom, Message, MessageID, + PersonalizedDMRoom, RoomOverview, SendMessage, ShareRoomID, @@ -83,22 +84,16 @@ export async function sendDM( return res.json(); } -export async function getDM(friendId: UserID): Promise< - DMRoom & { - name: string; - thumbnail: string; - } -> { +export async function getDM( + friendId: UserID, +): Promise { const res = await credFetch("GET", endpoints.dmWith(friendId)); if (res.status === 401) throw new ErrUnauthorized(); if (res.status !== 200) throw new Error( `getDM() failed: expected status code 200, got ${res.status}`, ); - const json: DMRoom & { - name: string; - thumbnail: string; - } = await res.json(); + const json: DMRoom & PersonalizedDMRoom = await res.json(); if (!Array.isArray(json?.messages)) return json; for (const m of json.messages) { m.createdAt = new Date(m.createdAt); diff --git a/web/app/chat/[id]/page.tsx b/web/app/chat/[id]/page.tsx index 1f717305..5f9d045f 100644 --- a/web/app/chat/[id]/page.tsx +++ b/web/app/chat/[id]/page.tsx @@ -1,27 +1,12 @@ "use client"; +import type { DMRoom, PersonalizedDMRoom } from "common/types"; import { useEffect, useState } from "react"; import * as chat from "~/api/chat/chat"; import { RoomWindow } from "~/components/chat/RoomWindow"; export default function Page({ params }: { params: { id: string } }) { const id = Number.parseInt(params.id); - const [room, setRoom] = useState< - | ({ - id: number; - isDM: true; - messages: { - id: number; - creator: number; - createdAt: Date; - content: string; - edited: boolean; - }[]; - } & { - name: string; - thumbnail: string; - }) - | null - >(null); + const [room, setRoom] = useState<(DMRoom & PersonalizedDMRoom) | null>(null); useEffect(() => { (async () => { const room = await chat.getDM(id); diff --git a/web/components/chat/RoomWindow.tsx b/web/components/chat/RoomWindow.tsx index d8b211ba..3acef3a5 100644 --- a/web/components/chat/RoomWindow.tsx +++ b/web/components/chat/RoomWindow.tsx @@ -1,6 +1,6 @@ "use client"; import type { Message, MessageID, SendMessage, UserID } from "common/types"; -import type { Content } from "common/zod/types"; +import type { Content, DMRoom, PersonalizedDMRoom } from "common/zod/types"; import { useSnackbar } from "notistack"; import { useCallback, useEffect, useRef, useState } from "react"; import * as chat from "~/api/chat/chat"; @@ -13,23 +13,8 @@ import { socket } from "../data/socket"; import { MessageInput } from "./MessageInput"; import { RoomHeader } from "./RoomHeader"; -type Props = { - friendId: UserID; - room: { - id: number; - messages: { - id: number; - creator: number; - createdAt: Date; - content: string; - edited: boolean; - }[]; - isDM: true; - } & { - name: string; - thumbnail: string; - }; -}; +type Props = { friendId: UserID; room: DMRoom & PersonalizedDMRoom }; + export function RoomWindow(props: Props) { const { friendId, room } = props; @@ -171,6 +156,10 @@ export function RoomWindow(props: Props) { return ( <> + {!room.isFriend && ( + + )} +
@@ -261,3 +250,22 @@ export function RoomWindow(props: Props) { ); } + +type FloatingMessageProps = { + message: string; +}; + +const FloatingMessage = ({ message }: FloatingMessageProps) => { + return ( +
+

+ {message} +

+
+ ); +}; From 2a850dd0a9f182661336802398bb57ff19993c05 Mon Sep 17 00:00:00 2001 From: KaichiManabe <154493010+KaichiManabe@users.noreply.github.com> Date: Sat, 30 Nov 2024 22:00:30 +0900 Subject: [PATCH 3/8] =?UTF-8?q?chat=E7=94=BB=E9=9D=A2=E3=81=AE=E3=83=A2?= =?UTF-8?q?=E3=83=BC=E3=83=80=E3=83=AB=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/components/chat/RoomWindow.tsx | 42 ++++++++++++++++++++++---- web/components/human/humanListItem.tsx | 8 ++--- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/web/components/chat/RoomWindow.tsx b/web/components/chat/RoomWindow.tsx index 3acef3a5..32857a6e 100644 --- a/web/components/chat/RoomWindow.tsx +++ b/web/components/chat/RoomWindow.tsx @@ -1,10 +1,12 @@ "use client"; import type { Message, MessageID, SendMessage, UserID } from "common/types"; import type { Content, DMRoom, PersonalizedDMRoom } from "common/zod/types"; +import { useRouter } from "next/navigation"; import { useSnackbar } from "notistack"; import { useCallback, useEffect, useRef, useState } from "react"; import * as chat from "~/api/chat/chat"; import { useMessages } from "~/api/chat/hooks"; +import request from "~/api/request"; import * as user from "~/api/user"; import { useMyID } from "~/api/user"; import { getIdToken } from "~/firebase/auth/lib"; @@ -157,7 +159,10 @@ export function RoomWindow(props: Props) { return ( <> {!room.isFriend && ( - + )}
@@ -253,19 +258,44 @@ export function RoomWindow(props: Props) { type FloatingMessageProps = { message: string; + friendId: UserID; }; -const FloatingMessage = ({ message }: FloatingMessageProps) => { +const FloatingMessage = ({ message, friendId }: FloatingMessageProps) => { + const router = useRouter(); return (
-

- {message} -

+
+

{message}

+ {/* biome-ignore lint/a11y/useButtonType: */} + + {/* biome-ignore lint/a11y/useButtonType: */} + +
); }; diff --git a/web/components/human/humanListItem.tsx b/web/components/human/humanListItem.tsx index 055ce200..151b06f9 100644 --- a/web/components/human/humanListItem.tsx +++ b/web/components/human/humanListItem.tsx @@ -69,8 +69,8 @@ export function HumanListItem(props: HumanListItemProps) {
From 1312cc2b7885cc36ed4572855bc1ccafe5511969 Mon Sep 17 00:00:00 2001 From: KaichiManabe <154493010+KaichiManabe@users.noreply.github.com> Date: Mon, 2 Dec 2024 17:58:44 +0900 Subject: [PATCH 7/8] =?UTF-8?q?=E3=83=A1=E3=83=83=E3=82=BB=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E3=82=92=E9=80=81=E3=81=A3=E3=81=9F=E4=BA=BA=E3=81=A8?= =?UTF-8?q?=E9=80=81=E3=82=89=E3=82=8C=E3=81=9F=E4=BA=BA=E3=81=A7=E8=A1=A8?= =?UTF-8?q?=E7=A4=BA=E3=82=92=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/database/chat.ts | 82 +++++++++++++++++++++--------- server/src/functions/chat.ts | 14 ++--- web/components/chat/RoomList.tsx | 32 +++++++++--- web/components/chat/RoomWindow.tsx | 59 +++++++++++++-------- 4 files changed, 125 insertions(+), 62 deletions(-) diff --git a/server/src/database/chat.ts b/server/src/database/chat.ts index 82a49999..2a1e8307 100644 --- a/server/src/database/chat.ts +++ b/server/src/database/chat.ts @@ -14,7 +14,11 @@ import type { } from "common/types"; import { prisma } from "./client"; import { getRelation } from "./matches"; -import { getMatchedUser, getPendingRequestsToUser } from "./requests"; +import { + getMatchedUser, + getPendingRequestsFromUser, + getPendingRequestsToUser, +} from "./requests"; export async function getOverview( user: UserID, @@ -23,10 +27,14 @@ export async function getOverview( const matched = await getMatchedUser(user); if (!matched.ok) return Err(matched.error); - const requester = await getPendingRequestsToUser(user); - if (!requester.ok) return Err(requester.error); + const senders = await getPendingRequestsToUser(user); + if (!senders.ok) return Err(senders.error); - const dm = await Promise.all( + const receivers = await getPendingRequestsFromUser(user); + if (!receivers.ok) return Err(receivers.error); + + //マッチングしている人のオーバービュー + const matchingOverview = await Promise.all( matched.value.map(async (friend) => { const lastMessageResult = await getLastMessage(user, friend.id); const lastMessage = lastMessageResult.ok @@ -34,7 +42,7 @@ export async function getOverview( : undefined; const overview: DMOverview = { isDM: true, - isFriend: true, + matchingStatus: "matched", friendId: friend.id, name: friend.name, thumbnail: friend.pictureUrl, @@ -44,6 +52,44 @@ export async function getOverview( }), ); + //自分にリクエストを送ってきた人のオーバービュー + const senderOverview = await Promise.all( + senders.value.map(async (sender) => { + const lastMessageResult = await getLastMessage(user, sender.id); + const lastMessage = lastMessageResult.ok + ? lastMessageResult.value + : undefined; + const overview: DMOverview = { + isDM: true, + matchingStatus: "otherRequest", + friendId: sender.id, + name: sender.name, + thumbnail: sender.pictureUrl, + lastMsg: lastMessage, + }; + return overview; + }), + ); + + //自分がリクエストを送った人のオーバービュー + const receiverOverview = await Promise.all( + receivers.value.map(async (receiver) => { + const lastMessageResult = await getLastMessage(user, receiver.id); + const lastMessage = lastMessageResult.ok + ? lastMessageResult.value + : undefined; + const overview: DMOverview = { + isDM: true, + matchingStatus: "myRequest", + friendId: receiver.id, + name: receiver.name, + thumbnail: receiver.pictureUrl, + lastMsg: lastMessage, + }; + return overview; + }), + ); + const sharedRooms: { id: number; name: string; @@ -65,26 +111,12 @@ export async function getOverview( return overview; }); - // リクエスター (友達申請者) のオーバービュー作成 - const requesterOverview = await Promise.all( - requester.value.map(async (requester) => { - const lastMessageResult = await getLastMessage(user, requester.id); - const lastMessage = lastMessageResult.ok - ? lastMessageResult.value - : undefined; - const overview: DMOverview = { - isDM: true, - isFriend: false, - friendId: requester.id, - name: requester.name, - thumbnail: requester.pictureUrl, - lastMsg: lastMessage, - }; - return overview; - }), - ); - - return Ok([...shared, ...dm, ...requesterOverview]); + return Ok([ + ...matchingOverview, + ...senderOverview, + ...receiverOverview, + ...shared, + ]); } catch (e) { return Err(e); } diff --git a/server/src/functions/chat.ts b/server/src/functions/chat.ts index 839a6a54..1e748918 100644 --- a/server/src/functions/chat.ts +++ b/server/src/functions/chat.ts @@ -61,17 +61,17 @@ export async function sendDM( } export async function getDM( - requester: UserID, - _with: UserID, + user: UserID, + friend: UserID, ): Promise> { - const rel = await getRelation(requester, _with); + const rel = await getRelation(user, friend); if (!rel.ok || rel.value.status === "REJECTED") return http.forbidden("cannot send to rejected-friend"); - const room = await db.getDMbetween(requester, _with); + const room = await db.getDMbetween(user, friend); if (!room.ok) return http.internalError(); - const friendData = await getUserByID(_with); + const friendData = await getUserByID(friend); if (!friendData.ok) return http.notFound("friend not found"); const personalized: PersonalizedDMRoom & DMRoom = { @@ -80,9 +80,9 @@ export async function getDM( matchingStatus: rel.value.status === "MATCHED" ? "matched" - : rel.value.status === "PENDING" + : rel.value.sendingUserId === user //どっちが送ったリクエストなのかを判定 ? "myRequest" - : "otherRequest", //Fix Me: これだとREJECTEDの時に、matchingStatus = otherRequest になってしまう。本当はちゃんと判別しないといけない。呼び出し口でこれがちゃんと使われてないので、無視している。 + : "otherRequest", ...room.value, }; diff --git a/web/components/chat/RoomList.tsx b/web/components/chat/RoomList.tsx index 154c9594..710feb3c 100644 --- a/web/components/chat/RoomList.tsx +++ b/web/components/chat/RoomList.tsx @@ -3,7 +3,6 @@ import { Box, List, Typography } from "@mui/material"; import type { RoomOverview } from "common/types"; import { useRouter } from "next/navigation"; -import request from "~/api/request"; import { HumanListItem } from "../human/humanListItem"; type RoomListProps = { @@ -35,7 +34,7 @@ export function RoomList(props: RoomListProps) {

{roomsData?.map((room) => { if (room.isDM) { - if (!room.isFriend) { + if (room.matchingStatus === "otherRequest") { return ( { - request.accept(room.friendId).then(() => location.reload()); - }} - onReject={() => { - request.reject(room.friendId).then(() => location.reload()); - }} + statusMessage="リクエストを受けました" /> ); } + if (room.matchingStatus === "myRequest") { + return ( + { + e.stopPropagation(); + navigateToRoom(room); + }} + > + + + ); + } + // if (room.matchingStatus === "matched") return ( - {!room.isFriend && ( + {room.matchingStatus !== "matched" && ( )} @@ -258,11 +263,17 @@ export function RoomWindow(props: Props) { type FloatingMessageProps = { message: string; - friendId: UserID; + friendId: number; + showButtons: boolean; }; -const FloatingMessage = ({ message, friendId }: FloatingMessageProps) => { +const FloatingMessage = ({ + message, + friendId, + showButtons, +}: FloatingMessageProps) => { const router = useRouter(); + return (
{ }} >

{message}

- {/* biome-ignore lint/a11y/useButtonType: */} - - {/* biome-ignore lint/a11y/useButtonType: */} - + {showButtons && ( +
+ {/* biome-ignore lint/a11y/useButtonType: */} + + {/* biome-ignore lint/a11y/useButtonType: */} + +
+ )}
); From 9ff61178b2157d1c605ac1e6ebcb01554ef0c6ba Mon Sep 17 00:00:00 2001 From: aster <137767097+aster-void@users.noreply.github.com> Date: Mon, 2 Dec 2024 18:51:06 +0900 Subject: [PATCH 8/8] change floating message --- web/components/chat/RoomWindow.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/web/components/chat/RoomWindow.tsx b/web/components/chat/RoomWindow.tsx index 75e05fd2..75a1a9bd 100644 --- a/web/components/chat/RoomWindow.tsx +++ b/web/components/chat/RoomWindow.tsx @@ -160,11 +160,7 @@ export function RoomWindow(props: Props) { <> {room.matchingStatus !== "matched" && (