diff --git a/Telegram/SourceFiles/boxes/url_auth_box.cpp b/Telegram/SourceFiles/boxes/url_auth_box.cpp index 6fa8050d7c..1cdda8fdeb 100644 --- a/Telegram/SourceFiles/boxes/url_auth_box.cpp +++ b/Telegram/SourceFiles/boxes/url_auth_box.cpp @@ -9,10 +9,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "boxes/url_auth_box_content.h" +#include "chat_helpers/stickers_emoji_pack.h" #include "core/application.h" #include "core/click_handler_types.h" #include "data/data_peer.h" #include "data/data_session.h" +#include "data/stickers/data_custom_emoji.h" #include "data/data_user.h" #include "history/history_item_components.h" #include "history/history_item.h" @@ -24,6 +26,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "ui/boxes/confirm_box.h" #include "ui/controls/userpic_button.h" +#include "ui/dynamic_thumbnails.h" +#include "ui/emoji_config.h" #include "ui/layers/generic_box.h" #include "ui/toast/toast.h" #include "ui/widgets/menu/menu_action.h" @@ -546,6 +550,39 @@ void RequestUrl( box, url, domain, + [=](QString code) -> std::shared_ptr { + auto emojiLength = 0; + const auto emoji = Ui::Emoji::Find(code, &emojiLength); + if (!emoji || emojiLength != code.size()) { + return nullptr; + } + const auto makeFor = [&](not_null source) { + if (const auto sticker = source->emojiStickersPack() + .stickerForEmoji(emoji)) { + return Ui::MakeEmojiThumbnail( + &source->data(), + Data::SerializeCustomEmojiId(sticker.document), + nullptr, + nullptr, + 1); + } + return std::shared_ptr(); + }; + const auto session = resolveSession(); + if (session->isTestMode()) { + for (const auto &account : Core::App().domain() + .orderedAccounts()) { + if (!account->sessionExists() + || account->session().isTestMode()) { + continue; + } + if (const auto image = makeFor(&account->session())) { + return image; + } + } + } + return makeFor(session); + }, callback, bot ? object_ptr( diff --git a/Telegram/SourceFiles/boxes/url_auth_box_content.cpp b/Telegram/SourceFiles/boxes/url_auth_box_content.cpp index ebd3c8f6ec..0a02eb053a 100644 --- a/Telegram/SourceFiles/boxes/url_auth_box_content.cpp +++ b/Telegram/SourceFiles/boxes/url_auth_box_content.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/qthelp_url.h" #include "lang/lang_keys.h" +#include "ui/dynamic_image.h" #include "ui/effects/ripple_animation.h" #include "ui/layers/generic_box.h" #include "ui/vertical_list.h" @@ -28,6 +29,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace UrlAuthBox { namespace { +constexpr auto kEmojiAnimationActiveFor = crl::time(250); + void PrepareFullWidthRoundButton( not_null button, not_null content, @@ -42,6 +45,7 @@ void PrepareFullWidthRoundButton( void ShowMatchCodesBox( not_null box, + Fn(QString)> emojiImageFactory, const QString &domain, const QStringList &codes, Fn callback) { @@ -83,10 +87,72 @@ void ShowMatchCodesBox( const auto overlay = Ui::CreateChild(button); overlay->setAttribute(Qt::WA_TransparentForMouseEvents); overlay->show(); + struct State { + std::shared_ptr image; + bool hovered = false; + crl::time lastFrameUpdate = 0; + }; + const auto state = overlay->lifetime().make_state(); + const auto animationActive = [=] { + return state->lastFrameUpdate + && (crl::now() - state->lastFrameUpdate + <= kEmojiAnimationActiveFor); + }; + const auto refreshImage = [=] { + if (state->image) { + state->image->subscribeToUpdates(nullptr); + } + state->image = emojiImageFactory + ? emojiImageFactory(code) + : nullptr; + if (state->image) { + state->image->subscribeToUpdates([=] { + state->lastFrameUpdate = crl::now(); + overlay->update(); + }); + } + overlay->update(); + }; + refreshImage(); + overlay->lifetime().add([=] { + if (state->image) { + state->image->subscribeToUpdates(nullptr); + } + }); + button->events() | rpl::on_next([=](not_null e) { + switch (e->type()) { + case QEvent::Enter: + if (!state->hovered) { + state->hovered = true; + if (!animationActive()) { + refreshImage(); + } + } + break; + case QEvent::Leave: + state->hovered = false; + break; + default: + break; + } + }, overlay->lifetime()); button->sizeValue() | rpl::on_next([=](QSize size) { overlay->resize(size); }, overlay->lifetime()); overlay->paintOn([=](QPainter &p) { + if (state->image) { + const auto side = st::urlAuthCodesButton.height; + const auto frame = state->image->image(side); + const auto visible = frame.isNull() + ? 0 + : (frame.width() / frame.devicePixelRatio()); + p.drawImage( + QPoint( + (overlay->width() - visible) / 2, + (overlay->height() - visible) / 2), + frame); + return; + } const auto size = Ui::Emoji::GetSizeLarge(); const auto visible = size / style::DevicePixelRatio(); Ui::Emoji::Draw( @@ -360,6 +426,7 @@ void ShowDetails( not_null box, const QString &url, const QString &domain, + Fn(QString)> emojiImageFactory, Fn callback, object_ptr userpicOwned, rpl::producer botName, @@ -487,6 +554,7 @@ void ShowDetails( not_null matchCodesBox) { ShowMatchCodesBox( matchCodesBox, + emojiImageFactory, domain, state->matchCodes, [=](QString matchCode) { diff --git a/Telegram/SourceFiles/boxes/url_auth_box_content.h b/Telegram/SourceFiles/boxes/url_auth_box_content.h index d2ad724c72..d6cb39486b 100644 --- a/Telegram/SourceFiles/boxes/url_auth_box_content.h +++ b/Telegram/SourceFiles/boxes/url_auth_box_content.h @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" namespace Ui { +class DynamicImage; class GenericBox; class VerticalLayout; } // namespace Ui @@ -64,6 +65,7 @@ void ShowDetails( not_null box, const QString &url, const QString &domain, + Fn(QString)> emojiImageFactory, Fn callback, object_ptr userpicOwned, rpl::producer botName, diff --git a/Telegram/SourceFiles/ui/dynamic_thumbnails.cpp b/Telegram/SourceFiles/ui/dynamic_thumbnails.cpp index 23a22cb316..0210e9ac9e 100644 --- a/Telegram/SourceFiles/ui/dynamic_thumbnails.cpp +++ b/Telegram/SourceFiles/ui/dynamic_thumbnails.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/empty_userpic.h" #include "ui/dynamic_image.h" #include "ui/painter.h" +#include "ui/text/text_custom_emoji.h" #include "ui/userpic_view.h" namespace Ui { @@ -213,6 +214,7 @@ public: EmojiThumbnail( not_null owner, const QString &data, + int loopLimit, Fn paused, Fn textColor); @@ -224,6 +226,7 @@ public: private: const not_null _owner; const QString _data; + const int _loopLimit = 0; std::unique_ptr _emoji; Fn _paused; Fn _textColor; @@ -615,10 +618,12 @@ void IconThumbnail::subscribeToUpdates(Fn callback) { EmojiThumbnail::EmojiThumbnail( not_null owner, const QString &data, + int loopLimit, Fn paused, Fn textColor) : _owner(owner) , _data(data) +, _loopLimit(loopLimit) , _paused(std::move(paused)) , _textColor(std::move(textColor)) { } @@ -628,10 +633,15 @@ void EmojiThumbnail::subscribeToUpdates(Fn callback) { _emoji = nullptr; return; } - _emoji = _owner->customEmojiManager().create( + auto emoji = _owner->customEmojiManager().create( _data, std::move(callback), Data::CustomEmojiSizeTag::Large); + _emoji = (_loopLimit > 0) + ? std::make_unique( + std::move(emoji), + _loopLimit) + : std::move(emoji); Ensures(_emoji != nullptr); } @@ -640,6 +650,7 @@ std::shared_ptr EmojiThumbnail::clone() { return std::make_shared( _owner, _data, + _loopLimit, _paused, _textColor); } @@ -716,10 +727,12 @@ std::shared_ptr MakeEmojiThumbnail( not_null owner, const QString &data, Fn paused, - Fn textColor) { + Fn textColor, + int loopLimit) { return std::make_shared( owner, data, + loopLimit, std::move(paused), std::move(textColor)); } diff --git a/Telegram/SourceFiles/ui/dynamic_thumbnails.h b/Telegram/SourceFiles/ui/dynamic_thumbnails.h index e58c300fab..7fd5d7f5d7 100644 --- a/Telegram/SourceFiles/ui/dynamic_thumbnails.h +++ b/Telegram/SourceFiles/ui/dynamic_thumbnails.h @@ -35,7 +35,8 @@ class DynamicImage; not_null owner, const QString &data, Fn paused = nullptr, - Fn textColor = nullptr); + Fn textColor = nullptr, + int loopLimit = 0); [[nodiscard]] std::shared_ptr MakePhotoThumbnail( not_null photo, FullMsgId fullId);