Add blocked users list in supergroups profiles.

This commit is contained in:
John Preston 2017-04-07 18:07:26 +03:00
parent 34ab04cbe6
commit 38f94c63e9
11 changed files with 279 additions and 80 deletions

View File

@ -496,6 +496,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_create_public_link" = "Create public link";
"lng_profile_edit_public_link" = "Edit public link";
"lng_profile_manage_admins" = "Manage administrators";
"lng_profile_manage_blocklist" = "Manage blocked users";
"lng_profile_common_groups" = "{count:_not_used_|# group|# groups} in common";
"lng_profile_common_groups_section" = "Groups in common";
"lng_profile_participants_section" = "Members";
@ -575,6 +576,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_channel_admin_sure" = "Add {user} to administrators?";
"lng_channel_admins_too_much" = "Sorry, you have reached the limit of the administrators. Please remove one administrator first.";
"lng_group_blocked_list_about" = "Blocked users are removed from the group and can only come back if invited by an admin.\nInvite links don't work for them.";
"lng_chat_all_members_admins" = "All Members Are Admins";
"lng_chat_about_all_admins" = "Group members can add new members, edit name and photo of the group.";
"lng_chat_about_admins" = "Group admins can add and remove members, edit name and photo of the group.";

View File

@ -300,6 +300,7 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
channel->setAbout(qs(f.vabout));
channel->setMembersCount(f.has_participants_count() ? f.vparticipants_count.v : 0);
channel->setAdminsCount(f.has_admins_count() ? f.vadmins_count.v : 0);
channel->setKickedCount(f.has_kicked_count() ? f.vkicked_count.v : 0);
channel->setInviteLink((f.vexported_invite.type() == mtpc_chatInviteExported) ? qs(f.vexported_invite.c_chatInviteExported().vlink) : QString());
if (auto h = App::historyLoaded(channel->id)) {
if (h->inboxReadBefore < f.vread_inbox_max_id.v + 1) {
@ -651,8 +652,10 @@ void ApiWrap::kickParticipant(PeerData *peer, UserData *user) {
auto kick = KickRequest(peer, user);
if (_kickRequests.contains(kick)) return;
if (peer->isChannel()) {
auto requestId = request(MTPchannels_KickFromChannel(peer->asChannel()->inputChannel, user->inputUser, MTP_bool(true))).done([this, peer, user](const MTPUpdates &result) {
if (auto channel = peer->asChannel()) {
auto requestId = request(MTPchannels_KickFromChannel(channel->inputChannel, user->inputUser, MTP_bool(true))).done([this, peer, user](const MTPUpdates &result) {
App::main()->sentUpdatesReceived(result);
_kickRequests.remove(KickRequest(peer, user));
if (auto channel = peer->asMegagroup()) {
auto megagroupInfo = channel->mgInfo;
@ -668,6 +671,7 @@ void ApiWrap::kickParticipant(PeerData *peer, UserData *user) {
megagroupInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsCountOutdated;
megagroupInfo->lastParticipantsCount = 0;
}
channel->setKickedCount(channel->kickedCount() + 1);
if (megagroupInfo->lastAdmins.contains(user)) {
megagroupInfo->lastAdmins.remove(user);
if (channel->adminsCount() > 1) {
@ -690,6 +694,30 @@ void ApiWrap::kickParticipant(PeerData *peer, UserData *user) {
}
}
void ApiWrap::unblockParticipant(PeerData *peer, UserData *user) {
auto kick = KickRequest(peer, user);
if (_kickRequests.contains(kick)) return;
if (auto channel = peer->asChannel()) {
auto requestId = request(MTPchannels_KickFromChannel(channel->inputChannel, user->inputUser, MTP_bool(false))).done([this, peer, user](const MTPUpdates &result) {
App::main()->sentUpdatesReceived(result);
_kickRequests.remove(KickRequest(peer, user));
if (auto channel = peer->asMegagroup()) {
if (channel->kickedCount() > 0) {
channel->setKickedCount(channel->kickedCount() - 1);
} else {
channel->updateFull(true);
}
}
}).fail([this, kick](const RPCError &error) {
_kickRequests.remove(kick);
}).send();
_kickRequests.insert(kick, requestId);
}
}
void ApiWrap::scheduleStickerSetRequest(uint64 setId, uint64 access) {
if (!_stickerSetRequests.contains(setId)) {
_stickerSetRequests.insert(setId, qMakePair(access, 0));

View File

@ -55,6 +55,7 @@ public:
void requestSelfParticipant(ChannelData *channel);
void kickParticipant(PeerData *peer, UserData *user);
void unblockParticipant(PeerData *peer, UserData *user);
void requestWebPageDelayed(WebPageData *page);
void clearWebPageRequest(WebPageData *page);

View File

@ -514,80 +514,81 @@ void MembersBox::Inner::onPeerNameChanged(PeerData *peer, const PeerData::Names
}
void MembersBox::Inner::membersReceived(const MTPchannels_ChannelParticipants &result, mtpRequestId req) {
Expects(result.type() == mtpc_channels_channelParticipants);
clear();
_loadingRequestId = 0;
if (result.type() == mtpc_channels_channelParticipants) {
auto &d = result.c_channels_channelParticipants();
auto &v = d.vparticipants.v;
_rows.reserve(v.size());
_datas.reserve(v.size());
_dates.reserve(v.size());
_roles.reserve(v.size());
auto &d = result.c_channels_channelParticipants();
auto &v = d.vparticipants.v;
_rows.reserve(v.size());
_datas.reserve(v.size());
_dates.reserve(v.size());
_roles.reserve(v.size());
if (_filter == MembersFilter::Recent && _channel->membersCount() < d.vcount.v) {
_channel->setMembersCount(d.vcount.v);
if (App::main()) emit App::main()->peerUpdated(_channel);
} else if (_filter == MembersFilter::Admins && _channel->adminsCount() < d.vcount.v) {
_channel->setAdminsCount(d.vcount.v);
if (App::main()) emit App::main()->peerUpdated(_channel);
if (_filter == MembersFilter::Recent && _channel->membersCount() < d.vcount.v) {
_channel->setMembersCount(d.vcount.v);
if (App::main()) emit App::main()->peerUpdated(_channel);
} else if (_filter == MembersFilter::Admins && _channel->adminsCount() < d.vcount.v) {
_channel->setAdminsCount(d.vcount.v);
if (App::main()) emit App::main()->peerUpdated(_channel);
}
App::feedUsers(d.vusers);
for (QVector<MTPChannelParticipant>::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) {
int32 userId = 0, addedTime = 0;
MemberRole role = MemberRole::None;
switch (i->type()) {
case mtpc_channelParticipant:
userId = i->c_channelParticipant().vuser_id.v;
addedTime = i->c_channelParticipant().vdate.v;
break;
case mtpc_channelParticipantSelf:
role = MemberRole::Self;
userId = i->c_channelParticipantSelf().vuser_id.v;
addedTime = i->c_channelParticipantSelf().vdate.v;
break;
case mtpc_channelParticipantModerator:
role = MemberRole::Moderator;
userId = i->c_channelParticipantModerator().vuser_id.v;
addedTime = i->c_channelParticipantModerator().vdate.v;
break;
case mtpc_channelParticipantEditor:
role = MemberRole::Editor;
userId = i->c_channelParticipantEditor().vuser_id.v;
addedTime = i->c_channelParticipantEditor().vdate.v;
break;
case mtpc_channelParticipantKicked:
userId = i->c_channelParticipantKicked().vuser_id.v;
addedTime = i->c_channelParticipantKicked().vdate.v;
role = MemberRole::Kicked;
break;
case mtpc_channelParticipantCreator:
userId = i->c_channelParticipantCreator().vuser_id.v;
addedTime = _channel->date;
role = MemberRole::Creator;
break;
}
App::feedUsers(d.vusers);
for (QVector<MTPChannelParticipant>::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) {
int32 userId = 0, addedTime = 0;
MemberRole role = MemberRole::None;
switch (i->type()) {
case mtpc_channelParticipant:
userId = i->c_channelParticipant().vuser_id.v;
addedTime = i->c_channelParticipant().vdate.v;
break;
case mtpc_channelParticipantSelf:
role = MemberRole::Self;
userId = i->c_channelParticipantSelf().vuser_id.v;
addedTime = i->c_channelParticipantSelf().vdate.v;
break;
case mtpc_channelParticipantModerator:
role = MemberRole::Moderator;
userId = i->c_channelParticipantModerator().vuser_id.v;
addedTime = i->c_channelParticipantModerator().vdate.v;
break;
case mtpc_channelParticipantEditor:
role = MemberRole::Editor;
userId = i->c_channelParticipantEditor().vuser_id.v;
addedTime = i->c_channelParticipantEditor().vdate.v;
break;
case mtpc_channelParticipantKicked:
userId = i->c_channelParticipantKicked().vuser_id.v;
addedTime = i->c_channelParticipantKicked().vdate.v;
role = MemberRole::Kicked;
break;
case mtpc_channelParticipantCreator:
userId = i->c_channelParticipantCreator().vuser_id.v;
addedTime = _channel->date;
role = MemberRole::Creator;
break;
}
if (UserData *user = App::userLoaded(userId)) {
_rows.push_back(user);
_dates.push_back(date(addedTime));
_roles.push_back(role);
_datas.push_back(0);
}
}
// update admins if we got all of them
if (_filter == MembersFilter::Admins && _channel->isMegagroup() && _rows.size() < Global::ChatSizeMax()) {
_channel->mgInfo->lastAdmins.clear();
for (int32 i = 0, l = _rows.size(); i != l; ++i) {
if (_roles.at(i) == MemberRole::Creator || _roles.at(i) == MemberRole::Editor) {
_channel->mgInfo->lastAdmins.insert(_rows.at(i));
}
}
Notify::peerUpdatedDelayed(_channel, Notify::PeerUpdate::Flag::AdminsChanged);
if (UserData *user = App::userLoaded(userId)) {
_rows.push_back(user);
_dates.push_back(date(addedTime));
_roles.push_back(role);
_datas.push_back(0);
}
}
// update admins if we got all of them
if (_filter == MembersFilter::Admins && _channel->isMegagroup() && _rows.size() < Global::ChatSizeMax()) {
_channel->mgInfo->lastAdmins.clear();
for (int32 i = 0, l = _rows.size(); i != l; ++i) {
if (_roles.at(i) == MemberRole::Creator || _roles.at(i) == MemberRole::Editor) {
_channel->mgInfo->lastAdmins.insert(_rows.at(i));
}
}
Notify::peerUpdatedDelayed(_channel, Notify::PeerUpdate::Flag::AdminsChanged);
}
if (_rows.isEmpty()) {
_rows.push_back(App::self());
_dates.push_back(date(MTP_int(_channel->date)));

View File

@ -1052,8 +1052,11 @@ void PeerListBox::Inner::setVisibleTopBottom(int visibleTop, int visibleBottom)
void PeerListBox::Inner::setSelected(Selected selected) {
updateRow(_selected.index);
_selected = selected;
updateRow(_selected.index);
if (_selected != selected) {
_selected = selected;
updateRow(_selected.index);
setCursor(_selected.action ? style::cur_pointer : style::cur_default);
}
}
void PeerListBox::Inner::restoreSelection() {
@ -1069,7 +1072,7 @@ void PeerListBox::Inner::updateSelection() {
auto in = parentWidget()->rect().contains(parentWidget()->mapFromGlobal(_lastMousePosition));
auto selected = Selected();
auto rowsPointY = point.y() - rowsTop;
selected.index.value = (in && rowsPointY >= 0) ? snap(rowsPointY / _rowHeight, 0, shownRowsCount() - 1) : -1;
selected.index.value = (in && rowsPointY >= 0 && rowsPointY < shownRowsCount() * _rowHeight) ? (rowsPointY / _rowHeight) : -1;
if (selected.index.value >= 0) {
auto row = getRow(selected.index);
if (row->disabled()) {
@ -1086,10 +1089,7 @@ void PeerListBox::Inner::updateSelection() {
}
}
}
if (_selected != selected) {
setSelected(selected);
setCursor(_selected.action ? style::cur_pointer : style::cur_default);
}
setSelected(selected);
}
void PeerListBox::Inner::peerUpdated(PeerData *peer) {

View File

@ -48,6 +48,7 @@ struct PeerUpdate {
InviteLinkChanged = 0x00000100U,
MembersChanged = 0x00000200U,
AdminsChanged = 0x00000400U,
BlockedUsersChanged = 0x00000800U,
// For users
UserCanShareContact = 0x00010000U,

View File

@ -42,7 +42,7 @@ protected:
// Resizes content and counts natural widget height for the desired width.
int resizeGetHeight(int newWidth) override;
private slots:
private slots:
void onAdmins();
void onMembers();

View File

@ -25,7 +25,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/widgets/checkbox.h"
#include "boxes/confirm_box.h"
#include "boxes/contacts_box.h"
#include "boxes/peer_list_box.h"
#include "observer_peer.h"
#include "auth_session.h"
#include "mainwidget.h"
#include "apiwrap.h"
#include "lang.h"
@ -33,6 +35,126 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "mainwindow.h" // tmp
namespace Profile {
namespace {
constexpr auto kBlockedPerPage = 40;
class BlockedBoxController : public PeerListBox::Controller, private base::Subscriber, private MTP::Sender {
public:
BlockedBoxController(ChannelData *channel) : _channel(channel) {
}
void prepare() override;
void rowClicked(PeerListBox::Row *row) override;
void rowActionClicked(PeerListBox::Row *row) override;
void preloadRows() override;
private:
bool appendRow(UserData *user);
bool prependRow(UserData *user);
std::unique_ptr<PeerListBox::Row> createRow(UserData *user) const;
ChannelData *_channel = nullptr;
int _offset = 0;
mtpRequestId _loadRequestId = 0;
bool _allLoaded = false;
};
void BlockedBoxController::prepare() {
view()->setTitle(lang(lng_blocked_list_title));
view()->addButton(lang(lng_close), [this] { view()->closeBox(); });
view()->setAboutText(lang(lng_contacts_loading));
view()->refreshRows();
preloadRows();
}
void BlockedBoxController::preloadRows() {
if (_loadRequestId || _allLoaded) {
return;
}
_loadRequestId = request(MTPchannels_GetParticipants(_channel->inputChannel, MTP_channelParticipantsKicked(), MTP_int(_offset), MTP_int(kBlockedPerPage))).done([this](const MTPchannels_ChannelParticipants &result) {
Expects(result.type() == mtpc_channels_channelParticipants);
_loadRequestId = 0;
if (!_offset) {
view()->setAboutText(lang(lng_group_blocked_list_about));
}
auto &participants = result.c_channels_channelParticipants();
App::feedUsers(participants.vusers);
auto &list = participants.vparticipants.v;
if (list.isEmpty()) {
_allLoaded = true;
} else {
for_const (auto &participant, list) {
++_offset;
if (participant.type() != mtpc_channelParticipantKicked) {
LOG(("API Error: Non kicked participant got while requesting for kicked participants: %1").arg(participant.type()));
continue;
}
auto &kicked = participant.c_channelParticipantKicked();
auto userId = kicked.vuser_id.v;
if (auto user = App::userLoaded(userId)) {
appendRow(user);
}
}
}
view()->refreshRows();
}).fail([this](const RPCError &error) {
_loadRequestId = 0;
}).send();
}
void BlockedBoxController::rowClicked(PeerListBox::Row *row) {
Ui::showPeerHistoryAsync(row->peer()->id, ShowAtUnreadMsgId);
}
void BlockedBoxController::rowActionClicked(PeerListBox::Row *row) {
auto user = row->peer()->asUser();
Expects(user != nullptr);
view()->removeRow(row);
view()->refreshRows();
AuthSession::Current().api().unblockParticipant(_channel, user);
}
bool BlockedBoxController::appendRow(UserData *user) {
if (view()->findRow(user)) {
return false;
}
view()->appendRow(createRow(user));
return true;
}
bool BlockedBoxController::prependRow(UserData *user) {
if (view()->findRow(user)) {
return false;
}
view()->prependRow(createRow(user));
return true;
}
std::unique_ptr<PeerListBox::Row> BlockedBoxController::createRow(UserData *user) const {
auto row = std::make_unique<PeerListBox::Row>(user);
row->setActionLink(lang(lng_blocked_list_unblock));
auto status = [user]() -> QString {
if (user->botInfo) {
return lang(lng_status_bot);
} else if (user->phone().isEmpty()) {
return lang(lng_blocked_list_unknown_phone);
}
return App::formatPhone(user->phone());
};
row->setCustomStatus(status());
return row;
}
} // namespace
using UpdateFlag = Notify::PeerUpdate::Flag;
@ -49,6 +171,7 @@ SettingsWidget::SettingsWidget(QWidget *parent, PeerData *peer) : BlockWidget(pa
if (channel->amCreator()) {
observeEvents |= UpdateFlag::UsernameChanged | UpdateFlag::InviteLinkChanged;
}
observeEvents |= UpdateFlag::ChannelAmEditor | UpdateFlag::BlockedUsersChanged;
}
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
notifyPeerUpdated(update);
@ -74,6 +197,9 @@ void SettingsWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
if (update.flags & (UpdateFlag::ChatCanEdit)) {
refreshManageAdminsButton();
}
if ((update.flags & UpdateFlag::ChannelAmEditor) || (update.flags & UpdateFlag::BlockedUsersChanged)) {
refreshManageBlockedUsersButton();
}
contentSizeUpdated();
}
@ -95,6 +221,7 @@ int SettingsWidget::resizeGetHeight(int newWidth) {
newHeight += button->height();
};
moveLink(_manageAdmins);
moveLink(_manageBlockedUsers);
moveLink(_inviteLink);
newHeight += st::profileBlockMarginBottom;
@ -104,6 +231,7 @@ int SettingsWidget::resizeGetHeight(int newWidth) {
void SettingsWidget::refreshButtons() {
refreshEnableNotifications();
refreshManageAdminsButton();
refreshManageBlockedUsersButton();
refreshInviteLinkButton();
}
@ -118,11 +246,11 @@ void SettingsWidget::refreshEnableNotifications() {
}
void SettingsWidget::refreshManageAdminsButton() {
auto hasManageAdmins = [this]() {
auto hasManageAdmins = [this] {
if (auto chat = peer()->asChat()) {
return (chat->amCreator() && chat->canEdit());
} else if (auto channel = peer()->asChannel()) {
return (channel->amCreator() && channel->isMegagroup());
} else if (auto channel = peer()->asMegagroup()) {
return channel->amCreator();
}
return false;
};
@ -134,6 +262,21 @@ void SettingsWidget::refreshManageAdminsButton() {
}
}
void SettingsWidget::refreshManageBlockedUsersButton() {
auto hasManageBlockedUsers = [this] {
if (auto channel = peer()->asMegagroup()) {
return (channel->amCreator() || channel->amEditor()) && (channel->kickedCount() > 0);
}
return false;
};
_manageBlockedUsers.destroy();
if (hasManageBlockedUsers()) {
_manageBlockedUsers.create(this, lang(lng_profile_manage_blocklist), st::defaultLeftOutlineButton);
_manageBlockedUsers->show();
connect(_manageBlockedUsers, SIGNAL(clicked()), this, SLOT(onManageBlockedUsers()));
}
}
void SettingsWidget::refreshInviteLinkButton() {
auto getInviteLinkText = [this]() -> QString {
if (auto chat = peer()->asChat()) {
@ -169,6 +312,12 @@ void SettingsWidget::onManageAdmins() {
}
}
void SettingsWidget::onManageBlockedUsers() {
if (auto channel = peer()->asMegagroup()) {
Ui::show(Box<PeerListBox>(std::make_unique<BlockedBoxController>(channel)));
}
}
void SettingsWidget::onInviteLink() {
auto getInviteLink = [this]() {
if (auto chat = peer()->asChat()) {

View File

@ -46,6 +46,7 @@ protected:
private slots:
void onNotificationsChange();
void onManageAdmins();
void onManageBlockedUsers();
void onInviteLink();
private:
@ -55,6 +56,7 @@ private:
void refreshButtons();
void refreshEnableNotifications();
void refreshManageAdminsButton();
void refreshManageBlockedUsersButton();
void refreshInviteLinkButton();
object_ptr<Ui::Checkbox> _enableNotifications;
@ -62,6 +64,7 @@ private:
// In groups: creator of non-deactivated groups can see this link.
// In channels: creator of supergroup can see this link.
object_ptr<Ui::LeftOutlineButton> _manageAdmins = { nullptr };
object_ptr<Ui::LeftOutlineButton> _manageBlockedUsers = { nullptr };
object_ptr<Ui::LeftOutlineButton> _inviteLink = { nullptr };
};

View File

@ -717,6 +717,13 @@ void ChannelData::setAdminsCount(int newAdminsCount) {
}
}
void ChannelData::setKickedCount(int newKickedCount) {
if (_kickedCount != newKickedCount) {
_kickedCount = newKickedCount;
Notify::peerUpdatedDelayed(this, Notify::PeerUpdate::Flag::BlockedUsersChanged);
}
}
void ChannelData::flagsUpdated() {
if (isMegagroup()) {
if (!mgInfo) {

View File

@ -740,6 +740,11 @@ public:
}
void setAdminsCount(int newAdminsCount);
int kickedCount() const {
return _kickedCount;
}
void setKickedCount(int newKickedCount);
int32 date = 0;
int version = 0;
MTPDchannel::Flags flags = { 0 };
@ -880,6 +885,7 @@ private:
int _membersCount = 1;
int _adminsCount = 1;
int _kickedCount = 0;
QString _restrictionReason;
QString _about;