From c6811640b5f8f5a64402a64e32a4052b12afa984 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 29 Dec 2021 17:54:01 +0300 Subject: [PATCH] Show box with reacted users. --- Telegram/Resources/langs/lang.strings | 4 + Telegram/SourceFiles/api/api_who_reacted.cpp | 31 ++++--- .../admin_log/history_admin_log_inner.cpp | 3 - .../admin_log/history_admin_log_inner.h | 2 - .../history/history_inner_widget.cpp | 26 +++--- .../history/history_inner_widget.h | 1 - .../history/view/history_view_element.cpp | 4 - .../history/view/history_view_element.h | 2 - .../history/view/history_view_list_widget.cpp | 3 - .../history/view/history_view_list_widget.h | 1 - Telegram/SourceFiles/ui/chat/chat.style | 2 +- .../controls/who_reacted_context_action.cpp | 80 ++++++++++++++----- .../ui/controls/who_reacted_context_action.h | 5 +- Telegram/lib_ui | 2 +- 14 files changed, 100 insertions(+), 66 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 3ad932465..5376862b6 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1803,6 +1803,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_context_seen_watched#one" = "{count} Watched"; "lng_context_seen_watched#other" = "{count} Watched"; "lng_context_seen_watched_none" = "Nobody Watched"; +"lng_context_seen_reacted#one" = "{count} Reacted"; +"lng_context_seen_reacted#other" = "{count} Reacted"; +"lng_context_seen_reacted_none" = "Nobody Reacted"; +"lng_context_seen_reacted_all" = "Show All Reactions"; "lng_send_image_empty" = "Could not send an empty file: {name}"; "lng_send_image_too_large" = "Could not send a file, because it is larger than 1500 MB: {name}"; diff --git a/Telegram/SourceFiles/api/api_who_reacted.cpp b/Telegram/SourceFiles/api/api_who_reacted.cpp index cb47fc215..d082a9dcd 100644 --- a/Telegram/SourceFiles/api/api_who_reacted.cpp +++ b/Telegram/SourceFiles/api/api_who_reacted.cpp @@ -178,7 +178,7 @@ struct State { && (list.front().peer == item->history()->session().userPeerId()); } -[[nodiscard]] Ui::WhoReadType DetectType(not_null item) { +[[nodiscard]] Ui::WhoReadType DetectSeenType(not_null item) { if (const auto media = item->media()) { if (!media->webpage()) { if (const auto document = media->document()) { @@ -425,7 +425,7 @@ bool WhoReadExists(not_null item) { if (!item->out()) { return false; } - const auto type = DetectType(item); + const auto type = DetectSeenType(item); const auto unseen = (type == Ui::WhoReadType::Seen) ? item->unread() : item->isUnreadMedia(); @@ -469,33 +469,30 @@ rpl::producer WhoReacted( return [=](auto consumer) { auto lifetime = rpl::lifetime(); + const auto resolveWhoRead = WhoReadExists(item); + const auto state = lifetime.make_state(); - state->current.type = [&] { - if (const auto media = item->media()) { - if (!media->webpage()) { - if (const auto document = media->document()) { - if (document->isVoiceMessage()) { - return Ui::WhoReadType::Listened; - } else if (document->isVideoMessage()) { - return Ui::WhoReadType::Watched; - } - } - } - } - return Ui::WhoReadType::Seen; - }(); const auto pushNext = [=] { consumer.put_next_copy(state->current); }; - const auto resolveWhoRead = WhoReadExists(item); const auto resolveWhoReacted = item->canViewReactions(); auto idsWithReactions = (resolveWhoRead && resolveWhoReacted) ? WhoReadOrReactedIds(item, context) : resolveWhoRead ? (WhoReadIds(item, context) | rpl::map(WithEmptyReactions)) : WhoReactedIds(item, context); + state->current.type = resolveWhoRead + ? DetectSeenType(item) + : Ui::WhoReadType::Reacted; if (resolveWhoReacted) { + const auto &list = item->reactions(); + state->current.fullReactionsCount = ranges::accumulate( + list, + 0, + ranges::plus{}, + [](const auto &pair) { return pair.second; }); + // #TODO reactions state->current.mostPopularReaction = item->reactions().front().first; } diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index a672aab44..6ce68c3da 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -670,9 +670,6 @@ void InnerWidget::elementReplyTo(const FullMsgId &to) { void InnerWidget::elementStartInteraction(not_null view) { } -void InnerWidget::elementShowReactions(not_null view) { -} - void InnerWidget::elementShowSpoilerAnimation() { _spoilerOpacity.stop(); _spoilerOpacity.start([=] { update(); }, 0., 1., st::fadeWrapDuration); diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h index 9d6b12eeb..c5a5c0cdf 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h @@ -139,8 +139,6 @@ public: void elementReplyTo(const FullMsgId &to) override; void elementStartInteraction( not_null view) override; - void elementShowReactions( - not_null view) override; void elementShowSpoilerAnimation() override; ~InnerWidget(); diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index f0e03a59e..61b2a61ac 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -2082,13 +2082,26 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { const auto participantChosen = [=](uint64 id) { controller->showPeerInfo(PeerId(id)); }; + const auto weak = Ui::MakeWeak(_menu.get()); + const auto showAllChosen = [=, id = _dragStateItem->fullId()] { + // Pressing on an item that has a submenu doesn't hide it :( + if (const auto strong = weak.data()) { + strong->hideMenu(); + } + if (const auto item = controller->session().data().message(id)) { + controller->window().show(HistoryView::ReactionsListBox( + controller, + item)); + } + }; if (!_menu->empty()) { _menu->addSeparator(); } _menu->addAction(Ui::WhoReactedContextAction( _menu.get(), Api::WhoReacted(_dragStateItem, this, st::defaultWhoRead), - participantChosen)); + participantChosen, + showAllChosen)); } if (_menu->empty()) { @@ -2896,12 +2909,6 @@ void HistoryInner::elementStartInteraction(not_null view) { _controller->emojiInteractions().startOutgoing(view); } -void HistoryInner::elementShowReactions(not_null view) { - _controller->window().show(HistoryView::ReactionsListBox( - _controller, - view->data())); -} - void HistoryInner::elementShowSpoilerAnimation() { _spoilerOpacity.stop(); _spoilerOpacity.start([=] { update(); }, 0., 1., st::fadeWrapDuration); @@ -3857,11 +3864,6 @@ not_null HistoryInner::ElementDelegate() { Instance->elementStartInteraction(view); } } - void elementShowReactions(not_null view) override { - if (Instance) { - Instance->elementShowReactions(view); - } - } void elementShowSpoilerAnimation() override { if (Instance) { Instance->elementShowSpoilerAnimation(); diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index 09a9c9663..c3391a6cd 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -124,7 +124,6 @@ public: not_null elementPathShiftGradient(); void elementReplyTo(const FullMsgId &to); void elementStartInteraction(not_null view); - void elementShowReactions(not_null view); void elementShowSpoilerAnimation(); void updateBotInfo(bool recount = true); diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index f6d002867..a188b342c 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -187,10 +187,6 @@ void SimpleElementDelegate::elementStartInteraction( not_null view) { } -void SimpleElementDelegate::elementShowReactions( - not_null view) { -} - void SimpleElementDelegate::elementShowSpoilerAnimation() { } diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index 3135acde6..b9be5fecd 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -99,7 +99,6 @@ public: virtual not_null elementPathShiftGradient() = 0; virtual void elementReplyTo(const FullMsgId &to) = 0; virtual void elementStartInteraction(not_null view) = 0; - virtual void elementShowReactions(not_null view) = 0; virtual void elementShowSpoilerAnimation() = 0; virtual ~ElementDelegate() { @@ -158,7 +157,6 @@ public: not_null elementPathShiftGradient() override; void elementReplyTo(const FullMsgId &to) override; void elementStartInteraction(not_null view) override; - void elementShowReactions(not_null view) override; void elementShowSpoilerAnimation() override; protected: diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 97f5c5725..29e2bdbe9 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -1481,9 +1481,6 @@ void ListWidget::elementReplyTo(const FullMsgId &to) { void ListWidget::elementStartInteraction(not_null view) { } -void ListWidget::elementShowReactions(not_null view) { -} - void ListWidget::elementShowSpoilerAnimation() { _spoilerOpacity.stop(); _spoilerOpacity.start([=] { update(); }, 0., 1., st::fadeWrapDuration); diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index 7b799bf14..062d3828d 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -290,7 +290,6 @@ public: not_null elementPathShiftGradient() override; void elementReplyTo(const FullMsgId &to) override; void elementStartInteraction(not_null view) override; - void elementShowReactions(not_null view) override; void elementShowSpoilerAnimation() override; void setEmptyInfoWidget(base::unique_qptr &&w); diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index 33ed778aa..576469881 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -908,7 +908,7 @@ defaultWhoRead: WhoRead { } whoReadMenu: PopupMenu(popupMenuWithIcons) { scrollPadding: margins(0px, 6px, 0px, 4px); - maxHeight: 387px; + maxHeight: 400px; menu: Menu(menuWithIcons) { separatorPadding: margins(0px, 4px, 0px, 4px); separatorWidth: 6px; diff --git a/Telegram/SourceFiles/ui/controls/who_reacted_context_action.cpp b/Telegram/SourceFiles/ui/controls/who_reacted_context_action.cpp index f233edc1b..fb5729bf1 100644 --- a/Telegram/SourceFiles/ui/controls/who_reacted_context_action.cpp +++ b/Telegram/SourceFiles/ui/controls/who_reacted_context_action.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/chat/group_call_userpics.h" #include "lang/lang_keys.h" #include "styles/style_chat.h" +#include "styles/style_menu_icons.h" namespace Ui { namespace { @@ -58,7 +59,8 @@ public: Action( not_null parentMenu, rpl::producer content, - Fn participantChosen); + Fn participantChosen, + Fn showAllChosen); bool isEnabled() const override; not_null action() const override; @@ -83,6 +85,7 @@ private: const not_null _parentMenu; const not_null _dummyAction; const Fn _participantChosen; + const Fn _showAllChosen; const std::unique_ptr _userpics; const style::Menu &_st; @@ -170,8 +173,15 @@ void EntryAction::paint(Painter &&p) { paintRipple(p, 0, 0); } const auto photoSize = st::defaultWhoRead.photoSize; + const auto photoLeft = st::defaultWhoRead.photoLeft; const auto photoTop = (height() - photoSize) / 2; - p.drawImage(st::defaultWhoRead.photoLeft, photoTop, _userpic); + if (!_userpic.isNull()) { + p.drawImage(photoLeft, photoTop, _userpic); + } else if (!_emoji) { + st::menuIconReactions.paintInCenter( + p, + QRect(photoLeft, photoTop, photoSize, photoSize)); + } p.setPen(selected ? _st.itemFgOver @@ -201,11 +211,13 @@ void EntryAction::paint(Painter &&p) { Action::Action( not_null parentMenu, rpl::producer content, - Fn participantChosen) + Fn participantChosen, + Fn showAllChosen) : ItemBase(parentMenu->menu(), parentMenu->menu()->st()) , _parentMenu(parentMenu) , _dummyAction(CreateChild(parentMenu->menu().get())) , _participantChosen(std::move(participantChosen)) +, _showAllChosen(std::move(showAllChosen)) , _userpics(std::make_unique( st::defaultWhoRead.userpics, rpl::never(), @@ -263,6 +275,10 @@ Action::Action( if (const auto onstack = _participantChosen) { onstack(_content.participants.front().id); } + } else if (_content.fullReactionsCount > 0) { + if (const auto onstack = _showAllChosen) { + onstack(); + } } }, lifetime()); @@ -280,7 +296,10 @@ void Action::resolveMinWidth() { return _st.itemStyle.font->width(text); }; const auto maxTextWidth = std::max({ - width(tr::lng_context_seen_text(tr::now, lt_count_short, 999999999)), + width(tr::lng_context_seen_reacted( + tr::now, + lt_count_short, + 999'999'999)), width(tr::lng_context_seen_text(tr::now, lt_count, 999)), width(tr::lng_context_seen_listened(tr::now, lt_count, 999)), width(tr::lng_context_seen_watched(tr::now, lt_count, 999)) }); @@ -327,21 +346,18 @@ void Action::populateSubmenu() { } const auto submenu = _parentMenu->ensureSubmenu(action()); - if (_submenuActions.size() > _content.participants.size()) { + const auto reactions = ranges::count_if( + _content.participants, + [](const auto &p) { return !p.reaction.isEmpty(); }); + const auto addShowAll = (_content.fullReactionsCount > reactions); + const auto actionsCount = int(_content.participants.size()) + + (addShowAll ? 1 : 0); + if (_submenuActions.size() > actionsCount) { _submenuActions.clear(); submenu->clearActions(); } auto index = 0; - for (const auto &participant : _content.participants) { - const auto chosen = [call = _participantChosen, id = participant.id] { - call(id); - }; - auto data = EntryData{ - .text = participant.name, - .reaction = participant.reaction, - .userpic = participant.userpicLarge, - .callback = chosen, - }; + const auto append = [&](EntryData &&data) { if (index < _submenuActions.size()) { _submenuActions[index]->setData(std::move(data)); } else { @@ -353,6 +369,23 @@ void Action::populateSubmenu() { submenu->addAction(std::move(item)); } ++index; + }; + for (const auto &participant : _content.participants) { + const auto chosen = [call = _participantChosen, id = participant.id] { + call(id); + }; + append({ + .text = participant.name, + .reaction = participant.reaction, + .userpic = participant.userpicLarge, + .callback = chosen, + }); + } + if (addShowAll) { + append({ + .text = tr::lng_context_seen_reacted_all(tr::now), + .callback = _showAllChosen, + }); } _parentMenu->checkSubmenuShow(); } @@ -410,13 +443,22 @@ void Action::paint(Painter &p) { } void Action::refreshText() { - const auto count = int(_content.participants.size()); + const auto usersCount = int(_content.participants.size()); + const auto count = std::max(_content.fullReactionsCount, usersCount); _text.setMarkedText( _st.itemStyle, { (_content.unknown ? tr::lng_context_seen_loading(tr::now) : (count == 1) ? _content.participants.front().name + : (_content.type == WhoReadType::Reacted + || (count > 0 && _content.fullReactionsCount > usersCount)) + ? (count + ? tr::lng_context_seen_reacted( + tr::now, + lt_count_short, + count) + : tr::lng_context_seen_reacted_none(tr::now)) : (_content.type == WhoReadType::Watched) ? (count ? tr::lng_context_seen_watched(tr::now, lt_count, count) @@ -492,11 +534,13 @@ bool operator!=(const WhoReadParticipant &a, const WhoReadParticipant &b) { base::unique_qptr WhoReactedContextAction( not_null menu, rpl::producer content, - Fn participantChosen) { + Fn participantChosen, + Fn showAllChosen) { return base::make_unique_q( menu, std::move(content), - std::move(participantChosen)); + std::move(participantChosen), + std::move(showAllChosen)); } } // namespace Ui diff --git a/Telegram/SourceFiles/ui/controls/who_reacted_context_action.h b/Telegram/SourceFiles/ui/controls/who_reacted_context_action.h index 4ffd6f14e..8f35d4f56 100644 --- a/Telegram/SourceFiles/ui/controls/who_reacted_context_action.h +++ b/Telegram/SourceFiles/ui/controls/who_reacted_context_action.h @@ -34,18 +34,21 @@ enum class WhoReadType { Seen, Listened, Watched, + Reacted, }; struct WhoReadContent { std::vector participants; WhoReadType type = WhoReadType::Seen; QString mostPopularReaction; + int fullReactionsCount = 0; bool unknown = false; }; [[nodiscard]] base::unique_qptr WhoReactedContextAction( not_null menu, rpl::producer content, - Fn participantChosen); + Fn participantChosen, + Fn showAllChosen); } // namespace Ui diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 0a0e5e125..a3b3745d7 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 0a0e5e12589d7918a31d60b100dc32de68ef2e8a +Subproject commit a3b3745d7c7ff94cfeac78daca2b0d89cb6c865e