mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 14:17:12 +02:00
Show userpics in who read context item.
This commit is contained in:
parent
8f480b52e7
commit
14314df26a
10 changed files with 302 additions and 181 deletions
|
@ -21,17 +21,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "main/main_session.h"
|
||||
#include "main/main_account.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "base/weak_ptr.h"
|
||||
#include "ui/controls/who_read_context_action.h"
|
||||
#include "apiwrap.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
|
||||
struct Cached {
|
||||
explicit Cached(UserId unknownFlag)
|
||||
: list(std::vector<UserId>{ unknownFlag }) {
|
||||
explicit Cached(PeerId unknownFlag)
|
||||
: list(std::vector<PeerId>{ unknownFlag }) {
|
||||
}
|
||||
rpl::variable<std::vector<UserId>> list;
|
||||
rpl::variable<std::vector<PeerId>> list;
|
||||
mtpRequestId requestId = 0;
|
||||
};
|
||||
|
||||
|
@ -46,11 +48,25 @@ struct Context {
|
|||
}
|
||||
return cached.emplace(
|
||||
item,
|
||||
Cached(item->history()->session().userId())
|
||||
Cached(item->history()->session().userPeerId())
|
||||
).first->second;
|
||||
}
|
||||
};
|
||||
|
||||
struct Userpic {
|
||||
not_null<PeerData*> peer;
|
||||
mutable std::shared_ptr<Data::CloudImageView> view;
|
||||
mutable InMemoryKey uniqueKey;
|
||||
};
|
||||
|
||||
struct State {
|
||||
std::vector<Userpic> userpics;
|
||||
Ui::WhoReadContent current;
|
||||
base::has_weak_ptr guard;
|
||||
bool someUserpicsNotLoaded = false;
|
||||
bool scheduled = false;
|
||||
};
|
||||
|
||||
[[nodiscard]] auto Contexts()
|
||||
-> base::flat_map<not_null<QWidget*>, std::unique_ptr<Context>> & {
|
||||
static auto result = base::flat_map<
|
||||
|
@ -82,10 +98,10 @@ struct Context {
|
|||
}
|
||||
|
||||
[[nodiscard]] bool ListUnknown(
|
||||
const std::vector<UserId> &list,
|
||||
const std::vector<PeerId> &list,
|
||||
not_null<HistoryItem*> item) {
|
||||
return (list.size() == 1)
|
||||
&& (list.front() == item->history()->session().userId());
|
||||
&& (list.front() == item->history()->session().userPeerId());
|
||||
}
|
||||
|
||||
[[nodiscard]] Ui::WhoReadType DetectType(not_null<HistoryItem*> item) {
|
||||
|
@ -103,6 +119,161 @@ struct Context {
|
|||
return Ui::WhoReadType::Seen;
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::producer<std::vector<PeerId>> WhoReadIds(
|
||||
not_null<HistoryItem*> item,
|
||||
not_null<QWidget*> context) {
|
||||
auto weak = QPointer<QWidget>(context.get());
|
||||
const auto fullId = item->fullId();
|
||||
const auto session = &item->history()->session();
|
||||
return [=](auto consumer) {
|
||||
if (!weak) {
|
||||
return rpl::lifetime();
|
||||
}
|
||||
const auto context = ContextAt(weak.data());
|
||||
if (!context->subscriptions.contains(session)) {
|
||||
session->changes().messageUpdates(
|
||||
Data::MessageUpdate::Flag::Destroyed
|
||||
) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
|
||||
const auto i = context->cached.find(update.item);
|
||||
if (i == end(context->cached)) {
|
||||
return;
|
||||
}
|
||||
session->api().request(i->second.requestId).cancel();
|
||||
context->cached.erase(i);
|
||||
}, context->subscriptions[session]);
|
||||
}
|
||||
auto &entry = context->cache(item);
|
||||
if (!entry.requestId) {
|
||||
entry.requestId = session->api().request(
|
||||
MTPmessages_GetMessageReadParticipants(
|
||||
item->history()->peer->input,
|
||||
MTP_int(item->id)
|
||||
)
|
||||
).done([=](const MTPVector<MTPlong> &result) {
|
||||
auto &entry = context->cache(item);
|
||||
entry.requestId = 0;
|
||||
auto peers = std::vector<PeerId>();
|
||||
peers.reserve(std::max(result.v.size(), 1));
|
||||
for (const auto &id : result.v) {
|
||||
peers.push_back(UserId(id));
|
||||
}
|
||||
entry.list = std::move(peers);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
auto &entry = context->cache(item);
|
||||
entry.requestId = 0;
|
||||
if (ListUnknown(entry.list.current(), item)) {
|
||||
entry.list = std::vector<PeerId>();
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
return entry.list.value().start_existing(consumer);
|
||||
};
|
||||
}
|
||||
|
||||
bool UpdateUserpics(
|
||||
not_null<State*> state,
|
||||
not_null<HistoryItem*> item,
|
||||
const std::vector<PeerId> &ids) {
|
||||
auto &owner = item->history()->owner();
|
||||
|
||||
const auto peers = ranges::views::all(
|
||||
ids
|
||||
) | ranges::views::transform([&](PeerId id) {
|
||||
return owner.peerLoaded(id);
|
||||
}) | ranges::views::filter([](PeerData *peer) {
|
||||
return peer != nullptr;
|
||||
}) | ranges::views::transform([](PeerData *peer) {
|
||||
return not_null(peer);
|
||||
}) | ranges::to_vector;
|
||||
|
||||
const auto same = ranges::equal(
|
||||
state->userpics,
|
||||
peers,
|
||||
ranges::less(),
|
||||
&Userpic::peer);
|
||||
if (same) {
|
||||
return false;
|
||||
}
|
||||
auto &was = state->userpics;
|
||||
auto now = std::vector<Userpic>();
|
||||
for (const auto peer : peers) {
|
||||
if (ranges::contains(now, peer, &Userpic::peer)) {
|
||||
continue;
|
||||
}
|
||||
const auto i = ranges::find(was, peer, &Userpic::peer);
|
||||
if (i != end(was)) {
|
||||
now.push_back(std::move(*i));
|
||||
continue;
|
||||
}
|
||||
now.push_back(Userpic{
|
||||
.peer = peer,
|
||||
});
|
||||
auto &userpic = now.back();
|
||||
userpic.uniqueKey = peer->userpicUniqueKey(userpic.view);
|
||||
peer->loadUserpic();
|
||||
}
|
||||
was = std::move(now);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RegenerateUserpics(not_null<State*> state, int small, int large) {
|
||||
Expects(state->userpics.size() == state->current.participants.size());
|
||||
|
||||
state->someUserpicsNotLoaded = false;
|
||||
const auto count = int(state->userpics.size());
|
||||
for (auto i = 0; i != count; ++i) {
|
||||
auto &userpic = state->userpics[i];
|
||||
auto &participant = state->current.participants[i];
|
||||
const auto peer = userpic.peer;
|
||||
const auto key = peer->userpicUniqueKey(userpic.view);
|
||||
if (peer->hasUserpic() && peer->useEmptyUserpic(userpic.view)) {
|
||||
state->someUserpicsNotLoaded = true;
|
||||
}
|
||||
if (userpic.uniqueKey == key) {
|
||||
continue;
|
||||
}
|
||||
participant.userpicKey = userpic.uniqueKey = key;
|
||||
participant.userpicLarge = peer->generateUserpicImage(
|
||||
userpic.view,
|
||||
large);
|
||||
if (i < Ui::WhoReadParticipant::kMaxSmallUserpics) {
|
||||
participant.userpicSmall = peer->generateUserpicImage(
|
||||
userpic.view,
|
||||
small);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RegenerateParticipants(not_null<State*> state, int small, int large) {
|
||||
auto old = base::take(state->current.participants);
|
||||
auto &now = state->current.participants;
|
||||
now.reserve(state->userpics.size());
|
||||
for (auto &userpic : state->userpics) {
|
||||
const auto peer = userpic.peer;
|
||||
const auto id = peer->id.value;
|
||||
const auto was = ranges::find(old, id, &Ui::WhoReadParticipant::id);
|
||||
if (was != end(old)) {
|
||||
was->name = peer->name;
|
||||
now.push_back(std::move(*was));
|
||||
continue;
|
||||
}
|
||||
now.push_back({
|
||||
.name = peer->name,
|
||||
.userpicLarge = peer->generateUserpicImage(
|
||||
userpic.view,
|
||||
large),
|
||||
.userpicKey = userpic.uniqueKey,
|
||||
.id = id,
|
||||
});
|
||||
if (now.size() <= Ui::WhoReadParticipant::kMaxSmallUserpics) {
|
||||
now.back().userpicSmall = peer->generateUserpicImage(
|
||||
userpic.view,
|
||||
small);
|
||||
}
|
||||
}
|
||||
RegenerateUserpics(state, small, large);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool WhoReadExists(not_null<HistoryItem*> item) {
|
||||
|
@ -144,91 +315,54 @@ bool WhoReadExists(not_null<HistoryItem*> item) {
|
|||
return true;
|
||||
}
|
||||
|
||||
rpl::producer<std::vector<UserId>> WhoReadIds(
|
||||
rpl::producer<Ui::WhoReadContent> WhoRead(
|
||||
not_null<HistoryItem*> item,
|
||||
not_null<QWidget*> context) {
|
||||
auto weak = QPointer<QWidget>(context.get());
|
||||
const auto fullId = item->fullId();
|
||||
const auto session = &item->history()->session();
|
||||
not_null<QWidget*> context,
|
||||
const style::WhoRead &st) {
|
||||
const auto small = st.userpics.size;
|
||||
const auto large = st.photoSize;
|
||||
return [=](auto consumer) {
|
||||
if (!weak) {
|
||||
return rpl::lifetime();
|
||||
}
|
||||
const auto context = ContextAt(weak.data());
|
||||
if (!context->subscriptions.contains(session)) {
|
||||
session->changes().messageUpdates(
|
||||
Data::MessageUpdate::Flag::Destroyed
|
||||
) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
|
||||
const auto i = context->cached.find(update.item);
|
||||
if (i == end(context->cached)) {
|
||||
auto lifetime = rpl::lifetime();
|
||||
|
||||
const auto state = lifetime.make_state<State>();
|
||||
const auto pushNext = [=] {
|
||||
consumer.put_next_copy(state->current);
|
||||
};
|
||||
|
||||
WhoReadIds(
|
||||
item,
|
||||
context
|
||||
) | rpl::start_with_next([=](const std::vector<PeerId> &peers) {
|
||||
if (ListUnknown(peers, item)) {
|
||||
state->userpics.clear();
|
||||
consumer.put_next(Ui::WhoReadContent{ .unknown = true });
|
||||
return;
|
||||
} else if (UpdateUserpics(state, item, peers)) {
|
||||
RegenerateParticipants(state, small, large);
|
||||
pushNext();
|
||||
}
|
||||
}, lifetime);
|
||||
|
||||
item->history()->session().downloaderTaskFinished(
|
||||
) | rpl::filter([=] {
|
||||
return state->someUserpicsNotLoaded && !state->scheduled;
|
||||
}) | rpl::start_with_next([=] {
|
||||
for (const auto &userpic : state->userpics) {
|
||||
if (userpic.peer->userpicUniqueKey(userpic.view)
|
||||
!= userpic.uniqueKey) {
|
||||
state->scheduled = true;
|
||||
crl::on_main(&state->guard, [=] {
|
||||
state->scheduled = false;
|
||||
RegenerateUserpics(state, small, large);
|
||||
pushNext();
|
||||
});
|
||||
return;
|
||||
}
|
||||
session->api().request(i->second.requestId).cancel();
|
||||
context->cached.erase(i);
|
||||
}, context->subscriptions[session]);
|
||||
}
|
||||
auto &entry = context->cache(item);
|
||||
if (!entry.requestId) {
|
||||
const auto makeEmpty = [=] {
|
||||
// Special value that marks a validated empty list.
|
||||
return std::vector<UserId>{
|
||||
item->history()->session().userId()
|
||||
};
|
||||
};
|
||||
entry.requestId = session->api().request(
|
||||
MTPmessages_GetMessageReadParticipants(
|
||||
item->history()->peer->input,
|
||||
MTP_int(item->id)
|
||||
)
|
||||
).done([=](const MTPVector<MTPlong> &result) {
|
||||
auto &entry = context->cache(item);
|
||||
entry.requestId = 0;
|
||||
auto users = std::vector<UserId>();
|
||||
users.reserve(std::max(result.v.size(), 1));
|
||||
for (const auto &id : result.v) {
|
||||
users.push_back(UserId(id));
|
||||
}
|
||||
entry.list = std::move(users);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
auto &entry = context->cache(item);
|
||||
entry.requestId = 0;
|
||||
if (ListUnknown(entry.list.current(), item)) {
|
||||
entry.list = std::vector<UserId>();
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
return entry.list.value().start_existing(consumer);
|
||||
}
|
||||
}, lifetime);
|
||||
|
||||
return lifetime;
|
||||
};
|
||||
}
|
||||
|
||||
rpl::producer<Ui::WhoReadContent> WhoRead(
|
||||
not_null<HistoryItem*> item,
|
||||
not_null<QWidget*> context) {
|
||||
return WhoReadIds(
|
||||
item,
|
||||
context
|
||||
) | rpl::map([=](const std::vector<UserId> &users) {
|
||||
const auto owner = &item->history()->owner();
|
||||
if (ListUnknown(users, item)) {
|
||||
return Ui::WhoReadContent{ .unknown = true };
|
||||
}
|
||||
auto participants = ranges::views::all(
|
||||
users
|
||||
) | ranges::views::transform([&](UserId id) {
|
||||
return owner->userLoaded(id);
|
||||
}) | ranges::views::filter([](UserData *user) {
|
||||
return user != nullptr;
|
||||
}) | ranges::views::transform([](UserData *user) {
|
||||
return Ui::WhoReadParticipant{
|
||||
.name = user->name,
|
||||
.id = user->id.value,
|
||||
};
|
||||
}) | ranges::to_vector;
|
||||
return Ui::WhoReadContent{
|
||||
.participants = std::move(participants),
|
||||
.type = DetectType(item),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
|
|
|
@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
class HistoryItem;
|
||||
|
||||
namespace style {
|
||||
struct WhoRead;
|
||||
} // namespace style
|
||||
|
||||
namespace Ui {
|
||||
struct WhoReadContent;
|
||||
} // namespace Ui
|
||||
|
@ -20,6 +24,7 @@ namespace Api {
|
|||
// The context must be destroyed before the session holding this item.
|
||||
[[nodiscard]] rpl::producer<Ui::WhoReadContent> WhoRead(
|
||||
not_null<HistoryItem*> item,
|
||||
not_null<QWidget*> context); // Cache results for this lifetime.
|
||||
not_null<QWidget*> context,
|
||||
const style::WhoRead &st); // Cache results for this lifetime.
|
||||
|
||||
} // namespace Api
|
||||
|
|
|
@ -462,19 +462,11 @@ void Viewport::RendererGL::validateUserpicFrame(
|
|||
} else if (!tileData.userpicFrame.isNull()) {
|
||||
return;
|
||||
}
|
||||
tileData.userpicFrame = QImage(
|
||||
tile->trackOrUserpicSize(),
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
tileData.userpicFrame.fill(Qt::black);
|
||||
{
|
||||
auto p = Painter(&tileData.userpicFrame);
|
||||
tile->row()->peer()->paintUserpicSquare(
|
||||
p,
|
||||
tile->row()->ensureUserpicView(),
|
||||
0,
|
||||
0,
|
||||
tileData.userpicFrame.width());
|
||||
}
|
||||
const auto size = tile->trackOrUserpicSize();
|
||||
tileData.userpicFrame = tile->row()->peer()->generateUserpicImage(
|
||||
tile->row()->ensureUserpicView(),
|
||||
size.width(),
|
||||
ImageRoundRadius::None);
|
||||
}
|
||||
|
||||
void Viewport::RendererGL::paintTile(
|
||||
|
|
|
@ -71,21 +71,12 @@ void Viewport::RendererSW::validateUserpicFrame(
|
|||
} else if (!data.userpicFrame.isNull()) {
|
||||
return;
|
||||
}
|
||||
auto userpic = QImage(
|
||||
tile->trackOrUserpicSize(),
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
userpic.fill(Qt::black);
|
||||
{
|
||||
auto p = Painter(&userpic);
|
||||
tile->row()->peer()->paintUserpicSquare(
|
||||
p,
|
||||
tile->row()->ensureUserpicView(),
|
||||
0,
|
||||
0,
|
||||
userpic.width());
|
||||
}
|
||||
const auto size = tile->trackOrUserpicSize();
|
||||
data.userpicFrame = Images::BlurLargeImage(
|
||||
std::move(userpic),
|
||||
tile->row()->peer()->generateUserpicImage(
|
||||
tile->row()->ensureUserpicView(),
|
||||
size.width(),
|
||||
ImageRoundRadius::None),
|
||||
kBlurRadius);
|
||||
}
|
||||
|
||||
|
|
|
@ -335,32 +335,6 @@ void PeerData::paintUserpic(
|
|||
}
|
||||
}
|
||||
|
||||
void PeerData::paintUserpicRounded(
|
||||
Painter &p,
|
||||
std::shared_ptr<Data::CloudImageView> &view,
|
||||
int x,
|
||||
int y,
|
||||
int size) const {
|
||||
if (const auto userpic = currentUserpic(view)) {
|
||||
p.drawPixmap(x, y, userpic->pixRounded(size, size, ImageRoundRadius::Small));
|
||||
} else {
|
||||
ensureEmptyUserpic()->paintRounded(p, x, y, x + size + x, size);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerData::paintUserpicSquare(
|
||||
Painter &p,
|
||||
std::shared_ptr<Data::CloudImageView> &view,
|
||||
int x,
|
||||
int y,
|
||||
int size) const {
|
||||
if (const auto userpic = currentUserpic(view)) {
|
||||
p.drawPixmap(x, y, userpic->pix(size, size));
|
||||
} else {
|
||||
ensureEmptyUserpic()->paintSquare(p, x, y, x + size + x, size);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerData::loadUserpic() {
|
||||
_userpic.load(&session(), userpicOrigin());
|
||||
}
|
||||
|
@ -398,14 +372,17 @@ void PeerData::saveUserpic(
|
|||
std::shared_ptr<Data::CloudImageView> &view,
|
||||
const QString &path,
|
||||
int size) const {
|
||||
genUserpic(view, size).save(path, "PNG");
|
||||
generateUserpicImage(view, size * cIntRetinaFactor()).save(path, "PNG");
|
||||
}
|
||||
|
||||
void PeerData::saveUserpicRounded(
|
||||
std::shared_ptr<Data::CloudImageView> &view,
|
||||
const QString &path,
|
||||
int size) const {
|
||||
genUserpicRounded(view, size).save(path, "PNG");
|
||||
generateUserpicImage(
|
||||
view,
|
||||
size * cIntRetinaFactor(),
|
||||
ImageRoundRadius::Small).save(path, "PNG");
|
||||
}
|
||||
|
||||
QPixmap PeerData::genUserpic(
|
||||
|
@ -424,20 +401,43 @@ QPixmap PeerData::genUserpic(
|
|||
return Ui::PixmapFromImage(std::move(result));
|
||||
}
|
||||
|
||||
QPixmap PeerData::genUserpicRounded(
|
||||
QImage PeerData::generateUserpicImage(
|
||||
std::shared_ptr<Data::CloudImageView> &view,
|
||||
int size) const {
|
||||
return generateUserpicImage(view, size, ImageRoundRadius::Ellipse);
|
||||
}
|
||||
|
||||
QImage PeerData::generateUserpicImage(
|
||||
std::shared_ptr<Data::CloudImageView> &view,
|
||||
int size,
|
||||
ImageRoundRadius radius) const {
|
||||
if (const auto userpic = currentUserpic(view)) {
|
||||
return userpic->pixRounded(size, size, ImageRoundRadius::Small);
|
||||
const auto options = (radius == ImageRoundRadius::Ellipse)
|
||||
? (Images::Option::RoundedAll | Images::Option::Circled)
|
||||
: (radius == ImageRoundRadius::None)
|
||||
? Images::Options()
|
||||
: (Images::Option::RoundedAll | Images::Option::RoundedSmall);
|
||||
return userpic->pixNoCache(
|
||||
size,
|
||||
size,
|
||||
Images::Option::Smooth | options
|
||||
).toImage();
|
||||
}
|
||||
auto result = QImage(QSize(size, size) * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
|
||||
result.setDevicePixelRatio(cRetinaFactor());
|
||||
auto result = QImage(
|
||||
QSize(size, size),
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
result.fill(Qt::transparent);
|
||||
{
|
||||
Painter p(&result);
|
||||
paintUserpicRounded(p, view, 0, 0, size);
|
||||
if (radius == ImageRoundRadius::Ellipse) {
|
||||
ensureEmptyUserpic()->paint(p, 0, 0, size, size);
|
||||
} else if (radius == ImageRoundRadius::None) {
|
||||
ensureEmptyUserpic()->paintSquare(p, 0, 0, size, size);
|
||||
} else {
|
||||
ensureEmptyUserpic()->paintRounded(p, 0, 0, size, size);
|
||||
}
|
||||
}
|
||||
return Ui::PixmapFromImage(std::move(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
Data::FileOrigin PeerData::userpicOrigin() const {
|
||||
|
|
|
@ -340,18 +340,6 @@ public:
|
|||
int size) const {
|
||||
paintUserpic(p, view, rtl() ? (w - x - size) : x, y, size);
|
||||
}
|
||||
void paintUserpicRounded(
|
||||
Painter &p,
|
||||
std::shared_ptr<Data::CloudImageView> &view,
|
||||
int x,
|
||||
int y,
|
||||
int size) const;
|
||||
void paintUserpicSquare(
|
||||
Painter &p,
|
||||
std::shared_ptr<Data::CloudImageView> &view,
|
||||
int x,
|
||||
int y,
|
||||
int size) const;
|
||||
void loadUserpic();
|
||||
[[nodiscard]] bool hasUserpic() const;
|
||||
[[nodiscard]] std::shared_ptr<Data::CloudImageView> activeUserpicView();
|
||||
|
@ -371,9 +359,13 @@ public:
|
|||
[[nodiscard]] QPixmap genUserpic(
|
||||
std::shared_ptr<Data::CloudImageView> &view,
|
||||
int size) const;
|
||||
[[nodiscard]] QPixmap genUserpicRounded(
|
||||
[[nodiscard]] QImage generateUserpicImage(
|
||||
std::shared_ptr<Data::CloudImageView> &view,
|
||||
int size) const;
|
||||
[[nodiscard]] QImage generateUserpicImage(
|
||||
std::shared_ptr<Data::CloudImageView> &view,
|
||||
int size,
|
||||
ImageRoundRadius radius) const;
|
||||
[[nodiscard]] ImageLocation userpicLocation() const {
|
||||
return _userpic.location();
|
||||
}
|
||||
|
|
|
@ -1591,8 +1591,9 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
|||
};
|
||||
_menu->addAction(Ui::WhoReadContextAction(
|
||||
_menu.get(),
|
||||
Api::WhoRead(item, this),
|
||||
Api::WhoRead(item, this, st::defaultWhoRead),
|
||||
participantChosen));
|
||||
_menu->addSeparator();
|
||||
}
|
||||
if (canSendMessages) {
|
||||
_menu->addAction(tr::lng_context_reply_msg(tr::now), [=] {
|
||||
|
|
|
@ -863,9 +863,18 @@ ttlDividerLabelPadding: margins(22px, 10px, 22px, 19px);
|
|||
ttlItemPadding: margins(0px, 4px, 0px, 4px);
|
||||
ttlItemTimerFont: font(12px);
|
||||
|
||||
seenItemUserpics: GroupCallUserpics {
|
||||
size: 32px;
|
||||
shift: 12px;
|
||||
stroke: 4px;
|
||||
align: align(right);
|
||||
WhoRead {
|
||||
userpics: GroupCallUserpics;
|
||||
photoSize: pixels;
|
||||
itemPadding: margins;
|
||||
}
|
||||
defaultWhoRead: WhoRead {
|
||||
userpics: GroupCallUserpics {
|
||||
size: 22px;
|
||||
shift: 8px;
|
||||
stroke: 4px;
|
||||
align: align(right);
|
||||
}
|
||||
photoSize: 30px;
|
||||
itemPadding: margins(17px, 8px, 17px, 6px);
|
||||
}
|
||||
|
|
|
@ -17,8 +17,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace Ui {
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxUserpics = 3;
|
||||
|
||||
class Action final : public Menu::ItemBase {
|
||||
public:
|
||||
Action(
|
||||
|
@ -77,23 +75,19 @@ Action::Action(
|
|||
, _dummyAction(new QAction(parentMenu->menu()))
|
||||
, _participantChosen(std::move(participantChosen))
|
||||
, _userpics(std::make_unique<GroupCallUserpics>(
|
||||
st::historyGroupCallUserpics,
|
||||
st::defaultWhoRead.userpics,
|
||||
rpl::never<bool>(),
|
||||
[=] { update(); }))
|
||||
, _st(parentMenu->menu()->st())
|
||||
, _height(_st.itemPadding.top()
|
||||
, _height(st::defaultWhoRead.itemPadding.top()
|
||||
+ _st.itemStyle.font->height
|
||||
+ _st.itemPadding.bottom()) {
|
||||
+ st::defaultWhoRead.itemPadding.bottom()) {
|
||||
const auto parent = parentMenu->menu();
|
||||
|
||||
setAcceptBoth(true);
|
||||
initResizeHook(parent->sizeValue());
|
||||
resolveMinWidth();
|
||||
|
||||
auto copy = std::move(
|
||||
content
|
||||
) | rpl::start_spawning(lifetime());
|
||||
|
||||
_userpics->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
_userpicsWidth = width;
|
||||
|
@ -160,12 +154,12 @@ void Action::updateUserpicsFromContent() {
|
|||
if (!_content.participants.empty()) {
|
||||
const auto count = std::min(
|
||||
int(_content.participants.size()),
|
||||
kMaxUserpics);
|
||||
WhoReadParticipant::kMaxSmallUserpics);
|
||||
users.reserve(count);
|
||||
for (auto i = 0; i != count; ++i) {
|
||||
const auto &participant = _content.participants[i];
|
||||
users.push_back({
|
||||
.userpic = participant.userpic,
|
||||
.userpic = participant.userpicSmall,
|
||||
.userpicKey = participant.userpicKey,
|
||||
.id = participant.id,
|
||||
});
|
||||
|
@ -217,8 +211,8 @@ void Action::paint(Painter &p) {
|
|||
_userpics->paint(
|
||||
p,
|
||||
width() - _st.itemPadding.right(),
|
||||
_st.itemPadding.top(),
|
||||
st::historyGroupCallUserpics.size);
|
||||
(height() - st::defaultWhoRead.userpics.size) / 2,
|
||||
st::defaultWhoRead.userpics.size);
|
||||
}
|
||||
|
||||
void Action::refreshText() {
|
||||
|
|
|
@ -18,9 +18,12 @@ class PopupMenu;
|
|||
|
||||
struct WhoReadParticipant {
|
||||
QString name;
|
||||
QImage userpic;
|
||||
QImage userpicSmall;
|
||||
QImage userpicLarge;
|
||||
std::pair<uint64, uint64> userpicKey = {};
|
||||
uint64 id = 0;
|
||||
|
||||
static constexpr auto kMaxSmallUserpics = 3;
|
||||
};
|
||||
|
||||
bool operator==(const WhoReadParticipant &a, const WhoReadParticipant &b);
|
||||
|
|
Loading…
Add table
Reference in a new issue