Skip to content

Commit eed497a

Browse files
authored
リクエストをキャンセルする (#390)
<!--変更したい場合は、`/.github/pull_request_template.md` を修正して下さい。--> # PRの概要 <!-- 変更の目的 もしくは 関連する Issue 番号 --> <!-- 以下のように書くと Issue にリンクでき、マージ時に自動で Issue を閉じられる--> <!-- closes #1 --> ## 具体的な変更内容 <!-- ビューの変更がある場合はスクショによる比較などがあるとわかりやすい --> ## 影響範囲 <!-- この関数を変更したのでこの機能にも影響がある、など --> ## 動作要件 <!-- 動作に必要な 環境変数 / 依存関係 / DBの更新 など --> ## 補足 <!-- レビューをする際に見てほしい点、ローカル環境で試す際の注意点、など --> ## レビューリクエストを出す前にチェック! - [ ] 改めてセルフレビューしたか - [ ] 手動での動作検証を行ったか - [ ] server の機能追加ならば、テストを書いたか - 理由: 書いた | server の機能追加ではない - [ ] 間違った使い方が存在するならば、それのドキュメントをコメントで書いたか - 理由: 書いた | 間違った使い方は存在しない - [ ] わかりやすいPRになっているか <!-- レビューリクエスト後は、Slackでもメンションしてお願いすることを推奨します。 -->
1 parent 835c45b commit eed497a

File tree

6 files changed

+90
-48
lines changed

6 files changed

+90
-48
lines changed

server/src/database/requests.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,27 @@ export async function rejectRequest(
9090
}
9191
}
9292

93+
export async function cancelRequest(
94+
senderId: UserID,
95+
receiverId: UserID,
96+
): Promise<Result<void>> {
97+
try {
98+
return prisma.relationship
99+
.delete({
100+
where: {
101+
sendingUserId_receivingUserId: {
102+
sendingUserId: senderId,
103+
receivingUserId: receiverId,
104+
},
105+
},
106+
})
107+
.then(() => Ok(undefined))
108+
.catch((err) => Err(err));
109+
} catch (err) {
110+
return Err(err);
111+
}
112+
}
113+
93114
//ユーザーへのリクエストを探す 俺をリクエストしているのは誰だ
94115
export async function getPendingRequestsToUser(
95116
userId: UserID,

server/src/router/requests.ts

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { UserID } from "../common/types";
44
import { safeParseInt } from "../common/lib/result/safeParseInt";
55
import {
66
approveRequest,
7+
cancelRequest,
78
rejectRequest,
89
sendRequest,
910
} from "../database/requests";
@@ -12,27 +13,6 @@ import { safeGetUserId } from "../firebase/auth/db";
1213

1314
const router = express.Router();
1415

15-
// I'm pretty sure that this is not used, but I'm not confident enough to delete this
16-
// 特定のユーザ同士が送信・受信したマッチリクエストの取得
17-
// router.get("/id/:matchId", async (req: Request, res: Response) => {
18-
// const { senderId, receiverId } = req.query;
19-
// if (!senderId && !receiverId) {
20-
// return res
21-
// .status(400)
22-
// .json({ error: "SenderID or ReceiverID is required" });
23-
// }
24-
// try {
25-
// const requests: Relationship[] = await getRequestsByUserId({
26-
// senderId: senderId ? parseInt(senderId as string) : undefined,
27-
// receiverId: receiverId ? parseInt(receiverId as string) : undefined,
28-
// });
29-
// res.status(200).json(requests);
30-
// } catch (error) {
31-
// console.error("Error fetching requests:", error);
32-
// res.status(500).json({ error: "Failed to fetch requests" });
33-
// }
34-
// });
35-
3616
// リクエストの送信
3717
router.put("/send/:receiverId", async (req: Request, res: Response) => {
3818
const receiverId = safeParseInt(req.params.receiverId);
@@ -70,6 +50,23 @@ router.put("/accept/:senderId", async (req: Request, res: Response) => {
7050
}
7151
});
7252

53+
router.put("/cancel/:opponentId", async (req: Request, res: Response) => {
54+
const opponentId = safeParseInt(req.params.opponentId);
55+
if (!opponentId.ok) return res.status(400).send("bad param encoding");
56+
57+
const requesterId = await safeGetUserId(req);
58+
if (!requesterId.ok) return res.status(401).send("auth error");
59+
60+
const result = await cancelRequest(requesterId.value, opponentId.value);
61+
62+
switch (result.ok) {
63+
case true:
64+
return res.status(204).send();
65+
case false:
66+
return res.status(500).send();
67+
}
68+
});
69+
7370
// リクエストの拒否
7471
router.put("/reject/:opponentId", async (req: Request, res: Response) => {
7572
const opponentId = safeParseInt(req.params.opponentId);

web/src/api/internal/endpoints.ts

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { CourseID, Day, GUID } from "../../common/types";
22
import type { MessageID, ShareRoomID } from "../../common/types";
33

4-
const origin: string | null = import.meta.env.VITE_API_ENDPOINT;
4+
export const origin: string | null = import.meta.env.VITE_API_ENDPOINT;
55
if (!origin) throw new Error("import.meta.env.VITE_API_ENDPOINT not found!");
66

77
// TODO: de-export this and use one from /common
@@ -16,7 +16,7 @@ export type UserID = number;
1616
* - 400: not found.
1717
* - 500: internal error.
1818
**/
19-
const user = (userId: UserID) => {
19+
export const user = (userId: UserID) => {
2020
return `${origin}/users/id/${userId}`;
2121
};
2222

@@ -36,7 +36,7 @@ const user = (userId: UserID) => {
3636
* - 500: internal error.
3737
*
3838
**/
39-
const users = `${origin}/users`;
39+
export const users = `${origin}/users`;
4040

4141
/**
4242
* [v] 実装済み
@@ -61,7 +61,7 @@ const users = `${origin}/users`;
6161
* - 500: internal error.
6262
*
6363
**/
64-
const me = `${origin}/users/me`;
64+
export const me = `${origin}/users/me`;
6565

6666
/**
6767
* [v] 実装済み
@@ -72,7 +72,7 @@ const me = `${origin}/users/me`;
7272
* - 401: unauthorized.
7373
* - 500: internal error.
7474
**/
75-
const matchedUsers = `${origin}/users/matched`;
75+
export const matchedUsers = `${origin}/users/matched`;
7676

7777
/**
7878
* [v] 実装済み
@@ -83,7 +83,7 @@ const matchedUsers = `${origin}/users/matched`;
8383
* - 401: unauthorized.
8484
* - 500: internal error.
8585
**/
86-
const pendingRequestsToMe = `${origin}/users/pending/to-me`;
86+
export const pendingRequestsToMe = `${origin}/users/pending/to-me`;
8787

8888
/**
8989
* [v] 実装済み
@@ -94,7 +94,7 @@ const pendingRequestsToMe = `${origin}/users/pending/to-me`;
9494
* - 401: unauthorized.
9595
* - 500: internal error.
9696
**/
97-
const pendingRequestsFromMe = `${origin}/users/pending/from-me`;
97+
export const pendingRequestsFromMe = `${origin}/users/pending/from-me`;
9898

9999
/**
100100
* [v] 実装済み
@@ -105,7 +105,7 @@ const pendingRequestsFromMe = `${origin}/users/pending/from-me`;
105105
* - 400: not found.
106106
* - 500: internal error.
107107
**/
108-
const userByGUID = (guid: GUID) => {
108+
export const userByGUID = (guid: GUID) => {
109109
return `${origin}/users/guid/${guid}`;
110110
};
111111

@@ -118,7 +118,7 @@ const userByGUID = (guid: GUID) => {
118118
* - 404: no, user doesn't exist.
119119
* - 500: internal error.
120120
**/
121-
const userExists = (guid: GUID) => {
121+
export const userExists = (guid: GUID) => {
122122
return `${origin}/users/exists/${guid}`;
123123
};
124124

@@ -132,7 +132,7 @@ const userExists = (guid: GUID) => {
132132
* (not implemented at server)
133133
* - 500: internal error.
134134
**/
135-
const match = (opponentID: UserID) => {
135+
export const match = (opponentID: UserID) => {
136136
return `${origin}/matches/${opponentID}`;
137137
};
138138

@@ -146,7 +146,7 @@ const match = (opponentID: UserID) => {
146146
* - 401: unauthorized.
147147
* - 500: internal error.
148148
**/
149-
const matches = `${origin}/matches`;
149+
export const matches = `${origin}/matches`;
150150

151151
/**
152152
* [x] 実装済み
@@ -167,7 +167,7 @@ const matches = `${origin}/matches`;
167167
* - 401: unauthorized.
168168
* - 500: internal error.
169169
*/
170-
const coursesUserId = (userId: UserID) => {
170+
export const coursesUserId = (userId: UserID) => {
171171
return `${origin}/courses/userId/${userId}`;
172172
};
173173

@@ -194,7 +194,7 @@ const coursesUserId = (userId: UserID) => {
194194
* - 401: unauthorized.
195195
* - 500: internal error.
196196
*/
197-
const coursesMine = `${origin}/courses/mine`;
197+
export const coursesMine = `${origin}/courses/mine`;
198198

199199
/**
200200
* [v] 実装済み
@@ -205,7 +205,7 @@ const coursesMine = `${origin}/courses/mine`;
205205
* - 401: unauthorized.
206206
* - 500: internal error.
207207
*/
208-
const coursesMineOverlaps = (courseId: CourseID) => {
208+
export const coursesMineOverlaps = (courseId: CourseID) => {
209209
return `${origin}/courses/mine/overlaps/${courseId}`;
210210
};
211211

@@ -218,7 +218,7 @@ const coursesMineOverlaps = (courseId: CourseID) => {
218218
* - 401: unauthorized.
219219
* - 500: internal error.
220220
**/
221-
const coursesDayPeriod = (day: Day, period: number) => {
221+
export const coursesDayPeriod = (day: Day, period: number) => {
222222
return `${origin}/courses/day-period?day=${day}&period=${period}`;
223223
};
224224

@@ -230,10 +230,20 @@ const coursesDayPeriod = (day: Day, period: number) => {
230230
* - 401: unauthorized.
231231
* - 500: internal error.
232232
**/
233-
const sendRequest = (opponentId: UserID) => {
233+
export const sendRequest = (opponentId: UserID) => {
234234
return `${origin}/requests/send/${opponentId}`;
235235
};
236236

237+
/**
238+
* PUT -> cancel request that you already sent.
239+
* - status:
240+
* - 204: Successfully
241+
* - 401: Unauthorized
242+
* - 500: internal error
243+
**/
244+
export const cancelRequest = (opponentId: UserID) => {
245+
return `${origin}/requests/cancel/${opponentId}`;
246+
};
237247
/**
238248
* [v] 実装済み
239249
* PUT -> accept request.
@@ -242,7 +252,7 @@ const sendRequest = (opponentId: UserID) => {
242252
* - 403: (not implemented) Forbidden. he hasn't sent a request to you.
243253
* - 500: internal error.
244254
**/
245-
const acceptRequest = (opponentId: UserID) => {
255+
export const acceptRequest = (opponentId: UserID) => {
246256
return `${origin}/requests/accept/${opponentId}`;
247257
};
248258

@@ -255,7 +265,7 @@ const acceptRequest = (opponentId: UserID) => {
255265
* - 404: (not implemented) Not found. he hasn't sent a request to you.
256266
* - 500: internal error.
257267
**/
258-
const rejectRequest = (opponentId: UserID) => {
268+
export const rejectRequest = (opponentId: UserID) => {
259269
return `${origin}/requests/reject/${opponentId}`;
260270
};
261271

@@ -268,7 +278,7 @@ const rejectRequest = (opponentId: UserID) => {
268278
* - 401: unauthorized
269279
* - 500: internal error
270280
*/
271-
const roomOverview = `${origin}/chat/overview`;
281+
export const roomOverview = `${origin}/chat/overview`;
272282

273283
/**
274284
* []実装済み
@@ -281,7 +291,7 @@ const roomOverview = `${origin}/chat/overview`;
281291
* - 403: Forbidden
282292
* - 500: internal error
283293
**/
284-
const dmTo = (userId: UserID) => `${origin}/chat/dm/to/${userId}`;
294+
export const dmTo = (userId: UserID) => `${origin}/chat/dm/to/${userId}`;
285295

286296
/**
287297
* PUT -> start dm with userId. created one if none was found. authorized.
@@ -295,7 +305,7 @@ const dmTo = (userId: UserID) => `${origin}/chat/dm/to/${userId}`;
295305
* - 403: forbidden. you and the user are not matched yet.
296306
* - 500: internal error.
297307
**/
298-
const dmWith = (userId: UserID) => `${origin}/chat/dm/with/${userId}`;
308+
export const dmWith = (userId: UserID) => `${origin}/chat/dm/with/${userId}`;
299309

300310
/**
301311
* POST -> Create a room. authenticated
@@ -307,14 +317,15 @@ const dmWith = (userId: UserID) => `${origin}/chat/dm/with/${userId}`;
307317
* - 403: forbidden (cannot invite non-friends)
308318
* - 500: internal error
309319
**/
310-
const sharedRooms = `${origin}/chat/shared`;
320+
export const sharedRooms = `${origin}/chat/shared`;
311321

312322
/** authorized
313323
* GET -> Get info of a room (including the message log).
314324
* PATCH -> update room info. (excluding the message log).
315325
* - body: UpdateRoom
316326
**/
317-
const sharedRoom = (roomId: ShareRoomID) => `${origin}/chat/shared/${roomId}`;
327+
export const sharedRoom = (roomId: ShareRoomID) =>
328+
`${origin}/chat/shared/${roomId}`;
318329

319330
/** POST: invite. authorized
320331
* - body: UserID[]
@@ -324,25 +335,25 @@ const sharedRoom = (roomId: ShareRoomID) => `${origin}/chat/shared/${roomId}`;
324335
* - 403: forbidden (cannot invite non-friends)
325336
* - 500: internal error
326337
**/
327-
const roomInvite = (roomId: ShareRoomID) =>
338+
export const roomInvite = (roomId: ShareRoomID) =>
328339
`${origin}/chat/shared/id/${roomId}/invite`;
329340

330341
/**
331342
* PATCH: authorized body=SendMessage
332343
* DELETE: authorized
333344
**/
334-
const message = (messageId: MessageID) =>
345+
export const message = (messageId: MessageID) =>
335346
`${origin}/chat/messages/id/${messageId}`;
336347

337348
/**
338349
* GET: get profile picture of URL (this is usually hard-encoded in pictureURL so this variable is barely used)
339350
*/
340-
const pictureOf = (guid: GUID) => `${origin}/picture/${guid}`;
351+
export const pictureOf = (guid: GUID) => `${origin}/picture/${guid}`;
341352

342353
/**
343354
* POST: update my profile picture.
344355
*/
345-
const picture = `${origin}/picture`;
356+
export const picture = `${origin}/picture`;
346357

347358
export default {
348359
user,
@@ -360,6 +371,7 @@ export default {
360371
sendRequest,
361372
acceptRequest,
362373
rejectRequest,
374+
cancelRequest,
363375
coursesUserId,
364376
coursesDayPeriod,
365377
roomOverview,

web/src/api/request.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ export async function send(receiverId: UserID) {
77
return res.text();
88
}
99

10+
export async function cancel(receiverId: UserID) {
11+
const res = await credFetch("PUT", endpoints.cancelRequest(receiverId));
12+
return await res.text();
13+
}
14+
1015
//相手からのリクエストを拒否する
1116
export async function reject(opponentID: UserID) {
1217
const res = await credFetch("PUT", endpoints.rejectRequest(opponentID));

web/src/components/human/humanListItem.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ type HumanListItemProps = {
1919
onOpen?: (user: { id: number; name: string; pictureUrl: string }) => void;
2020
onAccept?: (id: number) => void;
2121
onReject?: (id: number) => void;
22+
onCancel?: (id: number) => void;
2223
hasDots?: boolean;
2324
dotsActions?: object;
2425
};
@@ -33,6 +34,7 @@ export function HumanListItem(props: HumanListItemProps) {
3334
onOpen,
3435
onAccept,
3536
onReject,
37+
onCancel,
3638
hasDots,
3739
} = props;
3840
const handleDeleteClick = () => {
@@ -51,6 +53,7 @@ export function HumanListItem(props: HumanListItemProps) {
5153
<Stack direction="row" spacing={1}>
5254
{onAccept && <Button onClick={() => onAccept(id)}>承認</Button>}
5355
{onReject && <Button onClick={() => onReject(id)}>拒否</Button>}
56+
{onCancel && <Button onClick={() => onCancel(id)}>キャンセル</Button>}
5457
</Stack>
5558
}
5659
sx={{

web/src/components/match/myRequests.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Box } from "@mui/material";
22
import { List } from "@mui/material";
33
import hooks from "../../api/hooks";
4+
import * as request from "../../api/request";
45
import FullScreenCircularProgress from "../common/FullScreenCircularProgress";
56
import { useModal } from "../common/modal/ModalProvider";
67
import { HumanListItem } from "../human/humanListItem";
@@ -33,6 +34,9 @@ export default function MyReq() {
3334
name={receivingUser.name}
3435
pictureUrl={receivingUser.pictureUrl}
3536
onOpen={() => openModal(receivingUser)}
37+
onCancel={(id) => {
38+
request.cancel(id);
39+
}}
3640
/>
3741
))}
3842
</List>

0 commit comments

Comments
 (0)