diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 969ca1dc..80841633 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -86,7 +86,7 @@ jobs: - uses: actions/setup-node@v3 - uses: oven-sh/setup-bun@v2 - run: make prepare-deploy-web - - run: test `ls web/out | wc -l` != 0 + - run: test `ls web/.next | wc -l` != 0 deploy-test-server: name: Deploy Test (server) diff --git a/web/api/chat/chat.ts b/web/api/chat/chat.ts index b18a8e35..63788ea5 100644 --- a/web/api/chat/chat.ts +++ b/web/api/chat/chat.ts @@ -83,14 +83,22 @@ export async function sendDM( return res.json(); } -export async function getDM(friendId: UserID): Promise { +export async function getDM(friendId: UserID): Promise< + DMRoom & { + name: string; + thumbnail: string; + } +> { 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 = await res.json(); + const json: DMRoom & { + name: string; + thumbnail: string; + } = 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 new file mode 100644 index 00000000..1f717305 --- /dev/null +++ b/web/app/chat/[id]/page.tsx @@ -0,0 +1,38 @@ +"use client"; +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); + useEffect(() => { + (async () => { + const room = await chat.getDM(id); + setRoom(room); + })(); + }, [id]); + + return ( + <> +

idは{id}です。

+ {room ? :

データないよ

} + + ); +} diff --git a/web/app/chat/page.tsx b/web/app/chat/page.tsx index 66a344ed..ac10655a 100644 --- a/web/app/chat/page.tsx +++ b/web/app/chat/page.tsx @@ -1,10 +1,8 @@ "use client"; -import { useSearchParams } from "next/navigation"; import { Suspense } from "react"; import { useRoomsOverview } from "~/api/chat/hooks"; import RoomList from "~/components/chat/RoomList"; -import { RoomWindow } from "~/components/chat/RoomWindow"; import FullScreenCircularProgress from "~/components/common/FullScreenCircularProgress"; export default function Chat() { @@ -16,18 +14,8 @@ export default function Chat() { } function ChatListContent() { - const searchParams = useSearchParams(); - - const friendId = searchParams.get("friendId"); - const { state } = useRoomsOverview(); - - return friendId ? ( - <> -

Chat - friend Id: {friendId}

- - - ) : state.current === "loading" ? ( + return state.current === "loading" ? ( ) : state.current === "error" ? (

Error: {state.error.message}

diff --git a/web/components/chat/MessageInput.tsx b/web/components/chat/MessageInput.tsx index 06fbd08a..7d8119a6 100644 --- a/web/components/chat/MessageInput.tsx +++ b/web/components/chat/MessageInput.tsx @@ -1,27 +1,27 @@ -import type { DMOverview, SendMessage, UserID } from "common/types"; +import type { SendMessage, UserID } from "common/types"; import { parseContent } from "common/zod/methods"; import { useEffect, useState } from "react"; import { MdSend } from "react-icons/md"; type Props = { send: (to: UserID, m: SendMessage) => void; - room: DMOverview; + friendId: UserID; }; const crossRoomMessageState = new Map(); -export function MessageInput({ send, room }: Props) { +export function MessageInput({ send, friendId }: Props) { const [message, _setMessage] = useState(""); const [error, setError] = useState(null); function setMessage(m: string) { _setMessage(m); - crossRoomMessageState.set(room.friendId, m); + crossRoomMessageState.set(friendId, m); } useEffect(() => { - _setMessage(crossRoomMessageState.get(room.friendId) || ""); - }, [room.friendId]); + _setMessage(crossRoomMessageState.get(friendId) || ""); + }, [friendId]); function submit(e: React.FormEvent) { e.preventDefault(); @@ -34,7 +34,7 @@ export function MessageInput({ send, room }: Props) { } if (message.trim()) { - send(room.friendId, { content: message }); + send(friendId, { content: message }); setMessage(""); } } @@ -50,7 +50,7 @@ export function MessageInput({ send, room }: Props) { return; } if (message.trim()) { - send(room.friendId, { content: message }); + send(friendId, { content: message }); setMessage(""); } } diff --git a/web/components/chat/RoomHeader.tsx b/web/components/chat/RoomHeader.tsx index 17b2b495..bff1fdc2 100644 --- a/web/components/chat/RoomHeader.tsx +++ b/web/components/chat/RoomHeader.tsx @@ -1,10 +1,22 @@ -import type { DMOverview } from "common/types"; import Link from "next/link"; import { MdArrowBack } from "react-icons/md"; import UserAvatar from "../human/avatar"; type Props = { - room: DMOverview; + room: { + isDM: true; + id: number; + messages: { + id: number; + creator: number; + createdAt: Date; + content: string; + edited: boolean; + }[]; + } & { + name: string; + thumbnail: string; + }; }; export function RoomHeader(props: Props) { diff --git a/web/components/chat/RoomList.tsx b/web/components/chat/RoomList.tsx index 3bc6fea3..1c6b6fad 100644 --- a/web/components/chat/RoomList.tsx +++ b/web/components/chat/RoomList.tsx @@ -12,15 +12,8 @@ type RoomListProps = { export function RoomList(props: RoomListProps) { const { roomsData } = props; const router = useRouter(); - - /** - * FIXME: - * React Router が使えなくなったので、一時的に room の情報を URL に載せることで状態管理 - */ const navigateToRoom = (room: Extract) => { - router.push( - `./?friendId=${room.friendId}&roomData=${encodeURIComponent(JSON.stringify(room))}`, - ); + router.push(`/chat/${room.friendId}`); }; return ( diff --git a/web/components/chat/RoomWindow.tsx b/web/components/chat/RoomWindow.tsx index 23113e97..d8b211ba 100644 --- a/web/components/chat/RoomWindow.tsx +++ b/web/components/chat/RoomWindow.tsx @@ -1,12 +1,6 @@ -import type { - DMOverview, - Message, - MessageID, - SendMessage, - UserID, -} from "common/types"; +"use client"; +import type { Message, MessageID, SendMessage, UserID } from "common/types"; import type { Content } from "common/zod/types"; -import { useSearchParams } from "next/navigation"; import { useSnackbar } from "notistack"; import { useCallback, useEffect, useRef, useState } from "react"; import * as chat from "~/api/chat/chat"; @@ -19,13 +13,25 @@ import { socket } from "../data/socket"; import { MessageInput } from "./MessageInput"; import { RoomHeader } from "./RoomHeader"; -export function RoomWindow() { - // FIXME: React Router が使えなくなったので、一時的に room の情報を URL に載せることで状態管理 - const searchParams = useSearchParams(); - const roomData = searchParams.get("roomData"); - const room = roomData - ? (JSON.parse(decodeURIComponent(roomData)) as DMOverview) - : null; +type Props = { + friendId: UserID; + room: { + id: number; + messages: { + id: number; + creator: number; + createdAt: Date; + content: string; + edited: boolean; + }[]; + isDM: true; + } & { + name: string; + thumbnail: string; + }; +}; +export function RoomWindow(props: Props) { + const { friendId, room } = props; if (!room) { return ( @@ -36,7 +42,7 @@ export function RoomWindow() { const { state: { data: myId }, } = useMyID(); - const { state, reload, write } = useMessages(room.friendId); + const { state, reload, write } = useMessages(friendId); const [messages, setMessages] = useState(state.data); useEffect(() => { @@ -73,7 +79,7 @@ export function RoomWindow() { const idToken = await getIdToken(); socket.emit("register", idToken); socket.on("newMessage", async (msg: Message) => { - if (msg.creator === room.friendId) { + if (msg.creator === friendId) { appendLocalMessage(msg); } else { const creator = await user.get(msg.creator); @@ -87,7 +93,7 @@ export function RoomWindow() { } }); socket.on("updateMessage", async (msg: Message) => { - if (msg.creator === room.friendId) { + if (msg.creator === friendId) { updateLocalMessage(msg); } }); @@ -104,6 +110,7 @@ export function RoomWindow() { }; }, [ room, + friendId, enqueueSnackbar, appendLocalMessage, updateLocalMessage, @@ -142,11 +149,11 @@ export function RoomWindow() { const editedMessage = await chat.updateMessage( message, { content }, - room.friendId, + friendId, ); updateLocalMessage(editedMessage); }, - [updateLocalMessage, room.friendId], + [updateLocalMessage, friendId], ); const cancelEdit = useCallback(() => { @@ -167,7 +174,7 @@ export function RoomWindow() {
-
+
{messages && messages.length > 0 ? (
{messages.map((m) => ( @@ -225,7 +232,7 @@ export function RoomWindow() { { label: "削除", color: "red", - onClick: () => deleteMessage(m.id, room.friendId), + onClick: () => deleteMessage(m.id, friendId), alert: true, messages: { buttonMessage: "削除", @@ -248,8 +255,8 @@ export function RoomWindow() {
)}
-
- +
+
); diff --git a/web/firebase/auth/lib.ts b/web/firebase/auth/lib.ts index dbeeb190..1cae6c90 100644 --- a/web/firebase/auth/lib.ts +++ b/web/firebase/auth/lib.ts @@ -17,7 +17,8 @@ const token = new Promise((resolve) => { }); export async function getIdToken(): Promise { - return await token; + const toke = await token; + return toke; } type RequestMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"; diff --git a/web/next.config.mjs b/web/next.config.mjs index 4350313c..4678774e 100644 --- a/web/next.config.mjs +++ b/web/next.config.mjs @@ -1,6 +1,4 @@ /** @type {import('next').NextConfig} */ -const nextConfig = { - output: "export", -}; +const nextConfig = {}; export default nextConfig;