From 21fd381778aeef80fa1e2ee864563e929d5da830 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 9 Aug 2022 18:53:40 +0300 Subject: [PATCH] Show emoji status in chats list / top bar. --- Telegram/SourceFiles/data/data_changes.h | 29 ++--- Telegram/SourceFiles/data/data_session.cpp | 42 +++++++ Telegram/SourceFiles/data/data_user.cpp | 11 ++ Telegram/SourceFiles/data/data_user.h | 5 + Telegram/SourceFiles/dialogs/dialogs_entry.h | 5 + .../dialogs/dialogs_inner_widget.cpp | 80 +++++++++----- .../dialogs/dialogs_inner_widget.h | 5 +- Telegram/SourceFiles/dialogs/dialogs_row.cpp | 8 +- Telegram/SourceFiles/dialogs/dialogs_row.h | 18 ++- .../SourceFiles/dialogs/ui/dialogs_layout.cpp | 94 +++++++++------- .../dialogs/ui/dialogs_message_view.cpp | 104 +++++++++--------- .../dialogs/ui/dialogs_message_view.h | 9 +- .../controls/history_view_compose_search.cpp | 10 +- .../view/history_view_top_bar_widget.cpp | 21 ++-- .../view/history_view_top_bar_widget.h | 2 + Telegram/SourceFiles/ui/unread_badge.cpp | 73 +++++++++--- Telegram/SourceFiles/ui/unread_badge.h | 47 ++++++-- 17 files changed, 386 insertions(+), 177 deletions(-) diff --git a/Telegram/SourceFiles/data/data_changes.h b/Telegram/SourceFiles/data/data_changes.h index a7249d3fe..d72a0fd3d 100644 --- a/Telegram/SourceFiles/data/data_changes.h +++ b/Telegram/SourceFiles/data/data_changes.h @@ -78,26 +78,27 @@ struct PeerUpdate { HasCalls = (1ULL << 19), SupportInfo = (1ULL << 20), IsBot = (1ULL << 21), + EmojiStatus = (1ULL << 22), // For chats and channels - InviteLinks = (1ULL << 22), - Members = (1ULL << 23), - Admins = (1ULL << 24), - BannedUsers = (1ULL << 25), - Rights = (1ULL << 26), - PendingRequests = (1ULL << 27), - Reactions = (1ULL << 28), + InviteLinks = (1ULL << 23), + Members = (1ULL << 24), + Admins = (1ULL << 25), + BannedUsers = (1ULL << 26), + Rights = (1ULL << 27), + PendingRequests = (1ULL << 28), + Reactions = (1ULL << 29), // For channels - ChannelAmIn = (1ULL << 29), - StickersSet = (1ULL << 30), - ChannelLinkedChat = (1ULL << 31), - ChannelLocation = (1ULL << 32), - Slowmode = (1ULL << 33), - GroupCall = (1ULL << 34), + ChannelAmIn = (1ULL << 30), + StickersSet = (1ULL << 31), + ChannelLinkedChat = (1ULL << 32), + ChannelLocation = (1ULL << 33), + Slowmode = (1ULL << 34), + GroupCall = (1ULL << 35), // For iteration - LastUsedBit = (1ULL << 34), + LastUsedBit = (1ULL << 35), }; using Flags = base::flags; friend inline constexpr auto is_flag_type(Flag) { return true; } diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index c4b3eff85..8d81aeadb 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -400,6 +400,11 @@ ChannelData *Session::channelLoaded(ChannelId id) const { return nullptr; } +AssertIsDebug(); +base::flat_map< + not_null, + base::flat_set>> Emojis; + not_null Session::processUser(const MTPUser &data) { const auto result = user(data.match([](const auto &data) { return data.vid().v; @@ -554,6 +559,26 @@ not_null Session::processUser(const MTPUser &data) { } status = data.vstatus(); } + if (const auto &status = data.vemoji_status()) { + result->setEmojiStatus(status->match([&]( + const MTPDemojiStatus &data) { + return DocumentId(data.vdocument_id().v); + }, [&](const MTPDemojiStatusEmpty &) { + //return DocumentId(); + auto &emojis = Emojis[this]; + return emojis.empty() + ? DocumentId() + : (*(emojis.begin() + + base::RandomIndex(emojis.size())))->id; + })); + } else { + //result->setEmojiStatus(0); + auto &emojis = Emojis[this]; + result->setEmojiStatus(emojis.empty() + ? DocumentId() + : (*(emojis.begin() + + base::RandomIndex(emojis.size())))->id); + } if (!minimal) { if (const auto botInfoVersion = data.vbot_info_version()) { result->setBotInfoVersion(botInfoVersion->v); @@ -2943,6 +2968,23 @@ void Session::documentApplyFields( if (dc != 0 && access != 0) { document->setRemoteLocation(dc, access, fileReference); } + + AssertIsDebug(); + if (document->isPremiumEmoji()) { + auto &emojis = Emojis[this]; + if (emojis.emplace(document).second) { + const auto size = int(emojis.size()); + crl::on_main(_session, [=] { + for (auto &[id, peer] : _peers) { + if (const auto user = peer->asUser()) { + if (user->isPremium() && !base::RandomIndex(size)) { + user->setEmojiStatus(document->id); + } + } + } + }); + } + } } not_null Session::webpage(WebPageId id) { diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index 945fd949d..55f18b946 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -57,6 +57,17 @@ void UserData::setPhoto(const MTPUserProfilePhoto &photo) { }); } +void UserData::setEmojiStatus(DocumentId emojiStatusId) { + if (_emojiStatusId != emojiStatusId) { + _emojiStatusId = emojiStatusId; + session().changes().peerUpdated(this, UpdateFlag::EmojiStatus); + } +} + +DocumentId UserData::emojiStatusId() const { + return _emojiStatusId; +} + auto UserData::unavailableReasons() const -> const std::vector & { return _unavailableReasons; diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h index 3f1ac2f90..ef397d95e 100644 --- a/Telegram/SourceFiles/data/data_user.h +++ b/Telegram/SourceFiles/data/data_user.h @@ -71,6 +71,9 @@ public: const QString &newPhoneName, const QString &newUsername); + void setEmojiStatus(DocumentId emojiStatusId); + [[nodiscard]] DocumentId emojiStatusId() const; + void setPhone(const QString &newPhone); void setBotInfoVersion(int version); void setBotInfo(const MTPBotInfo &info); @@ -168,6 +171,8 @@ private: static constexpr auto kInaccessibleAccessHashOld = 0xFFFFFFFFFFFFFFFFULL; + DocumentId _emojiStatusId = 0; + }; namespace Data { diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.h b/Telegram/SourceFiles/dialogs/dialogs_entry.h index 2975a456b..970637635 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.h +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.h @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/flat_map.h" #include "dialogs/dialogs_key.h" +#include "ui/unread_badge.h" namespace Main { class Session; @@ -188,6 +189,9 @@ public: } [[nodiscard]] const Ui::Text::String &chatListNameText() const; + [[nodiscard]] Ui::PeerBadge &chatListBadge() const { + return _chatListBadge; + } protected: void notifyUnreadStateChange(const UnreadState &wasState); @@ -221,6 +225,7 @@ private: uint64 _sortKeyInChatList = 0; uint64 _sortKeyByDate = 0; base::flat_map _pinnedIndex; + mutable Ui::PeerBadge _chatListBadge; mutable Ui::Text::String _chatListNameText; mutable int _chatListNameVersion = 0; TimeId _timeId = 0; diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 166d8db7c..73991db15 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -122,6 +122,7 @@ struct InnerWidget::PeerSearchResult { } not_null peer; mutable Ui::Text::String name; + mutable Ui::PeerBadge badge; BasicRow row; }; @@ -614,7 +615,14 @@ void InnerWidget::paintEvent(QPaintEvent *e) { const auto selected = (from == (isPressed() ? _peerSearchPressed : _peerSearchSelected)); - paintPeerSearchResult(p, result.get(), fullWidth, active, selected); + paintPeerSearchResult( + p, + result.get(), + fullWidth, + active, + selected, + ms, + videoPaused); p.translate(0, st::dialogsRowHeight); } } @@ -767,7 +775,9 @@ void InnerWidget::paintPeerSearchResult( not_null result, int fullWidth, bool active, - bool selected) const { + bool selected, + crl::time now, + bool paused) { QRect fullRect(0, 0, fullWidth, st::dialogsRowHeight); p.fillRect(fullRect, active ? st::dialogsBgActive : (selected ? st::dialogsBgOver : st::dialogsBg)); if (!active) { @@ -794,29 +804,37 @@ void InnerWidget::paintPeerSearchResult( chatTypeIcon->paint(p, rectForName.topLeft(), fullWidth); rectForName.setLeft(rectForName.left() + st::dialogsChatTypeSkip); } - const auto badgeStyle = Ui::PeerBadgeStyle{ - (active - ? &st::dialogsVerifiedIconActive - : selected - ? &st::dialogsVerifiedIconOver - : &st::dialogsVerifiedIcon), - (active - ? &st::dialogsPremiumIconActive - : selected - ? &st::dialogsPremiumIconOver - : &st::dialogsPremiumIcon), - (active - ? &st::dialogsScamFgActive - : selected - ? &st::dialogsScamFgOver - : &st::dialogsScamFg) }; - const auto badgeWidth = Ui::DrawPeerBadgeGetWidth( - peer, + const auto badgeWidth = result->badge.drawGetWidth( p, rectForName, result->name.maxWidth(), fullWidth, - badgeStyle); + { + .peer = peer, + .verified = (active + ? &st::dialogsVerifiedIconActive + : selected + ? &st::dialogsVerifiedIconOver + : &st::dialogsVerifiedIcon), + .premium = (active + ? &st::dialogsPremiumIconActive + : selected + ? &st::dialogsPremiumIconOver + : &st::dialogsPremiumIcon), + .scam = (active + ? &st::dialogsScamFgActive + : selected + ? &st::dialogsScamFgOver + : &st::dialogsScamFg), + .preview = (active + ? st::dialogsScamFgActive + : selected + ? st::windowBgRipple + : st::windowBgOver)->c, + .customEmojiRepaint = [=] { updateSearchResult(peer); }, + .now = now, + .paused = paused, + }); rectForName.setWidth(rectForName.width() - badgeWidth); QRect tr(nameleft, st::dialogsPadding.y() + st::msgNameFont->height + st::dialogsSkip, namewidth, st::dialogsTextFont->height); @@ -1119,9 +1137,7 @@ void InnerWidget::mousePressEvent(QMouseEvent *e) { [this, peer = result->peer] { updateSearchResult(peer); }); } else if (base::in_range(_searchedPressed, 0, _searchResults.size())) { auto &row = _searchResults[_searchedPressed]; - row->addRipple(e->pos() - QPoint(0, searchedOffset() + _searchedPressed * st::dialogsRowHeight), QSize(width(), st::dialogsRowHeight), [this, index = _searchedPressed] { - rtlupdate(0, searchedOffset() + index * st::dialogsRowHeight, width(), st::dialogsRowHeight); - }); + row->addRipple(e->pos() - QPoint(0, searchedOffset() + _searchedPressed * st::dialogsRowHeight), QSize(width(), st::dialogsRowHeight), row->repaint()); } if (anim::Disabled() && (!_pressed || !_pressed->entry()->isPinnedDialog(_filterId))) { @@ -2122,10 +2138,12 @@ bool InnerWidget::searchReceived( && (!_searchInChat || inject->history() == _searchInChat.history())) { Assert(_searchResults.empty()); + const auto index = int(_searchResults.size()); _searchResults.push_back( std::make_unique( _searchInChat, - inject)); + inject, + [=] { repaintSearchResult(index); })); ++fullCount; } for (const auto &message : messages) { @@ -2140,10 +2158,12 @@ bool InnerWidget::searchReceived( NewMessageType::Existing); const auto history = item->history(); if (!uniquePeers || !hasHistoryInResults(history)) { + const auto index = int(_searchResults.size()); _searchResults.push_back( std::make_unique( _searchInChat, - item)); + item, + [=] { repaintSearchResult(index); })); if (uniquePeers && !history->unreadCountKnown()) { history->owner().histories().requestDialogEntry(history); } @@ -2459,6 +2479,14 @@ void InnerWidget::refreshSearchInChatLabel() { } } +void InnerWidget::repaintSearchResult(int index) { + rtlupdate( + 0, + searchedOffset() + index * st::dialogsRowHeight, + + width(), st::dialogsRowHeight); +} + void InnerWidget::clearFilter() { if (_state == WidgetState::Filtered || _searchInChat) { if (_searchInChat) { diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h index e7c248b39..75df037dd 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h @@ -294,7 +294,9 @@ private: not_null result, int fullWidth, bool active, - bool selected) const; + bool selected, + crl::time now, + bool paused); void paintSearchInChat(Painter &p) const; void paintSearchInPeer( Painter &p, @@ -318,6 +320,7 @@ private: const style::icon *icon, const Ui::Text::String &text) const; void refreshSearchInChatLabel(); + void repaintSearchResult(int index); Ui::VideoUserpic *validateVideoUserpic(not_null row); Ui::VideoUserpic *validateVideoUserpic(not_null history); diff --git a/Telegram/SourceFiles/dialogs/dialogs_row.cpp b/Telegram/SourceFiles/dialogs/dialogs_row.cpp index 164f3ce87..673932f3b 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_row.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_row.cpp @@ -339,9 +339,13 @@ void Row::paintUserpic( p.setOpacity(1.); } -FakeRow::FakeRow(Key searchInChat, not_null item) +FakeRow::FakeRow( + Key searchInChat, + not_null item, + Fn repaint) : _searchInChat(searchInChat) -, _item(item) { +, _item(item) +, _repaint(std::move(repaint)) { } const Ui::Text::String &FakeRow::name() const { diff --git a/Telegram/SourceFiles/dialogs/dialogs_row.h b/Telegram/SourceFiles/dialogs/dialogs_row.h index 94fec8115..2203e2ea0 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_row.h +++ b/Telegram/SourceFiles/dialogs/dialogs_row.h @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/text.h" #include "ui/effects/animations.h" +#include "ui/unread_badge.h" #include "dialogs/dialogs_key.h" #include "dialogs/ui/dialogs_message_view.h" @@ -147,7 +148,10 @@ private: class FakeRow : public BasicRow { public: - FakeRow(Key searchInChat, not_null item); + FakeRow( + Key searchInChat, + not_null item, + Fn repaint); [[nodiscard]] Key searchInChat() const { return _searchInChat; @@ -158,14 +162,22 @@ public: [[nodiscard]] Ui::MessageView &itemView() const { return _itemView; } + [[nodiscard]] Fn repaint() const { + return _repaint; + } + [[nodiscard]] Ui::PeerBadge &badge() const { + return _badge; + } [[nodiscard]] const Ui::Text::String &name() const; private: friend class Ui::RowPainter; - Key _searchInChat; - not_null _item; + const Key _searchInChat; + const not_null _item; + const Fn _repaint; mutable Ui::MessageView _itemView; + mutable Ui::PeerBadge _badge; mutable Ui::Text::String _name; }; diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp index 46f79c5d4..168ad6a87 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp @@ -311,6 +311,8 @@ void paintRow( VideoUserpic *videoUserpic, FilterId filterId, PeerData *from, + Ui::PeerBadge &fromBadge, + Fn customEmojiRepaint, const Ui::Text::String &fromName, const HiddenSenderInfo *hiddenSenderInfo, HistoryItem *item, @@ -474,9 +476,7 @@ void paintRow( Text::WithEntities); const auto context = Core::MarkedTextContext{ .session = &history->session(), - .customEmojiRepaint = [=] { - history->updateChatListEntry(); - }, + .customEmojiRepaint = customEmojiRepaint, }; history->cloudDraftTextCache.setMarkedText( st::dialogsTextStyle, @@ -570,30 +570,38 @@ void paintRow( : st::dialogsNameFg); p.drawTextLeft(rectForName.left(), rectForName.top(), fullWidth, text); } else if (from) { - if (!(flags & Flag::SearchResult)) { - const auto badgeStyle = PeerBadgeStyle{ - (active - ? &st::dialogsVerifiedIconActive - : selected - ? &st::dialogsVerifiedIconOver - : &st::dialogsVerifiedIcon), - (active - ? &st::dialogsPremiumIconActive - : selected - ? &st::dialogsPremiumIconOver - : &st::dialogsPremiumIcon), - (active - ? &st::dialogsScamFgActive - : selected - ? &st::dialogsScamFgOver - : &st::dialogsScamFg) }; - const auto badgeWidth = DrawPeerBadgeGetWidth( - from, + if (history && !(flags & Flag::SearchResult)) { + const auto badgeWidth = fromBadge.drawGetWidth( p, rectForName, fromName.maxWidth(), fullWidth, - badgeStyle); + { + .peer = from, + .verified = (active + ? &st::dialogsVerifiedIconActive + : selected + ? &st::dialogsVerifiedIconOver + : &st::dialogsVerifiedIcon), + .premium = (active + ? &st::dialogsPremiumIconActive + : selected + ? &st::dialogsPremiumIconOver + : &st::dialogsPremiumIcon), + .scam = (active + ? &st::dialogsScamFgActive + : selected + ? &st::dialogsScamFgOver + : &st::dialogsScamFg), + .preview = (active + ? st::dialogsScamFgActive + : selected + ? st::windowBgRipple + : st::windowBgOver)->c, + .customEmojiRepaint = customEmojiRepaint, + .now = ms, + .paused = bool(flags & Flag::VideoPaused), + }); rectForName.setWidth(rectForName.width() - badgeWidth); } p.setPen(active @@ -911,7 +919,7 @@ void RowPainter::paint( : (selected ? st::dialogsTextFgServiceOver : st::dialogsTextFgService); - const auto itemRect = QRect( + const auto rect = QRect( nameleft, texttop, availableWidth, @@ -919,23 +927,23 @@ void RowPainter::paint( const auto actionWasPainted = ShowSendActionInDialogs(history) ? history->sendActionPainter()->paint( p, - itemRect.x(), - itemRect.y(), - itemRect.width(), + rect.x(), + rect.y(), + rect.width(), fullWidth, color, ms) : false; if (const auto folder = row->folder()) { - PaintListEntryText(p, itemRect, active, selected, row); + PaintListEntryText(p, rect, active, selected, row); } else if (history && !actionWasPainted) { - history->lastItemDialogsView.paint( - p, - item, - itemRect, - active, - selected, - {}); + if (!history->lastItemDialogsView.prepared(item)) { + history->lastItemDialogsView.prepare( + item, + [=] { history->updateChatListEntry(); }, + {}); + } + history->lastItemDialogsView.paint(p, rect, active, selected); } }; const auto paintCounterCallback = [&] { @@ -959,6 +967,8 @@ void RowPainter::paint( videoUserpic, filterId, from, + entry->chatListBadge(), + [=] { history->updateChatListEntry(); }, entry->chatListNameText(), nullptr, item, @@ -1056,13 +1066,11 @@ void RowPainter::paint( texttop, availableWidth, st::dialogsTextFont->height); - row->itemView().paint( - p, - item, - itemRect, - active, - selected, - previewOptions); + auto &view = row->itemView(); + if (!view.prepared(item)) { + view.prepare(item, row->repaint(), previewOptions); + } + row->itemView().paint(p, itemRect, active, selected); }; const auto paintCounterCallback = [&] { PaintNarrowCounter( @@ -1094,6 +1102,8 @@ void RowPainter::paint( nullptr, FilterId(), from, + row->badge(), + row->repaint(), row->name(), hiddenSenderInfo, item, diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp index 77aa50d3e..874bb18cc 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp @@ -109,63 +109,67 @@ bool MessageView::dependsOn(not_null item) const { return (_textCachedFor == item.get()); } +bool MessageView::prepared(not_null item) const { + return (_textCachedFor == item.get()); +} + +void MessageView::prepare( + not_null item, + Fn customEmojiRepaint, + ToPreviewOptions options) { + options.existing = &_imagesCache; + auto preview = item->toPreview(options); + if (!preview.images.empty() && preview.imagesInTextPosition > 0) { + auto sender = ::Ui::Text::Mid( + preview.text, + 0, + preview.imagesInTextPosition); + TextUtilities::Trim(sender); + _senderCache.setMarkedText( + st::dialogsTextStyle, + std::move(sender), + DialogTextOptions()); + preview.text = ::Ui::Text::Mid( + preview.text, + preview.imagesInTextPosition); + } else { + _senderCache = { st::dialogsTextWidthMin }; + } + TextUtilities::Trim(preview.text); + const auto history = item->history(); + const auto context = Core::MarkedTextContext{ + .session = &history->session(), + .customEmojiRepaint = customEmojiRepaint, + }; + _textCache.setMarkedText( + st::dialogsTextStyle, + DialogsPreviewText(std::move(preview.text)), + DialogTextOptions(), + context); + _textCachedFor = item; + _imagesCache = std::move(preview.images); + if (preview.loadingContext.has_value()) { + if (!_loadingContext) { + _loadingContext = std::make_unique(); + item->history()->session().downloaderTaskFinished( + ) | rpl::start_with_next([=] { + _textCachedFor = nullptr; + }, _loadingContext->lifetime); + } + _loadingContext->context = std::move(preview.loadingContext); + } else { + _loadingContext = nullptr; + } +} + void MessageView::paint( Painter &p, - not_null item, const QRect &geometry, bool active, - bool selected, - ToPreviewOptions options) const { + bool selected) const { if (geometry.isEmpty()) { return; } - if (_textCachedFor != item.get()) { - options.existing = &_imagesCache; - auto preview = item->toPreview(options); - if (!preview.images.empty() && preview.imagesInTextPosition > 0) { - auto sender = ::Ui::Text::Mid( - preview.text, - 0, - preview.imagesInTextPosition); - TextUtilities::Trim(sender); - _senderCache.setMarkedText( - st::dialogsTextStyle, - std::move(sender), - DialogTextOptions()); - preview.text = ::Ui::Text::Mid( - preview.text, - preview.imagesInTextPosition); - } else { - _senderCache = { st::dialogsTextWidthMin }; - } - TextUtilities::Trim(preview.text); - const auto history = item->history(); - const auto context = Core::MarkedTextContext{ - .session = &history->session(), - .customEmojiRepaint = [=] { - history->updateChatListEntry(); - }, - }; - _textCache.setMarkedText( - st::dialogsTextStyle, - DialogsPreviewText(std::move(preview.text)), - DialogTextOptions(), - context); - _textCachedFor = item; - _imagesCache = std::move(preview.images); - if (preview.loadingContext.has_value()) { - if (!_loadingContext) { - _loadingContext = std::make_unique(); - item->history()->session().downloaderTaskFinished( - ) | rpl::start_with_next([=] { - _textCachedFor = nullptr; - }, _loadingContext->lifetime); - } - _loadingContext->context = std::move(preview.loadingContext); - } else { - _loadingContext = nullptr; - } - } p.setTextPalette(active ? st::dialogsTextPaletteActive : selected diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.h b/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.h index 7cbb706e3..6b7ee2dea 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.h +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.h @@ -40,13 +40,16 @@ public: void itemInvalidated(not_null item); [[nodiscard]] bool dependsOn(not_null item) const; + [[nodiscard]] bool prepared(not_null item) const; + void prepare( + not_null item, + Fn customEmojiRepaint, + ToPreviewOptions options); void paint( Painter &p, - not_null item, const QRect &geometry, bool active, - bool selected, - ToPreviewOptions options) const; + bool selected) const; private: struct LoadingContext; diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_search.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_search.cpp index c5c33fdf4..c28f8f399 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_search.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_search.cpp @@ -176,8 +176,14 @@ void ListController::addItems(const MessageIdsList &ids, bool clear) { const auto key = Dialogs::Key{ _history }; for (const auto &id : ids) { if (const auto item = owner.message(id)) { - delegate()->peerListAppendRow(std::make_unique( - std::make_unique(key, item))); + const auto shared = std::make_shared(nullptr); + auto row = std::make_unique( + std::make_unique( + key, + item, + [=] { delegate()->peerListUpdateRow(*shared); })); + *shared = row.get(); + delegate()->peerListAppendRow(std::move(row)); } } diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp index 2f0c42a45..0639937f7 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp @@ -526,13 +526,7 @@ void TopBarWidget::paintTopBar(Painter &p) { peer->topBarNameText(), Ui::NameTextOptions()); } - const auto badgeStyle = Ui::PeerBadgeStyle{ - &st::dialogsVerifiedIcon, - &st::dialogsPremiumIcon, - &st::attentionButtonFg, - }; - const auto badgeWidth = Ui::DrawPeerBadgeGetWidth( - peer, + const auto badgeWidth = _titleBadge.drawGetWidth( p, QRect( nameleft, @@ -541,7 +535,17 @@ void TopBarWidget::paintTopBar(Painter &p) { st::msgNameStyle.font->height), _title.maxWidth(), width(), - badgeStyle); + { + .peer = peer, + .verified = &st::dialogsVerifiedIcon, + .premium = &st::dialogsPremiumIcon, + .scam = &st::attentionButtonFg, + .preview = st::windowBgOver->c, + .customEmojiRepaint = [=] { update(); }, + .now = now, + .paused = _controller->isGifPausedAtLeastFor( + Window::GifPauseReason::Any), + }); const auto namewidth = availableWidth - badgeWidth; p.setPen(st::dialogsNameFg); @@ -698,6 +702,7 @@ void TopBarWidget::setActiveChat( update(); if (peerChanged) { + _titleBadge.unload(); _titleNameVersion = 0; _emojiInteractionSeen = nullptr; _activeChatLifetime.destroy(); diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h index 30db1eefa..7e0f3016b 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "ui/rp_widget.h" +#include "ui/unread_badge.h" #include "ui/effects/animations.h" #include "base/timer.h" #include "base/object_ptr.h" @@ -159,6 +160,7 @@ private: std::unique_ptr _emojiInteractionSeen; rpl::lifetime _activeChatLifetime; + Ui::PeerBadge _titleBadge; Ui::Text::String _title; int _titleNameVersion = 0; diff --git a/Telegram/SourceFiles/ui/unread_badge.cpp b/Telegram/SourceFiles/ui/unread_badge.cpp index 0f4e91bdd..b149ad734 100644 --- a/Telegram/SourceFiles/ui/unread_badge.cpp +++ b/Telegram/SourceFiles/ui/unread_badge.cpp @@ -8,6 +8,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/unread_badge.h" #include "data/data_peer.h" +#include "data/data_user.h" +#include "data/data_session.h" +#include "data/stickers/data_custom_emoji.h" #include "main/main_session.h" #include "dialogs/ui/dialogs_layout.h" #include "lang/lang_keys.h" @@ -103,14 +106,20 @@ void DrawScamBadge( st::dialogsScamFont->width(phrase)); } -int DrawPeerBadgeGetWidth( - not_null peer, +PeerBadge::PeerBadge() = default; + +PeerBadge::~PeerBadge() = default; + +int PeerBadge::drawGetWidth( Painter &p, QRect rectForName, int nameWidth, int outerWidth, - const PeerBadgeStyle &st) { - if ((peer->isScam() || peer->isFake()) && st.scam) { + const Descriptor &descriptor) { + Expects(descriptor.customEmojiRepaint != nullptr); + + const auto peer = descriptor.peer; + if ((peer->isScam() || peer->isFake()) && descriptor.scam) { const auto phrase = peer->isScam() ? tr::lng_scam_badge(tr::now) : tr::lng_fake_badge(tr::now); @@ -129,28 +138,62 @@ int DrawPeerBadgeGetWidth( rectForName.y() + (rectForName.height() - height) / 2, width, height); - DrawScamFakeBadge(p, rect, outerWidth, *st.scam, phrase, phraseWidth); + DrawScamFakeBadge( + p, + rect, + outerWidth, + *descriptor.scam, + phrase, + phraseWidth); return st::dialogsScamSkip + width; - } else if (peer->isVerified() && st.verified) { - const auto iconw = st.verified->width(); - st.verified->paint( + } else if (peer->isVerified() && descriptor.verified) { + const auto iconw = descriptor.verified->width(); + descriptor.verified->paint( p, rectForName.x() + qMin(nameWidth, rectForName.width() - iconw), rectForName.y(), outerWidth); return iconw; } else if (peer->isPremium() - && st.premium + && descriptor.premium && peer->session().premiumBadgesShown()) { - const auto iconw = st.premium->width(); - st.premium->paint( + const auto id = peer->isUser() ? peer->asUser()->emojiStatusId() : 0; + const auto iconw = descriptor.premium->width(); + const auto iconx = rectForName.x() + + qMin(nameWidth, rectForName.width() - iconw); + const auto icony = rectForName.y(); + if (!id) { + _emojiStatus = nullptr; + descriptor.premium->paint(p, iconx, icony, outerWidth); + return iconw; + } + if (!_emojiStatus) { + _emojiStatus = std::make_unique(); + const auto size = st::emojiSize * 1.; + const auto emoji = Ui::Text::AdjustCustomEmojiSize(st::emojiSize); + _emojiStatus->skip = (st::emojiSize - emoji) / 2; + } + if (_emojiStatus->id != id) { + auto &manager = peer->session().data().customEmojiManager(); + _emojiStatus->id = id; + _emojiStatus->emoji = manager.create( + id, + descriptor.customEmojiRepaint); + } + _emojiStatus->emoji->paint( p, - rectForName.x() + qMin(nameWidth, rectForName.width() - iconw), - rectForName.y(), - outerWidth); - return iconw; + iconx - _emojiStatus->skip, + icony + _emojiStatus->skip, + descriptor.now, + descriptor.preview, + descriptor.paused); + return iconw - 2 * _emojiStatus->skip; } return 0; } +void PeerBadge::unload() { + _emojiStatus = nullptr; +} + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/unread_badge.h b/Telegram/SourceFiles/ui/unread_badge.h index 7fd64c932..1614c2ec5 100644 --- a/Telegram/SourceFiles/ui/unread_badge.h +++ b/Telegram/SourceFiles/ui/unread_badge.h @@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/rp_widget.h" +namespace Ui::Text { +class CustomEmoji; +} // namespace Ui::Text + namespace Ui { class UnreadBadge : public RpWidget { @@ -27,18 +31,39 @@ private: }; -struct PeerBadgeStyle { - const style::icon *verified = nullptr; - const style::icon *premium = nullptr; - const style::color *scam = nullptr; +class PeerBadge { +public: + PeerBadge(); + ~PeerBadge(); + + struct Descriptor { + not_null peer; + const style::icon *verified = nullptr; + const style::icon *premium = nullptr; + const style::color *scam = nullptr; + QColor preview; + Fn customEmojiRepaint; + crl::time now = 0; + bool paused = false; + }; + int drawGetWidth( + Painter &p, + QRect rectForName, + int nameWidth, + int outerWidth, + const Descriptor &descriptor); + void unload(); + +private: + struct EmojiStatus { + DocumentId id = 0; + std::unique_ptr emoji; + int skip = 0; + }; + std::unique_ptr _emojiStatus; + }; -int DrawPeerBadgeGetWidth( - not_null peer, - Painter &p, - QRect rectForName, - int nameWidth, - int outerWidth, - const PeerBadgeStyle &st); + QSize ScamBadgeSize(bool fake); void DrawScamBadge( bool fake,