reactions, match sse, etc

This commit is contained in:
yohlo
2025-09-19 14:08:36 -05:00
parent 602e6e3473
commit f99d6efaf9
20 changed files with 474 additions and 100 deletions

View File

@@ -29,6 +29,8 @@ const MatchCard = ({ match }: MatchCardProps) => {
}
};
console.log(match);
return (
<Indicator
disabled={!isStarted}
@@ -88,18 +90,30 @@ const MatchCard = ({ match }: MatchCardProps) => {
</Box>
)}
</Box>
<Text
size="md"
fw={600}
lineClamp={1}
style={{ minWidth: 0, flex: 1 }}
>
{match.home?.name!}
</Text>
<Text
size="sm"
fw={600}
lineClamp={1}
style={{ minWidth: 0, flex: 1 }}
>
{match.home?.name!}
</Text>
</Group>
<Text size="xl" fw={700} c={"dimmed"}>
<Text
size="xl"
fw={700}
c={"dimmed"}
display={match.status === "ended" ? undefined : "none"}
>
{match.home_cups}
</Text>
<Stack gap={1}>
{match.home?.players.map((p) => (
<Text size="xs" fw={600} c="dimmed" ta="right">
{p.first_name} {p.last_name}
</Text>
))}
</Stack>
</Group>
<Group justify="space-between" align="center">
@@ -133,7 +147,7 @@ const MatchCard = ({ match }: MatchCardProps) => {
)}
</Box>
<Text
size="md"
size="sm"
fw={600}
lineClamp={1}
style={{ minWidth: 0, flex: 1 }}
@@ -141,9 +155,21 @@ const MatchCard = ({ match }: MatchCardProps) => {
{match.away?.name}
</Text>
</Group>
<Text size="xl" fw={700} c={"dimmed"}>
<Text
size="xl"
fw={700}
c={"dimmed"}
display={match.status === "ended" ? undefined : "none"}
>
{match.away_cups}
</Text>
<Stack gap={1}>
{match.away?.players.map((p) => (
<Text size="xs" fw={600} c="dimmed" ta="right">
{p.first_name} {p.last_name}
</Text>
))}
</Stack>
</Group>
</Stack>
</Paper>
@@ -163,7 +189,7 @@ const MatchCard = ({ match }: MatchCardProps) => {
border: "1px solid var(--mantine-color-default-border)",
}}
>
<EmojiBar />
<EmojiBar matchId={match.id} />
</Paper>
</Box>
</Indicator>

View File

@@ -0,0 +1,18 @@
import { useServerSuspenseQuery, useServerQuery } from "@/lib/tanstack-query/hooks";
import { getMatchReactions } from "./server";
export const matchKeys = {
list: ['matches', 'list'] as const,
details: (id: string) => ['matches', 'details', id] as const,
reactions: (id: string) => ['matches', 'reactions', id] as const,
};
export const matchQueries = {
reactions: (matchId: string) => ({
queryKey: matchKeys.reactions(matchId),
queryFn: () => getMatchReactions({ data: matchId }),
}),
};
export const useMatchReactions = (matchId: string) =>
useServerQuery(matchQueries.reactions(matchId));

View File

@@ -6,6 +6,9 @@ import { z } from "zod";
import { toServerResult } from "@/lib/tanstack-query/utils/to-server-result";
import brackets from "@/features/bracket/utils";
import { MatchInput } from "@/features/matches/types";
import { serverEvents } from "@/lib/events/emitter";
import { superTokensFunctionMiddleware } from "@/utils/supertokens";
import { PlayerInfo } from "../players/types";
const orderedTeamsSchema = z.object({
tournamentId: z.string(),
@@ -150,6 +153,13 @@ export const startMatch = createServerFn()
status: "started",
});
console.log('emitting start match...')
serverEvents.emit("match", {
type: "match",
matchId: match.id,
tournamentId: match.tournament.id
});
return match;
})
);
@@ -184,14 +194,9 @@ export const endMatch = createServerFn()
const matchLoser = home_cups < away_cups ? match.home : match.away;
if (!matchWinner || !matchLoser) throw new Error("Something went wrong");
console.log(matchWinner)
console.log(matchLoser)
// winner -> where to send match winner to, loser same
const { winner, loser } = await pbAdmin.getChildMatches(matchId);
console.log(winner, loser)
// reset match check
if (winner && winner.reset) {
const awayTeamWon = match.away === matchWinner;
@@ -232,8 +237,114 @@ export const endMatch = createServerFn()
});
}
// TODO: send SSE
serverEvents.emit("match", {
type: "match",
matchId: match.id,
tournamentId: match.tournament.id
});
return match;
})
);
const toggleReactionSchema = z.object({
matchId: z.string(),
emoji: z.string(),
});
export const toggleMatchReaction = createServerFn()
.validator(toggleReactionSchema)
.middleware([superTokensFunctionMiddleware])
.handler(async ({ data: { matchId, emoji }, context }) =>
toServerResult(async () => {
const user = await pbAdmin.getPlayerByAuthId(context.userAuthId);
const userId = user?.id;
if (!userId) return;
const match = await pbAdmin.getMatch(matchId);
if (!match) {
throw new Error("Match not found");
}
const existingReaction = await pbAdmin.getUserReaction(matchId, userId, emoji);
if (existingReaction) {
await pbAdmin.deleteReaction(existingReaction.id);
logger.info("Removed reaction", { matchId, emoji, userId });
} else {
await pbAdmin.createReaction(matchId, userId, emoji);
logger.info("Added reaction", { matchId, emoji, userId });
}
const all = await pbAdmin.getReactionsForMatch(matchId);
const reactionsByEmoji = all.reduce((acc, reaction) => {
const emoji = reaction.emoji;
if (!acc[emoji]) {
acc[emoji] = {
emoji,
count: 0,
players: [],
};
}
acc[emoji].count++;
acc[emoji].players.push({
id: reaction?.player?.id,
first_name: reaction.player?.first_name,
last_name: reaction.player?.last_name,
});
return acc;
}, {} as Record<string, any>);
const reactions = Object.values(reactionsByEmoji);
serverEvents.emit("reaction", {
type: "reaction",
matchId,
tournamentId: match.tournament.id,
reactions,
});
return reactions as Reaction[]
})
);
export interface Reaction {
emoji: string;
count: number;
players: PlayerInfo[];
}
export const getMatchReactions = createServerFn()
.validator(z.string())
.middleware([superTokensFunctionMiddleware])
.handler(async ({ data: matchId, context }) =>
toServerResult(async () => {
const match = await pbAdmin.getMatch(matchId);
if (!match) {
throw new Error("Match not found");
}
const all = await pbAdmin.getReactionsForMatch(matchId);
const reactionsByEmoji = all.reduce((acc, reaction) => {
const emoji = reaction.emoji;
if (!acc[emoji]) {
acc[emoji] = {
emoji,
count: 0,
players: [],
};
}
acc[emoji].count++;
acc[emoji].players.push({
id: reaction?.player?.id,
first_name: reaction.player?.first_name,
last_name: reaction.player?.last_name,
});
return acc;
}, {} as Record<string, any>);
const reactions = Object.values(reactionsByEmoji);
return reactions as Reaction[]
})
);