diff --git a/common/zod/schema.ts b/common/zod/schema.ts index eb92d704..1a597ca5 100644 --- a/common/zod/schema.ts +++ b/common/zod/schema.ts @@ -136,3 +136,33 @@ export type MYDATA = Omit & { allowNotifications: boolean; allowPeriodicNotifications: boolean; }; + +// chat types, maybe consider creating schemas for this? +type Room = { + id: string; + members: { + user: { + id: string; + name: string; + imageUrl: string | null; + }; + }[]; +}; +export type RoomPreview = Room & { + lastMessage: string | null; +}; +export type Message = { + id: string; + roomId: string; + senderId: string; + content: string; + createdAt: Date; + isPhoto: boolean; + isEdited: boolean; + sender: { + name: string; + }; +}; +export type ContentfulRoom = Room & { + messages: Message[]; +}; diff --git a/server/routes/chat.ts b/server/routes/chat.ts index 8aee2afd..3319fc17 100644 --- a/server/routes/chat.ts +++ b/server/routes/chat.ts @@ -6,16 +6,7 @@ import { streamSSE } from "hono/streaming"; import z from "zod"; import { prisma } from "../config/prisma.ts"; -// TODO: use types from schema -import type { Message as PrismaMessage } from "@prisma/client"; -export type { Room } from "@prisma/client"; -export type Message = PrismaMessage & { - sender: { - name: string; - }; -}; - -import { MESSAGE_MAX_LENGTH } from "common/zod/schema.ts"; +import { MESSAGE_MAX_LENGTH, type Message } from "common/zod/schema.ts"; import { HTTPException } from "hono/http-exception"; import { getUserID } from "../auth/func.ts"; import { onMessageSend } from "../email/hooks/onMessageSend.ts"; @@ -192,7 +183,14 @@ const router = new Hono() }, }); - return c.json(resp, 200); + return c.json( + resp.map((it) => ({ + ...it, + lastMessage: it.messages[0]?.content ?? null, + messages: undefined, + })), + 200, + ); }) // ## room data .get( @@ -305,7 +303,7 @@ const router = new Hono() }, }, }); - if (!resp) return c.json({ error: "room not found" }, 404); + if (!resp) throw new HTTPException(404, { message: "room not found" }); return c.json(resp, 200); }, ) @@ -332,7 +330,7 @@ const router = new Hono() const { room: roomId } = c.req.valid("param"); const json = c.req.valid("json"); - const message: PrismaMessage = { + const message = { ...json, roomId, id: randomUUIDv7(), @@ -379,7 +377,9 @@ const router = new Hono() } })(); const resp = await prisma.message.create({ - data: message, + data: { + ...message, + }, }); return c.json(resp, 201); }, diff --git a/server/routes/users/me.ts b/server/routes/users/me.ts index e7caef57..06dc07e6 100644 --- a/server/routes/users/me.ts +++ b/server/routes/users/me.ts @@ -65,18 +65,25 @@ const route = new Hono() : {}), }, include: { - fluentLanguages: { - select: { language: true }, - }, - learningLanguages: { - select: { language: true }, - }, campus: { include: { university: true, }, }, division: true, + motherLanguage: true, + fluentLanguages: { + include: { + language: true, + }, + }, + learningLanguages: { + include: { + language: true, + }, + }, + marking: true, + markedAs: true, }, }); diff --git a/tsconfig.json b/tsconfig.json index 2390076c..87d7d9c8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,8 +18,8 @@ "skipLibCheck": true, "noFallthroughCasesInSwitch": true, "noUncheckedIndexedAccess": true, - "noUnusedLocals": true, - "noUnusedParameters": true, + "noUnusedLocals": false, + "noUnusedParameters": false, // "noUncheckedSideEffectImports": true, // ? "incremental": true, diff --git a/web/src/app/[locale]/(auth)/chat/[id]/MessageInput.tsx b/web/src/app/[locale]/(auth)/chat/[id]/MessageInput.tsx new file mode 100644 index 00000000..d1bab7ca --- /dev/null +++ b/web/src/app/[locale]/(auth)/chat/[id]/MessageInput.tsx @@ -0,0 +1,63 @@ +"use client"; + +import { client } from "@/client"; +import { useAuthContext } from "@/features/auth/providers/AuthProvider"; +import clsx from "clsx"; +import { MESSAGE_MAX_LENGTH } from "common/zod/schema"; +import { useState } from "react"; +import { AiOutlineSend } from "react-icons/ai"; + +export function MessageInput({ roomId }: { roomId: string }) { + const { idToken: Authorization } = useAuthContext(); + const [input, setInput] = useState(""); + const [submitting, setSubmitting] = useState(false); + const isSendButtonDisabled = submitting || input === ""; + + const handleSubmit = async (ev: React.FormEvent) => { + ev.preventDefault(); + if (submitting) return; + setSubmitting(true); + setInput(""); + await client.chat.rooms[":room"].messages.$post({ + header: { Authorization }, + param: { + room: roomId, + }, + json: { + content: input, + isPhoto: false, + }, + }); + setSubmitting(false); + }; + + return ( +
+
+
+