Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,19 @@ dev-db:
-e POSTGRES_DB=database \
postgres:alpine
@echo "Waiting for PostgreSQL to be ready..."
@sleep 5 # PostgreSQLが起動するまでの待機(必要に応じて調整)
@sleep 2 # PostgreSQLが起動するまでの待機(必要に応じて調整)
@until docker exec postgres pg_isready -U user -d database; do \
echo "Waiting for PostgreSQL to be ready..."; \
sleep 1; \
done
@echo "PostgreSQL is ready. Running seed..."
@cd server; bunx prisma generate; bunx prisma db push; cd ..
@make seed;
@cd server; \
if command -v prisma; then\
prisma generate; prisma db push;\
else \
bunx prisma generate; bunx prisma db push;\
fi
@make seed
@echo "Seeding completed."

# Sync (install/update packages, generate prisma, etc)
Expand All @@ -88,7 +93,7 @@ sync-web:

sync-server:
cd server; bun install --frozen-lockfile
cd server; bunx prisma generate
cd server; if command -v prisma; then prisma generate; else bunx prisma generate; fi
# copy .env.sample -> .env only if .env is not there

sync-root:
Expand Down
1 change: 1 addition & 0 deletions common/zod/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ export const DMOverviewSchema = z.object({
name: NameSchema,
thumbnail: z.string(),
lastMsg: MessageSchema.optional(),
unreadMessages: z.number(),
});

export const SharedRoomOverviewSchema = z.object({
Expand Down
2 changes: 2 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
biome
pkg-config
openssl
lefthook
pkgs.prisma
] ++ [
rust-pkgs
];
Expand Down
3 changes: 2 additions & 1 deletion server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,6 @@
"globals": "^15.8.0",
"prisma": "^5.11.0",
"typescript": "^5.4.5"
}
},
"trustedPackages": ["prisma"]
}
3 changes: 2 additions & 1 deletion server/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ model InterestSubject {
id Int @id @default(autoincrement())
name String
group String // such as Computer Science | name = ML
Interest Interest[] // ignore this

@@unique([name, group])
Interest Interest[] // ignore this
}

// User->Interest->InterestSubject
Expand Down Expand Up @@ -138,6 +138,7 @@ model Message {
createdAt DateTime @default(now()) // @readonly
edited Boolean @default(false)
content String
read Boolean
relation Relationship? @relation(fields: [relationId], references: [id], onDelete: Cascade)
relationId Int?
sharedRoom SharedRoom? @relation(fields: [sharedRoomId], references: [id], onDelete: Cascade)
Expand Down
132 changes: 87 additions & 45 deletions server/src/database/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
getPendingRequestsFromUser,
getPendingRequestsToUser,
} from "./requests";
import { getUserByID } from "./users";

export async function getOverview(
user: UserID,
Expand All @@ -35,59 +36,17 @@ export async function getOverview(

//マッチングしている人のオーバービュー
const matchingOverview = await Promise.all(
matched.value.map(async (friend) => {
const lastMessageResult = await getLastMessage(user, friend.id);
const lastMessage = lastMessageResult.ok
? lastMessageResult.value
: undefined;
const overview: DMOverview = {
isDM: true,
matchingStatus: "matched",
friendId: friend.id,
name: friend.name,
thumbnail: friend.pictureUrl,
lastMsg: lastMessage,
};
return overview;
}),
matched.value.map(async (m) => getOverviewBetween(user, m.id)),
);

//自分にリクエストを送ってきた人のオーバービュー
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;
}),
senders.value.map((s) => getOverviewBetween(user, s.id)),
);

//自分がリクエストを送った人のオーバービュー
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;
}),
receivers.value.map((r) => getOverviewBetween(user, r.id)),
);

const sharedRooms: {
Expand Down Expand Up @@ -130,6 +89,67 @@ export async function getOverview(
}
}

async function getOverviewBetween(
user: number,
other: number,
): Promise<DMOverview> {
const relR = await getRelation(user, other);
if (!relR.ok) throw relR.error;
const rel = relR.value;

const friendId =
rel.receivingUserId === user ? rel.sendingUserId : rel.receivingUserId;
const lastMessage = getLastMessage(user, friendId).then((val) => {
if (val.ok) return val.value;
return undefined;
});
const unreadCount = unreadMessages(user, rel.id).then((val) => {
if (val.ok) return val.value;
throw val.error;
});
const friend = await getUserByID(friendId).then((val) => {
if (val.ok) return val.value;
throw val.error;
});
const overview: DMOverview = {
isDM: true,
matchingStatus: "matched",
friendId: friendId,
name: friend.name,
thumbnail: friend.pictureUrl,
lastMsg: await lastMessage,
unreadMessages: await unreadCount,
};
return overview;
}
export async function markAsRead(
rel: RelationshipID,
reader: UserID,
message: MessageID,
) {
const val = {
readerId: reader,
messageId: message,
relationId: rel,
};
return await prisma.message.updateMany({
where: {
id: {
lte: message,
},
relationId: rel,
creator: {
not: {
equals: reader,
},
},
},
data: {
read: true,
},
});
}

/**
* DM の送信
* 送信者の id は呼び出す側で指定すること
Expand All @@ -142,6 +162,7 @@ export async function sendDM(
const message = await prisma.message.create({
data: {
relationId: relation,
read: false,
...content,
},
});
Expand Down Expand Up @@ -372,3 +393,24 @@ export async function getLastMessage(
return Err(e);
}
}

// only works on Relationship (= DM) for now.
export async function unreadMessages(userId: UserID, roomId: RelationshipID) {
try {
// FIXME: this makes request twice to the database. it's not efficient.
const unreadMessages = await prisma.message.count({
where: {
read: false,
relationId: roomId,
creator: {
not: {
equals: userId,
},
},
},
});
return Ok(unreadMessages);
} catch (e) {
return Err(e);
}
}
2 changes: 1 addition & 1 deletion server/src/database/matches.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export async function getRelation(
u2: UserID,
): Promise<Result<Relationship>> {
try {
// TODO!!!! FIXME!!!!!! FIX THIS findMany!!!!!
// FIXME: fix this findMany
const rel = await prisma.relationship.findMany({
where: {
OR: [
Expand Down
23 changes: 23 additions & 0 deletions server/src/database/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,29 @@ export async function getMatchedUser(
}
}

export async function getMatchedRelations(
userId: UserID,
): Promise<Result<Relationship[]>> {
try {
const found = await prisma.relationship.findMany({
where: {
status: "MATCHED",
OR: [
{
sendingUserId: userId,
},
{
receivingUserId: userId,
},
],
},
});
return Ok(found);
} catch (e) {
return Err(e);
}
}

export async function matchWithMemo(userId: UserID) {
try {
const result = await prisma.relationship.create({
Expand Down
15 changes: 14 additions & 1 deletion server/src/router/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
} from "common/zod/schemas";
import express from "express";
import * as db from "../database/chat";
import { safeGetUserId } from "../firebase/auth/db";
import { getRelation } from "../database/matches";
import { getUserId, safeGetUserId } from "../firebase/auth/db";
import * as core from "../functions/chat";
import * as ws from "../lib/socket/socket";

Expand Down Expand Up @@ -56,6 +57,18 @@ router.get("/dm/with/:userid", async (req, res) => {
return res.status(result.code).send(result.body);
});

router.post("/mark-as-read/:rel/:messageId", async (req, res) => {
const user = await getUserId(req);
const message = Number.parseInt(req.params.messageId);
const rel = Number.parseInt(req.params.rel);
try {
await db.markAsRead(rel, user, message);
return res.status(200).end("ok");
} catch (err) {
return res.status(304).end("already marked");
}
});

// create a shared chat room.
router.post("/shared", async (req, res) => {
const user = await safeGetUserId(req);
Expand Down
17 changes: 16 additions & 1 deletion web/api/chat/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
Message,
MessageID,
PersonalizedDMRoom,
RelationshipID,
RoomOverview,
SendMessage,
ShareRoomID,
Expand All @@ -12,7 +13,7 @@ import type {
UserID,
} from "common/types";
import { ErrUnauthorized, credFetch } from "~/firebase/auth/lib";
import endpoints from "../internal/endpoints";
import * as endpoints from "../internal/endpoints";

/* TODO
import { UserID } from "common/types";
Expand All @@ -35,6 +36,20 @@ export async function deleteMessage(
);
}

export async function markAsRead(
relationId: RelationshipID,
messageId: MessageID,
) {
const res = await credFetch(
"POST",
endpoints.markAsRead(relationId, messageId),
);
if (res.status !== 200 && res.status !== 304)
throw new Error(
`on markAsRead(), expected status code of 200 or 304, but got ${res.status}`,
);
}

export async function updateMessage(
messageId: MessageID,
newMessage: SendMessage,
Expand Down
5 changes: 5 additions & 0 deletions web/api/internal/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,11 @@ export const rejectRequest = (opponentId: UserID) => {
return `${origin}/requests/reject/${opponentId}`;
};

/**
**/
export const markAsRead = (friendId: UserID, messageId: MessageID) => {
return `${origin}/chat/mark-as-read/${friendId}/${messageId}`;
};
/**
* []実装済み
* GET -> get personalized room overviews.
Expand Down
1 change: 1 addition & 0 deletions web/app/chat/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export default function Page({ params }: { params: { id: string } }) {
{room ? (
<RoomWindow friendId={id} room={room} />
) : (
// FIXME: this isn't an error when it's just loading
<p>
Sorry, an unexpected error has occurred.
<Link href="/home" className="text-blue-600">
Expand Down
3 changes: 3 additions & 0 deletions web/components/chat/RoomList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export function RoomList(props: RoomListProps) {
rollUpName={true}
lastMessage={room.lastMsg?.content}
statusMessage="リクエストを受けました"
unreadCount={room.unreadMessages}
/>
</Box>
);
Expand All @@ -72,6 +73,7 @@ export function RoomList(props: RoomListProps) {
rollUpName={true}
lastMessage={room.lastMsg?.content}
statusMessage="リクエスト中 メッセージを送りましょう!"
unreadCount={room.unreadMessages}
/>
</Box>
);
Expand All @@ -91,6 +93,7 @@ export function RoomList(props: RoomListProps) {
pictureUrl={room.thumbnail}
rollUpName={true}
lastMessage={room.lastMsg?.content}
unreadCount={room.unreadMessages}
/>
</Box>
);
Expand Down
Loading
Loading