diff --git a/src/backend/api/ingredients.hpp b/src/backend/api/ingredients.hpp index 7288e408..4ec684e4 100644 --- a/src/backend/api/ingredients.hpp +++ b/src/backend/api/ingredients.hpp @@ -30,8 +30,7 @@ class IngredientsApi : ApiBase { void putToStorage(UserId user, StorageId storage, IngredientId ingredient) const; void deleteFromStorage(UserId user, StorageId storage, IngredientId ingredient) const; - void - deleteMultipleFromStorage(UserId user, StorageId storage, const std::vector& ingredients = {}) const; + void deleteMultipleFromStorage(UserId user, StorageId storage, const std::vector& ingredients) const; [[nodiscard]] models::ingredient::IngredientSearchForStorageResponse searchForStorage(UserId user, diff --git a/src/handlers/CMakeLists.txt b/src/handlers/CMakeLists.txt index 8b88884b..54dfd8d6 100644 --- a/src/handlers/CMakeLists.txt +++ b/src/handlers/CMakeLists.txt @@ -22,8 +22,8 @@ target_sources(main PRIVATE src/handlers/personal_account/view.cpp src/handlers/personal_account/publication_history.cpp - src/handlers/suggested_recipe/add_storage.cpp - src/handlers/suggested_recipe/view.cpp + src/handlers/cooking_planning/add_storage.cpp + src/handlers/cooking_planning/view.cpp src/handlers/recipes_suggestions/view.cpp @@ -50,4 +50,6 @@ target_sources(main PRIVATE src/handlers/recipes_search/view.cpp src/handlers/recipe/view.cpp + + src/handlers/cooking/ingredients_spending.cpp ) diff --git a/src/handlers/commands/start.cpp b/src/handlers/commands/start.cpp index 8749ae54..05d32231 100644 --- a/src/handlers/commands/start.cpp +++ b/src/handlers/commands/start.cpp @@ -61,7 +61,7 @@ void handleStartCmd(MessageRef m, BotRef bot, SMRef stateManager, api::ApiClient auto recipe = api.getRecipesApi().get(userId, *mRecipeId); message::deleteMessageId(userId); renderRecipeView(recipe, *mRecipeId, userId, chatId, bot); - stateManager.put(RecipeView{.prevState = std::nullopt, .recipe = std::move(recipe)}); + stateManager.put(RecipeView{.prevState = std::nullopt, .recipe = std::move(recipe), .recipeId = *mRecipeId}); return; } }; diff --git a/src/handlers/commands/wanna_eat.cpp b/src/handlers/commands/wanna_eat.cpp index a33c15ae..52deb48f 100644 --- a/src/handlers/commands/wanna_eat.cpp +++ b/src/handlers/commands/wanna_eat.cpp @@ -10,12 +10,15 @@ #include "utils/utils.hpp" #include +#include +#include namespace cookcookhnya::handlers::commands { using namespace render::select_storages; using namespace render::main_menu; using namespace render::recipes_suggestions; +using namespace std::views; void handleWannaEatCmd(MessageRef m, BotRef bot, SMRef stateManager, api::ApiClientRef api) { auto storages = api.getStoragesApi().getStoragesList(m.from->id); @@ -25,15 +28,12 @@ void handleWannaEatCmd(MessageRef m, BotRef bot, SMRef stateManager, api::ApiCli stateManager.put(MainMenu{}); } else if (storages.size() == 1) { message::deleteMessageId(m.from->id); - renderRecipesSuggestion({storages}, 0, m.from->id, m.chat->id, bot, api); + renderRecipesSuggestion({storages[0].id}, 0, m.from->id, m.chat->id, bot, api); stateManager.put(SuggestedRecipesList{ - .selectedStorages = storages, - .pageNo = 0, - .fromStorage = false, - }); + .prevState = SuggestedRecipesList::FromMainMenuData{{}, std::move(storages[0])}, .pageNo = 0}); } else { message::deleteMessageId(m.from->id); - auto newState = StoragesSelection{}; + auto newState = StoragesSelection{.prevState = MainMenu{}, .selectedStorages = {}}; renderStorageSelection(newState, m.from->id, m.chat->id, bot, api); stateManager.put(newState); } diff --git a/src/handlers/common.hpp b/src/handlers/common.hpp index 1bdce456..dd9c61fd 100644 --- a/src/handlers/common.hpp +++ b/src/handlers/common.hpp @@ -25,8 +25,8 @@ using states::StorageList; using states::TotalPublicationHistory; -using states::RecipeStorageAddition; -using states::SuggestedRecipeView; +using states::CookingPlanning; +using states::CookingPlanningStorageAddition; using states::StorageCreationEnterName; using states::StorageDeletion; @@ -57,6 +57,8 @@ using states::RecipesSearch; using states::RecipeView; +using states::CookingIngredientsSpending; + // Type aliases using BotRef = const TgBot::Api&; using SMRef = const states::StateManager&; diff --git a/src/handlers/cooking/ingredients_spending.cpp b/src/handlers/cooking/ingredients_spending.cpp new file mode 100644 index 00000000..25818c59 --- /dev/null +++ b/src/handlers/cooking/ingredients_spending.cpp @@ -0,0 +1,70 @@ +#include "ingredients_spending.hpp" + +#include "backend/api/api.hpp" +#include "backend/id_types.hpp" +#include "handlers/common.hpp" +#include "render/cooking/ingredients_spending.hpp" +#include "render/cooking_planning/view.hpp" +#include "states.hpp" +#include "utils/parsing.hpp" +#include "utils/utils.hpp" + +#include +#include +#include +#include + +namespace cookcookhnya::handlers::cooking { + +using namespace render::cooking_planning; +using namespace render::cooking; +using states::helpers::SelectableIngredient; +using namespace std::literals; +using namespace std::views; +using std::ranges::to; + +void handleCookingIngredientsSpendingCQ( + CookingIngredientsSpending& state, CallbackQueryRef cq, BotRef bot, SMRef stateManager, api::ApiClientRef api) { + auto chatId = cq.message->chat->id; + auto userId = cq.from->id; + + if (cq.data == "back") { + bot.answerCallbackQuery(cq.id); + renderCookingPlanning(state.prevState.availability, state.prevState.recipeId, userId, chatId, bot, api); + stateManager.put(auto{std::move(state.prevState)}); + return; + } + + if (cq.data == "remove") { + if (!state.storageId) + return; + auto selected = state.ingredients | filter(&SelectableIngredient::selected) | + transform(&SelectableIngredient::id) | to(); + api.getIngredientsApi().deleteMultipleFromStorage(userId, *state.storageId, selected); + bot.answerCallbackQuery(cq.id, utils::utf8str(u8"Успешно удалено из выбранного хранилища")); + return; + } + + if (cq.data == "to_shopping_list") { + auto selected = state.ingredients | filter(&SelectableIngredient::selected) | + transform(&SelectableIngredient::id) | to(); + api.getShoppingListApi().put(userId, selected); + bot.answerCallbackQuery(cq.id, utils::utf8str(u8"Успешно добавлено")); + return; + } + + if (cq.data.starts_with("ingredient_")) { + auto mIngredientId = + utils::parseSafe(std::string_view{cq.data}.substr("ingredient_"sv.size())); + if (!mIngredientId) + return; + auto ingredientIter = std::ranges::find(state.ingredients, *mIngredientId, &SelectableIngredient::id); + if (ingredientIter == state.ingredients.end()) + return; + ingredientIter->selected = !ingredientIter->selected; + renderIngredientsSpending(state.ingredients, state.storageId.has_value(), userId, chatId, bot); + return; + } +} + +} // namespace cookcookhnya::handlers::cooking diff --git a/src/handlers/cooking/ingredients_spending.hpp b/src/handlers/cooking/ingredients_spending.hpp new file mode 100644 index 00000000..93361715 --- /dev/null +++ b/src/handlers/cooking/ingredients_spending.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include "backend/api/api.hpp" +#include "handlers/common.hpp" + +namespace cookcookhnya::handlers::cooking { + +void handleCookingIngredientsSpendingCQ( + CookingIngredientsSpending& state, CallbackQueryRef cq, BotRef bot, SMRef stateManager, api::ApiClientRef api); + +} // namespace cookcookhnya::handlers::cooking diff --git a/src/handlers/suggested_recipe/add_storage.cpp b/src/handlers/cooking_planning/add_storage.cpp similarity index 57% rename from src/handlers/suggested_recipe/add_storage.cpp rename to src/handlers/cooking_planning/add_storage.cpp index 699ed372..af68e142 100644 --- a/src/handlers/suggested_recipe/add_storage.cpp +++ b/src/handlers/cooking_planning/add_storage.cpp @@ -4,8 +4,8 @@ #include "backend/id_types.hpp" #include "backend/models/storage.hpp" #include "handlers/common.hpp" -#include "render/suggested_recipe/add_storage.hpp" -#include "render/suggested_recipe/view.hpp" +#include "render/cooking_planning/add_storage.hpp" +#include "render/cooking_planning/view.hpp" #include "states.hpp" #include "utils/ingredients_availability.hpp" #include "utils/parsing.hpp" @@ -15,20 +15,20 @@ #include #include -namespace cookcookhnya::handlers::suggested_recipe { +namespace cookcookhnya::handlers::cooking_planning { -using namespace render::suggested_recipe; +using namespace render::cooking_planning; using namespace api::models::storage; -void handleRecipeStorageAdditionCQ( - RecipeStorageAddition& state, CallbackQueryRef cq, BotRef bot, SMRef stateManager, api::ApiClientRef api) { +void handleCookingPlanningStorageAdditionCQ( + CookingPlanningStorageAddition& state, CallbackQueryRef cq, BotRef bot, SMRef stateManager, api::ApiClientRef api) { bot.answerCallbackQuery(cq.id); const std::string& data = cq.data; auto chatId = cq.message->chat->id; auto userId = cq.from->id; if (data == "back") { - renderRecipeView(state.prevState.availability, state.prevState.recipeId, userId, chatId, bot, api); + renderCookingPlanning(state.prevState.availability, state.prevState.recipeId, userId, chatId, bot, api); stateManager.put(auto{std::move(state.prevState)}); return; } @@ -40,14 +40,27 @@ void handleRecipeStorageAdditionCQ( const StorageSummary newStorage = {.id = *newStorageId, .name = newStorageDetails.name}; state.prevState.addedStorages.push_back(newStorage); utils::addStorage(state.prevState.availability, newStorage); + + using StoragesList = std::vector; + auto selectedStorages = state.prevState.getStorages(); + const StoragesList* selectedStoragesPtr = nullptr; + // very optimized decision! (no) + if (auto* storagesVal = std::get_if(&selectedStorages)) + selectedStoragesPtr = storagesVal; + else if (auto* storagesRef = std::get_if>(&selectedStorages)) + selectedStoragesPtr = &storagesRef->get(); + else + return; + renderStoragesSuggestion(state.prevState.availability, - state.prevState.prevState.selectedStorages, + *selectedStoragesPtr, state.prevState.addedStorages, state.prevState.recipeId, userId, chatId, bot, api); + return; } } @@ -59,16 +72,29 @@ void handleRecipeStorageAdditionCQ( state.prevState.addedStorages.erase(std::ranges::find( state.prevState.addedStorages, newStorageId, &api::models::storage::StorageSummary::id)); utils::deleteStorage(state.prevState.availability, newStorage); + + using StoragesList = std::vector; + auto selectedStorages = state.prevState.getStorages(); + const StoragesList* selectedStoragesPtr = nullptr; + // very optimized decision! (no) + if (auto* storagesVal = std::get_if(&selectedStorages)) + selectedStoragesPtr = storagesVal; + else if (auto* storagesRef = std::get_if>(&selectedStorages)) + selectedStoragesPtr = &storagesRef->get(); + else + return; + renderStoragesSuggestion(state.prevState.availability, - state.prevState.prevState.selectedStorages, + *selectedStoragesPtr, state.prevState.addedStorages, state.prevState.recipeId, userId, chatId, bot, api); + return; } } } -} // namespace cookcookhnya::handlers::suggested_recipe +} // namespace cookcookhnya::handlers::cooking_planning diff --git a/src/handlers/cooking_planning/add_storage.hpp b/src/handlers/cooking_planning/add_storage.hpp new file mode 100644 index 00000000..d5e0207c --- /dev/null +++ b/src/handlers/cooking_planning/add_storage.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include "backend/api/api.hpp" +#include "handlers/common.hpp" + +namespace cookcookhnya::handlers::cooking_planning { + +void handleCookingPlanningStorageAdditionCQ( + CookingPlanningStorageAddition& state, CallbackQueryRef cq, BotRef bot, SMRef stateManager, api::ApiClientRef api); + +} // namespace cookcookhnya::handlers::cooking_planning diff --git a/src/handlers/cooking_planning/view.cpp b/src/handlers/cooking_planning/view.cpp new file mode 100644 index 00000000..ae5773e6 --- /dev/null +++ b/src/handlers/cooking_planning/view.cpp @@ -0,0 +1,136 @@ +#include "view.hpp" + +#include "backend/api/api.hpp" +#include "backend/id_types.hpp" +#include "backend/models/ingredient.hpp" +#include "backend/models/storage.hpp" +#include "handlers/common.hpp" +#include "render/cooking/ingredients_spending.hpp" +#include "render/cooking_planning/add_storage.hpp" +#include "render/main_menu/view.hpp" +#include "render/recipes_search/view.hpp" +#include "render/recipes_suggestions/view.hpp" +#include "render/shopping_list/create.hpp" +#include "states.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace cookcookhnya::handlers::cooking_planning { + +using namespace render::recipes_suggestions; +using namespace render::shopping_list; +using namespace render::cooking_planning; +using namespace render::cooking; +using namespace render::recipes_search; + +using namespace api::models::ingredient; +using namespace api::models::storage; +using IngredientAvailability = states::CookingPlanning::IngredientAvailability; +using AvailabilityType = states::CookingPlanning::AvailabilityType; +using states::helpers::SelectableIngredient; + +using namespace std::views; +using std::ranges::to; + +namespace { + +std::optional getTheOnlyStorage(const CookingPlanning& state) { + // what a pattern matching hell (c) Team lead + if (const auto* prevState = std::get_if(&state.prevState)) { + if (const auto* prevPrevState = std::get_if(&prevState->prevState)) + return prevPrevState->storageId; + if (const auto* prevPrevState = std::get_if(&prevState->prevState)) + return prevPrevState->second.id; + if (const auto* prevPrevState = std::get_if(&prevState->prevState)) + if (prevPrevState->selectedStorages.size() == 1) + return prevPrevState->selectedStorages.front().id; + } else if (const auto* prevState = std::get_if(&state.prevState)) { + if (prevState->second.size() == 1) + return prevState->second[0].id; + return {}; + } + return {}; +} + +} // namespace + +void handleCookingPlanningCQ( + CookingPlanning& state, CallbackQueryRef cq, BotRef bot, SMRef stateManager, api::ApiClientRef api) { + bot.answerCallbackQuery(cq.id); + const std::string data = cq.data; + auto chatId = cq.message->chat->id; + auto userId = cq.from->id; + + if (data == "start_cooking") { + auto ingredients = + state.availability | + transform([](const auto& ia) { return SelectableIngredient{{ia.ingredient.id, ia.ingredient.name}}; }) | + to(); + std::optional theOnlyStorage = getTheOnlyStorage(state); + renderIngredientsSpending(ingredients, theOnlyStorage.has_value(), userId, chatId, bot); + CookingIngredientsSpending newState{ + .prevState = std::move(state), .storageId = theOnlyStorage, .ingredients = std::move(ingredients)}; + stateManager.put(std::move(newState)); + return; + } + + if (data == "shopping_list") { + std::vector selectedIngredients; + std::vector allIngredients; + for (const auto& av : state.availability) { + if (av.available == AvailabilityType::NOT_AVAILABLE) { + selectedIngredients.push_back({.id = av.ingredient.id, .name = av.ingredient.name}); + } + allIngredients.push_back({.id = av.ingredient.id, .name = av.ingredient.name}); + } + renderShoppingListCreation(selectedIngredients, allIngredients, userId, chatId, bot); + stateManager.put(ShoppingListCreation{ + .prevState = std::move(state), + .selectedIngredients = selectedIngredients, + .allIngredients = allIngredients, + }); + return; + } + + if (data == "back") { + if (auto* prevState = std::get_if(&state.prevState)) { + renderRecipesSuggestion(prevState->getStorageIds(), prevState->pageNo, userId, chatId, bot, api); + stateManager.put(auto{std::move(*prevState)}); + } else if (auto* prevState = std::get_if(&state.prevState)) { + if (auto& mSearchState = prevState->first.prevState) { + renderRecipesSearch(mSearchState->pagination, mSearchState->page, userId, chatId, bot); + stateManager.put(auto{std::move(*mSearchState)}); + } else { + render::main_menu::renderMainMenu(true, std::nullopt, userId, chatId, bot, api); + stateManager.put(MainMenu{}); + } + } + return; + } + + if (data == "add_storages") { + using StoragesList = std::vector; + auto selectedStorages = state.getStorages(); + const StoragesList* selectedStoragesPtr = nullptr; + // very optimized decision! (no) + if (auto* storagesVal = std::get_if(&selectedStorages)) + selectedStoragesPtr = storagesVal; + else if (auto* storagesRef = std::get_if>(&selectedStorages)) + selectedStoragesPtr = &storagesRef->get(); + else + return; + + renderStoragesSuggestion( + state.availability, *selectedStoragesPtr, state.addedStorages, state.recipeId, userId, chatId, bot, api); + stateManager.put(CookingPlanningStorageAddition{.prevState = std::move(state)}); + return; + } +} + +} // namespace cookcookhnya::handlers::cooking_planning diff --git a/src/handlers/cooking_planning/view.hpp b/src/handlers/cooking_planning/view.hpp new file mode 100644 index 00000000..387a8ab4 --- /dev/null +++ b/src/handlers/cooking_planning/view.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include "backend/api/api.hpp" +#include "handlers/common.hpp" + +namespace cookcookhnya::handlers::cooking_planning { + +void handleCookingPlanningCQ( + CookingPlanning& state, CallbackQueryRef cq, BotRef bot, SMRef stateManager, api::ApiClientRef api); + +} // namespace cookcookhnya::handlers::cooking_planning diff --git a/src/handlers/handlers_list.hpp b/src/handlers/handlers_list.hpp index 69619593..99142f0f 100644 --- a/src/handlers/handlers_list.hpp +++ b/src/handlers/handlers_list.hpp @@ -24,8 +24,8 @@ #include "personal_account/publication_history.hpp" #include "personal_account/view.hpp" -#include "suggested_recipe/add_storage.hpp" -#include "suggested_recipe/view.hpp" +#include "cooking_planning/add_storage.hpp" +#include "cooking_planning/view.hpp" #include "recipes_suggestions/view.hpp" @@ -53,6 +53,8 @@ #include "recipe/view.hpp" +#include "cooking/ingredients_spending.hpp" + #include #include @@ -65,7 +67,7 @@ using namespace handlers::personal_account::ingredients; using namespace handlers::personal_account::recipe; using namespace handlers::personal_account::recipes_list; using namespace handlers::shopping_list; -using namespace handlers::suggested_recipe; +using namespace handlers::cooking_planning; using namespace handlers::storage; using namespace handlers::storage::ingredients; using namespace handlers::storage::members; @@ -74,6 +76,7 @@ using namespace handlers::storages_selection; using namespace handlers::recipes_suggestions; using namespace handlers::recipes_search; using namespace handlers::recipe; +using namespace handlers::cooking; using namespace tg_stater; @@ -141,9 +144,10 @@ using storageIngredientsListIQHandler = Handler; -// RecipeView -using suggestedRecipeViewCQHandler = Handler; -using recipeStorageAdditionCQHandler = Handler; +// Cooking planning +using cookingPlanningCQHandler = Handler; +using cookingPlanningStorageAdditionCQHandler = + Handler; using shoppingListCreationCQHandler = Handler; // Shopping list @@ -176,4 +180,7 @@ using recipesSearchIQHandler = Handler; +// Cooking +using cookingIngredientsSpendingCQHandler = Handler; + } // namespace cookcookhnya::handlers::bot_handlers diff --git a/src/handlers/main_menu/view.cpp b/src/handlers/main_menu/view.cpp index 392d2993..a51a07ac 100644 --- a/src/handlers/main_menu/view.cpp +++ b/src/handlers/main_menu/view.cpp @@ -1,8 +1,6 @@ #include "view.hpp" #include "backend/api/api.hpp" -#include "backend/api/storages.hpp" -#include "backend/models/storage.hpp" #include "handlers/common.hpp" #include "render/personal_account/view.hpp" #include "render/recipes_search/view.hpp" @@ -24,8 +22,7 @@ using namespace render::personal_account; using namespace render::recipes_search; using namespace std::views; -void handleMainMenuCQ( - MainMenu& /*unused*/, CallbackQueryRef cq, BotRef& bot, SMRef stateManager, api::ApiClientRef api) { +void handleMainMenuCQ(MainMenu& state, CallbackQueryRef cq, BotRef& bot, SMRef stateManager, api::ApiClientRef api) { bot.answerCallbackQuery(cq.id); auto userId = cq.from->id; auto chatId = cq.message->chat->id; @@ -39,13 +36,13 @@ void handleMainMenuCQ( if (cq.data == "wanna_eat") { if (storages.size() == 1) { - std::vector storage = {storages[0]}; - renderRecipesSuggestion(storage, 0, userId, chatId, bot, api); - stateManager.put(SuggestedRecipesList{.selectedStorages = storage, .pageNo = 0, .fromStorage = false}); + renderRecipesSuggestion({storages[0].id}, 0, userId, chatId, bot, api); + stateManager.put(SuggestedRecipesList{ + .prevState = SuggestedRecipesList::FromMainMenuData{state, std::move(storages[0])}, .pageNo = 0}); return; } renderStorageSelection({}, userId, chatId, bot, api); - stateManager.put(StoragesSelection{.selectedStorages = std::vector{}}); + stateManager.put(StoragesSelection{.prevState = state, .selectedStorages = {}}); return; } diff --git a/src/handlers/recipe/view.cpp b/src/handlers/recipe/view.cpp index 45cf7fcf..a57e4e87 100644 --- a/src/handlers/recipe/view.cpp +++ b/src/handlers/recipe/view.cpp @@ -1,16 +1,24 @@ #include "view.hpp" #include "backend/api/api.hpp" +#include "backend/id_types.hpp" #include "handlers/common.hpp" +#include "render/cooking_planning/view.hpp" #include "render/main_menu/view.hpp" #include "render/recipes_search/view.hpp" +#include "render/storages_selection/view.hpp" +#include "states.hpp" +#include "utils/ingredients_availability.hpp" #include #include +#include namespace cookcookhnya::handlers::recipe { using namespace render::recipes_search; +using namespace render::cooking_planning; +using namespace render::select_storages; using namespace render::main_menu; void handleRecipeViewCQ(RecipeView& state, CallbackQueryRef cq, BotRef bot, SMRef stateManager, api::ApiClientRef api) { @@ -28,6 +36,30 @@ void handleRecipeViewCQ(RecipeView& state, CallbackQueryRef cq, BotRef bot, SMRe } return; } + + if (cq.data == "cook") { + auto storages = api.getStoragesApi().getStoragesList(userId); + if (storages.size() <= 1) { + std::vector storagesIds; + if (!storages.empty()) + storagesIds.push_back(storages[0].id); + + const api::RecipeId recipeId = state.recipeId; + auto availability = utils::inStoragesAvailability(storagesIds, recipeId, userId, api); + + renderCookingPlanning(availability, recipeId, userId, chatId, bot, api); + stateManager.put( + CookingPlanning{.prevState = CookingPlanning::FromRecipeViewData{std::move(state), std::move(storages)}, + .addedStorages = {}, + .availability = std::move(availability), + .recipeId = recipeId}); + return; + } + StoragesSelection newState{.prevState = std::move(state), .selectedStorages = {}}; + renderStorageSelection(newState, userId, chatId, bot, api); + stateManager.put(std::move(newState)); + return; + } } } // namespace cookcookhnya::handlers::recipe diff --git a/src/handlers/recipes_search/view.cpp b/src/handlers/recipes_search/view.cpp index 0317e1f1..a5e99a13 100644 --- a/src/handlers/recipes_search/view.cpp +++ b/src/handlers/recipes_search/view.cpp @@ -59,7 +59,8 @@ void handleRecipesSearchCQ( return; auto recipe = api.getRecipesApi().get(userId, *mRecipeId); renderRecipeView(recipe, *mRecipeId, userId, chatId, bot); - stateManager.put(RecipeView{.prevState = {std::move(state)}, .recipe = std::move(recipe)}); + stateManager.put( + RecipeView{.prevState = {std::move(state)}, .recipe = std::move(recipe), .recipeId = *mRecipeId}); return; } } diff --git a/src/handlers/recipes_suggestions/view.cpp b/src/handlers/recipes_suggestions/view.cpp index 4698cd4c..ed737970 100644 --- a/src/handlers/recipes_suggestions/view.cpp +++ b/src/handlers/recipes_suggestions/view.cpp @@ -2,48 +2,54 @@ #include "backend/api/api.hpp" #include "backend/id_types.hpp" +#include "backend/models/storage.hpp" #include "handlers/common.hpp" +#include "render/cooking_planning/view.hpp" #include "render/main_menu/view.hpp" #include "render/recipes_suggestions/view.hpp" #include "render/storage/view.hpp" #include "render/storages_selection/view.hpp" -#include "render/suggested_recipe/view.hpp" #include "utils/ingredients_availability.hpp" #include "utils/parsing.hpp" #include +#include #include #include +#include +#include namespace cookcookhnya::handlers::recipes_suggestions { using namespace render::recipes_suggestions; using namespace render::select_storages; using namespace render::storage; -using namespace render::suggested_recipe; +using namespace render::cooking_planning; using namespace render::main_menu; +using namespace api::models::storage; +using namespace std::views; void handleSuggestedRecipesListCQ( SuggestedRecipesList& state, CallbackQueryRef cq, BotRef bot, SMRef stateManager, api::ApiClientRef api) { bot.answerCallbackQuery(cq.id); auto chatId = cq.message->chat->id; auto userId = cq.from->id; - - auto data = cq.data; + const std::string& data = cq.data; if (data == "back") { - if (state.fromStorage) { - renderStorageView(state.selectedStorages[0].id, userId, chatId, bot, api); - stateManager.put(StorageView{state.selectedStorages[0].id}); // Go to the only one storage - } else { - if (api.getStoragesApi().getStoragesList(userId).size() == 1) { + if (auto* prevState = std::get_if(&state.prevState)) { + renderStorageView(prevState->storageId, userId, chatId, bot, api); + std::string storageName = api.getStoragesApi().get(userId, prevState->storageId).name; + stateManager.put(StorageView{prevState->storageId, std::move(storageName)}); + } else if (auto* prevState = std::get_if(&state.prevState)) { + renderMainMenu(true, std::nullopt, userId, chatId, bot, api); + stateManager.put(prevState->first); + } else if (auto* prevState = std::get_if(&state.prevState)) { + if (auto* prevPrevState = std::get_if(&prevState->prevState)) { renderMainMenu(true, std::nullopt, userId, chatId, bot, api); - stateManager.put(MainMenu{}); - } else { - auto newState = StoragesSelection{.selectedStorages = std::move(state.selectedStorages)}; - renderStorageSelection(newState, userId, chatId, bot, api); - stateManager.put(std::move(newState)); + stateManager.put(auto{*prevPrevState}); } + throw std::runtime_error{"Unreachable path reached"}; } bot.answerCallbackQuery(cq.id); return; @@ -53,14 +59,13 @@ void handleSuggestedRecipesListCQ( auto recipeId = utils::parseSafe(data.substr(sizeof("recipe_") - 1)); if (!recipeId) return; - auto inStorage = utils::inStoragesAvailability(state.selectedStorages, *recipeId, userId, api); - renderRecipeView(inStorage, *recipeId, userId, chatId, bot, api); - stateManager.put(SuggestedRecipeView{ - .prevState = std::move(state), - .addedStorages = {}, - .availability = inStorage, - .recipeId = *recipeId, - }); + + std::vector inStorage = + utils::inStoragesAvailability(state.getStorageIds(), *recipeId, userId, api); + + renderCookingPlanning(inStorage, *recipeId, userId, chatId, bot, api); + stateManager.put(CookingPlanning{ + .prevState = std::move(state), .addedStorages = {}, .availability = inStorage, .recipeId = *recipeId}); return; } @@ -69,7 +74,7 @@ void handleSuggestedRecipesListCQ( state.pageNo--; else if (data == "page_right") state.pageNo++; - renderRecipesSuggestion(state.selectedStorages, state.pageNo, userId, chatId, bot, api); + renderRecipesSuggestion(state.getStorageIds(), state.pageNo, userId, chatId, bot, api); return; } } diff --git a/src/handlers/shopping_list/create.cpp b/src/handlers/shopping_list/create.cpp index 0c2e65a5..6f18d3da 100644 --- a/src/handlers/shopping_list/create.cpp +++ b/src/handlers/shopping_list/create.cpp @@ -4,8 +4,8 @@ #include "backend/id_types.hpp" #include "backend/models/ingredient.hpp" #include "handlers/common.hpp" +#include "render/cooking_planning/view.hpp" #include "render/shopping_list/create.hpp" -#include "render/suggested_recipe/view.hpp" #include "utils/parsing.hpp" #include @@ -16,7 +16,7 @@ namespace cookcookhnya::handlers::shopping_list { using namespace render::shopping_list; -using namespace render::suggested_recipe; +using namespace render::cooking_planning; void handleShoppingListCreationCQ( ShoppingListCreation& state, CallbackQueryRef cq, BotRef bot, SMRef stateManager, api::ApiClientRef api) { @@ -25,7 +25,7 @@ void handleShoppingListCreationCQ( auto userId = cq.from->id; if (data == "back") { - renderRecipeView(state.prevState.availability, state.prevState.recipeId, userId, chatId, bot, api); + renderCookingPlanning(state.prevState.availability, state.prevState.recipeId, userId, chatId, bot, api); stateManager.put(auto{std::move(state.prevState)}); bot.answerCallbackQuery(cq.id); return; @@ -39,7 +39,7 @@ void handleShoppingListCreationCQ( putIds.push_back(ingredient.id); } shoppingApi.put(userId, putIds); - renderRecipeView(state.prevState.availability, state.prevState.recipeId, userId, chatId, bot, api); + renderCookingPlanning(state.prevState.availability, state.prevState.recipeId, userId, chatId, bot, api); stateManager.put(auto{std::move(state.prevState)}); bot.answerCallbackQuery(cq.id); return; diff --git a/src/handlers/shopping_list/view.cpp b/src/handlers/shopping_list/view.cpp index c7120375..051243f4 100644 --- a/src/handlers/shopping_list/view.cpp +++ b/src/handlers/shopping_list/view.cpp @@ -44,7 +44,7 @@ void handleShoppingListViewCQ( } if (cq.data == "remove") { - using SelectableItem = ShoppingListView::SelectableItem; + using SelectableItem = states::helpers::SelectableShoppingListItem; auto toDelete = state.items.getValues() | filter(&SelectableItem::selected) | views::transform(&SelectableItem::ingredientId) | to(); @@ -57,7 +57,7 @@ void handleShoppingListViewCQ( } if (cq.data == "buy") { - using SelectableItem = ShoppingListView::SelectableItem; + using SelectableItem = states::helpers::SelectableShoppingListItem; auto toBuy = state.items.getValues() | filter(&SelectableItem::selected) | views::transform(&SelectableItem::ingredientId) | to(); if (storages.size() == 1) { diff --git a/src/handlers/storage/delete.cpp b/src/handlers/storage/delete.cpp index 3440bc0e..9e6dfc71 100644 --- a/src/handlers/storage/delete.cpp +++ b/src/handlers/storage/delete.cpp @@ -6,6 +6,7 @@ #include "render/storage/view.hpp" #include "render/storages_list/view.hpp" #include "states.hpp" +#include namespace cookcookhnya::handlers::storage { @@ -15,14 +16,19 @@ using namespace render::storage; void handleStorageDeletionCQ( StorageDeletion& state, CallbackQueryRef cq, BotRef bot, SMRef stateManager, api::StorageApiRef storageApi) { bot.answerCallbackQuery(cq.id); + auto userId = cq.from->id; + auto chatId = cq.message->chat->id; + if (cq.data == "confirm") { - storageApi.delete_(cq.from->id, state.storageId); - renderStorageList(true, cq.from->id, cq.message->chat->id, bot, storageApi); + storageApi.delete_(userId, state.storageId); + renderStorageList(true, userId, chatId, bot, storageApi); stateManager.put(StorageList{}); } + if (cq.data == "back") { - renderStorageView(state.storageId, cq.from->id, cq.message->chat->id, bot, storageApi); - stateManager.put(StorageView{state.storageId}); + renderStorageView(state.storageId, userId, chatId, bot, storageApi); + std::string storageName = storageApi.get(userId, state.storageId).name; + stateManager.put(StorageView{state.storageId, std::move(storageName)}); } }; diff --git a/src/handlers/storage/ingredients/view.cpp b/src/handlers/storage/ingredients/view.cpp index e72e3575..1efd88ac 100644 --- a/src/handlers/storage/ingredients/view.cpp +++ b/src/handlers/storage/ingredients/view.cpp @@ -74,7 +74,8 @@ void handleStorageIngredientsListCQ( if (cq.data == "back") { renderStorageView(state.storageId, userId, chatId, bot, api); - stateManager.put(StorageView{state.storageId}); + std::string storageName = api.getStoragesApi().get(userId, state.storageId).name; + stateManager.put(StorageView{state.storageId, std::move(storageName)}); return; } diff --git a/src/handlers/storage/members/view.cpp b/src/handlers/storage/members/view.cpp index ffa8156e..76e61803 100644 --- a/src/handlers/storage/members/view.cpp +++ b/src/handlers/storage/members/view.cpp @@ -5,6 +5,7 @@ #include "render/storage/members/add.hpp" #include "render/storage/members/delete.hpp" #include "render/storage/view.hpp" +#include namespace cookcookhnya::handlers::storage::members { @@ -24,7 +25,8 @@ void handleStorageMemberViewCQ( stateManager.put(StorageMemberDeletion{state.storageId}); } else if (cq.data == "back") { renderStorageView(state.storageId, userId, chatId, bot, storageApi); - stateManager.put(StorageView{state.storageId}); + std::string storageName = storageApi.get(userId, state.storageId).name; + stateManager.put(StorageView{state.storageId, std::move(storageName)}); } }; diff --git a/src/handlers/storage/view.cpp b/src/handlers/storage/view.cpp index d3e88451..ece4ea12 100644 --- a/src/handlers/storage/view.cpp +++ b/src/handlers/storage/view.cpp @@ -51,11 +51,8 @@ void handleStorageViewCQ( } if (cq.data == "wanna_eat") { - auto storageDetails = api.getStoragesApi().get(userId, state.storageId); - const StorageSummary storage = {.id = state.storageId, .name = storageDetails.name}; - std::vector storages = {storage}; - renderRecipesSuggestion(storages, 0, userId, chatId, bot, api); - stateManager.put(SuggestedRecipesList{.selectedStorages = storages, .pageNo = 0, .fromStorage = true}); + renderRecipesSuggestion({state.storageId}, 0, userId, chatId, bot, api); + stateManager.put(SuggestedRecipesList{.prevState = state, .pageNo = 0}); return; } diff --git a/src/handlers/storages_list/view.cpp b/src/handlers/storages_list/view.cpp index 1abfc1fb..190c7618 100644 --- a/src/handlers/storages_list/view.cpp +++ b/src/handlers/storages_list/view.cpp @@ -9,6 +9,7 @@ #include "utils/parsing.hpp" #include +#include namespace cookcookhnya::handlers::storages_list { @@ -38,7 +39,8 @@ void handleStorageListCQ( auto storageId = utils::parseSafe(cq.data); if (storageId) { renderStorageView(*storageId, cq.from->id, chatId, bot, api); - stateManager.put(StorageView{*storageId}); + std::string storageName = api.getStoragesApi().get(userId, *storageId).name; + stateManager.put(StorageView{*storageId, std::move(storageName)}); } } diff --git a/src/handlers/storages_selection/view.cpp b/src/handlers/storages_selection/view.cpp index 271cbba6..d89360a0 100644 --- a/src/handlers/storages_selection/view.cpp +++ b/src/handlers/storages_selection/view.cpp @@ -4,14 +4,19 @@ #include "backend/id_types.hpp" #include "backend/models/storage.hpp" #include "handlers/common.hpp" +#include "render/cooking_planning/view.hpp" #include "render/main_menu/view.hpp" +#include "render/recipe/view.hpp" #include "render/recipes_suggestions/view.hpp" #include "render/storages_selection/view.hpp" +#include "utils/ingredients_availability.hpp" #include "utils/parsing.hpp" #include #include +#include #include +#include #include namespace cookcookhnya::handlers::storages_selection { @@ -20,6 +25,10 @@ using api::models::storage::StorageSummary; using namespace render::recipes_suggestions; using namespace render::select_storages; using namespace render::main_menu; +using namespace render::cooking_planning; +using namespace render::recipe; +using namespace std::views; +using std::ranges::to; void handleStoragesSelectionCQ( StoragesSelection& state, CallbackQueryRef cq, BotRef bot, SMRef stateManager, api::ApiClientRef api) { @@ -28,15 +37,33 @@ void handleStoragesSelectionCQ( auto userId = cq.from->id; if (cq.data == "confirm") { - renderRecipesSuggestion(state.selectedStorages, 0, userId, chatId, bot, api); - stateManager.put(SuggestedRecipesList{ - .selectedStorages = std::move(state.selectedStorages), .pageNo = 0, .fromStorage = false}); + auto storagesIds = state.selectedStorages | transform(&StorageSummary::id) | to(); + if (auto* prevState = std::get_if(&state.prevState)) { + const api::RecipeId recipeId = prevState->recipeId; + auto availability = utils::inStoragesAvailability(storagesIds, recipeId, userId, api); + + renderCookingPlanning(availability, recipeId, userId, chatId, bot, api); + stateManager.put( + CookingPlanning{.prevState = CookingPlanning::FromRecipeViewData{std::move(*prevState), + std::move(state.selectedStorages)}, + .addedStorages = {}, + .availability = std::move(availability), + .recipeId = recipeId}); + } else if (std::holds_alternative(state.prevState)) { + renderRecipesSuggestion(storagesIds, 0, userId, chatId, bot, api); + stateManager.put(SuggestedRecipesList{.prevState = std::move(state), .pageNo = 0}); + } return; } if (cq.data == "cancel") { - renderMainMenu(true, std::nullopt, userId, chatId, bot, api); - stateManager.put(MainMenu{}); + if (auto* prevState = std::get_if(&state.prevState)) { + renderMainMenu(true, std::nullopt, userId, chatId, bot, api); + stateManager.put(auto{*prevState}); + } else if (auto* prevState = std::get_if(&state.prevState)) { + renderRecipeView(prevState->recipe, prevState->recipeId, userId, chatId, bot); + stateManager.put(auto{std::move(*prevState)}); + } return; } diff --git a/src/handlers/suggested_recipe/add_storage.hpp b/src/handlers/suggested_recipe/add_storage.hpp deleted file mode 100644 index 9e08e3b9..00000000 --- a/src/handlers/suggested_recipe/add_storage.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "backend/api/api.hpp" -#include "handlers/common.hpp" - -namespace cookcookhnya::handlers::suggested_recipe { - -void handleRecipeStorageAdditionCQ( - RecipeStorageAddition& state, CallbackQueryRef cq, BotRef bot, SMRef stateManager, api::ApiClientRef api); - -} // namespace cookcookhnya::handlers::suggested_recipe diff --git a/src/handlers/suggested_recipe/view.cpp b/src/handlers/suggested_recipe/view.cpp deleted file mode 100644 index 7a2d7fa2..00000000 --- a/src/handlers/suggested_recipe/view.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "view.hpp" - -#include "backend/api/api.hpp" -#include "backend/id_types.hpp" -#include "backend/models/ingredient.hpp" -#include "handlers/common.hpp" -#include "render/recipes_suggestions/view.hpp" -#include "render/shopping_list/create.hpp" -#include "render/suggested_recipe/add_storage.hpp" -#include "states.hpp" - -#include -#include -#include - -namespace cookcookhnya::handlers::suggested_recipe { - -using namespace render::recipes_suggestions; -using namespace render::shopping_list; -using namespace render::suggested_recipe; -using namespace api::models::ingredient; -using IngredientAvailability = states::SuggestedRecipeView::IngredientAvailability; -using AvailabilityType = states::SuggestedRecipeView::AvailabilityType; - -void handleRecipeViewCQ( - SuggestedRecipeView& state, CallbackQueryRef cq, BotRef bot, SMRef stateManager, api::ApiClientRef api) { - const std::string data = cq.data; - auto chatId = cq.message->chat->id; - auto userId = cq.from->id; - - if (data == "start_cooking") { - // TODO: add state of begginig of cooking - return; - } - - if (data == "shopping_list") { - std::vector selectedIngredients; - std::vector allIngredients; - for (const auto& av : state.availability) { - if (av.available == AvailabilityType::NOT_AVAILABLE) { - selectedIngredients.push_back({.id = av.ingredient.id, .name = av.ingredient.name}); - } - allIngredients.push_back({.id = av.ingredient.id, .name = av.ingredient.name}); - } - renderShoppingListCreation(selectedIngredients, allIngredients, userId, chatId, bot); - stateManager.put(ShoppingListCreation{ - .prevState = std::move(state), - .selectedIngredients = selectedIngredients, - .allIngredients = allIngredients, - }); - bot.answerCallbackQuery(cq.id); - return; - } - - if (data == "back_from_recipe_view") { - renderRecipesSuggestion(state.prevState.selectedStorages, state.prevState.pageNo, userId, chatId, bot, api); - stateManager.put(auto{std::move(state.prevState)}); - bot.answerCallbackQuery(cq.id); - return; - } - - if (data == "add_storages") { - renderStoragesSuggestion(state.availability, - state.prevState.selectedStorages, - state.addedStorages, - state.recipeId, - userId, - chatId, - bot, - api); - stateManager.put(RecipeStorageAddition{.prevState = std::move(state)}); - return; - } -} - -} // namespace cookcookhnya::handlers::suggested_recipe diff --git a/src/handlers/suggested_recipe/view.hpp b/src/handlers/suggested_recipe/view.hpp deleted file mode 100644 index f38a8de9..00000000 --- a/src/handlers/suggested_recipe/view.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "backend/api/api.hpp" -#include "handlers/common.hpp" - -namespace cookcookhnya::handlers::suggested_recipe { - -void handleRecipeViewCQ( - SuggestedRecipeView& state, CallbackQueryRef cq, BotRef bot, SMRef stateManager, api::ApiClientRef api); - -} // namespace cookcookhnya::handlers::suggested_recipe diff --git a/src/main.cpp b/src/main.cpp index a3894820..11b6668a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -60,8 +60,8 @@ int main(int argc, char* argv[]) { storageIngredientsListIQHandler, storageIngredientsDeletionCQHandler, suggestedRecipeListCQHandler, - suggestedRecipeViewCQHandler, - recipeStorageAdditionCQHandler, + cookingPlanningCQHandler, + cookingPlanningStorageAdditionCQHandler, shoppingListCreationCQHandler, shoppingListViewCQHandler, personalAccountMenuCQHandler, @@ -78,7 +78,8 @@ int main(int argc, char* argv[]) { shoppingListIngredientSearchIQHandler, recipesSearchCQHandler, recipesSearchIQHandler, - recipeViewCQHandler> + recipeViewCQHandler, + cookingIngredientsSpendingCQHandler> bot{{}, {ApiClient{utils::getenvWithError("API_URL")}}}; TgBot::Bot tgBot{utils::getenvWithError("BOT_TOKEN")}; // sdf diff --git a/src/render/CMakeLists.txt b/src/render/CMakeLists.txt index b49d15ef..9faecd9e 100644 --- a/src/render/CMakeLists.txt +++ b/src/render/CMakeLists.txt @@ -16,8 +16,8 @@ target_sources(main PRIVATE src/render/personal_account/view.cpp src/render/personal_account/publication_history.cpp - src/render/suggested_recipe/add_storage.cpp - src/render/suggested_recipe/view.cpp + src/render/cooking_planning/add_storage.cpp + src/render/cooking_planning/view.cpp src/render/recipes_suggestions/view.cpp @@ -44,4 +44,6 @@ target_sources(main PRIVATE src/render/recipes_search/view.cpp src/render/recipe/view.cpp + + src/render/cooking/ingredients_spending.cpp ) diff --git a/src/render/cooking/ingredients_spending.cpp b/src/render/cooking/ingredients_spending.cpp new file mode 100644 index 00000000..251b8907 --- /dev/null +++ b/src/render/cooking/ingredients_spending.cpp @@ -0,0 +1,55 @@ +#include "ingredients_spending.hpp" + +#include "message_tracker.hpp" +#include "render/common.hpp" +#include "states.hpp" +#include "utils/utils.hpp" + +#include +#include + +namespace cookcookhnya::render::cooking { + +using namespace std::views; +using SelectableIngedient = states::helpers::SelectableShoppingListItem; + +void renderIngredientsSpending(const std::vector& ingredients, + bool canRemove, + UserId userId, + ChatId chatId, + BotRef bot) { + const std::string text = utils::utf8str( + canRemove ? u8"🧾 Вы можете убрать из хранилища продукты, если они закончились после готовки, а также сразу " + u8"добавить их в список покупок" + : u8"🧾 Вы можете добавить закончившиеся после готовки продукты в список покупок"); + + const bool anySelected = std::ranges::any_of(ingredients, &states::helpers::SelectableIngredient::selected); + + InlineKeyboardBuilder keyboard{2 + ((ingredients.size() / 2) + 1)}; // remove and/or buy, list (n/2), back + + if (anySelected) { + if (canRemove) + keyboard << makeCallbackButton(u8"🗑 Убрать", "remove"); + keyboard << makeCallbackButton(u8"🛒 В список покупок", "to_shopping_list") << NewRow{}; + } + + for (auto row : ingredients | chunk(2)) { + for (const auto& ing : row) { + const char8_t* const selectedMark = ing.selected ? u8"[ + ] " : u8"[ᅠ] "; // not empty! + keyboard << makeCallbackButton(utils::utf8str(selectedMark) + ing.name, + "ingredient_" + utils::to_string(ing.id)); + } + keyboard << NewRow{}; + } + + keyboard << makeCallbackButton(u8"↩️ Назад", "back"); + + if (auto messageId = message::getMessageId(userId)) { + bot.editMessageText(text, chatId, *messageId, std::move(keyboard)); + } else { + auto message = bot.sendMessage(chatId, text, std::move(keyboard)); + message::addMessageId(userId, message->messageId); + } +} + +} // namespace cookcookhnya::render::cooking diff --git a/src/render/cooking/ingredients_spending.hpp b/src/render/cooking/ingredients_spending.hpp new file mode 100644 index 00000000..e4aca33c --- /dev/null +++ b/src/render/cooking/ingredients_spending.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "render/common.hpp" +#include "states.hpp" + +#include + +namespace cookcookhnya::render::cooking { + +void renderIngredientsSpending(const std::vector& ingredients, + bool canRemove, + UserId userId, + ChatId chatId, + BotRef bot); + +} // namespace cookcookhnya::render::cooking diff --git a/src/render/suggested_recipe/add_storage.cpp b/src/render/cooking_planning/add_storage.cpp similarity index 86% rename from src/render/suggested_recipe/add_storage.cpp rename to src/render/cooking_planning/add_storage.cpp index 2b526aa9..0f4c4c0b 100644 --- a/src/render/suggested_recipe/add_storage.cpp +++ b/src/render/cooking_planning/add_storage.cpp @@ -8,7 +8,6 @@ #include "render/common.hpp" #include "states.hpp" #include "utils/utils.hpp" -#include "view.hpp" #include #include @@ -17,14 +16,22 @@ #include #include -namespace cookcookhnya::render::suggested_recipe { +namespace cookcookhnya::render::cooking_planning { using namespace api::models::recipe; using namespace api::models::storage; -using IngredientAvailability = states::SuggestedRecipeView::IngredientAvailability; -using AvailabilityType = states::SuggestedRecipeView::AvailabilityType; +using IngredientAvailability = states::CookingPlanning::IngredientAvailability; +using AvailabilityType = states::CookingPlanning::AvailabilityType; -TextGenInfo storageAdditionView(const std::vector& inStoragesAvailability, +namespace { + +struct CookingInfo { + std::string renderText; + bool isIngredientNotAvailable; + bool isIngredientInOtherStorages; +}; + +CookingInfo storageAdditionView(const std::vector& inStoragesAvailability, const std::vector& selectedStorages, api::RecipeId recipeId, UserId userId, @@ -61,11 +68,13 @@ TextGenInfo storageAdditionView(const std::vector& inSto if (recipe.link) text += utils::utf8str(u8"\n🌐 Источник: ") + *recipe.link; - return {.text = text, + return {.renderText = text, .isIngredientNotAvailable = isIngredientNotAvailable, - .isIngredientIsOtherStorages = isIngredientIsOtherStorages}; + .isIngredientInOtherStorages = isIngredientIsOtherStorages}; } +} // namespace + void renderStoragesSuggestion(const std::vector& inStoragesAvailability, const std::vector& selectedStorages, const std::vector& addedStorages, @@ -107,7 +116,8 @@ void renderStoragesSuggestion(const std::vector& inStora auto messageId = message::getMessageId(userId); if (messageId) { - bot.editMessageText(textGen.text, chatId, *messageId, makeKeyboardMarkup(std::move(keyboard)), "Markdown"); + bot.editMessageText( + textGen.renderText, chatId, *messageId, makeKeyboardMarkup(std::move(keyboard)), "Markdown"); } } -} // namespace cookcookhnya::render::suggested_recipe +} // namespace cookcookhnya::render::cooking_planning diff --git a/src/render/cooking_planning/add_storage.hpp b/src/render/cooking_planning/add_storage.hpp new file mode 100644 index 00000000..b34595bd --- /dev/null +++ b/src/render/cooking_planning/add_storage.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "backend/api/api.hpp" +#include "backend/id_types.hpp" +#include "backend/models/storage.hpp" +#include "render/common.hpp" +#include "states.hpp" + +#include + +namespace cookcookhnya::render::cooking_planning { + +void renderStoragesSuggestion( + const std::vector& inStoragesAvailability, + const std::vector& selectedStorages, + const std::vector& addedStorages, + api::RecipeId recipeId, + UserId userId, + ChatId chatId, + BotRef bot, + api::ApiClientRef api); + +} // namespace cookcookhnya::render::cooking_planning diff --git a/src/render/cooking_planning/view.cpp b/src/render/cooking_planning/view.cpp new file mode 100644 index 00000000..87ac6bd1 --- /dev/null +++ b/src/render/cooking_planning/view.cpp @@ -0,0 +1,105 @@ +#include "view.hpp" + +#include "backend/api/api.hpp" +#include "backend/id_types.hpp" +#include "backend/models/recipe.hpp" +#include "message_tracker.hpp" +#include "render/common.hpp" +#include "states.hpp" +#include "utils/u8format.hpp" +#include "utils/utils.hpp" + +#include + +#include +#include +#include +#include + +namespace cookcookhnya::render::cooking_planning { + +using namespace api::models::recipe; +using IngredientAvailability = states::CookingPlanning::IngredientAvailability; +using AvailabilityType = states::CookingPlanning::AvailabilityType; + +namespace { + +struct CookingInfo { + std::string renderText; + std::string recipeName; + bool isIngredientNotAvailable; + bool isIngredientInOtherStorages; +}; + +CookingInfo recipeView(const std::vector& inStoragesAvailability, + api::RecipeId recipeId, + UserId userId, + api::ApiClientRef api) { + auto recipeIngredients = api.getRecipesApi().getSuggested(userId, recipeId); + + bool isIngredientNotAvailable = false; + bool isIngredientIsOtherStorages = false; + std::string& recipeName = recipeIngredients.name; + auto text = std::format("{} *{}* \n\n", utils::utf8str(u8"📖 Ингредиенты для"), recipeName); + + for (const auto& availability : inStoragesAvailability) { + if (availability.available == AvailabilityType::AVAILABLE) { + text += "`[+]` " + availability.ingredient.name + "\n"; + } else if (availability.available == AvailabilityType::OTHER_STORAGES) { + text += "`[?]` " + availability.ingredient.name + "\n"; + isIngredientIsOtherStorages = true; + } else { + text += "`[ ]` " + availability.ingredient.name + "\n"; + isIngredientNotAvailable = true; + } + } + if (recipeIngredients.link) + text += utils::utf8str(u8"\n🌐 Источник: ") + *recipeIngredients.link; + + return {.renderText = text, + .recipeName = recipeName, + .isIngredientNotAvailable = isIngredientNotAvailable, + .isIngredientInOtherStorages = isIngredientIsOtherStorages}; +} + +} // namespace + +void renderCookingPlanning(const std::vector& inStoragesAvailability, + api::RecipeId recipeId, + UserId userId, + ChatId chatId, + BotRef bot, + api::ApiClientRef api) { + auto cookingInfo = recipeView(inStoragesAvailability, recipeId, userId, api); + InlineKeyboardBuilder keyboard{4}; // Cook + add storages, shopping list, share, back + + keyboard << makeCallbackButton(u8"🧑‍🍳 Готовить", "start_cooking"); + if (cookingInfo.isIngredientInOtherStorages) + keyboard << makeCallbackButton(u8"?", "add_storages"); + keyboard << NewRow{}; + + if (cookingInfo.isIngredientNotAvailable) + keyboard << makeCallbackButton(u8"📝 Составить список продуктов", "shopping_list") << NewRow{}; + + auto shareButton = std::make_shared(); + shareButton->text = utils::utf8str(u8"📤 Поделиться"); + const std::string botAlias = bot.getUnderlying().getMe()->username; + const std::string recipeUrl = std::format("https://t.me/{}?start=recipe_{}", botAlias, recipeId); + const std::string shareText = + utils::u8format("{} **{}**", u8"Хочу поделиться с тобой рецептом", cookingInfo.recipeName); + + boost::urls::url url{"https://t.me/share/url"}; + url.params().append({"url", recipeUrl}); + url.params().append({"text", shareText}); + shareButton->url = url.buffer(); + + keyboard << std::move(shareButton) << NewRow{} << makeCallbackButton(u8"↩️ Назад", "back"); + + auto messageId = message::getMessageId(userId); + if (messageId) { + // Only on difference between function above + bot.editMessageText(cookingInfo.renderText, chatId, *messageId, std::move(keyboard), "Markdown"); + } +} + +} // namespace cookcookhnya::render::cooking_planning diff --git a/src/render/cooking_planning/view.hpp b/src/render/cooking_planning/view.hpp new file mode 100644 index 00000000..1c08b3b4 --- /dev/null +++ b/src/render/cooking_planning/view.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "backend/api/api.hpp" +#include "backend/id_types.hpp" +#include "render/common.hpp" +#include "states.hpp" + +#include + +namespace cookcookhnya::render::cooking_planning { + +void renderCookingPlanning(const std::vector& inStoragesAvailability, + api::RecipeId recipeId, + UserId userId, + ChatId chatId, + BotRef bot, + api::ApiClientRef api); + +} // namespace cookcookhnya::render::cooking_planning diff --git a/src/render/personal_account/publication_history.cpp b/src/render/personal_account/publication_history.cpp index 2d41bb9a..16c578f9 100644 --- a/src/render/personal_account/publication_history.cpp +++ b/src/render/personal_account/publication_history.cpp @@ -7,8 +7,11 @@ #include #include #include + namespace cookcookhnya::render::personal_account { + using namespace std::views; + void renderRequestHistory(UserId userId, size_t pageNo, size_t numOfInstances, @@ -22,26 +25,20 @@ void renderRequestHistory(UserId userId, std::string toPrint = utils::utf8str(u8"ℹ️История запросов на публикацию ваших рецептов и ингредиентов\n\n"); for (auto& req : history | reverse) { - std::string rcpIngRender; - if (req.requestType == "recipe") - rcpIngRender = utils::utf8str(u8"📖"); - else - rcpIngRender = utils::utf8str(u8"🥬"); - toPrint += std::format( - "{} {}: *{}* статус: {} ", rcpIngRender, req.requestType, req.name, utils::to_string(req.status.status)); + std::string emoji = utils::utf8str(req.requestType == "recipe" ? u8"📖" : u8"🥬"); + toPrint += std::format("{} *{}*\nСтатус: {}\n", emoji, req.name, utils::to_string(req.status.status)); if (req.status.reason.has_value()) - toPrint += std::format("по причине: {} ", req.status.reason.value()); - toPrint += std::format("запрос создан: {} ", utils::to_string(req.created)); + toPrint += std::format("По причине: {}\n", req.status.reason.value()); + toPrint += std::format("Запрос создан: {}\n", utils::to_string(req.created)); if (req.updated.has_value()) { - toPrint += std::format("последенее обновление: {}", utils::to_string(req.updated.value())); + toPrint += std::format("Последенее обновление: {}\n", utils::to_string(req.updated.value())); } toPrint += "\n\n"; } keyboard << makeCallbackButton(u8"↩️ Назад", "back"); - auto messageId = message::getMessageId(userId); - if (messageId) { + if (auto messageId = message::getMessageId(userId)) bot.editMessageText(toPrint, chatId, *messageId, std::move(keyboard), "Markdown"); - } } + } // namespace cookcookhnya::render::personal_account diff --git a/src/render/recipe/view.cpp b/src/render/recipe/view.cpp index fc5992ff..4783a329 100644 --- a/src/render/recipe/view.cpp +++ b/src/render/recipe/view.cpp @@ -27,7 +27,7 @@ void renderRecipeView(const api::models::recipe::RecipeDetails& recipe, if (recipe.creator) text += utils::u8format("\n{}: {}\n", u8"👤 Автор", recipe.creator->fullName); - InlineKeyboardBuilder keyboard{2}; // share, back + InlineKeyboardBuilder keyboard{3}; // cook, share, back auto shareButton = std::make_shared(); shareButton->text = utils::utf8str(u8"📤 Поделиться"); @@ -40,7 +40,8 @@ void renderRecipeView(const api::models::recipe::RecipeDetails& recipe, url.params().append({"text", shareText}); shareButton->url = url.buffer(); - keyboard << std::move(shareButton) << NewRow{} << makeCallbackButton(u8"↩️ Назад", "back"); + keyboard << makeCallbackButton(u8"🧑‍🍳 Хочу приготовить", "cook") << NewRow{} + << std::move(shareButton) << NewRow{} << makeCallbackButton(u8"↩️ Назад", "back"); if (auto mMessageId = message::getMessageId(userId)) bot.editMessageText(text, chatId, *mMessageId, std::move(keyboard), "Markdown"); diff --git a/src/render/recipes_search/view.cpp b/src/render/recipes_search/view.cpp index 7dfce0da..8a9a3fbc 100644 --- a/src/render/recipes_search/view.cpp +++ b/src/render/recipes_search/view.cpp @@ -7,6 +7,7 @@ #include "states.hpp" #include "utils/utils.hpp" +#include #include namespace cookcookhnya::render::recipes_search { @@ -30,7 +31,8 @@ void renderRecipesSearch(const states::helpers::Pagination& pagination, auto makeRecipeButton = [](const RecipeSummary& r) { return makeCallbackButton(utils::utf8str(u8"🔖 ") + r.name, "recipe_" + utils::to_string(r.id)); }; - keyboard << constructPagination(pagination.pageNo, page.size(), pagination.totalItems, page, makeRecipeButton); + const std::size_t pageSize = 5; + keyboard << constructPagination(pagination.pageNo, pageSize, pagination.totalItems, page, makeRecipeButton); keyboard << makeCallbackButton(u8"↩️ Назад", "back"); diff --git a/src/render/recipes_suggestions/view.cpp b/src/render/recipes_suggestions/view.cpp index 901e5d6f..576ad5eb 100644 --- a/src/render/recipes_suggestions/view.cpp +++ b/src/render/recipes_suggestions/view.cpp @@ -1,8 +1,8 @@ #include "view.hpp" #include "backend/api/recipes.hpp" +#include "backend/id_types.hpp" #include "backend/models/recipe.hpp" -#include "backend/models/storage.hpp" #include "message_tracker.hpp" #include "render/common.hpp" #include "render/pagination.hpp" @@ -11,7 +11,6 @@ #include #include #include -#include #include #include @@ -24,7 +23,6 @@ namespace cookcookhnya::render::recipes_suggestions { using namespace api::models::storage; using namespace api::models::recipe; using namespace std::views; -using std::ranges::to; namespace { @@ -42,7 +40,7 @@ constructKeyboard(std::size_t pageNo, std::size_t pageSize, RecipesListWithIngre } // namespace -void renderRecipesSuggestion(std::vector& storages, +void renderRecipesSuggestion(const std::vector& storages, std::size_t pageNo, UserId userId, ChatId chatId, @@ -50,9 +48,8 @@ void renderRecipesSuggestion(std::vector& storages, api::RecipesApiRef recipesApi) { const std::size_t numOfRecipesOnPage = 5; - auto storagesIds = storages | transform(&StorageSummary::id) | to(); auto recipesList = - recipesApi.getSuggestedRecipes(userId, storagesIds, numOfRecipesOnPage, pageNo * numOfRecipesOnPage); + recipesApi.getSuggestedRecipes(userId, storages, numOfRecipesOnPage, pageNo * numOfRecipesOnPage); const std::string text = utils::utf8str(recipesList.found > 0 ? u8"🔪 Рецепты подобранные специально для вас" diff --git a/src/render/recipes_suggestions/view.hpp b/src/render/recipes_suggestions/view.hpp index 34491c7d..6c91d193 100644 --- a/src/render/recipes_suggestions/view.hpp +++ b/src/render/recipes_suggestions/view.hpp @@ -1,7 +1,7 @@ #pragma once #include "backend/api/recipes.hpp" -#include "backend/models/storage.hpp" +#include "backend/id_types.hpp" #include "render/common.hpp" #include @@ -11,7 +11,7 @@ namespace cookcookhnya::render::recipes_suggestions { using namespace tg_types; -void renderRecipesSuggestion(std::vector& storages, +void renderRecipesSuggestion(const std::vector& storages, std::size_t pageNo, UserId userId, ChatId chatId, diff --git a/src/render/shopping_list/view.cpp b/src/render/shopping_list/view.cpp index 95f344f0..49da6e35 100644 --- a/src/render/shopping_list/view.cpp +++ b/src/render/shopping_list/view.cpp @@ -15,7 +15,7 @@ using namespace std::views; void renderShoppingList(const states::ShoppingListView& state, UserId userId, ChatId chatId, BotRef bot) { auto items = state.items.getValues(); - const bool anySelected = std::ranges::any_of(items, &states::ShoppingListView::SelectableItem::selected); + const bool anySelected = std::ranges::any_of(items, &states::helpers::SelectableShoppingListItem::selected); InlineKeyboardBuilder keyboard{3 + ((items.size() / 2) + 1)}; // add, remove and/or buy, list (n/2), back diff --git a/src/render/suggested_recipe/add_storage.hpp b/src/render/suggested_recipe/add_storage.hpp deleted file mode 100644 index b29e43ae..00000000 --- a/src/render/suggested_recipe/add_storage.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "backend/api/api.hpp" -#include "backend/id_types.hpp" -#include "backend/models/storage.hpp" -#include "render/common.hpp" -#include "render/suggested_recipe/view.hpp" -#include "states.hpp" - -#include - -namespace cookcookhnya::render::suggested_recipe { - -TextGenInfo -storageAdditionView(const std::vector& inStoragesAvailability, - const std::vector& selectedStorages, - api::RecipeId recipeId, - UserId userId, - api::ApiClientRef api); - -void renderStoragesSuggestion( - const std::vector& inStoragesAvailability, - const std::vector& selectedStorages, - const std::vector& addedStorages, - api::RecipeId recipeId, - UserId userId, - ChatId chatId, - BotRef bot, - api::ApiClientRef api); - -} // namespace cookcookhnya::render::suggested_recipe diff --git a/src/render/suggested_recipe/view.cpp b/src/render/suggested_recipe/view.cpp deleted file mode 100644 index 2933c9b3..00000000 --- a/src/render/suggested_recipe/view.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "view.hpp" - -#include "backend/api/api.hpp" -#include "backend/id_types.hpp" -#include "backend/models/recipe.hpp" -#include "message_tracker.hpp" -#include "render/common.hpp" -#include "states.hpp" -#include "utils/utils.hpp" - -#include -#include -#include -#include -#include - -namespace cookcookhnya::render::suggested_recipe { - -using namespace api::models::recipe; -using IngredientAvailability = states::SuggestedRecipeView::IngredientAvailability; -using AvailabilityType = states::SuggestedRecipeView::AvailabilityType; - -TextGenInfo recipeView(const std::vector& inStoragesAvailability, - api::RecipeId recipeId, - UserId userId, - api::ApiClientRef api) { - auto recipeIngredients = api.getRecipesApi().getSuggested(userId, recipeId); - - bool isIngredientNotAvailable = false; - bool isIngredientIsOtherStorages = false; - std::string& recipeName = recipeIngredients.name; - auto text = std::format("{} *{}* \n\n", utils::utf8str(u8"📖 Ингредиенты для"), recipeName); - - for (const auto& availability : inStoragesAvailability) { - if (availability.available == AvailabilityType::AVAILABLE) { - text += "`[+]` " + availability.ingredient.name + "\n"; - } else if (availability.available == AvailabilityType::OTHER_STORAGES) { - text += "`[?]` " + availability.ingredient.name + "\n"; - isIngredientIsOtherStorages = true; - } else { - text += "`[ ]` " + availability.ingredient.name + "\n"; - isIngredientNotAvailable = true; - } - } - if (recipeIngredients.link) - text += utils::utf8str(u8"\n🌐 Источник: ") + *recipeIngredients.link; - - return {.text = text, - .isIngredientNotAvailable = isIngredientNotAvailable, - .isIngredientIsOtherStorages = isIngredientIsOtherStorages}; -} - -void renderRecipeView(std::vector& inStoragesAvailability, - api::RecipeId recipeId, - UserId userId, - ChatId chatId, - BotRef bot, - api::ApiClientRef api) { - auto textGen = recipeView(inStoragesAvailability, recipeId, userId, api); - const std::size_t buttonRows = textGen.isIngredientNotAvailable ? 3 : 2; - InlineKeyboard keyboard(buttonRows); - - keyboard[0].push_back(makeCallbackButton(u8"🧑‍🍳 Готовить", "start_cooking")); - - if (textGen.isIngredientIsOtherStorages) { - keyboard[0].push_back(makeCallbackButton(u8"?", "add_storages")); - } - if (textGen.isIngredientNotAvailable) { - keyboard[1].push_back(makeCallbackButton(u8"📝 Составить список продуктов", "shopping_list")); - } - - keyboard[buttonRows - 1].push_back(makeCallbackButton(u8"↩️ Назад", "back_from_recipe_view")); - - auto messageId = message::getMessageId(userId); - if (messageId) { - // Only on difference between function above - bot.editMessageText(textGen.text, chatId, *messageId, makeKeyboardMarkup(std::move(keyboard)), "Markdown"); - } -} - -} // namespace cookcookhnya::render::suggested_recipe diff --git a/src/render/suggested_recipe/view.hpp b/src/render/suggested_recipe/view.hpp deleted file mode 100644 index 57b40f3b..00000000 --- a/src/render/suggested_recipe/view.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "backend/api/api.hpp" -#include "backend/id_types.hpp" -#include "render/common.hpp" -#include "states.hpp" - -#include -#include - -namespace cookcookhnya::render::suggested_recipe { - -struct TextGenInfo { - std::string text; - bool isIngredientNotAvailable; - bool isIngredientIsOtherStorages; -}; - -void renderRecipeView(std::vector& inStoragesAvailability, - api::RecipeId recipeId, - UserId userId, - ChatId chatId, - BotRef bot, - api::ApiClientRef api); - -TextGenInfo recipeView(const std::vector& inStoragesAvailability, - api::RecipeId recipeId, - UserId userId, - api::ApiClientRef api); - -} // namespace cookcookhnya::render::suggested_recipe diff --git a/src/states.hpp b/src/states.hpp index b949e414..b7a75ffa 100644 --- a/src/states.hpp +++ b/src/states.hpp @@ -8,11 +8,14 @@ #include "utils/fast_sorted_db.hpp" #include "utils/utils.hpp" +#include +#include #include #include #include #include +#include #include #include #include @@ -32,10 +35,34 @@ struct Pagination { std::size_t totalItems; }; +struct SelectableShoppingListItem : api::models::shopping_list::ShoppingListItem { + bool selected = false; + SelectableShoppingListItem(api::models::shopping_list::ShoppingListItem item) // NOLINT(*explicit*) + : ShoppingListItem{std::move(item)} {} +}; + +struct SelectableIngredient : api::models::ingredient::Ingredient { + bool selected = false; + SelectableIngredient(api::models::ingredient::Ingredient item) // NOLINT(*explicit*) + : Ingredient{std::move(item)} {} +}; + } // namespace helpers struct MainMenu {}; +struct RecipesSearch { + std::string query; + helpers::Pagination pagination; + std::vector page; +}; + +struct RecipeView { + std::optional prevState; + api::models::recipe::RecipeDetails recipe; + api::RecipeId recipeId; +}; + struct PersonalAccountMenu {}; struct TotalPublicationHistory { std::size_t pageNo; @@ -49,7 +76,6 @@ struct CustomIngredientConfirmation { // All optionals are for "back" from this menu, so this state won't erase all info std::optional recipeFrom; std::optional> ingredients; - std::optional storageFrom; explicit CustomIngredientConfirmation( @@ -65,7 +91,9 @@ struct CustomIngredientPublish {}; struct StorageList {}; struct StorageCreationEnterName {}; -struct StorageView : helpers::StorageIdMixin {}; +struct StorageView : helpers::StorageIdMixin { + std::string name; +}; struct StorageDeletion : helpers::StorageIdMixin {}; struct StorageMemberView : helpers::StorageIdMixin {}; struct StorageMemberAddition : helpers::StorageIdMixin {}; @@ -93,34 +121,79 @@ struct StorageIngredientsDeletion : helpers::StorageIdMixin { }; struct StoragesSelection { + std::variant prevState; std::vector selectedStorages; }; struct SuggestedRecipesList { - std::vector selectedStorages; + private: + using StorageSummary = api::models::storage::StorageSummary; + + public: + using FromMainMenuData = std::pair; + std::variant prevState; std::size_t pageNo; - bool fromStorage; + + [[nodiscard]] std::vector getStorageIds() const { + if (const auto* prevState_ = std::get_if(&prevState)) + return {prevState_->storageId}; + + if (const auto* prevState_ = std::get_if(&prevState)) + return {prevState_->second.id}; + + if (const auto* prevState_ = std::get_if(&prevState)) + return prevState_->selectedStorages | std::views::transform(&StorageSummary::id) | + std::ranges::to(); + return {}; + } + + [[nodiscard]] std::variant>, std::vector> + getStorages() const { + if (const auto* prevState_ = std::get_if(&prevState)) + return std::vector{StorageSummary{.id = prevState_->storageId, .name = prevState_->name}}; + if (const auto* prevState_ = std::get_if(&prevState)) + return std::vector{prevState_->second}; + if (const auto* prevState_ = std::get_if(&prevState)) + return std::ref(prevState_->selectedStorages); + return std::vector{}; + } }; -struct SuggestedRecipeView { +struct CookingPlanning { + private: + using StorageSummary = api::models::storage::StorageSummary; + + public: enum struct AvailabilityType : std::uint8_t { NOT_AVAILABLE, AVAILABLE, OTHER_STORAGES }; struct IngredientAvailability { cookcookhnya::api::models::recipe::IngredientInRecipe ingredient; AvailabilityType available = AvailabilityType::NOT_AVAILABLE; - std::vector storages; + std::vector storages; }; - SuggestedRecipesList prevState; - std::vector addedStorages; + using FromRecipeViewData = std::pair>; + + std::variant prevState; + std::vector addedStorages; std::vector availability; api::RecipeId recipeId; + + [[nodiscard]] std::variant>, std::vector> + getStorages() const { + if (const auto* prevState_ = std::get_if(&prevState)) + return prevState_->getStorages(); + if (const auto* prevState_ = std::get_if(&prevState)) { + return std::ref(prevState_->second); + } + return std::vector{}; + } }; -struct RecipeStorageAddition { - SuggestedRecipeView prevState; +struct CookingPlanningStorageAddition { + CookingPlanning prevState; }; struct ShoppingListCreation { - SuggestedRecipeView prevState; + CookingPlanning prevState; std::vector selectedIngredients; std::vector allIngredients; }; @@ -161,12 +234,8 @@ struct RecipeIngredientsSearch { }; struct ShoppingListView { // NOLINT(*member-init) // Strange. Flags only this struct due to ItemsDb - struct SelectableItem : api::models::shopping_list::ShoppingListItem { - bool selected = false; - SelectableItem(api::models::shopping_list::ShoppingListItem item) // NOLINT(*explicit*) - : ShoppingListItem{std::move(item)} {} - }; - using ItemsDb = utils::FastSortedDb; + using ItemsDb = + utils::FastSortedDb; ItemsDb items; bool canBuy; @@ -190,15 +259,10 @@ struct CustomRecipePublicationHistory { std::string errorReport; }; -struct RecipesSearch { - std::string query; - helpers::Pagination pagination; - std::vector page; -}; - -struct RecipeView { - std::optional prevState; - api::models::recipe::RecipeDetails recipe; +struct CookingIngredientsSpending { // NOLINT(*member-init) + CookingPlanning prevState; + std::optional storageId; + std::vector ingredients; }; using State = std::variant; + RecipeView, + CookingIngredientsSpending>; using StateManager = tg_stater::StateProxy>; diff --git a/src/utils/ingredients_availability.cpp b/src/utils/ingredients_availability.cpp index ef117c4c..f677f024 100644 --- a/src/utils/ingredients_availability.cpp +++ b/src/utils/ingredients_availability.cpp @@ -18,19 +18,18 @@ namespace cookcookhnya::utils { using namespace api; using namespace api::models::storage; using namespace tg_types; -using IngredientAvailability = states::SuggestedRecipeView::IngredientAvailability; -using AvailabilityType = states::SuggestedRecipeView::AvailabilityType; -using namespace std::views; -using namespace std::ranges; +using IngredientAvailability = states::CookingPlanning::IngredientAvailability; +using AvailabilityType = states::CookingPlanning::AvailabilityType; +using std::ranges::to; -std::vector inStoragesAvailability(std::vector& selectedStorages, +std::vector inStoragesAvailability(const std::vector& selectedStorages, RecipeId recipeId, UserId userId, const api::ApiClient& api) { auto allStorages = api.getStoragesApi().getStoragesList(userId); auto recipe = api.getRecipesApi().getSuggested(userId, recipeId); - auto selectedStoragesSet = selectedStorages | views::transform(&StorageSummary::id) | to(); + auto selectedStoragesSet = selectedStorages | to(); std::unordered_map allStoragesMap; for (const auto& storage : allStorages) { diff --git a/src/utils/ingredients_availability.hpp b/src/utils/ingredients_availability.hpp index 0c68d8d6..5b800a50 100644 --- a/src/utils/ingredients_availability.hpp +++ b/src/utils/ingredients_availability.hpp @@ -10,16 +10,16 @@ namespace cookcookhnya::utils { -std::vector -inStoragesAvailability(std::vector& selectedStorages, +std::vector +inStoragesAvailability(const std::vector& selectedStorages, api::RecipeId recipeId, tg_types::UserId userId, const api::ApiClient& api); -void addStorage(std::vector& availability, +void addStorage(std::vector& availability, const api::models::storage::StorageSummary& storage); -void deleteStorage(std::vector& availability, +void deleteStorage(std::vector& availability, const api::models::storage::StorageSummary& storage); } // namespace cookcookhnya::utils