play walkout songs

This commit is contained in:
yohlo
2025-09-22 17:57:29 -05:00
parent ae934e77f4
commit b93ce38d48
12 changed files with 152 additions and 42 deletions

View File

@@ -1,8 +1,9 @@
import { ActionIcon, Card, Flex, Text, Stack, Indicator } from "@mantine/core";
import { ActionIcon, Card, Flex, Text, Indicator } from "@mantine/core";
import { PlayIcon, PencilIcon, SpeakerHighIcon } from "@phosphor-icons/react";
import React, { useCallback, useMemo } from "react";
import { MatchSlot } from "./match-slot";
import { Match } from "@/features/matches/types";
import { Team } from "@/features/teams/types";
import { useSheet } from "@/hooks/use-sheet";
import { MatchForm } from "./match-form";
import Sheet from "@/components/sheet/sheet";
@@ -10,6 +11,7 @@ import { useServerMutation } from "@/lib/tanstack-query/hooks";
import { endMatch, startMatch } from "@/features/matches/server";
import { tournamentKeys } from "@/features/tournaments/queries";
import { useQueryClient } from "@tanstack/react-query";
import { useSpotifyPlayback } from "@/lib/spotify/hooks";
interface MatchCardProps {
match: Match;
@@ -24,6 +26,7 @@ export const MatchCard: React.FC<MatchCardProps> = ({
}) => {
const queryClient = useQueryClient();
const editSheet = useSheet();
const { playTrack, pause } = useSpotifyPlayback();
const homeSlot = useMemo(
() => ({
from: orders[match.home_from_lid],
@@ -65,6 +68,8 @@ export const MatchCard: React.FC<MatchCardProps> = ({
[showControls, match.status]
);
const hasWalkoutData = showControls && match.home && match.away && 'song_id' in match.home && 'song_id' in match.away;
const start = useServerMutation({
mutationFn: startMatch,
successMessage: "Match started!",
@@ -84,19 +89,13 @@ export const MatchCard: React.FC<MatchCardProps> = ({
},
});
const handleStart = useCallback(async () => {
await start.mutate({
data: match.id,
});
}, [match]);
const handleFormSubmit = useCallback(
async (data: {
home_cups: number;
away_cups: number;
ot_count: number;
}) => {
await end.mutate({
end.mutate({
data: {
...data,
matchId: match.id,
@@ -107,12 +106,14 @@ export const MatchCard: React.FC<MatchCardProps> = ({
[match.id, editSheet]
);
const handleSpeakerClick = useCallback(() => {
if ("speechSynthesis" in window && match.home?.name && match.away?.name) {
const utterance = new SpeechSynthesisUtterance(
`${match.home.name} vs. ${match.away.name}`
);
const speak = useCallback((text: string): Promise<void> => {
return new Promise((resolve) => {
if (!("speechSynthesis" in window)) {
resolve();
return;
}
const utterance = new SpeechSynthesisUtterance(text);
const voices = window.speechSynthesis.getVoices();
const preferredVoice =
@@ -130,9 +131,71 @@ export const MatchCard: React.FC<MatchCardProps> = ({
utterance.volume = 0.8;
utterance.pitch = 1.0;
utterance.onend = () => resolve();
utterance.onerror = () => resolve();
window.speechSynthesis.speak(utterance);
});
}, []);
const playTeamWalkout = useCallback((team: Team): Promise<void> => {
return new Promise((resolve) => {
const songDuration = (team.song_end - team.song_start) * 1000;
playTrack(team.song_id, undefined, team.song_start * 1000);
setTimeout(async () => {
await pause();
resolve();
}, songDuration);
});
}, [playTrack, pause]);
const handleSpeakerClick = useCallback(async () => {
if (!hasWalkoutData || !match.home?.name || !match.away?.name) {
await speak(`${match.home?.name || "Home"} vs. ${match.away?.name || "Away"}`);
return;
}
}, [match.home?.name, match.away?.name]);
try {
const homeTeam = match.home as Team;
const awayTeam = match.away as Team;
await playTeamWalkout(homeTeam);
await speak(homeTeam.name);
await speak("versus");
await playTeamWalkout(awayTeam);
await speak(awayTeam.name);
await speak("have fun, good luck!");
} catch (error) {
console.warn('Walkout sequence error:', error);
await speak(`${match.home.name} vs. ${match.away.name}`);
}
}, [hasWalkoutData, match.home, match.away, speak, playTeamWalkout]);
const handleStart = useCallback(async () => {
start.mutate({
data: match.id,
});
// Play walkout sequence after starting the match
if (hasWalkoutData && match.home?.name && match.away?.name) {
try {
const homeTeam = match.home as Team;
const awayTeam = match.away as Team;
await playTeamWalkout(homeTeam);
await speak(homeTeam.name);
await speak("versus");
await playTeamWalkout(awayTeam);
await speak(awayTeam.name);
await speak("have fun, good luck!");
} catch (error) {
console.warn('Auto-walkout sequence error:', error);
}
}
}, [match, start, hasWalkoutData, playTeamWalkout, speak]);
return (
<Flex direction="row" align="center" justify="end" gap={8}>
@@ -175,7 +238,7 @@ export const MatchCard: React.FC<MatchCardProps> = ({
</Text>
)}
{showControls && (
{showControls && match.status !== "tbd" && (
<ActionIcon
pos="absolute"
bottom={-2}
@@ -210,6 +273,7 @@ export const MatchCard: React.FC<MatchCardProps> = ({
</Flex>
)}
{showEditButton && (
<Flex direction="column" justify="center" align="center">
<ActionIcon