Full upgradable variants preview.

This commit is contained in:
John Preston
2025-12-02 17:25:38 +04:00
parent 65b3a36984
commit 0b67fa65f2
30 changed files with 1719 additions and 72 deletions

View File

@@ -121,7 +121,7 @@ namespace {
return true;
};
if (!updateThumbnail()) {
document->owner().session().downloaderTaskFinished(
document->session().downloaderTaskFinished(
) | rpl::start_with_next([=] {
if (updateThumbnail()) {
state->loadingLifetime.destroy();

View File

@@ -1731,14 +1731,14 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
}
return result;
};
auto &api = history->owner().session().api();
auto &api = history->session().api();
auto &histories = history->owner().histories();
const auto donePhraseArgs = CreateForwardedMessagePhraseArgs(
result,
msgIds);
const auto showRecentForwardsToSelf = result.size() == 1
&& result.front()->peer()->isSelf()
&& history->owner().session().premium();
&& history->session().premium();
const auto requestType = Data::Histories::RequestType::Send;
for (const auto thread : result) {
if (!comment.text.isEmpty()) {

View File

@@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/send_credits_box.h" // CreditsEmojiSmall
#include "boxes/share_box.h"
#include "boxes/star_gift_box.h"
#include "boxes/star_gift_preview_box.h"
#include "boxes/star_gift_resale_box.h"
#include "calls/group/calls_group_common.h"
#include "core/application.h"
@@ -1190,11 +1191,14 @@ void AuctionGotGiftsBox(
}
}
[[nodiscard]] rpl::producer<Data::UniqueGift> MakePreviewAuctionStream(
[[nodiscard]] rpl::producer<UniqueGiftCover> MakePreviewAuctionStream(
const Data::StarGift &info,
rpl::producer<Data::UniqueGiftAttributes> attributes) {
Expects(attributes);
const auto cover = [](Data::UniqueGift gift) {
return UniqueGiftCover{ std::move(gift) };
};
auto initial = Data::UniqueGift{
.title = info.resellTitle,
.model = Data::UniqueGiftModel{
@@ -1207,14 +1211,14 @@ void AuctionGotGiftsBox(
? info.background->backdrop()
: Data::UniqueGiftBackdrop()),
};
return rpl::single(initial) | rpl::then(std::move(
return rpl::single(cover(initial)) | rpl::then(std::move(
attributes
) | rpl::map([=](const Data::UniqueGiftAttributes &values)
-> rpl::producer<Data::UniqueGift> {
-> rpl::producer<UniqueGiftCover> {
if (values.backdrops.empty()
|| values.models.empty()
|| values.patterns.empty()) {
return rpl::never<Data::UniqueGift>();
return rpl::never<UniqueGiftCover>();
}
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
@@ -1256,12 +1260,12 @@ void AuctionGotGiftsBox(
auto &models = state->data.models;
auto &patterns = state->data.patterns;
auto &backdrops = state->data.backdrops;
consumer.put_next(Data::UniqueGift{
consumer.put_next(cover({
.title = info.resellTitle,
.model = models[index(state->modelIndices, models)],
.pattern = patterns[index(state->patternIndices, patterns)],
.backdrop = backdrops[index(state->backdropIndices, backdrops)],
});
}));
};
put();
@@ -1520,18 +1524,7 @@ void AuctionInfoBox(
st::boxRowPadding + st::uniqueGiftValueAvailableMargin,
style::al_top
)->setClickHandlerFilter([=](const auto &...) {
//if (state->previewRequested) {
// return false;
//}
//state->previewRequested = true;
//const auto &value = state->value.current();
//const auto &gift = *value.gift;
//state->previewLifetime = ShowStarGiftResale(
// window,
// peer,
// gift.id,
// gift.resellTitle,
// [=] { state->previewRequested = false; });
show->show(Box(StarGiftPreviewBox, window, *now.gift, list));
return false;
});
}, box->lifetime());

View File

@@ -2798,7 +2798,7 @@ void SetupResalePriceButton(
void AddUniqueGiftCover(
not_null<VerticalLayout*> container,
rpl::producer<Data::UniqueGift> data,
rpl::producer<UniqueGiftCover> data,
UniqueGiftCoverArgs &&args) {
const auto cover = container->add(object_ptr<RpWidget>(container));
@@ -2812,16 +2812,18 @@ void AddUniqueGiftCover(
style::FlatLabel st;
PeerData *by = nullptr;
QColor bg;
QColor fg;
};
const auto released = cover->lifetime().make_state<Released>();
released->st = st::uniqueGiftReleasedBy;
released->st.palette.linkFg = released->link.color();
const auto repaintedHook = args.repaintedHook;
if (args.resalePrice) {
auto background = rpl::duplicate(
data
) | rpl::map([=](const Data::UniqueGift &unique) {
auto result = unique.backdrop.patternColor;
) | rpl::map([=](const UniqueGiftCover &cover) {
auto result = cover.values.backdrop.patternColor;
result.setAlphaF(kGradientButtonBgOpacity * result.alphaF());
return result;
});
@@ -2870,12 +2872,15 @@ void AddUniqueGiftCover(
cover,
rpl::duplicate(
data
) | rpl::map([](const Data::UniqueGift &now) { return now.title; }),
) | rpl::map([](const UniqueGiftCover &now) {
return now.values.title;
}),
st::uniqueGiftTitle);
title->setTextColorOverride(QColor(255, 255, 255));
released->subtitleText = args.subtitle
? std::move(args.subtitle)
: rpl::duplicate(data) | rpl::map([=](const Data::UniqueGift &gift) {
: rpl::duplicate(data) | rpl::map([=](const UniqueGiftCover &cover) {
const auto &gift = cover.values;
released->by = gift.releasedBy;
return gift.releasedBy
? tr::lng_gift_unique_number_by(
@@ -2940,6 +2945,7 @@ void AddUniqueGiftCover(
std::unique_ptr<Lottie::SinglePlayer> lottie;
std::unique_ptr<Text::CustomEmoji> emoji;
base::flat_map<float64, QImage> emojis;
bool forced = false;
rpl::lifetime lifetime;
};
struct State {
@@ -2947,11 +2953,23 @@ void AddUniqueGiftCover(
GiftView next;
Animations::Simple crossfade;
bool animating = false;
bool updateAttributesPending = false;
};
const auto state = cover->lifetime().make_state<State>();
const auto lottieSize = st::creditsHistoryEntryStarGiftSize;
const auto updateLinkFg = args.subtitleLinkColored;
const auto updateColors = [=](float64 progress) {
if (repaintedHook) {
repaintedHook(state->now.gift, state->next.gift, progress);
}
released->bg = (progress == 0.)
? state->now.gift->backdrop.patternColor
: (progress == 1.)
? state->next.gift->backdrop.patternColor
: anim::color(
state->now.gift->backdrop.patternColor,
state->next.gift->backdrop.patternColor,
progress);
const auto color = (progress == 0.)
? state->now.gift->backdrop.textColor
: (progress == 1.)
@@ -2963,22 +2981,16 @@ void AddUniqueGiftCover(
if (updateLinkFg) {
released->link.update(color);
}
released->fg = color;
subtitle->setTextColorOverride(color);
released->bg = (progress == 0.)
? state->now.gift->backdrop.patternColor
: (progress == 1.)
? state->next.gift->backdrop.patternColor
: anim::color(
state->now.gift->backdrop.patternColor,
state->next.gift->backdrop.patternColor,
progress);
};
std::move(
rpl::duplicate(
data
) | rpl::start_with_next([=](const Data::UniqueGift &gift) {
) | rpl::start_with_next([=](const UniqueGiftCover &now) {
const auto setup = [&](GiftView &to) {
to.gift = gift;
const auto document = gift.model.document;
to.gift = now.values;
to.forced = now.force;
const auto document = now.values.model.document;
to.media = document->createMediaView();
to.media->automaticLoad({}, nullptr);
rpl::single() | rpl::then(
@@ -3003,7 +3015,7 @@ void AddUniqueGiftCover(
}, to.lifetime);
}, to.lifetime);
to.emoji = document->owner().customEmojiManager().create(
gift.pattern.document,
now.values.pattern.document,
[=] { cover->update(); },
Data::CustomEmojiSizeTag::Large);
[[maybe_unused]] const auto preload = to.emoji->ready();
@@ -3013,11 +3025,122 @@ void AddUniqueGiftCover(
setup(state->now);
cover->update();
updateColors(0.);
} else if (!state->next.gift) {
} else if (!state->next.gift || now.force) {
setup(state->next);
}
}, cover->lifetime());
const auto attrs = args.attributesInfo
? CreateChild<RpWidget>(cover)
: nullptr;
auto updateAttrs = Fn<void(const Data::UniqueGift &)>([](const auto &) {
});
if (attrs) {
struct AttributeState {
Ui::Text::String name;
Ui::Text::String type;
Ui::Text::String percent;
};
struct AttributesState {
AttributeState model;
AttributeState pattern;
AttributeState backdrop;
};
const auto astate = cover->lifetime().make_state<AttributesState>();
const auto setType = [&](AttributeState &state, tr::phrase<> text) {
state.type = Ui::Text::String(
st::uniqueAttributeType,
text(tr::now));
};
setType(astate->model, tr::lng_auction_preview_model);
setType(astate->pattern, tr::lng_auction_preview_symbol);
setType(astate->backdrop, tr::lng_auction_preview_backdrop);
updateAttrs = [=](const Data::UniqueGift &gift) {
const auto set = [&](
AttributeState &state,
const Data::UniqueGiftAttribute &value) {
state.name = Ui::Text::String(
st::uniqueAttributeName,
value.name);
state.percent = Ui::Text::String(
st::uniqueAttributePercent,
QString::number(value.rarityPermille / 10.) + '%');
};
set(astate->model, gift.model);
set(astate->pattern, gift.pattern);
set(astate->backdrop, gift.backdrop);
attrs->update();
};
const auto height = st::uniqueAttributeTop
+ st::uniqueAttributePadding.top()
+ st::uniqueAttributeName.font->height
+ st::uniqueAttributeType.font->height
+ st::uniqueAttributePadding.bottom();
attrs->resize(attrs->width(), height);
attrs->paintOn([=](QPainter &p) {
const auto skip = st::giftBoxGiftSkip.x();
const auto available = attrs->width() - 2 * skip;
const auto single = available / 3;
if (single <= 0) {
return;
}
auto hq = PainterHighQualityEnabler(p);
auto bg = released->bg;
bg.setAlphaF(kGradientButtonBgOpacity * bg.alphaF());
const auto innert = st::uniqueAttributeTop;
const auto innerh = height - innert;
const auto radius = innerh / 3.;
const auto paint = [&](int x, const AttributeState &state) {
p.setPen(Qt::NoPen);
p.setBrush(bg);
p.drawRoundedRect(x, innert, single, innerh, radius, radius);
p.setPen(QColor(255, 255, 255));
const auto padding = st::uniqueAttributePadding;
const auto inner = single - padding.left() - padding.right();
const auto namew = std::min(inner, state.name.maxWidth());
state.name.draw(p, {
.position = QPoint(
x + (single - namew) / 2,
innert + padding.top()),
.availableWidth = namew,
.elisionLines = 1,
});
p.setPen(released->fg);
const auto typew = std::min(inner, state.type.maxWidth());
state.type.draw(p, {
.position = QPoint(
x + (single - typew) / 2,
innert + padding.top() + state.name.minHeight()),
.availableWidth = typew,
});
p.setPen(Qt::NoPen);
p.setBrush(anim::color(released->bg, released->fg, 0.3));
const auto r = st::uniqueAttributePercent.font->height / 2.;
const auto left = x + single - state.percent.maxWidth();
const auto top = st::uniqueAttributePercentPadding.top();
const auto percent = QRect(
left,
top,
state.percent.maxWidth(),
st::uniqueAttributeType.font->height);
p.drawRoundedRect(
percent.marginsAdded(st::uniqueAttributePercentPadding),
r,
r);
p.setPen(QColor(255, 255, 255));
state.percent.draw(p, {
.position = percent.topLeft(),
});
};
auto left = 0;
paint(left, astate->model);
paint(left + single + skip, astate->backdrop);
paint(attrs->width() - single - left, astate->pattern);
});
}
updateAttrs(*state->now.gift);
cover->widthValue() | rpl::start_with_next([=](int width) {
const auto skip = st::uniqueGiftBottom;
if (width <= 3 * skip) {
@@ -3043,14 +3166,32 @@ void AddUniqueGiftCover(
}
subtitle->moveToLeft(skip, top);
top += subtitle->height() + skip;
cover->resize(width, subtitle->y() + subtitle->height() + skip);
if (attrs) {
attrs->resizeToWidth(width
- st::giftBoxPadding.left()
- st::giftBoxPadding.right());
attrs->moveToLeft(st::giftBoxPadding.left(), top);
top += attrs->height() + (skip / 2);
}
cover->resize(width, top);
}, cover->lifetime());
cover->paintRequest() | rpl::start_with_next([=] {
auto p = QPainter(cover);
auto progress = state->crossfade.value(state->animating ? 1. : 0.);
if (state->updateAttributesPending && progress >= 0.5) {
state->updateAttributesPending = false;
updateAttrs(*state->next.gift);
} else if (state->updateAttributesPending
&& !state->animating
&& !state->crossfade.animating()) {
state->updateAttributesPending = false;
updateAttrs(*state->now.gift);
}
if (state->animating) {
updateColors(progress);
}
@@ -3105,10 +3246,13 @@ void AddUniqueGiftCover(
};
if (progress < 1.) {
const auto finished = paint(state->now, 1. - progress);
const auto finished = paint(state->now, 1. - progress)
|| (state->next.forced
&& (!state->animating || !state->crossfade.animating()));
const auto next = finished ? state->next.lottie.get() : nullptr;
if (next && next->ready()) {
state->animating = true;
state->updateAttributesPending = true;
state->crossfade.start([=] {
cover->update();
}, 0., 1., kCrossfadeDuration);
@@ -3803,12 +3947,12 @@ struct UpgradeArgs : StarGiftUpgradeArgs {
std::vector<UpgradePrice> nextPrices;
};
[[nodiscard]] rpl::producer<Data::UniqueGift> MakeUpgradeGiftStream(
[[nodiscard]] rpl::producer<UniqueGiftCover> MakeUpgradeGiftStream(
const UpgradeArgs &args) {
if (args.models.empty()
|| args.patterns.empty()
|| args.backdrops.empty()) {
return rpl::never<Data::UniqueGift>();
return rpl::never<UniqueGiftCover>();
}
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
@@ -3846,14 +3990,14 @@ struct UpgradeArgs : StarGiftUpgradeArgs {
auto &models = state->data.models;
auto &patterns = state->data.patterns;
auto &backdrops = state->data.backdrops;
consumer.put_next(Data::UniqueGift{
consumer.put_next(UniqueGiftCover{ Data::UniqueGift{
.title = (state->data.savedId
? tr::lng_gift_upgrade_title(tr::now)
: tr::lng_gift_upgrade_preview_title(tr::now)),
.model = models[index(state->modelIndices, models)],
.pattern = patterns[index(state->patternIndices, patterns)],
.backdrop = backdrops[index(state->backdropIndices, backdrops)],
});
} });
};
put();

View File

@@ -72,11 +72,20 @@ struct UniqueGiftCoverArgs {
bool subtitleLinkColored = false;
rpl::producer<CreditsAmount> resalePrice;
Fn<void()> resaleClick;
bool attributesInfo = false;
Fn<void(
std::optional<Data::UniqueGift> now,
std::optional<Data::UniqueGift> next,
float64 progress)> repaintedHook;
};
struct UniqueGiftCover {
Data::UniqueGift values;
bool force = false;
};
void AddUniqueGiftCover(
not_null<VerticalLayout*> container,
rpl::producer<Data::UniqueGift> data,
rpl::producer<UniqueGiftCover> data,
UniqueGiftCoverArgs &&args);
void AddWearGiftCover(
not_null<VerticalLayout*> container,

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace Data {
struct StarGift;
struct UniqueGiftAttributes;
} // namespace Data
namespace Window {
class SessionController;
} // namespace Window
namespace Ui {
class GenericBox;
void StarGiftPreviewBox(
not_null<GenericBox*> box,
not_null<Window::SessionController*> controller,
const Data::StarGift &gift,
const Data::UniqueGiftAttributes &attributes);
} // namespace Ui

View File

@@ -1548,7 +1548,7 @@ void StickerSetBox::Inner::fillDeleteStickerBox(
const auto buttonWidth = state->saveButton
? state->saveButton->width()
: 0;
state->requestId = document->owner().session().api().request(
state->requestId = document->session().api().request(
MTPstickers_RemoveStickerFromSet(document->mtpInput()
)).done([=](const TLStickerSet &result) {
result.match([&](const MTPDmessages_stickerSet &d) {
@@ -1590,7 +1590,7 @@ void StickerSetBox::Inner::fillDeleteStickerBox(
state->requestId.value() | rpl::map(rpl::mappers::_1 > 0));
}
box->addButton(tr::lng_close(), [=] {
document->owner().session().api().request(
document->session().api().request(
state->requestId.current()).cancel();
box->closeBox();
});