Chooser App - это интерактивное веб-приложение, созданное для вечеринок и компаний друзей. Оно позволяет выбрать случайного участника из группы с помощью простого и забавного процесса: все игроки одновременно кладут пальцы на сенсорный экран устройства. Приложение поддерживает два режима игры:
- Простой режим: Быстро выбирается один "счастливчик".
- Режим с заданиями: Выбранному игроку выдается задание. Задания могут браться из встроенной базы данных или генерироваться с помощью Искусственного Интеллекта (Google Gemini), если включена соответствующая опция. В этом режиме также можно включить ограничение по времени.
Приложение построено на стеке MERN (MongoDB, Express, React, Node.js).
- Node.js (рекомендуется LTS версия, например, 18.x или 20.x)
- npm (обычно идет с Node.js)
- Аккаунт MongoDB Atlas (или локально установленная MongoDB)
- API ключ Google Generative AI (для функции AI-заданий). Получить ключ
- Клонируйте репозиторий:
git clone https://github.com/AlemzhanJ/chooseApp cd chooseApp
- Установите зависимости для бэкенда:
cd server npm install
- Установите зависимости для фронтенда:
cd ../client npm install
- Бэкенд:
- Перейдите в директорию
server/
. - Создайте файл
.env
. - Добавьте в него строку подключения к вашей MongoDB:
(Замените
MONGODB_URI=mongodb+srv://<user>:<password>@<cluster-url>/<database-name>?retryWrites=true&w=majority
<...>
на ваши реальные данные) - Добавьте порт для сервера (по умолчанию 5000):
PORT=5000
- Добавьте ваш API ключ для Google Gemini:
GEMINI_API_KEY=ВАШ_GOOGLE_GEMINI_API_KEY
- Перейдите в директорию
- Фронтенд:
- В файле
client/package.json
уже настроенproxy
для перенаправления запросов/api
наhttp://localhost:5000
во время локальной разработки.
- В файле
-
Запустите бэкенд (из директории
server/
):npm run dev
Сервер запустится на порту, указанном в
.env
(или 5000). Вы должны увидеть сообщения о старте сервера и подключении к MongoDB. -
Запустите фронтенд (из директории
client/
, в новом окне терминала):npm start
Приложение откроется в браузере по адресу
http://localhost:3000
. -
Инициализация: Настройка MERN-проекта с базовой структурой для бэкенда (Express, Mongoose) и фронтенда (Create React App).
-
Проектирование: Определение схем MongoDB для Задач (
Task
) и Игр (Game
), разработка API эндпоинтов для управления ими. -
Реализация UI: Создание основных экранов (Старт, Настройки, Игра) и компонентов (Область касания, Анимация выбора, Отображение задания/победителя) с использованием React и CSS.
-
Интеграция: Связывание фронтенда с бэкендом через API-сервис (
axios
), реализация игрового цикла. -
Обработка касаний: Использование Touch Events API для отслеживания нескольких одновременных касаний.
-
Рефакторинг: Вынесение логики API-вызовов в отдельный сервис (
services/api.js
), замена стандартных кнопок на переиспользуемый компонент (Button.js
). -
AI-интеграция (Бонус): Добавление сервиса для взаимодействия с Google Gemini API (
ai-service.js
), создание эндпоинта для генерации задач, интеграция с логикой игры и UI. -
Улучшения UI: Добавление пояснений для игроков, улучшение читаемости элементов.
-
Подготовка к деплою: Настройка CORS, создание шаблона
README.md
.
- Обработка Multi-Touch: Использование нативного Touch Events API для определения количества и удержания пальцев на экране.
- Генерация контента AI: Интеграция с Google Gemini API (
@google/generative-ai
) для динамической генерации игровых заданий по запросу пользователя, с учетом выбранной сложности. - Анимация выбора: Визуализация процесса выбора путем поочередного подсвечивания активных пальцев с ускорением (реализовано через
setTimeout
и CSS). - Структура API: Использование сервисного слоя (
services/api.js
) на фронтенде для инкапсуляции логики взаимодействия с бэкендом.
- Анимация: Использована простая JavaScript/CSS анимация поочередного подсвечивания вместо более сложных визуальных эффектов для ускорения разработки.
- Обновление состояния: Приложение не использует WebSocket или Server-Sent Events для обновлений в реальном времени. Состояние игры обновляется после действий пользователя или при перезагрузке (в
GameScreen
есть TODO на эту тему). - Сохранение AI-задач: Сгенерированные AI задачи не сохраняются в базу данных по умолчанию для простоты. Они генерируются "на лету".
- Аутентификация: Эндпоинт для добавления новых задач (
POST /api/tasks
) не защищен, предполагается, что база данных будет наполнена заранее или доступ будет ограничен другими способами (например, на уровне сети при деплое).
- Удаление игр: Завершенные игры автоматически удаляются при выходе пользователя с экрана игры (
GameScreen
) путем вызоваDELETE /api/games/:gameId
(реализовано как fire-and-forget). - Совместимость Touch Events: Работа
FingerPlacementArea
зависит от корректной реализации Touch Events API браузером и устройством. На редких или старых устройствах могут быть нюансы. - Тестирование касаний: Без сенсорного экрана тестирование функции размещения пальцев затруднено (кнопка симуляции была удалена). Необходимо использовать реальное устройство или инструменты разработчика в браузере.
- Зависимость от внешних сервисов: Работа AI-генерации зависит от доступности Google Gemini API и наличия правильного API-ключа. Возможны ошибки при проблемах с сетью или API.
- Деплой: При деплое необходимо корректно настроить переменные окружения (
MONGODB_URI
,GEMINI_API_KEY
,PORT
), CORS и IP Whitelist в MongoDB Atlas.
В ходе разработки у меня возник ряд сложных проблем, потребовавших итеративной отладки:
-
Несоответствие выбранного игрока и исполнителя задания:
- Проблема: Анимация выбора на клиенте останавливалась на одном игроке, а задание после вызова API назначалось другому.
- Причина: Изначально и клиент (визуально), и сервер (логически) выбирали случайного игрока независимо.
- Решение: Изменена логика. Клиент выбирает игрока в конце анимации и отправляет его ID на сервер. Сервер валидирует этот ID и использует его для назначения задания, устраняя расхождение.
-
Задание не завершалось при удержании пальца в зонах ДА/НЕТ:
- Проблема: Игрок удерживал палец в зоне ответа более 2 секунд, но игра не реагировала, задание продолжалось.
- Причина: Обнаружено несколько проблем:
- Некорректная передача аргументов (
action
иfingerId
) из компонентаFingerPlacementArea
в обработчикhandleTaskAction
вGameScreen
. - Ошибка на сервере в
updatePlayerStatus
при поиске игрока из-за неверного сравнения типовfingerId
(число и строка). - Потенциальные проблемы с обновлением состояния React (
gameData
,currentTaskDetails
) после асинхронных вызовов API, что приводило к отображению устаревшего состояния.
- Некорректная передача аргументов (
- Решение:
- Исправлен порядок аргументов при вызове колбэка
onTaskAction
. - Исправлено сравнение
fingerId
на сервере с использованиемparseInt
. - Упрощена логика обновления состояния в
handlePlayerAction
на клиенте для немедленного отображения результата. - Активно использовалась отладочная информация, выводимая прямо в интерфейс приложения, для отслеживания состояния и ответов API в реальном времени на мобильном устройстве.
- Исправлен порядок аргументов при вызове колбэка
-
Визуальные проблемы с подсветкой выбранного игрока:
- Проблема: Выбранный игрок не подсвечивался красным цветом после завершения анимации или подсветка сразу сбрасывалась.
- Причина: Конфликт между установкой подсветки после ответа API и ее сбросом в функции очистки
useEffect
анимации, а также проблемы с timing'ом обновления состояния React. - Решение: Логика установки и сброса
highlightedFingerId
была скорректирована, чтобы избежать преждевременного сброса и обеспечить своевременное отображение.
Этот итеративный процесс отладки с анализом логов (как серверных, так и клиентских через UI) позволил выявить и устранить неочевидные ошибки взаимодействия между фронтендом, бэкендом и асинхронными операциями. Клиентские логи при этом отслеживать было невозможно, потому что тестирование и отладка - всё на телефоне. Я не знал, как пользоваться инструментами разработчика на телефоне, поэтому мне пришлось писать код для отладочных боксов со всех нужной информацией. Также в процессе я сделал очень много коммитов, потому что каждое изменения мне нужно было деплоить, чтобы я мог протестировать изменения именно на телефоне (разбираться с ngrok было лень).
Стек MERN (MongoDB, Express, React, Node.js) был выбран по следующим причинам:
- Node.js/Express: Позволяет использовать JavaScript для бэкенда, что упрощает разработку при наличии опыта с JS/React. Express - минималистичный и гибкий фреймворк для создания API.
- MongoDB: Гибкость NoSQL схемы хорошо подходит для проекта, где структура данных (особенно для Игр) может развиваться. Mongoose предоставляет удобный интерфейс для работы с MongoDB из Node.js.
- React: Мощная и популярная библиотека для создания динамических пользовательских интерфейсов. Компонентный подход упрощает разработку и поддержку UI.
- Единый язык: Использование JavaScript на всех уровнях (фронтенд, бэкенд) ускоряет разработку и упрощает обмен кодом/логикой.
- Большое сообщество: Обширная документация, множество библиотек и активное сообщество для всех частей стека.