From 167e2323becdbf8f9ab2154bf5b50fa339526012 Mon Sep 17 00:00:00 2001 From: Kirill Date: Thu, 27 Nov 2025 17:33:04 +0300 Subject: [PATCH 01/11] Announce UserContext struct --- modules/bot/front/include/handlers.hpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/modules/bot/front/include/handlers.hpp b/modules/bot/front/include/handlers.hpp index 7440d00..313bee8 100644 --- a/modules/bot/front/include/handlers.hpp +++ b/modules/bot/front/include/handlers.hpp @@ -2,6 +2,7 @@ #include #include #include +#include /// @brief Структура возвращаемого значения класса BotHandlers для изменения текущего сообщения struct HandlerResult { @@ -9,6 +10,22 @@ struct HandlerResult { TgBot::InlineKeyboardMarkup::Ptr keyboard; }; +enum class UserState { + MAIN_MENU, // Главное меню + VIEWING_MY_TITLES, // Список моих тайтлов + AWAITING_TITLE_NAME, // Жду название тайтла для поиска + VIEWING_TITLE_PAGE, // Смотрю страничку тайтла + AWAITING_REVIEW, // Жду ревью на тайтл + VIEWING_REVIEW_LIST, // Смотрю список ревью на тайтл + VIEWING_REVIEW, // Смотрю (конкретное) ревью на тайтл + VIEWING_DESCRIPTION, // Смотрю описание тайтла +}; + +struct UserContext { + UserState state; + // Информация о тайтле +}; + class BotHandlers { public: BotHandlers(TgBot::Api api) : botApi(api) {;} @@ -28,6 +45,7 @@ public: private: TgBot::Api botApi; + std::unordered_map userContexts; void handleNavigation(TgBot::CallbackQuery::Ptr query); From bd309d38c62b4c3f8822e68d3a27e1319e48fbc4 Mon Sep 17 00:00:00 2001 From: Kirill Date: Thu, 27 Nov 2025 17:34:04 +0300 Subject: [PATCH 02/11] Fixed .gitignore --- api/.gitignore | 1 + modules/bot/front/.gitignore | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) create mode 100644 api/.gitignore diff --git a/api/.gitignore b/api/.gitignore new file mode 100644 index 0000000..c492c4d --- /dev/null +++ b/api/.gitignore @@ -0,0 +1 @@ +generated-client/ \ No newline at end of file diff --git a/modules/bot/front/.gitignore b/modules/bot/front/.gitignore index c315271..fd3551f 100644 --- a/modules/bot/front/.gitignore +++ b/modules/bot/front/.gitignore @@ -1,4 +1,2 @@ build/ -out/ -.vscode -api/generated-client \ No newline at end of file +out/ \ No newline at end of file From 7efd7bb6b03f1cb646fc8ed54f61d32c9fca6dcd Mon Sep 17 00:00:00 2001 From: Kirill Date: Thu, 27 Nov 2025 18:13:40 +0300 Subject: [PATCH 03/11] Added cursor field to UserContext --- modules/bot/front/include/handlers.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/bot/front/include/handlers.hpp b/modules/bot/front/include/handlers.hpp index 313bee8..b0c5545 100644 --- a/modules/bot/front/include/handlers.hpp +++ b/modules/bot/front/include/handlers.hpp @@ -22,8 +22,8 @@ enum class UserState { }; struct UserContext { - UserState state; - // Информация о тайтле + UserState state; // Текущее состояние пользователя + int64_t cursor; // Текущий курсор пользователя (id тайтла) }; class BotHandlers { From 12648e1a8fd449a7b6c984f1ebab3b29b8ae70ac Mon Sep 17 00:00:00 2001 From: Kirill Date: Fri, 28 Nov 2025 12:25:40 +0300 Subject: [PATCH 04/11] Changing the structure of the UserContext. Adding context history --- modules/bot/front/include/handlers.hpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/modules/bot/front/include/handlers.hpp b/modules/bot/front/include/handlers.hpp index b0c5545..558bd19 100644 --- a/modules/bot/front/include/handlers.hpp +++ b/modules/bot/front/include/handlers.hpp @@ -19,11 +19,17 @@ enum class UserState { VIEWING_REVIEW_LIST, // Смотрю список ревью на тайтл VIEWING_REVIEW, // Смотрю (конкретное) ревью на тайтл VIEWING_DESCRIPTION, // Смотрю описание тайтла + ERROR, // Ошибка состояния }; +struct NavigationStep { + UserState state; + int64_t payload; // ID тайтла, ревью и т.д. +}; + + struct UserContext { - UserState state; // Текущее состояние пользователя - int64_t cursor; // Текущий курсор пользователя (id тайтла) + std::vector history; // Текущее состояние пользователя + история предыдущих состояний }; class BotHandlers { From 28a7d9e69169dae5d1ac7f74f4eb1fd510448cd9 Mon Sep 17 00:00:00 2001 From: Kirill Date: Fri, 28 Nov 2025 12:29:53 +0300 Subject: [PATCH 05/11] Added payload constant --- modules/bot/front/include/constants.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/bot/front/include/constants.hpp b/modules/bot/front/include/constants.hpp index 75e691e..7a93931 100644 --- a/modules/bot/front/include/constants.hpp +++ b/modules/bot/front/include/constants.hpp @@ -3,6 +3,8 @@ #include namespace BotConstants { + const int64_t NULL_PAYLOAD = -1; + namespace Button { const std::string FIND_ANIME = "Найти аниме"; const std::string MY_TITLES = "Мои тайтлы"; From e09b6658b297c8f23379382ea9a6f3af2986dc2f Mon Sep 17 00:00:00 2001 From: Kirill Date: Fri, 28 Nov 2025 12:30:34 +0300 Subject: [PATCH 06/11] Bad variant of navigation handler (not working) --- modules/bot/front/src/handlers.cpp | 99 +++++++++++++++++++++++++----- 1 file changed, 85 insertions(+), 14 deletions(-) diff --git a/modules/bot/front/src/handlers.cpp b/modules/bot/front/src/handlers.cpp index 4ae1040..977751e 100644 --- a/modules/bot/front/src/handlers.cpp +++ b/modules/bot/front/src/handlers.cpp @@ -32,22 +32,41 @@ HandlerResult BotHandlers::returnMyTitles(int64_t userId) { } void BotHandlers::handleNavigation(TgBot::CallbackQuery::Ptr query) { + int64_t userId = query->from->id; + auto it = userContexts.find(userId); + if (it == userContexts.end()) { + botApi.sendMessage(query->message->chat->id, BotConstants::Text::SAD_ERROR); + return; + } + + UserContext& ctx = it->second; const std::string& data = query->data; - if (data == BotConstants::Callback::MY_TITLES) { - auto [text, kb] = BotHandlers::returnMyTitles(321); - botApi.editMessageText( - text, - query->message->chat->id, - query->message->messageId, - "", - "", - nullptr, - kb - ); - } - else { - botApi.sendMessage(query->message->chat->id, BotConstants::Text::SAD_ERROR, nullptr, nullptr); + + //HandlerResult response; + //UserContext newCtx; + + auto [response, newCtx] = newStateNavigation(query, ctx); + switch (ctx.state) { + case UserState::VIEWING_MY_TITLES: + response = BotHandlers::returnMyTitles(ctx.cursor); + break; + case UserState::VIEWING_REVIEW_LIST: + response = BotHandlers::returnReviewList(ctx.cursor); + break; + default: + botApi.sendMessage(query->message->chat->id, BotConstants::Text::SAD_ERROR); + return; } + + botApi.editMessageText( + response.message, + query->message->chat->id, + query->message->messageId, + "", + "", + nullptr, + response.keyboard + ); } void BotHandlers::handleMessage(TgBot::Message::Ptr message) { @@ -65,3 +84,55 @@ void BotHandlers::processCallbackImpl(TgBot::CallbackQuery::Ptr query) { botApi.sendMessage(query->message->chat->id, BotConstants::Text::SAD_ERROR, nullptr, nullptr); } } + +std::pair BotHandlers::newStateNavigation(const TgBot::CallbackQuery::Ptr& query, const UserContext& ctx) { + const std::string& data = query->data; + switch (ctx.state) { + case UserState::MAIN_MENU: + if(data == BotConstants::Callback::MY_TITLES) { + UserContext newCtx{.state = UserState::VIEWING_MY_TITLES, .cursor = BotConstants::CURSOR_NOT_INIT}; + HandlerResult result = returnMyTitles(query->from->id); + + return {result, newCtx}; + } + case UserState::VIEWING_MY_TITLES: + if(data == BotConstants::Callback::LIST_PREV) { + + } + else if (data == BotConstants::Callback::LIST_NEXT) { + + } + case UserState::AWAITING_TITLE_NAME: + if(data == BotConstants::Callback::LIST_PREV) { + + } + case UserState::VIEWING_TITLE_PAGE: + if(data == BotConstants::Callback::LIST_PREV) { + + } + case UserState::AWAITING_REVIEW: + if(data == BotConstants::Callback::LIST_PREV) { + + } + case UserState::VIEWING_REVIEW_LIST: + if(data == BotConstants::Callback::LIST_PREV) { + + } + else if (data == BotConstants::Callback::LIST_NEXT) { + + } + case UserState::VIEWING_REVIEW: + if(data == BotConstants::Callback::LIST_PREV) { + + } + case UserState::VIEWING_DESCRIPTION: + if(data == BotConstants::Callback::LIST_PREV) { + + } + default: + UserContext newCtx{.state = UserState::ERROR, .cursor = BotConstants::CURSOR_NOT_INIT}; + HandlerResult result = {"", nullptr}; + + return {result, newCtx}; + } +} From a8dd448c954487fe73ebd552c8fc327396dbe1a1 Mon Sep 17 00:00:00 2001 From: Kirill Date: Fri, 28 Nov 2025 12:42:11 +0300 Subject: [PATCH 07/11] Added a callback to go back through the state stack --- modules/bot/front/include/constants.hpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/bot/front/include/constants.hpp b/modules/bot/front/include/constants.hpp index 7a93931..df7b534 100644 --- a/modules/bot/front/include/constants.hpp +++ b/modules/bot/front/include/constants.hpp @@ -18,13 +18,14 @@ namespace BotConstants { const std::string ADD_STATUS = ACTION + "add_status"; const std::string STATUS = "status:"; const std::string WATCHING = STATUS + "watching"; - const std::string SEEN = STATUS + "seen"; - const std::string WANT = STATUS + "want"; - const std::string THROWN = STATUS + "thrown"; + const std::string SEEN = STATUS + "seen"; + const std::string WANT = STATUS + "want"; + const std::string THROWN = STATUS + "thrown"; const std::string NAVIGATION = "navigation:"; const std::string MY_TITLES = NAVIGATION + "my_titles"; - const std::string LIST_PREV = NAVIGATION + "prev"; - const std::string LIST_NEXT = NAVIGATION + "next"; + const std::string LIST_PREV = NAVIGATION + "prev"; // Пагинация + const std::string LIST_NEXT = NAVIGATION + "next"; // Пагинация + const std::string NAV_BACK = NAVIGATION + "back"; // Возврат по стеку состояний const std::string CHOICE = "choice:"; } namespace Text { From b368ecc43b3b8a5b5f5e85c03e57f40f7ef1805e Mon Sep 17 00:00:00 2001 From: Kirill Date: Fri, 28 Nov 2025 12:57:07 +0300 Subject: [PATCH 08/11] Added context navigation logic --- modules/bot/front/include/handlers.hpp | 11 +++++++++++ modules/bot/front/src/handlers.cpp | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/modules/bot/front/include/handlers.hpp b/modules/bot/front/include/handlers.hpp index 558bd19..935de04 100644 --- a/modules/bot/front/include/handlers.hpp +++ b/modules/bot/front/include/handlers.hpp @@ -61,4 +61,15 @@ private: /// @param userId Идентификатор пользователя /// @return HandlerResult static HandlerResult returnMyTitles(int64_t userId); + + /// @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); }; diff --git a/modules/bot/front/src/handlers.cpp b/modules/bot/front/src/handlers.cpp index 977751e..426fa6b 100644 --- a/modules/bot/front/src/handlers.cpp +++ b/modules/bot/front/src/handlers.cpp @@ -136,3 +136,13 @@ std::pair BotHandlers::newStateNavigation(const TgBo return {result, newCtx}; } } + +void BotHandlers::pushState(UserContext& ctx, UserState newState, int64_t payload) { + ctx.history.push_back({newState, payload}); +} + +bool BotHandlers::popState(UserContext& ctx) { + if (ctx.history.size() <= 1) return false; // нельзя выйти из MAIN_MENU + ctx.history.pop_back(); + return true; +} From d69f5fcddf96dc54a564e0aa223ba6be7b75a6d8 Mon Sep 17 00:00:00 2001 From: Kirill Date: Fri, 28 Nov 2025 15:18:31 +0300 Subject: [PATCH 09/11] fix(tgbot-front): start fixing navigation callback processing function --- modules/bot/front/src/handlers.cpp | 134 ++++++++++++++++++----------- 1 file changed, 85 insertions(+), 49 deletions(-) diff --git a/modules/bot/front/src/handlers.cpp b/modules/bot/front/src/handlers.cpp index 426fa6b..a3633eb 100644 --- a/modules/bot/front/src/handlers.cpp +++ b/modules/bot/front/src/handlers.cpp @@ -85,56 +85,66 @@ void BotHandlers::processCallbackImpl(TgBot::CallbackQuery::Ptr query) { } } -std::pair BotHandlers::newStateNavigation(const TgBot::CallbackQuery::Ptr& query, const UserContext& ctx) { - const std::string& data = query->data; - switch (ctx.state) { - case UserState::MAIN_MENU: - if(data == BotConstants::Callback::MY_TITLES) { - UserContext newCtx{.state = UserState::VIEWING_MY_TITLES, .cursor = BotConstants::CURSOR_NOT_INIT}; - HandlerResult result = returnMyTitles(query->from->id); - - return {result, newCtx}; - } - case UserState::VIEWING_MY_TITLES: - if(data == BotConstants::Callback::LIST_PREV) { - - } - else if (data == BotConstants::Callback::LIST_NEXT) { - - } - case UserState::AWAITING_TITLE_NAME: - if(data == BotConstants::Callback::LIST_PREV) { - - } - case UserState::VIEWING_TITLE_PAGE: - if(data == BotConstants::Callback::LIST_PREV) { - - } - case UserState::AWAITING_REVIEW: - if(data == BotConstants::Callback::LIST_PREV) { - - } - case UserState::VIEWING_REVIEW_LIST: - if(data == BotConstants::Callback::LIST_PREV) { - - } - else if (data == BotConstants::Callback::LIST_NEXT) { - - } - case UserState::VIEWING_REVIEW: - if(data == BotConstants::Callback::LIST_PREV) { - - } - case UserState::VIEWING_DESCRIPTION: - if(data == BotConstants::Callback::LIST_PREV) { - - } - default: - UserContext newCtx{.state = UserState::ERROR, .cursor = BotConstants::CURSOR_NOT_INIT}; - HandlerResult result = {"", nullptr}; - - return {result, newCtx}; +void BotHandlers::handleNavigation(TgBot::CallbackQuery::Ptr query) { + int64_t userId = query->from->id; + auto it = userContexts.find(userId); + if (it == userContexts.end()) { + // TODO: log + std::cout << "Error: Не нашел пользователя " << userId; + return; } + + UserContext& ctx = it->second; + const auto& current = ctx.history.back(); // текущий экран + const std::string& data = query->data; + + // Пагинация (в списках) + 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)) { + + int64_t newPayload = current.payload; + if (data == BotConstants::Callback::LIST_PREV && newPayload > 0) { + reducePayload(newPayload, current.state); + } else if (data == BotConstants::Callback::LIST_NEXT) { + increasePayload(newPayload, current.state); + } else { + if (data == BotConstants::Callback::LIST_PREV) { + std::cout << "Error: navigation:prev callback for 1st page" << std::endl; + return; + } + // TODO: log + std::cout << "Error: navigation:prev unknown error" << std::endl; + } + + ctx.history.back().payload = newPayload; + + auto result = renderCurrent(ctx); + editMessage(query, result); + return; + } + + // Обработка back по интерфейсу + if (data == BotConstants::Callback::NAV_BACK) { + if (!popState(ctx)) { + botApi.answerCallbackQuery(query->id, "Некуда возвращаться", true); + return; + } + auto result = renderCurrent(ctx); + editMessage(query, result); + return; + } + + // Переходы вперёд (push) + auto newStepOpt = computeNextStep(query, current); + if (!newStepOpt.has_value()) { + sendError(query->message->chat->id); + return; + } + + ctx.history.push_back(*newStepOpt); + auto result = renderCurrent(ctx); + editMessage(query, result); } void BotHandlers::pushState(UserContext& ctx, UserState newState, int64_t payload) { @@ -146,3 +156,29 @@ bool BotHandlers::popState(UserContext& ctx) { ctx.history.pop_back(); return true; } + +void BotHandlers::reducePayload(int64_t& payload, const UserState curState) { + if (curState == UserState::VIEWING_MY_TITLES || + curState == UserState::VIEWING_FOUND_TITLES) { + payload -= BotConstants::DISP_TITLES_NUM; + } else if (curState == UserState::VIEWING_REVIEW_LIST) { + payload -= BotConstants::DISP_REVIEW_NUM; + } else { + // TODO: log + payload = BotConstants::NULL_PAYLOAD; + std::cerr << "Error: reducePayload" << std::endl; + } +} + +void BotHandlers::increasePayload(int64_t& payload, const UserState curState) { + if (curState == UserState::VIEWING_MY_TITLES || + curState == UserState::VIEWING_FOUND_TITLES) { + payload += BotConstants::DISP_TITLES_NUM; + } else if (curState == UserState::VIEWING_REVIEW_LIST) { + payload += BotConstants::DISP_REVIEW_NUM; + } else { + // TODO: log + payload = BotConstants::NULL_PAYLOAD; + std::cerr << "Error: increasePayload" << std::endl; + } +} \ No newline at end of file From c815e96f4c11cd64c2afb737a2a000888a224ea5 Mon Sep 17 00:00:00 2001 From: Kirill Date: Fri, 28 Nov 2025 15:21:07 +0300 Subject: [PATCH 10/11] feat(tgbot-front): add new funcs to work with payload --- modules/bot/front/include/handlers.hpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/modules/bot/front/include/handlers.hpp b/modules/bot/front/include/handlers.hpp index 935de04..c3753b6 100644 --- a/modules/bot/front/include/handlers.hpp +++ b/modules/bot/front/include/handlers.hpp @@ -14,6 +14,7 @@ enum class UserState { MAIN_MENU, // Главное меню VIEWING_MY_TITLES, // Список моих тайтлов AWAITING_TITLE_NAME, // Жду название тайтла для поиска + VIEWING_FOUND_TITLES, // Смотрю найденные тайтлы VIEWING_TITLE_PAGE, // Смотрю страничку тайтла AWAITING_REVIEW, // Жду ревью на тайтл VIEWING_REVIEW_LIST, // Смотрю список ревью на тайтл @@ -72,4 +73,14 @@ private: /// @param ctx Текущий контекст /// @return true в случае успеха bool popState(UserContext& ctx); + + /// @brief Уменьшает значение нагрузки с учетом текущего состояния + /// @param payload Изменяемое значение нагрузки + /// @param curState Текущее состояние + void reducePayload(int64_t& payload, const UserState curState); + + /// @brief Увеличивает значение нагрузки с учетом текущего состояния + /// @param payload Изменяемое значение нагрузки + /// @param curState Текущее состояние + void increasePayload(int64_t& payload, const UserState curState); }; From 0fdf5776124ac31dcabfe7d6a09853b478c598d7 Mon Sep 17 00:00:00 2001 From: Kirill Date: Fri, 28 Nov 2025 15:22:42 +0300 Subject: [PATCH 11/11] feat(tgbot-front): add consts for titles and revs number --- modules/bot/front/include/constants.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/bot/front/include/constants.hpp b/modules/bot/front/include/constants.hpp index df7b534..e384186 100644 --- a/modules/bot/front/include/constants.hpp +++ b/modules/bot/front/include/constants.hpp @@ -3,7 +3,9 @@ #include namespace BotConstants { - const int64_t NULL_PAYLOAD = -1; + const int64_t NULL_PAYLOAD = -1; // Default value для payload + const int64_t DISP_TITLES_NUM = 6; // Количество тайтлов, отображаемых на страничке + const int64_t DISP_REVIEW_NUM = 4; // Количество ревью, отображаемых на страничке namespace Button { const std::string FIND_ANIME = "Найти аниме";