From 4b9648d8d9ae1293f9fb5e029ce4d6cd8a5e399d Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 13 Dec 2023 08:14:41 +0000 Subject: [PATCH] Update API scheme. Giveaway winners. --- Telegram/Resources/art/winners.tgs | Bin 0 -> 14103 bytes Telegram/Resources/langs/lang.strings | 15 ++ Telegram/Resources/qrc/telegram/telegram.qrc | 1 + .../SourceFiles/boxes/gift_premium_box.cpp | 49 +++-- Telegram/SourceFiles/boxes/gift_premium_box.h | 6 +- .../chat_helpers/stickers_dice_pack.cpp | 5 +- .../chat_helpers/stickers_dice_pack.h | 1 + .../SourceFiles/data/data_media_types.cpp | 117 +++++++++-- Telegram/SourceFiles/data/data_media_types.h | 61 +++++- .../export/data/export_data_types.cpp | 4 +- .../export/data/export_data_types.h | 4 +- .../export/output/export_output_html.cpp | 8 +- .../export/output/export_output_json.cpp | 2 +- .../admin_log/history_admin_log_item.cpp | 134 ++++++++----- Telegram/SourceFiles/history/history_item.cpp | 8 +- .../history/view/history_view_message.cpp | 5 + .../history/view/history_view_view_button.cpp | 26 ++- .../view/media/history_view_giveaway.cpp | 189 +++++++++++++++--- .../view/media/history_view_giveaway.h | 42 +++- Telegram/SourceFiles/mtproto/scheme/api.tl | 30 +-- Telegram/SourceFiles/ui/chat/chat.style | 1 + 21 files changed, 552 insertions(+), 156 deletions(-) create mode 100644 Telegram/Resources/art/winners.tgs diff --git a/Telegram/Resources/art/winners.tgs b/Telegram/Resources/art/winners.tgs new file mode 100644 index 0000000000000000000000000000000000000000..d1112ecbe732edca653edd932309de28c6393ea6 GIT binary patch literal 14103 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 VK3R8fCVlhA{|7}PTEIU30RV~bMGOD{ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index d2333c7cf..6b6b81afa 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2246,6 +2246,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_prizes_cancelled" = "The channel cancelled the prizes by reversing the payment for them."; "lng_prizes_badge" = "x{amount}"; +"lng_prizes_results_title" = "Winners Selected!"; +"lng_prizes_results_about#one" = "**{count}** winner of the {link} was randomly selected by Telegram."; +"lng_prizes_results_about#other" = "**{count}** winners of the {link} were randomly selected by Telegram."; +"lng_prizes_results_link" = "Giveaway"; +"lng_prizes_results_winners" = "Winners"; +"lng_prizes_results_more#one" = "and {count} more!"; +"lng_prizes_results_more#other" = "and {count} more!"; +"lng_prizes_results_all" = "All winners received gift links in private messages."; +"lng_prizes_results_some" = "Some winners couldn't be selected."; + "lng_gift_link_title" = "Gift Link"; "lng_gift_link_about" = "This link allows you to activate\na **Telegram Premium** subscription."; "lng_gift_link_label_from" = "From"; @@ -3629,6 +3639,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_admin_log_set_background_emoji" = "{from} set channel background emoji to {emoji}"; "lng_admin_log_change_background_emoji" = "{from} changed channel background emoji from {previous} to {emoji}"; "lng_admin_log_removed_background_emoji" = "{from} removed channel background emoji {emoji}"; +"lng_admin_log_change_profile_color" = "{from} changed channel profile color from {previous} to {color}"; +"lng_admin_log_set_profile_background_emoji" = "{from} set channel profile background emoji to {emoji}"; +"lng_admin_log_change_profile_background_emoji" = "{from} changed channel profile background emoji from {previous} to {emoji}"; +"lng_admin_log_removed_profile_background_emoji" = "{from} removed channel profile background emoji {emoji}"; +"lng_admin_log_change_wallpaper" = "{from} changed channel wallpaper"; "lng_admin_log_user_with_username" = "{name} ({mention})"; "lng_admin_log_messages_ttl_set" = "{from} enabled messages auto-delete after {duration}"; "lng_admin_log_messages_ttl_changed" = "{from} changed messages auto-delete period from {previous} to {duration}"; diff --git a/Telegram/Resources/qrc/telegram/telegram.qrc b/Telegram/Resources/qrc/telegram/telegram.qrc index 0e43acb03..57afe3882 100644 --- a/Telegram/Resources/qrc/telegram/telegram.qrc +++ b/Telegram/Resources/qrc/telegram/telegram.qrc @@ -15,6 +15,7 @@ ../../art/slot_2_idle.tgs ../../art/slot_back.tgs ../../art/slot_pull.tgs + ../../art/winners.tgs ../../day-blue.tdesktop-theme ../../night.tdesktop-theme ../../night-green.tdesktop-theme diff --git a/Telegram/SourceFiles/boxes/gift_premium_box.cpp b/Telegram/SourceFiles/boxes/gift_premium_box.cpp index 97cb8a25d..08110dea5 100644 --- a/Telegram/SourceFiles/boxes/gift_premium_box.cpp +++ b/Telegram/SourceFiles/boxes/gift_premium_box.cpp @@ -16,7 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_boosts.h" #include "data/data_changes.h" #include "data/data_channel.h" -#include "data/data_media_types.h" // Data::Giveaway +#include "data/data_media_types.h" // Data::GiveawayStart. #include "data/data_peer_values.h" // Data::PeerPremiumValue. #include "data/data_session.h" #include "data/data_subscription_option.h" @@ -739,8 +739,11 @@ void ResolveGiftCode( void GiveawayInfoBox( not_null box, not_null controller, - Data::Giveaway giveaway, + std::optional start, + std::optional results, Api::GiveawayInfo info) { + Expects(start || results); + using State = Api::GiveawayState; const auto finished = (info.state == State::Finished) || (info.state == State::Refunded); @@ -749,8 +752,10 @@ void GiveawayInfoBox( ? tr::lng_prizes_end_title : tr::lng_prizes_how_title)()); - const auto first = !giveaway.channels.empty() - ? giveaway.channels.front()->name() + const auto first = results + ? results->channel->name() + : !start->channels.empty() + ? start->channels.front()->name() : u"channel"_q; auto text = TextWithEntities(); @@ -767,6 +772,10 @@ void GiveawayInfoBox( text.append("\n\n"); } + const auto quantity = start + ? start->quantity + : (results->winnersCount + results->unclaimedCount); + const auto months = start ? start->months : results->months; text.append((finished ? tr::lng_prizes_end_text : tr::lng_prizes_how_text)( @@ -775,18 +784,21 @@ void GiveawayInfoBox( tr::lng_prizes_admins( tr::now, lt_count, - giveaway.quantity, + quantity, lt_channel, Ui::Text::Bold(first), lt_duration, - TextWithEntities{ GiftDuration(giveaway.months) }, + TextWithEntities{ GiftDuration(months) }, Ui::Text::RichLangValue), Ui::Text::RichLangValue)); - const auto many = (giveaway.channels.size() > 1); + const auto many = start + ? (start->channels.size() > 1) + : (results->additionalPeersCount > 0); const auto count = info.winnersCount ? info.winnersCount - : giveaway.quantity; - auto winners = giveaway.all + : quantity; + const auto all = start ? start->all : results->all; + auto winners = all ? (many ? tr::lng_prizes_winners_all_of_many : tr::lng_prizes_winners_all_of_one)( @@ -808,7 +820,10 @@ void GiveawayInfoBox( Ui::Text::Bold( langDateTime(base::unixtime::parse(info.startDate))), Ui::Text::RichLangValue); - if (!giveaway.additionalPrize.isEmpty()) { + const auto additionalPrize = results + ? results->additionalPrize + : start->additionalPrize; + if (!additionalPrize.isEmpty()) { text.append("\n\n").append(tr::lng_prizes_additional_added( tr::now, lt_count, @@ -816,16 +831,19 @@ void GiveawayInfoBox( lt_channel, Ui::Text::Bold(first), lt_prize, - TextWithEntities{ giveaway.additionalPrize }, + TextWithEntities{ additionalPrize }, Ui::Text::RichLangValue)); } + const auto untilDate = start + ? start->untilDate + : results->untilDate; text.append("\n\n").append((finished ? tr::lng_prizes_end_when_finish : tr::lng_prizes_how_when_finish)( tr::now, lt_date, Ui::Text::Bold(langDayOfMonthFull( - base::unixtime::parse(giveaway.untilDate).date())), + base::unixtime::parse(untilDate).date())), lt_winners, winners, Ui::Text::RichLangValue)); @@ -876,7 +894,7 @@ void GiveawayInfoBox( Ui::Text::Bold(first), lt_date, Ui::Text::Bold(langDayOfMonthFull( - base::unixtime::parse(giveaway.untilDate).date())), + base::unixtime::parse(untilDate).date())), Ui::Text::RichLangValue)); } } @@ -920,14 +938,15 @@ void ResolveGiveawayInfo( not_null controller, not_null peer, MsgId messageId, - Data::Giveaway giveaway) { + std::optional start, + std::optional results) { const auto show = [=](Api::GiveawayInfo info) { if (!info) { controller->showToast( tr::lng_confirm_phone_link_invalid(tr::now)); } else { controller->uiShow()->showBox( - Box(GiveawayInfoBox, controller, giveaway, info)); + Box(GiveawayInfoBox, controller, start, results, info)); } }; controller->session().api().premium().resolveGiveawayInfo( diff --git a/Telegram/SourceFiles/boxes/gift_premium_box.h b/Telegram/SourceFiles/boxes/gift_premium_box.h index 1891ce5ff..1ea8d21d9 100644 --- a/Telegram/SourceFiles/boxes/gift_premium_box.h +++ b/Telegram/SourceFiles/boxes/gift_premium_box.h @@ -16,7 +16,8 @@ struct GiftCode; } // namespace Api namespace Data { -struct Giveaway; +struct GiveawayStart; +struct GiveawayResults; } // namespace Data namespace Ui { @@ -62,4 +63,5 @@ void ResolveGiveawayInfo( not_null controller, not_null peer, MsgId messageId, - Data::Giveaway giveaway); + std::optional start, + std::optional results); diff --git a/Telegram/SourceFiles/chat_helpers/stickers_dice_pack.cpp b/Telegram/SourceFiles/chat_helpers/stickers_dice_pack.cpp index dcebbfdc4..69aaddee1 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_dice_pack.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_dice_pack.cpp @@ -26,6 +26,7 @@ const QString DicePacks::kDartString = QString::fromUtf8("\xF0\x9F\x8E\xAF"); const QString DicePacks::kSlotString = QString::fromUtf8("\xF0\x9F\x8E\xB0"); const QString DicePacks::kFballString = QString::fromUtf8("\xE2\x9A\xBD"); const QString DicePacks::kBballString = QString::fromUtf8("\xF0\x9F\x8F\x80"); +const QString DicePacks::kPartyPopper = QString::fromUtf8("\xf0\x9f\x8e\x89"); DicePack::DicePack(not_null session, const QString &emoji) : _session(session) @@ -35,7 +36,7 @@ DicePack::DicePack(not_null session, const QString &emoji) DicePack::~DicePack() = default; DocumentData *DicePack::lookup(int value) { - if (!_requestId) { + if (!_requestId && _emoji != DicePacks::kPartyPopper) { load(); } tryGenerateLocalZero(); @@ -117,6 +118,8 @@ void DicePack::tryGenerateLocalZero() { generateLocal(8, u"slot_0_idle"_q); generateLocal(14, u"slot_1_idle"_q); generateLocal(20, u"slot_2_idle"_q); + } else if (_emoji == DicePacks::kPartyPopper) { + generateLocal(0, u"winners"_q); } } diff --git a/Telegram/SourceFiles/chat_helpers/stickers_dice_pack.h b/Telegram/SourceFiles/chat_helpers/stickers_dice_pack.h index 53564363a..3a1c49f46 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_dice_pack.h +++ b/Telegram/SourceFiles/chat_helpers/stickers_dice_pack.h @@ -44,6 +44,7 @@ public: static const QString kSlotString; static const QString kFballString; static const QString kBballString; + static const QString kPartyPopper; [[nodiscard]] static bool IsSlot(const QString &emoji) { return (emoji == kSlotString); diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index e14f0712e..f716984ad 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -61,6 +61,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_file_origin.h" #include "data/data_stories.h" #include "data/data_story.h" +#include "data/data_user.h" #include "main/main_session.h" #include "main/main_session_settings.h" #include "core/application.h" @@ -363,10 +364,10 @@ Call ComputeCallData(const MTPDmessageActionPhoneCall &call) { return result; } -Giveaway ComputeGiveawayData( +GiveawayStart ComputeGiveawayStartData( not_null item, const MTPDmessageMediaGiveaway &data) { - auto result = Giveaway{ + auto result = GiveawayStart{ .untilDate = data.vuntil_date().v, .quantity = data.vquantity().v, .months = data.vmonths().v, @@ -389,6 +390,32 @@ Giveaway ComputeGiveawayData( return result; } +GiveawayResults ComputeGiveawayResultsData( + not_null item, + const MTPDmessageMediaGiveawayResults &data) { + const auto additional = data.vadditional_peers_count(); + auto result = GiveawayResults{ + .channel = item->history()->owner().channel(data.vchannel_id()), + .untilDate = data.vuntil_date().v, + .launchId = data.vlaunch_msg_id().v, + .additionalPeersCount = additional.value_or_empty(), + .winnersCount = data.vwinners_count().v, + .unclaimedCount = data.vunclaimed_count().v, + .months = data.vmonths().v, + .refunded = data.is_refunded(), + .all = !data.is_only_new_subscribers(), + }; + result.winners.reserve(data.vwinners().v.size()); + const auto owner = &item->history()->owner(); + for (const auto &id : data.vwinners().v) { + result.winners.push_back(owner->user(UserId(id))); + } + if (const auto additional = data.vprize_description()) { + result.additionalPrize = qs(*additional); + } + return result; +} + Media::Media(not_null parent) : _parent(parent) { } @@ -456,7 +483,11 @@ bool Media::storyMention() const { return false; } -const Giveaway *Media::giveaway() const { +const GiveawayStart *Media::giveawayStart() const { + return nullptr; +} + +const GiveawayResults *Media::giveawayResults() const { return nullptr; } @@ -2241,53 +2272,103 @@ std::unique_ptr MediaStory::createView( } } -MediaGiveaway::MediaGiveaway( +MediaGiveawayStart::MediaGiveawayStart( not_null parent, - const Giveaway &data) + const GiveawayStart &data) : Media(parent) -, _giveaway(data) { +, _data(data) { parent->history()->session().giftBoxStickersPacks().load(); } -std::unique_ptr MediaGiveaway::clone(not_null parent) { - return std::make_unique(parent, _giveaway); +std::unique_ptr MediaGiveawayStart::clone( + not_null parent) { + return std::make_unique(parent, _data); } -const Giveaway *MediaGiveaway::giveaway() const { - return &_giveaway; +const GiveawayStart *MediaGiveawayStart::giveawayStart() const { + return &_data; } -TextWithEntities MediaGiveaway::notificationText() const { +TextWithEntities MediaGiveawayStart::notificationText() const { return { - .text = tr::lng_prizes_title(tr::now, lt_count, _giveaway.quantity), + .text = tr::lng_prizes_title(tr::now, lt_count, _data.quantity), }; } -QString MediaGiveaway::pinnedTextSubstring() const { +QString MediaGiveawayStart::pinnedTextSubstring() const { return QString::fromUtf8("\xC2\xAB") + notificationText().text + QString::fromUtf8("\xC2\xBB"); } -TextForMimeData MediaGiveaway::clipboardText() const { +TextForMimeData MediaGiveawayStart::clipboardText() const { return TextForMimeData(); } -bool MediaGiveaway::updateInlineResultMedia(const MTPMessageMedia &media) { +bool MediaGiveawayStart::updateInlineResultMedia(const MTPMessageMedia &media) { return true; } -bool MediaGiveaway::updateSentMedia(const MTPMessageMedia &media) { +bool MediaGiveawayStart::updateSentMedia(const MTPMessageMedia &media) { return true; } -std::unique_ptr MediaGiveaway::createView( +std::unique_ptr MediaGiveawayStart::createView( not_null message, not_null realParent, HistoryView::Element *replacing) { return std::make_unique( message, - HistoryView::GenerateGiveawayStart(message, &_giveaway)); + HistoryView::GenerateGiveawayStart(message, &_data)); +} + +MediaGiveawayResults::MediaGiveawayResults( + not_null parent, + const GiveawayResults &data) +: Media(parent) +, _data(data) { +} + +std::unique_ptr MediaGiveawayResults::clone( + not_null parent) { + return std::make_unique(parent, _data); +} + +const GiveawayResults *MediaGiveawayResults::giveawayResults() const { + return &_data; +} + +TextWithEntities MediaGiveawayResults::notificationText() const { + return { + .text = tr::lng_prizes_results_title(tr::now), + }; +} + +QString MediaGiveawayResults::pinnedTextSubstring() const { + return QString::fromUtf8("\xC2\xAB") + + notificationText().text + + QString::fromUtf8("\xC2\xBB"); +} + +TextForMimeData MediaGiveawayResults::clipboardText() const { + return TextForMimeData(); +} + +bool MediaGiveawayResults::updateInlineResultMedia(const MTPMessageMedia &media) { + return true; +} + +bool MediaGiveawayResults::updateSentMedia(const MTPMessageMedia &media) { + return true; +} + +std::unique_ptr MediaGiveawayResults::createView( + not_null message, + not_null realParent, + HistoryView::Element *replacing) { + return std::make_unique( + message, + HistoryView::GenerateGiveawayResults(message, &_data)); } } // namespace Data diff --git a/Telegram/SourceFiles/data/data_media_types.h b/Telegram/SourceFiles/data/data_media_types.h index 667b84ffc..2a4e18e90 100644 --- a/Telegram/SourceFiles/data/data_media_types.h +++ b/Telegram/SourceFiles/data/data_media_types.h @@ -90,7 +90,7 @@ struct Invoice { bool isTest = false; }; -struct Giveaway { +struct GiveawayStart { std::vector> channels; std::vector countries; QString additionalPrize; @@ -100,6 +100,20 @@ struct Giveaway { bool all = false; }; +struct GiveawayResults { + not_null channel; + std::vector> winners; + QString additionalPrize; + TimeId untilDate = 0; + MsgId launchId = 0; + int additionalPeersCount = 0; + int winnersCount = 0; + int unclaimedCount = 0; + int months = 0; + bool refunded = false; + bool all = false; +}; + struct GiftCode { QString slug; ChannelData *channel = nullptr; @@ -136,7 +150,8 @@ public: virtual FullStoryId storyId() const; virtual bool storyExpired(bool revalidate = false); virtual bool storyMention() const; - virtual const Giveaway *giveaway() const; + virtual const GiveawayStart *giveawayStart() const; + virtual const GiveawayResults *giveawayResults() const; virtual bool uploading() const; virtual Storage::SharedMediaTypesMask sharedMediaTypes() const; @@ -635,15 +650,15 @@ private: }; -class MediaGiveaway final : public Media { +class MediaGiveawayStart final : public Media { public: - MediaGiveaway( + MediaGiveawayStart( not_null parent, - const Giveaway &data); + const GiveawayStart &data); std::unique_ptr clone(not_null parent) override; - const Giveaway *giveaway() const override; + const GiveawayStart *giveawayStart() const override; TextWithEntities notificationText() const override; QString pinnedTextSubstring() const override; @@ -657,7 +672,33 @@ public: HistoryView::Element *replacing = nullptr) override; private: - Giveaway _giveaway; + GiveawayStart _data; + +}; + +class MediaGiveawayResults final : public Media { +public: + MediaGiveawayResults( + not_null parent, + const GiveawayResults &data); + + std::unique_ptr clone(not_null parent) override; + + const GiveawayResults *giveawayResults() const override; + + TextWithEntities notificationText() const override; + QString pinnedTextSubstring() const override; + TextForMimeData clipboardText() const override; + + bool updateInlineResultMedia(const MTPMessageMedia &media) override; + bool updateSentMedia(const MTPMessageMedia &media) override; + std::unique_ptr createView( + not_null message, + not_null realParent, + HistoryView::Element *replacing = nullptr) override; + +private: + GiveawayResults _data; }; @@ -671,8 +712,12 @@ private: [[nodiscard]] Call ComputeCallData(const MTPDmessageActionPhoneCall &call); -[[nodiscard]] Giveaway ComputeGiveawayData( +[[nodiscard]] GiveawayStart ComputeGiveawayStartData( not_null item, const MTPDmessageMediaGiveaway &data); +[[nodiscard]] GiveawayResults ComputeGiveawayResultsData( + not_null item, + const MTPDmessageMediaGiveawayResults &data); + } // namespace Data diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp index 83e2c10c9..d92b99f94 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.cpp +++ b/Telegram/SourceFiles/export/data/export_data_types.cpp @@ -681,8 +681,8 @@ Poll ParsePoll(const MTPDmessageMediaPoll &data) { return result; } -Giveaway ParseGiveaway(const MTPDmessageMediaGiveaway &data) { - auto result = Giveaway{ +GiveawayStart ParseGiveaway(const MTPDmessageMediaGiveaway &data) { + auto result = GiveawayStart{ .untilDate = data.vuntil_date().v, .quantity = data.vquantity().v, .months = data.vmonths().v, diff --git a/Telegram/SourceFiles/export/data/export_data_types.h b/Telegram/SourceFiles/export/data/export_data_types.h index 272c94f17..0dbb419bc 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.h +++ b/Telegram/SourceFiles/export/data/export_data_types.h @@ -197,7 +197,7 @@ struct Poll { bool closed = false; }; -struct Giveaway { +struct GiveawayStart { std::vector channels; TimeId untilDate = 0; int quantity = 0; @@ -336,7 +336,7 @@ struct Media { Game, Invoice, Poll, - Giveaway, + GiveawayStart, UnsupportedMedia> content; TimeId ttl = 0; diff --git a/Telegram/SourceFiles/export/output/export_output_html.cpp b/Telegram/SourceFiles/export/output/export_output_html.cpp index 624da2dda..3f0b03eaf 100644 --- a/Telegram/SourceFiles/export/output/export_output_html.cpp +++ b/Telegram/SourceFiles/export/output/export_output_html.cpp @@ -613,7 +613,7 @@ private: [[nodiscard]] QByteArray pushPoll(const Data::Poll &data); [[nodiscard]] QByteArray pushGiveaway( const PeersMap &peers, - const Data::Giveaway &data); + const Data::GiveawayStart &data); File _file; QByteArray _composedStart; @@ -1501,7 +1501,7 @@ QByteArray HtmlWriter::Wrap::pushMedia( return pushPhotoMedia(*photo, basePath); } else if (const auto poll = std::get_if(&content)) { return pushPoll(*poll); - } else if (const auto giveaway = std::get_if(&content)) { + } else if (const auto giveaway = std::get_if(&content)) { return pushGiveaway(peers, *giveaway); } Assert(v::is_null(content)); @@ -1826,7 +1826,7 @@ QByteArray HtmlWriter::Wrap::pushPoll(const Data::Poll &data) { QByteArray HtmlWriter::Wrap::pushGiveaway( const PeersMap &peers, - const Data::Giveaway &data) { + const Data::GiveawayStart &data) { auto result = pushDiv("media_wrap clearfix"); result.append(pushDiv("media_giveaway")); @@ -2028,7 +2028,7 @@ MediaData HtmlWriter::Wrap::prepareMediaData( result.description = data.description; result.status = Data::FormatMoneyAmount(data.amount, data.currency); }, [](const Poll &data) { - }, [](const Giveaway &data) { + }, [](const GiveawayStart &data) { }, [](const UnsupportedMedia &data) { Unexpected("Unsupported message."); }, [](v::null_t) {}); diff --git a/Telegram/SourceFiles/export/output/export_output_json.cpp b/Telegram/SourceFiles/export/output/export_output_json.cpp index 8439093a8..a830aceec 100644 --- a/Telegram/SourceFiles/export/output/export_output_json.cpp +++ b/Telegram/SourceFiles/export/output/export_output_json.cpp @@ -753,7 +753,7 @@ QByteArray SerializeMessage( { "total_voters", NumberToString(data.totalVotes) }, { "answers", serialized } })); - }, [&](const Giveaway &data) { + }, [&](const GiveawayStart &data) { context.nesting.push_back(Context::kObject); const auto channels = ranges::views::all( data.channels diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp index df8819d10..fcbe1cd6c 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp @@ -767,8 +767,9 @@ void GenerateItems( using LogDeleteTopic = MTPDchannelAdminLogEventActionDeleteTopic; using LogPinTopic = MTPDchannelAdminLogEventActionPinTopic; using LogToggleAntiSpam = MTPDchannelAdminLogEventActionToggleAntiSpam; - using LogChangeColor = MTPDchannelAdminLogEventActionChangeColor; - using LogChangeBackgroundEmoji = MTPDchannelAdminLogEventActionChangeBackgroundEmoji; + using LogChangePeerColor = MTPDchannelAdminLogEventActionChangePeerColor; + using LogChangeProfilePeerColor = MTPDchannelAdminLogEventActionChangeProfilePeerColor; + using LogChangeWallpaper = MTPDchannelAdminLogEventActionChangeWallpaper; const auto session = &history->session(); const auto id = event.vid().v; @@ -1817,52 +1818,94 @@ void GenerateItems( addSimpleServiceMessage(text); }; - const auto createChangeColor = [&](const LogChangeColor &data) { - const auto text = tr::lng_admin_log_change_color( - tr::now, - lt_from, - fromLinkText, - lt_previous, - { '#' + QString::number(data.vprev_value().v + 1) }, - lt_color, - { '#' + QString::number(data.vnew_value().v + 1) }, - Ui::Text::WithEntities); - addSimpleServiceMessage(text); - }; - - const auto createChangeBackgroundEmoji = [&](const LogChangeBackgroundEmoji &data) { - const auto was = data.vprev_value().v; - const auto now = data.vnew_value().v; - const auto text = !was - ? tr::lng_admin_log_set_background_emoji( - tr::now, - lt_from, - fromLinkText, - lt_emoji, - Ui::Text::SingleCustomEmoji( - Data::SerializeCustomEmojiId(now)), - Ui::Text::WithEntities) - : !now - ? tr::lng_admin_log_removed_background_emoji( - tr::now, - lt_from, - fromLinkText, - lt_emoji, - Ui::Text::SingleCustomEmoji( - Data::SerializeCustomEmojiId(was)), - Ui::Text::WithEntities) - : tr::lng_admin_log_change_background_emoji( + const auto createColorChange = [&]( + const MTPPeerColor &was, + const MTPPeerColor &now, + const auto &colorPhrase, + const auto &setEmoji, + const auto &removeEmoji, + const auto &changeEmoji) { + const auto prevColor = was.data().vcolor(); + const auto nextColor = now.data().vcolor(); + if (prevColor != nextColor) { + const auto wrap = [&](tl::conditional value) { + return value + ? value->v + : Data::DecideColorIndex(history->peer->id); + }; + const auto text = colorPhrase( tr::now, lt_from, fromLinkText, lt_previous, - Ui::Text::SingleCustomEmoji( - Data::SerializeCustomEmojiId(was)), - lt_emoji, - Ui::Text::SingleCustomEmoji( - Data::SerializeCustomEmojiId(now)), + { '#' + QString::number(wrap(prevColor) + 1) }, + lt_color, + { '#' + QString::number(wrap(nextColor) + 1) }, Ui::Text::WithEntities); - addSimpleServiceMessage(text); + addSimpleServiceMessage(text); + } + const auto prevEmoji = was.data().vbackground_emoji_id().value_or_empty(); + const auto nextEmoji = now.data().vbackground_emoji_id().value_or_empty(); + if (prevEmoji != nextEmoji) { + const auto text = !prevEmoji + ? setEmoji( + tr::now, + lt_from, + fromLinkText, + lt_emoji, + Ui::Text::SingleCustomEmoji( + Data::SerializeCustomEmojiId(nextEmoji)), + Ui::Text::WithEntities) + : !nextEmoji + ? removeEmoji( + tr::now, + lt_from, + fromLinkText, + lt_emoji, + Ui::Text::SingleCustomEmoji( + Data::SerializeCustomEmojiId(prevEmoji)), + Ui::Text::WithEntities) + : changeEmoji( + tr::now, + lt_from, + fromLinkText, + lt_previous, + Ui::Text::SingleCustomEmoji( + Data::SerializeCustomEmojiId(prevEmoji)), + lt_emoji, + Ui::Text::SingleCustomEmoji( + Data::SerializeCustomEmojiId(nextEmoji)), + Ui::Text::WithEntities); + addSimpleServiceMessage(text); + } + }; + + const auto createChangePeerColor = [&](const LogChangePeerColor &data) { + createColorChange( + data.vprev_value(), + data.vnew_value(), + tr::lng_admin_log_change_color, + tr::lng_admin_log_set_background_emoji, + tr::lng_admin_log_removed_background_emoji, + tr::lng_admin_log_change_background_emoji); + }; + + const auto createChangeProfilePeerColor = [&](const LogChangeProfilePeerColor &data) { + createColorChange( + data.vprev_value(), + data.vnew_value(), + tr::lng_admin_log_change_profile_color, + tr::lng_admin_log_set_profile_background_emoji, + tr::lng_admin_log_removed_profile_background_emoji, + tr::lng_admin_log_change_profile_background_emoji); + }; + + const auto createChangeWallpaper = [&](const LogChangeWallpaper &data) { + addSimpleServiceMessage(tr::lng_admin_log_change_wallpaper( + tr::now, + lt_from, + fromLinkText, + Ui::Text::WithEntities)); }; action.match( @@ -1909,8 +1952,9 @@ void GenerateItems( createDeleteTopic, createPinTopic, createToggleAntiSpam, - createChangeColor, - createChangeBackgroundEmoji); + createChangePeerColor, + createChangeProfilePeerColor, + createChangeWallpaper); } } // namespace AdminLog diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 2861bf26a..b078c358e 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -311,13 +311,13 @@ std::unique_ptr HistoryItem::CreateMedia( media.vid().v, }, media.is_via_mention()); }, [&](const MTPDmessageMediaGiveaway &media) -> Result { - return std::make_unique( + return std::make_unique( item, - Data::ComputeGiveawayData(item, media)); + Data::ComputeGiveawayStartData(item, media)); }, [&](const MTPDmessageMediaGiveawayResults &media) -> Result { - return nullptr;/* std::make_unique( + return std::make_unique( item, - Data::ComputeGiveawayData(item, media));*/ + Data::ComputeGiveawayResultsData(item, media)); }, [](const MTPDmessageMediaEmpty &) -> Result { return nullptr; }, [](const MTPDmessageMediaUnsupported &) -> Result { diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 09e6851ab..35546b013 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -403,6 +403,11 @@ Message::Message( , _bottomInfo( &data->history()->owner().reactions(), BottomInfoDataFromMessage(this)) { + if (const auto media = data->media()) { + if (media->giveawayResults()) { + _hideReply = 1; + } + } initLogEntryOriginal(); initPsa(); refreshReactions(); diff --git a/Telegram/SourceFiles/history/view/history_view_view_button.cpp b/Telegram/SourceFiles/history/view/history_view_view_button.cpp index 0780e4ddf..52e9847ae 100644 --- a/Telegram/SourceFiles/history/view/history_view_view_button.cpp +++ b/Telegram/SourceFiles/history/view/history_view_view_button.cpp @@ -23,14 +23,21 @@ namespace { [[nodiscard]] ClickHandlerPtr MakeMediaButtonClickHandler( not_null media) { - const auto giveaway = media->giveaway(); - Assert(giveaway != nullptr); + const auto start = media->giveawayStart(); + const auto results = media->giveawayResults(); + Assert(start || results); + const auto peer = media->parent()->history()->peer; const auto messageId = media->parent()->id; if (media->parent()->isSending() || media->parent()->hasFailed()) { return nullptr; } - const auto info = *giveaway; + const auto maybeStart = start + ? *start + : std::optional(); + const auto maybeResults = results + ? *results + : std::optional(); return std::make_shared([=]( ClickContext context) { const auto my = context.other.value(); @@ -38,13 +45,18 @@ namespace { if (!controller) { return; } - ResolveGiveawayInfo(controller, peer, messageId, info); + ResolveGiveawayInfo( + controller, + peer, + messageId, + maybeStart, + maybeResults); }); } [[nodiscard]] QString MakeMediaButtonText(not_null media) { - const auto giveaway = media->giveaway(); - Assert(giveaway != nullptr); + Expects(media->giveawayStart() || media->giveawayResults()); + return Ui::Text::Upper(tr::lng_prizes_how_works(tr::now)); } @@ -72,7 +84,7 @@ struct ViewButton::Inner { }; bool ViewButton::MediaHasViewButton(not_null media) { - return (media->giveaway() != nullptr); + return media->giveawayStart() || media->giveawayResults(); } ViewButton::Inner::Inner( diff --git a/Telegram/SourceFiles/history/view/media/history_view_giveaway.cpp b/Telegram/SourceFiles/history/view/media/history_view_giveaway.cpp index 0045f962b..a4d59ad04 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_giveaway.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_giveaway.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unixtime.h" #include "boxes/gift_premium_box.h" #include "chat_helpers/stickers_gift_box_pack.h" +#include "chat_helpers/stickers_dice_pack.h" #include "countries/countries_instance.h" #include "data/data_channel.h" #include "data/data_document.h" @@ -19,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/history_item.h" #include "history/history_item_components.h" +#include "history/history_item_helpers.h" #include "history/view/history_view_element.h" #include "history/view/history_view_cursor_state.h" #include "lang/lang_keys.h" @@ -54,7 +56,8 @@ constexpr auto kAdditionalPrizesWithLineOpacity = 0.6; TextState MediaInBubble::Part::textState( QPoint point, - StateRequest request) const { + StateRequest request, + int outerWidth) const { return {}; } @@ -127,7 +130,8 @@ TextState MediaInBubble::textState( StateRequest request) const { auto result = TextState(_parent); - if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) { + const auto outer = width(); + if (outer < st::msgPadding.left() + st::msgPadding.right() + 1) { return result; } @@ -135,7 +139,7 @@ TextState MediaInBubble::textState( const auto raw = entry.object.get(); const auto height = raw->height(); if (point.y() >= 0 && point.y() < height) { - const auto part = raw->textState(point, request); + const auto part = raw->textState(point, request, outer); result.link = part.link; return result; } @@ -186,10 +190,14 @@ QMargins MediaInBubble::inBubblePadding() const { TextMediaInBubblePart::TextMediaInBubblePart( TextWithEntities text, - QMargins margins) + QMargins margins, + const base::flat_map &links) : _text(st::msgMinWidth) , _margins(margins) { _text.setMarkedText(st::defaultTextStyle, text); + for (const auto &[index, link] : links) { + _text.setLink(index, link); + } } void TextMediaInBubblePart::draw( @@ -207,6 +215,18 @@ void TextMediaInBubblePart::draw( }); } +TextState TextMediaInBubblePart::textState( + QPoint point, + StateRequest request, + int outerWidth) const { + point -= QPoint{ (outerWidth - width()) / 2, _margins.top() }; + auto result = TextState(); + auto forText = request.forText(); + forText.align = style::al_top; + result.link = _text.getState(point, width(), forText).link; + return result; +} + QSize TextMediaInBubblePart::countOptimalSize() { return { _margins.left() + _text.maxWidth() + _margins.right(), @@ -278,7 +298,7 @@ QSize TextDelimeterPart::countCurrentSize(int newWidth) { StickerWithBadgePart::StickerWithBadgePart( not_null parent, - Fn lookup, + Fn lookup, QString badge) : _parent(parent) , _lookup(std::move(lookup)) @@ -293,7 +313,7 @@ void StickerWithBadgePart::draw( const auto stickerSize = st::msgServiceGiftBoxStickerSize; const auto sticker = QRect( (outerWidth - stickerSize.width()) / 2, - st::chatGiveawayStickerTop, + st::chatGiveawayStickerTop + _skipTop, stickerSize.width(), stickerSize.height()); @@ -327,12 +347,14 @@ QSize StickerWithBadgePart::countCurrentSize(int newWidth) { void StickerWithBadgePart::ensureCreated() const { if (_sticker) { return; - } else if (const auto document = _lookup()) { + } else if (const auto data = _lookup()) { + const auto document = data.sticker; if (const auto sticker = document->sticker()) { const auto skipPremiumEffect = false; + _skipTop = data.skipTop; _sticker.emplace(_parent, document, skipPremiumEffect, _parent); _sticker->setDiceIndex(sticker->alt, 1); - _sticker->setGiftBoxSticker(true); + _sticker->setGiftBoxSticker(data.isGiftBoxSticker); _sticker->initSize(); } } @@ -427,6 +449,7 @@ PeerBubbleListPart::PeerBubbleListPart( kDefaultTextOptions, st::msgMinWidth), .thumbnail = Dialogs::Stories::MakeUserpicThumbnail(peer), + .link = peer->openLink(), .colorIndex = peer->colorIndex(), }); } @@ -528,9 +551,43 @@ int PeerBubbleListPart::layout(int x, int y, int available) { return y + size + skip; } +TextState PeerBubbleListPart::textState( + QPoint point, + StateRequest request, + int outerWidth) const { + auto result = TextState(_parent); + for (const auto &peer : _peers) { + if (peer.geometry.contains(point)) { + result.link = peer.link; + _lastPoint = point; + break; + } + } + return result; +} + void PeerBubbleListPart::clickHandlerPressedChanged( - const ClickHandlerPtr &p, - bool pressed) { + const ClickHandlerPtr &p, + bool pressed) { + for (auto &peer : _peers) { + if (peer.link != p) { + continue; + } + if (pressed) { + if (!peer.ripple) { + peer.ripple = std::make_unique( + st::defaultRippleAnimation, + Ui::RippleAnimation::RoundRectMask( + peer.geometry.size(), + peer.geometry.height() / 2), + [=] { _parent->repaint(); }); + } + peer.ripple->add(_lastPoint - peer.geometry.topLeft()); + } else if (peer.ripple) { + peer.ripple->lastStop(); + } + break; + } } bool PeerBubbleListPart::hasHeavyPart() { @@ -579,18 +636,19 @@ QSize PeerBubbleListPart::countCurrentSize(int newWidth) { auto GenerateGiveawayStart( not_null parent, - not_null giveaway) + not_null data) -> Fn)>)> { return [=](Fn)> push) { - const auto months = giveaway->months; - const auto quantity = giveaway->quantity; + const auto months = data->months; + const auto quantity = data->quantity; + using Data = StickerWithBadgePart::Data; const auto sticker = [=] { const auto &session = parent->history()->session(); auto &packs = session.giftBoxStickersPacks(); - return packs.lookup(months); + return Data{ packs.lookup(months), 0, true }; }; - push(std::make_unique( + push(std::make_unique( parent, sticker, tr::lng_prizes_badge( @@ -598,27 +656,31 @@ auto GenerateGiveawayStart( lt_amount, QString::number(quantity)))); - auto pushText = [&](TextWithEntities text, QMargins margins = {}) { - push(std::make_unique( + auto pushText = [&]( + TextWithEntities text, + QMargins margins = {}, + const base::flat_map &links = {}) { + push(std::make_unique( std::move(text), - margins)); + margins, + links)); }; pushText( Ui::Text::Bold( tr::lng_prizes_title(tr::now, lt_count, quantity)), st::chatGiveawayPrizesTitleMargin); - if (!giveaway->additionalPrize.isEmpty()) { + if (!data->additionalPrize.isEmpty()) { pushText( tr::lng_prizes_additional( tr::now, lt_count, quantity, lt_prize, - TextWithEntities{ giveaway->additionalPrize }, + TextWithEntities{ data->additionalPrize }, Ui::Text::RichLangValue), st::chatGiveawayPrizesMargin); - push(std::make_unique( + push(std::make_unique( tr::lng_prizes_additional_with(tr::now), st::chatGiveawayPrizesWithPadding)); } @@ -636,26 +698,26 @@ auto GenerateGiveawayStart( Ui::Text::Bold(tr::lng_prizes_participants(tr::now)), st::chatGiveawayPrizesTitleMargin); - pushText({ (giveaway->all + pushText({ (data->all ? tr::lng_prizes_participants_all : tr::lng_prizes_participants_new)( tr::now, lt_count, - giveaway->channels.size()), + data->channels.size()), }, st::chatGiveawayParticipantsMargin); auto list = ranges::views::all( - giveaway->channels + data->channels ) | ranges::views::transform([](not_null channel) { return not_null(channel); }) | ranges::to_vector; - push(std::make_unique( + push(std::make_unique( parent, std::move(list))); const auto &instance = Countries::Instance(); auto countries = QStringList(); - for (const auto &country : giveaway->countries) { + for (const auto &country : data->countries) { const auto name = instance.countryNameByISO2(country); const auto flag = instance.flagEmojiByISO2(country); countries.push_back(flag + QChar(0xA0) + name); @@ -683,7 +745,80 @@ auto GenerateGiveawayStart( ? st::chatGiveawayNoCountriesTitleMargin : st::chatGiveawayPrizesMargin)); pushText({ - langDateTime(base::unixtime::parse(giveaway->untilDate)), + langDateTime(base::unixtime::parse(data->untilDate)), + }, st::chatGiveawayEndDateMargin); + }; +} + +auto GenerateGiveawayResults( + not_null parent, + not_null data) +-> Fn)>)> { + return [=](Fn)> push) { + const auto months = data->months; + const auto quantity = data->winnersCount; + + using Data = StickerWithBadgePart::Data; + const auto sticker = [=] { + const auto &session = parent->history()->session(); + auto &packs = session.diceStickersPacks(); + const auto &emoji = Stickers::DicePacks::kPartyPopper; + const auto skip = st::chatGiveawayWinnersTopSkip; + return Data{ packs.lookup(emoji, 0), skip }; + }; + push(std::make_unique( + parent, + sticker, + tr::lng_prizes_badge( + tr::now, + lt_amount, + QString::number(quantity)))); + + auto pushText = [&]( + TextWithEntities text, + QMargins margins = {}, + const base::flat_map &links = {}) { + push(std::make_unique( + std::move(text), + margins, + links)); + }; + pushText( + Ui::Text::Bold( + tr::lng_prizes_results_title(tr::now)), + st::chatGiveawayPrizesTitleMargin); + const auto showGiveawayHandler = JumpToMessageClickHandler( + data->channel, + data->launchId, + parent->data()->fullId()); + pushText( + tr::lng_prizes_results_about( + tr::now, + lt_count, + quantity, + lt_link, + Ui::Text::Link(tr::lng_prizes_results_link(tr::now)), + Ui::Text::RichLangValue), + st::chatGiveawayPrizesMargin, + { { 1, showGiveawayHandler } }); + pushText( + Ui::Text::Bold(tr::lng_prizes_results_winners(tr::now)), + st::chatGiveawayPrizesTitleMargin); + + push(std::make_unique( + parent, + data->winners)); + if (data->winnersCount > data->winners.size()) { + pushText( + Ui::Text::Bold(tr::lng_prizes_results_more( + tr::now, + lt_count, + data->winnersCount - data->winners.size())), + st::chatGiveawayNoCountriesTitleMargin); + } + pushText({ data->unclaimedCount + ? tr::lng_prizes_results_some(tr::now) + : tr::lng_prizes_results_all(tr::now) }, st::chatGiveawayEndDateMargin); }; } diff --git a/Telegram/SourceFiles/history/view/media/history_view_giveaway.h b/Telegram/SourceFiles/history/view/media/history_view_giveaway.h index 6f5afb8f7..a6e3faada 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_giveaway.h +++ b/Telegram/SourceFiles/history/view/media/history_view_giveaway.h @@ -11,7 +11,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/media/history_view_sticker.h" namespace Data { -struct Giveaway; +struct GiveawayStart; +struct GiveawayResults; } // namespace Data namespace Dialogs::Stories { @@ -36,7 +37,8 @@ public: int outerWidth) const = 0; [[nodiscard]] virtual TextState textState( QPoint point, - StateRequest request) const; + StateRequest request, + int outerWidth) const; virtual void clickHandlerPressedChanged( const ClickHandlerPtr &p, bool pressed); @@ -82,7 +84,6 @@ public: private: struct Entry { std::unique_ptr object; - int top = 0; }; QSize countOptimalSize() override; @@ -96,12 +97,19 @@ private: class TextMediaInBubblePart final : public MediaInBubble::Part { public: - TextMediaInBubblePart(TextWithEntities text, QMargins margins); + TextMediaInBubblePart( + TextWithEntities text, + QMargins margins, + const base::flat_map &links = {}); void draw( Painter &p, const PaintContext &context, int outerWidth) const override; + TextState textState( + QPoint point, + StateRequest request, + int outerWidth) const override; QSize countOptimalSize() override; QSize countCurrentSize(int newWidth) override; @@ -132,9 +140,18 @@ private: class StickerWithBadgePart final : public MediaInBubble::Part { public: + struct Data { + DocumentData *sticker = nullptr; + int skipTop = 0; + bool isGiftBoxSticker = false; + + explicit operator bool() const { + return sticker != nullptr; + } + }; StickerWithBadgePart( not_null parent, - Fn lookup, + Fn lookup, QString badge); void draw( @@ -153,8 +170,9 @@ private: void paintBadge(Painter &p, const PaintContext &context) const; const not_null _parent; - Fn _lookup; + Fn _lookup; QString _badgeText; + mutable int _skipTop = 0; mutable std::optional _sticker; mutable QColor _badgeFg; mutable QColor _badgeBorder; @@ -173,6 +191,10 @@ public: Painter &p, const PaintContext &context, int outerWidth) const override; + TextState textState( + QPoint point, + StateRequest request, + int outerWidth) const override; void clickHandlerPressedChanged( const ClickHandlerPtr &p, bool pressed) override; @@ -199,13 +221,19 @@ private: const not_null _parent; std::vector _peers; + mutable QPoint _lastPoint; mutable bool _subscribed = false; }; [[nodiscard]] auto GenerateGiveawayStart( not_null parent, - not_null giveaway) + not_null data) +-> Fn)>)>; + +[[nodiscard]] auto GenerateGiveawayResults( + not_null parent, + not_null data) -> Fn)>)>; } // namespace HistoryView diff --git a/Telegram/SourceFiles/mtproto/scheme/api.tl b/Telegram/SourceFiles/mtproto/scheme/api.tl index 7afd1cec3..6946a6e56 100644 --- a/Telegram/SourceFiles/mtproto/scheme/api.tl +++ b/Telegram/SourceFiles/mtproto/scheme/api.tl @@ -97,11 +97,11 @@ userStatusLastMonth#77ebc742 = UserStatus; chatEmpty#29562865 id:long = Chat; chat#41cbf256 flags:# creator:flags.0?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true noforwards:flags.25?true id:long title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat; chatForbidden#6592a1a7 id:long title:string = Chat; -channel#8e87ccd8 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true forum:flags.30?true flags2:# stories_hidden:flags2.1?true stories_hidden_min:flags2.2?true stories_unavailable:flags2.3?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int usernames:flags2.0?Vector stories_max_id:flags2.4?int color:flags2.7?PeerColor = Chat; +channel#a636a3e2 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true forum:flags.30?true flags2:# stories_hidden:flags2.1?true stories_hidden_min:flags2.2?true stories_unavailable:flags2.3?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int usernames:flags2.0?Vector stories_max_id:flags2.4?int color:flags2.7?PeerColor profile_color:flags2.8?PeerColor = Chat; channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat; chatFull#c9d31138 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true translations_disabled:flags.19?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector available_reactions:flags.18?ChatReactions = ChatFull; -channelFull#723027bd flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true translations_disabled:flags2.3?true stories_pinned_available:flags2.5?true view_forum_as_messages:flags2.6?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions stories:flags2.4?PeerStories = ChatFull; +channelFull#f2bcb6f flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true translations_disabled:flags2.3?true stories_pinned_available:flags2.5?true view_forum_as_messages:flags2.6?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper = ChatFull; chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant; chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant; @@ -132,7 +132,7 @@ messageMediaPoll#4bd6e798 poll:Poll results:PollResults = MessageMedia; messageMediaDice#3f7ee58b value:int emoticon:string = MessageMedia; messageMediaStory#68cb6283 flags:# via_mention:flags.1?true peer:Peer id:int story:flags.0?StoryItem = MessageMedia; messageMediaGiveaway#daad85b0 flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.2?true channels:Vector countries_iso2:flags.1?Vector prize_description:flags.3?string quantity:int months:int until_date:int = MessageMedia; -messageMediaGiveawayResults#b11ff5a1 flags:# refunded:flags.0?true channel_id:long launch_msg_id:int winners_count:int unclaimed_count:int winners:Vector months:int prize_description:flags.1?string = MessageMedia; +messageMediaGiveawayResults#c6991068 flags:# only_new_subscribers:flags.0?true refunded:flags.2?true channel_id:long additional_peers_count:flags.3?int launch_msg_id:int winners_count:int unclaimed_count:int winners:Vector months:int prize_description:flags.1?string until_date:int = MessageMedia; messageActionEmpty#b6aef7b0 = MessageAction; messageActionChatCreate#bd47cbad title:string users:Vector = MessageAction; @@ -172,7 +172,7 @@ messageActionTopicEdit#c0944820 flags:# title:flags.0?string icon_emoji_id:flags messageActionSuggestProfilePhoto#57de635e photo:Photo = MessageAction; messageActionRequestedPeer#fe77345d button_id:int peer:Peer = MessageAction; messageActionSetChatWallPaper#5060a3f4 flags:# same:flags.0?true for_both:flags.1?true wallpaper:WallPaper = MessageAction; -messageActionGiftCode#d2cfdb0e flags:# via_giveaway:flags.0?true unclaimed:flags.2?true boost_peer:flags.1?Peer months:int slug:string = MessageAction; +messageActionGiftCode#678c2e09 flags:# via_giveaway:flags.0?true unclaimed:flags.2?true boost_peer:flags.1?Peer months:int slug:string currency:flags.2?string amount:flags.2?long crypto_currency:flags.3?string crypto_amount:flags.3?long = MessageAction; messageActionGiveawayLaunch#332ba9ed = MessageAction; messageActionGiveawayResults#2a9fadc5 winners_count:int unclaimed_count:int = MessageAction; @@ -970,8 +970,9 @@ channelAdminLogEventActionEditTopic#f06fe208 prev_topic:ForumTopic new_topic:For channelAdminLogEventActionDeleteTopic#ae168909 topic:ForumTopic = ChannelAdminLogEventAction; channelAdminLogEventActionPinTopic#5d8d353b flags:# prev_topic:flags.0?ForumTopic new_topic:flags.1?ForumTopic = ChannelAdminLogEventAction; channelAdminLogEventActionToggleAntiSpam#64f36dfc new_value:Bool = ChannelAdminLogEventAction; -channelAdminLogEventActionChangeColor#3c2b247b prev_value:int new_value:int = ChannelAdminLogEventAction; -channelAdminLogEventActionChangeBackgroundEmoji#445fc434 prev_value:long new_value:long = ChannelAdminLogEventAction; +channelAdminLogEventActionChangePeerColor#5796e780 prev_value:PeerColor new_value:PeerColor = ChannelAdminLogEventAction; +channelAdminLogEventActionChangeProfilePeerColor#5e477b25 prev_value:PeerColor new_value:PeerColor = ChannelAdminLogEventAction; +channelAdminLogEventActionChangeWallpaper#31bb5d52 prev_value:WallPaper new_value:WallPaper = ChannelAdminLogEventAction; channelAdminLogEvent#1fad68cd id:long date:int user_id:long action:ChannelAdminLogEventAction = ChannelAdminLogEvent; @@ -1554,8 +1555,10 @@ stories.allStories#6efc5e81 flags:# has_more:flags.0?true count:int state:string stories.stories#5dd8c3c8 count:int stories:Vector chats:Vector users:Vector = stories.Stories; storyView#b0bdeac5 flags:# blocked:flags.0?true blocked_my_stories_from:flags.1?true user_id:long date:int reaction:flags.2?Reaction = StoryView; +//storyViewPublicForward#9083670b flags:# blocked:flags.0?true blocked_my_stories_from:flags.1?true message:Message = StoryView; +//storyViewPublicRepost#bd74cf49 flags:# blocked:flags.0?true blocked_my_stories_from:flags.1?true peer_id:Peer story:StoryItem = StoryView; -stories.storyViewsList#46e9b9ec flags:# count:int reactions_count:int views:Vector users:Vector next_offset:flags.0?string = stories.StoryViewsList; +stories.storyViewsList#19a16886 flags:# count:int views_count:int forwards_count:int reactions_count:int views:Vector users:Vector next_offset:flags.0?string = stories.StoryViewsList; stories.storyViews#de9eed1d views:Vector users:Vector = stories.StoryViews; @@ -1620,10 +1623,11 @@ help.peerColorOption#135bd42f flags:# hidden:flags.0?true color_id:int colors:fl help.peerColorsNotModified#2ba1f5ce = help.PeerColors; help.peerColors#f8ed08 hash:int colors:Vector = help.PeerColors; -storyPeerReaction#7decc433 peer_id:Peer date:int reaction:Reaction = StoryPeerReaction; -storyPeerPublicRepost#f7fbc17d peer_id:Peer story:StoryItem = StoryPeerReaction; +storyReaction#6090d6d5 peer_id:Peer date:int reaction:Reaction = StoryReaction; +storyReactionPublicForward#bbab2643 message:Message = StoryReaction; +storyReactionPublicRepost#cfcd0f13 peer_id:Peer story:StoryItem = StoryReaction; -stories.storyReactionsList#d86c162a flags:# count:int reactions:Vector chats:Vector users:Vector next_offset:flags.0?string = stories.StoryReactionsList; +stories.storyReactionsList#aa5f789c flags:# count:int reactions:Vector chats:Vector users:Vector next_offset:flags.0?string = stories.StoryReactionsList; ---functions--- @@ -2068,7 +2072,7 @@ channels.toggleAntiSpam#68f3e4eb channel:InputChannel enabled:Bool = Updates; channels.reportAntiSpamFalsePositive#a850a693 channel:InputChannel msg_id:int = Bool; channels.toggleParticipantsHidden#6a6e7854 channel:InputChannel enabled:Bool = Updates; channels.clickSponsoredMessage#18afbc93 channel:InputChannel random_id:bytes = Bool; -channels.updateColor#621a201f flags:# channel:InputChannel color:int background_emoji_id:flags.0?long = Updates; +channels.updateColor#d8aa3671 flags:# for_profile:flags.1?true channel:InputChannel color:flags.2?int background_emoji_id:flags.0?long = Updates; channels.toggleViewForumAsMessages#9738bb15 channel:InputChannel enabled:Bool = Updates; channels.getChannelRecommendations#83b70d97 channel:InputChannel = messages.Chats; @@ -2189,7 +2193,7 @@ stories.getStoriesByID#5774ca74 peer:InputPeer id:Vector = stories.Stories; stories.toggleAllStoriesHidden#7c2557c4 hidden:Bool = Bool; stories.readStories#a556dac8 peer:InputPeer max_id:int = Vector; stories.incrementStoryViews#b2028afb peer:InputPeer id:Vector = Bool; -stories.getStoryViewsList#7ed23c57 flags:# just_contacts:flags.0?true reactions_first:flags.2?true peer:InputPeer q:flags.1?string id:int offset:string limit:int = stories.StoryViewsList; +stories.getStoryViewsList#7ed23c57 flags:# just_contacts:flags.0?true reactions_first:flags.2?true forwards_first:flags.3?true peer:InputPeer q:flags.1?string id:int offset:string limit:int = stories.StoryViewsList; stories.getStoriesViews#28e16cc8 peer:InputPeer id:Vector = stories.StoryViews; stories.exportStoryLink#7b8def20 peer:InputPeer id:int = ExportedStoryLink; stories.report#1923fa8c peer:InputPeer id:Vector reason:ReportReason message:string = Bool; @@ -2200,7 +2204,7 @@ stories.getAllReadPeerStories#9b5ae7f9 = Updates; stories.getPeerMaxIDs#535983c3 id:Vector = Vector; stories.getChatsToSend#a56a8b60 = messages.Chats; stories.togglePeerStoriesHidden#bd0415c4 peer:InputPeer hidden:Bool = Bool; -stories.getStoryReactionsList#b9b2881f flags:# reposts_first:flags.2?true peer:InputPeer id:int reaction:flags.0?Reaction offset:flags.1?string limit:int = stories.StoryReactionsList; +stories.getStoryReactionsList#b9b2881f flags:# forwards_first:flags.2?true peer:InputPeer id:int reaction:flags.0?Reaction offset:flags.1?string limit:int = stories.StoryReactionsList; premium.getBoostsList#60f67660 flags:# gifts:flags.0?true peer:InputPeer offset:string limit:int = premium.BoostsList; premium.getMyBoosts#be77b4a = premium.MyBoosts; diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index aaec33461..03e6a03c3 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -974,6 +974,7 @@ storyMentionButtonSkip: 5px; chatGiveawayWidth: 292px; chatGiveawayStickerTop: -16px; +chatGiveawayWinnersTopSkip: 25px; chatGiveawayBadgeFont: font(12px bold); chatGiveawayBadgeTop: 106px; chatGiveawayBadgePadding: margins(7px, 1px, 5px, 3px);