1 import axios from 'axios';
2 import moment from 'moment';
3 import React from 'react';
4 import { Container } from 'react-bootstrap';
5 import { Helmet } from 'react-helmet';
6 import { useParams } from 'react-router-dom';
13 } from '../helpers/Channel';
14 import ErrorBoundary from '../components/common/ErrorBoundary';
15 import Icon from '../components/common/Icon';
16 import Slider from '../components/common/Slider';
18 export const Component = () => {
19 const [channel, setChannel] = React.useState({});
20 const [guesses, setGuesses] = React.useState([]);
21 const [winnerExpiry, setWinnerExpiry] = React.useState(moment().subtract(15, 'second'));
22 const [winners, setWinners] = React.useState([]);
24 const params = useParams();
25 const { key } = params;
27 React.useEffect(() => {
29 axios.get(`/api/guessing-game-monitor/${key}`)
31 setChannel(res.data.channel);
32 res.data.guesses.forEach(g => {
33 setGuesses(gs => patchGuess(gs, g));
35 res.data.winners.forEach(w => {
36 setWinners(ws => patchGuess(ws, w));
39 window.Echo.channel(`ChannelKey.${key}`)
40 .listen('.GuessingGuessCreated', (e) => {
41 setGuesses(gs => patchGuess(gs, e.model));
43 .listen('.GuessingWinnerCreated', (e) => {
44 setWinners(ws => patchWinner(ws, e.model));
46 .listen('.ChannelUpdated', (e) => {
47 setChannel(c => ({ ...c, ...e.model }));
50 window.Echo.leave(`ChannelKey.${key}`);
54 React.useEffect(() => {
55 if (isAcceptingGuesses(channel)) {
56 setGuesses(gs => gs.filter(g => g.created_at >= channel.guessing_start));
61 React.useEffect(() => {
62 const interval = setInterval(() => {
63 setWinnerExpiry(moment().subtract(15, 'second'));
66 clearInterval(interval);
70 const guessingStats = React.useMemo(() => {
78 for (let i = 0; i < 22; ++i) {
80 stats.wins.push(false);
83 guesses.forEach(guess => {
84 if (seen[guess.uid]) {
85 --stats.counts[parseInt(seen[guess.uid].guess, 10) - 1];
87 ++stats.counts[parseInt(guess.guess, 10) - 1];
88 seen[guess.uid] = guess;
90 winners.forEach(winner => {
92 stats.wins[parseInt(winner.guess, 10) - 1] = true;
93 stats.winners.push(winner.uname);
95 if (!stats.lastWin || stats.lastWin < winner.created_at) {
96 stats.lastWin = winner.created_at;
99 for (let i = 0; i < 22; ++i) {
100 if (stats.counts[i] > stats.max) {
101 stats.max = stats.counts[i];
105 }, [guesses, winners]);
107 const getNumberHeight = React.useCallback((number) => {
108 if (!guessingStats || !guessingStats.max) return 3;
109 if (!number) return 3;
110 return Math.max(0.05, number / guessingStats.max) * 100;
113 const getStatClass = React.useCallback((index) => {
114 const names = ['guessing-stat'];
115 if (guessingStats.wins[index]) {
116 names.push('has-won');
118 return names.join(' ');
121 const showOpen = React.useMemo(() => {
122 return isAcceptingGuesses(channel);
125 const showClosed = React.useMemo(() => {
126 return hasActiveGuessing(channel) && !isAcceptingGuesses(channel);
129 const showWinners = React.useMemo(() => {
130 return !hasActiveGuessing(channel) && (
131 guessingStats?.lastWin &&
132 moment(guessingStats.lastWin).isAfter(winnerExpiry));
133 }, [channel, guessingStats, winnerExpiry]);
135 return <ErrorBoundary>
137 <title>Guessing Game</title>
139 <Container className="guessing-game-monitor" fluid>
140 {showOpen || showClosed || showWinners ?
141 <div className="message-box">
143 <div className="message-title accepting-guesses">
144 <Icon.WARNING className="message-icon" />
145 <div className="message-text">
146 <Slider duration={3500}>
147 <Slider.Slide>GT Big Key Guessing Game</Slider.Slide>
148 <Slider.Slide>Zahlen von 1 bis 22 in den Chat!</Slider.Slide>
151 <Icon.WARNING className="message-icon" />
155 <div className="message-title guessing-closed">
156 <div className="message-text">
157 Anmeldung geschlossen
162 <div className="message-title guessing-winners">
163 <div className="message-text">
164 {guessingStats.winners.length ?
165 <Slider duration={2500}>
166 <Slider.Slide>Herzlichen Glückwunsch!</Slider.Slide>
167 {guessingStats.winners.map(winner =>
168 <Slider.Slide key={winner}>{winner}</Slider.Slide>
172 'Leider keiner richtig'
177 <div className="guessing-stats">
178 {guessingStats.counts.map((number, index) =>
179 <div className={getStatClass(index)} key={index}>
180 <div className="guessing-box">
182 className="guessing-box-bar"
183 style={{ height: `${getNumberHeight(number)}%` }}
186 <div className="guessing-number">{index + 1}</div>