refactor(tgbot-front): moved the navigation handler to a separate file
This commit is contained in:
parent
a22c96e7a0
commit
19164b8d9d
3 changed files with 150 additions and 145 deletions
|
|
@ -71,7 +71,7 @@ private:
|
||||||
/// @param userId Идентификатор пользователя
|
/// @param userId Идентификатор пользователя
|
||||||
/// @param payload Полезная нагрузка
|
/// @param payload Полезная нагрузка
|
||||||
/// @return HandlerResult
|
/// @return HandlerResult
|
||||||
static HandlerResult returnMyTitles(int64_t userId, int64_t payload);
|
/// static HandlerResult returnMyTitles(int64_t userId, int64_t payload);
|
||||||
|
|
||||||
/// @brief Вход в новое состояние
|
/// @brief Вход в новое состояние
|
||||||
/// @param ctx текущий контекст
|
/// @param ctx текущий контекст
|
||||||
|
|
|
||||||
147
modules/bot/front/src/handleNavigation.cpp
Normal file
147
modules/bot/front/src/handleNavigation.cpp
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
#include "handlers.hpp"
|
||||||
|
#include "KeyboardFactory.hpp"
|
||||||
|
#include "structs.hpp"
|
||||||
|
#include "constants.hpp"
|
||||||
|
|
||||||
|
void BotHandlers::handleNavigation(TgBot::CallbackQuery::Ptr query, UserContext& ctx) {
|
||||||
|
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(query, ctx);
|
||||||
|
if(result.message == "meow") return; // TODO: убрать
|
||||||
|
editMessage(query, result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обработка back по интерфейсу
|
||||||
|
if (data == BotConstants::Callback::NAV_BACK) {
|
||||||
|
if (!popState(ctx)) {
|
||||||
|
sendError(query, BotConstants::Text::SAD_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto result = renderCurrent(query, ctx);
|
||||||
|
if(result.message == "meow") return; // TODO: убрать
|
||||||
|
editMessage(query, result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Переходы вперёд (push)
|
||||||
|
auto newStepOpt = computeNextStep(query, current);
|
||||||
|
if (!newStepOpt.has_value()) {
|
||||||
|
sendError(query, BotConstants::Text::SAD_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.history.push_back(*newStepOpt);
|
||||||
|
auto result = renderCurrent(query, ctx);
|
||||||
|
if(result.message == "meow") return; // TODO: убрать
|
||||||
|
editMessage(query, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
HandlerResult BotHandlers::renderCurrent(TgBot::CallbackQuery::Ptr query, const UserContext& ctx) {
|
||||||
|
const auto& step = ctx.history.back();
|
||||||
|
int64_t userId = query->from->id;
|
||||||
|
switch (step.state) {
|
||||||
|
case UserState::MAIN_MENU:
|
||||||
|
return showMainMenu();
|
||||||
|
case UserState::VIEWING_MY_TITLES:
|
||||||
|
server_.fetchUserTitlesAsync(std::to_string(2)) // ALARM: тестовое значение вместо userId
|
||||||
|
.then([this, query](pplx::task<std::vector<BotStructs::Title>> t) {
|
||||||
|
try {
|
||||||
|
auto titles = t.get();
|
||||||
|
|
||||||
|
std::string message = formatTitlesList(titles);
|
||||||
|
auto keyboard = KeyboardFactory::createMyTitles(titles);
|
||||||
|
|
||||||
|
editMessage(query, {message, keyboard});
|
||||||
|
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
sendError(query, BotConstants::Text::SERVER_ERROR);
|
||||||
|
// Логирование ошибки (например, в cerr)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {"meow", nullptr};
|
||||||
|
/*
|
||||||
|
case UserState::VIEWING_TITLE_PAGE:
|
||||||
|
return returnTitlePage(step.payload); // payload = titleId
|
||||||
|
case UserState::VIEWING_REVIEW:
|
||||||
|
return returnReview(step.payload); // payload = reviewId
|
||||||
|
case UserState::AWAITING_REVIEW:
|
||||||
|
return HandlerResult{"Пришлите текст отзыва:", nullptr};
|
||||||
|
// ...
|
||||||
|
*/
|
||||||
|
default:
|
||||||
|
return HandlerResult{BotConstants::Text::SAD_ERROR, nullptr};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<NavigationStep> BotHandlers::computeNextStep(
|
||||||
|
const TgBot::CallbackQuery::Ptr& query,
|
||||||
|
const NavigationStep& current
|
||||||
|
) {
|
||||||
|
const std::string& data = query->data;
|
||||||
|
|
||||||
|
switch (current.state) {
|
||||||
|
case UserState::MAIN_MENU:
|
||||||
|
if (data == BotConstants::Callback::MY_TITLES) {
|
||||||
|
return NavigationStep{UserState::VIEWING_MY_TITLES, 0};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
/*
|
||||||
|
case UserState::VIEWING_MY_TITLES:
|
||||||
|
if (data.starts_with("title_")) {
|
||||||
|
int64_t titleId = parseId(data);
|
||||||
|
return NavigationStep{UserState::VIEWING_TITLE_PAGE, titleId};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UserState::VIEWING_TITLE_PAGE:
|
||||||
|
if (data == BotConstants::Callback::ACTION_ADD_REVIEW) {
|
||||||
|
return NavigationStep{UserState::AWAITING_REVIEW, current.payload};
|
||||||
|
}
|
||||||
|
if (data.starts_with("review_")) {
|
||||||
|
int64_t reviewId = parseId(data);
|
||||||
|
return NavigationStep{UserState::VIEWING_REVIEW, reviewId};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
*/
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string BotHandlers::formatTitlesList(const std::vector<BotStructs::Title>& titles) {
|
||||||
|
if (titles.empty()) {
|
||||||
|
return "У вас пока нет тайтлов.";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string msg;
|
||||||
|
for (size_t i = 0; i < titles.size(); ++i) {
|
||||||
|
// num — 0-based, но в сообщении показываем 1-based
|
||||||
|
msg += std::to_string(i + 1) + ". " + titles[i].name + "\n";
|
||||||
|
}
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
@ -20,6 +20,7 @@ void BotHandlers::handleCallback(TgBot::CallbackQuery::Ptr query) {
|
||||||
processCallbackImpl(query);
|
processCallbackImpl(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* deprecated. will be deleted soon.
|
||||||
HandlerResult BotHandlers::returnMyTitles(int64_t userId, int64_t payload) {
|
HandlerResult BotHandlers::returnMyTitles(int64_t userId, int64_t payload) {
|
||||||
// Здесь должен происходить запрос на сервер
|
// Здесь должен происходить запрос на сервер
|
||||||
std::vector<BotStructs::Title> titles = {{123, "Школа мертвяков", "", 1}, {321, "KissXsis", "", 2}};
|
std::vector<BotStructs::Title> titles = {{123, "Школа мертвяков", "", 1}, {321, "KissXsis", "", 2}};
|
||||||
|
|
@ -29,7 +30,7 @@ HandlerResult BotHandlers::returnMyTitles(int64_t userId, int64_t payload) {
|
||||||
result.message = "1. Школа мертвяков\n2. KissXsis\n";
|
result.message = "1. Школа мертвяков\n2. KissXsis\n";
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
void BotHandlers::handleMessage(TgBot::Message::Ptr message) {
|
void BotHandlers::handleMessage(TgBot::Message::Ptr message) {
|
||||||
//TODO: просмотр состояния пользователя
|
//TODO: просмотр состояния пользователя
|
||||||
|
|
@ -60,62 +61,6 @@ void BotHandlers::processCallbackImpl(TgBot::CallbackQuery::Ptr query) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BotHandlers::handleNavigation(TgBot::CallbackQuery::Ptr query, UserContext& ctx) {
|
|
||||||
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(query, ctx);
|
|
||||||
if(result.message == "meow") return; // TODO: убрать
|
|
||||||
editMessage(query, result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Обработка back по интерфейсу
|
|
||||||
if (data == BotConstants::Callback::NAV_BACK) {
|
|
||||||
if (!popState(ctx)) {
|
|
||||||
sendError(query, BotConstants::Text::SAD_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto result = renderCurrent(query, ctx);
|
|
||||||
if(result.message == "meow") return; // TODO: убрать
|
|
||||||
editMessage(query, result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Переходы вперёд (push)
|
|
||||||
auto newStepOpt = computeNextStep(query, current);
|
|
||||||
if (!newStepOpt.has_value()) {
|
|
||||||
sendError(query, BotConstants::Text::SAD_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.history.push_back(*newStepOpt);
|
|
||||||
auto result = renderCurrent(query, ctx);
|
|
||||||
if(result.message == "meow") return; // TODO: убрать
|
|
||||||
editMessage(query, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BotHandlers::pushState(UserContext& ctx, UserState newState, int64_t payload) {
|
void BotHandlers::pushState(UserContext& ctx, UserState newState, int64_t payload) {
|
||||||
ctx.history.push_back({newState, payload});
|
ctx.history.push_back({newState, payload});
|
||||||
}
|
}
|
||||||
|
|
@ -164,44 +109,6 @@ void BotHandlers::editMessage(TgBot::CallbackQuery::Ptr query, HandlerResult res
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
HandlerResult BotHandlers::renderCurrent(TgBot::CallbackQuery::Ptr query, const UserContext& ctx) {
|
|
||||||
const auto& step = ctx.history.back();
|
|
||||||
int64_t userId = query->from->id;
|
|
||||||
switch (step.state) {
|
|
||||||
case UserState::MAIN_MENU:
|
|
||||||
return showMainMenu();
|
|
||||||
case UserState::VIEWING_MY_TITLES:
|
|
||||||
server_.fetchUserTitlesAsync(std::to_string(2)) // ALARM: тестовое значение вместо userId
|
|
||||||
.then([this, query](pplx::task<std::vector<BotStructs::Title>> t) {
|
|
||||||
try {
|
|
||||||
auto titles = t.get();
|
|
||||||
|
|
||||||
std::string message = formatTitlesList(titles);
|
|
||||||
auto keyboard = KeyboardFactory::createMyTitles(titles);
|
|
||||||
|
|
||||||
editMessage(query, {message, keyboard});
|
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
sendError(query, BotConstants::Text::SERVER_ERROR);
|
|
||||||
// Логирование ошибки (например, в cerr)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {"meow", nullptr};
|
|
||||||
/*
|
|
||||||
case UserState::VIEWING_TITLE_PAGE:
|
|
||||||
return returnTitlePage(step.payload); // payload = titleId
|
|
||||||
case UserState::VIEWING_REVIEW:
|
|
||||||
return returnReview(step.payload); // payload = reviewId
|
|
||||||
case UserState::AWAITING_REVIEW:
|
|
||||||
return HandlerResult{"Пришлите текст отзыва:", nullptr};
|
|
||||||
// ...
|
|
||||||
*/
|
|
||||||
default:
|
|
||||||
return HandlerResult{BotConstants::Text::SAD_ERROR, nullptr};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HandlerResult BotHandlers::showMainMenu() {
|
HandlerResult BotHandlers::showMainMenu() {
|
||||||
auto keyboard = KeyboardFactory::createMainMenu();
|
auto keyboard = KeyboardFactory::createMainMenu();
|
||||||
|
|
||||||
|
|
@ -221,42 +128,6 @@ void BotHandlers::sendError(TgBot::CallbackQuery::Ptr query, const std::string&
|
||||||
editMessage(query, {errText, keyboard});
|
editMessage(query, {errText, keyboard});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<NavigationStep> BotHandlers::computeNextStep(
|
|
||||||
const TgBot::CallbackQuery::Ptr& query,
|
|
||||||
const NavigationStep& current
|
|
||||||
) {
|
|
||||||
const std::string& data = query->data;
|
|
||||||
|
|
||||||
switch (current.state) {
|
|
||||||
case UserState::MAIN_MENU:
|
|
||||||
if (data == BotConstants::Callback::MY_TITLES) {
|
|
||||||
return NavigationStep{UserState::VIEWING_MY_TITLES, 0};
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
/*
|
|
||||||
case UserState::VIEWING_MY_TITLES:
|
|
||||||
if (data.starts_with("title_")) {
|
|
||||||
int64_t titleId = parseId(data);
|
|
||||||
return NavigationStep{UserState::VIEWING_TITLE_PAGE, titleId};
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case UserState::VIEWING_TITLE_PAGE:
|
|
||||||
if (data == BotConstants::Callback::ACTION_ADD_REVIEW) {
|
|
||||||
return NavigationStep{UserState::AWAITING_REVIEW, current.payload};
|
|
||||||
}
|
|
||||||
if (data.starts_with("review_")) {
|
|
||||||
int64_t reviewId = parseId(data);
|
|
||||||
return NavigationStep{UserState::VIEWING_REVIEW, reviewId};
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
*/
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BotHandlers::createInitContext(int64_t chatId) {
|
void BotHandlers::createInitContext(int64_t chatId) {
|
||||||
NavigationStep init = {UserState::MAIN_MENU, BotConstants::NULL_PAYLOAD};
|
NavigationStep init = {UserState::MAIN_MENU, BotConstants::NULL_PAYLOAD};
|
||||||
userContexts[chatId] = {chatId, {init}};
|
userContexts[chatId] = {chatId, {init}};
|
||||||
|
|
@ -276,17 +147,4 @@ void BotHandlers::handleError(TgBot::CallbackQuery::Ptr query, UserContext& ctx)
|
||||||
HandlerResult result = {BotConstants::Text::AUTH_ERROR, nullptr};
|
HandlerResult result = {BotConstants::Text::AUTH_ERROR, nullptr};
|
||||||
editMessage(query, result);
|
editMessage(query, result);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
std::string BotHandlers::formatTitlesList(const std::vector<BotStructs::Title>& titles) {
|
|
||||||
if (titles.empty()) {
|
|
||||||
return "У вас пока нет тайтлов.";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string msg;
|
|
||||||
for (size_t i = 0; i < titles.size(); ++i) {
|
|
||||||
// num — 0-based, но в сообщении показываем 1-based
|
|
||||||
msg += std::to_string(i + 1) + ". " + titles[i].name + "\n";
|
|
||||||
}
|
|
||||||
return msg;
|
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue