refactor(tgbot-front): replaced the context usage with a new thread-safe one
This commit is contained in:
parent
b1c035ae35
commit
a6848bb4d7
5 changed files with 55 additions and 56 deletions
|
|
@ -3,6 +3,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include "constants.hpp"
|
||||||
|
|
||||||
enum class UserState {
|
enum class UserState {
|
||||||
MAIN_MENU, // Главное меню
|
MAIN_MENU, // Главное меню
|
||||||
|
|
@ -50,6 +51,13 @@ public:
|
||||||
// Получить текущий шаг (последний в истории) или std::nullopt, если нет истории
|
// Получить текущий шаг (последний в истории) или std::nullopt, если нет истории
|
||||||
std::optional<NavigationStep> getCurrentStep(int64_t userId) const;
|
std::optional<NavigationStep> getCurrentStep(int64_t userId) const;
|
||||||
|
|
||||||
|
// pop последнего состояния. true в случае удачи
|
||||||
|
bool popStep(int64_t userId);
|
||||||
|
|
||||||
// Удалить контекст пользователя (например, при логауте)
|
// Удалить контекст пользователя (например, при логауте)
|
||||||
void removeContext(int64_t userId);
|
void removeContext(int64_t userId);
|
||||||
|
|
||||||
|
/// @brief Создает контекст начального меню для пользователя
|
||||||
|
/// @param userId
|
||||||
|
void createInitContext(int64_t userId);
|
||||||
};
|
};
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#include <structs.hpp>
|
#include <structs.hpp>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include "BotToServer.hpp"
|
#include "BotToServer.hpp"
|
||||||
|
#include "BotUserContext.hpp"
|
||||||
|
|
||||||
/// @brief Структура возвращаемого значения класса BotHandlers для изменения текущего сообщения
|
/// @brief Структура возвращаемого значения класса BotHandlers для изменения текущего сообщения
|
||||||
struct HandlerResult {
|
struct HandlerResult {
|
||||||
|
|
@ -11,30 +12,6 @@ struct HandlerResult {
|
||||||
TgBot::InlineKeyboardMarkup::Ptr keyboard;
|
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<NavigationStep> history; // Текущее состояние пользователя + история предыдущих состояний
|
|
||||||
};
|
|
||||||
|
|
||||||
class BotHandlers {
|
class BotHandlers {
|
||||||
public:
|
public:
|
||||||
BotHandlers(TgBot::Api api) : botApi(api) {;}
|
BotHandlers(TgBot::Api api) : botApi(api) {;}
|
||||||
|
|
@ -52,18 +29,15 @@ public:
|
||||||
/// @param message обрабатываемое сообщение
|
/// @param message обрабатываемое сообщение
|
||||||
void handleMessage(TgBot::Message::Ptr message);
|
void handleMessage(TgBot::Message::Ptr message);
|
||||||
|
|
||||||
/// @brief Создает контекст начального меню для пользователя
|
void initUser(int64_t userId);
|
||||||
/// @param chatId id чата пользователя
|
|
||||||
void createInitContext(int64_t chatId);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TgBot::Api botApi;
|
TgBot::Api botApi;
|
||||||
std::unordered_map<int64_t, UserContext> userContexts;
|
BotUserContext contextManager;
|
||||||
BotToServer server_;
|
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);
|
void processCallbackImpl(TgBot::CallbackQuery::Ptr query);
|
||||||
|
|
||||||
|
|
@ -73,17 +47,6 @@ private:
|
||||||
/// @return HandlerResult
|
/// @return HandlerResult
|
||||||
/// static HandlerResult returnMyTitles(int64_t userId, int64_t payload);
|
/// 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 Уменьшает значение нагрузки с учетом текущего состояния
|
/// @brief Уменьшает значение нагрузки с учетом текущего состояния
|
||||||
/// @param payload Изменяемое значение нагрузки
|
/// @param payload Изменяемое значение нагрузки
|
||||||
/// @param curState Текущее состояние
|
/// @param curState Текущее состояние
|
||||||
|
|
@ -105,7 +68,7 @@ private:
|
||||||
|
|
||||||
/// @brief Отрисовка текущего экрана (соотв. контексту)
|
/// @brief Отрисовка текущего экрана (соотв. контексту)
|
||||||
/// @param ctx - текущий контекст
|
/// @param ctx - текущий контекст
|
||||||
void renderCurrent(TgBot::CallbackQuery::Ptr query, const UserContext& ctx);
|
void renderCurrent(TgBot::CallbackQuery::Ptr query);
|
||||||
|
|
||||||
/// @brief Логика переходов между контекстами (навигация на следующий шаг)
|
/// @brief Логика переходов между контекстами (навигация на следующий шаг)
|
||||||
/// @param query - запрос
|
/// @param query - запрос
|
||||||
|
|
|
||||||
|
|
@ -41,3 +41,18 @@ void BotUserContext::removeContext(int64_t userId) {
|
||||||
std::lock_guard<std::mutex> lock(mtx);
|
std::lock_guard<std::mutex> lock(mtx);
|
||||||
userContexts.erase(userId);
|
userContexts.erase(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BotUserContext::popStep(int64_t userId) {
|
||||||
|
std::lock_guard<std::mutex> 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}});
|
||||||
|
}
|
||||||
|
|
@ -13,7 +13,7 @@ void AnimeBot::setupHandlers() {
|
||||||
bot.getEvents().onCommand("start", [this](TgBot::Message::Ptr message) {
|
bot.getEvents().onCommand("start", [this](TgBot::Message::Ptr message) {
|
||||||
sendMainMenu(message->chat->id);
|
sendMainMenu(message->chat->id);
|
||||||
//TODO: производить инициализацию контекста только после авторизации
|
//TODO: производить инициализацию контекста только после авторизации
|
||||||
handler.createInitContext(message->chat->id);
|
handler.initUser(message->from->id);
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.getEvents().onCallbackQuery([this](TgBot::CallbackQuery::Ptr query) {
|
bot.getEvents().onCallbackQuery([this](TgBot::CallbackQuery::Ptr query) {
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,15 @@
|
||||||
#include "structs.hpp"
|
#include "structs.hpp"
|
||||||
#include "constants.hpp"
|
#include "constants.hpp"
|
||||||
|
|
||||||
void BotHandlers::handleNavigation(TgBot::CallbackQuery::Ptr query, UserContext& ctx) {
|
void BotHandlers::handleNavigation(TgBot::CallbackQuery::Ptr query) {
|
||||||
const auto& current = ctx.history.back(); // текущий экран
|
//const auto& current = ctx.history.back(); // текущий экран
|
||||||
const std::string& data = query->data;
|
const std::string& data = query->data;
|
||||||
|
int64_t userId = query->from->id;
|
||||||
int64_t chatId = query->message->chat->id;
|
int64_t chatId = query->message->chat->id;
|
||||||
int64_t messageId = query->message->messageId;
|
int64_t messageId = query->message->messageId;
|
||||||
|
|
||||||
// Пагинация (в списках)
|
// Пагинация (в списках)
|
||||||
|
/* Временно отключаем, все равно не функционирует :)
|
||||||
if ((data == BotConstants::Callback::LIST_PREV || data == BotConstants::Callback::LIST_NEXT)
|
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_MY_TITLES || current.state == UserState::VIEWING_REVIEW_LIST ||
|
||||||
current.state == UserState::VIEWING_FOUND_TITLES)) {
|
current.state == UserState::VIEWING_FOUND_TITLES)) {
|
||||||
|
|
@ -32,35 +34,46 @@ void BotHandlers::handleNavigation(TgBot::CallbackQuery::Ptr query, UserContext&
|
||||||
|
|
||||||
renderCurrent(query, ctx);
|
renderCurrent(query, ctx);
|
||||||
return;
|
return;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
// Обработка back по интерфейсу
|
// Обработка back по интерфейсу
|
||||||
if (data == BotConstants::Callback::NAV_BACK) {
|
if (data == BotConstants::Callback::NAV_BACK) {
|
||||||
if (!popState(ctx)) {
|
if (!contextManager.popStep(userId)) {
|
||||||
sendError(chatId, messageId, BotConstants::Text::SAD_ERROR);
|
sendError(chatId, messageId, BotConstants::Text::SAD_ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
renderCurrent(query, ctx);
|
renderCurrent(query);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Переходы вперёд (push)
|
// Переходы вперёд (push)
|
||||||
auto newStepOpt = computeNextStep(query, current);
|
std::optional<NavigationStep> 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()) {
|
if (!newStepOpt.has_value()) {
|
||||||
sendError(chatId, messageId, BotConstants::Text::SAD_ERROR);
|
sendError(chatId, messageId, BotConstants::Text::SAD_ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.history.push_back(*newStepOpt);
|
contextManager.pushNavigationStep(userId, newStepOpt.value());
|
||||||
renderCurrent(query, ctx);
|
renderCurrent(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BotHandlers::renderCurrent(TgBot::CallbackQuery::Ptr query, const UserContext& ctx) {
|
void BotHandlers::renderCurrent(TgBot::CallbackQuery::Ptr query) {
|
||||||
const auto& step = ctx.history.back();
|
int64_t userId = query->from->id;
|
||||||
//int64_t userId = query->from->id;
|
|
||||||
int64_t chatId = query->message->chat->id;
|
int64_t chatId = query->message->chat->id;
|
||||||
int64_t messageId = query->message->messageId;
|
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:
|
case UserState::MAIN_MENU:
|
||||||
editMessage(chatId, messageId, showMainMenu());
|
editMessage(chatId, messageId, showMainMenu());
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue