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),
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<Flag>;
friend inline constexpr auto is_flag_type(Flag) { return true; }

View file

@ -400,6 +400,11 @@ ChannelData *Session::channelLoaded(ChannelId id) const {
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) {
const auto result = user(data.match([](const auto &data) {
return data.vid().v;
@ -554,6 +559,26 @@ not_null<UserData*> 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<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
-> const std::vector<Data::UnavailableReason> & {
return _unavailableReasons;

View file

@ -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 {

View file

@ -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<FilterId, int> _pinnedIndex;
mutable Ui::PeerBadge _chatListBadge;
mutable Ui::Text::String _chatListNameText;
mutable int _chatListNameVersion = 0;
TimeId _timeId = 0;

View file

@ -122,6 +122,7 @@ struct InnerWidget::PeerSearchResult {
}
not_null<PeerData*> 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<const PeerSearchResult*> 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<FakeRow>(
_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<FakeRow>(
_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) {

View file

@ -294,7 +294,9 @@ private:
not_null<const PeerSearchResult*> 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*> row);
Ui::VideoUserpic *validateVideoUserpic(not_null<History*> history);

View file

@ -339,9 +339,13 @@ void Row::paintUserpic(
p.setOpacity(1.);
}
FakeRow::FakeRow(Key searchInChat, not_null<HistoryItem*> item)
FakeRow::FakeRow(
Key searchInChat,
not_null<HistoryItem*> item,
Fn<void()> repaint)
: _searchInChat(searchInChat)
, _item(item) {
, _item(item)
, _repaint(std::move(repaint)) {
}
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/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<HistoryItem*> item);
FakeRow(
Key searchInChat,
not_null<HistoryItem*> item,
Fn<void()> repaint);
[[nodiscard]] Key searchInChat() const {
return _searchInChat;
@ -158,14 +162,22 @@ public:
[[nodiscard]] Ui::MessageView &itemView() const {
return _itemView;
}
[[nodiscard]] Fn<void()> 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<HistoryItem*> _item;
const Key _searchInChat;
const not_null<HistoryItem*> _item;
const Fn<void()> _repaint;
mutable Ui::MessageView _itemView;
mutable Ui::PeerBadge _badge;
mutable Ui::Text::String _name;
};

View file

@ -311,6 +311,8 @@ void paintRow(
VideoUserpic *videoUserpic,
FilterId filterId,
PeerData *from,
Ui::PeerBadge &fromBadge,
Fn<void()> 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,

View file

@ -109,63 +109,67 @@ bool MessageView::dependsOn(not_null<const HistoryItem*> item) const {
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(
Painter &p,
not_null<const HistoryItem*> 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<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
? st::dialogsTextPaletteActive
: selected

View file

@ -40,13 +40,16 @@ public:
void itemInvalidated(not_null<const HistoryItem*> item);
[[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(
Painter &p,
not_null<const HistoryItem*> item,
const QRect &geometry,
bool active,
bool selected,
ToPreviewOptions options) const;
bool selected) const;
private:
struct LoadingContext;

View file

@ -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<Row>(
std::make_unique<Dialogs::FakeRow>(key, item)));
const auto shared = std::make_shared<Row*>(nullptr);
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(),
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();

View file

@ -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<EmojiInteractionSeenAnimation> _emojiInteractionSeen;
rpl::lifetime _activeChatLifetime;
Ui::PeerBadge _titleBadge;
Ui::Text::String _title;
int _titleNameVersion = 0;

View file

@ -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<PeerData*> 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<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,
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

View file

@ -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<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);
void DrawScamBadge(
bool fake,