Skip to content
Open

H2 2 #82

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
"plugins": ["prettier"],
"rules": {
"camelcase": ["error", { "properties": "never" }],
"prettier/prettier": "error",
"prettier/prettier": [
"error",
{
"endOfLine": "auto"
}
],
"eqeqeq": ["error", "always"],
"no-unused-vars": ["error"]
}
Expand Down
7 changes: 4 additions & 3 deletions docs/mvp-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
Количество карточек для каждого уровня сложности можете назначать и свои или выбрать готовый пресет.

Предлагаем следующее пресеты:
- Легкий уровень - 6 карточек (3 пары)
- Средний уровень - 12 карточек (6 пар)
- Сложный уровень - 18 карточек (9 пар)

- Легкий уровень - 6 карточек (3 пары)
- Средний уровень - 12 карточек (6 пар)
- Сложный уровень - 18 карточек (9 пар)

Как только уровень сложности выбран, игроку показывается на игровой поле.

Expand Down
1 change: 1 addition & 0 deletions lefthook.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ pre-commit:
eslint:
glob: "*.{js,jsx}"
run: npm run lint
"prettier/prettier": ["error", { "endOfLine": "auto" }]
14 changes: 14 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"classnames": "^2.3.2",
"clsx": "^2.1.1",
"gh-pages": "^6.0.0",
"lodash": "^4.17.21",
"react": "^18.2.0",
Expand Down
29 changes: 29 additions & 0 deletions src/API/leaders.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export async function getLeaders() {
try {
const response = await fetch("https://wedev-api.sky.pro/api/leaderboard/", {
method: "GET",
});
const isResponseOk = response.ok;
const result = await response.json();
if (isResponseOk) {
return result.leaders;
} else {
throw new Error(result.error);
}
} catch (error) {
throw new Error(error.message);
}
}

//добавление лидера в список
export const addLeader = async data => {
const response = await fetch("https://wedev-api.sky.pro/api/leaderboard/", {
method: "POST",
body: JSON.stringify(data),
});

if (!response.ok) {
throw new Error("Упс, ошибка");
}
return response.json();
};
29 changes: 28 additions & 1 deletion src/components/Cards/Cards.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { shuffle } from "lodash";
import { useEffect, useState } from "react";
import { useContext, useEffect, useState } from "react";
import { generateDeck } from "../../utils/cards";
import styles from "./Cards.module.css";
import { EndGameModal } from "../../components/EndGameModal/EndGameModal";
import { Button } from "../../components/Button/Button";
import { Card } from "../../components/Card/Card";
import { EasyContext } from "../../context/Context";

// Игра закончилась
const STATUS_LOST = "STATUS_LOST";
Expand Down Expand Up @@ -41,6 +42,7 @@ function getTimerValue(startDate, endDate) {
* previewSeconds - сколько секунд пользователь будет видеть все карты открытыми до начала игры
*/
export function Cards({ pairsCount = 3, previewSeconds = 5 }) {
const { isEasyMode, tries, setTries } = useContext(EasyContext);
// В cards лежит игровое поле - массив карт и их состояние открыта\закрыта
const [cards, setCards] = useState([]);
// Текущий статус игры
Expand Down Expand Up @@ -73,6 +75,9 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) {
setGameEndDate(null);
setTimer(getTimerValue(null, null));
setStatus(STATUS_PREVIEW);
if (isEasyMode) {
setTries(3);
}
}

/**
Expand Down Expand Up @@ -127,6 +132,27 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) {

// "Игрок проиграл", т.к на поле есть две открытые карты без пары
if (playerLost) {
if (isEasyMode) {
setTries(tries - 1);
if (tries === 1) {
finishGame(STATUS_LOST);
} else {
const newCards = cards.map(c => {
if (openCardsWithoutPair.find(card => card.id === c.id)) {
return {
...c,
open: false,
};
}
return c;
});

setTimeout(() => {
setCards(newCards);
}, 500);
}
return;
}
finishGame(STATUS_LOST);
return;
}
Expand Down Expand Up @@ -196,6 +222,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) {
)}
</div>
{status === STATUS_IN_PROGRESS ? <Button onClick={resetGame}>Начать заново</Button> : null}
{isEasyMode && <span>Колличество жизней: {tries}</span>}
</div>

<div className={styles.cards}>
Expand Down
59 changes: 55 additions & 4 deletions src/components/EndGameModal/EndGameModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,75 @@ import { Button } from "../Button/Button";

import deadImageUrl from "./images/dead.png";
import celebrationImageUrl from "./images/celebration.png";
import { Link, useNavigate, useParams } from "react-router-dom";
import { useState } from "react";
import { addLeader } from "../../API/leaders.js";

export function EndGameModal({ isWon, gameDurationSeconds, gameDurationMinutes, onClick }) {
const title = isWon ? "Вы победили!" : "Вы проиграли!";
const { pairsCount } = useParams();
const [error, setError] = useState();
const nav = useNavigate();

const thirdLevelPairs = 9;
const isLeader = isWon && Number(pairsCount) === thirdLevelPairs;
const title = isLeader ? "Вы попали на лидерборд!" : isWon ? "Вы выйграли!" : "Вы проиграли!";

//const title = isWon ? "Вы выйграли!" : "Вы проиграли!";

const imgSrc = isWon ? celebrationImageUrl : deadImageUrl;

const imgAlt = isWon ? "celebration emodji" : "dead emodji";

const [leader, setAddLeader] = useState({
name: "",
time: gameDurationMinutes.toString().padStart("2", "0") + gameDurationSeconds.toString().padStart("2", "0"),
});

const addLeaderToList = async e => {
e.preventDefault();
if (leader.name === "") {
setError("Введите имя пользователя");
return;
}
try {
await addLeader({ name: leader.name, time: leader.time }).then(res => {
setAddLeader(res.leaders);
nav("/leaderBoard");
});
} catch (error) {
setError(error.message);
}
};

return (
<div className={styles.modal}>
<img className={styles.image} src={imgSrc} alt={imgAlt} />
<h2 className={styles.title}>{title}</h2>
{isLeader ? (
<div className={styles.userBlockName}>
<input
onChange={e => setAddLeader({ ...leader, name: e.target.value })}
id="name-input"
type="text"
name="name"
className={styles.input}
placeholder="Пользователь"
/>
</div>
) : null}
<p className={styles.description}>Затраченное время:</p>
<div className={styles.time}>
{gameDurationMinutes.toString().padStart("2", "0")}.{gameDurationSeconds.toString().padStart("2", "0")}
{gameDurationMinutes.toString().padStart("2", "0")}: {gameDurationSeconds.toString().padStart("2", "0")}
</div>

<Button onClick={onClick}>Начать сначала</Button>
<Button onClick={onClick}>Играть снова</Button>
{isLeader ? (
<Link to="/leaderboard">
<button onClick={addLeaderToList} className={styles.btnLeaderBoard} type="submit">
Перейти к лидерборду
</button>
</Link>
) : null}
{error && <p> {error}</p>}
</div>
);
}
42 changes: 40 additions & 2 deletions src/components/EndGameModal/EndGameModal.module.css
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
.modal {
width: 480px;
height: 459px;
min-height: 459px;
border-radius: 12px;
background: #c2f5ff;
display: flex;
gap: 10px;
flex-direction: column;
justify-content: center;
align-items: center;
padding-top: 18px;
padding-bottom: 28px;
}

.image {
Expand All @@ -23,8 +26,9 @@
font-style: normal;
font-weight: 400;
line-height: 48px;

margin-bottom: 28px;
width: 276px;
text-align: center;
}

.description {
Expand All @@ -49,3 +53,37 @@

margin-bottom: 40px;
}

.btnLeaderBoard {
padding-top: 10px;
background: none;
border: none;
text-decoration: underline;
cursor: pointer;
font-size: 18px;
font-family: Roboto;
font-weight: 400;
line-height: 32px;
text-align: left;
color: #565eef;
}
.userBlockName {
margin-bottom: 10px;
}
.input {
width: 276px;
height: 45px;
top: 334px;
left: 374px;
gap: 0px;
border-radius: 10px;
opacity: 0px;
border: none;
margin-bottom: 30px;
font-family: Roboto;
font-size: 24px;
font-weight: 400;
line-height: 32px;
text-align: center;
color: #999999;
}
9 changes: 9 additions & 0 deletions src/context/Context.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createContext, useState } from "react";

export const EasyContext = createContext(false);

export const EasyProvider = ({ children }) => {
const [tries, setTries] = useState(3);
const [isEasyMode, setEasyMode] = useState(false);
return <EasyContext.Provider value={{ tries, setTries, isEasyMode, setEasyMode }}>{children}</EasyContext.Provider>;
};
11 changes: 11 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}

html {
margin: 0;
}
Expand Down Expand Up @@ -35,3 +41,8 @@ ol {
font-weight: 400;
font-style: normal;
}

ul {
list-style-type: none;
}

5 changes: 4 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ import ReactDOM from "react-dom/client";
import "./index.css";
import { RouterProvider } from "react-router-dom";
import { router } from "./router";
import { EasyProvider } from "./context/Context";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<RouterProvider router={router}></RouterProvider>
<EasyProvider>
<RouterProvider router={router}></RouterProvider>
</EasyProvider>
</React.StrictMode>,
);
Loading