Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ build/Release/CMakeCache.txt: conanfile.txt
build-debug: build/Debug/CMakeCache.txt
cmake --build --preset=conan-debug
build-debug-j5: build/Release/CMakeCache.txt
cmake --build . --preset=conan-debug -- -j3
cmake --build . --preset=conan-debug -- -j2
build-release: build/Release/CMakeCache.txt
cmake --build --preset=conan-release

Expand Down
2 changes: 0 additions & 2 deletions src/backend/models/ingredient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
#include <boost/json/value_to.hpp>
#include <optional>

#include <optional>

namespace cookcookhnya::api::models::ingredient {

namespace json = boost::json;
Expand Down
8 changes: 4 additions & 4 deletions src/backend/models/moderation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ PublicationRequest tag_invoke(json::value_to_tag<PublicationRequest> /*tag*/, co
return {
.name = value_to<decltype(PublicationRequest::name)>(j.at("name")),
.requestType = value_to<decltype(PublicationRequest::requestType)>(j.at("requestType")),
.status = j.as_object().if_contains("status") ? value_to<PublicationRequestStatus>(j.at("status"))
: PublicationRequestStatus::NO_REQUEST,
.status = j.as_object().if_contains("status")
? value_to<PublicationRequestStatusStruct>(j.at("status"))
: PublicationRequestStatusStruct{.status = PublicationRequestStatus::NO_REQUEST,
.reason = std::nullopt},
.created = utils::parseIsoTime(value_to<std::string>(j.at("createdAt"))),
.updated = j.as_object().if_contains("updatedAt")
? std::optional{utils::parseIsoTime(value_to<std::string>(j.at("updatedAt")))}
: std::nullopt,
.reason = j.as_object().if_contains("reason") ? value_to<decltype(PublicationRequest::reason)>(j.at("reason"))
: std::nullopt,
};
}

Expand Down
3 changes: 1 addition & 2 deletions src/backend/models/moderation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@ namespace cookcookhnya::api::models::moderation {
struct PublicationRequest {
std::string name;
std::string requestType;
PublicationRequestStatus status = PublicationRequestStatus::NO_REQUEST;
PublicationRequestStatusStruct status;
std::chrono::system_clock::time_point created;
std::optional<std::chrono::system_clock::time_point> updated;
std::optional<std::string> reason;

friend PublicationRequest tag_invoke(boost::json::value_to_tag<PublicationRequest>, const boost::json::value& j);
};
Expand Down
17 changes: 15 additions & 2 deletions src/backend/models/publication_request_status.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

#include "utils/utils.hpp"

#include <array>
#include <boost/json/conversion.hpp>
#include <boost/json/value.hpp>

#include <array>
#include <boost/json/value_from.hpp>
#include <boost/json/value_to.hpp>
#include <cstddef>
#include <string>

Expand All @@ -20,6 +21,18 @@ PublicationRequestStatus tag_invoke(boost::json::value_to_tag<PublicationRequest
if (j == "rejected")
return PublicationRequestStatus::REJECTED;
return PublicationRequestStatus::NO_REQUEST;
};

PublicationRequestStatusStruct tag_invoke(boost::json::value_to_tag<PublicationRequestStatusStruct> /*unused*/,
const boost::json::value& j) {
if (j.is_object()) {
return {.status = j.as_object().if_contains("type") ? value_to<PublicationRequestStatus>(j.at("type"))
: PublicationRequestStatus::NO_REQUEST,
.reason = j.as_object().if_contains("reason")
? value_to<decltype(PublicationRequestStatusStruct::reason)>(j.at("reason"))
: std::nullopt};
}
return {.status = PublicationRequestStatus::NO_REQUEST, .reason = std::nullopt};
}

} // namespace cookcookhnya::api::models::moderation
Expand Down
9 changes: 9 additions & 0 deletions src/backend/models/publication_request_status.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <boost/json/value.hpp>

#include <cstdint>
#include <optional>
#include <string>

namespace cookcookhnya::api::models::moderation {
Expand All @@ -12,6 +13,14 @@ enum class PublicationRequestStatus : std::uint8_t { PENDING, ACCEPTED, REJECTED

PublicationRequestStatus tag_invoke(boost::json::value_to_tag<PublicationRequestStatus>, const boost::json::value& j);

struct PublicationRequestStatusStruct {
PublicationRequestStatus status = PublicationRequestStatus::NO_REQUEST;
std::optional<std::string> reason;

friend PublicationRequestStatusStruct tag_invoke(boost::json::value_to_tag<PublicationRequestStatusStruct>,
const boost::json::value& j);
};

} // namespace cookcookhnya::api::models::moderation

namespace cookcookhnya::utils {
Expand Down
23 changes: 12 additions & 11 deletions src/backend/models/recipe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,14 @@ SuggestedRecipeDetails tag_invoke(json::value_to_tag<SuggestedRecipeDetails> /*t
.name = value_to<decltype(SuggestedRecipeDetails::name)>(j.at("name")),
.link = value_to<decltype(SuggestedRecipeDetails::link)>(j.at("sourceLink")),
// Deal with optionals using ternary operator
.creator = j.as_object().if_contains("creator")
? value_to<decltype(SuggestedRecipeDetails::creator)>(j.at("creator"))
: std::nullopt,
.moderationStatus = j.as_object().if_contains("moderationStatus")
? value_to<PublicationRequestStatus>(j.at("moderationStatus"))
: PublicationRequestStatus::NO_REQUEST,
.creator = j.as_object().if_contains("creator") ? value_to<decltype(RecipeDetails::creator)>(j.at("creator"))
: std::nullopt,
.moderationStatus =
j.as_object().if_contains("moderationStatus")
? value_to<moderation::PublicationRequestStatusStruct>(j.at("moderationStatus"))
: moderation::PublicationRequestStatusStruct{.status = PublicationRequestStatus::NO_REQUEST,
.reason = std::nullopt},

};
}

Expand All @@ -86,15 +88,14 @@ RecipeSearchResponse tag_invoke(json::value_to_tag<RecipeSearchResponse> /*tag*/

RecipePublicationRequest tag_invoke(json::value_to_tag<RecipePublicationRequest> /*tag*/, const json::value& j) {
return {
.status = j.as_object().if_contains("status") ? value_to<moderation::PublicationRequestStatus>(j.at("status"))
: moderation::PublicationRequestStatus::NO_REQUEST,
.status = j.as_object().if_contains("status")
? value_to<moderation::PublicationRequestStatusStruct>(j.at("status"))
: moderation::PublicationRequestStatusStruct{.status = PublicationRequestStatus::NO_REQUEST,
.reason = std::nullopt},
.created = utils::parseIsoTime(value_to<std::string>(j.at("createdAt"))),
.updated = j.as_object().if_contains("updatedAt")
? std::optional{utils::parseIsoTime(value_to<std::string>(j.at("updatedAt")))}
: std::nullopt,
.reason = j.as_object().if_contains("reason")
? value_to<decltype(RecipePublicationRequest::reason)>(j.at("reason"))
: std::nullopt,
};
}

Expand Down
5 changes: 2 additions & 3 deletions src/backend/models/recipe.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ struct SuggestedRecipeDetails {
std::string name;
std::optional<std::string> link;
std::optional<user::UserDetails> creator;
moderation::PublicationRequestStatus moderationStatus = moderation::PublicationRequestStatus::NO_REQUEST;
moderation::PublicationRequestStatusStruct moderationStatus;

friend SuggestedRecipeDetails tag_invoke(boost::json::value_to_tag<SuggestedRecipeDetails>,
const boost::json::value& j);
Expand Down Expand Up @@ -93,10 +93,9 @@ struct RecipeSearchResponse {
};

struct RecipePublicationRequest {
moderation::PublicationRequestStatus status = moderation::PublicationRequestStatus::NO_REQUEST;
moderation::PublicationRequestStatusStruct status;
std::chrono::system_clock::time_point created;
std::optional<std::chrono::system_clock::time_point> updated;
std::optional<std::string> reason;

friend RecipePublicationRequest tag_invoke(boost::json::value_to_tag<RecipePublicationRequest>,
const boost::json::value& j);
Expand Down
20 changes: 13 additions & 7 deletions src/handlers/personal_account/recipe/moderation_history.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ void handleCustomRecipePublicationHistoryCQ(

if (data == "backFromRules") {
auto history = api.getRecipesApi().getRecipeRequestHistory(userId, state.recipeId);
renderPublicationHistory(userId, chatId, state.recipeName, history, bot);
renderPublicationHistory(userId, chatId, state.recipeName, state.errorReport, history, bot);
bot.answerCallbackQuery(cq.id);
return;
}
Expand All @@ -36,19 +36,25 @@ void handleCustomRecipePublicationHistoryCQ(
}

if (data == "confirm") {
// Peeking (if button with this data then accepted or pending)
auto history = api.getRecipesApi().getRecipeRequestHistory(userId, state.recipeId);

auto history = api.getRecipesApi().getRecipeRequestHistory(userId, state.recipeId);
// Here check for emptiness first, thanks to lazy compilator
const bool shouldPublish =
history.empty() || (history.back().status == api::models::moderation::PublicationRequestStatus::REJECTED);
const bool shouldPublish = history.empty() || (history.back().status.status ==
api::models::moderation::PublicationRequestStatus::REJECTED);

if (shouldPublish) {
api.getRecipesApi().publishCustom(userId, state.recipeId);
try {
api.getRecipesApi().publishCustom(userId, state.recipeId);
state.errorReport = "";
} catch (...) {
state.errorReport =
utils::utf8str(u8"⚠️Что-то пошло не так, вероятно ваш рецепт содержит неопубликованные ингредиенты");
bot.answerCallbackQuery(cq.id);
}
// Get updated history
history = api.getRecipesApi().getRecipeRequestHistory(userId, state.recipeId);
}
renderPublicationHistory(userId, chatId, state.recipeName, history, bot);
renderPublicationHistory(userId, chatId, state.recipeName, state.errorReport, history, bot);
bot.answerCallbackQuery(cq.id);
return;
}
Expand Down
5 changes: 3 additions & 2 deletions src/handlers/personal_account/recipe/view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,10 @@ void handleRecipeCustomViewCQ(

if (data == "publish") {
auto history = api.getRecipesApi().getRecipeRequestHistory(userId, state.recipeId);
renderPublicationHistory(userId, chatId, state.recipeName, history, bot);
std::string temp;
renderPublicationHistory(userId, chatId, state.recipeName, temp, history, bot);
stateManager.put(CustomRecipePublicationHistory{
.recipeId = state.recipeId, .pageNo = state.pageNo, .recipeName = state.recipeName});
.recipeId = state.recipeId, .pageNo = state.pageNo, .recipeName = state.recipeName, .errorReport = ""});
bot.answerCallbackQuery(cq.id);
return;
}
Expand Down
6 changes: 3 additions & 3 deletions src/render/personal_account/publication_history.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ void renderRequestHistory(UserId userId,
else
rcpIngRender = utils::utf8str(u8"🥬");
toPrint += std::format(
"{} {}: *{}* статус: {} ", rcpIngRender, req.requestType, req.name, utils::to_string(req.status));
if (req.reason.has_value())
toPrint += std::format("по причине: {} ", req.reason.value());
"{} {}: *{}* статус: {} ", rcpIngRender, req.requestType, 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));
if (req.updated.has_value()) {
toPrint += std::format("последенее обновление: {}", utils::to_string(req.updated.value()));
Expand Down
22 changes: 14 additions & 8 deletions src/render/personal_account/recipe/moderation_history.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ using namespace std::views;
void renderPublicationHistory(UserId userId,
ChatId chatId,
std::string& recipeName,
std::string& errorReport,
std::vector<api::models::recipe::RecipePublicationRequest> history,
BotRef bot) {

Expand All @@ -29,11 +30,13 @@ void renderPublicationHistory(UserId userId,
toPrint = (utils::utf8str(u8"История запросов на публикацию *") + recipeName + "*\n\n");
if (!history.empty()) {
// Show confirm only if in those states
isConfirm = history[history.size() - 1].status == api::models::moderation::PublicationRequestStatus::REJECTED;
isConfirm =
history[history.size() - 1].status.status == api::models::moderation::PublicationRequestStatus::REJECTED;
const size_t lastUpdatedInstance = history.size() - 1;
// Construct current status string
toPrint += utils::utf8str(u8"ℹ️ Текущий статус: ") + utils::to_string(history[lastUpdatedInstance].status);
if (auto reason = history[lastUpdatedInstance].reason) {
toPrint +=
utils::utf8str(u8"ℹ️ Текущий статус: ") + utils::to_string(history[lastUpdatedInstance].status.status);
if (auto reason = history[lastUpdatedInstance].status.reason) {
toPrint += std::format(" по причине {}", reason.value());
}
toPrint += " " + utils::to_string(history[lastUpdatedInstance].created) + "\n\n";
Expand All @@ -42,9 +45,9 @@ void renderPublicationHistory(UserId userId,
history.erase(history.end());

for (auto& req : history | reverse) {
toPrint += std::format("Статус: {} ", utils::to_string(req.status));
if (req.reason.has_value())
toPrint += std::format("по причине: {} ", req.reason.value());
toPrint += std::format("Статус: {} ", 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));
if (req.updated.has_value()) {
toPrint += std::format("последенее обновление: {}", utils::to_string(req.updated.value()));
Expand All @@ -53,6 +56,8 @@ void renderPublicationHistory(UserId userId,
}
}

toPrint += errorReport;

if (isConfirm) {
keyboard << makeCallbackButton(u8"▶️Подтвердить", "confirm") << NewRow{};
}
Expand All @@ -74,8 +79,9 @@ void renderPublicationRules(UserId userId, ChatId chatId, BotRef bot) {
"ингредиенты, отклонённые администрацией.\n3. *Название рецепта*\n - Не должно содержать "
"нецензурную лексику, оскорбления или спам;\n - Должно точно отражать содержание рецепта (например: "
"\"Спагетти карбонара\", а не \"Вкуснятина\"). \n4. *Дополнительно*\n - Запрещено размещать контактные "
"данные или рекламу." +
utils::utf8str(u8"\n⚠️ Нарушение правил приведёт к отклонению рецепта ");
"данные или рекламу;\n - Вы не сможете редактировать рецепт с статусом " +
utils::utf8str(u8"\"🟢 Принят\" и \"🟡 На рассмотрении\"");
utils::utf8str(u8"\n⚠️ Нарушение правил приведёт к отклонению рецепта ");

InlineKeyboardBuilder keyboard{1}; // back
keyboard << makeCallbackButton(u8"↩️ Назад", "backFromRules");
Expand Down
1 change: 1 addition & 0 deletions src/render/personal_account/recipe/moderation_history.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace cookcookhnya::render::personal_account::recipe {
void renderPublicationHistory(UserId userId,
ChatId chatId,
std::string& recipeName,
std::string& errorReport,
std::vector<api::models::recipe::RecipePublicationRequest> history,
BotRef bot);
void renderPublicationRules(UserId userId, ChatId chatId, BotRef bot);
Expand Down
13 changes: 9 additions & 4 deletions src/render/personal_account/recipe/view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,18 @@ std::pair<std::vector<Ingredient>, std::string> renderCustomRecipe(
ingredients.push_back({.id = it.id, .name = it.name});
}

toPrint += "\n🌐 [Статус проверки] " + utils::to_string(recipeDetails.moderationStatus);
toPrint += "\n🌐 [Статус проверки] " + utils::to_string(recipeDetails.moderationStatus.status);

keyboard << makeCallbackButton(u8"🚮 Удалить", "delete") << NewRow{};
keyboard << makeCallbackButton(u8"✏️ Редактировать", "change") << NewRow{};
// Allow to edit recipe only if no request was made or it was rejected
if (recipeDetails.moderationStatus.status == PublicationRequestStatus::NO_REQUEST ||
recipeDetails.moderationStatus.status == PublicationRequestStatus::REJECTED) {
keyboard << makeCallbackButton(u8"✏️ Редактировать", "change") << NewRow{};
}

// Show publish button only iff the status is not emty AND not rejected
if (recipeDetails.moderationStatus == PublicationRequestStatus::NO_REQUEST ||
recipeDetails.moderationStatus == PublicationRequestStatus::REJECTED) {
if (recipeDetails.moderationStatus.status == PublicationRequestStatus::NO_REQUEST ||
recipeDetails.moderationStatus.status == PublicationRequestStatus::REJECTED) {
keyboard << makeCallbackButton(u8"📢 Опубликовать", "publish") << NewRow{};
} else {
keyboard << makeCallbackButton(u8"📢 История публикаций", "publish") << NewRow{};
Expand Down
1 change: 1 addition & 0 deletions src/states.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ struct CustomRecipePublicationHistory {
api::RecipeId recipeId;
std::size_t pageNo;
std::string recipeName;
std::string errorReport;
};

struct RecipesSearch {
Expand Down