Use ParticipantsBoxController for members list.

Add search in channel/supergroup members inside PeerListBox.
Also MembersBox is not used anymore.
This commit is contained in:
John Preston 2017-07-10 19:13:40 +03:00
parent b79ddb7a1c
commit 06d4ea2975
8 changed files with 187 additions and 44 deletions

View File

@ -557,6 +557,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_invite_link_section" = "Invite link";
"lng_profile_create_public_link" = "Create public link";
"lng_profile_edit_public_link" = "Edit public link";
"lng_profile_search_members" = "Search members";
"lng_profile_manage_admins" = "Manage administrators";
"lng_profile_manage_blocklist" = "Manage banned users";
"lng_profile_manage_restrictedlist" = "Manage restricted users";

View File

@ -58,9 +58,10 @@ void BoxContent::setInner(object_ptr<TWidget> inner, const style::ScrollArea &st
_topShadow.create(this, object_ptr<BoxLayerTitleShadow>(this));
_bottomShadow.create(this, object_ptr<BoxLayerTitleShadow>(this));
}
updateScrollAreaGeometry();
connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
connect(_scroll, SIGNAL(innerResized()), this, SLOT(onInnerResize()));
if (!_preparing) {
// We didn't set dimensions yet, this will be called from finishPrepare();
finishScrollCreate();
}
} else {
getDelegate()->setLayerType(false);
_scroll.destroyDelayed();
@ -69,6 +70,21 @@ void BoxContent::setInner(object_ptr<TWidget> inner, const style::ScrollArea &st
}
}
void BoxContent::finishPrepare() {
_preparing = false;
if (_scroll) {
finishScrollCreate();
}
setInnerFocus();
}
void BoxContent::finishScrollCreate() {
Expects(_scroll != nullptr);
updateScrollAreaGeometry();
connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
connect(_scroll, SIGNAL(innerResized()), this, SLOT(onInnerResize()));
}
void BoxContent::onScrollToY(int top, int bottom) {
if (_scroll) {
_scroll->scrollToY(top, bottom);

View File

@ -111,8 +111,9 @@ public:
void setDelegate(BoxContentDelegate *newDelegate) {
_delegate = newDelegate;
_preparing = true;
prepare();
setInnerFocus();
finishPrepare();
}
public slots:
@ -177,6 +178,8 @@ private slots:
void onDraggingScrollTimer();
private:
void finishPrepare();
void finishScrollCreate();
void setInner(object_ptr<TWidget> inner);
void setInner(object_ptr<TWidget> inner, const style::ScrollArea &st);
void updateScrollAreaGeometry();
@ -190,6 +193,7 @@ private:
}
BoxContentDelegate *_delegate = nullptr;
bool _preparing = false;
bool _noContentMargin = false;
int _innerTopSkip = 0;
object_ptr<Ui::ScrollArea> _scroll = { nullptr };

View File

@ -144,7 +144,7 @@ int ChannelMembersWidget::resizeGetHeight(int newWidth) {
void ChannelMembersWidget::onMembers() {
if (auto channel = peer()->asChannel()) {
Ui::show(Box<MembersBox>(channel, MembersFilter::Recent));
ParticipantsBoxController::Start(channel, ParticipantsBoxController::Role::Members);
}
}

View File

@ -96,6 +96,7 @@ int SettingsWidget::resizeGetHeight(int newWidth) {
button->moveToLeft(left, newHeight);
newHeight += button->height();
};
moveLink(_searchMembers);
moveLink(_manageAdmins);
moveLink(_recentActions);
moveLink(_manageBannedUsers);
@ -109,6 +110,11 @@ int SettingsWidget::resizeGetHeight(int newWidth) {
void SettingsWidget::refreshButtons() {
refreshEnableNotifications();
refreshManageAdminsButton();
if (auto megagroup = peer()->asMegagroup()) {
_searchMembers.create(this, lang(lng_profile_search_members), st::defaultLeftOutlineButton);
_searchMembers->show();
connect(_searchMembers, SIGNAL(clicked()), this, SLOT(onSearchMembers()));
}
refreshManageBannedUsersButton();
refreshInviteLinkButton();
}
@ -208,6 +214,12 @@ void SettingsWidget::onNotificationsChange() {
App::main()->updateNotifySetting(peer(), _enableNotifications->checked() ? NotifySettingSetNotify : NotifySettingSetMuted);
}
void SettingsWidget::onSearchMembers() {
if (auto channel = peer()->asChannel()) {
ParticipantsBoxController::Start(channel, ParticipantsBoxController::Role::Members);
}
}
void SettingsWidget::onManageAdmins() {
if (auto chat = peer()->asChat()) {
Ui::show(Box<ContactsBox>(chat, MembersFilter::Admins));

View File

@ -45,6 +45,7 @@ protected:
private slots:
void onNotificationsChange();
void onSearchMembers();
void onManageAdmins();
void onRecentActions();
void onManageBannedUsers();
@ -62,9 +63,7 @@ private:
void refreshInviteLinkButton();
object_ptr<Ui::Checkbox> _enableNotifications;
// In groups: creator of non-deactivated groups can see this link.
// In channels: creator of supergroup can see this link.
object_ptr<Ui::LeftOutlineButton> _searchMembers = { nullptr };
object_ptr<Ui::LeftOutlineButton> _manageAdmins = { nullptr };
object_ptr<Ui::LeftOutlineButton> _recentActions = { nullptr };
object_ptr<Ui::LeftOutlineButton> _manageBannedUsers = { nullptr };

View File

@ -37,7 +37,7 @@ constexpr auto kParticipantsPerPage = 200;
} // namespace
ParticipantsBoxController::ParticipantsBoxController(gsl::not_null<ChannelData*> channel, Role role) : PeerListController((role == Role::Admins) ? nullptr : std::make_unique<BannedBoxSearchController>(channel, role, &_additional))
ParticipantsBoxController::ParticipantsBoxController(gsl::not_null<ChannelData*> channel, Role role) : PeerListController((role == Role::Admins) ? nullptr : std::make_unique<ParticipantsBoxSearchController>(channel, role, &_additional))
, _channel(channel)
, _role(role) {
if (_channel->mgInfo) {
@ -51,6 +51,7 @@ void ParticipantsBoxController::Start(gsl::not_null<ChannelData*> channel, Role
box->addButton(langFactory(lng_close), [box] { box->closeBox(); });
auto canAddNewItem = [role, channel] {
switch (role) {
case Role::Members: return false;
case Role::Admins: return channel->canAddAdmins();
case Role::Restricted:
case Role::Kicked: return channel->canBanMembers();
@ -104,7 +105,7 @@ std::unique_ptr<PeerListRow> ParticipantsBoxController::createSearchRow(gsl::not
template <typename Callback>
void ParticipantsBoxController::HandleParticipant(const MTPChannelParticipant &participant, Role role, gsl::not_null<Additional*> additional, Callback callback) {
if (role == Role::Admins && participant.type() == mtpc_channelParticipantAdmin) {
if ((role == Role::Members || role == Role::Admins) && participant.type() == mtpc_channelParticipantAdmin) {
auto &admin = participant.c_channelParticipantAdmin();
if (auto user = App::userLoaded(admin.vuser_id.v)) {
additional->adminRights[user] = admin.vadmin_rights;
@ -125,18 +126,28 @@ void ParticipantsBoxController::HandleParticipant(const MTPChannelParticipant &p
}
callback(user);
}
} else if (role == Role::Admins && participant.type() == mtpc_channelParticipantCreator) {
} else if ((role == Role::Members || role == Role::Admins) && participant.type() == mtpc_channelParticipantCreator) {
auto &creator = participant.c_channelParticipantCreator();
if (auto user = App::userLoaded(creator.vuser_id.v)) {
additional->creator = user;
callback(user);
}
} else if ((role == Role::Restricted || role == Role::Kicked) && participant.type() == mtpc_channelParticipantBanned) {
} else if ((role == Role::Members || role == Role::Restricted || role == Role::Kicked) && participant.type() == mtpc_channelParticipantBanned) {
auto &banned = participant.c_channelParticipantBanned();
if (auto user = App::userLoaded(banned.vuser_id.v)) {
additional->restrictedRights[user] = banned.vbanned_rights;
callback(user);
}
} else if (role == Role::Members && participant.type() == mtpc_channelParticipant) {
auto &member = participant.c_channelParticipant();
if (auto user = App::userLoaded(member.vuser_id.v)) {
callback(user);
}
} else if (role == Role::Members && participant.type() == mtpc_channelParticipantSelf) {
auto &member = participant.c_channelParticipantSelf();
if (auto user = App::userLoaded(member.vuser_id.v)) {
callback(user);
}
} else {
LOG(("API Error: Bad participant type got while requesting for participants: %1").arg(participant.type()));
}
@ -148,13 +159,19 @@ void ParticipantsBoxController::prepare() {
delegate()->peerListSetTitle(langFactory(lng_channel_admins));
} else {
delegate()->peerListSetSearchMode(PeerListSearchMode::Complex);
delegate()->peerListSetTitle(langFactory((_role == Role::Restricted) ? lng_restricted_list_title : lng_banned_list_title));
if (_role == Role::Members) {
delegate()->peerListSetTitle(langFactory(lng_profile_participants_section));
} else if (_role == Role::Restricted) {
delegate()->peerListSetTitle(langFactory(lng_restricted_list_title));
} else {
delegate()->peerListSetTitle(langFactory(lng_banned_list_title));
}
}
setDescriptionText(lang(lng_contacts_loading));
setSearchNoResultsText(lang(lng_blocked_list_not_found));
delegate()->peerListRefreshRows();
loadMoreRows();
delegate()->peerListRefreshRows();
}
void ParticipantsBoxController::loadMoreRows() {
@ -165,8 +182,14 @@ void ParticipantsBoxController::loadMoreRows() {
return;
}
if (feedMegagroupLastParticipants()) {
return;
}
auto filter = [this] {
if (_role == Role::Admins) {
if (_role == Role::Members) {
return MTP_channelParticipantsRecent();
} else if (_role == Role::Admins) {
return MTP_channelParticipantsAdmins();
} else if (_role == Role::Restricted) {
return MTP_channelParticipantsBanned(MTP_string(QString()));
@ -185,26 +208,71 @@ void ParticipantsBoxController::loadMoreRows() {
setDescriptionText((_role == Role::Restricted) ? lang(lng_group_blocked_list_about) : QString());
}
auto &participants = result.c_channels_channelParticipants();
App::feedUsers(participants.vusers);
App::feedUsers(participants.vusers);
auto &list = participants.vparticipants.v;
if (list.isEmpty()) {
// To be sure - wait for a whole empty result list.
_allLoaded = true;
} else {
for_const (auto &participant, list) {
++_offset;
HandleParticipant(participant, _role, &_additional, [this](gsl::not_null<UserData*> user) {
appendRow(user);
});
}
}
delegate()->peerListRefreshRows();
auto &list = participants.vparticipants.v;
if (list.isEmpty()) {
// To be sure - wait for a whole empty result list.
_allLoaded = true;
} else {
for_const (auto &participant, list) {
++_offset;
HandleParticipant(participant, _role, &_additional, [this](gsl::not_null<UserData*> user) {
appendRow(user);
});
}
}
delegate()->peerListRefreshRows();
}).fail([this](const RPCError &error) {
_loadRequestId = 0;
}).send();
}
bool ParticipantsBoxController::feedMegagroupLastParticipants() {
if (_role != Role::Members || _offset > 0) {
return false;
}
auto megagroup = _channel->asMegagroup();
if (!megagroup) {
return false;
}
auto info = megagroup->mgInfo.get();
if (info->lastParticipantsStatus != MegagroupInfo::LastParticipantsUpToDate) {
return false;
}
if (info->lastParticipants.isEmpty()) {
return false;
}
if (info->creator) {
_additional.creator = info->creator;
}
for_const (auto user, info->lastParticipants) {
auto admin = info->lastAdmins.constFind(user);
if (admin != info->lastAdmins.cend()) {
_additional.restrictedRights.erase(user);
if (admin->canEdit) {
_additional.adminCanEdit.emplace(user);
} else {
_additional.adminCanEdit.erase(user);
}
_additional.adminRights.emplace(user, admin->rights);
} else {
_additional.adminCanEdit.erase(user);
_additional.adminRights.erase(user);
auto restricted = info->lastRestricted.constFind(user);
if (restricted != info->lastRestricted.cend()) {
_additional.restrictedRights.emplace(user, restricted->rights);
} else {
_additional.restrictedRights.erase(user);
}
}
appendRow(user);
++_offset;
}
return true;
}
void ParticipantsBoxController::rowClicked(gsl::not_null<PeerListRow*> row) {
auto user = row->peer()->asUser();
Expects(user != nullptr);
@ -222,7 +290,9 @@ void ParticipantsBoxController::rowActionClicked(gsl::not_null<PeerListRow*> row
auto user = row->peer()->asUser();
Expects(user != nullptr);
if (_role == Role::Admins) {
if (_role == Role::Members) {
kickMember(user);
} else if (_role == Role::Admins) {
showAdmin(user);
} else if (_role == Role::Restricted) {
showRestricted(user);
@ -350,6 +420,30 @@ void ParticipantsBoxController::editRestrictedDone(gsl::not_null<UserData*> user
delegate()->peerListRefreshRows();
}
void ParticipantsBoxController::kickMember(gsl::not_null<UserData*> user) {
auto text = (_channel->isMegagroup() ? lng_profile_sure_kick : lng_profile_sure_kick_channel)(lt_user, user->firstName);
auto weak = base::weak_unique_ptr<ParticipantsBoxController>(this);
_editBox = Ui::show(Box<ConfirmBox>(text, lang(lng_box_remove), [weak, user] {
if (weak) {
weak->kickMemberSure(user);
}
}), KeepOtherLayers);
}
void ParticipantsBoxController::kickMemberSure(gsl::not_null<UserData*> user) {
if (_editBox) {
_editBox->closeBox();
}
auto alreadyIt = _additional.restrictedRights.find(user);
auto currentRights = (alreadyIt == _additional.restrictedRights.cend()) ? MTP_channelBannedRights(MTP_flags(0), MTP_int(0)) : alreadyIt->second;
if (auto row = delegate()->peerListFindRow(user->id)) {
delegate()->peerListRemoveRow(row);
delegate()->peerListRefreshRows();
}
AuthSession::Current().api().kickParticipant(_channel, user, currentRights);
}
void ParticipantsBoxController::removeKicked(gsl::not_null<PeerListRow*> row, gsl::not_null<UserData*> user) {
delegate()->peerListRemoveRow(row);
delegate()->peerListRefreshRows();
@ -394,21 +488,27 @@ std::unique_ptr<PeerListRow> ParticipantsBoxController::createRow(gsl::not_null<
auto row = std::make_unique<PeerListRowWithLink>(user);
if (_role == Role::Admins) {
auto promotedBy = _additional.adminPromotedBy.find(user);
if (promotedBy == _additional.adminPromotedBy.end()) {
if (promotedBy == _additional.adminPromotedBy.cend()) {
row->setCustomStatus(lang(lng_channel_admin_status_creator));
} else {
row->setCustomStatus(lng_channel_admin_status_promoted_by(lt_user, App::peerName(promotedBy->second)));
}
}
if (_role == Role::Restricted || (_role == Role::Admins && _additional.adminCanEdit.find(user) != _additional.adminCanEdit.end())) {
if (_role == Role::Restricted || (_role == Role::Admins && _additional.adminCanEdit.find(user) != _additional.adminCanEdit.cend())) {
// row->setActionLink(lang(lng_profile_edit_permissions));
} else if (_role == Role::Kicked) {
row->setActionLink(lang(lng_blocked_list_unblock));
} else if (_role == Role::Members) {
if (_channel->canBanMembers() && _additional.creator != user
&& (_additional.adminRights.find(user) == _additional.adminRights.cend()
|| _additional.adminCanEdit.find(user) != _additional.adminCanEdit.cend())) {
row->setActionLink(lang(lng_profile_kick));
}
}
return std::move(row);
}
BannedBoxSearchController::BannedBoxSearchController(gsl::not_null<ChannelData*> channel, Role role, gsl::not_null<Additional*> additional)
ParticipantsBoxSearchController::ParticipantsBoxSearchController(gsl::not_null<ChannelData*> channel, Role role, gsl::not_null<Additional*> additional)
: _channel(channel)
, _role(role)
, _additional(additional) {
@ -416,7 +516,7 @@ BannedBoxSearchController::BannedBoxSearchController(gsl::not_null<ChannelData*>
_timer.setCallback([this] { searchOnServer(); });
}
void BannedBoxSearchController::searchQuery(const QString &query) {
void ParticipantsBoxSearchController::searchQuery(const QString &query) {
if (_query != query) {
_query = query;
_offset = 0;
@ -430,16 +530,16 @@ void BannedBoxSearchController::searchQuery(const QString &query) {
}
}
void BannedBoxSearchController::searchOnServer() {
void ParticipantsBoxSearchController::searchOnServer() {
Expects(!_query.isEmpty());
loadMoreRows();
}
bool BannedBoxSearchController::isLoading() {
bool ParticipantsBoxSearchController::isLoading() {
return _timer.isActive() || _requestId;
}
bool BannedBoxSearchController::searchInCache() {
bool ParticipantsBoxSearchController::searchInCache() {
auto it = _cache.find(_query);
if (it != _cache.cend()) {
_requestId = 0;
@ -449,18 +549,25 @@ bool BannedBoxSearchController::searchInCache() {
return false;
}
bool BannedBoxSearchController::loadMoreRows() {
bool ParticipantsBoxSearchController::loadMoreRows() {
if (_query.isEmpty()) {
return false;
}
if (!_allLoaded && !isLoading()) {
auto filter = (_role == Role::Restricted) ? MTP_channelParticipantsBanned(MTP_string(_query)) : MTP_channelParticipantsKicked(MTP_string(_query));
auto filter = [this] {
switch (_role) {
case Role::Members: return MTP_channelParticipantsSearch(MTP_string(_query));
case Role::Restricted: return MTP_channelParticipantsBanned(MTP_string(_query));
case Role::Kicked: return MTP_channelParticipantsKicked(MTP_string(_query));
}
Unexpected("Role in ParticipantsBoxSearchController::loadMoreRows()");
};
// For search we request a lot of rows from the first query.
// (because we've waited for search request by timer already,
// so we don't expect it to be fast, but we want to fill cache).
auto perPage = kParticipantsPerPage;
_requestId = request(MTPchannels_GetParticipants(_channel->inputChannel, filter, MTP_int(_offset), MTP_int(perPage))).done([this, perPage](const MTPchannels_ChannelParticipants &result, mtpRequestId requestId) {
_requestId = request(MTPchannels_GetParticipants(_channel->inputChannel, filter(), MTP_int(_offset), MTP_int(perPage))).done([this, perPage](const MTPchannels_ChannelParticipants &result, mtpRequestId requestId) {
searchDone(requestId, result, perPage);
}).fail([this](const RPCError &error, mtpRequestId requestId) {
if (_requestId == requestId) {
@ -478,7 +585,7 @@ bool BannedBoxSearchController::loadMoreRows() {
return true;
}
void BannedBoxSearchController::searchDone(mtpRequestId requestId, const MTPchannels_ChannelParticipants &result, int requestedCount) {
void ParticipantsBoxSearchController::searchDone(mtpRequestId requestId, const MTPchannels_ChannelParticipants &result, int requestedCount) {
Expects(result.type() == mtpc_channels_channelParticipants);
auto &participants = result.c_channels_channelParticipants();

View File

@ -30,6 +30,7 @@ namespace Profile {
class ParticipantsBoxController : public PeerListController, private base::Subscriber, private MTP::Sender, public base::enable_weak_from_this {
public:
enum class Role {
Members,
Admins,
Restricted,
Kicked,
@ -69,10 +70,13 @@ private:
void showRestricted(gsl::not_null<UserData*> user);
void editRestrictedDone(gsl::not_null<UserData*> user, const MTPChannelBannedRights &rights);
void removeKicked(gsl::not_null<PeerListRow*> row, gsl::not_null<UserData*> user);
void kickMember(gsl::not_null<UserData*> user);
void kickMemberSure(gsl::not_null<UserData*> user);
bool appendRow(gsl::not_null<UserData*> user);
bool prependRow(gsl::not_null<UserData*> user);
bool removeRow(gsl::not_null<UserData*> user);
std::unique_ptr<PeerListRow> createRow(gsl::not_null<UserData*> user) const;
bool feedMegagroupLastParticipants();
gsl::not_null<ChannelData*> _channel;
Role _role = Role::Admins;
@ -85,13 +89,13 @@ private:
};
// Banned and restricted users server side search.
class BannedBoxSearchController : public PeerListSearchController, private MTP::Sender {
// Members, banned and restricted users server side search.
class ParticipantsBoxSearchController : public PeerListSearchController, private MTP::Sender {
public:
using Role = ParticipantsBoxController::Role;
using Additional = ParticipantsBoxController::Additional;
BannedBoxSearchController(gsl::not_null<ChannelData*> channel, Role role, gsl::not_null<Additional*> additional);
ParticipantsBoxSearchController(gsl::not_null<ChannelData*> channel, Role role, gsl::not_null<Additional*> additional);
void searchQuery(const QString &query) override;
bool isLoading() override;