🎨 style(index.html): update meta description and title for better SEO and user experience

🔧 refactor(App.js): introduce MUI theme for dark mode and improve code readability
🔧 refactor(ConsoleCard.js): remove image and center text for better UI
🔧 refactor(ConsoleList.js): use MUI Box for better layout and code readability
🔧 refactor(GameCard.js): remove unused code and center text for better UI
🔧 refactor(GameList.js): add search and pagination functionality for better UX
🔧 refactor(GamePlayer.js): improve game loading logic and code readability
🔧 refactor(Topbar.js): add navigation to home on title click for better UX
🔧 refactor(ConsolePage.jsx, GamePage.jsx, HomePage.jsx): add Topbar for consistent navigation across pages
This commit is contained in:
Djalim Simaila 2024-03-26 13:45:54 +01:00
parent 1dcc6f00f5
commit 5c54c73a56
11 changed files with 182 additions and 100 deletions

View File

@ -7,7 +7,7 @@
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
content="Gros site bien legal la, bien nintendo friendly, on a que de la joie ici, PTDR"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
@ -24,7 +24,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>Site de Retrogaming legal</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

View File

@ -9,10 +9,25 @@ import ConsoleList from './page_elements/ConsoleList';
import GameList from './page_elements/GameList';
import GamePlayer from './page_elements/GamePlayer';
import Topbar from './page_elements/Topbar';
import {createTheme, ThemeProvider } from '@mui/material/styles';
import * as React from 'react';
var API_URL = process.env.VIDEOGAME_API_URL
const dacula_theme = createTheme({
palette: {
mode: 'dark',
primary: {
main: '#bd93f9',
},
background: {
paper: '#44475a',
default: '#282a36',
},
divider: '#44475a',
},
})
function App() {
console.log(API_URL);
const [gameId, setGameId] = React.useState(0);
@ -43,10 +58,11 @@ function App() {
}
return <>
<Topbar/>
<Router/>
<ThemeProvider theme={dacula_theme}>
<Router/>
</ThemeProvider>
</>
}
export default App;

View File

@ -34,14 +34,9 @@ export default function ConsoleCard(props) {
}
return (
<Card style={{margin: "10px"}} onClick={() => navigate("/console/"+game_console_id)} sx={{ width: 250 }}>
<Card onClick={() => navigate("/console/"+game_console_id)}
sx={{ width: 250, margin: "10px", textAlign:"center" }}>
<CardActionArea>
<CardMedia
component="img"
height="140"
image={placeholder}
alt="green iguana"
/>
<CardContent>
<Typography gutterBottom variant="h5" component="div">
{game_console}

View File

@ -6,6 +6,9 @@ import ConsoleCard from './ConsoleCard';
import axios from 'axios';
import * as React from 'react';
import { LinearProgress } from '@mui/material';
import Box from '@mui/material/Box';
function ConsoleList(props) {
const [game_consoles, setGameConsoles] = React.useState([]);
@ -14,7 +17,7 @@ function ConsoleList(props) {
if (!has_loaded){
let consoles = JSON.parse(localStorage.getItem("consoles"));
if (consoles !== null) {
console.log("loaded cache");
for (const game_console of consoles) {
@ -42,16 +45,26 @@ function ConsoleList(props) {
if (!has_loaded){
return (
<div style={{display: "flex", flexWrap:"wrap", flexDirection:"row", justifyContent:"center"}}>
<div style={{display: "flex", flexWrap:"wrap", flexDirection:"row", alignItems:"center", justifyContent:"center"}}>
<LinearProgress />
</div>
);
}
else{
return (
<div style={{display: "flex", flexWrap:"wrap", flexDirection:"row", justifyContent:"center"}}>
<Box
display="flex"
flexDirection="row"
flexWrap="wrap"
justifyContent="center"
flex="1 0 21%"
alignItems="center"
gap={1}
p={2}
sx={{
}}>
{game_consoles}
</div>
</Box>
);
}
}

View File

@ -1,47 +1,24 @@
import * as React from 'react';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import CardMedia from '@mui/material/CardMedia';
import Typography from '@mui/material/Typography';
import { CardActionArea } from '@mui/material';
import placeholder from '../images/placeholder.jpg';
import { useNavigate } from 'react-router-dom';
export default function GameCard(props) {
const game_name = props.name;
const game_id = props.id;
const liste = true;
const navigate = useNavigate();
if (liste) {
return (
<Card sx={{ }} style={{margin:"20px"}} onClick={ ()=> navigate("/console/"+props.console_id+"/"+game_id)}>
<Card sx={{margin:"20px", flexGrow:1}} onClick={ ()=> navigate("/console/"+props.console_id+"/"+game_id)}>
<CardActionArea>
<CardContent>
<Typography gutterBottom variant="h5" component="div">
<Typography gutterBottom variant="h5" component="div" sx={{textAlign:"center"}}>
{game_name}
</Typography>
</CardContent>
</CardActionArea>
</Card>
);
} else {
return (
<Card sx={{ maxWidth: 150 }} style={{margin:"20px"}}>
<CardActionArea>
<CardMedia
component="img"
height="140"
image={placeholder}
alt="green iguana"
/>
<CardContent>
<Typography gutterBottom variant="h5" component="div">
{game_name}
</Typography>
</CardContent>
</CardActionArea>
</Card>
);
}
}

View File

@ -3,30 +3,47 @@ import '@fontsource/roboto/400.css';
import '@fontsource/roboto/500.css';
import '@fontsource/roboto/700.css';
import * as React from 'react';
import { CircularProgress, Popover } from '@mui/material';
import { CircularProgress, Button,Typography,Box } from '@mui/material';
import Pagination from '@mui/material/Pagination';
import Stack from '@mui/material/Stack';
import axios from 'axios';
import GameCard from './GameCard';
import {TextField} from '@mui/material';
import { useParams } from 'react-router-dom';
import { useSearchParams, useNavigate } from "react-router-dom";
async function loadgamesfromlocalstorage(rom_name,console_id){
var itemPerPage = 50;
async function loadgamesfromlocalstorage(rom_name,console_id,filter){
let console_rom = JSON.parse(localStorage.getItem(rom_name));
let game_components = []
console_rom = console_rom.filter(roms => roms["name"].toLowerCase().includes(filter.toLowerCase()))
console_rom.sort((a,b) => a["name"].localeCompare(b["name"]))
for (const roms of console_rom) {
const card = <GameCard id={roms["id"]} name={roms["name"]} console_id={console_id} />
game_components.push(card);
}
return game_components;
}
}
function GameList(props) {
const [game_consoles, setGameConsoles] = React.useState([]);
const [has_loaded, setHasLoaded] = React.useState(false);
const game_components = []
const params = useParams()
let console_id = params.consoleId;
const [search_filter, setSearchFilter] = React.useState("");
const [current_page, setCurrentPage] = React.useState(0);
let [searchParams, setSearchParams] = useSearchParams();
const navigate = useNavigate();
console.log(searchParams);
let filter = "";
if (searchParams.get("q") !== null){
filter = searchParams.get("q");
}
console.log(filter);
const game_components = [];
const params = useParams();
let console_id = params.consoleId;
let rom_name = "rom" + props.id;
let console_rom = JSON.parse(localStorage.getItem(rom_name));
@ -37,6 +54,8 @@ function GameList(props) {
axios.get('https://videogamedb.simailadjalim.fr/consoles/'+ console_id + '/roms')
.then((result) => {
localStorage.setItem(rom_name,JSON.stringify(result.data));
result.data = result.data.filter(roms => roms["name"].includes(filter));
result.data.sort((a,b) => a["name"].localeCompare(b["name"]))
for (const roms of result.data) {
const card = <GameCard id={roms["id"]} name={roms["name"]} console_id={console_id} />
game_components.push(card);
@ -46,24 +65,65 @@ function GameList(props) {
})
}else{
console.log("loading roms from cache");
loadgamesfromlocalstorage(rom_name,props.id).then(game_components =>{
loadgamesfromlocalstorage(rom_name,props.id,filter).then(game_components =>{
setGameConsoles(game_components);
setHasLoaded(true);
});
}
}
if (!has_loaded){
return <>
const handleKeyDown = (event) => {
if (event.key === 'Enter') {
// 👇 Get input value
setSearchParams({"q" : search_filter});
navigate(0);
}
};
function NavigationButtons(){
let len = (game_consoles.length - (game_consoles.length % itemPerPage)) / itemPerPage;
console.log(len);
return (
<Stack spacing={2} sx={{display:"flex",justifyContent:"center"}}>
<Pagination count={len} page={current_page} onChange={(event,value)=>setCurrentPage(value)} />
</Stack>)
}
if (!has_loaded){
return <Box
display="flex"
flexDirection="column"
justifyContent="center"
padding="1rem"
>
<br/>
<CircularProgress/>
</>
</Box>
}
else{
let shown = game_consoles.slice(current_page*itemPerPage,current_page*itemPerPage+itemPerPage)
return (
<div style={{display: "flex", flexDirection: "column", flexWrap: "wrap"}}>
<p>Jeux</p>
{game_consoles}
</div>
<Box
display="flex"
flexDirection="column"
justifyContent="center"
padding="1rem"
>
<TextField id="outlined-basic" label="Rechercher" variant="outlined" onKeyDown={handleKeyDown} sx={{marginBottom:"1rem"}} onChange={e => setSearchFilter(e.target.value)} />
<NavigationButtons/>
<Typography variant="h4"
component="div"
sx={{ flexGrow: 1, display: { xs: 'none', sm: 'block' }, textAlign:"center", color:"white" }} >
Jeux de cette console
</Typography>
<div style={{display: "flex", flexDirection: "row", flexWrap: "wrap", justifyContent:"space-evenly"}}>
{shown}
</div>
<NavigationButtons/>
</Box>
);
}
}

View File

@ -4,42 +4,51 @@ import Button from '@mui/material/Button';
import { Box } from '@mui/material';
export default function GamePlayer(props) {
const [game, setGame] = React.useState(null);
var game_id = props.id;
var console_core = JSON.parse(localStorage.getItem("console"+props.consoleId)).core;
console.log(console_core);
axios.get('https://videogamedb.simailadjalim.fr/roms/' + game_id).then((result) => {
setGame(result.data["name"]);
})
function handleDownload(){
const fileUrl = 'https://videogamedb.simailadjalim.fr/roms/' + props.id + '?romfile=true';
const link = document.createElement('a');
link.href = fileUrl;
document.body.appendChild(link);
link.click();
// Clean up the temporary URL and remove the <a> element
document.body.removeChild(link);
};
return (
<div style={{display: "flex",flexDirection:"column",alignItems:"center",width:"100%"}}>
<h2>GamePlayer</h2>
<h3>{game}</h3>
<iframe
title="EmulatorJS"
src={"https://videogamedb.simailadjalim.fr/emulator?rom_id=" + game_id + "&console_core=" + console_core}
width="640"
height="480"
allowFullScreen
/>
<Box sx={{
paddingTop: "10px"
}}>
const [game, setGame] = React.useState(null);
const [console_core,setConsoleCore] = React.useState(null);
var game_id = props.id;
var current_console = JSON.parse(localStorage.getItem("console"+props.consoleId));
</Box>
<Button variant="contained" onClick={handleDownload}>Télécharger le jeu</Button>
</div>
);
if (console_core == null) {
if (current_console === null){
axios.get('https://videogamedb.simailadjalim.fr/consoles/' + props.consoleId).then((result) => {
localStorage.setItem("console"+props.consoleId,JSON.stringify(result.data));
setConsoleCore(result.data["core"]);
})
}else{
setConsoleCore(current_console.core);
}
}
axios.get('https://videogamedb.simailadjalim.fr/roms/' + game_id).then((result) => {
setGame(result.data["name"]);
})
function handleDownload(){
const fileUrl = 'https://videogamedb.simailadjalim.fr/roms/' + props.id + '?romfile=true';
const link = document.createElement('a');
link.href = fileUrl;
document.body.appendChild(link);
link.click();
// Clean up the temporary URL and remove the <a> element
document.body.removeChild(link);
};
return (
<div style={{display: "flex",flexDirection:"column",alignItems:"center",width:"100%"}}>
<h2>GamePlayer</h2>
<h3>{game}</h3>
<iframe
title="EmulatorJS"
src={"https://videogamedb.simailadjalim.fr/emulator?rom_id=" + game_id + "&console_core=" + console_core}
width="640"
height="480"
allowFullScreen
/>
<Box sx={{
paddingTop: "10px"
}}>
</Box>
<Button variant="contained" onClick={handleDownload}>Télécharger le jeu</Button>
</div>
);
}

View File

@ -8,6 +8,9 @@ import Typography from '@mui/material/Typography';
import InputBase from '@mui/material/InputBase';
import MenuIcon from '@mui/icons-material/Menu';
import SearchIcon from '@mui/icons-material/Search';
import { useNavigate } from 'react-router-dom';
const Search = styled('div')(({ theme }) => ({
position: 'relative',
@ -52,10 +55,11 @@ const StyledInputBase = styled(InputBase)(({ theme }) => ({
}));
export default function Topbar() {
const navigate = useNavigate();
return (
<Box sx={{ flexGrow: 1 }}>
<Box sx={{ flexGrow: 1, marginBottom:"1rem" }}>
<AppBar position="static">
<Toolbar>
<Toolbar >
<IconButton
size="large"
edge="start"
@ -69,6 +73,7 @@ export default function Topbar() {
variant="h6"
noWrap
component="div"
onClick={()=> navigate("/") }
sx={{ flexGrow: 1, display: { xs: 'none', sm: 'block' } }}
>
Gros site de retrogaming bien legal la

View File

@ -1,11 +1,12 @@
import GameList from "../page_elements/GameList";
import {useParams} from 'react-router-dom';
import Topbar from '../page_elements/Topbar';
export default function ConsolePage(){
const params = useParams();
return <>
<Topbar/>
<GameList id={params.consoleId} />
</>
}

View File

@ -1,11 +1,15 @@
import GamePlayer from "../page_elements/GamePlayer";
import { useParams } from "react-router-dom";
import Topbar from '../page_elements/Topbar';
export default function GamePage() {
const params = useParams();
let gameId = params.gameId;
let consoleId = params.consoleId;
return <GamePlayer id={gameId} consoleId={consoleId} />;
return <>
<Topbar/>
<GamePlayer id={gameId} consoleId={consoleId} />;
</>
}

View File

@ -1,13 +1,15 @@
import ConsoleList from "../page_elements/ConsoleList";
import Typography from '@mui/material/Typography';
import Topbar from '../page_elements/Topbar';
export function HomePage(){
return <>
<Topbar/>
<Typography
variant="h3"
noWrap
component="div"
sx={{ flexGrow: 1, display: { xs: 'none', sm: 'block' } }}
sx={{ flexGrow: 1, display: { xs: 'none', sm: 'block' }, textAlign:"center", color:"white" }}
>
Bienvenue sur le site dont tu ne dois pas partager l'existance.
</Typography>