diff --git a/src/components/flags/FlagsApi.js b/src/components/flags/FlagsApi.js
index 056a9b7..b79bae9 100644
--- a/src/components/flags/FlagsApi.js
+++ b/src/components/flags/FlagsApi.js
@@ -23,6 +23,10 @@ class FlagsApi extends React.Component {
gameEnded = false;
answerLocked = false;
+ state = {
+ loading: true
+ };
+
async handleClick(action) {
if (action === 'api') {
const res = await axios.get(api.url+'/api/flags/test');
@@ -115,8 +119,12 @@ class FlagsApi extends React.Component {
async startGame() {
this.gameEnded = false;
this.answerLocked = false;
+ this.setState({ loading: true });
this.stopTimer();
- await this.handleClick('api').then(() => this.startTimer()).then(() => this.prepareStat());
+ await this.handleClick('api')
+ .then(() => this.startTimer())
+ .then(() => this.prepareStat())
+ .then(() => this.setState({ loading: false }));
}
async showFlags() {
@@ -249,6 +257,10 @@ class FlagsApi extends React.Component {
}
render() {
+ if (this.state.loading) {
+ return
Fetching question...
;
+ }
+
return (
{/*// */}
@@ -301,7 +313,9 @@ class FlagsApi extends React.Component {
'margin' : '10px',
'margin-bottom' : '0px'
}}>
- {this.props.text}
+
+ {this.props.text}
+
Total time: {this.props.sessionTimer}
{
style={{
position: 'absolute',
top: '10px',
- right: '10px',
+ right: '15px',
background: 'none',
border: 'none',
- fontSize: '1.5rem',
+ fontSize: '2rem',
+ fontWeight: 'bold',
cursor: 'pointer',
- opacity: 0.5
+ opacity: 0.7,
+ lineHeight: 1
}}
aria-label="Close"
>
diff --git a/src/components/flags/styles.css b/src/components/flags/styles.css
index 1eaf718..a71dc7d 100644
--- a/src/components/flags/styles.css
+++ b/src/components/flags/styles.css
@@ -72,3 +72,45 @@ td:nth-of-type(2){
font-size: 15px;
font-weight: bold;
}
+
+/* Feedback animations */
+@keyframes pop-in {
+ 0% {
+ transform: scale(0.5);
+ opacity: 0;
+ }
+ 50% {
+ transform: scale(1.3);
+ }
+ 100% {
+ transform: scale(1);
+ opacity: 1;
+ }
+}
+
+@keyframes shake {
+ 0%, 100% {
+ transform: translateX(0);
+ }
+ 20%, 60% {
+ transform: translateX(-5px);
+ }
+ 40%, 80% {
+ transform: translateX(5px);
+ }
+}
+
+.feedback-correct {
+ display: inline-block;
+ color: #28a745;
+ font-weight: bold;
+ font-size: 1.1em;
+ animation: pop-in 0.4s ease-out;
+}
+
+.feedback-incorrect {
+ display: inline-block;
+ color: #dc3545;
+ font-weight: bold;
+ animation: shake 0.4s ease-out;
+}
diff --git a/src/components/home/Home.js b/src/components/home/Home.js
index 2c0e823..fc9728d 100644
--- a/src/components/home/Home.js
+++ b/src/components/home/Home.js
@@ -1,7 +1,6 @@
import React, {useEffect, useState} from 'react';
import "./styles.css";
import axios from "../../config/Axios";
-import Card from "react-bootstrap/Card";
import api from "../../config/Api";
import Container from "react-bootstrap/Container";
import Col from "react-bootstrap/Col";
@@ -23,15 +22,21 @@ const Home = () => {
if (!token) return false;
const expiresAt = localStorage.getItem('tokenExpiresAt');
- if (expiresAt) {
- // Check if token is expired (with 60s buffer)
- if (Date.now() >= parseInt(expiresAt) - 60000) {
- // Token expired, clear it
- localStorage.removeItem('accessToken');
- localStorage.removeItem('refreshToken');
- localStorage.removeItem('tokenExpiresAt');
- return false;
- }
+
+ // Token exists but no expiration - legacy token, treat as expired
+ if (!expiresAt) {
+ localStorage.removeItem('accessToken');
+ localStorage.removeItem('refreshToken');
+ return false;
+ }
+
+ // Check if token is expired (with 60s buffer)
+ if (Date.now() >= parseInt(expiresAt) - 60000) {
+ // Token expired, clear it
+ localStorage.removeItem('accessToken');
+ localStorage.removeItem('refreshToken');
+ localStorage.removeItem('tokenExpiresAt');
+ return false;
}
return true;
@@ -67,9 +72,9 @@ const Home = () => {
{isLoggedIn ? (
<>