From d05155a403706297ee2f4609eccead2413c706a4 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 4 Jul 2025 16:29:53 +0400 Subject: [PATCH] Show who released the gift. --- Telegram/Resources/langs/lang.strings | 3 + Telegram/SourceFiles/api/api_premium.cpp | 1 + Telegram/SourceFiles/boxes/star_gift_box.cpp | 166 +++++++++++++++++- Telegram/SourceFiles/boxes/star_gift_box.h | 2 + Telegram/SourceFiles/data/data_credits.h | 1 + .../SourceFiles/data/data_media_types.cpp | 4 +- Telegram/SourceFiles/data/data_media_types.h | 1 + Telegram/SourceFiles/data/data_star_gift.h | 1 + Telegram/SourceFiles/history/history_item.cpp | 2 + .../view/media/history_view_premium_gift.cpp | 29 ++- .../view/media/history_view_premium_gift.h | 3 + .../view/media/history_view_service_box.cpp | 60 +++++++ .../view/media/history_view_service_box.h | 7 + .../view/media/history_view_unique_gift.cpp | 104 +++++++++++ .../settings/settings_credits_graphics.cpp | 21 ++- Telegram/SourceFiles/ui/chat/chat.style | 2 + Telegram/SourceFiles/ui/effects/credits.style | 4 + 17 files changed, 400 insertions(+), 11 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 734a93e453..9c50c0e773 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2863,6 +2863,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_credits_box_history_entry_gift_converted" = "Converted Gift"; "lng_credits_box_history_entry_gift_transfer" = "Gift Transfer"; "lng_credits_box_history_entry_gift_unavailable" = "Unavailable"; +"lng_credits_box_history_entry_gift_released" = "released by {name}"; "lng_credits_box_history_entry_gift_sold_out" = "This gift has sold out"; "lng_credits_box_history_entry_gift_out_about" = "With Stars, **{user}** will be able to unlock content and services on Telegram.\n{link}"; "lng_credits_box_history_entry_gift_in_about" = "Use Stars to unlock content and services on Telegram. {link}"; @@ -3581,12 +3582,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_gift_self_about" = "Buy yourself a gift to display on your page or reserve for later.\n\nLimited-edition gifts upgraded to collectibles can be gifted to others later."; "lng_gift_channel_title" = "Send a Gift"; "lng_gift_channel_about" = "Select a gift to show appreciation for {name}."; +"lng_gift_released_by" = "Released by {name}"; "lng_gift_unique_owner" = "Owner"; "lng_gift_unique_address_copied" = "Address copied to clipboard."; "lng_gift_unique_status" = "Status"; "lng_gift_unique_status_non" = "Non-Unique"; "lng_gift_unique_status_upgrade" = "upgrade"; "lng_gift_unique_number" = "Collectible #{index}"; +"lng_gift_unique_number_by" = "Collectible #{index} by {name}"; "lng_gift_unique_model" = "Model"; "lng_gift_unique_backdrop" = "Backdrop"; "lng_gift_unique_symbol" = "Symbol"; diff --git a/Telegram/SourceFiles/api/api_premium.cpp b/Telegram/SourceFiles/api/api_premium.cpp index 946d954466..5065541e27 100644 --- a/Telegram/SourceFiles/api/api_premium.cpp +++ b/Telegram/SourceFiles/api/api_premium.cpp @@ -867,6 +867,7 @@ std::optional FromTL( .ownerId = (data.vowner_id() ? peerFromMTP(*data.vowner_id()) : PeerId()), + .releasedBy = releasedBy, .number = data.vnum().v, .starsForResale = int(data.vresell_stars().value_or_empty()), .model = *model, diff --git a/Telegram/SourceFiles/boxes/star_gift_box.cpp b/Telegram/SourceFiles/boxes/star_gift_box.cpp index 6e4608a47d..473e840881 100644 --- a/Telegram/SourceFiles/boxes/star_gift_box.cpp +++ b/Telegram/SourceFiles/boxes/star_gift_box.cpp @@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "chat_helpers/stickers_lottie.h" #include "chat_helpers/tabbed_panel.h" #include "chat_helpers/tabbed_selector.h" +#include "core/application.h" #include "core/ui_integration.h" #include "data/components/promo_suggestions.h" #include "data/data_birthday.h" @@ -94,6 +95,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/slide_wrap.h" #include "window/themes/window_theme.h" #include "window/section_widget.h" +#include "window/window_controller.h" #include "window/window_session_controller.h" #include "styles/style_boxes.h" #include "styles/style_chat.h" @@ -333,6 +335,69 @@ private: }; +class TextBubblePart final : public MediaGenericTextPart { +public: + TextBubblePart( + TextWithEntities text, + QMargins margins, + const style::TextStyle &st = st::defaultTextStyle, + const base::flat_map &links = {}, + const Ui::Text::MarkedContext &context = {}, + style::align align = style::al_top); + + void draw( + Painter &p, + not_null owner, + const PaintContext &context, + int outerWidth) const override; + +private: + void setupPen( + Painter &p, + not_null owner, + const PaintContext &context) const override; + int elisionLines() const override; + +}; + +TextBubblePart::TextBubblePart( + TextWithEntities text, + QMargins margins, + const style::TextStyle &st, + const base::flat_map &links, + const Ui::Text::MarkedContext &context, + style::align align) +: MediaGenericTextPart(std::move(text), margins, st, links, context, align) { +} + +void TextBubblePart::draw( + Painter &p, + not_null owner, + const PaintContext &context, + int outerWidth) const { + p.setPen(Qt::NoPen); + p.setBrush(context.st->msgServiceBg()); + const auto radius = height() / 2.; + const auto left = (outerWidth - width()) / 2; + const auto r = QRect(left, 0, width(), height()); + p.drawRoundedRect(r, radius, radius); + + MediaGenericTextPart::draw(p, owner, context, outerWidth); +} + +void TextBubblePart::setupPen( + Painter &p, + not_null owner, + const PaintContext &context) const { + auto pen = context.st->msgServiceFg()->c; + pen.setAlphaF(pen.alphaF() * 0.65); + p.setPen(pen); +} + +int TextBubblePart::elisionLines() const { + return 1; +} + [[nodiscard]] AttributeId FromTL(const MTPStarGiftAttributeId &id) { return id.match([&](const MTPDstarGiftAttributeIdBackdrop &data) { return AttributeId{ @@ -526,6 +591,21 @@ auto GenerateGiftMedia( st::giftBoxPreviewTitlePadding, {}, context); + + if (v::is(descriptor)) { + const auto &stars = v::get(descriptor); + if (const auto by = stars.info.releasedBy) { + push(std::make_unique( + tr::lng_gift_released_by( + tr::now, + lt_name, + Ui::Text::Link('@' + by->username()), + Ui::Text::WithEntities), + st::giftBoxReleasedByMargin, + st::defaultTextStyle)); + } + } + pushText( std::move(description), st::giftBoxPreviewTextPadding, @@ -778,7 +858,7 @@ void PreviewWrap::prepare(rpl::producer details) { owned.get(), GenerateGiftMedia(owned.get(), _item.get(), _recipient, details), MediaGenericDescriptor{ - .maxWidth = st::chatIntroWidth, + .maxWidth = st::chatGiftPreviewWidth, .service = true, })); _item = std::move(owned); @@ -3820,6 +3900,19 @@ void AddUniqueGiftCover( Fn resaleClick) { const auto cover = container->add(object_ptr(container)); + struct Released { + Released() : white(QColor(255, 255, 255)) { + } + + style::owned_color white; + style::FlatLabel st; + PeerData *by = nullptr; + QColor bg; + }; + const auto released = cover->lifetime().make_state(); + released->st = st::uniqueGiftSubtitle; + released->st.palette.linkFg = released->white.color(); + if (resalePrice) { auto background = rpl::duplicate( data @@ -3841,17 +3934,56 @@ void AddUniqueGiftCover( st::uniqueGiftTitle); title->setTextColorOverride(QColor(255, 255, 255)); auto subtitleText = subtitleOverride - ? std::move(subtitleOverride) - : rpl::duplicate(data) | rpl::map([](const Data::UniqueGift &gift) { - return tr::lng_gift_unique_number( - tr::now, - lt_index, - QString::number(gift.number)); + ? std::move(subtitleOverride) | Ui::Text::ToWithEntities() + : rpl::duplicate(data) | rpl::map([=](const Data::UniqueGift &gift) { + released->by = gift.releasedBy; + released->bg = gift.backdrop.patternColor; + return gift.releasedBy + ? tr::lng_gift_unique_number_by( + tr::now, + lt_index, + TextWithEntities{ QString::number(gift.number) }, + lt_name, + Ui::Text::Link('@' + gift.releasedBy->username()), + Ui::Text::WithEntities) + : tr::lng_gift_unique_number( + tr::now, + lt_index, + TextWithEntities{ QString::number(gift.number) }, + Ui::Text::WithEntities); }); const auto subtitle = CreateChild( cover, std::move(subtitleText), - st::uniqueGiftSubtitle); + released->st); + if (released->by) { + const auto button = CreateChild(cover); + subtitle->raise(); + subtitle->setAttribute(Qt::WA_TransparentForMouseEvents); + + button->setClickedCallback([=] { + GiftReleasedByHandler(released->by); + }); + subtitle->geometryValue( + ) | rpl::start_with_next([=](QRect geometry) { + button->setGeometry( + geometry.marginsAdded(st::giftBoxReleasedByMargin)); + }, button->lifetime()); + button->paintRequest() | rpl::start_with_next([=] { + auto p = QPainter(button); + auto hq = PainterHighQualityEnabler(p); + const auto use = subtitle->textMaxWidth(); + const auto add = button->width() - subtitle->width(); + const auto full = use + add; + const auto left = (button->width() - full) / 2; + const auto height = button->height(); + const auto radius = height / 2.; + p.setPen(Qt::NoPen); + p.setBrush(released->bg); + p.setOpacity(0.5); + p.drawRoundedRect(left, 0, full, height, radius, radius); + }, button->lifetime()); + } struct GiftView { QImage gradient; @@ -4417,6 +4549,24 @@ void ShowUniqueGiftSellBox( })); } +void GiftReleasedByHandler(not_null peer) { + const auto session = &peer->session(); + const auto window = session->tryResolveWindow(peer); + if (window) { + window->showPeerHistory(peer); + return; + } + const auto account = not_null(&session->account()); + if (const auto window = Core::App().windowFor(account)) { + window->invokeForSessionController( + &session->account(), + peer, + [=](not_null window) { + window->showPeerHistory(peer); + }); + } +} + struct UpgradeArgs : StarGiftUpgradeArgs { std::vector models; std::vector patterns; diff --git a/Telegram/SourceFiles/boxes/star_gift_box.h b/Telegram/SourceFiles/boxes/star_gift_box.h index d26270d1a4..78ee14a028 100644 --- a/Telegram/SourceFiles/boxes/star_gift_box.h +++ b/Telegram/SourceFiles/boxes/star_gift_box.h @@ -82,6 +82,8 @@ void ShowUniqueGiftSellBox( Data::SavedStarGiftId savedId, Settings::GiftWearBoxStyleOverride st); +void GiftReleasedByHandler(not_null peer); + struct PatternPoint { QPointF position; float64 scale = 1.; diff --git a/Telegram/SourceFiles/data/data_credits.h b/Telegram/SourceFiles/data/data_credits.h index 21f2427a4e..0b0b9382ed 100644 --- a/Telegram/SourceFiles/data/data_credits.h +++ b/Telegram/SourceFiles/data/data_credits.h @@ -65,6 +65,7 @@ struct CreditsHistoryEntry final { uint64 bareGiveawayMsgId = 0; uint64 bareGiftStickerId = 0; uint64 bareGiftOwnerId = 0; + uint64 bareGiftReleasedById = 0; uint64 bareGiftResaleRecipientId = 0; uint64 bareActorId = 0; uint64 bareEntryOwnerId = 0; diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index 30b3929be7..c8a1d35d07 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -2569,7 +2569,9 @@ std::unique_ptr MediaGiftBox::createView( message, HistoryView::GenerateUniqueGiftMedia(message, replacing, unique), HistoryView::MediaGenericDescriptor{ - .maxWidth = st::msgServiceGiftBoxSize.width(), + .maxWidth = (_data.stargiftReleasedBy + ? st::msgServiceStarGiftByWidth + : st::msgServiceGiftBoxSize.width()), .paintBg = HistoryView::UniqueGiftBg(message, unique), .service = true, }); diff --git a/Telegram/SourceFiles/data/data_media_types.h b/Telegram/SourceFiles/data/data_media_types.h index dab3e57b2e..bf5d003830 100644 --- a/Telegram/SourceFiles/data/data_media_types.h +++ b/Telegram/SourceFiles/data/data_media_types.h @@ -144,6 +144,7 @@ struct GiftCode { QString slug; uint64 stargiftId = 0; DocumentData *document = nullptr; + PeerData *stargiftReleasedBy = nullptr; std::shared_ptr unique; TextWithEntities message; ChannelData *channel = nullptr; diff --git a/Telegram/SourceFiles/data/data_star_gift.h b/Telegram/SourceFiles/data/data_star_gift.h index e6e6bc9ca2..df0aacc953 100644 --- a/Telegram/SourceFiles/data/data_star_gift.h +++ b/Telegram/SourceFiles/data/data_star_gift.h @@ -44,6 +44,7 @@ struct UniqueGift { QString ownerAddress; QString ownerName; PeerId ownerId = 0; + PeerData *releasedBy = nullptr; int number = 0; int starsForTransfer = -1; int starsForResale = -1; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 691661d8c0..2f8c24b60f 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -6349,6 +6349,7 @@ void HistoryItem::applyAction(const MTPMessageAction &action) { fields.stargiftId = gift->id; fields.starsToUpgrade = gift->starsToUpgrade; fields.document = gift->document; + fields.stargiftReleasedBy = gift->releasedBy; fields.limitedCount = gift->limitedCount; fields.limitedLeft = gift->limitedLeft; fields.count = gift->stars; @@ -6384,6 +6385,7 @@ void HistoryItem::applyAction(const MTPMessageAction &action) { if (auto gift = Api::FromTL(&history()->session(), data.vgift())) { fields.stargiftId = gift->id; fields.document = gift->document; + fields.stargiftReleasedBy = gift->releasedBy; fields.limitedCount = gift->limitedCount; fields.limitedLeft = gift->limitedLeft; fields.count = gift->stars; diff --git a/Telegram/SourceFiles/history/view/media/history_view_premium_gift.cpp b/Telegram/SourceFiles/history/view/media/history_view_premium_gift.cpp index d3ef5e26a1..bcbcaf70e2 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_premium_gift.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_premium_gift.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_premium.h" #include "base/unixtime.h" #include "boxes/gift_premium_box.h" // ResolveGiftCode +#include "boxes/star_gift_box.h" // GiftReleasedByHandler #include "chat_helpers/stickers_gift_box_pack.h" #include "core/click_handler_types.h" // ClickHandlerContext #include "data/stickers/data_custom_emoji.h" @@ -54,7 +55,9 @@ int PremiumGift::top() { } int PremiumGift::width() { - return st::msgServiceStarGiftBoxWidth; + return _data.stargiftReleasedBy + ? st::msgServiceStarGiftByWidth + : st::msgServiceStarGiftBoxWidth; } QSize PremiumGift::size() { @@ -120,6 +123,18 @@ TextWithEntities PremiumGift::title() { : tr::lng_prize_title(tr::now, WithEntities); } +TextWithEntities PremiumGift::author() { + using namespace Ui::Text; + if (!_data.stargiftReleasedBy) { + return {}; + } + return tr::lng_gift_released_by( + tr::now, + lt_name, + Ui::Text::Link('@' + _data.stargiftReleasedBy->username()), + Ui::Text::WithEntities); +} + TextWithEntities PremiumGift::subtitle() { if (tonGift()) { return tr::lng_action_gift_got_ton(tr::now, Ui::Text::WithEntities); @@ -319,6 +334,18 @@ ClickHandlerPtr PremiumGift::createViewLink() { }); } +ClickHandlerPtr PremiumGift::authorLink() { + if (const auto by = _data.stargiftReleasedBy) { + if (!_authorLink) { + _authorLink = std::make_shared([=] { + Ui::GiftReleasedByHandler(by); + }); + } + return _authorLink; + } + return nullptr; +} + int PremiumGift::buttonSkip() { return st::msgServiceGiftBoxButtonMargins.top(); } diff --git a/Telegram/SourceFiles/history/view/media/history_view_premium_gift.h b/Telegram/SourceFiles/history/view/media/history_view_premium_gift.h index ef0bf67612..10be8b9bcb 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_premium_gift.h +++ b/Telegram/SourceFiles/history/view/media/history_view_premium_gift.h @@ -29,6 +29,7 @@ public: int width() override; QSize size() override; TextWithEntities title() override; + TextWithEntities author() override; TextWithEntities subtitle() override; rpl::producer button() override; std::optional buttonMinistars() override; @@ -39,6 +40,7 @@ public: const PaintContext &context, const QRect &geometry) override; ClickHandlerPtr createViewLink() override; + ClickHandlerPtr authorLink() override; bool hideServiceText() override; void stickerClearLoopPlayed() override; @@ -63,6 +65,7 @@ private: const not_null _parent; const not_null _gift; const Data::GiftCode &_data; + ClickHandlerPtr _authorLink; QImage _badgeCache; Info::PeerGifts::GiftBadge _badgeKey; mutable std::optional _sticker; diff --git a/Telegram/SourceFiles/history/view/media/history_view_service_box.cpp b/Telegram/SourceFiles/history/view/media/history_view_service_box.cpp index 5e078f7d19..4e5e11d65f 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_service_box.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_service_box.cpp @@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/rect.h" #include "ui/power_saving.h" #include "styles/style_chat.h" +#include "styles/style_credits.h" #include "styles/style_premium.h" #include "styles/style_layers.h" @@ -51,6 +52,11 @@ ServiceBox::ServiceBox( .session = &parent->history()->session(), .repaint = [parent] { parent->customEmojiRepaint(); }, })) +, _author( + st::defaultTextStyle, + _content->author(), + kMarkupTextOptions, + _maxWidth) , _subtitle( st::premiumPreviewAbout.style, Ui::Text::Filtered( @@ -79,6 +85,12 @@ ServiceBox::ServiceBox( ? 0 : (_title.countHeight(_maxWidth) + st::msgServiceGiftBoxTitlePadding.bottom())) + + (_author.isEmpty() + ? 0 + : (st::giftBoxReleasedByMargin.top() + + st::defaultTextStyle.font->height + + st::giftBoxReleasedByMargin.bottom() + + st::msgServiceGiftBoxTitlePadding.bottom())) + _subtitle.countHeight(_maxWidth) + (!_content->button() ? 0 @@ -164,6 +176,37 @@ void ServiceBox::draw(Painter &p, const PaintContext &context) const { }); top += _title.countHeight(_maxWidth) + padding.bottom(); } + if (!_author.isEmpty()) { + auto hq = PainterHighQualityEnabler(p); + p.setPen(Qt::NoPen); + p.setBrush(context.st->msgServiceBg()); + const auto use = std::min(_maxWidth, _author.maxWidth()) + + st::giftBoxReleasedByMargin.left() + + st::giftBoxReleasedByMargin.right(); + const auto left = st::msgPadding.left() + (_maxWidth - use) / 2; + const auto height = st::giftBoxReleasedByMargin.top() + + st::defaultTextStyle.font->height + + st::giftBoxReleasedByMargin.bottom(); + const auto radius = height / 2.; + p.drawRoundedRect(left, top, use, height, radius, radius); + + auto fg = context.st->msgServiceFg()->c; + fg.setAlphaF(0.65 * fg.alphaF()); + p.setPen(fg); + _author.draw(p, { + .position = QPoint( + left + st::giftBoxReleasedByMargin.left(), + top + st::giftBoxReleasedByMargin.top()), + .availableWidth = (use + - st::giftBoxReleasedByMargin.left() + - st::giftBoxReleasedByMargin.right()), + .palette = &context.st->serviceTextPalette(), + .elisionLines = 1, + }); + p.setPen(context.st->msgServiceFg()); + + top += height + st::msgServiceGiftBoxTitlePadding.bottom(); + } _parent->prepareCustomEmojiPaint(p, context, _subtitle); _subtitle.draw(p, { .position = QPoint(st::msgPadding.left(), top), @@ -231,6 +274,23 @@ TextState ServiceBox::textState(QPoint point, StateRequest request) const { if (!_title.isEmpty()) { top += _title.countHeight(_maxWidth) + padding.bottom(); } + if (!_author.isEmpty()) { + const auto use = std::min(_maxWidth, _author.maxWidth()) + + st::giftBoxReleasedByMargin.left() + + st::giftBoxReleasedByMargin.right(); + const auto left = st::msgPadding.left() + (_maxWidth - use) / 2; + const auto height = st::giftBoxReleasedByMargin.top() + + st::defaultTextStyle.font->height + + st::giftBoxReleasedByMargin.bottom(); + if (point.x() >= left + && point.y() >= top + && point.x() < left + use + && point.y() < top + height) { + result.link = _content->authorLink(); + } + top += height + st::msgServiceGiftBoxTitlePadding.bottom(); + } + auto subtitleRequest = request.forText(); subtitleRequest.align = style::al_top; const auto state = _subtitle.getState( diff --git a/Telegram/SourceFiles/history/view/media/history_view_service_box.h b/Telegram/SourceFiles/history/view/media/history_view_service_box.h index fe7018e849..b15ded3306 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_service_box.h +++ b/Telegram/SourceFiles/history/view/media/history_view_service_box.h @@ -28,6 +28,9 @@ public: [[nodiscard]] virtual int top() = 0; [[nodiscard]] virtual QSize size() = 0; [[nodiscard]] virtual TextWithEntities title() = 0; + [[nodiscard]] virtual TextWithEntities author() { + return {}; + } [[nodiscard]] virtual TextWithEntities subtitle() = 0; [[nodiscard]] virtual int buttonSkip() { return top(); @@ -45,6 +48,9 @@ public: const PaintContext &context, const QRect &geometry) = 0; [[nodiscard]] virtual ClickHandlerPtr createViewLink() = 0; + [[nodiscard]] virtual ClickHandlerPtr authorLink() { + return nullptr; + } [[nodiscard]] virtual bool hideServiceText() = 0; @@ -123,6 +129,7 @@ private: const int _maxWidth = 0; Ui::Text::String _title; + Ui::Text::String _author; Ui::Text::String _subtitle; const QSize _size; const QSize _innerSize; diff --git a/Telegram/SourceFiles/history/view/media/history_view_unique_gift.cpp b/Telegram/SourceFiles/history/view/media/history_view_unique_gift.cpp index b44d10f000..350b7846c5 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_unique_gift.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_unique_gift.cpp @@ -81,6 +81,95 @@ private: }; +class TextBubblePart final : public MediaGenericTextPart { +public: + TextBubblePart( + TextWithEntities text, + QMargins margins, + Data::UniqueGiftBackdrop backdrop, + ClickHandlerPtr link); + + void draw( + Painter &p, + not_null owner, + const PaintContext &context, + int outerWidth) const override; + TextState textState( + QPoint point, + StateRequest request, + int outerWidth) const override; + +private: + void setupPen( + Painter &p, + not_null owner, + const PaintContext &context) const override; + int elisionLines() const override; + + Data::UniqueGiftBackdrop _backdrop; + ClickHandlerPtr _link; + +}; + +TextBubblePart::TextBubblePart( + TextWithEntities text, + QMargins margins, + Data::UniqueGiftBackdrop backdrop, + ClickHandlerPtr link) +: MediaGenericTextPart( + std::move(text), + margins, + st::defaultTextStyle, + {}, + {}, + style::al_top) +, _backdrop(backdrop) +, _link(std::move(link)) { +} + +void TextBubblePart::draw( + Painter &p, + not_null owner, + const PaintContext &context, + int outerWidth) const { + p.setPen(Qt::NoPen); + p.setOpacity(0.5); + p.setBrush(_backdrop.patternColor); + const auto radius = height() / 2.; + const auto left = (outerWidth - width()) / 2; + const auto r = QRect(left, 0, width(), height()); + p.drawRoundedRect(r, radius, radius); + p.setOpacity(1.); + + MediaGenericTextPart::draw(p, owner, context, outerWidth); +} + +TextState TextBubblePart::textState( + QPoint point, + StateRequest request, + int outerWidth) const { + auto result = TextState(); + const auto left = (outerWidth - width()) / 2; + if (point.x() >= left + && point.y() >= 0 + && point.x() < left + width() + && point.y() < height()) { + result.link = _link; + } + return result; +} + +void TextBubblePart::setupPen( + Painter &p, + not_null owner, + const PaintContext &context) const { + p.setPen(_backdrop.textColor); +} + +int TextBubblePart::elisionLines() const { + return 1; +} + ButtonPart::ButtonPart( const QString &text, QMargins margins, @@ -284,6 +373,21 @@ auto GenerateUniqueGiftMedia( gift->backdrop.textColor, st::chatUniqueTextPadding); + if (const auto by = gift->releasedBy) { + const auto handler = std::make_shared([=] { + Ui::GiftReleasedByHandler(by); + }); + push(std::make_unique( + tr::lng_gift_released_by( + tr::now, + lt_name, + Ui::Text::Link('@' + by->username()), + Ui::Text::WithEntities), + st::giftBoxReleasedByMargin, + gift->backdrop, + handler)); + } + const auto name = [](const Data::UniqueGiftAttribute &value) { return Ui::Text::Bold(value.name); }; diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp index 15d889fea8..afb9b9c64b 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp @@ -1434,7 +1434,23 @@ void GenericCreditsEntryBox( Ui::AddSkip(content); } - if (!isStarGift || creditsHistoryStarGift || e.soldOutInfo) { + if (e.bareGiftReleasedById && !e.uniqueGift) { + const auto peer = owner->peer(PeerId(e.bareGiftReleasedById)); + const auto released = content->add( + object_ptr>( + box, + object_ptr( + content, + tr::lng_credits_box_history_entry_gift_released( + lt_name, + rpl::single(Ui::Text::Link('@' + peer->username())), + Ui::Text::WithEntities), + st::creditsReleasedByLabel))); + released->entity()->setClickHandlerFilter([=](const auto &...) { + Ui::GiftReleasedByHandler(peer); + return false; + }); + } else if (!isStarGift || creditsHistoryStarGift || e.soldOutInfo) { constexpr auto kMinus = QChar(0x2212); auto &lifetime = content->lifetime(); const auto text = lifetime.make_state(); @@ -2282,6 +2298,9 @@ void StarGiftViewBox( .bareGiftOwnerId = (data.unique ? data.unique->ownerId.value : toId.value), + .bareGiftReleasedById = (data.stargiftReleasedBy + ? data.stargiftReleasedBy->id.value + : 0), .bareActorId = (toChannel ? data.channelFrom->id.value : 0), .bareEntryOwnerId = (toChannel ? data.channel->id.value : 0), .giftChannelSavedId = data.channelSavedId, diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index 37a16ad80f..cdc6d2be56 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -936,6 +936,7 @@ msgServiceGiftBoxTitlePadding: margins(0px, 20px, 0px, 6px); msgServiceGiftBoxStickerTop: -19px; msgServiceGiftBoxStickerSize: 140px; msgServiceStarGiftBoxWidth: 224px; +msgServiceStarGiftByWidth: 272px; msgServiceStarGiftStickerTop: 24px; msgServiceStarGiftStickerSize: 100px; @@ -1105,6 +1106,7 @@ chatIntroWidth: 224px; chatIntroTitleMargin: margins(11px, 16px, 11px, 4px); chatIntroMargin: margins(11px, 0px, 11px, 0px); chatIntroStickerPadding: margins(10px, 8px, 10px, 16px); +chatGiftPreviewWidth: 264px; liveLocationLongInIcon: icon {{ "chat/live_location_long", msgInServiceFg }}; liveLocationLongInIconSelected: icon {{ "chat/live_location_long", msgInServiceFgSelected }}; diff --git a/Telegram/SourceFiles/ui/effects/credits.style b/Telegram/SourceFiles/ui/effects/credits.style index 3c865fead6..b9c69f00e5 100644 --- a/Telegram/SourceFiles/ui/effects/credits.style +++ b/Telegram/SourceFiles/ui/effects/credits.style @@ -66,6 +66,9 @@ creditsBoxAboutDivider: FlatLabel(boxDividerLabel) { creditsBoxButtonLabel: FlatLabel(defaultFlatLabel) { style: semiboldTextStyle; } +creditsReleasedByLabel: FlatLabel(defaultFlatLabel) { + textFg: windowSubTextFg; +} starIconEmoji: IconEmoji { icon: icon{{ "payments/premium_emoji", creditsBg1 }}; @@ -175,6 +178,7 @@ giftBoxButtonBottomByStars: 18px; giftBoxButtonPadding: margins(8px, 4px, 8px, 4px); giftBoxPreviewStickerPadding: margins(10px, 12px, 10px, 16px); giftBoxPreviewTitlePadding: margins(12px, 4px, 12px, 4px); +giftBoxReleasedByMargin: margins(12px, 2px, 12px, 2px); giftBoxPreviewTextPadding: margins(12px, 4px, 12px, 4px); giftBoxButtonMargin: margins(12px, 8px, 12px, 12px); giftBoxStickerTop: 0px;