From 5141c19cb7d6cf969be8fe4d103b1cd9bd26bc74 Mon Sep 17 00:00:00 2001 From: Felix Hamme Date: Thu, 3 Oct 2019 11:47:34 +0200 Subject: [PATCH 01/10] Link fullscreen page from tournament page --- pages/tournament.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pages/tournament.js b/pages/tournament.js index 7fa6b53..80aed4a 100644 --- a/pages/tournament.js +++ b/pages/tournament.js @@ -45,6 +45,7 @@ function StatusBar(props) { {props.tournament.name} + ); } @@ -55,6 +56,12 @@ function StatisticsButton(props) { ); } +function FullscreenButton(props) { + return ( + Vollbild-Ansicht + ); +} + function mapStateToTournamentPageProperties(state) { const {isSignedIn, username} = state.userinfo; From 6205187bd494d11ad5a395b826948035dde6056b Mon Sep 17 00:00:00 2001 From: Felix Hamme Date: Thu, 3 Oct 2019 11:50:23 +0200 Subject: [PATCH 02/10] Link fullscreen page from statistics page --- pages/tournament-statistics.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pages/tournament-statistics.js b/pages/tournament-statistics.js index afbfbc2..dcea4b6 100644 --- a/pages/tournament-statistics.js +++ b/pages/tournament-statistics.js @@ -45,6 +45,9 @@ class StatisticsTournamentPage extends React.Component { zurück zum Turnier + + Turnier-Vollbild-Ansicht +
From 1371d8d2073395965c6ce72f5911aaa5815bcef9 Mon Sep 17 00:00:00 2001 From: Felix Hamme Date: Wed, 6 Nov 2019 10:08:55 +0100 Subject: [PATCH 03/10] Create blank fullscreen page with header --- js/redux/tournamentApi.js | 9 ++++ pages/tournament-fullscreen.js | 72 ++++++++++++++++++++++++---- static/css/tournament-fullscreen.css | 3 ++ 3 files changed, 75 insertions(+), 9 deletions(-) create mode 100644 static/css/tournament-fullscreen.css diff --git a/js/redux/tournamentApi.js b/js/redux/tournamentApi.js index 84e1085..746a176 100644 --- a/js/redux/tournamentApi.js +++ b/js/redux/tournamentApi.js @@ -25,6 +25,15 @@ export function getStage(stageId, successCallback, errorCallback) { .catch(errorCallback); } +export function getTournamentMeta(tournamentId, successCallback, errorCallback) { + getRequest(getState(), '/tournaments/' + tournamentId + '?simple=true') + .then(response => { + successCallback(response.status, response.data); + }) + .catch(errorCallback); +} + + function convertTournament(apiTournament) { let groupStage = null; const playoffStages = []; diff --git a/pages/tournament-fullscreen.js b/pages/tournament-fullscreen.js index 3df8089..9f8b83e 100644 --- a/pages/tournament-fullscreen.js +++ b/pages/tournament-fullscreen.js @@ -1,20 +1,74 @@ import Head from 'next/head'; import React from 'react'; +import {ErrorPageComponent} from '../js/components/ErrorComponents'; +import 'bootstrap/dist/css/bootstrap.min.css'; +import '../static/css/everypage.css'; +import '../static/css/tournament-fullscreen.css'; +import {getTournamentMeta} from '../js/redux/tournamentApi'; +import {Navbar, NavbarBrand, NavItem} from 'reactstrap'; -class FullscreenTournamentPage extends React.Component { + +function FullscreenPage(props) { + return (
+ +
); +} + +function FullscreenPageHeader(props) { + return ( + {props.levelName} + {props.title} + + Turnier-Code: {props.code} + + ); +} + +class Main extends React.Component { static async getInitialProps({query}) { return {query}; } + constructor(props) { + super(props); + + this.state = { + tournamentMeta: null + }; + this.onTournamentRequestSuccess = this.onTournamentRequestSuccess.bind(this); + this.onTournamentRequestError = this.onTournamentRequestError.bind(this); + } + + componentDidMount() { + getTournamentMeta(this.props.query.code, this.onTournamentRequestSuccess, this.onTournamentRequestError); + } + + onTournamentRequestSuccess(requestStatus, tournament) { + this.setState({metaStatus: requestStatus, tournamentMeta: tournament}); + } + + onTournamentRequestError(error) { + if (error.response) { + this.setState({metaStatus: error.response.status}); + } else { + this.setState({metaStatus: -1}); + } + } + + render() { - return (
- - Turnie.re - Turnieranzeige (Vollbild) - -

Turnieranzeige (Vollbild)

-

Code: {this.props.query.code}

-
); + const {metaStatus, tournamentMeta} = this.state; + if (metaStatus === 200) { + return (
+ + {tournamentMeta.name}: turnie.re + + +
); + } else { + return ; + } } } -export default FullscreenTournamentPage; +export default Main; diff --git a/static/css/tournament-fullscreen.css b/static/css/tournament-fullscreen.css new file mode 100644 index 0000000..c606da3 --- /dev/null +++ b/static/css/tournament-fullscreen.css @@ -0,0 +1,3 @@ +nav li { + list-style-type: none; +} From aa3d026f83e09fb83003f780761043b1bbbdec9b Mon Sep 17 00:00:00 2001 From: Felix Hamme Date: Wed, 6 Nov 2019 10:30:33 +0100 Subject: [PATCH 04/10] Fullscreen page: fetch matches from api --- js/redux/tournamentApi.js | 12 ++++++++++ pages/tournament-fullscreen.js | 40 +++++++++++++++++++++++++++++----- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/js/redux/tournamentApi.js b/js/redux/tournamentApi.js index 746a176..4669257 100644 --- a/js/redux/tournamentApi.js +++ b/js/redux/tournamentApi.js @@ -33,6 +33,18 @@ export function getTournamentMeta(tournamentId, successCallback, errorCallback) .catch(errorCallback); } +export function getTournamentMatches(tournamentId, successCallback, errorCallback, matchState=null) { + let matchFilter = ''; + if (matchState) { + matchFilter = '?state=' + matchState; + } + getRequest(getState(), '/tournaments/' + tournamentId + '/matches' + matchFilter) + .then(response => { + successCallback(response.status, response.data); + }) + .catch(errorCallback); +} + function convertTournament(apiTournament) { let groupStage = null; diff --git a/pages/tournament-fullscreen.js b/pages/tournament-fullscreen.js index 9f8b83e..5aa3f9f 100644 --- a/pages/tournament-fullscreen.js +++ b/pages/tournament-fullscreen.js @@ -4,16 +4,28 @@ import {ErrorPageComponent} from '../js/components/ErrorComponents'; import 'bootstrap/dist/css/bootstrap.min.css'; import '../static/css/everypage.css'; import '../static/css/tournament-fullscreen.css'; -import {getTournamentMeta} from '../js/redux/tournamentApi'; +import {getTournamentMatches, getTournamentMeta} from '../js/redux/tournamentApi'; import {Navbar, NavbarBrand, NavItem} from 'reactstrap'; function FullscreenPage(props) { return (
+ {JSON.stringify(props.tournamentMeta)} +
); } +function Matches(props) { + return (
+ {props.matches.map(match => )} +
); +} + +function Match(props) { + return
{JSON.stringify(props.match)}
; +} + function FullscreenPageHeader(props) { return ( {props.levelName} @@ -33,16 +45,22 @@ class Main extends React.Component { super(props); this.state = { - tournamentMeta: null + tournamentMeta: null, matches: [] }; this.onTournamentRequestSuccess = this.onTournamentRequestSuccess.bind(this); this.onTournamentRequestError = this.onTournamentRequestError.bind(this); + this.onTournamentMatchesRequestSuccess = this.onTournamentMatchesRequestSuccess.bind(this); + this.onTournamentMatchesRequestError = this.onTournamentMatchesRequestError.bind(this); } componentDidMount() { - getTournamentMeta(this.props.query.code, this.onTournamentRequestSuccess, this.onTournamentRequestError); + const tournamentId = this.props.query.code; + getTournamentMeta(tournamentId, this.onTournamentRequestSuccess, this.onTournamentRequestError); + getTournamentMatches(tournamentId, this.onTournamentMatchesRequestSuccess, + this.onTournamentMatchesRequestError); } + onTournamentRequestSuccess(requestStatus, tournament) { this.setState({metaStatus: requestStatus, tournamentMeta: tournament}); } @@ -55,15 +73,27 @@ class Main extends React.Component { } } + onTournamentMatchesRequestSuccess(requestStatus, matches) { + this.setState({matchesStatus: requestStatus, matches: matches}); + } + + onTournamentMatchesRequestError(error) { + if (error.response) { + this.setState({matchesStatus: error.response.status}); + } else { + this.setState({matchesStatus: -1}); + } + } + render() { - const {metaStatus, tournamentMeta} = this.state; + const {metaStatus, tournamentMeta, matches} = this.state; if (metaStatus === 200) { return (
{tournamentMeta.name}: turnie.re - +
); } else { return ; From c05d5bb9e0ade4d4b5ab693bf83722e85c5a7bf2 Mon Sep 17 00:00:00 2001 From: Felix Hamme Date: Wed, 6 Nov 2019 11:20:53 +0100 Subject: [PATCH 05/10] Fullscreen page: update matches every 3 seconds --- pages/tournament-fullscreen.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pages/tournament-fullscreen.js b/pages/tournament-fullscreen.js index 5aa3f9f..dd422a7 100644 --- a/pages/tournament-fullscreen.js +++ b/pages/tournament-fullscreen.js @@ -51,11 +51,23 @@ class Main extends React.Component { this.onTournamentRequestError = this.onTournamentRequestError.bind(this); this.onTournamentMatchesRequestSuccess = this.onTournamentMatchesRequestSuccess.bind(this); this.onTournamentMatchesRequestError = this.onTournamentMatchesRequestError.bind(this); + this.updateMatches = this.updateMatches.bind(this); } componentDidMount() { const tournamentId = this.props.query.code; getTournamentMeta(tournamentId, this.onTournamentRequestSuccess, this.onTournamentRequestError); + this.updateMatches(); + const intervalId = setInterval(this.updateMatches, 3000); + this.setState({intervalId: intervalId}); + } + + componentWillUnmount() { + clearInterval(this.state.intervalId); + } + + updateMatches() { + const tournamentId = this.props.query.code; getTournamentMatches(tournamentId, this.onTournamentMatchesRequestSuccess, this.onTournamentMatchesRequestError); } From 8e759716990635213eac1ac5d6c41c2c80b87f6b Mon Sep 17 00:00:00 2001 From: Felix Hamme Date: Fri, 8 Nov 2019 12:07:49 +0100 Subject: [PATCH 06/10] Fullscreen page: display matches like on tournament page --- js/redux/tournamentApi.js | 2 +- pages/tournament-fullscreen.js | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/js/redux/tournamentApi.js b/js/redux/tournamentApi.js index 4669257..ddb883a 100644 --- a/js/redux/tournamentApi.js +++ b/js/redux/tournamentApi.js @@ -40,7 +40,7 @@ export function getTournamentMatches(tournamentId, successCallback, errorCallbac } getRequest(getState(), '/tournaments/' + tournamentId + '/matches' + matchFilter) .then(response => { - successCallback(response.status, response.data); + successCallback(response.status, response.data.map(match => convertMatch(match))); }) .catch(errorCallback); } diff --git a/pages/tournament-fullscreen.js b/pages/tournament-fullscreen.js index dd422a7..c4a9631 100644 --- a/pages/tournament-fullscreen.js +++ b/pages/tournament-fullscreen.js @@ -5,26 +5,25 @@ import 'bootstrap/dist/css/bootstrap.min.css'; import '../static/css/everypage.css'; import '../static/css/tournament-fullscreen.css'; import {getTournamentMatches, getTournamentMeta} from '../js/redux/tournamentApi'; -import {Navbar, NavbarBrand, NavItem} from 'reactstrap'; +import {Col, Navbar, NavbarBrand, NavItem, Row} from 'reactstrap'; +import {Match} from '../js/components/Match'; function FullscreenPage(props) { return (
- {JSON.stringify(props.tournamentMeta)}
); } function Matches(props) { - return (
- {props.matches.map(match => )} + return (
+ + {props.matches.map(match => )} +
); } -function Match(props) { - return
{JSON.stringify(props.match)}
; -} function FullscreenPageHeader(props) { return ( From 4005df19dabd2570254c08b754dc6733388093c1 Mon Sep 17 00:00:00 2001 From: Felix Hamme Date: Fri, 8 Nov 2019 14:17:28 +0100 Subject: [PATCH 07/10] Fullscreen page: add dropdown for selecting a match filter (api parameter) --- pages/tournament-fullscreen.js | 49 +++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/pages/tournament-fullscreen.js b/pages/tournament-fullscreen.js index c4a9631..ecbe69b 100644 --- a/pages/tournament-fullscreen.js +++ b/pages/tournament-fullscreen.js @@ -5,13 +5,15 @@ import 'bootstrap/dist/css/bootstrap.min.css'; import '../static/css/everypage.css'; import '../static/css/tournament-fullscreen.css'; import {getTournamentMatches, getTournamentMeta} from '../js/redux/tournamentApi'; -import {Col, Navbar, NavbarBrand, NavItem, Row} from 'reactstrap'; +import { + Col, DropdownItem, DropdownMenu, DropdownToggle, Navbar, NavbarBrand, NavItem, Row, UncontrolledDropdown +} from 'reactstrap'; import {Match} from '../js/components/Match'; function FullscreenPage(props) { return (
- +
); } @@ -24,10 +26,25 @@ function Matches(props) {
); } +function FilterDropdown(props) { + return ( + Match-Filter: + + {props.selected.label} + + + {Object.keys(matchFilters).map(matchFilter => props.select(matchFilters[matchFilter])}> + {matchFilters[matchFilter].label} + )} + + ); +} + function FullscreenPageHeader(props) { return ( - {props.levelName} + {props.title} Turnier-Code: {props.code} @@ -35,6 +52,15 @@ function FullscreenPageHeader(props) { ); } +const matchFilters = { + 'all': {backend: null, label: 'alle'}, + 'in_progress': {backend: 'in_progress', label: 'laufend'}, + 'not_started': {backend: 'not_started', label: 'bereit zum Starten'}, + 'finished': {backend: 'finished', label: 'beendet'}, + 'single_team': {backend: 'single_team', label: 'ohne Gegner'}, + 'not_ready': {backend: 'not_ready', label: 'noch nicht festgelegt'} +}; + class Main extends React.Component { static async getInitialProps({query}) { return {query}; @@ -44,13 +70,19 @@ class Main extends React.Component { super(props); this.state = { - tournamentMeta: null, matches: [] + tournamentMeta: null, matches: [], matchFilter: matchFilters.all }; this.onTournamentRequestSuccess = this.onTournamentRequestSuccess.bind(this); this.onTournamentRequestError = this.onTournamentRequestError.bind(this); this.onTournamentMatchesRequestSuccess = this.onTournamentMatchesRequestSuccess.bind(this); this.onTournamentMatchesRequestError = this.onTournamentMatchesRequestError.bind(this); this.updateMatches = this.updateMatches.bind(this); + this.selectFilter = this.selectFilter.bind(this); + } + + selectFilter(filter) { + this.setState({matchFilter: filter}); + this.updateMatches(); } componentDidMount() { @@ -67,8 +99,8 @@ class Main extends React.Component { updateMatches() { const tournamentId = this.props.query.code; - getTournamentMatches(tournamentId, this.onTournamentMatchesRequestSuccess, - this.onTournamentMatchesRequestError); + getTournamentMatches(tournamentId, this.onTournamentMatchesRequestSuccess, this.onTournamentMatchesRequestError, + this.state.matchFilter.backend); } @@ -99,12 +131,15 @@ class Main extends React.Component { render() { const {metaStatus, tournamentMeta, matches} = this.state; + const filter = { + selected: this.state.matchFilter, select: this.selectFilter + }; if (metaStatus === 200) { return (
{tournamentMeta.name}: turnie.re - +
); } else { return ; From 9d3945a3a740439d3dc683b31c75e954a0ed9c32 Mon Sep 17 00:00:00 2001 From: Felix Hamme Date: Fri, 8 Nov 2019 18:16:30 +0100 Subject: [PATCH 08/10] Fullscreen page: add backend loading indicators --- pages/tournament-fullscreen.js | 57 +++++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/pages/tournament-fullscreen.js b/pages/tournament-fullscreen.js index ecbe69b..84a7c7b 100644 --- a/pages/tournament-fullscreen.js +++ b/pages/tournament-fullscreen.js @@ -6,9 +6,10 @@ import '../static/css/everypage.css'; import '../static/css/tournament-fullscreen.css'; import {getTournamentMatches, getTournamentMeta} from '../js/redux/tournamentApi'; import { - Col, DropdownItem, DropdownMenu, DropdownToggle, Navbar, NavbarBrand, NavItem, Row, UncontrolledDropdown + Col, Container, DropdownItem, DropdownMenu, DropdownToggle, Navbar, NavbarBrand, NavItem, Row, UncontrolledDropdown } from 'reactstrap'; import {Match} from '../js/components/Match'; +import {Spinner} from 'react-bootstrap'; function FullscreenPage(props) { @@ -19,10 +20,19 @@ function FullscreenPage(props) { } function Matches(props) { - return (
- + let matches; + if (props.matches == null) { + matches = (
+ + lade Matches +
); + } else { + matches = ( {props.matches.map(match => )} - +
); + } + return (
+ {matches}
); } @@ -70,7 +80,7 @@ class Main extends React.Component { super(props); this.state = { - tournamentMeta: null, matches: [], matchFilter: matchFilters.all + tournamentMeta: null, matches: [], matchFilter: matchFilters.all, loadedMeta: false, loadedMatches: false }; this.onTournamentRequestSuccess = this.onTournamentRequestSuccess.bind(this); this.onTournamentRequestError = this.onTournamentRequestError.bind(this); @@ -81,7 +91,7 @@ class Main extends React.Component { } selectFilter(filter) { - this.setState({matchFilter: filter}); + this.setState({matchFilter: filter, loadedMatches: false}); this.updateMatches(); } @@ -105,36 +115,55 @@ class Main extends React.Component { onTournamentRequestSuccess(requestStatus, tournament) { - this.setState({metaStatus: requestStatus, tournamentMeta: tournament}); + this.setState({metaStatus: requestStatus, tournamentMeta: tournament, loadedMeta: true}); } onTournamentRequestError(error) { if (error.response) { - this.setState({metaStatus: error.response.status}); + this.setState({metaStatus: error.response.status, loadedMeta: true}); } else { - this.setState({metaStatus: -1}); + this.setState({metaStatus: -1, loadedMeta: true}); } } onTournamentMatchesRequestSuccess(requestStatus, matches) { - this.setState({matchesStatus: requestStatus, matches: matches}); + this.setState({matchesStatus: requestStatus, matches: matches, loadedMatches: true}); } onTournamentMatchesRequestError(error) { if (error.response) { - this.setState({matchesStatus: error.response.status}); + this.setState({matchesStatus: error.response.status, loadedMatches: true}); } else { - this.setState({matchesStatus: -1}); + this.setState({matchesStatus: -1, loadedMatches: true}); } } render() { - const {metaStatus, tournamentMeta, matches} = this.state; + const {metaStatus, matchesStatus, tournamentMeta, matches} = this.state; const filter = { selected: this.state.matchFilter, select: this.selectFilter }; - if (metaStatus === 200) { + if (!this.state.loadedMeta) { + return (
+ + Vollbild-Ansicht: turnie.re + + + + lade Vollbild-Ansicht + +
); + } + if (!this.state.loadedMatches) { + return (
+ + {tournamentMeta.name}: turnie.re + + +
); + } + if (metaStatus === 200 && matchesStatus === 200) { return (
{tournamentMeta.name}: turnie.re From 66e9a2e1a52e417a47efb62aec37d271fb846637 Mon Sep 17 00:00:00 2001 From: Felix Hamme Date: Fri, 8 Nov 2019 18:19:53 +0100 Subject: [PATCH 09/10] Fullscreen page: add message when there are no matches --- pages/tournament-fullscreen.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pages/tournament-fullscreen.js b/pages/tournament-fullscreen.js index 84a7c7b..16a7be0 100644 --- a/pages/tournament-fullscreen.js +++ b/pages/tournament-fullscreen.js @@ -26,6 +26,8 @@ function Matches(props) { lade Matches
); + } else if (props.matches.length === 0) { + matches = (
keine Matches
); } else { matches = ( {props.matches.map(match => )} From 1720aa9bc28e9a85c5ad602e1ea1d0656227358d Mon Sep 17 00:00:00 2001 From: Felix Hamme Date: Fri, 8 Nov 2019 18:21:46 +0100 Subject: [PATCH 10/10] Fullscreen page: add new mock match state (the backend knows what to do) --- pages/tournament-fullscreen.js | 1 + 1 file changed, 1 insertion(+) diff --git a/pages/tournament-fullscreen.js b/pages/tournament-fullscreen.js index 16a7be0..9b23f9c 100644 --- a/pages/tournament-fullscreen.js +++ b/pages/tournament-fullscreen.js @@ -67,6 +67,7 @@ function FullscreenPageHeader(props) { const matchFilters = { 'all': {backend: null, label: 'alle'}, 'in_progress': {backend: 'in_progress', label: 'laufend'}, + 'upcoming': {backend: 'upcoming', label: 'kommend'}, 'not_started': {backend: 'not_started', label: 'bereit zum Starten'}, 'finished': {backend: 'finished', label: 'beendet'}, 'single_team': {backend: 'single_team', label: 'ohne Gegner'},