Implement dates of who read your message list.

This commit is contained in:
John Preston 2023-03-03 17:15:02 +04:00 committed by 23rd
parent af51307aa6
commit b95ea28e12
15 changed files with 214 additions and 122 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 471 B

After

Width:  |  Height:  |  Size: 414 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 720 B

After

Width:  |  Height:  |  Size: 812 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,008 B

View file

@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_media_types.h" #include "data/data_media_types.h"
#include "data/data_message_reaction_id.h" #include "data/data_message_reaction_id.h"
#include "lang/lang_keys.h"
#include "main/main_app_config.h" #include "main/main_app_config.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "main/main_account.h" #include "main/main_account.h"
@ -36,37 +37,33 @@ constexpr auto kContextReactionsLimit = 50;
using Data::ReactionId; using Data::ReactionId;
struct Peers { struct Peers {
std::vector<PeerId> list; std::vector<WhoReadPeer> list;
bool unknown = false; bool unknown = false;
friend inline bool operator==(
const Peers &a,
const Peers &b) noexcept = default;
}; };
inline bool operator==(const Peers &a, const Peers &b) noexcept {
return (a.list == b.list) && (a.unknown == b.unknown);
}
struct PeerWithReaction { struct PeerWithReaction {
PeerId peer = 0; WhoReadPeer peerWithDate;
ReactionId reaction; ReactionId reaction;
};
inline bool operator==( friend inline bool operator==(
const PeerWithReaction &a, const PeerWithReaction &a,
const PeerWithReaction &b) noexcept { const PeerWithReaction &b) noexcept = default;
return (a.peer == b.peer) && (a.reaction == b.reaction); };
}
struct PeersWithReactions { struct PeersWithReactions {
std::vector<PeerWithReaction> list; std::vector<PeerWithReaction> list;
std::vector<PeerId> read; std::vector<WhoReadPeer> read;
int fullReactionsCount = 0; int fullReactionsCount = 0;
bool unknown = false; bool unknown = false;
};
inline bool operator==( friend inline bool operator==(
const PeersWithReactions &a, const PeersWithReactions &a,
const PeersWithReactions &b) noexcept { const PeersWithReactions &b) noexcept = default;
return (a.fullReactionsCount == b.fullReactionsCount) };
&& (a.list == b.list)
&& (a.read == b.read)
&& (a.unknown == b.unknown);
}
struct CachedRead { struct CachedRead {
CachedRead() CachedRead()
@ -113,6 +110,7 @@ struct Context {
struct Userpic { struct Userpic {
not_null<PeerData*> peer; not_null<PeerData*> peer;
TimeId date = 0;
QString customEntityData; QString customEntityData;
mutable Ui::PeerUserpicView view; mutable Ui::PeerUserpicView view;
mutable InMemoryKey uniqueKey; mutable InMemoryKey uniqueKey;
@ -234,7 +232,10 @@ struct State {
auto parsed = Peers(); auto parsed = Peers();
parsed.list.reserve(result.v.size()); parsed.list.reserve(result.v.size());
for (const auto &id : result.v) { for (const auto &id : result.v) {
parsed.list.push_back(UserId(id.data().vuser_id())); parsed.list.push_back({
.peer = UserId(id.data().vuser_id()),
.date = id.data().vdate().v,
});
} }
entry.data = std::move(parsed); entry.data = std::move(parsed);
}).fail([=] { }).fail([=] {
@ -252,8 +253,8 @@ struct State {
[[nodiscard]] PeersWithReactions WithEmptyReactions( [[nodiscard]] PeersWithReactions WithEmptyReactions(
Peers &&peers) { Peers &&peers) {
auto result = PeersWithReactions{ auto result = PeersWithReactions{
.list = peers.list | ranges::views::transform([](PeerId peer) { .list = peers.list | ranges::views::transform([](WhoReadPeer peer) {
return PeerWithReaction{ .peer = peer }; return PeerWithReaction{ .peerWithDate = peer };
}) | ranges::to_vector, }) | ranges::to_vector,
.unknown = peers.unknown, .unknown = peers.unknown,
}; };
@ -302,7 +303,9 @@ struct State {
for (const auto &vote : data.vreactions().v) { for (const auto &vote : data.vreactions().v) {
vote.match([&](const auto &data) { vote.match([&](const auto &data) {
parsed.list.push_back(PeerWithReaction{ parsed.list.push_back(PeerWithReaction{
.peer = peerFromMTP(data.vpeer_id()), .peerWithDate = {
.peer = peerFromMTP(data.vpeer_id()),
},
.reaction = Data::ReactionFromMTP( .reaction = Data::ReactionFromMTP(
data.vreaction()), data.vreaction()),
}); });
@ -334,9 +337,16 @@ struct State {
return PeersWithReactions{ .unknown = true }; return PeersWithReactions{ .unknown = true };
} }
auto &list = reacted.list; auto &list = reacted.list;
for (const auto &peer : read.list) { for (const auto &peerWithDate : read.list) {
if (!ranges::contains(list, peer, &PeerWithReaction::peer)) { const auto i = ranges::find(
list.push_back({ .peer = peer }); list,
peerWithDate.peer,
[](const PeerWithReaction &p) {
return p.peerWithDate.peer; });
if (i != end(list)) {
i->peerWithDate.date = peerWithDate.date;
} else {
list.push_back({ .peerWithDate = peerWithDate });
} }
} }
reacted.read = std::move(read.list); reacted.read = std::move(read.list);
@ -344,6 +354,37 @@ struct State {
}); });
} }
[[nodiscard]] QString FormatReadDate(TimeId date, const QDateTime &now) {
if (!date) {
return {};
}
const auto parsed = base::unixtime::parse(date);
const auto readDate = parsed.date();
const auto nowDate = now.date();
if (readDate == nowDate) {
return tr::lng_mediaview_today(
tr::now,
lt_time,
QLocale().toString(parsed.time(), QLocale::ShortFormat));
} else if (readDate.addDays(1) == nowDate) {
return tr::lng_mediaview_yesterday(
tr::now,
lt_time,
QLocale().toString(parsed.time(), QLocale::ShortFormat));
}
return tr::lng_mediaview_date_time(
tr::now,
lt_date,
tr::lng_month_day(
tr::now,
lt_month,
Lang::MonthDay(readDate.month())(tr::now),
lt_day,
QString::number(readDate.day())),
lt_time,
QLocale().toString(parsed.time(), QLocale::ShortFormat));
}
bool UpdateUserpics( bool UpdateUserpics(
not_null<State*> state, not_null<State*> state,
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
@ -352,13 +393,15 @@ bool UpdateUserpics(
struct ResolvedPeer { struct ResolvedPeer {
PeerData *peer = nullptr; PeerData *peer = nullptr;
TimeId date = 0;
ReactionId reaction; ReactionId reaction;
}; };
const auto peers = ranges::views::all( const auto peers = ranges::views::all(
ids ids
) | ranges::views::transform([&](PeerWithReaction id) { ) | ranges::views::transform([&](PeerWithReaction id) {
return ResolvedPeer{ return ResolvedPeer{
.peer = owner.peerLoaded(id.peer), .peer = owner.peerLoaded(id.peerWithDate.peer),
.date = id.peerWithDate.date,
.reaction = id.reaction, .reaction = id.reaction,
}; };
}) | ranges::views::filter([](ResolvedPeer resolved) { }) | ranges::views::filter([](ResolvedPeer resolved) {
@ -369,8 +412,8 @@ bool UpdateUserpics(
state->userpics, state->userpics,
peers, peers,
ranges::equal_to(), ranges::equal_to(),
&Userpic::peer, [](const Userpic &u) { return std::pair(u.peer.get(), u.date); },
[](const ResolvedPeer &r) { return not_null{ r.peer }; }); [](const ResolvedPeer &r) { return std::pair(r.peer, r.date); });
if (same) { if (same) {
return false; return false;
} }
@ -381,12 +424,14 @@ bool UpdateUserpics(
const auto &data = ReactionEntityData(resolved.reaction); const auto &data = ReactionEntityData(resolved.reaction);
const auto i = ranges::find(was, peer, &Userpic::peer); const auto i = ranges::find(was, peer, &Userpic::peer);
if (i != end(was) && i->view.cloud) { if (i != end(was) && i->view.cloud) {
i->date = resolved.date;
now.push_back(std::move(*i)); now.push_back(std::move(*i));
now.back().customEntityData = data; now.back().customEntityData = data;
continue; continue;
} }
now.push_back(Userpic{ now.push_back(Userpic{
.peer = peer, .peer = peer,
.date = resolved.date,
.customEntityData = data, .customEntityData = data,
}); });
auto &userpic = now.back(); auto &userpic = now.back();
@ -422,20 +467,24 @@ void RegenerateUserpics(not_null<State*> state, int small, int large) {
} }
void RegenerateParticipants(not_null<State*> state, int small, int large) { void RegenerateParticipants(not_null<State*> state, int small, int large) {
const auto currentDate = QDateTime::currentDateTime();
auto old = base::take(state->current.participants); auto old = base::take(state->current.participants);
auto &now = state->current.participants; auto &now = state->current.participants;
now.reserve(state->userpics.size()); now.reserve(state->userpics.size());
for (auto &userpic : state->userpics) { for (auto &userpic : state->userpics) {
const auto peer = userpic.peer; const auto peer = userpic.peer;
const auto date = userpic.date;
const auto id = peer->id.value; const auto id = peer->id.value;
const auto was = ranges::find(old, id, &Ui::WhoReadParticipant::id); const auto was = ranges::find(old, id, &Ui::WhoReadParticipant::id);
if (was != end(old)) { if (was != end(old)) {
was->name = peer->name(); was->name = peer->name();
was->date = FormatReadDate(date, currentDate);
now.push_back(std::move(*was)); now.push_back(std::move(*was));
continue; continue;
} }
now.push_back({ now.push_back({
.name = peer->name(), .name = peer->name(),
.date = FormatReadDate(date, currentDate),
.customEntityData = userpic.customEntityData, .customEntityData = userpic.customEntityData,
.userpicLarge = GenerateUserpic(userpic, large), .userpicLarge = GenerateUserpic(userpic, large),
.userpicKey = userpic.uniqueKey, .userpicKey = userpic.uniqueKey,
@ -522,7 +571,7 @@ rpl::producer<Ui::WhoReadContent> WhoReacted(
&PeerWithReaction::reaction); &PeerWithReaction::reaction);
whoReadIds->list = (peers.read.size() > reacted) whoReadIds->list = (peers.read.size() > reacted)
? std::move(peers.read) ? std::move(peers.read)
: std::vector<PeerId>(); : std::vector<WhoReadPeer>();
} }
if (UpdateUserpics(state, item, peers.list)) { if (UpdateUserpics(state, item, peers.list)) {
RegenerateParticipants(state, small, large); RegenerateParticipants(state, small, large);

View file

@ -34,8 +34,17 @@ enum class WhoReactedList {
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
WhoReactedList list); WhoReactedList list);
struct WhoReadPeer {
PeerId peer = 0;
TimeId date = 0;
friend inline bool operator==(
const WhoReadPeer &a,
const WhoReadPeer &b) noexcept = default;
};
struct WhoReadList { struct WhoReadList {
std::vector<PeerId> list; std::vector<WhoReadPeer> list;
Ui::WhoReadType type = {}; Ui::WhoReadType type = {};
}; };

View file

@ -250,8 +250,8 @@ uint64 Controller::id(
void Controller::fillWhoRead() { void Controller::fillWhoRead() {
if (_whoReadIds && !_whoReadIds->list.empty() && _whoRead.empty()) { if (_whoReadIds && !_whoReadIds->list.empty() && _whoRead.empty()) {
auto &owner = _window->session().data(); auto &owner = _window->session().data();
for (const auto &peerId : _whoReadIds->list) { for (const auto &peerWithDate : _whoReadIds->list) {
if (const auto peer = owner.peerLoaded(peerId)) { if (const auto peer = owner.peerLoaded(peerWithDate.peer)) {
_whoRead.push_back(peer); _whoRead.push_back(peer);
} }
} }

View file

@ -52,72 +52,7 @@ inline QString langDateMaybeWithYear(
return withoutYear(month, year); return withoutYear(month, year);
} }
tr::phrase<> Month(int index) { using namespace Lang;
switch (index) {
case 1: return tr::lng_month1;
case 2: return tr::lng_month2;
case 3: return tr::lng_month3;
case 4: return tr::lng_month4;
case 5: return tr::lng_month5;
case 6: return tr::lng_month6;
case 7: return tr::lng_month7;
case 8: return tr::lng_month8;
case 9: return tr::lng_month9;
case 10: return tr::lng_month10;
case 11: return tr::lng_month11;
case 12: return tr::lng_month12;
}
Unexpected("Index in MonthSmall.");
}
tr::phrase<> MonthSmall(int index) {
switch (index) {
case 1: return tr::lng_month1_small;
case 2: return tr::lng_month2_small;
case 3: return tr::lng_month3_small;
case 4: return tr::lng_month4_small;
case 5: return tr::lng_month5_small;
case 6: return tr::lng_month6_small;
case 7: return tr::lng_month7_small;
case 8: return tr::lng_month8_small;
case 9: return tr::lng_month9_small;
case 10: return tr::lng_month10_small;
case 11: return tr::lng_month11_small;
case 12: return tr::lng_month12_small;
}
Unexpected("Index in MonthSmall.");
}
tr::phrase<> MonthDay(int index) {
switch (index) {
case 1: return tr::lng_month_day1;
case 2: return tr::lng_month_day2;
case 3: return tr::lng_month_day3;
case 4: return tr::lng_month_day4;
case 5: return tr::lng_month_day5;
case 6: return tr::lng_month_day6;
case 7: return tr::lng_month_day7;
case 8: return tr::lng_month_day8;
case 9: return tr::lng_month_day9;
case 10: return tr::lng_month_day10;
case 11: return tr::lng_month_day11;
case 12: return tr::lng_month_day12;
}
Unexpected("Index in MonthDay.");
}
tr::phrase<> Weekday(int index) {
switch (index) {
case 1: return tr::lng_weekday1;
case 2: return tr::lng_weekday2;
case 3: return tr::lng_weekday3;
case 4: return tr::lng_weekday4;
case 5: return tr::lng_weekday5;
case 6: return tr::lng_weekday6;
case 7: return tr::lng_weekday7;
}
Unexpected("Index in Weekday.");
}
} // namespace } // namespace
@ -245,4 +180,71 @@ QString LanguageIdOrDefault(const QString &id) {
return !id.isEmpty() ? id : DefaultLanguageId(); return !id.isEmpty() ? id : DefaultLanguageId();
} }
tr::phrase<> Month(int index) {
switch (index) {
case 1: return tr::lng_month1;
case 2: return tr::lng_month2;
case 3: return tr::lng_month3;
case 4: return tr::lng_month4;
case 5: return tr::lng_month5;
case 6: return tr::lng_month6;
case 7: return tr::lng_month7;
case 8: return tr::lng_month8;
case 9: return tr::lng_month9;
case 10: return tr::lng_month10;
case 11: return tr::lng_month11;
case 12: return tr::lng_month12;
}
Unexpected("Index in MonthSmall.");
}
tr::phrase<> MonthSmall(int index) {
switch (index) {
case 1: return tr::lng_month1_small;
case 2: return tr::lng_month2_small;
case 3: return tr::lng_month3_small;
case 4: return tr::lng_month4_small;
case 5: return tr::lng_month5_small;
case 6: return tr::lng_month6_small;
case 7: return tr::lng_month7_small;
case 8: return tr::lng_month8_small;
case 9: return tr::lng_month9_small;
case 10: return tr::lng_month10_small;
case 11: return tr::lng_month11_small;
case 12: return tr::lng_month12_small;
}
Unexpected("Index in MonthSmall.");
}
tr::phrase<> MonthDay(int index) {
switch (index) {
case 1: return tr::lng_month_day1;
case 2: return tr::lng_month_day2;
case 3: return tr::lng_month_day3;
case 4: return tr::lng_month_day4;
case 5: return tr::lng_month_day5;
case 6: return tr::lng_month_day6;
case 7: return tr::lng_month_day7;
case 8: return tr::lng_month_day8;
case 9: return tr::lng_month_day9;
case 10: return tr::lng_month_day10;
case 11: return tr::lng_month_day11;
case 12: return tr::lng_month_day12;
}
Unexpected("Index in MonthDay.");
}
tr::phrase<> Weekday(int index) {
switch (index) {
case 1: return tr::lng_weekday1;
case 2: return tr::lng_weekday2;
case 3: return tr::lng_weekday3;
case 4: return tr::lng_weekday4;
case 5: return tr::lng_weekday5;
case 6: return tr::lng_weekday6;
case 7: return tr::lng_weekday7;
}
Unexpected("Index in Weekday.");
}
} // namespace Lang } // namespace Lang

View file

@ -37,4 +37,9 @@ namespace Lang {
[[nodiscard]] QString DefaultLanguageId(); [[nodiscard]] QString DefaultLanguageId();
[[nodiscard]] QString LanguageIdOrDefault(const QString &id); [[nodiscard]] QString LanguageIdOrDefault(const QString &id);
[[nodiscard]] tr::phrase<> Month(int index);
[[nodiscard]] tr::phrase<> MonthSmall(int index);
[[nodiscard]] tr::phrase<> MonthDay(int index);
[[nodiscard]] tr::phrase<> Weekday(int index);
} // namespace Lang } // namespace Lang

View file

@ -23,29 +23,11 @@ namespace {
constexpr auto kMinimalSchedule = TimeId(10); constexpr auto kMinimalSchedule = TimeId(10);
tr::phrase<> MonthDay(int index) {
switch (index) {
case 1: return tr::lng_month_day1;
case 2: return tr::lng_month_day2;
case 3: return tr::lng_month_day3;
case 4: return tr::lng_month_day4;
case 5: return tr::lng_month_day5;
case 6: return tr::lng_month_day6;
case 7: return tr::lng_month_day7;
case 8: return tr::lng_month_day8;
case 9: return tr::lng_month_day9;
case 10: return tr::lng_month_day10;
case 11: return tr::lng_month_day11;
case 12: return tr::lng_month_day12;
}
Unexpected("Index in MonthDay.");
}
QString DayString(const QDate &date) { QString DayString(const QDate &date) {
return tr::lng_month_day( return tr::lng_month_day(
tr::now, tr::now,
lt_month, lt_month,
MonthDay(date.month())(tr::now), Lang::MonthDay(date.month())(tr::now),
lt_day, lt_day,
QString::number(date.day())); QString::number(date.day()));
} }

View file

@ -1095,6 +1095,17 @@ whoReadMenu: PopupMenu(popupMenuExpandedSeparator) {
scrollPadding: margins(0px, 6px, 0px, 4px); scrollPadding: margins(0px, 6px, 0px, 4px);
maxHeight: 400px; maxHeight: 400px;
} }
whoReadNameWithDateTop: 3px;
whoReadDateTop: 20px;
whoReadDateSkip: 15px;
whoReadDateChecks: icon{{ "menu/read_ticks_s", windowSubTextFg }};
whoReadDateChecksOver: icon{{ "menu/read_ticks_s", windowSubTextFgOver }};
whoReadDateChecksPosition: point(-7px, -4px);
whoReadDateStyle: TextStyle(defaultTextStyle) {
font: font(12px);
linkFont: font(12px);
linkFontOver: font(12px underline);
}
whoReadChecks: icon{{ "menu/read_ticks", windowBoldFg }}; whoReadChecks: icon{{ "menu/read_ticks", windowBoldFg }};
whoReadChecksOver: icon{{ "menu/read_ticks", windowBoldFg }}; whoReadChecksOver: icon{{ "menu/read_ticks", windowBoldFg }};
whoReadChecksDisabled: icon{{ "menu/read_ticks", menuFgDisabled }}; whoReadChecksDisabled: icon{{ "menu/read_ticks", menuFgDisabled }};

View file

@ -73,6 +73,7 @@ using Text::CustomEmojiFactory;
struct EntryData { struct EntryData {
QString text; QString text;
QString date;
QString customEntityData; QString customEntityData;
QImage userpic; QImage userpic;
Fn<void()> callback; Fn<void()> callback;
@ -287,7 +288,7 @@ void Action::updateUserpicsFromContent() {
} }
void Action::populateSubmenu() { void Action::populateSubmenu() {
if (_content.participants.size() < 2) { if (_content.participants.size() < 1) {
_submenu.clear(); _submenu.clear();
_parentMenu->removeSubmenu(action()); _parentMenu->removeSubmenu(action());
if (!isEnabled()) { if (!isEnabled()) {
@ -487,6 +488,7 @@ private:
const int _height = 0; const int _height = 0;
Text::String _text; Text::String _text;
Text::String _date;
std::unique_ptr<Ui::Text::CustomEmoji> _custom; std::unique_ptr<Ui::Text::CustomEmoji> _custom;
QImage _userpic; QImage _userpic;
int _textWidth = 0; int _textWidth = 0;
@ -533,11 +535,21 @@ void WhoReactedListMenu::EntryAction::setData(EntryData &&data) {
setClickedCallback(std::move(data.callback)); setClickedCallback(std::move(data.callback));
_userpic = std::move(data.userpic); _userpic = std::move(data.userpic);
_text.setMarkedText(_st.itemStyle, { data.text }, MenuTextOptions); _text.setMarkedText(_st.itemStyle, { data.text }, MenuTextOptions);
if (data.date.isEmpty()) {
_date = Text::String();
} else {
_date.setMarkedText(
st::whoReadDateStyle,
{ data.date },
MenuTextOptions);
}
_custom = _customEmojiFactory(data.customEntityData, [=] { update(); }); _custom = _customEmojiFactory(data.customEntityData, [=] { update(); });
const auto ratio = style::DevicePixelRatio(); const auto ratio = style::DevicePixelRatio();
const auto size = Emoji::GetSizeNormal() / ratio; const auto size = Emoji::GetSizeNormal() / ratio;
_customSize = Text::AdjustCustomEmojiSize(size); _customSize = Text::AdjustCustomEmojiSize(size);
const auto textWidth = _text.maxWidth(); const auto textWidth = std::max(
_text.maxWidth(),
st::whoReadDateSkip + _date.maxWidth());
const auto &padding = _st.itemPadding; const auto &padding = _st.itemPadding;
const auto rightSkip = padding.right() const auto rightSkip = padding.right()
+ (_custom ? (size + padding.right()) : 0); + (_custom ? (size + padding.right()) : 0);
@ -571,6 +583,10 @@ void WhoReactedListMenu::EntryAction::paint(Painter &&p) {
QRect(photoLeft, photoTop, photoSize, photoSize)); QRect(photoLeft, photoTop, photoSize, photoSize));
} }
const auto withDate = !_date.isEmpty();
const auto textTop = withDate
? st::whoReadNameWithDateTop
: (height() - _st.itemStyle.font->height) / 2;
p.setPen(selected p.setPen(selected
? _st.itemFgOver ? _st.itemFgOver
: enabled : enabled
@ -579,10 +595,25 @@ void WhoReactedListMenu::EntryAction::paint(Painter &&p) {
_text.drawLeftElided( _text.drawLeftElided(
p, p,
st::defaultWhoRead.nameLeft, st::defaultWhoRead.nameLeft,
(height() - _st.itemStyle.font->height) / 2, textTop,
_textWidth, _textWidth,
width()); width());
if (withDate) {
const auto iconPosition = QPoint(
st::defaultWhoRead.nameLeft,
st::whoReadDateTop) + st::whoReadDateChecksPosition;
const auto &icon = selected
? st::whoReadDateChecksOver
: st::whoReadDateChecks;
icon.paint(p, iconPosition, width());
p.setPen(selected ? _st.itemFgShortcutOver : _st.itemFgShortcut);
_date.drawLeftElided(
p,
st::defaultWhoRead.nameLeft + st::whoReadDateSkip,
st::whoReadDateTop,
_textWidth - st::whoReadDateSkip,
width());
}
if (_custom) { if (_custom) {
const auto ratio = style::DevicePixelRatio(); const auto ratio = style::DevicePixelRatio();
const auto size = Emoji::GetSizeNormal() / ratio; const auto size = Emoji::GetSizeNormal() / ratio;
@ -600,6 +631,7 @@ void WhoReactedListMenu::EntryAction::paint(Painter &&p) {
bool operator==(const WhoReadParticipant &a, const WhoReadParticipant &b) { bool operator==(const WhoReadParticipant &a, const WhoReadParticipant &b) {
return (a.id == b.id) return (a.id == b.id)
&& (a.name == b.name) && (a.name == b.name)
&& (a.date == b.date)
&& (a.userpicKey == b.userpicKey); && (a.userpicKey == b.userpicKey);
} }
@ -680,6 +712,7 @@ void WhoReactedListMenu::populate(
}; };
append({ append({
.text = participant.name, .text = participant.name,
.date = participant.date,
.customEntityData = participant.customEntityData, .customEntityData = participant.customEntityData,
.userpic = participant.userpicLarge, .userpic = participant.userpicLarge,
.callback = chosen, .callback = chosen,

View file

@ -19,6 +19,7 @@ class PopupMenu;
struct WhoReadParticipant { struct WhoReadParticipant {
QString name; QString name;
QString date;
QString customEntityData; QString customEntityData;
QImage userpicSmall; QImage userpicSmall;
QImage userpicLarge; QImage userpicLarge;