Top peers context menu.

This commit is contained in:
John Preston 2024-04-11 13:12:57 +04:00
parent 56e28feb00
commit 19ae76d8de
8 changed files with 126 additions and 18 deletions

View file

@ -56,6 +56,19 @@ rpl::producer<> TopPeers::updates() const {
return _updates.events();
}
void TopPeers::remove(not_null<PeerData*> peer) {
const auto i = ranges::find(_list, peer, &TopPeer::peer);
if (i != end(_list)) {
_list.erase(i);
_updates.fire({});
}
_requestId = _session->api().request(MTPcontacts_ResetTopPeerRating(
MTP_topPeerCategoryCorrespondents(),
peer->input
)).send();
}
void TopPeers::increment(not_null<PeerData*> peer, TimeId date) {
if (date <= _lastReceivedDate) {
return;

View file

@ -22,6 +22,7 @@ public:
[[nodiscard]] bool disabled() const;
[[nodiscard]] rpl::producer<> updates() const;
void remove(not_null<PeerData*> peer);
void increment(not_null<PeerData*> peer, TimeId date);
void reload();

View file

@ -1027,7 +1027,7 @@ void Widget::updateControlsVisibility(bool fast) {
_suggestions->show();
}
updateStoriesVisibility();
if ((_openedFolder || _openedForum) && _searchHasFocus.current()) {
if ((_openedFolder || _openedForum) && _searchHasFocus) {
setInnerFocus();
}
if (_updateTelegram) {
@ -1066,7 +1066,7 @@ void Widget::updateControlsVisibility(bool fast) {
if (_hideChildListCanvas) {
_hideChildListCanvas->show();
}
if (_childList && _searchHasFocus.current()) {
if (_childList && _searchHasFocus) {
setInnerFocus();
}
updateLockUnlockPosition();
@ -1090,16 +1090,26 @@ void Widget::updateLockUnlockPosition() {
void Widget::updateHasFocus(not_null<QWidget*> focused) {
const auto has = (focused == _search.data());
if (_searchHasFocus.current() != has) {
_searchHasFocus = (focused == _search.data());
updateStoriesVisibility();
updateForceDisplayWide();
updateSuggestions(anim::type::normal);
if (_searchHasFocus != has) {
_searchHasFocus = has;
const auto update = [=] {
updateStoriesVisibility();
updateForceDisplayWide();
updateSuggestions(anim::type::normal);
};
if (has) {
update();
} else {
// Search field may loose focus from the destructor of some
// widget, in that case we don't want to destroy _suggestions
// syncrhonously, because it may lead to a crash.
crl::on_main(this, update);
}
}
}
void Widget::updateSuggestions(anim::type animated) {
const auto suggest = _searchHasFocus.current()
const auto suggest = _searchHasFocus
&& !_searchInChat
&& (_inner->state() == WidgetState::Default);
if (!suggest && _suggestions) {
@ -1108,6 +1118,7 @@ void Widget::updateSuggestions(anim::type animated) {
} else if (suggest && !_suggestions) {
_suggestions = std::make_unique<Suggestions>(
this,
controller(),
TopPeersContent(&session()));
_suggestions->topPeerChosen(
@ -1563,7 +1574,7 @@ void Widget::updateStoriesVisibility() {
|| _openedForum
|| !_widthAnimationCache.isNull()
|| _childList
|| _searchHasFocus.current()
|| _searchHasFocus
|| !_search->getLastText().isEmpty()
|| _searchInChat
|| _stories->empty();
@ -1681,7 +1692,7 @@ void Widget::slideFinished() {
_shownProgressValue = 1.;
updateControlsVisibility(true);
if ((!_subsectionTopBar || !_subsectionTopBar->searchHasFocus())
&& !_searchHasFocus.current()) {
&& !_searchHasFocus) {
controller()->widget()->setInnerFocus();
}
}
@ -2524,7 +2535,7 @@ void Widget::applySearchUpdate(bool force) {
}
void Widget::updateForceDisplayWide() {
controller()->setChatsForceDisplayWide(_searchHasFocus.current()
controller()->setChatsForceDisplayWide(_searchHasFocus
|| !_search->getLastText().isEmpty()
|| _searchInChat);
}
@ -3258,7 +3269,7 @@ bool Widget::cancelSearch() {
_inner->clearFilter();
clearSearchField();
applySearchUpdate();
if (!_searchInChat && _searchHasFocus.current()) {
if (!_searchInChat && _searchHasFocus) {
setFocus();
}
return clearingQuery || clearingInChat;

View file

@ -300,7 +300,7 @@ private:
std::vector<Data::ReactionId> _searchTags;
rpl::lifetime _searchTagsLifetime;
QString _lastSearchText;
rpl::variable<bool> _searchHasFocus = false;
bool _searchHasFocus = false;
rpl::event_stream<rpl::producer<Stories::Content>> _storiesContents;
base::flat_map<PeerId, Ui::PeerUserpicView> _storiesUserpicsViewsHidden;

View file

@ -22,13 +22,55 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/dynamic_thumbnails.h"
#include "window/window_session_controller.h"
#include "styles/style_dialogs.h"
#include "styles/style_layers.h"
#include "styles/style_menu_icons.h"
namespace Dialogs {
namespace {
void FillTopPeerMenu(
not_null<Window::SessionController*> controller,
const ShowTopPeerMenuRequest &request,
Fn<void(not_null<PeerData*>)> remove) {
const auto owner = &controller->session().data();
const auto peer = owner->peer(PeerId(request.id));
const auto &add = request.callback;
const auto group = peer->isMegagroup();
const auto channel = peer->isChannel();
const auto showHistoryText = group
? tr::lng_context_open_group(tr::now)
: channel
? tr::lng_context_open_channel(tr::now)
: tr::lng_profile_send_message(tr::now);
add(showHistoryText, [=] {
controller->showPeerHistory(peer);
}, channel ? &st::menuIconChannel : &st::menuIconChatBubble);
const auto viewProfileText = group
? tr::lng_context_view_group(tr::now)
: channel
? tr::lng_context_view_channel(tr::now)
: tr::lng_context_view_profile(tr::now);
add(viewProfileText, [=] {
controller->showPeerInfo(peer);
}, channel ? &st::menuIconInfo : &st::menuIconProfile);
add({
.text = tr::lng_recent_remove(tr::now),
.handler = [=] { remove(peer); },
.icon = &st::menuIconDeleteAttention,
.isAttention = true,
});
}
} // namespace
Suggestions::Suggestions(
not_null<QWidget*> parent,
not_null<Window::SessionController*> controller,
rpl::producer<TopPeersList> topPeers)
: RpWidget(parent)
, _scroll(std::make_unique<Ui::ElasticScroll>(this))
@ -45,6 +87,15 @@ Suggestions::Suggestions(
_topPeers->clicks() | rpl::start_with_next([=](uint64 peerIdRaw) {
_topPeerChosen.fire(PeerId(peerIdRaw));
}, _topPeers->lifetime());
_topPeers->showMenuRequests(
) | rpl::start_with_next([=](const ShowTopPeerMenuRequest &request) {
const auto remove = crl::guard(this, [=](not_null<PeerData*> peer) {
peer->session().topPeers().remove(peer);
_topPeers->removeLocally(peer->id.value);
});
FillTopPeerMenu(controller, request, remove);
}, _topPeers->lifetime());
}
Suggestions::~Suggestions() = default;
@ -111,6 +162,9 @@ rpl::producer<TopPeersList> TopPeersContent(
const auto now = base::unixtime::now();
for (const auto &peer : top) {
const auto user = peer->asUser();
if (user->isInaccessible()) {
continue;
}
const auto self = user && user->isSelf();
const auto history = peer->owner().history(peer);
const auto badges = history->chatListBadgesState();

View file

@ -22,12 +22,17 @@ template <typename Widget>
class SlideWrap;
} // namespace Ui
namespace Window {
class SessionController;
} // namespace Window
namespace Dialogs {
class Suggestions final : public Ui::RpWidget {
public:
Suggestions(
not_null<QWidget*> parent,
not_null<Window::SessionController*> controller,
rpl::producer<TopPeersList> topPeers);
~Suggestions();

View file

@ -212,13 +212,29 @@ auto TopPeersStrip::showMenuRequests() const
return _showMenuRequests.events();
}
void TopPeersStrip::removeLocally(uint64 id) {
_removed.emplace(id);
const auto i = ranges::find(_entries, id, &Entry::id);
if (i == end(_entries)) {
return;
} else if (i->subscribed) {
i->userpic->subscribeToUpdates(nullptr);
}
_entries.erase(i);
updateScrollMax();
if (_entries.empty()) {
_empty = true;
}
update();
}
void TopPeersStrip::apply(const TopPeersList &list) {
auto now = std::vector<Entry>();
if (list.entries.empty()) {
_empty = true;
}
for (const auto &entry : list.entries) {
if (_removed.contains(entry.id)) {
continue;
}
const auto i = ranges::find(_entries, entry.id, &Entry::id);
if (i != end(_entries)) {
now.push_back(base::take(*i));
@ -227,6 +243,9 @@ void TopPeersStrip::apply(const TopPeersList &list) {
}
apply(now.back(), entry);
}
if (now.empty()) {
_empty = true;
}
for (auto &entry : _entries) {
if (entry.subscribed) {
entry.userpic->subscribeToUpdates(nullptr);
@ -234,6 +253,7 @@ void TopPeersStrip::apply(const TopPeersList &list) {
}
}
_entries = std::move(now);
updateScrollMax();
unsubscribeUserpics();
if (!_entries.empty()) {
_empty = false;
@ -289,9 +309,10 @@ void TopPeersStrip::paintEvent(QPaintEvent *e) {
const auto single = st.photoLeft * 2 + st.photo;
const auto from = std::min(_scrollLeft / single, int(_entries.size()));
const auto till = std::max(
const auto till = std::clamp(
(_scrollLeft + width() + single - 1) / single + 1,
from);
from,
int(_entries.size()));
auto x = -_scrollLeft + from * single;
for (auto i = from; i != till; ++i) {

View file

@ -49,6 +49,8 @@ public:
[[nodiscard]] auto showMenuRequests() const
-> rpl::producer<ShowTopPeerMenuRequest>;
void removeLocally(uint64 id);
private:
struct Entry;
@ -73,6 +75,7 @@ private:
std::vector<Entry> _entries;
rpl::variable<bool> _empty = true;
base::flat_set<uint64> _removed;
rpl::event_stream<uint64> _clicks;
rpl::event_stream<ShowTopPeerMenuRequest> _showMenuRequests;