diff --git a/modules/bot/front/include/BotUserContext.hpp b/modules/bot/front/include/BotUserContext.hpp index bf69058..a3e2609 100644 --- a/modules/bot/front/include/BotUserContext.hpp +++ b/modules/bot/front/include/BotUserContext.hpp @@ -3,6 +3,7 @@ #include #include #include +#include "constants.hpp" enum class UserState { MAIN_MENU, // Главное меню @@ -50,6 +51,13 @@ public: // Получить текущий шаг (последний в истории) или std::nullopt, если нет истории std::optional getCurrentStep(int64_t userId) const; + // pop последнего состояния. true в случае удачи + bool popStep(int64_t userId); + // Удалить контекст пользователя (например, при логауте) void removeContext(int64_t userId); + + /// @brief Создает контекст начального меню для пользователя + /// @param userId + void createInitContext(int64_t userId); }; \ No newline at end of file diff --git a/modules/bot/front/include/handlers.hpp b/modules/bot/front/include/handlers.hpp index 417025f..327c17b 100644 --- a/modules/bot/front/include/handlers.hpp +++ b/modules/bot/front/include/handlers.hpp @@ -4,6 +4,7 @@ #include #include #include "BotToServer.hpp" +#include "BotUserContext.hpp" /// @brief Структура возвращаемого значения класса BotHandlers для изменения текущего сообщения struct HandlerResult { @@ -11,30 +12,6 @@ struct HandlerResult { TgBot::InlineKeyboardMarkup::Ptr keyboard; }; -enum class UserState { - MAIN_MENU, // Главное меню - VIEWING_MY_TITLES, // Список моих тайтлов - AWAITING_TITLE_NAME, // Жду название тайтла для поиска - VIEWING_FOUND_TITLES, // Смотрю найденные тайтлы - VIEWING_TITLE_PAGE, // Смотрю страничку тайтла - AWAITING_REVIEW, // Жду ревью на тайтл - VIEWING_REVIEW_LIST, // Смотрю список ревью на тайтл - VIEWING_REVIEW, // Смотрю (конкретное) ревью на тайтл - VIEWING_DESCRIPTION, // Смотрю описание тайтла - ERROR, // Ошибка состояния -}; - -struct NavigationStep { - UserState state; - int64_t payload; // ID тайтла, ревью и т.д. -}; - - -struct UserContext { - int64_t userId; - std::vector history; // Текущее состояние пользователя + история предыдущих состояний -}; - class BotHandlers { public: BotHandlers(TgBot::Api api) : botApi(api) {;} @@ -51,19 +28,16 @@ public: /// в боте. /// @param message обрабатываемое сообщение void handleMessage(TgBot::Message::Ptr message); - - /// @brief Создает контекст начального меню для пользователя - /// @param chatId id чата пользователя - void createInitContext(int64_t chatId); + void initUser(int64_t userId); private: TgBot::Api botApi; - std::unordered_map userContexts; + BotUserContext contextManager; BotToServer server_; - void handleNavigation(TgBot::CallbackQuery::Ptr query, UserContext& ctx); + void handleNavigation(TgBot::CallbackQuery::Ptr query); - void handleError(TgBot::CallbackQuery::Ptr query, UserContext& ctx); + void handleError(TgBot::CallbackQuery::Ptr query); void processCallbackImpl(TgBot::CallbackQuery::Ptr query); @@ -73,17 +47,6 @@ private: /// @return HandlerResult /// static HandlerResult returnMyTitles(int64_t userId, int64_t payload); - /// @brief Вход в новое состояние - /// @param ctx текущий контекст - /// @param newState новое состояние, добавляемое в стек - /// @param payload полезная нагрузка этого состояния - void pushState(UserContext& ctx, UserState newState, int64_t payload); - - /// @brief Возврат в предыдущее состояние - /// @param ctx Текущий контекст - /// @return true в случае успеха - bool popState(UserContext& ctx); - /// @brief Уменьшает значение нагрузки с учетом текущего состояния /// @param payload Изменяемое значение нагрузки /// @param curState Текущее состояние @@ -105,7 +68,7 @@ private: /// @brief Отрисовка текущего экрана (соотв. контексту) /// @param ctx - текущий контекст - void renderCurrent(TgBot::CallbackQuery::Ptr query, const UserContext& ctx); + void renderCurrent(TgBot::CallbackQuery::Ptr query); /// @brief Логика переходов между контекстами (навигация на следующий шаг) /// @param query - запрос diff --git a/modules/bot/front/src/BotUserContext.cpp b/modules/bot/front/src/BotUserContext.cpp index 04a423c..2b2eb41 100644 --- a/modules/bot/front/src/BotUserContext.cpp +++ b/modules/bot/front/src/BotUserContext.cpp @@ -40,4 +40,19 @@ std::optional BotUserContext::getCurrentStep(int64_t userId) con void BotUserContext::removeContext(int64_t userId) { std::lock_guard lock(mtx); userContexts.erase(userId); +} + +bool BotUserContext::popStep(int64_t userId) { + std::lock_guard lock(mtx); + auto it = userContexts.find(userId); + if (it != userContexts.end() && (it->second.history.size() > 1)) { + it->second.history.pop_back(); + return true; + } + return false; +} + +void BotUserContext::createInitContext(int64_t userId) { + NavigationStep initStep = {UserState::MAIN_MENU, BotConstants::NULL_PAYLOAD}; + setContext(userId, {userId, {initStep}}); } \ No newline at end of file diff --git a/modules/bot/front/src/front.cpp b/modules/bot/front/src/front.cpp index 4f463e1..b9abd73 100644 --- a/modules/bot/front/src/front.cpp +++ b/modules/bot/front/src/front.cpp @@ -13,7 +13,7 @@ void AnimeBot::setupHandlers() { bot.getEvents().onCommand("start", [this](TgBot::Message::Ptr message) { sendMainMenu(message->chat->id); //TODO: производить инициализацию контекста только после авторизации - handler.createInitContext(message->chat->id); + handler.initUser(message->from->id); }); bot.getEvents().onCallbackQuery([this](TgBot::CallbackQuery::Ptr query) { diff --git a/modules/bot/front/src/handleNavigation.cpp b/modules/bot/front/src/handleNavigation.cpp index b0515ae..b8af2ef 100644 --- a/modules/bot/front/src/handleNavigation.cpp +++ b/modules/bot/front/src/handleNavigation.cpp @@ -3,13 +3,15 @@ #include "structs.hpp" #include "constants.hpp" -void BotHandlers::handleNavigation(TgBot::CallbackQuery::Ptr query, UserContext& ctx) { - const auto& current = ctx.history.back(); // текущий экран +void BotHandlers::handleNavigation(TgBot::CallbackQuery::Ptr query) { + //const auto& current = ctx.history.back(); // текущий экран const std::string& data = query->data; + int64_t userId = query->from->id; int64_t chatId = query->message->chat->id; int64_t messageId = query->message->messageId; // Пагинация (в списках) + /* Временно отключаем, все равно не функционирует :) if ((data == BotConstants::Callback::LIST_PREV || data == BotConstants::Callback::LIST_NEXT) && (current.state == UserState::VIEWING_MY_TITLES || current.state == UserState::VIEWING_REVIEW_LIST || current.state == UserState::VIEWING_FOUND_TITLES)) { @@ -32,35 +34,46 @@ void BotHandlers::handleNavigation(TgBot::CallbackQuery::Ptr query, UserContext& renderCurrent(query, ctx); return; - } + }*/ // Обработка back по интерфейсу if (data == BotConstants::Callback::NAV_BACK) { - if (!popState(ctx)) { + if (!contextManager.popStep(userId)) { sendError(chatId, messageId, BotConstants::Text::SAD_ERROR); return; } - renderCurrent(query, ctx); + renderCurrent(query); return; } // Переходы вперёд (push) - auto newStepOpt = computeNextStep(query, current); + std::optional currentStep = contextManager.getCurrentStep(userId); + if(!currentStep.has_value()) { + sendError(chatId, messageId, BotConstants::Text::SAD_ERROR); + return; + } + auto newStepOpt = computeNextStep(query, currentStep.value()); if (!newStepOpt.has_value()) { sendError(chatId, messageId, BotConstants::Text::SAD_ERROR); return; } - ctx.history.push_back(*newStepOpt); - renderCurrent(query, ctx); + contextManager.pushNavigationStep(userId, newStepOpt.value()); + renderCurrent(query); } -void BotHandlers::renderCurrent(TgBot::CallbackQuery::Ptr query, const UserContext& ctx) { - const auto& step = ctx.history.back(); - //int64_t userId = query->from->id; +void BotHandlers::renderCurrent(TgBot::CallbackQuery::Ptr query) { + int64_t userId = query->from->id; int64_t chatId = query->message->chat->id; int64_t messageId = query->message->messageId; - switch (step.state) { + + auto step = contextManager.getCurrentStep(userId); + if(!step.has_value()) {; + sendError(chatId, messageId, BotConstants::Text::SAD_ERROR); + return; + } + + switch (step.value().state) { case UserState::MAIN_MENU: editMessage(chatId, messageId, showMainMenu()); return;