├── public
├── favicon.ico
├── logo192.png
├── logo512.png
├── robots.txt
├── manifest.json
└── index.html
├── src
├── img
│ ├── star-full.png
│ ├── star-empty.png
│ ├── logo.svg
│ ├── apple.svg
│ ├── nintendo.svg
│ ├── gamepad.svg
│ ├── xbox.svg
│ ├── playstation.svg
│ └── steam.svg
├── reducers
│ ├── index.js
│ ├── detailReducer.js
│ └── gameReducer.js
├── util.js
├── reportWebVitals.js
├── App.js
├── actions
│ ├── detailAction.js
│ └── gamesAction.js
├── animations.js
├── index.js
├── components
│ ├── GlobalStyles.js
│ ├── Game.js
│ ├── Nav.js
│ └── GameDetail.js
├── api.js
└── pages
│ └── Home.js
├── .gitignore
├── README.md
└── package.json
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriticalflare/Ignite/main/public/favicon.ico
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriticalflare/Ignite/main/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriticalflare/Ignite/main/public/logo512.png
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/img/star-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriticalflare/Ignite/main/src/img/star-full.png
--------------------------------------------------------------------------------
/src/img/star-empty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriticalflare/Ignite/main/src/img/star-empty.png
--------------------------------------------------------------------------------
/src/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from "redux";
2 | import gamesReducer from "./gameReducer";
3 | import detailReducer from "./detailReducer";
4 |
5 | const rootReducer = combineReducers({
6 | games: gamesReducer,
7 | detail: detailReducer,
8 | });
9 |
10 | export default rootReducer;
11 |
--------------------------------------------------------------------------------
/src/util.js:
--------------------------------------------------------------------------------
1 | export const smallImage = (imagePath, size) => {
2 | if (imagePath === null) return imagePath;
3 | const image = imagePath.match(/media\/screenshots/)
4 | ? imagePath.replace(
5 | "media/screenshots",
6 | `media/resize/${size}/-/screenshots`
7 | )
8 | : imagePath.replace("/media/games/", `/media/resize/${size}/-/games/`);
9 | return image;
10 | };
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/src/img/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import GlobalStyles from "./components/GlobalStyles";
3 | import Nav from "./components/Nav";
4 | import Home from "./pages/Home";
5 | import { Route } from "react-router-dom";
6 |
7 | function App() {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | );
17 | }
18 |
19 | export default App;
20 |
--------------------------------------------------------------------------------
/src/actions/detailAction.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import { gameDetailsUrl, gameScreenshotUrl } from "../api";
3 |
4 | export const loadDetail = (id) => async (dispatch) => {
5 | dispatch({
6 | type: "LOADING_DETAIL",
7 | });
8 |
9 | const details = await axios.get(gameDetailsUrl(id));
10 | const screenshots = await axios.get(gameScreenshotUrl(id));
11 |
12 | dispatch({
13 | type: "GET_DETAIL",
14 | payload: {
15 | game: details.data,
16 | screen: screenshots.data,
17 | },
18 | });
19 | };
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ignite website
2 | Made this following [Dev Ed](https://www.youtube.com/channel/UClb90NQQcskPUGDIXsQEz5Q)'s excellent [course](https://developedbyed.com/p/the-creative-react-and-redux-course), but making the site responsive and mobile friendly was kept as an exercise to viewer, so here's my stab at it :)
3 |
4 | ## Built With 🛠
5 |
6 | - [React](https://reactjs.org/)
7 | - [React-router-dom](https://www.npmjs.com/package/react-router-dom)
8 | - [Framer Motion](https://www.framer.com/motion/)
9 | - [Styled Components](https://styled-components.com/)
10 | - [Redux](https://redux.js.org/)
11 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/src/animations.js:
--------------------------------------------------------------------------------
1 | export const fadeIn = {
2 | hidden: {
3 | opacity: 0,
4 | },
5 | show: {
6 | opacity: 1,
7 | transition: {
8 | duration: 0.75,
9 | },
10 | },
11 | exit: {
12 | opacity: 0,
13 | transition: {
14 | duration: 0.75,
15 | },
16 | },
17 | };
18 |
19 | export const popup = {
20 | hidden: {
21 | opacity: 0,
22 | scale: 0.5,
23 | },
24 | show: {
25 | opacity: 1,
26 | scale: 1,
27 | transition: {
28 | duration: 0.75,
29 | },
30 | },
31 | exit: {
32 | opacity: 0,
33 | transition: {
34 | duration: 0.75,
35 | },
36 | },
37 | };
38 |
--------------------------------------------------------------------------------
/src/img/apple.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/reducers/detailReducer.js:
--------------------------------------------------------------------------------
1 | const initialState = {
2 | game: { platforms: [] },
3 | screen: { results: [] },
4 | isLoading: true,
5 | };
6 |
7 | const detailReducer = (state = initialState, action) => {
8 | switch (action.type) {
9 | case "GET_DETAIL":
10 | return {
11 | ...state,
12 | isLoading: false,
13 | game: action.payload.game,
14 | screen: action.payload.screen,
15 | };
16 | case "LOADING_DETAIL":
17 | return {
18 | ...state,
19 | isLoading: true,
20 | };
21 | default:
22 | return {
23 | ...state,
24 | };
25 | }
26 | };
27 |
28 | export default detailReducer;
29 |
--------------------------------------------------------------------------------
/src/reducers/gameReducer.js:
--------------------------------------------------------------------------------
1 | const initState = {
2 | popular: [],
3 | newGames: [],
4 | upcoming: [],
5 | searched: [],
6 | };
7 |
8 | const gamesReducer = (state = initState, action) => {
9 | switch (action.type) {
10 | case "FETCH_GAMES":
11 | return {
12 | ...state,
13 | popular: action.payload.popular,
14 | upcoming: action.payload.upcoming,
15 | newGames: action.payload.newGames,
16 | };
17 | case "FETCH_SEARCHED":
18 | return {
19 | ...state,
20 | searched: action.payload.searched,
21 | };
22 | case "CLEAR_SEARCHED":
23 | return {
24 | ...state,
25 | searched: [],
26 | };
27 | default:
28 | return { ...state };
29 | }
30 | };
31 |
32 | export default gamesReducer;
33 |
--------------------------------------------------------------------------------
/src/img/nintendo.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/src/actions/gamesAction.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import {
3 | popularGamesUrl,
4 | upcomingGamesUrl,
5 | newGamesUrl,
6 | searchGameUrl,
7 | } from "../api";
8 |
9 | export const loadGames = () => async (dispatch) => {
10 | console.log(popularGamesUrl());
11 | const popularData = await axios.get(popularGamesUrl());
12 | const newGamesData = await axios.get(newGamesUrl());
13 | const upcomingData = await axios.get(upcomingGamesUrl());
14 |
15 | dispatch({
16 | type: "FETCH_GAMES",
17 | payload: {
18 | popular: popularData.data.results,
19 | newGames: newGamesData.data.results,
20 | upcoming: upcomingData.data.results,
21 | },
22 | });
23 | };
24 |
25 | export const fetchSearch = (game_name) => async (dispatch) => {
26 | const searchGames = await axios.get(searchGameUrl(game_name));
27 |
28 | dispatch({
29 | type: "FETCH_SEARCHED",
30 | payload: {
31 | searched: searchGames.data.results,
32 | },
33 | });
34 | };
35 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import App from "./App";
4 | import reportWebVitals from "./reportWebVitals";
5 | import { createStore, applyMiddleware, compose } from "redux";
6 | import { Provider } from "react-redux";
7 | import rootReducer from "./reducers/index";
8 | import thunk from "redux-thunk";
9 | import { BrowserRouter } from "react-router-dom";
10 |
11 | const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
12 |
13 | const store = createStore(rootReducer, composeEnhancer(applyMiddleware(thunk)));
14 |
15 | ReactDOM.render(
16 |
17 |
18 |
19 |
20 |
21 |
22 | ,
23 | document.getElementById("root")
24 | );
25 |
26 | // If you want to start measuring performance in your app, pass a function
27 | // to log results (for example: reportWebVitals(console.log))
28 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
29 | reportWebVitals();
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ignite",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.6",
7 | "@testing-library/react": "^11.2.2",
8 | "@testing-library/user-event": "^12.6.0",
9 | "axios": "^0.21.0",
10 | "framer-motion": "^3.1.1",
11 | "react": "^17.0.1",
12 | "react-dom": "^17.0.1",
13 | "react-redux": "^7.2.2",
14 | "react-router-dom": "^5.2.0",
15 | "react-scripts": "4.0.1",
16 | "redux": "^4.0.5",
17 | "redux-thunk": "^2.3.0",
18 | "styled-components": "^5.2.1",
19 | "web-vitals": "^0.2.4"
20 | },
21 | "scripts": {
22 | "start": "react-scripts start",
23 | "build": "react-scripts build",
24 | "test": "react-scripts test",
25 | "eject": "react-scripts eject"
26 | },
27 | "eslintConfig": {
28 | "extends": [
29 | "react-app",
30 | "react-app/jest"
31 | ]
32 | },
33 | "browserslist": {
34 | "production": [
35 | ">0.2%",
36 | "not dead",
37 | "not op_mini all"
38 | ],
39 | "development": [
40 | "last 1 chrome version",
41 | "last 1 firefox version",
42 | "last 1 safari version"
43 | ]
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/components/GlobalStyles.js:
--------------------------------------------------------------------------------
1 | import { createGlobalStyle } from "styled-components";
2 |
3 | const GlobalStyles = createGlobalStyle`
4 | * {
5 | margin: 0;
6 | padding: 0;
7 | box-sizing: border-box;
8 | }
9 |
10 | html {
11 | &::-webkit-scrollbar{
12 | width: 0.5rem;
13 | }
14 | &::-webkit-scrollbar-thumb{
15 | background-color: darkgrey
16 | }
17 | &::-webkit-scrollbar-track {
18 | background: white;
19 | }
20 | }
21 |
22 | body {
23 | font-family: 'Montserrat', sans-serif;
24 | width: 100%;
25 | }
26 |
27 | h2 {
28 | font-size: 3rem;
29 | font-family: 'Abril Fatface', cursive;
30 | font-weight: lighter;
31 | }
32 |
33 | h3 {
34 | font-size: 1.3rem;
35 | color: #333;
36 | padding: 1.5rem 0rem;
37 | }
38 |
39 | p {
40 | font-size: 1.2rem;
41 | line-height: 200%;
42 | color: #696969;
43 | }
44 |
45 | a {
46 | text-decoration: none;
47 | color: #333;
48 | }
49 |
50 | img {
51 | display:block;
52 | }
53 | input {
54 | font-family: 'Montserrat', sans-serif;
55 | }
56 | `;
57 |
58 | export default GlobalStyles;
59 |
--------------------------------------------------------------------------------
/src/components/Game.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 | import { motion } from "framer-motion";
4 | import { useDispatch } from "react-redux";
5 | import { loadDetail } from "../actions/detailAction";
6 | import { Link } from "react-router-dom";
7 | import { smallImage } from "../util";
8 | import { popup } from "../animations";
9 |
10 | const Game = ({ name, released, image, id }) => {
11 | const dispatch = useDispatch();
12 | const stringPathId = id.toString();
13 |
14 | const loadDetailHandler = () => {
15 | document.body.style.overflow = "hidden";
16 | dispatch(loadDetail(id));
17 | };
18 |
19 | return (
20 |
21 |
28 | {name}
29 | {released}
30 |
35 |
36 |
37 | );
38 | };
39 |
40 | const StyledGame = styled(motion.div)`
41 | min-height: 30vh;
42 | box-shadow: 0px 5px 20px rgba(0, 0, 0, 0.2);
43 | text-align: center;
44 | border-radius: 1rem;
45 | cursor: pointer;
46 | overflow: hidden;
47 | img {
48 | width: 100%;
49 | height: 40vh;
50 | object-fit: cover;
51 | }
52 |
53 | `;
54 |
55 | export default Game;
56 |
--------------------------------------------------------------------------------
/src/img/gamepad.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/api.js:
--------------------------------------------------------------------------------
1 | // Base URL
2 |
3 | const base_url = "https://api.rawg.io/api/";
4 |
5 | // Get current month
6 | const getMonth = () => {
7 | const month = new Date().getMonth() + 1;
8 | if (month < 10) {
9 | return `0${month}`;
10 | } else {
11 | return month;
12 | }
13 | };
14 |
15 | // Get current day
16 | const getDay = () => {
17 | const day = new Date().getDay() + 1;
18 | if (day < 10) {
19 | return `0${day}`;
20 | } else {
21 | return day;
22 | }
23 | };
24 |
25 | const currentYear = new Date().getFullYear();
26 | const currentMonth = getMonth();
27 | const currentDay = getDay();
28 | const currentDate = `${currentYear}-${currentMonth}-${currentDay}`;
29 | const lastYear = `${currentYear - 1}-${currentMonth}-${currentDay}`;
30 | const nextYear = `${currentYear + 1}-${currentMonth}-${currentDay}`;
31 |
32 | // Popular games
33 |
34 | const popular_games = `games?dates=${lastYear},${currentDate}&ordering=-rating&page_size=10`;
35 | const upcoming_games = `games?dates=${currentDate},${nextYear}&ordering=-added&page_size=10`;
36 | const new_games = `games?dates=${lastYear},${currentDate}&ordering=-released&page_size=10`;
37 | console.log(new_games);
38 |
39 | export const popularGamesUrl = () => `${base_url}${popular_games}`;
40 |
41 | export const upcomingGamesUrl = () => `${base_url}${upcoming_games}`;
42 |
43 | export const newGamesUrl = () => `${base_url}${new_games}`;
44 |
45 | export const gameDetailsUrl = (game_id) => `${base_url}games/${game_id}`;
46 |
47 | export const gameScreenshotUrl = (game_id) =>
48 | `${base_url}games/${game_id}/screenshots`;
49 |
50 | export const searchGameUrl = (game_name) =>
51 | `${base_url}games?search=${game_name}&page_size=9`;
52 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
13 |
14 |
18 |
19 |
28 | React App
29 |
30 |
31 |
32 |
33 |
34 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/img/xbox.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/components/Nav.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import styled from "styled-components";
3 | import { motion } from "framer-motion";
4 | import logo from "../img/logo.svg";
5 | import { useDispatch } from "react-redux";
6 | import { fetchSearch } from "../actions/gamesAction";
7 | import { fadeIn } from "../animations";
8 | const Nav = () => {
9 | const dispatch = useDispatch();
10 | const [textInput, setTextInput] = useState("");
11 | const inputHandler = (e) => {
12 | setTextInput(e.target.value);
13 | };
14 | const submitSearch = (e) => {
15 | e.preventDefault();
16 | dispatch(fetchSearch(textInput));
17 | setTextInput("");
18 | };
19 | const clearSearched = () => {
20 | dispatch({
21 | type: "CLEAR_SEARCHED",
22 | });
23 | };
24 |
25 | return (
26 |
27 |
28 |
29 | Ignite
30 |
31 |
37 |
38 | );
39 | };
40 |
41 | const StyledNav = styled(motion.nav)`
42 | padding: 3rem 5rem;
43 | text-align: center;
44 | input {
45 | width: 30%;
46 | font-size: 1.5rem;
47 | padding: 0.5rem;
48 | border: none;
49 | margin-top: 1rem;
50 | box-shadow: 0px 0px 30px rgba(0, 0, 0, 0.2);
51 | font-weight: bold;
52 | }
53 | button {
54 | font-size: 1.5rem;
55 | border: none;
56 | padding: 0.5rem 2rem;
57 | cursor: pointer;
58 | background: #ff7676;
59 | color: white;
60 | }
61 | @media screen and (max-width: 800px) {
62 | padding: 0rem 1rem;
63 | input {
64 | width: 100%;
65 | }
66 | button {
67 | font-size: 1rem;
68 | margin-top: 0.5rem;
69 | }
70 | }
71 | `;
72 | const Logo = styled(motion.div)`
73 | display: flex;
74 | justify-content: center;
75 | padding: 1rem;
76 | cursor: pointer;
77 | img {
78 | height: 2rem;
79 | width: 2rem;
80 | }
81 | `;
82 | export default Nav;
83 |
--------------------------------------------------------------------------------
/src/img/playstation.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/pages/Home.js:
--------------------------------------------------------------------------------
1 | import { motion, AnimatePresence, AnimateSharedLayout } from "framer-motion";
2 | import React, { useEffect } from "react";
3 | import { useDispatch, useSelector } from "react-redux";
4 | import { loadGames } from "../actions/gamesAction";
5 | import styled from "styled-components";
6 | import Game from "../components/Game";
7 | import GameDetail from "../components/GameDetail";
8 | import { useLocation } from "react-router-dom";
9 | import { fadeIn } from "../animations";
10 |
11 | const Home = () => {
12 | const location = useLocation();
13 |
14 | const pathId = location.pathname.split("/")[2];
15 |
16 | const dispatch = useDispatch();
17 |
18 | useEffect(() => {
19 | dispatch(loadGames());
20 | }, []);
21 |
22 | useEffect(() => {
23 | // to unlock scrolling when moving back from details page on mobile
24 | if (typeof pathId === "undefined") {
25 | document.body.style.overflow = "auto";
26 | }
27 | }, [pathId]);
28 |
29 | const { popular, newGames, upcoming, searched } = useSelector(
30 | (state) => state.games
31 | );
32 |
33 | return (
34 |
35 |
36 |
37 | {pathId && }
38 |
39 | {searched.length ? (
40 |
41 |
Searched Games
42 |
43 | {searched.map((game) => (
44 |
51 | ))}
52 |
53 |
54 | ) : (
55 | ""
56 | )}
57 | Upcoming Games
58 |
59 | {upcoming.map((game) => (
60 |
67 | ))}
68 |
69 | Popular Games
70 |
71 | {popular.map((game) => (
72 |
79 | ))}
80 |
81 | New Games
82 |
83 | {newGames.map((game) => (
84 |
91 | ))}
92 |
93 |
94 |
95 | );
96 | };
97 |
98 | const GameList = styled(motion.div)`
99 | padding: 0rem 5rem;
100 | h2 {
101 | padding: 5rem 0rem;
102 | }
103 | @media screen and (max-width: 800px) {
104 | padding: 0rem 1rem;
105 | h2 {
106 | font-size: 2rem;
107 | padding: 2rem 0rem;
108 | text-align: center;
109 | }
110 | }
111 | `;
112 |
113 | const Games = styled(motion.div)`
114 | min-height: 80vh;
115 | display: grid;
116 | grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
117 | padding: 0rem 5rem;
118 | column-gap: 3rem;
119 | row-gap: 5rem;
120 | @media screen and (max-width: 800px) {
121 | display: flex;
122 | flex-direction: column;
123 | padding: 0rem 0rem;
124 | column-gap: 0rem;
125 | row-gap: 2rem;
126 | justify-content: space-evenly;
127 | }
128 | `;
129 |
130 | export default Home;
131 |
--------------------------------------------------------------------------------
/src/components/GameDetail.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 | import { motion } from "framer-motion";
4 | import { useSelector } from "react-redux";
5 | import { useHistory } from "react-router-dom";
6 | import { smallImage } from "../util";
7 | import playstation from "../img/playstation.svg";
8 | import steam from "../img/steam.svg";
9 | import xbox from "../img/xbox.svg";
10 | import nintendo from "../img/nintendo.svg";
11 | import apple from "../img/apple.svg";
12 | import gamepad from "../img/gamepad.svg";
13 | import starEmpty from "../img/star-empty.png";
14 | import starFull from "../img/star-full.png";
15 |
16 | const GameDetail = ({ pathId }) => {
17 | const { screen, game, isLoading } = useSelector((state) => state.detail);
18 |
19 | const history = useHistory();
20 |
21 | const exitDetailHandler = (e) => {
22 | const element = e.target;
23 | if (element.classList.contains("shadow")) {
24 | document.body.style.overflow = "auto";
25 | history.push("/");
26 | }
27 | };
28 |
29 | const getStars = () => {
30 | const stars = [];
31 | const rating = Math.floor(game.rating);
32 | for (let i = 1; i <= 5; i++) {
33 | if (i <= rating) {
34 | stars.push(
);
35 | } else {
36 | stars.push(
);
37 | }
38 | }
39 | return stars;
40 | };
41 |
42 | const getPlatform = (platform) => {
43 | switch (platform) {
44 | case "PlayStation 4":
45 | return playstation;
46 | case "Xbox One":
47 | return xbox;
48 | case "PC":
49 | return steam;
50 | case "Nintendo Switch":
51 | return nintendo;
52 | case "iOS":
53 | return apple;
54 | default:
55 | return gamepad;
56 | }
57 | };
58 |
59 | return (
60 | <>
61 | {!isLoading && (
62 |
63 |
64 |
65 |
66 |
{game.name}
67 |
Rating: {game.rating}
68 | {getStars()}
69 |
70 |
71 | Platforms
72 |
73 | {game.platforms.map((data) => (
74 |
78 | ))}
79 |
80 |
81 |
82 |
83 |
88 |
89 |
90 | {game.description_raw}
91 |
92 |
93 | {screen.results.map((screen) => (
94 |

99 | ))}
100 |
101 |
102 |
103 | )}
104 | >
105 | );
106 | };
107 |
108 | const CardShadow = styled(motion.div)`
109 | width: 100%;
110 | min-height: 100vh;
111 | overflow-y: scroll;
112 | background: rgba(0, 0, 0, 0.5);
113 | position: fixed;
114 | z-index: 10;
115 | top: 0;
116 | left: 0;
117 | &::-webkit-scrollbar {
118 | width: 0.5rem;
119 | }
120 | &::-webkit-scrollbar-thumb {
121 | background-color: #ff7676;
122 | }
123 | &::-webkit-scrollbar-track {
124 | background: white;
125 | }
126 | `;
127 |
128 | const Detail = styled(motion.div)`
129 | width: 80%;
130 | border-radius: 1rem;
131 | padding: 2rem 5rem;
132 | background: white;
133 | position: absolute;
134 | left: 10%;
135 | color: black;
136 | img {
137 | width: 100%;
138 | }
139 | @media screen and (max-width: 800px) {
140 | width: 100%;
141 | padding: 1rem 1rem;
142 | left: 0%;
143 | border-radius: 0rem;
144 | }
145 | `;
146 |
147 | const Stats = styled(motion.div)`
148 | display: flex;
149 | align-items: center;
150 | justify-content: space-between;
151 | img {
152 | display: inline;
153 | width: 2rem;
154 | height: 2rem;
155 | }
156 | `;
157 |
158 | const Info = styled(motion.div)`
159 | text-align: center;
160 | `;
161 |
162 | const Platforms = styled(motion.div)`
163 | display: flex;
164 | justify-content: space-evenly;
165 | img {
166 | margin-left: 3rem;
167 | }
168 | @media screen and (max-width: 800px) {
169 | display: block;
170 | }
171 | `;
172 |
173 | const Media = styled(motion.div)`
174 | margin-top: 5rem;
175 | img {
176 | width: 100%;
177 | }
178 | `;
179 |
180 | const Description = styled(motion.div)`
181 | margin: 5rem 0rem;
182 | `;
183 | export default GameDetail;
184 |
--------------------------------------------------------------------------------
/src/img/steam.svg:
--------------------------------------------------------------------------------
1 |
35 |
--------------------------------------------------------------------------------