mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +02:00
Add a tab with "Who Seen" to "Who Reacted" box.
This commit is contained in:
parent
74a28ffdf7
commit
c8f7a8c795
9 changed files with 252 additions and 137 deletions
|
@ -51,16 +51,16 @@ inline bool operator==(
|
|||
|
||||
struct PeersWithReactions {
|
||||
std::vector<PeerWithReaction> list;
|
||||
std::vector<PeerId> read;
|
||||
int fullReactionsCount = 0;
|
||||
int fullReadCount = 0;
|
||||
bool unknown = false;
|
||||
};
|
||||
inline bool operator==(
|
||||
const PeersWithReactions &a,
|
||||
const PeersWithReactions &b) noexcept {
|
||||
return (a.fullReactionsCount == b.fullReactionsCount)
|
||||
&& (a.fullReadCount == b.fullReadCount)
|
||||
&& (a.list == b.list)
|
||||
&& (a.read == b.read)
|
||||
&& (a.unknown == b.unknown);
|
||||
}
|
||||
|
||||
|
@ -246,14 +246,15 @@ struct State {
|
|||
}
|
||||
|
||||
[[nodiscard]] PeersWithReactions WithEmptyReactions(
|
||||
const Peers &peers) {
|
||||
return PeersWithReactions{
|
||||
Peers &&peers) {
|
||||
auto result = PeersWithReactions{
|
||||
.list = peers.list | ranges::views::transform([](PeerId peer) {
|
||||
return PeerWithReaction{.peer = peer };
|
||||
}) | ranges::to_vector,
|
||||
.fullReadCount = int(peers.list.size()),
|
||||
.unknown = peers.unknown,
|
||||
};
|
||||
result.read = std::move(peers.list);
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::producer<PeersWithReactions> WhoReactedIds(
|
||||
|
@ -322,17 +323,17 @@ struct State {
|
|||
return rpl::combine(
|
||||
WhoReactedIds(item, QString(), context),
|
||||
WhoReadIds(item, context)
|
||||
) | rpl::map([=](PeersWithReactions reacted, Peers read) {
|
||||
) | rpl::map([=](PeersWithReactions &&reacted, Peers &&read) {
|
||||
if (reacted.unknown || read.unknown) {
|
||||
return PeersWithReactions{ .unknown = true };
|
||||
}
|
||||
auto &list = reacted.list;
|
||||
reacted.fullReadCount = int(read.list.size());
|
||||
for (const auto &peer : read.list) {
|
||||
if (!ranges::contains(list, peer, &PeerWithReaction::peer)) {
|
||||
list.push_back({ .peer = peer });
|
||||
}
|
||||
}
|
||||
reacted.read = std::move(read.list);
|
||||
return reacted;
|
||||
});
|
||||
}
|
||||
|
@ -442,6 +443,104 @@ void RegenerateParticipants(not_null<State*> state, int small, int large) {
|
|||
RegenerateUserpics(state, small, large);
|
||||
}
|
||||
|
||||
rpl::producer<Ui::WhoReadContent> WhoReacted(
|
||||
not_null<HistoryItem*> item,
|
||||
const QString &reaction,
|
||||
not_null<QWidget*> context,
|
||||
const style::WhoRead &st,
|
||||
std::shared_ptr<WhoReadList> whoReadIds) {
|
||||
const auto small = st.userpics.size;
|
||||
const auto large = st.photoSize;
|
||||
return [=](auto consumer) {
|
||||
auto lifetime = rpl::lifetime();
|
||||
|
||||
const auto resolveWhoRead = reaction.isEmpty()
|
||||
&& WhoReadExists(item);
|
||||
|
||||
const auto state = lifetime.make_state<State>();
|
||||
const auto pushNext = [=] {
|
||||
consumer.put_next_copy(state->current);
|
||||
};
|
||||
|
||||
const auto resolveWhoReacted = !reaction.isEmpty()
|
||||
|| item->canViewReactions();
|
||||
auto idsWithReactions = (resolveWhoRead && resolveWhoReacted)
|
||||
? WhoReadOrReactedIds(item, context)
|
||||
: resolveWhoRead
|
||||
? (WhoReadIds(item, context) | rpl::map(WithEmptyReactions))
|
||||
: WhoReactedIds(item, reaction, context);
|
||||
state->current.type = resolveWhoRead
|
||||
? DetectSeenType(item)
|
||||
: Ui::WhoReadType::Reacted;
|
||||
if (resolveWhoReacted) {
|
||||
const auto &list = item->reactions();
|
||||
state->current.fullReactionsCount = reaction.isEmpty()
|
||||
? ranges::accumulate(
|
||||
list,
|
||||
0,
|
||||
ranges::plus{},
|
||||
[](const auto &pair) { return pair.second; })
|
||||
: list.contains(reaction)
|
||||
? list.find(reaction)->second
|
||||
: 0;
|
||||
|
||||
// #TODO reactions
|
||||
state->current.singleReaction = !reaction.isEmpty()
|
||||
? reaction
|
||||
: (list.size() == 1)
|
||||
? list.front().first
|
||||
: QString();
|
||||
}
|
||||
std::move(
|
||||
idsWithReactions
|
||||
) | rpl::start_with_next([=](PeersWithReactions &&peers) {
|
||||
if (peers.unknown) {
|
||||
state->userpics.clear();
|
||||
consumer.put_next(Ui::WhoReadContent{
|
||||
.type = state->current.type,
|
||||
.fullReactionsCount = state->current.fullReactionsCount,
|
||||
.fullReadCount = state->current.fullReadCount,
|
||||
.unknown = true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
state->current.fullReadCount = int(peers.read.size());
|
||||
state->current.fullReactionsCount = peers.fullReactionsCount;
|
||||
if (whoReadIds) {
|
||||
whoReadIds->list = (peers.read.size() > peers.list.size())
|
||||
? std::move(peers.read)
|
||||
: std::vector<PeerId>();
|
||||
}
|
||||
if (UpdateUserpics(state, item, peers.list)) {
|
||||
RegenerateParticipants(state, small, large);
|
||||
pushNext();
|
||||
} else if (peers.list.empty()) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}, lifetime);
|
||||
|
||||
return lifetime;
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool WhoReadExists(not_null<HistoryItem*> item) {
|
||||
|
@ -486,8 +585,9 @@ bool WhoReactedExists(not_null<HistoryItem*> item) {
|
|||
rpl::producer<Ui::WhoReadContent> WhoReacted(
|
||||
not_null<HistoryItem*> item,
|
||||
not_null<QWidget*> context,
|
||||
const style::WhoRead &st) {
|
||||
return WhoReacted(item, QString(), context, st);
|
||||
const style::WhoRead &st,
|
||||
std::shared_ptr<WhoReadList> whoReadIds) {
|
||||
return WhoReacted(item, QString(), context, st, std::move(whoReadIds));
|
||||
}
|
||||
|
||||
rpl::producer<Ui::WhoReadContent> WhoReacted(
|
||||
|
@ -495,90 +595,7 @@ rpl::producer<Ui::WhoReadContent> WhoReacted(
|
|||
const QString &reaction,
|
||||
not_null<QWidget*> context,
|
||||
const style::WhoRead &st) {
|
||||
const auto small = st.userpics.size;
|
||||
const auto large = st.photoSize;
|
||||
return [=](auto consumer) {
|
||||
auto lifetime = rpl::lifetime();
|
||||
|
||||
const auto resolveWhoRead = reaction.isEmpty() && WhoReadExists(item);
|
||||
|
||||
const auto state = lifetime.make_state<State>();
|
||||
const auto pushNext = [=] {
|
||||
consumer.put_next_copy(state->current);
|
||||
};
|
||||
|
||||
const auto resolveWhoReacted = !reaction.isEmpty()
|
||||
|| item->canViewReactions();
|
||||
auto idsWithReactions = (resolveWhoRead && resolveWhoReacted)
|
||||
? WhoReadOrReactedIds(item, context)
|
||||
: resolveWhoRead
|
||||
? (WhoReadIds(item, context) | rpl::map(WithEmptyReactions))
|
||||
: WhoReactedIds(item, reaction, context);
|
||||
state->current.type = resolveWhoRead
|
||||
? DetectSeenType(item)
|
||||
: Ui::WhoReadType::Reacted;
|
||||
if (resolveWhoReacted) {
|
||||
const auto &list = item->reactions();
|
||||
state->current.fullReactionsCount = reaction.isEmpty()
|
||||
? ranges::accumulate(
|
||||
list,
|
||||
0,
|
||||
ranges::plus{},
|
||||
[](const auto &pair) { return pair.second; })
|
||||
: list.contains(reaction)
|
||||
? list.find(reaction)->second
|
||||
: 0;
|
||||
|
||||
// #TODO reactions
|
||||
state->current.singleReaction = !reaction.isEmpty()
|
||||
? reaction
|
||||
: (list.size() == 1)
|
||||
? list.front().first
|
||||
: QString();
|
||||
}
|
||||
std::move(
|
||||
idsWithReactions
|
||||
) | rpl::start_with_next([=](const PeersWithReactions &peers) {
|
||||
if (peers.unknown) {
|
||||
state->userpics.clear();
|
||||
consumer.put_next(Ui::WhoReadContent{
|
||||
.type = state->current.type,
|
||||
.fullReactionsCount = state->current.fullReactionsCount,
|
||||
.fullReadCount = state->current.fullReadCount,
|
||||
.unknown = true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
state->current.fullReadCount = peers.fullReadCount;
|
||||
state->current.fullReactionsCount = peers.fullReactionsCount;
|
||||
if (UpdateUserpics(state, item, peers.list)) {
|
||||
RegenerateParticipants(state, small, large);
|
||||
pushNext();
|
||||
} else if (peers.list.empty()) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}, lifetime);
|
||||
|
||||
return lifetime;
|
||||
};
|
||||
return WhoReacted(item, reaction, context, st, nullptr);
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
|
|
|
@ -15,6 +15,7 @@ struct WhoRead;
|
|||
|
||||
namespace Ui {
|
||||
struct WhoReadContent;
|
||||
enum class WhoReadType;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Api {
|
||||
|
@ -22,15 +23,21 @@ namespace Api {
|
|||
[[nodiscard]] bool WhoReadExists(not_null<HistoryItem*> item);
|
||||
[[nodiscard]] bool WhoReactedExists(not_null<HistoryItem*> item);
|
||||
|
||||
struct WhoReadList {
|
||||
std::vector<PeerId> list;
|
||||
Ui::WhoReadType type = {};
|
||||
};
|
||||
|
||||
// The context must be destroyed before the session holding this item.
|
||||
[[nodiscard]] rpl::producer<Ui::WhoReadContent> WhoReacted(
|
||||
not_null<HistoryItem*> item,
|
||||
not_null<QWidget*> context,
|
||||
const style::WhoRead &st); // Cache results for this lifetime.
|
||||
not_null<QWidget*> context, // Cache results for this lifetime.
|
||||
const style::WhoRead &st,
|
||||
std::shared_ptr<WhoReadList> whoReadIds = nullptr);
|
||||
[[nodiscard]] rpl::producer<Ui::WhoReadContent> WhoReacted(
|
||||
not_null<HistoryItem*> item,
|
||||
const QString &reaction,
|
||||
not_null<QWidget*> context,
|
||||
const style::WhoRead &st); // Cache results for this lifetime.
|
||||
not_null<QWidget*> context, // Cache results for this lifetime.
|
||||
const style::WhoRead &st);
|
||||
|
||||
} // namespace Api
|
||||
|
|
|
@ -1078,6 +1078,7 @@ void AddWhoReactedAction(
|
|||
not_null<QWidget*> context,
|
||||
not_null<HistoryItem*> item,
|
||||
not_null<Window::SessionController*> controller) {
|
||||
const auto whoReadIds = std::make_shared<Api::WhoReadList>();
|
||||
const auto participantChosen = [=](uint64 id) {
|
||||
controller->showPeerInfo(PeerId(id));
|
||||
};
|
||||
|
@ -1091,7 +1092,8 @@ void AddWhoReactedAction(
|
|||
controller->window().show(ReactionsListBox(
|
||||
controller,
|
||||
item,
|
||||
QString()));
|
||||
QString(),
|
||||
whoReadIds));
|
||||
}
|
||||
};
|
||||
if (!menu->empty()) {
|
||||
|
@ -1099,7 +1101,7 @@ void AddWhoReactedAction(
|
|||
}
|
||||
menu->addAction(Ui::WhoReactedContextAction(
|
||||
menu.get(),
|
||||
Api::WhoReacted(item, context, st::defaultWhoRead),
|
||||
Api::WhoReacted(item, context, st::defaultWhoRead, whoReadIds),
|
||||
participantChosen,
|
||||
showAllChosen));
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "window/window_session_controller.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history.h"
|
||||
#include "api/api_who_reacted.h"
|
||||
#include "ui/controls/who_reacted_context_action.h"
|
||||
#include "main/main_session.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
|
@ -50,7 +52,8 @@ public:
|
|||
not_null<Window::SessionController*> window,
|
||||
not_null<HistoryItem*> item,
|
||||
const QString &selected,
|
||||
rpl::producer<QString> switches);
|
||||
rpl::producer<QString> switches,
|
||||
std::shared_ptr<Api::WhoReadList> whoReadIds);
|
||||
|
||||
Main::Session &session() const override;
|
||||
void prepare() override;
|
||||
|
@ -60,7 +63,8 @@ public:
|
|||
private:
|
||||
using AllEntry = std::pair<not_null<UserData*>, QString>;
|
||||
|
||||
void loadMore(const QString &offset);
|
||||
void fillWhoRead();
|
||||
void loadMore(const QString &reaction);
|
||||
bool appendRow(not_null<UserData*> user, QString reaction);
|
||||
std::unique_ptr<PeerListRow> createRow(
|
||||
not_null<UserData*> user,
|
||||
|
@ -72,6 +76,8 @@ private:
|
|||
MTP::Sender _api;
|
||||
|
||||
QString _shownReaction;
|
||||
std::shared_ptr<Api::WhoReadList> _whoReadIds;
|
||||
std::vector<not_null<UserData*>> _whoRead;
|
||||
|
||||
std::vector<AllEntry> _all;
|
||||
QString _allOffset;
|
||||
|
@ -127,11 +133,13 @@ Controller::Controller(
|
|||
not_null<Window::SessionController*> window,
|
||||
not_null<HistoryItem*> item,
|
||||
const QString &selected,
|
||||
rpl::producer<QString> switches)
|
||||
rpl::producer<QString> switches,
|
||||
std::shared_ptr<Api::WhoReadList> whoReadIds)
|
||||
: _window(window)
|
||||
, _item(item)
|
||||
, _api(&window->session().mtp())
|
||||
, _shownReaction(selected) {
|
||||
, _shownReaction(selected)
|
||||
, _whoReadIds(whoReadIds) {
|
||||
std::move(
|
||||
switches
|
||||
) | rpl::filter([=](const QString &reaction) {
|
||||
|
@ -146,10 +154,14 @@ Main::Session &Controller::session() const {
|
|||
}
|
||||
|
||||
void Controller::prepare() {
|
||||
setDescriptionText(tr::lng_contacts_loading(tr::now));
|
||||
if (_shownReaction == u"read"_q) {
|
||||
fillWhoRead();
|
||||
setDescriptionText(QString());
|
||||
} else {
|
||||
setDescriptionText(tr::lng_contacts_loading(tr::now));
|
||||
}
|
||||
delegate()->peerListRefreshRows();
|
||||
|
||||
loadMore(QString());
|
||||
loadMore(_shownReaction);
|
||||
}
|
||||
|
||||
void Controller::showReaction(const QString &reaction) {
|
||||
|
@ -163,7 +175,9 @@ void Controller::showReaction(const QString &reaction) {
|
|||
}
|
||||
|
||||
_shownReaction = reaction;
|
||||
if (_shownReaction.isEmpty()) {
|
||||
if (_shownReaction == u"read"_q) {
|
||||
fillWhoRead();
|
||||
} else if (_shownReaction.isEmpty()) {
|
||||
_filtered.clear();
|
||||
for (const auto &[user, reaction] : _all) {
|
||||
appendRow(user, reaction);
|
||||
|
@ -177,14 +191,29 @@ void Controller::showReaction(const QString &reaction) {
|
|||
for (const auto user : _filtered) {
|
||||
appendRow(user, _shownReaction);
|
||||
}
|
||||
loadMore(QString());
|
||||
_filteredOffset = QString();
|
||||
}
|
||||
loadMore(_shownReaction);
|
||||
setDescriptionText(delegate()->peerListFullRowsCount()
|
||||
? QString()
|
||||
: tr::lng_contacts_loading(tr::now));
|
||||
delegate()->peerListRefreshRows();
|
||||
}
|
||||
|
||||
void Controller::fillWhoRead() {
|
||||
if (_whoReadIds && !_whoReadIds->list.empty() && _whoRead.empty()) {
|
||||
auto &owner = _window->session().data();
|
||||
for (const auto &peerId : _whoReadIds->list) {
|
||||
if (const auto user = owner.userLoaded(peerToUser(peerId))) {
|
||||
_whoRead.push_back(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto &user : _whoRead) {
|
||||
appendRow(user, QString());
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::loadMoreRows() {
|
||||
const auto &offset = _shownReaction.isEmpty()
|
||||
? _allOffset
|
||||
|
@ -192,26 +221,37 @@ void Controller::loadMoreRows() {
|
|||
if (_loadRequestId || offset.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
loadMore(offset);
|
||||
loadMore(_shownReaction);
|
||||
}
|
||||
|
||||
void Controller::loadMore(const QString &offset) {
|
||||
void Controller::loadMore(const QString &reaction) {
|
||||
if (reaction == u"read"_q) {
|
||||
loadMore(QString());
|
||||
return;
|
||||
} else if (reaction.isEmpty() && _allOffset.isEmpty() && !_all.empty()) {
|
||||
return;
|
||||
}
|
||||
_api.request(_loadRequestId).cancel();
|
||||
|
||||
const auto &offset = reaction.isEmpty()
|
||||
? _allOffset
|
||||
: _filteredOffset;
|
||||
|
||||
using Flag = MTPmessages_GetMessageReactionsList::Flag;
|
||||
const auto flags = Flag(0)
|
||||
| (offset.isEmpty() ? Flag(0) : Flag::f_offset)
|
||||
| (_shownReaction.isEmpty() ? Flag(0) : Flag::f_reaction);
|
||||
| (reaction.isEmpty() ? Flag(0) : Flag::f_reaction);
|
||||
_loadRequestId = _api.request(MTPmessages_GetMessageReactionsList(
|
||||
MTP_flags(flags),
|
||||
_item->history()->peer->input,
|
||||
MTP_int(_item->id),
|
||||
MTP_string(_shownReaction),
|
||||
MTP_string(reaction),
|
||||
MTP_string(offset),
|
||||
MTP_int(kPerPageFirst)
|
||||
)).done([=](const MTPmessages_MessageReactionsList &result) {
|
||||
_loadRequestId = 0;
|
||||
const auto filtered = !_shownReaction.isEmpty();
|
||||
const auto filtered = !reaction.isEmpty();
|
||||
const auto shown = (reaction == _shownReaction);
|
||||
result.match([&](const MTPDmessages_messageReactionsList &data) {
|
||||
const auto sessionData = &session().data();
|
||||
sessionData->processUsers(data.vusers());
|
||||
|
@ -222,7 +262,7 @@ void Controller::loadMore(const QString &offset) {
|
|||
const auto user = sessionData->userLoaded(
|
||||
data.vuser_id().v);
|
||||
const auto reaction = qs(data.vreaction());
|
||||
if (user && appendRow(user, reaction)) {
|
||||
if (user && (!shown || appendRow(user, reaction))) {
|
||||
if (filtered) {
|
||||
_filtered.emplace_back(user);
|
||||
} else {
|
||||
|
@ -232,8 +272,10 @@ void Controller::loadMore(const QString &offset) {
|
|||
});
|
||||
}
|
||||
});
|
||||
setDescriptionText(QString());
|
||||
delegate()->peerListRefreshRows();
|
||||
if (shown) {
|
||||
setDescriptionText(QString());
|
||||
delegate()->peerListRefreshRows();
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
|
||||
|
@ -264,20 +306,29 @@ std::unique_ptr<PeerListRow> Controller::createRow(
|
|||
object_ptr<Ui::BoxContent> ReactionsListBox(
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<HistoryItem*> item,
|
||||
QString selected) {
|
||||
QString selected,
|
||||
std::shared_ptr<Api::WhoReadList> whoReadIds) {
|
||||
Expects(IsServerMsgId(item->id));
|
||||
|
||||
if (!item->reactions().contains(selected)) {
|
||||
selected = QString();
|
||||
}
|
||||
if (selected.isEmpty() && whoReadIds && !whoReadIds->list.empty()) {
|
||||
selected = u"read"_q;
|
||||
}
|
||||
const auto tabRequests = std::make_shared<rpl::event_stream<QString>>();
|
||||
const auto initBox = [=](not_null<PeerListBox*> box) {
|
||||
box->setNoContentMargin(true);
|
||||
|
||||
auto map = item->reactions();
|
||||
if (whoReadIds && !whoReadIds->list.empty()) {
|
||||
map.emplace(u"read"_q, int(whoReadIds->list.size()));
|
||||
}
|
||||
const auto selector = CreateReactionSelector(
|
||||
box,
|
||||
item->reactions(),
|
||||
selected);
|
||||
map,
|
||||
selected,
|
||||
whoReadIds ? whoReadIds->type : Ui::WhoReadType::Reacted);
|
||||
selector->changes(
|
||||
) | rpl::start_to_stream(*tabRequests, box->lifetime());
|
||||
|
||||
|
@ -299,7 +350,8 @@ object_ptr<Ui::BoxContent> ReactionsListBox(
|
|||
window,
|
||||
item,
|
||||
selected,
|
||||
tabRequests->events()),
|
||||
tabRequests->events(),
|
||||
whoReadIds),
|
||||
initBox);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
class HistoryItem;
|
||||
|
||||
namespace Api {
|
||||
struct WhoReadList;
|
||||
} // namespace Api
|
||||
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
@ -24,6 +28,7 @@ namespace HistoryView {
|
|||
object_ptr<Ui::BoxContent> ReactionsListBox(
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<HistoryItem*> item,
|
||||
QString selected);
|
||||
QString selected,
|
||||
std::shared_ptr<Api::WhoReadList> whoReadIds = nullptr);
|
||||
|
||||
} // namespace HistoryView
|
||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "ui/rp_widget.h"
|
||||
#include "ui/abstract_button.h"
|
||||
#include "ui/controls/who_reacted_context_action.h"
|
||||
#include "styles/style_widgets.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
||||
|
@ -19,6 +20,7 @@ not_null<Ui::AbstractButton*> CreateTab(
|
|||
not_null<QWidget*> parent,
|
||||
const style::MultiSelect &st,
|
||||
const QString &reaction,
|
||||
Ui::WhoReadType whoReadType,
|
||||
int count,
|
||||
rpl::producer<bool> selected) {
|
||||
struct State {
|
||||
|
@ -71,9 +73,19 @@ not_null<Ui::AbstractButton*> CreateTab(
|
|||
const auto shift = (height - (size / factor)) / 2;
|
||||
Ui::Emoji::Draw(p, emoji, size, icon.x() + shift, shift);
|
||||
} else {
|
||||
(state->selected
|
||||
? st::reactionsTabAllSelected
|
||||
: st::reactionsTabAll).paintInCenter(p, icon);
|
||||
using Type = Ui::WhoReadType;
|
||||
(reaction.isEmpty()
|
||||
? (state->selected
|
||||
? st::reactionsTabAllSelected
|
||||
: st::reactionsTabAll)
|
||||
: (whoReadType == Type::Watched
|
||||
|| whoReadType == Type::Listened)
|
||||
? (state->selected
|
||||
? st::reactionsTabPlayedSelected
|
||||
: st::reactionsTabPlayed)
|
||||
: (state->selected
|
||||
? st::reactionsTabChecksSelected
|
||||
: st::reactionsTabChecks)).paintInCenter(p, icon);
|
||||
}
|
||||
|
||||
const auto textLeft = height + stm->padding.left();
|
||||
|
@ -91,23 +103,14 @@ not_null<Ui::AbstractButton*> CreateTab(
|
|||
not_null<Selector*> CreateReactionSelector(
|
||||
not_null<QWidget*> parent,
|
||||
const base::flat_map<QString, int> &items,
|
||||
const QString &selected) {
|
||||
const QString &selected,
|
||||
Ui::WhoReadType whoReadType) {
|
||||
struct State {
|
||||
rpl::variable<QString> selected;
|
||||
std::vector<not_null<Ui::AbstractButton*>> tabs;
|
||||
};
|
||||
const auto result = Ui::CreateChild<Selector>(parent.get());
|
||||
using Entry = std::pair<int, QString>;
|
||||
auto sorted = std::vector<Entry>();
|
||||
for (const auto &[reaction, count] : items) {
|
||||
sorted.emplace_back(count, reaction);
|
||||
}
|
||||
ranges::sort(sorted, std::greater<>(), &Entry::first);
|
||||
const auto count = ranges::accumulate(
|
||||
sorted,
|
||||
0,
|
||||
std::plus<>(),
|
||||
&Entry::first);
|
||||
auto tabs = Ui::CreateChild<Ui::RpWidget>(parent.get());
|
||||
const auto st = &st::reactionsTabs;
|
||||
const auto state = tabs->lifetime().make_state<State>();
|
||||
|
@ -118,6 +121,7 @@ not_null<Selector*> CreateReactionSelector(
|
|||
tabs,
|
||||
*st,
|
||||
reaction,
|
||||
whoReadType,
|
||||
count,
|
||||
state->selected.value() | rpl::map(_1 == reaction));
|
||||
tab->setClickedCallback([=] {
|
||||
|
@ -125,6 +129,20 @@ not_null<Selector*> CreateReactionSelector(
|
|||
});
|
||||
state->tabs.push_back(tab);
|
||||
};
|
||||
auto sorted = std::vector<Entry>();
|
||||
for (const auto &[reaction, count] : items) {
|
||||
if (reaction == u"read"_q) {
|
||||
append(reaction, count);
|
||||
} else {
|
||||
sorted.emplace_back(count, reaction);
|
||||
}
|
||||
}
|
||||
ranges::sort(sorted, std::greater<>(), &Entry::first);
|
||||
const auto count = ranges::accumulate(
|
||||
sorted,
|
||||
0,
|
||||
std::plus<>(),
|
||||
&Entry::first);
|
||||
append(QString(), count);
|
||||
for (const auto &[count, reaction] : sorted) {
|
||||
append(reaction, count);
|
||||
|
|
|
@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
namespace Ui {
|
||||
enum class WhoReadType;
|
||||
} // namespace Ui
|
||||
|
||||
namespace HistoryView {
|
||||
|
||||
struct Selector {
|
||||
|
@ -19,6 +23,7 @@ struct Selector {
|
|||
not_null<Selector*> CreateReactionSelector(
|
||||
not_null<QWidget*> parent,
|
||||
const base::flat_map<QString, int> &items,
|
||||
const QString &selected);
|
||||
const QString &selected,
|
||||
Ui::WhoReadType whoReadType);
|
||||
|
||||
} // namespace HistoryView
|
||||
|
|
|
@ -926,6 +926,10 @@ whoReadReactionsDisabled: icon{{ "menu/read_reactions", menuFgDisabled }};
|
|||
|
||||
reactionsTabAll: icon {{ "menu/read_reactions", windowFg }};
|
||||
reactionsTabAllSelected: icon {{ "menu/read_reactions", activeButtonFg }};
|
||||
reactionsTabPlayed: icon {{ "menu/read_audio", windowFg }};
|
||||
reactionsTabPlayedSelected: icon {{ "menu/read_audio", activeButtonFg }};
|
||||
reactionsTabChecks: icon {{ "menu/read_ticks", windowFg }};
|
||||
reactionsTabChecksSelected: icon {{ "menu/read_ticks", activeButtonFg }};
|
||||
reactionsTabs: MultiSelect(defaultMultiSelect) {
|
||||
padding: margins(12px, 10px, 12px, 10px);
|
||||
}
|
||||
|
|
|
@ -352,6 +352,10 @@ void Action::paint(Painter &p) {
|
|||
|
||||
void Action::refreshText() {
|
||||
const auto usersCount = int(_content.participants.size());
|
||||
const auto onlySeenCount = ranges::count(
|
||||
_content.participants,
|
||||
QString(),
|
||||
&WhoReadParticipant::reaction);
|
||||
const auto count = std::max(_content.fullReactionsCount, usersCount);
|
||||
_text.setMarkedText(
|
||||
_st.itemStyle,
|
||||
|
@ -365,7 +369,8 @@ void Action::refreshText() {
|
|||
_content.fullReactionsCount,
|
||||
_content.fullReadCount)
|
||||
: (_content.type == WhoReadType::Reacted
|
||||
|| (count > 0 && _content.fullReactionsCount > usersCount))
|
||||
|| (count > 0 && _content.fullReactionsCount > usersCount)
|
||||
|| (count > 0 && onlySeenCount == 0))
|
||||
? (count
|
||||
? tr::lng_context_seen_reacted(
|
||||
tr::now,
|
||||
|
|
Loading…
Add table
Reference in a new issue