diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 695bc67ff..cdcc32295 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2169,6 +2169,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_context_animated_emoji" = "This message contains emoji from **{name} pack**."; "lng_context_animated_emoji_many#one" = "This message contains emoji from **{count} pack**."; "lng_context_animated_emoji_many#other" = "This message contains emoji from **{count} packs**."; +"lng_context_animated_reaction" = "This reaction is from **{name} pack**."; +"lng_context_animated_reactions" = "Reactions contain emoji from **{name} pack**."; +"lng_context_animated_reactions_many#one" = "Reactions contain emoji from **{count} pack**."; +"lng_context_animated_reactions_many#other" = "Reactions contain emoji from **{count} packs**."; "lng_downloads_section" = "Downloads"; "lng_downloads_view_in_chat" = "View in chat"; diff --git a/Telegram/SourceFiles/api/api_who_reacted.cpp b/Telegram/SourceFiles/api/api_who_reacted.cpp index 9dcb62f8c..a802787ff 100644 --- a/Telegram/SourceFiles/api/api_who_reacted.cpp +++ b/Telegram/SourceFiles/api/api_who_reacted.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item.h" #include "history/history.h" +#include "data/stickers/data_custom_emoji.h" #include "data/data_peer.h" #include "data/data_chat.h" #include "data/data_channel.h" @@ -112,7 +113,7 @@ struct Context { struct Userpic { not_null peer; - QString reaction; + QString customEntityData; mutable std::shared_ptr view; mutable InMemoryKey uniqueKey; }; @@ -377,17 +378,16 @@ bool UpdateUserpics( auto now = std::vector(); for (const auto &resolved : peers) { const auto peer = not_null{ resolved.peer }; - if (ranges::contains(now, peer, &Userpic::peer)) { - continue; - } + const auto &data = ReactionEntityData(resolved.reaction); const auto i = ranges::find(was, peer, &Userpic::peer); - if (i != end(was)) { + if (i != end(was) && i->view) { now.push_back(std::move(*i)); + now.back().customEntityData = data; continue; } now.push_back(Userpic{ .peer = peer, - .reaction = resolved.reaction.emoji(), // #TODO reactions + .customEntityData = data, }); auto &userpic = now.back(); userpic.uniqueKey = peer->userpicUniqueKey(userpic.view); @@ -436,7 +436,7 @@ void RegenerateParticipants(not_null state, int small, int large) { } now.push_back({ .name = peer->name(), - .reaction = userpic.reaction, + .customEntityData = userpic.customEntityData, .userpicLarge = GenerateUserpic(userpic, large), .userpicKey = userpic.uniqueKey, .id = id, @@ -493,13 +493,12 @@ rpl::producer WhoReacted( &Data::MessageReaction::id); return (i != end(list)) ? i->count : 0; }(); - - // #TODO reactions - state->current.singleReaction = (!reaction.empty() + state->current.singleCustomEntityData = ReactionEntityData( + !reaction.empty() ? reaction : (list.size() == 1) ? list.front().id - : ReactionId()).emoji(); + : ReactionId()); } std::move( idsWithReactions diff --git a/Telegram/SourceFiles/data/data_message_reaction_id.cpp b/Telegram/SourceFiles/data/data_message_reaction_id.cpp index 653a481d6..c5f4670b3 100644 --- a/Telegram/SourceFiles/data/data_message_reaction_id.cpp +++ b/Telegram/SourceFiles/data/data_message_reaction_id.cpp @@ -7,8 +7,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "data/data_message_reaction_id.h" +#include "data/stickers/data_custom_emoji.h" + namespace Data { +QString ReactionEntityData(const ReactionId &id) { + if (id.empty()) { + return {}; + } else if (const auto custom = id.custom()) { + return SerializeCustomEmojiId({ + .id = custom, + }); + } + return u"default:"_q + id.emoji(); +} + ReactionId ReactionFromMTP(const MTPReaction &reaction) { return reaction.match([](MTPDreactionEmpty) { return ReactionId{ QString() }; diff --git a/Telegram/SourceFiles/data/data_message_reaction_id.h b/Telegram/SourceFiles/data/data_message_reaction_id.h index ca64a6191..b885d5a6e 100644 --- a/Telegram/SourceFiles/data/data_message_reaction_id.h +++ b/Telegram/SourceFiles/data/data_message_reaction_id.h @@ -39,6 +39,8 @@ inline bool operator==(const ReactionId &a, const ReactionId &b) { return a.data == b.data; } +[[nodiscard]] QString ReactionEntityData(const ReactionId &id); + [[nodiscard]] ReactionId ReactionFromMTP(const MTPReaction &reaction); [[nodiscard]] MTPReaction ReactionToMTP(ReactionId id); diff --git a/Telegram/SourceFiles/data/data_message_reactions.cpp b/Telegram/SourceFiles/data/data_message_reactions.cpp index cf793780e..9bcf6c151 100644 --- a/Telegram/SourceFiles/data/data_message_reactions.cpp +++ b/Telegram/SourceFiles/data/data_message_reactions.cpp @@ -72,7 +72,6 @@ constexpr auto kTopReactionsLimit = 10; .centerIcon = document, .active = true, }; - } } // namespace @@ -99,16 +98,13 @@ PossibleItemReactionsRef LookupPossibleReactions( return true; // #TODO reactions }(); auto added = base::flat_set(); - const auto addOne = [&](const Reaction &reaction) { - if (added.emplace(reaction.id).second) { - result.recent.push_back(&reaction); - } - }; const auto add = [&](auto predicate) { - auto &&all = ranges::views::concat(top, recent, full); + auto &&all = ranges::views::concat(recent, top, full); for (const auto &reaction : all) { if (predicate(reaction)) { - addOne(reaction); + if (added.emplace(reaction.id).second) { + result.recent.push_back(&reaction); + } } } }; diff --git a/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp b/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp index 9fa351267..029978ed7 100644 --- a/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp +++ b/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp @@ -14,20 +14,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_document_media.h" #include "data/data_file_origin.h" #include "data/data_peer.h" +#include "data/data_message_reactions.h" +#include "data/stickers/data_stickers.h" #include "lottie/lottie_common.h" #include "lottie/lottie_emoji.h" #include "ffmpeg/ffmpeg_emoji.h" #include "chat_helpers/stickers_lottie.h" -#include "ui/text/text_block.h" +#include "ui/widgets/input_fields.h" +#include "ui/text/text_custom_emoji.h" #include "ui/ui_utility.h" #include "apiwrap.h" #include "styles/style_chat.h" #include "styles/style_chat_helpers.h" -#include "data/stickers/data_stickers.h" -#include "ui/widgets/input_fields.h" -#include "ui/text/text_block.h" - namespace Data { namespace { @@ -42,7 +41,6 @@ using SizeTag = CustomEmojiManager::SizeTag; case SizeTag::Normal: return LottieSize::EmojiInteraction; case SizeTag::Large: return LottieSize::EmojiInteractionReserved1; case SizeTag::Isolated: return LottieSize::EmojiInteractionReserved2; - case SizeTag::ReactionFake: return LottieSize::EmojiInteractionReserved3; } Unexpected("SizeTag value in CustomEmojiManager-LottieSizeFromTag."); } @@ -54,12 +52,16 @@ using SizeTag = CustomEmojiManager::SizeTag; case SizeTag::Isolated: return (st::largeEmojiSize + 2 * st::largeEmojiOutline) * style::DevicePixelRatio(); - case SizeTag::ReactionFake: - return st::reactStripImage * style::DevicePixelRatio(); } Unexpected("SizeTag value in CustomEmojiManager-SizeFromTag."); } +[[nodiscard]] int FrameSizeFromTag(SizeTag tag, int sizeOverride) { + return sizeOverride + ? (sizeOverride * style::DevicePixelRatio()) + : FrameSizeFromTag(tag); +} + } // namespace class CustomEmojiLoader final @@ -69,8 +71,12 @@ public: CustomEmojiLoader( not_null owner, const CustomEmojiId id, - SizeTag tag); - CustomEmojiLoader(not_null document, SizeTag tag); + SizeTag tag, + int sizeOverride); + CustomEmojiLoader( + not_null document, + SizeTag tag, + int sizeOverride); [[nodiscard]] bool resolving() const; void resolved(not_null document); @@ -120,6 +126,7 @@ private: const CustomEmojiId &id); std::variant _state; + ushort _sizeOverride = 0; SizeTag _tag = SizeTag::Normal; }; @@ -127,16 +134,24 @@ private: CustomEmojiLoader::CustomEmojiLoader( not_null owner, const CustomEmojiId id, - SizeTag tag) + SizeTag tag, + int sizeOverride) : _state(InitialState(owner, id)) +, _sizeOverride(sizeOverride) , _tag(tag) { + Expects(sizeOverride >= 0 + && sizeOverride <= std::numeric_limits::max()); } CustomEmojiLoader::CustomEmojiLoader( not_null document, - SizeTag tag) + SizeTag tag, + int sizeOverride) : _state(Lookup{ document }) +, _sizeOverride(sizeOverride) , _tag(tag) { + Expects(sizeOverride >= 0 + && sizeOverride <= std::numeric_limits::max()); } bool CustomEmojiLoader::resolving() const { @@ -232,7 +247,7 @@ void CustomEmojiLoader::startCacheLookup( lookup->process = std::make_unique(Process{ .loaded = std::move(loaded), }); - const auto size = FrameSizeFromTag(_tag); + const auto size = FrameSizeFromTag(_tag, _sizeOverride); const auto weak = base::make_weak(&lookup->process->guard); document->owner().cacheBigFile().get(key, [=](QByteArray value) { auto cache = Ui::CustomEmoji::Cache::FromSerialized(value, size); @@ -251,8 +266,12 @@ void CustomEmojiLoader::lookupDone( return; } const auto tag = _tag; + const auto sizeOverride = int(_sizeOverride); auto loader = [=] { - return std::make_unique(document, tag); + return std::make_unique( + document, + tag, + sizeOverride); }; auto done = std::move(lookup->process->loaded); done(Ui::CustomEmoji::Cached( @@ -285,10 +304,14 @@ void CustomEmojiLoader::check() { load->process->lifetime.destroy(); const auto tag = _tag; - const auto size = FrameSizeFromTag(_tag); + const auto sizeOverride = int(_sizeOverride); + const auto size = FrameSizeFromTag(_tag, _sizeOverride); auto bytes = Lottie::ReadContent(data, filepath); auto loader = [=] { - return std::make_unique(document, tag); + return std::make_unique( + document, + tag, + sizeOverride); }; auto put = [=, key = cacheKey(document)](QByteArray value) { document->owner().cacheBigFile().put(key, std::move(value)); @@ -347,7 +370,7 @@ Ui::CustomEmoji::Preview CustomEmojiLoader::preview() { || !dimensions.width()) { return {}; } - const auto scale = (FrameSizeFromTag(_tag) * 1.) + const auto scale = (FrameSizeFromTag(_tag, _sizeOverride) * 1.) / (style::DevicePixelRatio() * dimensions.width()); return { document->createMediaView()->thumbnailPath(), scale }; }; @@ -371,6 +394,7 @@ std::unique_ptr CustomEmojiManager::create( DocumentId documentId, Fn update, SizeTag tag, + int sizeOverride, LoaderFactory factory) { auto &instances = _instances[SizeIndex(tag)]; auto i = instances.find(documentId); @@ -385,10 +409,10 @@ std::unique_ptr CustomEmojiManager::create( documentId, std::make_unique(Loading{ factory(), - prepareNonExactPreview(documentId, tag) + prepareNonExactPreview(documentId, tag, sizeOverride) }, std::move(repaint))).first; } else if (!i->second->hasImagePreview()) { - auto preview = prepareNonExactPreview(documentId, tag); + auto preview = prepareNonExactPreview(documentId, tag, sizeOverride); if (preview.isImage()) { i->second->updatePreview(std::move(preview)); } @@ -400,7 +424,8 @@ std::unique_ptr CustomEmojiManager::create( Ui::CustomEmoji::Preview CustomEmojiManager::prepareNonExactPreview( DocumentId documentId, - SizeTag tag) const { + SizeTag tag, + int sizeOverride) const { for (auto i = _instances.size(); i != 0;) { if (SizeIndex(tag) == --i) { continue; @@ -410,7 +435,7 @@ Ui::CustomEmoji::Preview CustomEmojiManager::prepareNonExactPreview( if (j == end(other)) { continue; } else if (const auto nonExact = j->second->imagePreview()) { - const auto size = FrameSizeFromTag(tag); + const auto size = FrameSizeFromTag(tag, sizeOverride); return { nonExact.image().scaled( size, @@ -427,26 +452,31 @@ Ui::CustomEmoji::Preview CustomEmojiManager::prepareNonExactPreview( std::unique_ptr CustomEmojiManager::create( QStringView data, Fn update, - SizeTag tag) { + SizeTag tag, + int sizeOverride) { const auto parsed = ParseCustomEmojiData(data); - return parsed.id ? create(parsed.id, std::move(update), tag) : nullptr; + return parsed.id + ? create(parsed.id, std::move(update), tag, sizeOverride) + : nullptr; } std::unique_ptr CustomEmojiManager::create( DocumentId documentId, Fn update, - SizeTag tag) { - return create(documentId, std::move(update), tag, [&] { - return createLoader(documentId, tag); + SizeTag tag, + int sizeOverride) { + return create(documentId, std::move(update), tag, sizeOverride, [&] { + return createLoader(documentId, tag, sizeOverride); }); } std::unique_ptr CustomEmojiManager::create( not_null document, Fn update, - SizeTag tag) { - return create(document->id, std::move(update), tag, [&] { - return createLoader(document, tag); + SizeTag tag, + int sizeOverride) { + return create(document->id, std::move(update), tag, sizeOverride, [&] { + return createLoader(document, tag, sizeOverride); }); } @@ -485,17 +515,20 @@ void CustomEmojiManager::unregisterListener(not_null listener) { std::unique_ptr CustomEmojiManager::createLoader( not_null document, - SizeTag tag) { - return std::make_unique(document, tag); + SizeTag tag, + int sizeOverride) { + return std::make_unique(document, tag, sizeOverride); } std::unique_ptr CustomEmojiManager::createLoader( DocumentId documentId, - SizeTag tag) { + SizeTag tag, + int sizeOverride) { auto result = std::make_unique( _owner, CustomEmojiId{ .id = documentId }, - tag); + tag, + sizeOverride); if (result->resolving()) { const auto i = SizeIndex(tag); _loaders[i][documentId].push_back(base::make_weak(result.get())); @@ -667,9 +700,6 @@ Session &CustomEmojiManager::owner() const { int FrameSizeFromTag(SizeTag tag) { const auto emoji = EmojiSizeFromTag(tag); - if (tag == SizeTag::ReactionFake) { - return emoji; - } const auto factor = style::DevicePixelRatio(); return Ui::Text::AdjustCustomEmojiSize(emoji / factor) * factor; } @@ -705,4 +735,37 @@ void InsertCustomEmoji( Ui::InputField::CustomEmojiLink(SerializeCustomEmojiId(document))); } +Ui::Text::CustomEmojiFactory ReactedMenuFactory( + not_null session) { + return [owner = &session->data()]( + QStringView data, + Fn repaint) -> std::unique_ptr { + const auto prefix = u"default:"_q; + if (data.startsWith(prefix)) { + const auto &list = owner->reactions().list( + Data::Reactions::Type::All); + const auto emoji = data.mid(prefix.size()).toString(); + const auto id = Data::ReactionId{ { emoji } }; + const auto i = ranges::find(list, id, &Data::Reaction::id); + if (i != end(list)) { + const auto document = i->centerIcon + ? not_null(i->centerIcon) + : i->selectAnimation; + const auto size = st::emojiSize * (i->centerIcon ? 2 : 1); + const auto tag = Data::CustomEmojiManager::SizeTag::Normal; + const auto skip = (Data::FrameSizeFromTag(tag) - size) / 2; + return std::make_unique( + std::make_unique( + owner->customEmojiManager().create( + document, + std::move(repaint), + tag, + size), + QPoint(skip, skip))); + } + } + return owner->customEmojiManager().create(data, std::move(repaint)); + }; +} + } // namespace Data diff --git a/Telegram/SourceFiles/data/stickers/data_custom_emoji.h b/Telegram/SourceFiles/data/stickers/data_custom_emoji.h index c772226a0..11e4b979c 100644 --- a/Telegram/SourceFiles/data/stickers/data_custom_emoji.h +++ b/Telegram/SourceFiles/data/stickers/data_custom_emoji.h @@ -29,11 +29,10 @@ struct CustomEmojiId { class CustomEmojiManager final : public base::has_weak_ptr { public: - enum class SizeTag { + enum class SizeTag : uchar { Normal, Large, Isolated, - ReactionFake, kCount, }; @@ -44,15 +43,18 @@ public: [[nodiscard]] std::unique_ptr create( QStringView data, Fn update, - SizeTag tag = SizeTag::Normal); + SizeTag tag = SizeTag::Normal, + int sizeOverride = 0); [[nodiscard]] std::unique_ptr create( DocumentId documentId, Fn update, - SizeTag tag = SizeTag::Normal); + SizeTag tag = SizeTag::Normal, + int sizeOverride = 0); [[nodiscard]] std::unique_ptr create( not_null document, Fn update, - SizeTag tag = SizeTag::Normal); + SizeTag tag = SizeTag::Normal, + int sizeOverride = 0); class Listener { public: @@ -65,10 +67,12 @@ public: [[nodiscard]] std::unique_ptr createLoader( not_null document, - SizeTag tag); + SizeTag tag, + int sizeOverride = 0); [[nodiscard]] std::unique_ptr createLoader( DocumentId documentId, - SizeTag tag); + SizeTag tag, + int sizeOverride = 0); [[nodiscard]] QString lookupSetName(uint64 setId); @@ -94,12 +98,14 @@ private: [[nodiscard]] Ui::CustomEmoji::Preview prepareNonExactPreview( DocumentId documentId, - SizeTag tag) const; + SizeTag tag, + int sizeOverride) const; template [[nodiscard]] std::unique_ptr create( DocumentId documentId, Fn update, SizeTag tag, + int sizeOverride, LoaderFactory factory); [[nodiscard]] static int SizeIndex(SizeTag tag); @@ -145,4 +151,7 @@ void InsertCustomEmoji( not_null field, not_null document); +[[nodiscard]] Ui::Text::CustomEmojiFactory ReactedMenuFactory( + not_null session); + } // namespace Data diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 2c2a5395b..fdf90c110 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -2418,14 +2418,11 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } - auto emojiPackIds = _dragStateItem - ? HistoryView::CollectEmojiPacks(_dragStateItem) - : std::vector(); - if (!emojiPackIds.empty()) { + if (_dragStateItem) { HistoryView::AddEmojiPacksAction( _menu, - this, - std::move(emojiPackIds), + _dragStateItem, + HistoryView::EmojiPacksSource::Message, _controller); } if (hasWhoReactedItem) { diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index 053e53f64..279889223 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -1004,14 +1004,11 @@ base::unique_qptr FillContextMenu( AddCopyLinkAction(result, link); AddMessageActions(result, request, list); - auto emojiPackIds = item - ? CollectEmojiPacks(item) - : std::vector(); - if (!emojiPackIds.empty()) { + if (item) { AddEmojiPacksAction( result, - list, - std::move(emojiPackIds), + item, + HistoryView::EmojiPacksSource::Message, list->controller()); } if (hasWhoReactedItem) { @@ -1162,6 +1159,7 @@ void AddWhoReactedAction( menu->addAction(Ui::WhoReactedContextAction( menu.get(), Api::WhoReacted(item, context, st::defaultWhoRead, whoReadIds), + Data::ReactedMenuFactory(&controller->session()), participantChosen, showAllChosen)); } @@ -1185,12 +1183,15 @@ void ShowWhoReactedMenu( id)); } }; - const auto reactions = &controller->session().data().reactions(); + const auto owner = &controller->session().data(); + const auto reactions = &owner->reactions(); const auto &list = reactions->list( Data::Reactions::Type::Active); const auto activeNonQuick = (id != reactions->favoriteId()) - && ranges::contains(list, id, &Data::Reaction::id); + && (ranges::contains(list, id, &Data::Reaction::id) + || (controller->session().premium() && id.custom())); const auto filler = lifetime.make_state( + Data::ReactedMenuFactory(&controller->session()), participantChosen, showAllChosen); Api::WhoReacted( @@ -1219,6 +1220,17 @@ void ShowWhoReactedMenu( } filler->populate(menu->get(), content); + if (const auto custom = id.custom()) { + if (const auto set = owner->document(custom)->sticker()) { + if (set->set.id) { + AddEmojiPacksAction( + menu->get(), + { set->set }, + EmojiPacksSource::Reaction, + controller); + } + } + } if (creating) { (*menu)->popup(position); } @@ -1226,31 +1238,51 @@ void ShowWhoReactedMenu( } std::vector CollectEmojiPacks( - not_null item) { + not_null item, + EmojiPacksSource source) { auto result = std::vector(); const auto owner = &item->history()->owner(); - for (const auto &entity : item->originalText().entities) { - if (entity.type() == EntityType::CustomEmoji) { - const auto data = Data::ParseCustomEmojiData(entity.data()); - if (const auto set = owner->document(data.id)->sticker()) { - if (set->set.id - && !ranges::contains( - result, - set->set.id, - &StickerSetIdentifier::id)) { - result.push_back(set->set); - } + const auto push = [&](DocumentId id) { + if (const auto set = owner->document(id)->sticker()) { + if (set->set.id + && !ranges::contains( + result, + set->set.id, + &StickerSetIdentifier::id)) { + result.push_back(set->set); } } + }; + switch (source) { + case EmojiPacksSource::Message: + for (const auto &entity : item->originalText().entities) { + if (entity.type() == EntityType::CustomEmoji) { + const auto data = Data::ParseCustomEmojiData(entity.data()); + push(data.id); + } + } + break; + case EmojiPacksSource::Reactions: + for (const auto &reaction : item->reactions()) { + if (const auto customId = reaction.id.custom()) { + push(customId); + } + } + break; + default: Unexpected("Source in CollectEmojiPacks."); } return result; } void AddEmojiPacksAction( not_null menu, - not_null context, std::vector packIds, + EmojiPacksSource source, not_null controller) { + if (packIds.empty()) { + return; + } + class Item final : public Ui::Menu::ItemBase { public: Item( @@ -1333,20 +1365,48 @@ void AddEmojiPacksAction( if (!menu->empty()) { menu->addSeparator(); } + auto text = [&] { + switch (source) { + case EmojiPacksSource::Message: + return name.text.isEmpty() + ? tr::lng_context_animated_emoji_many( + tr::now, + lt_count, + count, + Ui::Text::RichLangValue) + : tr::lng_context_animated_emoji( + tr::now, + lt_name, + TextWithEntities{ name }, + Ui::Text::RichLangValue); + case EmojiPacksSource::Reaction: + if (!name.text.isEmpty()) { + return tr::lng_context_animated_reaction( + tr::now, + lt_name, + TextWithEntities{ name }, + Ui::Text::RichLangValue); + } + [[fallthrough]]; + case EmojiPacksSource::Reactions: + return name.text.isEmpty() + ? tr::lng_context_animated_reactions_many( + tr::now, + lt_count, + count, + Ui::Text::RichLangValue) + : tr::lng_context_animated_reactions( + tr::now, + lt_name, + TextWithEntities{ name }, + Ui::Text::RichLangValue); + } + Unexpected("Source in AddEmojiPacksAction."); + }(); auto button = base::make_unique_q( menu->menu(), menu->st().menu, - (name.text.isEmpty() - ? tr::lng_context_animated_emoji_many( - tr::now, - lt_count, - count, - Ui::Text::RichLangValue) - : tr::lng_context_animated_emoji( - tr::now, - lt_name, - TextWithEntities{ name }, - Ui::Text::RichLangValue))); + std::move(text)); const auto weak = base::make_weak(controller.get()); button->setClickedCallback([=] { const auto strong = weak.get(); @@ -1368,4 +1428,16 @@ void AddEmojiPacksAction( menu->addAction(std::move(button)); } +void AddEmojiPacksAction( + not_null menu, + not_null item, + EmojiPacksSource source, + not_null controller) { + AddEmojiPacksAction( + menu, + CollectEmojiPacks(item, source), + source, + controller); +} + } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.h b/Telegram/SourceFiles/history/view/history_view_context_menu.h index dc842b2b4..8f2e64640 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.h +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.h @@ -83,12 +83,23 @@ void ShowWhoReactedMenu( not_null controller, rpl::lifetime &lifetime); +enum class EmojiPacksSource { + Message, + Reaction, + Reactions, +}; [[nodiscard]] std::vector CollectEmojiPacks( - not_null item); + not_null item, + EmojiPacksSource source); void AddEmojiPacksAction( not_null menu, - not_null context, std::vector packIds, + EmojiPacksSource source, + not_null controller); +void AddEmojiPacksAction( + not_null menu, + not_null item, + EmojiPacksSource source, not_null controller); } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions.cpp b/Telegram/SourceFiles/history/view/reactions/history_view_reactions.cpp index 77b9fe141..96f479370 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions.cpp +++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions.cpp @@ -19,7 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_peer.h" #include "data/data_session.h" #include "lang/lang_tag.h" -#include "ui/text/text_block.h" +#include "ui/text/text_custom_emoji.h" #include "ui/chat/chat_style.h" #include "styles/style_chat.h" @@ -95,6 +95,7 @@ void InlineList::unloadCustomEmoji() { custom->unload(); } } + _customCache = QImage(); } void InlineList::layout() { @@ -406,19 +407,13 @@ void InlineList::paint( inner.topLeft() + QPoint(skip, skip), QSize(st::reactionInlineImage, st::reactionInlineImage)); if (!skipImage) { - if (button.custom) { - if (!_customSkip) { - using namespace Ui::Text; - const auto size = st::emojiSize; - _customSkip = (size - AdjustCustomEmojiSize(size)) / 2; - } - button.custom->paint(p, { - .preview = textFg.color(), - .now = context.now, - .position = (inner.topLeft() - + QPoint(_customSkip, _customSkip)), - .paused = p.inactive(), - }); + if (const auto custom = button.custom.get()) { + paintCustomFrame( + p, + custom, + inner.topLeft(), + context.now, + textFg.color()); } else if (!button.image.isNull()) { p.drawImage(image.topLeft(), button.image); } @@ -535,6 +530,42 @@ void InlineList::resolveUserpicsImage(const Button &button) const { kMaxRecentUserpics); } +void InlineList::paintCustomFrame( + Painter &p, + not_null emoji, + QPoint innerTopLeft, + crl::time now, + const QColor &preview) const { + if (_customCache.isNull()) { + using namespace Ui::Text; + const auto size = st::emojiSize; + const auto factor = style::DevicePixelRatio(); + const auto adjusted = AdjustCustomEmojiSize(size); + _customCache = QImage( + QSize(adjusted, adjusted) * factor, + QImage::Format_ARGB32_Premultiplied); + _customCache.setDevicePixelRatio(factor); + _customSkip = (size - adjusted) / 2; + } + _customCache.fill(Qt::transparent); + auto q = QPainter(&_customCache); + emoji->paint(q, { + .preview = preview, + .now = now, + .paused = p.inactive(), + }); + q.end(); + _customCache = Images::Round( + std::move(_customCache), + (Images::Option::RoundLarge + | Images::Option::RoundSkipTopRight + | Images::Option::RoundSkipBottomRight)); + + p.drawImage( + innerTopLeft + QPoint(_customSkip, _customSkip), + _customCache); +} + auto InlineList::takeAnimations() -> base::flat_map> { auto result = base::flat_map< diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions.h b/Telegram/SourceFiles/history/view/reactions/history_view_reactions.h index f8b08c498..3cd64ff9b 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions.h +++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions.h @@ -104,6 +104,12 @@ private: const std::vector> &peers); [[nodiscard]] Button prepareButtonWithId(const ReactionId &id); void resolveUserpicsImage(const Button &button) const; + void paintCustomFrame( + Painter &p, + not_null emoji, + QPoint innerTopLeft, + crl::time now, + const QColor &preview) const; QSize countOptimalSize() override; @@ -113,6 +119,7 @@ private: Data _data; std::vector