Nice gift craft error handling.

This commit is contained in:
John Preston
2026-02-02 13:12:51 +04:00
parent 4189c0b2dc
commit 86a622c49c
6 changed files with 105 additions and 18 deletions
+2
View File
@@ -4148,6 +4148,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_craft_failed_about#one" = "This crafting attempt was unsuccessful.\n**{count}** gift was lost.";
"lng_gift_craft_failed_about#other" = "This crafting attempt was unsuccessful.\n**{count}** gifts were lost.";
"lng_gift_craft_new_button" = "Craft New Gift";
"lng_gift_craft_unavailable" = "Crafting unavailable";
"lng_gift_craft_when" = "This gift will become available for crafting on {date} at {time}.";
"lng_auction_about_title" = "Auction";
"lng_auction_about_subtitle" = "Join the battle for exclusive gifts.";
@@ -8,8 +8,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/star_gift_craft_animation.h"
#include "base/call_delayed.h"
#include "base/unixtime.h"
#include "boxes/star_gift_box.h"
#include "boxes/star_gift_cover_box.h"
#include "boxes/star_gift_craft_box.h"
#include "chat_helpers/compose/compose_show.h"
#include "chat_helpers/stickers_lottie.h"
#include "data/data_credits.h"
@@ -23,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "settings/settings_credits_graphics.h"
#include "ui/boxes/boost_box.h"
#include "ui/boxes/confirm_box.h"
#include "ui/image/image_prepare.h"
#include "ui/layers/generic_box.h"
#include "ui/widgets/gradient_round_button.h"
@@ -1478,7 +1481,7 @@ void StartCraftAnimation(
not_null<GenericBox*> box,
std::shared_ptr<ChatHelpers::Show> show,
std::shared_ptr<CraftState> shared,
Fn<void(CraftResultCallback)> startRequest,
Fn<void(Fn<void(CraftResult)> callback)> startRequest,
Fn<void()> closeParent,
Fn<void(Fn<void()> closeCurrent)> retryWithNewGift) {
const auto container = box->verticalLayout();
@@ -1832,11 +1835,24 @@ void StartCraftAnimation(
if (startRequest) {
const auto weak = base::make_weak(raw);
startRequest([=](std::shared_ptr<Data::GiftUpgradeResult> result) {
startRequest([=](CraftResult result) {
if (!weak) {
return;
} else if (v::is<CraftResultError>(result)) {
box->uiShow()->show(MakeInformBox({
.text = v::get<CraftResultError>(result).type,
}));
box->closeBox();
return;
} else if (v::is<CraftResultWait>(result)) {
const auto when = base::unixtime::now()
+ v::get<CraftResultWait>(result).seconds;
ShowCraftLaterError(box->uiShow(), when);
box->closeBox();
return;
}
state->craftResult = std::move(result);
using Result = std::shared_ptr<Data::GiftUpgradeResult>;
state->craftResult = v::get<Result>(result);
if (const auto result = state->craftResult->get()) {
state->successAnimation = std::make_unique<
CraftDoneAnimation
@@ -38,7 +38,18 @@ class RpWidget;
class VerticalLayout;
class UniqueGiftCoverWidget;
using CraftResultCallback = Fn<void(std::shared_ptr<Data::GiftUpgradeResult>)>;
struct CraftResultError {
QString type;
};
struct CraftResultWait {
TimeId seconds = 0;
};
using CraftResult = std::variant<
std::shared_ptr<Data::GiftUpgradeResult>,
CraftResultError,
CraftResultWait>;
struct BackdropView {
Data::UniqueGiftBackdrop colors;
@@ -205,7 +216,7 @@ void StartCraftAnimation(
not_null<GenericBox*> box,
std::shared_ptr<ChatHelpers::Show> show,
std::shared_ptr<CraftState> state,
Fn<void(CraftResultCallback)> startRequest,
Fn<void(Fn<void(CraftResult)> callback)> startRequest,
Fn<void()> closeParent,
Fn<void(Fn<void()> closeCurrent)> retryWithNewGift);
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/call_delayed.h"
#include "base/random.h"
#include "base/unixtime.h"
#include "boxes/star_gift_auction_box.h"
#include "boxes/star_gift_craft_animation.h"
#include "boxes/star_gift_preview_box.h"
@@ -29,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_app_config.h"
#include "main/main_session.h"
#include "ui/boxes/about_cocoon_box.h" // AddUniqueCloseButton.
#include "ui/boxes/confirm_box.h"
#include "ui/controls/button_labels.h"
#include "ui/controls/feature_list.h"
#include "ui/effects/numbers_animation.h"
@@ -933,7 +935,7 @@ void Craft(
const std::vector<GiftForCraft> &gifts,
Fn<void()> closeParent) {
auto show = controller->uiShow();
auto startRequest = [=](CraftResultCallback done) {
auto startRequest = [=](Fn<void(CraftResult)> done) {
#if 0
constexpr auto kDelays = std::array<crl::time, 7>{
100, 200, 300, 400, 500, 1000, 2000
@@ -978,7 +980,15 @@ void Craft(
session->data().nextForUpgradeGiftInvalidate(session->user());
done(FindUniqueGift(session, result));
}).fail([=](const MTP::Error &error) {
show->showToast(error.type());
const auto type = error.type();
const auto waitPrefix = u"STARGIFT_CRAFT_TOO_EARLY_"_q;
if (type.startsWith(waitPrefix)) {
done(CraftResultWait{
.seconds = type.mid(waitPrefix.size()).toInt(),
});
} else {
done(CraftResultError{ type });
}
}).send();
};
const auto requested = std::make_shared<bool>();
@@ -989,11 +999,14 @@ void Craft(
}
*requested = true;
ShowSelectGiftBox(controller, giftId, [=](GiftForCraft chosen) {
ShowGiftCraftBox(
controller,
{ chosen },
closeParent,
false);
const auto unique = chosen.unique;
if (!ShowCraftLaterError(show, unique)) {
ShowGiftCraftBox(
controller,
{ chosen },
closeParent,
false);
}
closeCurrent();
}, {}, [=] { *requested = false; });
};
@@ -1208,13 +1221,16 @@ void MakeCraftContent(
}
state->requestingIndex = index;
const auto callback = [=](GiftForCraft chosen) {
auto copy = state->chosen.current();
if (state->requestingIndex < copy.size()) {
copy[state->requestingIndex] = chosen;
} else {
copy.push_back(chosen);
const auto unique = chosen.unique;
if (!ShowCraftLaterError(controller->uiShow(), unique)) {
auto copy = state->chosen.current();
if (state->requestingIndex < copy.size()) {
copy[state->requestingIndex] = chosen;
} else {
copy.push_back(chosen);
}
state->chosen = std::move(copy);
}
state->chosen = std::move(copy);
};
ShowSelectGiftBox(
controller,
@@ -1496,4 +1512,33 @@ void ShowTestGiftCraftBox(
ShowGiftCraftBox(controller, std::move(converted), [] {}, true);
}
bool ShowCraftLaterError(
std::shared_ptr<Show> show,
std::shared_ptr<Data::UniqueGift> gift) {
const auto now = base::unixtime::now();
if (gift->canCraftAt <= now) {
return false;
}
ShowCraftLaterError(show, gift->canCraftAt);
return true;
}
void ShowCraftLaterError(
std::shared_ptr<Show> show,
TimeId when) {
const auto data = base::unixtime::parse(when);
const auto time = QLocale().toString(data.time(), QLocale::ShortFormat);
const auto date = langDayOfMonthShort(data.date());
show->show(MakeInformBox({
.text = tr::lng_gift_craft_when(
lt_date,
rpl::single(tr::bold(date)),
lt_time,
rpl::single(tr::bold(time)),
tr::marked),
.title = tr::lng_gift_craft_unavailable(),
}));
}
} // namespace Ui
@@ -19,6 +19,8 @@ class SessionController;
namespace Ui {
class Show;
struct GiftForCraftEntry {
std::shared_ptr<Data::UniqueGift> unique;
Data::SavedStarGiftId manageId;
@@ -34,4 +36,12 @@ void ShowTestGiftCraftBox(
not_null<Window::SessionController*> controller,
std::vector<GiftForCraftEntry> gifts);
[[nodiscard]] bool ShowCraftLaterError(
std::shared_ptr<Show> show,
std::shared_ptr<Data::UniqueGift> gift);
void ShowCraftLaterError(
std::shared_ptr<Show> show,
TimeId when);
} // namespace Ui
@@ -1584,6 +1584,9 @@ void GenericCreditsEntryBody(
const auto canCraft = CanCraftGift(session, e);
const auto craft = canCraft ? [=] {
const auto unique = e.uniqueGift;
if (Ui::ShowCraftLaterError(show, unique)) {
return;
}
const auto savedId = EntryToSavedStarGiftId(&show->session(), e);
if (const auto window = show->resolveWindow()) {
const auto closeParent = crl::guard(box, [=] {