From d81a8f8cc6953d0af1952bec67b0e9365dc2600f Mon Sep 17 00:00:00 2001 From: JP1998 Date: Tue, 11 Dec 2018 13:34:18 +0100 Subject: [PATCH 1/9] Change the way the request methods get the current state Since the state cannot be retrieved while the store has changes to be dispatched the actual state (which is used for building the auth headers for requests) has to be retrieved beforehand and given to the action as parameter. --- js/api.js | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/js/api.js b/js/api.js index 3c5db29..7d5c8d6 100644 --- a/js/api.js +++ b/js/api.js @@ -59,11 +59,11 @@ export function deleteRequest(state, url) { } function generateHeaders(state) { - if(state.isSignedIn) { + if(state.userinfo.isSignedIn) { return { - 'access-token' : state.accesstoken, - 'client' : state.client, - 'uid' : state.uid + 'access-token' : state.userinfo.accesstoken, + 'client' : state.userinfo.client, + 'uid' : state.userinfo.uid }; } else { return {}; @@ -102,7 +102,7 @@ function checkForAuthenticationHeaders(response) { const reducer_userinfo = (state = defaultstate_userinfo, action) => { switch(action.type) { case actiontypes_userinfo.REGISTER: - postRequest(state, '/users', { + postRequest(action.state, '/users', { 'username' : action.parameters.username, 'email' : action.parameters.email, 'password' : action.parameters.password @@ -143,7 +143,7 @@ const reducer_userinfo = (state = defaultstate_userinfo, action) => { errorMessages : action.parameters.errorMessages }); case actiontypes_userinfo.LOGIN: - postRequest(state, '/users/sign_in', { + postRequest(action.state, '/users/sign_in', { email : action.parameters.email, password : action.parameters.password }).then((resp) => { @@ -186,7 +186,7 @@ const reducer_userinfo = (state = defaultstate_userinfo, action) => { errorMessages : action.parameters.errorMessages }); case actiontypes_userinfo.LOGOUT: - deleteRequest(state, '/users/sign_out').then(() => { + deleteRequest(action.state, '/users/sign_out').then(() => { __store.dispatch({ type : actiontypes_userinfo.CLEAR }); }).catch(() => { __store.dispatch({ type : actiontypes_userinfo.CLEAR }); @@ -200,7 +200,7 @@ const reducer_userinfo = (state = defaultstate_userinfo, action) => { uid : action.parameters.uid }); case actiontypes_userinfo.VERIFY_CREDENTIALS: - getRequest(state, '/users/validate_token').then((resp) => { + getRequest(action.state, '/users/validate_token').then((resp) => { storeOptionalToken(resp); }).catch(() => { __store.dispatch({ type: actiontypes_userinfo.CLEAR }); @@ -250,7 +250,10 @@ export function verifyCredentials() { rehydrateApplicationState(); if(__store.getState().userinfo.isSignedIn) { - __store.dispatch({ type: actiontypes_userinfo.VERIFY_CREDENTIALS }); + __store.dispatch({ + type: actiontypes_userinfo.VERIFY_CREDENTIALS, + state: __store.getState() + }); } } @@ -261,7 +264,8 @@ export function register(username, email, password) { username: username, email: email, password: password - } + }, + state: __store.getState() }); } @@ -271,16 +275,16 @@ export function login(email, password) { parameters: { email: email, password: password - } + }, + state: __store.getState() }); } export function logout() { - __store.dispatch({ type : actiontypes_userinfo.LOGOUT }); -} - -export function getState() { - return __store.getState(); + __store.dispatch({ + type : actiontypes_userinfo.LOGOUT, + state: __store.getState() + }); } function rehydrateApplicationState() { From 909faff65c2baaa58487bd03c3c88499a6d42986 Mon Sep 17 00:00:00 2001 From: JP1998 Date: Wed, 12 Dec 2018 15:43:34 +0100 Subject: [PATCH 2/9] Create a first draft of the edit-tournament-page --- js/api.js | 92 ++++++++++- js/components/ErrorComponents.js | 86 ++++++++++ pages/_error.js | 76 +-------- pages/index.js | 2 +- pages/tournament-edit.js | 273 +++++++++++++++++++++++++++++++ server.js | 8 +- static/css/index.css | 4 + 7 files changed, 463 insertions(+), 78 deletions(-) create mode 100644 js/components/ErrorComponents.js create mode 100644 pages/tournament-edit.js diff --git a/js/api.js b/js/api.js index 7d5c8d6..7d81418 100644 --- a/js/api.js +++ b/js/api.js @@ -40,6 +40,28 @@ const defaultstate_userinfo = { uid : null }; +const actiontypes_tournamentinfo = { + 'REQUEST_TOURNAMENT' : 'REQUEST_TOURNAMENT', + 'REQUEST_TOURNAMENT_SUCCESS' : 'REQUEST_TOURNAMENT_SUCCESS', + + 'MODIFY_TOURNAMENT' : 'MODIFY_TOURNAMENT', + 'MODIFY_TOURNAMENT_SUCCESS' : 'MODIFY_TOURNAMENT_SUCCESS', + 'MODIFY_TOURNAMENT_ERROR' : 'MODIFY_TOURNAMENT_ERROR', + + 'REHYDRATE' : 'TOURNAMENTINFO_REHYDRATE', + 'CLEAR' : 'TOURNAMENTINFO_CLEAR', +}; + +const defaultstate_tournamentinfo = { + code : '', + description : '', + id : -1, + name : '', + isPublic : '', + stages: [], + teams : [] +} + export function postRequest(state, url, data) { return axios.post(api_url + url, data, { headers : generateHeaders(state) @@ -58,6 +80,9 @@ export function deleteRequest(state, url) { }); } +// PATCH /teams/{ id } +// { 'name' : ... } + function generateHeaders(state) { if(state.userinfo.isSignedIn) { return { @@ -224,12 +249,59 @@ const reducer_userinfo = (state = defaultstate_userinfo, action) => { } }; +const reducer_tournamentinfo = (state = defaultstate_tournamentinfo, action) => { + switch(action.type) { + case actiontypes_tournamentinfo.REQUEST_TOURNAMENT: + getRequest(action.state, '/tournaments/' + action.parameters.code).then((resp) => { + __store.dispatch({ + type: actiontypes_tournamentinfo.REQUEST_TOURNAMENT_SUCCESS, + parameters: resp.data + }); + storeOptionalToken(resp); + action.parameters.successCallback(); + }).catch((error) => { + console.log(error); + action.parameters.errorCallback(); + }); + return Object.assign({}, state, {}); + case actiontypes_tournamentinfo.REQUEST_TOURNAMENT_SUCCESS: + return Object.assign({}, state, { + code : action.parameters.code, + description : action.parameters.description, + id : action.parameters.id, + name : action.parameters.name, + isPublic : action.parameters.public, + stages: action.parameters.stages, + teams : action.parameters.teams + }); + case actiontypes_tournamentinfo.MODIFY_TOURNAMENT: + + return Object.assign({}, state, {}); + case actiontypes_tournamentinfo.MODIFY_TOURNAMENT_SUCCESS: + + return Object.assign({}, state, {}); + case actiontypes_tournamentinfo.MODIFY_TOURNAMENT_ERROR: + + return Object.assign({}, state, {}); + + case actiontypes_tournamentinfo.REHYDRATE: + + return Object.assign({}, state, {}); + case actiontypes_tournamentinfo.CLEAR: + + return Object.assign({}, state, {}); + default: return state; + } +} + const reducers = { - userinfo: reducer_userinfo + userinfo: reducer_userinfo, + tournamentinfo: reducer_tournamentinfo }; const default_applicationstate = { - userinfo : defaultstate_userinfo + userinfo : defaultstate_userinfo, + tournamentinfo: defaultstate_tournamentinfo }; var __store; @@ -287,6 +359,18 @@ export function logout() { }); } +export function requestTournament(code, successCallback, errorCallback) { + __store.dispatch({ + type: actiontypes_tournamentinfo.REQUEST_TOURNAMENT, + parameters: { + code: code, + successCallback: successCallback, + errorCallback: errorCallback + }, + state: __store.getState() + }); +} + function rehydrateApplicationState() { const persistedState = localStorage.getItem('reduxState') ? JSON.parse(localStorage.getItem('reduxState')) : @@ -297,6 +381,10 @@ function rehydrateApplicationState() { type : actiontypes_userinfo.REHYDRATE, parameters : Object.assign({}, persistedState.userinfo, {}) }); + __store.dispatch({ + type : actiontypes_tournamentinfo.REHYDRATE, + parameters : Object.assign({}, persistedState.tournamentinfo, {}) + }); } } diff --git a/js/components/ErrorComponents.js b/js/components/ErrorComponents.js new file mode 100644 index 0000000..d33190b --- /dev/null +++ b/js/components/ErrorComponents.js @@ -0,0 +1,86 @@ +import Head from 'next/head'; +import React from 'react'; +import {Footer, TurniereNavigation} from '../CommonComponents'; +import 'bootstrap/dist/css/bootstrap.min.css'; +import {Container} from 'reactstrap'; +import '../../static/everypage.css'; +import '../../static/css/error.css'; + +export class ErrorPageComponent extends React.Component { + + static getInitialProps({ statusCode }) { + return { statusCode }; + } + + render() { + return ( +
+ + turnie.re - Error {this.props.statusCode} + + + +
+
+ ); + } +} + +function ErrorPage(props){ + return ( + +
+
+ +
+
+

{props.statusCode}

+
+
+ +
+ ); +} + +function ErrorMessage(props) { + switch (props.code) { + case 400: + return (
+

Deine Anfrage ist fehlerhaft.

+

+ Wir empfehlen, die eingegebene Seite über die Startseite zu suchen. +

+
); + case 403: + return (
+

Du bist nicht autorisiert, diese Seite aufzurufen.

+

+ Bitte stelle sicher, dass Du angemeldet bist und auf dieses Turnier oder dieses Match zugreifen darfst. +

+

+ Wir empfehlen, die eingegebene Seite über die Startseite zu suchen. +

+
); + case 404: + return (
+

Die aufgerufene Seite wurde leider nicht gefunden.

+

+ Entweder hast Du dich vertippt, oder die gesuchte Seite gibt es nicht. +

+

+ Wir empfehlen, die eingegebene Seite über die Startseite zu suchen. +

+
); + case 500: + return (
+

Diese Seite funktioniert nicht.

+

+ turnie.re kann Deine Anfrage im Moment nicht verarbeiten. Bitte versuche es später erneut. +

+
); + default: + return (
+

Ein unbekannter Fehler ist aufgetreten.

+
); + } +} diff --git a/pages/_error.js b/pages/_error.js index 202f1ca..0a1bf26 100644 --- a/pages/_error.js +++ b/pages/_error.js @@ -1,10 +1,4 @@ -import Head from 'next/head'; -import React from 'react'; -import {Footer, TurniereNavigation} from '../js/CommonComponents'; -import 'bootstrap/dist/css/bootstrap.min.css'; -import {Container} from 'reactstrap'; -import '../static/everypage.css'; -import '../static/css/error.css'; +import { ErrorPageComponent } from '../js/components/ErrorComponents.js'; export default class Error extends React.Component { static getInitialProps({ res, err }) { @@ -14,73 +8,7 @@ export default class Error extends React.Component { render() { return ( -
- - turnie.re - Error {this.props.statusCode} - - - -
-
+ ); } } - -function ErrorPage(props){ - return ( - -
-
- -
-
-

{props.statusCode}

-
-
- -
- ); -} - -function ErrorMessage(props) { - switch (props.code) { - case 400: - return (
-

Deine Anfrage ist fehlerhaft.

-

- Wir empfehlen, die eingegebene Seite über die Startseite zu suchen. -

-
); - case 403: - return (
-

Du bist nicht autorisiert, diese Seite aufzurufen.

-

- Bitte stelle sicher, dass Du angemeldet bist und auf dieses Turnier oder dieses Match zugreifen darfst. -

-

- Wir empfehlen, die eingegebene Seite über die Startseite zu suchen. -

-
); - case 404: - return (
-

Die aufgerufene Seite wurde leider nicht gefunden.

-

- Entweder hast Du dich vertippt, oder die gesuchte Seite gibt es nicht. -

-

- Wir empfehlen, die eingegebene Seite über die Startseite zu suchen. -

-
); - case 500: - return (
-

Diese Seite funktioniert nicht.

-

- turnie.re kann Deine Anfrage im Moment nicht verarbeiten. Bitte versuche es später erneut. -

-
); - default: - return (
-

Ein unbekannter Fehler ist aufgetreten.

-
); - } -} \ No newline at end of file diff --git a/pages/index.js b/pages/index.js index dee8a02..7d043df 100644 --- a/pages/index.js +++ b/pages/index.js @@ -114,7 +114,7 @@ function PromotedLinkTournamentCode() {
- +

Gib hier einen Turnier Code ein, um direkt zum entsprechenden Turnier zu gelangen.

diff --git a/pages/tournament-edit.js b/pages/tournament-edit.js new file mode 100644 index 0000000..5545c22 --- /dev/null +++ b/pages/tournament-edit.js @@ -0,0 +1,273 @@ +import Head from 'next/head'; +import React from 'react'; + +import { requestTournament } from '../js/api'; +import { BigImage, Footer, TurniereNavigation } from '../js/CommonComponents.js'; +import { ErrorPageComponent } from '../js/components/ErrorComponents.js'; + +import { + Container, + Button, + Card, + CardBody, + CardTitle, + Table +} from 'reactstrap'; + +import { connect } from 'react-redux'; + +import '../static/everypage.css'; +import '../static/css/index.css'; + +class EditTournamentPage extends React.Component { + + static async getInitialProps({query}) { + return {query}; + } + + constructor(props) { + super(props); + + this.state = { + validCode: true + }; + } + + componentDidMount() { + requestTournament(this.props.query.code, () => { + this.setState({ validCode: true }); + this._edittournamentcontent.notifyOfContentUpdate(); + }, () => { + this.setState({ validCode: false }); + }); + } + + render() { + const { validCode } = this.state; + const { name } = this.props; + + if(validCode) { + return ( +
+ + Turnie.re - Turnier bearbeiten + + + + + { this._edittournamentcontent = edittournamentcontent; }}/> + +
+
+ ); + } else { + return ( + + ); + } + } +} + +function mapStateToTournamentInfo(state) { + const { name } = state.tournamentinfo; + return { name }; +} + +export default connect( + mapStateToTournamentInfo +)(EditTournamentPage); + +class EditTournamentContent extends React.Component { + + render() { + const { code } = this.props; + + return ( +
+ + { this._edittournamentpropertiesfield = field; }}/> + { this._editteamfield = field; }}/> +
+ ); + } + + notifyOfContentUpdate() { + this._edittournamentpropertiesfield.notifyOfContentUpdate(); + this._editteamfield.notifyOfContentUpdate(); + } +} + +function ReturnToTournamentButton() { + return ( + + + + ); +} + +class EditTournamentPropertiesField extends React.Component { + + render() { + return ( + + +

Turnier-Eigenschaften ändern

+ { this._visibleedittournamentform = form; }}/> +
+
+ ); + } + + notifyOfContentUpdate() { + this._visibleedittournamentform.getWrappedInstance().notifyOfContentUpdate(); + } +} + +class EditTournamentForm extends React.Component { + + constructor(props) { + super(props); + + this.state = { + name : '', + description : '', + isPublic : false + }; + } + + render() { + const { name, description, isPublic } = this.state; + + return ( +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
+
+ ); + } + + notifyOfContentUpdate() { + const { name, description, isPublic } = this.props; + + this.setState({ + name : name? name : '', + description : description? description : '', + isPublic : isPublic + }); + } + + handleClick(input) { + // TODO: Apply changes to the tournament properties + } + + handleNameInput(input) { + this.setState({ name : input.target.value }); + } + + handleDescriptionInput(input) { + this.setState({ description : input.target.value }); + } + + handlePublicInput(input) { + this.setState({ public : input.target.value }); + } +} + +function mapStateToTournamentFormProps(state) { + const { name, description, isPublic } = state.tournamentinfo; + return { name, description, isPublic }; +} + +const VisibleEditTournamentForm = connect( + mapStateToTournamentFormProps, + null, null, { withRef : true} +)(EditTournamentForm); + +class EditTeamField extends React.Component { + + render() { + return ( + + +

Team-Namen ändern

+ { this._visibleeditteamnamesform = form; }}/> +
+
+ ); + } + + notifyOfContentUpdate() { + this._visibleeditteamnamesform.getWrappedInstance().notifyOfContentUpdate(); + } +} + +class EditTeamNamesForm extends React.Component { + + constructor(props) { + super(props); + + this.state = { + teams : [] + }; + } + + render() { + const { teams } = this.state; + + return ( +
+ + + { + teams.map((team, index) => { + + }) + } + +
+
+ ); + } + + notifyOfContentUpdate() { + const { teams } = this.props; + + this.setState({ + teams : teams + }); + } + + handleClick(input) { + // TODO: Apply changes to the tournament properties + } + + handleNameInput(input) { + this.setState({ name : input.target.value }); + } +} + +function mapStateToTeamFormProps(state) { + const { teams } = state.tournamentinfo; + return { teams }; +} + +const VisibleEditTeamNamesForm = connect( + mapStateToTeamFormProps, + null, null, { withRef : true } +)(EditTeamNamesForm); + diff --git a/server.js b/server.js index daccc7d..188d268 100644 --- a/server.js +++ b/server.js @@ -8,7 +8,7 @@ const handle = app.getRequestHandler(); app.prepare() .then(() => { const server = express(); - + server.get('/t/:code', (req, res) => { const actualPage = '/tournament'; const queryParam = { code: req.params.code }; @@ -21,6 +21,12 @@ app.prepare() app.render(req, res, actualPage, queryParam); }); + server.get('/t/:code/edit', (req, res) => { + const actualPage = '/tournament-edit'; + const queryParam = { code: req.params.code }; + app.render(req, res, actualPage, queryParam); + }); + server.get('*', (req, res) => { return handle(req, res); }); diff --git a/static/css/index.css b/static/css/index.css index 0ae4d38..665ccab 100644 --- a/static/css/index.css +++ b/static/css/index.css @@ -1,4 +1,8 @@ .index-cards { background: url("/static/images/tennis-blurred.jpg") no-repeat center; background-size: cover; +} + +h2, h3, h4 { + font-family: Halt, sans-serif; } \ No newline at end of file From 68aeade59996db2961c89a8df846d8b8810ce1df Mon Sep 17 00:00:00 2001 From: Felix Hamme Date: Wed, 12 Dec 2018 16:02:16 +0100 Subject: [PATCH 3/9] Fix footer spacing on tournament edit page --- pages/tournament-edit.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pages/tournament-edit.js b/pages/tournament-edit.js index 5545c22..9a93d43 100644 --- a/pages/tournament-edit.js +++ b/pages/tournament-edit.js @@ -1,5 +1,6 @@ import Head from 'next/head'; import React from 'react'; +import 'bootstrap/dist/css/bootstrap.min.css'; import { requestTournament } from '../js/api'; import { BigImage, Footer, TurniereNavigation } from '../js/CommonComponents.js'; @@ -48,7 +49,7 @@ class EditTournamentPage extends React.Component { if(validCode) { return ( -
+
Turnie.re - Turnier bearbeiten @@ -83,7 +84,7 @@ class EditTournamentContent extends React.Component { const { code } = this.props; return ( -
+
{ this._edittournamentpropertiesfield = field; }}/> { this._editteamfield = field; }}/> From 30b2891b7228e4d660169dbc2b5dc903586fbbe4 Mon Sep 17 00:00:00 2001 From: JP1998 Date: Wed, 12 Dec 2018 16:22:36 +0100 Subject: [PATCH 4/9] Add display of team names --- pages/tournament-edit.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/pages/tournament-edit.js b/pages/tournament-edit.js index 9a93d43..7d56a1e 100644 --- a/pages/tournament-edit.js +++ b/pages/tournament-edit.js @@ -235,9 +235,12 @@ class EditTeamNamesForm extends React.Component { { - teams.map((team, index) => { - - }) + teams.map((team, index) => + + + + + ) }
{ team.name }
@@ -253,13 +256,9 @@ class EditTeamNamesForm extends React.Component { }); } - handleClick(input) { + handleClick(input, index) { // TODO: Apply changes to the tournament properties } - - handleNameInput(input) { - this.setState({ name : input.target.value }); - } } function mapStateToTeamFormProps(state) { From ec0a75e5dfbb72b7a80cf9a2a468e7f7d45392e6 Mon Sep 17 00:00:00 2001 From: JP1998 Date: Wed, 12 Dec 2018 17:50:03 +0100 Subject: [PATCH 5/9] Implement editing of team names --- js/api.js | 33 +++++++++++++++++++++++++++++---- package.json | 1 + pages/tournament-edit.js | 28 +++++++++++++++++++++++----- yarn.lock | 10 +++++++++- 4 files changed, 62 insertions(+), 10 deletions(-) diff --git a/js/api.js b/js/api.js index 7d81418..a8ae841 100644 --- a/js/api.js +++ b/js/api.js @@ -80,8 +80,11 @@ export function deleteRequest(state, url) { }); } -// PATCH /teams/{ id } -// { 'name' : ... } +export function patchRequest(state, url, data) { + return axios.patch(api_url + url, data, { + headers : generateHeaders(state) + }); +} function generateHeaders(state) { if(state.userinfo.isSignedIn) { @@ -260,7 +263,6 @@ const reducer_tournamentinfo = (state = defaultstate_tournamentinfo, action) => storeOptionalToken(resp); action.parameters.successCallback(); }).catch((error) => { - console.log(error); action.parameters.errorCallback(); }); return Object.assign({}, state, {}); @@ -275,7 +277,17 @@ const reducer_tournamentinfo = (state = defaultstate_tournamentinfo, action) => teams : action.parameters.teams }); case actiontypes_tournamentinfo.MODIFY_TOURNAMENT: - + patchRequest(action.state, '/teams/' + action.parameters.teamid, { + name: action.parameters.name + }).then((resp) => { + storeOptionalToken(resp); + action.parameters.onSuccess(); + }).catch((error) => { + if(error.response) { + storeOptionalToken(error.response); + } + action.parameters.onError(); + }); return Object.assign({}, state, {}); case actiontypes_tournamentinfo.MODIFY_TOURNAMENT_SUCCESS: @@ -371,6 +383,19 @@ export function requestTournament(code, successCallback, errorCallback) { }); } +export function updateTeamName(team, successCB, errorCB) { + __store.dispatch({ + type: actiontypes_tournamentinfo.MODIFY_TOURNAMENT, + parameters: { + teamid: team.id, + name: team.name, + onSuccess : successCB, + onError : errorCB + }, + state: __store.getState() + }); +} + function rehydrateApplicationState() { const persistedState = localStorage.getItem('reduxState') ? JSON.parse(localStorage.getItem('reduxState')) : diff --git a/package.json b/package.json index 50ad571..601fd1b 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "next": "^7.0.2", "react": "^16.6.1", "react-dom": "^16.6.1", + "react-notify-toast": "^0.5.0", "react-redux": "^5.1.1", "reactstrap": "^6.5.0", "redux": "^4.0.1", diff --git a/pages/tournament-edit.js b/pages/tournament-edit.js index 7d56a1e..d1a85d6 100644 --- a/pages/tournament-edit.js +++ b/pages/tournament-edit.js @@ -1,6 +1,8 @@ import Head from 'next/head'; import React from 'react'; import 'bootstrap/dist/css/bootstrap.min.css'; +import { connect } from 'react-redux'; +import { notify } from 'react-notify-toast'; import { requestTournament } from '../js/api'; import { BigImage, Footer, TurniereNavigation } from '../js/CommonComponents.js'; @@ -15,7 +17,9 @@ import { Table } from 'reactstrap'; -import { connect } from 'react-redux'; +import { + updateTeamName +} from '../js/api'; import '../static/everypage.css'; import '../static/css/index.css'; @@ -237,8 +241,8 @@ class EditTeamNamesForm extends React.Component { { teams.map((team, index) => - - { team.name } + + ) } @@ -256,8 +260,22 @@ class EditTeamNamesForm extends React.Component { }); } - handleClick(input, index) { - // TODO: Apply changes to the tournament properties + handleNameInput(index, input) { + var team = this.state.teams.slice(); + + team[index].name = input.target.value; + + this.setState({ + teams : team + }); + } + + handleClick(index, input) { + updateTeamName(this.state.teams[index], () => { + notify.show('Team Name wurde erfolgreich geändert.', 'success', 5000); + }, () => { + notify.show('Team Name konnte nicht geändert werden.', 'warning', 5000); + }); } } diff --git a/yarn.lock b/yarn.lock index 042a220..912caed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5635,7 +5635,7 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= -object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -6388,6 +6388,14 @@ react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== +react-notify-toast@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/react-notify-toast/-/react-notify-toast-0.5.0.tgz#b00cf50a3cc97a1d222ecd7d7a8e7f14bef5fa67" + integrity sha1-sAz1CjzJeh0iLs19eo5/FL71+mc= + dependencies: + object-assign "^4.0.0" + prop-types "^15.5.8" + react-popper@^0.10.4: version "0.10.4" resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-0.10.4.tgz#af2a415ea22291edd504678d7afda8a6ee3295aa" From cbaa1b8270662f274629b9f4e7756d479ee0368a Mon Sep 17 00:00:00 2001 From: JP1998 Date: Wed, 12 Dec 2018 20:47:09 +0100 Subject: [PATCH 6/9] Fix issue with order of mounting of components Since the credentials were checked in the componentDidMount of the App but the tournamen was loaded in the componentDidMount of the EditTournament component, which was done first. Thus the auth headers were cleared, and the user was logged out. --- pages/_app.js | 7 ++---- pages/_error.js | 8 +++++++ pages/create.js | 38 ++++++++++++++++++++++----------- pages/faq.js | 35 ++++++++++++++++++++---------- pages/imprint.js | 35 ++++++++++++++++++++---------- pages/index.js | 9 ++++++++ pages/list.js | 30 +++++++++++++++++++------- pages/login.js | 37 +++++++++++++++++++++----------- pages/privacy.js | 35 ++++++++++++++++++++---------- pages/register.js | 39 ++++++++++++++++++++++------------ pages/tournament-edit.js | 2 ++ pages/tournament-fullscreen.js | 8 +++++++ pages/tournament.js | 8 +++++++ 13 files changed, 208 insertions(+), 83 deletions(-) diff --git a/pages/_app.js b/pages/_app.js index 3bf91cd..39529b0 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -2,18 +2,15 @@ import App, {Container} from 'next/app'; import React from 'react'; import { Provider } from 'react-redux'; import withReduxStore from '../js/redux/reduxStoreBinder'; -import { verifyCredentials } from '../js/api'; +import Notifications from 'react-notify-toast'; class TurniereApp extends App { - componentDidMount() { - verifyCredentials(); - } - render () { const {Component, pageProps, reduxStore} = this.props; return ( + diff --git a/pages/_error.js b/pages/_error.js index 0a1bf26..0383518 100644 --- a/pages/_error.js +++ b/pages/_error.js @@ -1,11 +1,19 @@ import { ErrorPageComponent } from '../js/components/ErrorComponents.js'; +import { + verifyCredentials +} from '../js/api'; + export default class Error extends React.Component { static getInitialProps({ res, err }) { const statusCode = res ? res.statusCode : err ? err.statusCode : 400; return { statusCode }; } + componentDidMount() { + verifyCredentials(); + } + render() { return ( diff --git a/pages/create.js b/pages/create.js index d2ee8db..b1606f4 100644 --- a/pages/create.js +++ b/pages/create.js @@ -16,20 +16,34 @@ import { Label } from 'reactstrap'; +import { + verifyCredentials +} from '../js/api'; + import EditableStringList from '../js/EditableStringList'; -export default () => ( -
- - Turnier erstellen: turnie.re - - -
- -
-
-
-); +export default class CreatePage extends React.Component { + + componentDidMount() { + verifyCredentials(); + } + + render() { + return ( +
+ + Turnier erstellen: turnie.re + + +
+ +
+
+
+ ); + } +} + function CreateTournamentCard() { return ( diff --git a/pages/faq.js b/pages/faq.js index 57e3bdb..d747e7d 100644 --- a/pages/faq.js +++ b/pages/faq.js @@ -5,6 +5,10 @@ import 'bootstrap/dist/css/bootstrap.min.css'; import { BigImage, Footer, TurniereNavigation } from '../js/CommonComponents.js'; import '../static/everypage.css'; +import { + verifyCredentials +} from '../js/api'; + function Main() { return (
@@ -216,14 +220,23 @@ function TournamentFaq() { ); } -export default () => ( -
- - FAQ: turnie.re - - - -
-
-
-); +export default class FaqPage extends React.Component { + + componentDidMount() { + verifyCredentials(); + } + + render() { + return ( +
+ + FAQ: turnie.re + + + +
+
+
+ ); + } +} diff --git a/pages/imprint.js b/pages/imprint.js index feec607..34fb942 100644 --- a/pages/imprint.js +++ b/pages/imprint.js @@ -5,6 +5,10 @@ import 'bootstrap/dist/css/bootstrap.min.css'; import {BigImage, Footer, TurniereNavigation} from '../js/CommonComponents.js'; import '../static/everypage.css'; +import { + verifyCredentials +} from '../js/api'; + function Main() { return (
@@ -69,14 +73,23 @@ function ImprintText(){ } -export default () => ( -
- - Impressum: turnie.re - - - -
-
-
-); +export default class ImprintPage extends React.Component { + + componentDidMount() { + verifyCredentials(); + } + + render() { + return ( +
+ + Impressum: turnie.re + + + +
+
+
+ ); + } +} diff --git a/pages/index.js b/pages/index.js index 7d043df..8dfc2ea 100644 --- a/pages/index.js +++ b/pages/index.js @@ -12,6 +12,10 @@ import '../static/css/index.css'; import { connect } from 'react-redux'; +import { + verifyCredentials +} from '../js/api'; + function Main() { return (
@@ -165,6 +169,11 @@ function PromotedLinkCreateTournament() { class Index extends React.Component { + + componentDidMount() { + verifyCredentials(); + } + render () { return (
diff --git a/pages/list.js b/pages/list.js index 8dfe530..07dbc80 100644 --- a/pages/list.js +++ b/pages/list.js @@ -1,10 +1,24 @@ import Head from 'next/head'; +import React from 'react'; -export default () => ( -
- - Turnie.re - Turnierliste - -

Turnierliste

-
-); +import { + verifyCredentials +} from '../js/api'; + +export default class ListPage extends React.Component { + + componentDidMount() { + verifyCredentials(); + } + + render() { + return ( +
+ + Turnie.re - Turnierliste + +

Turnierliste

+
+ ); + } +} diff --git a/pages/login.js b/pages/login.js index 6acc489..1a58e49 100644 --- a/pages/login.js +++ b/pages/login.js @@ -6,18 +6,31 @@ import { Button, Card, CardBody, Container, Form, FormGroup, Input, Label } from import { login } from '../js/api'; import { connect } from 'react-redux'; -export default () => ( -
- - Login: turnie.re - - -
- -
-
-
-); +import { + verifyCredentials +} from '../js/api'; + +export default class LoginPage extends React.Component { + + componentDidMount() { + verifyCredentials(); + } + + render() { + return ( +
+ + Login: turnie.re + + +
+ +
+
+
+ ); + } +} function Login() { return ( diff --git a/pages/privacy.js b/pages/privacy.js index 9f0c2b9..e4263eb 100644 --- a/pages/privacy.js +++ b/pages/privacy.js @@ -5,6 +5,10 @@ import 'bootstrap/dist/css/bootstrap.min.css'; import { BigImage, Footer, TurniereNavigation } from '../js/CommonComponents.js'; import '../static/everypage.css'; +import { + verifyCredentials +} from '../js/api'; + function Main() { return (
@@ -488,14 +492,23 @@ function PrivacyText(){ } -export default () => ( -
- - Datenschutzerklärung: turnie.re - - - -
-
-
-); +export default class PrivacyPage extends React.Component { + + componentDidMount() { + verifyCredentials(); + } + + render() { + return ( +
+ + Datenschutzerklärung: turnie.re + + + +
+
+
+ ); + } +} diff --git a/pages/register.js b/pages/register.js index cf0365f..096d6c1 100644 --- a/pages/register.js +++ b/pages/register.js @@ -6,19 +6,32 @@ import { Button, Card, CardBody, Container, Form, FormGroup, FormText, Input, La import { register } from '../js/api'; import { connect } from 'react-redux'; -export default () => ( -
- - Registrieren: turnie.re - - -
- - -
-
-
-); +import { + verifyCredentials +} from '../js/api'; + +export default class RegisterPage extends React.Component { + + componentDidMount() { + verifyCredentials(); + } + + render() { + return ( +
+ + Registrieren: turnie.re + + +
+ + +
+
+
+ ); + } +} function Register() { return ( diff --git a/pages/tournament-edit.js b/pages/tournament-edit.js index d1a85d6..f778dea 100644 --- a/pages/tournament-edit.js +++ b/pages/tournament-edit.js @@ -18,6 +18,7 @@ import { } from 'reactstrap'; import { + verifyCredentials, updateTeamName } from '../js/api'; @@ -39,6 +40,7 @@ class EditTournamentPage extends React.Component { } componentDidMount() { + verifyCredentials(); requestTournament(this.props.query.code, () => { this.setState({ validCode: true }); this._edittournamentcontent.notifyOfContentUpdate(); diff --git a/pages/tournament-fullscreen.js b/pages/tournament-fullscreen.js index 09cde55..21fddff 100644 --- a/pages/tournament-fullscreen.js +++ b/pages/tournament-fullscreen.js @@ -1,11 +1,19 @@ import Head from 'next/head'; import React from 'react'; +import { + verifyCredentials +} from '../js/api'; + class FullscreenTournamentPage extends React.Component { static async getInitialProps({query}) { return {query}; } + + componentDidMount() { + verifyCredentials(); + } render() { return ( diff --git a/pages/tournament.js b/pages/tournament.js index bba1bf2..bb01189 100644 --- a/pages/tournament.js +++ b/pages/tournament.js @@ -2,12 +2,20 @@ import Head from 'next/head'; import React from 'react'; import '../style.css'; +import { + verifyCredentials +} from '../js/api'; + class TournamentPage extends React.Component { static async getInitialProps({query}) { return {query}; } + componentDidMount() { + verifyCredentials(); + } + render() { return (
From e66638dcc0c84495eb7a50dc7ab434274ce95112 Mon Sep 17 00:00:00 2001 From: JP1998 Date: Wed, 12 Dec 2018 20:59:37 +0100 Subject: [PATCH 7/9] Fix code style issues --- js/api.js | 6 +++--- pages/_error.js | 1 + pages/tournament-edit.js | 7 ++----- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/js/api.js b/js/api.js index a8ae841..d3b9b10 100644 --- a/js/api.js +++ b/js/api.js @@ -60,7 +60,7 @@ const defaultstate_tournamentinfo = { isPublic : '', stages: [], teams : [] -} +}; export function postRequest(state, url, data) { return axios.post(api_url + url, data, { @@ -262,7 +262,7 @@ const reducer_tournamentinfo = (state = defaultstate_tournamentinfo, action) => }); storeOptionalToken(resp); action.parameters.successCallback(); - }).catch((error) => { + }).catch(() => { action.parameters.errorCallback(); }); return Object.assign({}, state, {}); @@ -304,7 +304,7 @@ const reducer_tournamentinfo = (state = defaultstate_tournamentinfo, action) => return Object.assign({}, state, {}); default: return state; } -} +}; const reducers = { userinfo: reducer_userinfo, diff --git a/pages/_error.js b/pages/_error.js index 0383518..c0babdc 100644 --- a/pages/_error.js +++ b/pages/_error.js @@ -1,4 +1,5 @@ import { ErrorPageComponent } from '../js/components/ErrorComponents.js'; +import React from 'react'; import { verifyCredentials diff --git a/pages/tournament-edit.js b/pages/tournament-edit.js index f778dea..68d3add 100644 --- a/pages/tournament-edit.js +++ b/pages/tournament-edit.js @@ -13,7 +13,6 @@ import { Button, Card, CardBody, - CardTitle, Table } from 'reactstrap'; @@ -87,8 +86,6 @@ export default connect( class EditTournamentContent extends React.Component { render() { - const { code } = this.props; - return (
@@ -178,7 +175,7 @@ class EditTournamentForm extends React.Component { }); } - handleClick(input) { + handleClick() { // TODO: Apply changes to the tournament properties } @@ -272,7 +269,7 @@ class EditTeamNamesForm extends React.Component { }); } - handleClick(index, input) { + handleClick(index) { updateTeamName(this.state.teams[index], () => { notify.show('Team Name wurde erfolgreich geändert.', 'success', 5000); }, () => { From fb69be47353f7b1da356c0d2a04eea053ae6184a Mon Sep 17 00:00:00 2001 From: JP1998 Date: Wed, 12 Dec 2018 22:44:52 +0100 Subject: [PATCH 8/9] Assign a unique id to elements on /t/:code/edit --- pages/tournament-edit.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pages/tournament-edit.js b/pages/tournament-edit.js index 68d3add..0c89d91 100644 --- a/pages/tournament-edit.js +++ b/pages/tournament-edit.js @@ -146,19 +146,19 @@ class EditTournamentForm extends React.Component {
- +
- +
- +
- +
@@ -240,8 +240,8 @@ class EditTeamNamesForm extends React.Component { { teams.map((team, index) => - - + + ) } From d307cdb907a919a5ed3ec3e93c9e16ad972d647f Mon Sep 17 00:00:00 2001 From: JP1998 Date: Thu, 13 Dec 2018 08:49:01 +0100 Subject: [PATCH 9/9] Resolve conflicts and style issues --- js/api.js | 4 ++++ pages/tournament.js | 46 ++++++++++++++++++++++++++++----------------- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/js/api.js b/js/api.js index d3b9b10..83f281b 100644 --- a/js/api.js +++ b/js/api.js @@ -396,6 +396,10 @@ export function updateTeamName(team, successCB, errorCB) { }); } +export function getState() { + return __store.getState(); +} + function rehydrateApplicationState() { const persistedState = localStorage.getItem('reduxState') ? JSON.parse(localStorage.getItem('reduxState')) : diff --git a/pages/tournament.js b/pages/tournament.js index c3905a6..40753f5 100644 --- a/pages/tournament.js +++ b/pages/tournament.js @@ -31,31 +31,25 @@ import { class TournamentPage extends React.Component { - static async getInitialProps({query}) { - return {query}; - } - - componentDidMount() { - verifyCredentials(); - } - render() { + const { id, description, isPublic, code, ownerUsername, playoffStages } = this.props.tournament; + // TODO: Change href-prop of the anchor tag to contain the tournament code return (
- Turnier bearbeiten -

{props.tournament.description}

+ Turnier bearbeiten +

{description}

- {props.tournament.isPublic ? 'Das Turnier ist öffentlich.' : 'Das Turnier ist privat.'} + {isPublic ? 'Das Turnier ist öffentlich.' : 'Das Turnier ist privat.'} - Turnier-Code: {props.tournament.code} - von {props.tournament.ownerUsername} + Turnier-Code: {code} + von {ownerUsername}
- {props.tournament.playoffStages.map(stage => + {playoffStages.map(stage => )}
@@ -73,10 +67,12 @@ function getLevelName(levelNumber) { } function TournamentContainer(props) { - if (props.data === null) { + const { tournament } = props.data; + + if (tournament === null) { return null; } else { - return ; + return ; } } @@ -355,9 +351,24 @@ function convertMatch(apiMatch) { } class Main extends React.Component { + + static async getInitialProps({query}) { + return {query}; + } + constructor(props) { super(props); + + this.state = { + tournament : null + }; + } + + componentDidMount() { + verifyCredentials(); + const code = this.props.query.code; + getRequest(getState(), '/tournaments/' + code) .then(response => { this.setState({tournament: convertTournament(response.data)}); @@ -365,8 +376,9 @@ class Main extends React.Component { .catch(() => { /* TODO: Show some kind of error or smth */ }); } + render() { - const tournamentName = this.state === null ? 'Turnier' : this.state.tournament.name; + const tournamentName = this.state.tournament === null ? 'Turnier' : this.state.tournament.name; return (