Skip to content

Commit dfa0766

Browse files
authored
Merge pull request #14 from ItaloMedici/feature/13-change-socket-for-pooling
Feature/13 change socket for pooling
2 parents 3e321c3 + d9d863c commit dfa0766

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+774
-1043
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
2+
import { Button } from "@/components/ui/button";
3+
import { useBoard } from "@/context/board";
4+
import { cn } from "@/lib/utils";
5+
6+
const MAX_AVATAR_DISPLAY = 5;
7+
8+
export const BoardToolbar = () => {
9+
const { others, self, handleRevealCards, reveal, handleReset } = useBoard();
10+
11+
if (!others.length) {
12+
return null;
13+
}
14+
15+
const onRevealClick = () => {
16+
if (reveal) {
17+
handleReset();
18+
}
19+
handleRevealCards();
20+
};
21+
22+
const playersAvatar = () => {
23+
let avatarList = [self, ...others].map((player) => ({
24+
name: player.name
25+
.split(" ")
26+
.map((name) => name[0])
27+
.slice(0, 2)
28+
.join(""),
29+
imageUrl: player.imageUrl ?? undefined,
30+
}));
31+
32+
if (avatarList.length > MAX_AVATAR_DISPLAY) {
33+
avatarList = avatarList.slice(0, MAX_AVATAR_DISPLAY);
34+
avatarList.push({
35+
name: `+${others.length - MAX_AVATAR_DISPLAY}`,
36+
imageUrl: undefined,
37+
});
38+
}
39+
40+
return (
41+
<div className="flex items-center gap-[-4px]">
42+
{avatarList.map((player, index) => (
43+
<Avatar
44+
key={player.name}
45+
className={cn("border-2 border-white", {
46+
"-ml-2": index > 0,
47+
})}
48+
>
49+
<AvatarImage src={player.imageUrl ?? undefined} />
50+
<AvatarFallback className="text-xs bg-gradient-to-tr from-sky-300 to-gray-300 ">
51+
{player.name}
52+
</AvatarFallback>
53+
</Avatar>
54+
))}
55+
</div>
56+
);
57+
};
58+
59+
return (
60+
<div className=" border border-gray-200 p-2 rounded-xl flex items-center justify-between gap-2">
61+
<Button onClick={onRevealClick} size={"sm"}>
62+
{reveal ? "Iniciar outro jogo" : "Revelar cartas 👀"}
63+
</Button>
64+
65+
<Button variant={"ghost"} onClick={handleReset} size={"sm"}>
66+
Limpar
67+
</Button>
68+
69+
<hr className="w-[1px] border-l border-gray-200 h-full" />
70+
71+
{playersAvatar()}
72+
</div>
73+
);
74+
};

app/(dashboard)/room/[roomId]/_components/board.tsx

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
11
"use client";
22

3-
import { useBoard } from "@/context/board";
43
import { BoardNavbar } from "./board-navbar";
4+
import { BoardToolbar } from "./board-toolbar";
55
import { CardsPicker } from "./cards-picker";
6-
import { PlayersCards } from "./players-cards";
6+
import { Deck } from "./deck";
77

88
export function Board() {
9-
const { self, others } = useBoard();
10-
119
return (
12-
<div className="absolute inset-0 -z-10 h-full w-full bg-gray-50">
10+
<div className="absolute inset-0 -z-10 h-full w-full bg-gray-50 overflow-hidden">
1311
<BoardNavbar />
14-
<div className="flex h-full items-center justify-center px-6">
15-
<PlayersCards />
16-
</div>
17-
<div className="fixed w-full bottom-16 mx-auto px-6">
12+
<div className="flex h-full flex-col items-center justify-between gap-6 p-4 pt-20 pb-12">
13+
<BoardToolbar />
14+
<Deck />
1815
<CardsPicker />
1916
</div>
2017
</div>

app/(dashboard)/room/[roomId]/_components/cards-picker.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import { useBoard } from "@/context/board";
33
import { cn } from "@/lib/utils";
44

55
export const CardsPicker = () => {
6-
const { choiceOptions, handleChoice, self } = useBoard();
6+
const { choiceOptions, handleChoice, selfChoice } = useBoard();
77

8-
const isSelfOption = (option: string) => self.choice === option;
8+
const isSelfOption = (option: string) => selfChoice == option;
99

1010
return (
1111
<div className="flex flex-col items-center justify-center flex-wrap gap-6">
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { useBoard } from "@/context/board";
2+
import { PlayerCard } from "./player-card";
3+
4+
export const Deck = () => {
5+
const { others, self } = useBoard();
6+
7+
const players = [self, ...others];
8+
9+
if (!others.length) {
10+
return (
11+
<div className="flex items-center justify-center h-full">
12+
<p className="text-gray-500">Sozinho por aqui... 😴</p>
13+
{/* adicionar botão de convidar */}
14+
</div>
15+
);
16+
}
17+
18+
return (
19+
<div className="grid grid-cols-4 gap-4">
20+
{players.map((player) => (
21+
<PlayerCard key={player.id} player={player} />
22+
))}
23+
</div>
24+
);
25+
};

app/(dashboard)/room/[roomId]/_components/player-card.tsx

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,41 @@
11
import { NumericCard } from "@/components/card/numeric-card";
22
import { useBoard } from "@/context/board";
33
import { Player } from "@/lib/schemas/player";
4+
import { PlayerNotificationPopup } from "./player-notification-popup";
45

56
type PlayerCardProps = {
67
player: Player;
78
};
89

910
export const PlayerCard = ({ player }: PlayerCardProps) => {
10-
const { revealCards, self } = useBoard();
11+
const { reveal, self, selfChoice } = useBoard();
1112

1213
const isSelf = player.id === self.id;
1314

15+
const formatedChoice =
16+
reveal && player.choice
17+
? Buffer.from(player.choice, "base64").toString()
18+
: "";
19+
1420
return (
1521
<>
1622
{isSelf ? (
1723
<NumericCard
18-
value={player.choice}
19-
color={player.choice ? "primary" : "gray"}
24+
value={selfChoice}
25+
color={selfChoice ? "primary" : "gray"}
2026
size={"large"}
2127
label={"Você"}
2228
/>
2329
) : (
24-
<NumericCard
25-
value={revealCards ? player.choice : ""}
26-
color={revealCards && player.choice ? "primary" : "gray"}
27-
bgColor={revealCards ? "white" : player.choice ? "primary" : "gray"}
28-
size={"large"}
29-
label={player.name}
30-
/>
30+
<PlayerNotificationPopup player={player}>
31+
<NumericCard
32+
value={formatedChoice}
33+
color={reveal && player.choice ? "primary" : "gray"}
34+
bgColor={reveal ? "white" : player.choice ? "primary" : "gray"}
35+
size={"large"}
36+
label={player.name}
37+
/>
38+
</PlayerNotificationPopup>
3139
)}
3240
</>
3341
);
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { useBoard } from "@/context/board";
2+
import { Player } from "@/lib/schemas/player";
3+
import { cn } from "@/lib/utils";
4+
import { notificationIcons } from "@/messages/notification";
5+
import { EnumNotification } from "@/types/notifications";
6+
import {
7+
Popover,
8+
PopoverContent,
9+
PopoverPortal,
10+
PopoverTrigger,
11+
} from "@radix-ui/react-popover";
12+
import { ReactNode, useState } from "react";
13+
14+
export function PlayerNotificationPopup({
15+
children,
16+
player,
17+
}: {
18+
children: ReactNode;
19+
player: Player;
20+
}) {
21+
const { handleNotifyPlayer } = useBoard();
22+
const [open, setOpen] = useState(false);
23+
24+
const onNotifificationClick = (notification: EnumNotification) => {
25+
setOpen(false);
26+
if (player.choice) return;
27+
28+
handleNotifyPlayer(player.id, notification);
29+
};
30+
31+
return (
32+
<Popover open={open} onOpenChange={(open) => setOpen(open)}>
33+
<PopoverTrigger
34+
onClick={() => setOpen((prev) => !prev)}
35+
disabled={!!player.choice}
36+
>
37+
<div
38+
className={cn(
39+
"before:absolute before:top-1/2 before:-translate-x-1/2 before:-translate-y-full before:z-10 before:scale-0 before:hover:scale-100 before:transition-transform relative",
40+
{
41+
"before:content-['🔉']": !player.choice,
42+
"before:scale-100": open,
43+
}
44+
)}
45+
>
46+
{children}
47+
</div>
48+
</PopoverTrigger>
49+
<PopoverPortal>
50+
<PopoverContent side="top" sideOffset={10}>
51+
<div className="flex items-center rounded-full p-1 bg-white shadow-md gap-4 border border-gray-200">
52+
{Object.keys(notificationIcons).map((notification) => (
53+
<div
54+
key={notification}
55+
role="presentation"
56+
onClick={() =>
57+
onNotifificationClick(notification as EnumNotification)
58+
}
59+
className="p-1 w-10 cursor-pointer aspect-square rounded-full hover:bg-gray-100"
60+
>
61+
<span className="text-2xl flex items-center justify-center">
62+
{notificationIcons[notification as EnumNotification]}
63+
</span>
64+
</div>
65+
))}
66+
</div>
67+
</PopoverContent>
68+
</PopoverPortal>
69+
</Popover>
70+
);
71+
}

app/(dashboard)/room/[roomId]/_components/players-cards.tsx

Lines changed: 0 additions & 45 deletions
This file was deleted.
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
"use client";
22

33
import { BoardProvider } from "@/context/board";
4-
import { SocketClientProvider } from "@/context/socket-client";
54

65
type RoomProps = {
76
children: React.ReactNode;
87
roomId: string;
98
};
109

1110
export const Room = ({ children, roomId }: RoomProps) => {
12-
return (
13-
<SocketClientProvider>
14-
<BoardProvider roomId={roomId}>{children}</BoardProvider>
15-
</SocketClientProvider>
16-
);
11+
return <BoardProvider roomId={roomId}>{children}</BoardProvider>;
1712
};

app/api/[roomId]/board/leave/route.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { leaveBoard } from "@/use-cases/board/leave-board";
2+
3+
export async function POST(
4+
request: Request,
5+
{ params }: { params: { roomId: string } }
6+
) {
7+
try {
8+
const { roomId } = params;
9+
const { playerId } = await request.json();
10+
11+
if (!playerId || !roomId) {
12+
return Response.json({ message: "Invalid input" }, { status: 400 });
13+
}
14+
15+
await leaveBoard({ roomId, playerId });
16+
17+
return Response.json({ message: "Succefully leave board" });
18+
} catch (error: any) {
19+
console.error(error);
20+
return Response.json({ message: error?.message }, { status: 500 });
21+
}
22+
}

app/api/[roomId]/board/reset/route.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { resetBoard } from "@/use-cases/board/reset-board";
2+
import { getServerSession } from "next-auth";
3+
4+
export async function POST(request: Request) {
5+
try {
6+
const { boardId } = await request.json();
7+
8+
const session = await getServerSession();
9+
10+
if (!session?.user) {
11+
return Response.json({ message: "Unauthorized" }, { status: 401 });
12+
}
13+
14+
if (!boardId) {
15+
return Response.json({ message: "Invalid input" }, { status: 400 });
16+
}
17+
18+
const result = await resetBoard({ boardId });
19+
20+
if (!result.count)
21+
return Response.json(
22+
{ message: "Error while reseting board" },
23+
{ status: 404 }
24+
);
25+
26+
return Response.json({ message: "Succefully reset the board" });
27+
} catch (error: any) {
28+
console.error(error);
29+
return Response.json({ message: error?.message }, { status: 500 });
30+
}
31+
}

0 commit comments

Comments
 (0)