Show emoji status in chats list / top bar.

This commit is contained in:
John Preston 2022-08-09 18:53:40 +03:00
parent 285ce81b7b
commit 21fd381778
17 changed files with 386 additions and 177 deletions

View file

@ -78,26 +78,27 @@ struct PeerUpdate {
HasCalls = (1ULL << 19), HasCalls = (1ULL << 19),
SupportInfo = (1ULL << 20), SupportInfo = (1ULL << 20),
IsBot = (1ULL << 21), IsBot = (1ULL << 21),
EmojiStatus = (1ULL << 22),
// For chats and channels // For chats and channels
InviteLinks = (1ULL << 22), InviteLinks = (1ULL << 23),
Members = (1ULL << 23), Members = (1ULL << 24),
Admins = (1ULL << 24), Admins = (1ULL << 25),
BannedUsers = (1ULL << 25), BannedUsers = (1ULL << 26),
Rights = (1ULL << 26), Rights = (1ULL << 27),
PendingRequests = (1ULL << 27), PendingRequests = (1ULL << 28),
Reactions = (1ULL << 28), Reactions = (1ULL << 29),
// For channels // For channels
ChannelAmIn = (1ULL << 29), ChannelAmIn = (1ULL << 30),
StickersSet = (1ULL << 30), StickersSet = (1ULL << 31),
ChannelLinkedChat = (1ULL << 31), ChannelLinkedChat = (1ULL << 32),
ChannelLocation = (1ULL << 32), ChannelLocation = (1ULL << 33),
Slowmode = (1ULL << 33), Slowmode = (1ULL << 34),
GroupCall = (1ULL << 34), GroupCall = (1ULL << 35),
// For iteration // For iteration
LastUsedBit = (1ULL << 34), LastUsedBit = (1ULL << 35),
}; };
using Flags = base::flags<Flag>; using Flags = base::flags<Flag>;
friend inline constexpr auto is_flag_type(Flag) { return true; } friend inline constexpr auto is_flag_type(Flag) { return true; }

View file

@ -400,6 +400,11 @@ ChannelData *Session::channelLoaded(ChannelId id) const {
return nullptr; return nullptr;
} }
AssertIsDebug();
base::flat_map<
not_null<Data::Session*>,
base::flat_set<not_null<DocumentData*>>> Emojis;
not_null<UserData*> Session::processUser(const MTPUser &data) { not_null<UserData*> Session::processUser(const MTPUser &data) {
const auto result = user(data.match([](const auto &data) { const auto result = user(data.match([](const auto &data) {
return data.vid().v; return data.vid().v;
@ -554,6 +559,26 @@ not_null<UserData*> Session::processUser(const MTPUser &data) {
} }
status = data.vstatus(); 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 (!minimal) {
if (const auto botInfoVersion = data.vbot_info_version()) { if (const auto botInfoVersion = data.vbot_info_version()) {
result->setBotInfoVersion(botInfoVersion->v); result->setBotInfoVersion(botInfoVersion->v);
@ -2943,6 +2968,23 @@ void Session::documentApplyFields(
if (dc != 0 && access != 0) { if (dc != 0 && access != 0) {
document->setRemoteLocation(dc, access, fileReference); 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<WebPageData*> Session::webpage(WebPageId id) { not_null<WebPageData*> Session::webpage(WebPageId id) {

View file

@ -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 auto UserData::unavailableReasons() const
-> const std::vector<Data::UnavailableReason> & { -> const std::vector<Data::UnavailableReason> & {
return _unavailableReasons; return _unavailableReasons;

View file

@ -71,6 +71,9 @@ public:
const QString &newPhoneName, const QString &newPhoneName,
const QString &newUsername); const QString &newUsername);
void setEmojiStatus(DocumentId emojiStatusId);
[[nodiscard]] DocumentId emojiStatusId() const;
void setPhone(const QString &newPhone); void setPhone(const QString &newPhone);
void setBotInfoVersion(int version); void setBotInfoVersion(int version);
void setBotInfo(const MTPBotInfo &info); void setBotInfo(const MTPBotInfo &info);
@ -168,6 +171,8 @@ private:
static constexpr auto kInaccessibleAccessHashOld static constexpr auto kInaccessibleAccessHashOld
= 0xFFFFFFFFFFFFFFFFULL; = 0xFFFFFFFFFFFFFFFFULL;
DocumentId _emojiStatusId = 0;
}; };
namespace Data { namespace Data {

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/flat_map.h" #include "base/flat_map.h"
#include "dialogs/dialogs_key.h" #include "dialogs/dialogs_key.h"
#include "ui/unread_badge.h"
namespace Main { namespace Main {
class Session; class Session;
@ -188,6 +189,9 @@ public:
} }
[[nodiscard]] const Ui::Text::String &chatListNameText() const; [[nodiscard]] const Ui::Text::String &chatListNameText() const;
[[nodiscard]] Ui::PeerBadge &chatListBadge() const {
return _chatListBadge;
}
protected: protected:
void notifyUnreadStateChange(const UnreadState &wasState); void notifyUnreadStateChange(const UnreadState &wasState);
@ -221,6 +225,7 @@ private:
uint64 _sortKeyInChatList = 0; uint64 _sortKeyInChatList = 0;
uint64 _sortKeyByDate = 0; uint64 _sortKeyByDate = 0;
base::flat_map<FilterId, int> _pinnedIndex; base::flat_map<FilterId, int> _pinnedIndex;
mutable Ui::PeerBadge _chatListBadge;
mutable Ui::Text::String _chatListNameText; mutable Ui::Text::String _chatListNameText;
mutable int _chatListNameVersion = 0; mutable int _chatListNameVersion = 0;
TimeId _timeId = 0; TimeId _timeId = 0;

View file

@ -122,6 +122,7 @@ struct InnerWidget::PeerSearchResult {
} }
not_null<PeerData*> peer; not_null<PeerData*> peer;
mutable Ui::Text::String name; mutable Ui::Text::String name;
mutable Ui::PeerBadge badge;
BasicRow row; BasicRow row;
}; };
@ -614,7 +615,14 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
const auto selected = (from == (isPressed() const auto selected = (from == (isPressed()
? _peerSearchPressed ? _peerSearchPressed
: _peerSearchSelected)); : _peerSearchSelected));
paintPeerSearchResult(p, result.get(), fullWidth, active, selected); paintPeerSearchResult(
p,
result.get(),
fullWidth,
active,
selected,
ms,
videoPaused);
p.translate(0, st::dialogsRowHeight); p.translate(0, st::dialogsRowHeight);
} }
} }
@ -767,7 +775,9 @@ void InnerWidget::paintPeerSearchResult(
not_null<const PeerSearchResult*> result, not_null<const PeerSearchResult*> result,
int fullWidth, int fullWidth,
bool active, bool active,
bool selected) const { bool selected,
crl::time now,
bool paused) {
QRect fullRect(0, 0, fullWidth, st::dialogsRowHeight); QRect fullRect(0, 0, fullWidth, st::dialogsRowHeight);
p.fillRect(fullRect, active ? st::dialogsBgActive : (selected ? st::dialogsBgOver : st::dialogsBg)); p.fillRect(fullRect, active ? st::dialogsBgActive : (selected ? st::dialogsBgOver : st::dialogsBg));
if (!active) { if (!active) {
@ -794,29 +804,37 @@ void InnerWidget::paintPeerSearchResult(
chatTypeIcon->paint(p, rectForName.topLeft(), fullWidth); chatTypeIcon->paint(p, rectForName.topLeft(), fullWidth);
rectForName.setLeft(rectForName.left() + st::dialogsChatTypeSkip); rectForName.setLeft(rectForName.left() + st::dialogsChatTypeSkip);
} }
const auto badgeStyle = Ui::PeerBadgeStyle{ const auto badgeWidth = result->badge.drawGetWidth(
(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,
p, p,
rectForName, rectForName,
result->name.maxWidth(), result->name.maxWidth(),
fullWidth, 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); rectForName.setWidth(rectForName.width() - badgeWidth);
QRect tr(nameleft, st::dialogsPadding.y() + st::msgNameFont->height + st::dialogsSkip, namewidth, st::dialogsTextFont->height); 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); }); [this, peer = result->peer] { updateSearchResult(peer); });
} else if (base::in_range(_searchedPressed, 0, _searchResults.size())) { } else if (base::in_range(_searchedPressed, 0, _searchResults.size())) {
auto &row = _searchResults[_searchedPressed]; auto &row = _searchResults[_searchedPressed];
row->addRipple(e->pos() - QPoint(0, searchedOffset() + _searchedPressed * st::dialogsRowHeight), QSize(width(), st::dialogsRowHeight), [this, index = _searchedPressed] { row->addRipple(e->pos() - QPoint(0, searchedOffset() + _searchedPressed * st::dialogsRowHeight), QSize(width(), st::dialogsRowHeight), row->repaint());
rtlupdate(0, searchedOffset() + index * st::dialogsRowHeight, width(), st::dialogsRowHeight);
});
} }
if (anim::Disabled() if (anim::Disabled()
&& (!_pressed || !_pressed->entry()->isPinnedDialog(_filterId))) { && (!_pressed || !_pressed->entry()->isPinnedDialog(_filterId))) {
@ -2122,10 +2138,12 @@ bool InnerWidget::searchReceived(
&& (!_searchInChat && (!_searchInChat
|| inject->history() == _searchInChat.history())) { || inject->history() == _searchInChat.history())) {
Assert(_searchResults.empty()); Assert(_searchResults.empty());
const auto index = int(_searchResults.size());
_searchResults.push_back( _searchResults.push_back(
std::make_unique<FakeRow>( std::make_unique<FakeRow>(
_searchInChat, _searchInChat,
inject)); inject,
[=] { repaintSearchResult(index); }));
++fullCount; ++fullCount;
} }
for (const auto &message : messages) { for (const auto &message : messages) {
@ -2140,10 +2158,12 @@ bool InnerWidget::searchReceived(
NewMessageType::Existing); NewMessageType::Existing);
const auto history = item->history(); const auto history = item->history();
if (!uniquePeers || !hasHistoryInResults(history)) { if (!uniquePeers || !hasHistoryInResults(history)) {
const auto index = int(_searchResults.size());
_searchResults.push_back( _searchResults.push_back(
std::make_unique<FakeRow>( std::make_unique<FakeRow>(
_searchInChat, _searchInChat,
item)); item,
[=] { repaintSearchResult(index); }));
if (uniquePeers && !history->unreadCountKnown()) { if (uniquePeers && !history->unreadCountKnown()) {
history->owner().histories().requestDialogEntry(history); 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() { void InnerWidget::clearFilter() {
if (_state == WidgetState::Filtered || _searchInChat) { if (_state == WidgetState::Filtered || _searchInChat) {
if (_searchInChat) { if (_searchInChat) {

View file

@ -294,7 +294,9 @@ private:
not_null<const PeerSearchResult*> result, not_null<const PeerSearchResult*> result,
int fullWidth, int fullWidth,
bool active, bool active,
bool selected) const; bool selected,
crl::time now,
bool paused);
void paintSearchInChat(Painter &p) const; void paintSearchInChat(Painter &p) const;
void paintSearchInPeer( void paintSearchInPeer(
Painter &p, Painter &p,
@ -318,6 +320,7 @@ private:
const style::icon *icon, const style::icon *icon,
const Ui::Text::String &text) const; const Ui::Text::String &text) const;
void refreshSearchInChatLabel(); void refreshSearchInChatLabel();
void repaintSearchResult(int index);
Ui::VideoUserpic *validateVideoUserpic(not_null<Row*> row); Ui::VideoUserpic *validateVideoUserpic(not_null<Row*> row);
Ui::VideoUserpic *validateVideoUserpic(not_null<History*> history); Ui::VideoUserpic *validateVideoUserpic(not_null<History*> history);

View file

@ -339,9 +339,13 @@ void Row::paintUserpic(
p.setOpacity(1.); p.setOpacity(1.);
} }
FakeRow::FakeRow(Key searchInChat, not_null<HistoryItem*> item) FakeRow::FakeRow(
Key searchInChat,
not_null<HistoryItem*> item,
Fn<void()> repaint)
: _searchInChat(searchInChat) : _searchInChat(searchInChat)
, _item(item) { , _item(item)
, _repaint(std::move(repaint)) {
} }
const Ui::Text::String &FakeRow::name() const { const Ui::Text::String &FakeRow::name() const {

View file

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/text/text.h" #include "ui/text/text.h"
#include "ui/effects/animations.h" #include "ui/effects/animations.h"
#include "ui/unread_badge.h"
#include "dialogs/dialogs_key.h" #include "dialogs/dialogs_key.h"
#include "dialogs/ui/dialogs_message_view.h" #include "dialogs/ui/dialogs_message_view.h"
@ -147,7 +148,10 @@ private:
class FakeRow : public BasicRow { class FakeRow : public BasicRow {
public: public:
FakeRow(Key searchInChat, not_null<HistoryItem*> item); FakeRow(
Key searchInChat,
not_null<HistoryItem*> item,
Fn<void()> repaint);
[[nodiscard]] Key searchInChat() const { [[nodiscard]] Key searchInChat() const {
return _searchInChat; return _searchInChat;
@ -158,14 +162,22 @@ public:
[[nodiscard]] Ui::MessageView &itemView() const { [[nodiscard]] Ui::MessageView &itemView() const {
return _itemView; return _itemView;
} }
[[nodiscard]] Fn<void()> repaint() const {
return _repaint;
}
[[nodiscard]] Ui::PeerBadge &badge() const {
return _badge;
}
[[nodiscard]] const Ui::Text::String &name() const; [[nodiscard]] const Ui::Text::String &name() const;
private: private:
friend class Ui::RowPainter; friend class Ui::RowPainter;
Key _searchInChat; const Key _searchInChat;
not_null<HistoryItem*> _item; const not_null<HistoryItem*> _item;
const Fn<void()> _repaint;
mutable Ui::MessageView _itemView; mutable Ui::MessageView _itemView;
mutable Ui::PeerBadge _badge;
mutable Ui::Text::String _name; mutable Ui::Text::String _name;
}; };

View file

@ -311,6 +311,8 @@ void paintRow(
VideoUserpic *videoUserpic, VideoUserpic *videoUserpic,
FilterId filterId, FilterId filterId,
PeerData *from, PeerData *from,
Ui::PeerBadge &fromBadge,
Fn<void()> customEmojiRepaint,
const Ui::Text::String &fromName, const Ui::Text::String &fromName,
const HiddenSenderInfo *hiddenSenderInfo, const HiddenSenderInfo *hiddenSenderInfo,
HistoryItem *item, HistoryItem *item,
@ -474,9 +476,7 @@ void paintRow(
Text::WithEntities); Text::WithEntities);
const auto context = Core::MarkedTextContext{ const auto context = Core::MarkedTextContext{
.session = &history->session(), .session = &history->session(),
.customEmojiRepaint = [=] { .customEmojiRepaint = customEmojiRepaint,
history->updateChatListEntry();
},
}; };
history->cloudDraftTextCache.setMarkedText( history->cloudDraftTextCache.setMarkedText(
st::dialogsTextStyle, st::dialogsTextStyle,
@ -570,30 +570,38 @@ void paintRow(
: st::dialogsNameFg); : st::dialogsNameFg);
p.drawTextLeft(rectForName.left(), rectForName.top(), fullWidth, text); p.drawTextLeft(rectForName.left(), rectForName.top(), fullWidth, text);
} else if (from) { } else if (from) {
if (!(flags & Flag::SearchResult)) { if (history && !(flags & Flag::SearchResult)) {
const auto badgeStyle = PeerBadgeStyle{ const auto badgeWidth = fromBadge.drawGetWidth(
(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,
p, p,
rectForName, rectForName,
fromName.maxWidth(), fromName.maxWidth(),
fullWidth, 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); rectForName.setWidth(rectForName.width() - badgeWidth);
} }
p.setPen(active p.setPen(active
@ -911,7 +919,7 @@ void RowPainter::paint(
: (selected : (selected
? st::dialogsTextFgServiceOver ? st::dialogsTextFgServiceOver
: st::dialogsTextFgService); : st::dialogsTextFgService);
const auto itemRect = QRect( const auto rect = QRect(
nameleft, nameleft,
texttop, texttop,
availableWidth, availableWidth,
@ -919,23 +927,23 @@ void RowPainter::paint(
const auto actionWasPainted = ShowSendActionInDialogs(history) const auto actionWasPainted = ShowSendActionInDialogs(history)
? history->sendActionPainter()->paint( ? history->sendActionPainter()->paint(
p, p,
itemRect.x(), rect.x(),
itemRect.y(), rect.y(),
itemRect.width(), rect.width(),
fullWidth, fullWidth,
color, color,
ms) ms)
: false; : false;
if (const auto folder = row->folder()) { if (const auto folder = row->folder()) {
PaintListEntryText(p, itemRect, active, selected, row); PaintListEntryText(p, rect, active, selected, row);
} else if (history && !actionWasPainted) { } else if (history && !actionWasPainted) {
history->lastItemDialogsView.paint( if (!history->lastItemDialogsView.prepared(item)) {
p, history->lastItemDialogsView.prepare(
item, item,
itemRect, [=] { history->updateChatListEntry(); },
active, {});
selected, }
{}); history->lastItemDialogsView.paint(p, rect, active, selected);
} }
}; };
const auto paintCounterCallback = [&] { const auto paintCounterCallback = [&] {
@ -959,6 +967,8 @@ void RowPainter::paint(
videoUserpic, videoUserpic,
filterId, filterId,
from, from,
entry->chatListBadge(),
[=] { history->updateChatListEntry(); },
entry->chatListNameText(), entry->chatListNameText(),
nullptr, nullptr,
item, item,
@ -1056,13 +1066,11 @@ void RowPainter::paint(
texttop, texttop,
availableWidth, availableWidth,
st::dialogsTextFont->height); st::dialogsTextFont->height);
row->itemView().paint( auto &view = row->itemView();
p, if (!view.prepared(item)) {
item, view.prepare(item, row->repaint(), previewOptions);
itemRect, }
active, row->itemView().paint(p, itemRect, active, selected);
selected,
previewOptions);
}; };
const auto paintCounterCallback = [&] { const auto paintCounterCallback = [&] {
PaintNarrowCounter( PaintNarrowCounter(
@ -1094,6 +1102,8 @@ void RowPainter::paint(
nullptr, nullptr,
FilterId(), FilterId(),
from, from,
row->badge(),
row->repaint(),
row->name(), row->name(),
hiddenSenderInfo, hiddenSenderInfo,
item, item,

View file

@ -109,63 +109,67 @@ bool MessageView::dependsOn(not_null<const HistoryItem*> item) const {
return (_textCachedFor == item.get()); return (_textCachedFor == item.get());
} }
bool MessageView::prepared(not_null<const HistoryItem*> item) const {
return (_textCachedFor == item.get());
}
void MessageView::prepare(
not_null<const HistoryItem*> item,
Fn<void()> 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<LoadingContext>();
item->history()->session().downloaderTaskFinished(
) | rpl::start_with_next([=] {
_textCachedFor = nullptr;
}, _loadingContext->lifetime);
}
_loadingContext->context = std::move(preview.loadingContext);
} else {
_loadingContext = nullptr;
}
}
void MessageView::paint( void MessageView::paint(
Painter &p, Painter &p,
not_null<const HistoryItem*> item,
const QRect &geometry, const QRect &geometry,
bool active, bool active,
bool selected, bool selected) const {
ToPreviewOptions options) const {
if (geometry.isEmpty()) { if (geometry.isEmpty()) {
return; 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<LoadingContext>();
item->history()->session().downloaderTaskFinished(
) | rpl::start_with_next([=] {
_textCachedFor = nullptr;
}, _loadingContext->lifetime);
}
_loadingContext->context = std::move(preview.loadingContext);
} else {
_loadingContext = nullptr;
}
}
p.setTextPalette(active p.setTextPalette(active
? st::dialogsTextPaletteActive ? st::dialogsTextPaletteActive
: selected : selected

View file

@ -40,13 +40,16 @@ public:
void itemInvalidated(not_null<const HistoryItem*> item); void itemInvalidated(not_null<const HistoryItem*> item);
[[nodiscard]] bool dependsOn(not_null<const HistoryItem*> item) const; [[nodiscard]] bool dependsOn(not_null<const HistoryItem*> item) const;
[[nodiscard]] bool prepared(not_null<const HistoryItem*> item) const;
void prepare(
not_null<const HistoryItem*> item,
Fn<void()> customEmojiRepaint,
ToPreviewOptions options);
void paint( void paint(
Painter &p, Painter &p,
not_null<const HistoryItem*> item,
const QRect &geometry, const QRect &geometry,
bool active, bool active,
bool selected, bool selected) const;
ToPreviewOptions options) const;
private: private:
struct LoadingContext; struct LoadingContext;

View file

@ -176,8 +176,14 @@ void ListController::addItems(const MessageIdsList &ids, bool clear) {
const auto key = Dialogs::Key{ _history }; const auto key = Dialogs::Key{ _history };
for (const auto &id : ids) { for (const auto &id : ids) {
if (const auto item = owner.message(id)) { if (const auto item = owner.message(id)) {
delegate()->peerListAppendRow(std::make_unique<Row>( const auto shared = std::make_shared<Row*>(nullptr);
std::make_unique<Dialogs::FakeRow>(key, item))); auto row = std::make_unique<Row>(
std::make_unique<Dialogs::FakeRow>(
key,
item,
[=] { delegate()->peerListUpdateRow(*shared); }));
*shared = row.get();
delegate()->peerListAppendRow(std::move(row));
} }
} }

View file

@ -526,13 +526,7 @@ void TopBarWidget::paintTopBar(Painter &p) {
peer->topBarNameText(), peer->topBarNameText(),
Ui::NameTextOptions()); Ui::NameTextOptions());
} }
const auto badgeStyle = Ui::PeerBadgeStyle{ const auto badgeWidth = _titleBadge.drawGetWidth(
&st::dialogsVerifiedIcon,
&st::dialogsPremiumIcon,
&st::attentionButtonFg,
};
const auto badgeWidth = Ui::DrawPeerBadgeGetWidth(
peer,
p, p,
QRect( QRect(
nameleft, nameleft,
@ -541,7 +535,17 @@ void TopBarWidget::paintTopBar(Painter &p) {
st::msgNameStyle.font->height), st::msgNameStyle.font->height),
_title.maxWidth(), _title.maxWidth(),
width(), 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; const auto namewidth = availableWidth - badgeWidth;
p.setPen(st::dialogsNameFg); p.setPen(st::dialogsNameFg);
@ -698,6 +702,7 @@ void TopBarWidget::setActiveChat(
update(); update();
if (peerChanged) { if (peerChanged) {
_titleBadge.unload();
_titleNameVersion = 0; _titleNameVersion = 0;
_emojiInteractionSeen = nullptr; _emojiInteractionSeen = nullptr;
_activeChatLifetime.destroy(); _activeChatLifetime.destroy();

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once #pragma once
#include "ui/rp_widget.h" #include "ui/rp_widget.h"
#include "ui/unread_badge.h"
#include "ui/effects/animations.h" #include "ui/effects/animations.h"
#include "base/timer.h" #include "base/timer.h"
#include "base/object_ptr.h" #include "base/object_ptr.h"
@ -159,6 +160,7 @@ private:
std::unique_ptr<EmojiInteractionSeenAnimation> _emojiInteractionSeen; std::unique_ptr<EmojiInteractionSeenAnimation> _emojiInteractionSeen;
rpl::lifetime _activeChatLifetime; rpl::lifetime _activeChatLifetime;
Ui::PeerBadge _titleBadge;
Ui::Text::String _title; Ui::Text::String _title;
int _titleNameVersion = 0; int _titleNameVersion = 0;

View file

@ -8,6 +8,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/unread_badge.h" #include "ui/unread_badge.h"
#include "data/data_peer.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 "main/main_session.h"
#include "dialogs/ui/dialogs_layout.h" #include "dialogs/ui/dialogs_layout.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
@ -103,14 +106,20 @@ void DrawScamBadge(
st::dialogsScamFont->width(phrase)); st::dialogsScamFont->width(phrase));
} }
int DrawPeerBadgeGetWidth( PeerBadge::PeerBadge() = default;
not_null<PeerData*> peer,
PeerBadge::~PeerBadge() = default;
int PeerBadge::drawGetWidth(
Painter &p, Painter &p,
QRect rectForName, QRect rectForName,
int nameWidth, int nameWidth,
int outerWidth, int outerWidth,
const PeerBadgeStyle &st) { const Descriptor &descriptor) {
if ((peer->isScam() || peer->isFake()) && st.scam) { Expects(descriptor.customEmojiRepaint != nullptr);
const auto peer = descriptor.peer;
if ((peer->isScam() || peer->isFake()) && descriptor.scam) {
const auto phrase = peer->isScam() const auto phrase = peer->isScam()
? tr::lng_scam_badge(tr::now) ? tr::lng_scam_badge(tr::now)
: tr::lng_fake_badge(tr::now); : tr::lng_fake_badge(tr::now);
@ -129,28 +138,62 @@ int DrawPeerBadgeGetWidth(
rectForName.y() + (rectForName.height() - height) / 2, rectForName.y() + (rectForName.height() - height) / 2,
width, width,
height); height);
DrawScamFakeBadge(p, rect, outerWidth, *st.scam, phrase, phraseWidth); DrawScamFakeBadge(
p,
rect,
outerWidth,
*descriptor.scam,
phrase,
phraseWidth);
return st::dialogsScamSkip + width; return st::dialogsScamSkip + width;
} else if (peer->isVerified() && st.verified) { } else if (peer->isVerified() && descriptor.verified) {
const auto iconw = st.verified->width(); const auto iconw = descriptor.verified->width();
st.verified->paint( descriptor.verified->paint(
p, p,
rectForName.x() + qMin(nameWidth, rectForName.width() - iconw), rectForName.x() + qMin(nameWidth, rectForName.width() - iconw),
rectForName.y(), rectForName.y(),
outerWidth); outerWidth);
return iconw; return iconw;
} else if (peer->isPremium() } else if (peer->isPremium()
&& st.premium && descriptor.premium
&& peer->session().premiumBadgesShown()) { && peer->session().premiumBadgesShown()) {
const auto iconw = st.premium->width(); const auto id = peer->isUser() ? peer->asUser()->emojiStatusId() : 0;
st.premium->paint( 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<EmojiStatus>();
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, p,
rectForName.x() + qMin(nameWidth, rectForName.width() - iconw), iconx - _emojiStatus->skip,
rectForName.y(), icony + _emojiStatus->skip,
outerWidth); descriptor.now,
return iconw; descriptor.preview,
descriptor.paused);
return iconw - 2 * _emojiStatus->skip;
} }
return 0; return 0;
} }
void PeerBadge::unload() {
_emojiStatus = nullptr;
}
} // namespace Ui } // namespace Ui

View file

@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/rp_widget.h" #include "ui/rp_widget.h"
namespace Ui::Text {
class CustomEmoji;
} // namespace Ui::Text
namespace Ui { namespace Ui {
class UnreadBadge : public RpWidget { class UnreadBadge : public RpWidget {
@ -27,18 +31,39 @@ private:
}; };
struct PeerBadgeStyle { class PeerBadge {
const style::icon *verified = nullptr; public:
const style::icon *premium = nullptr; PeerBadge();
const style::color *scam = nullptr; ~PeerBadge();
struct Descriptor {
not_null<PeerData*> peer;
const style::icon *verified = nullptr;
const style::icon *premium = nullptr;
const style::color *scam = nullptr;
QColor preview;
Fn<void()> 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<Ui::Text::CustomEmoji> emoji;
int skip = 0;
};
std::unique_ptr<EmojiStatus> _emojiStatus;
}; };
int DrawPeerBadgeGetWidth(
not_null<PeerData*> peer,
Painter &p,
QRect rectForName,
int nameWidth,
int outerWidth,
const PeerBadgeStyle &st);
QSize ScamBadgeSize(bool fake); QSize ScamBadgeSize(bool fake);
void DrawScamBadge( void DrawScamBadge(
bool fake, bool fake,