Add admins from search in admins list box.

When searching in admins list box show all group members and allow
to appoint a new administrator right from this box.
This commit is contained in:
John Preston 2017-07-19 11:18:20 +03:00
parent 90311dbf24
commit ed4c3cccb2
6 changed files with 142 additions and 53 deletions

View File

@ -640,7 +640,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_report_button" = "Report";
"lng_report_thanks" = "Thank you! Your report will be reviewed by our team very soon.";
"lng_channel_add_admins" = "New administrator";
"lng_channel_add_members" = "Add members";
"lng_channel_add_banned" = "Ban user";
"lng_channel_add_restricted" = "Restrict user";
@ -652,6 +651,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_channel_admins_too_much" = "Sorry, you have reached the limit of the administrators. Please remove one administrator first.";
"lng_channel_admin_status_creator" = "Creator";
"lng_channel_admin_status_promoted_by" = "Promoted by {user}";
"lng_channel_admin_status_not_admin" = "Not administrator";
"lng_group_blocked_list_about" = "Banned users are removed from the group and can only come back if invited by an admin.\nInvite links don't work for them.";
@ -1405,6 +1405,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_topbar_info" = "Info";
"lng_profile_group_info" = "Group info";
"lng_profile_channel_info" = "Channel info";
"lng_channel_add_admins" = "New administrator";
// Wnd specific

View File

@ -233,12 +233,13 @@ MTPChannelAdminRights EditAdminBox::DefaultRights(gsl::not_null<ChannelData*> ch
void EditAdminBox::prepare() {
EditParticipantBox::prepare();
setTitle(langFactory(lng_rights_edit_admin));
auto hadRights = _oldRights.c_channelAdminRights().vflags.v;
setTitle(langFactory(hadRights ? lng_rights_edit_admin : lng_channel_add_admin));
addControl(object_ptr<Divider>(this), QMargins());
addControl(object_ptr<Ui::FlatLabel>(this, lang(lng_rights_edit_admin_header), Ui::FlatLabel::InitType::Simple, st::rightsHeaderLabel), st::rightsHeaderMargin);
auto prepareRights = (_oldRights.c_channelAdminRights().vflags.v ? _oldRights : DefaultRights(channel()));
auto prepareRights = (hadRights ? _oldRights : DefaultRights(channel()));
auto addCheckbox = [this, &prepareRights](Flags flags, const QString &text) {
auto checked = (prepareRights.c_channelAdminRights().vflags.v & flags) != 0;
auto control = addControl(object_ptr<Ui::Checkbox>(this, text, checked, st::rightsCheckbox, st::rightsToggle), st::rightsToggleMargin);

View File

@ -145,6 +145,10 @@ void PeerListBox::peerListPrependRow(std::unique_ptr<PeerListRow> row) {
_inner->prependRow(std::move(row));
}
void PeerListBox::peerListPrependRowFromSearchResult(gsl::not_null<PeerListRow*> row) {
_inner->prependRowFromSearchResult(row);
}
PeerListRow *PeerListBox::peerListFindRow(PeerListRowId id) {
return _inner->findRow(id);
}
@ -157,6 +161,10 @@ void PeerListBox::peerListRemoveRow(gsl::not_null<PeerListRow*> row) {
_inner->removeRow(row);
}
void PeerListBox::peerListConvertRowToSearchResult(gsl::not_null<PeerListRow*> row) {
_inner->convertRowToSearchResult(row);
}
void PeerListBox::peerListSetRowChecked(gsl::not_null<PeerListRow*> row, bool checked) {
auto peer = row->peer();
if (checked) {
@ -203,7 +211,7 @@ void PeerListBox::peerListSetSearchNoResults(object_ptr<Ui::FlatLabel> noResults
void PeerListBox::peerListSetSearchMode(PeerListSearchMode mode) {
_inner->setSearchMode(mode);
if (mode != PeerListSearchMode::None && !_select) {
if (mode != PeerListSearchMode::Disabled && !_select) {
_select = createMultiSelect();
_select->entity()->setSubmittedCallback([this](bool chtrlShiftEnter) { _inner->submitted(); });
_select->entity()->setQueryChangedCallback([this](const QString &query) { searchQueryChanged(query); });
@ -219,7 +227,7 @@ void PeerListBox::peerListSetSearchMode(PeerListSearchMode mode) {
_select->moveToLeft(0, 0);
}
if (_select) {
_select->toggleAnimated(mode != PeerListSearchMode::None);
_select->toggleAnimated(mode != PeerListSearchMode::Disabled);
}
}
@ -245,8 +253,12 @@ PeerListController::PeerListController(std::unique_ptr<PeerListSearchController>
}
}
bool PeerListController::hasComplexSearch() const {
return (_searchController != nullptr);
}
void PeerListController::search(const QString &query) {
Expects(_searchController != nullptr);
Expects(hasComplexSearch());
_searchController->searchQuery(query);
}
@ -534,7 +546,7 @@ void PeerListBox::Inner::addRowEntry(gsl::not_null<PeerListRow*> row) {
if (addingToSearchIndex()) {
addToSearchIndex(row);
}
if (_searchMode != PeerListSearchMode::None) {
if (_searchMode != PeerListSearchMode::Disabled) {
t_assert(row->id() == row->peer()->id);
if (_controller->isRowSelected(row->peer())) {
changeCheckState(row, true, PeerListRow::SetStyle::Fast);
@ -553,7 +565,7 @@ void PeerListBox::Inner::invalidatePixmapsCache() {
bool PeerListBox::Inner::addingToSearchIndex() const {
// If we started indexing already, we continue.
return (_searchMode != PeerListSearchMode::None) || !_searchIndex.empty();
return (_searchMode != PeerListSearchMode::Disabled) || !_searchIndex.empty();
}
void PeerListBox::Inner::addToSearchIndex(gsl::not_null<PeerListRow*> row) {
@ -594,6 +606,25 @@ void PeerListBox::Inner::prependRow(std::unique_ptr<PeerListRow> row) {
}
}
void PeerListBox::Inner::prependRowFromSearchResult(gsl::not_null<PeerListRow*> row) {
if (!row->isSearchResult()) {
return;
}
t_assert(_rowsById.find(row->id()) != _rowsById.cend());
auto index = row->absoluteIndex();
t_assert(index >= 0 && index < _searchRows.size());
t_assert(_searchRows[index].get() == row);
row->setIsSearchResult(false);
_rows.insert(_rows.begin(), std::move(_searchRows[index]));
refreshIndices();
removeRowAtIndex(_searchRows, index);
if (addingToSearchIndex()) {
addToSearchIndex(row);
}
}
void PeerListBox::Inner::refreshIndices() {
auto index = 0;
for (auto &row : _rows) {
@ -601,6 +632,13 @@ void PeerListBox::Inner::refreshIndices() {
}
}
void PeerListBox::Inner::removeRowAtIndex(std::vector<std::unique_ptr<PeerListRow>> &from, int index) {
from.erase(from.begin() + index);
for (auto i = index, count = int(from.size()); i != count; ++i) {
from[i]->setAbsoluteIndex(i);
}
}
PeerListRow *PeerListBox::Inner::findRow(PeerListRowId id) {
auto it = _rowsById.find(id);
return (it == _rowsById.cend()) ? nullptr : it->second.get();
@ -622,14 +660,28 @@ void PeerListBox::Inner::removeRow(gsl::not_null<PeerListRow*> row) {
byPeer.erase(std::remove(byPeer.begin(), byPeer.end(), row), byPeer.end());
removeFromSearchIndex(row);
_filterResults.erase(std::find(_filterResults.begin(), _filterResults.end(), row), _filterResults.end());
eraseFrom.erase(eraseFrom.begin() + index);
for (auto i = index, count = int(eraseFrom.size()); i != count; ++i) {
eraseFrom[i]->setAbsoluteIndex(i);
}
removeRowAtIndex(eraseFrom, index);
restoreSelection();
}
void PeerListBox::Inner::convertRowToSearchResult(gsl::not_null<PeerListRow*> row) {
if (row->isSearchResult()) {
return;
} else if (!showingSearch() || !_controller->hasComplexSearch()) {
return removeRow(row);
}
auto index = row->absoluteIndex();
t_assert(index >= 0 && index < _rows.size());
t_assert(_rows[index].get() == row);
removeFromSearchIndex(row);
row->setIsSearchResult(true);
row->setAbsoluteIndex(_searchRows.size());
_searchRows.push_back(std::move(_rows[index]));
removeRowAtIndex(_rows, index);
}
int PeerListBox::Inner::fullRowsCount() const {
return _rows.size();
}
@ -709,7 +761,7 @@ void PeerListBox::Inner::setSearchMode(PeerListSearchMode mode) {
}
}
_searchMode = mode;
if (_searchMode == PeerListSearchMode::Complex) {
if (_controller->hasComplexSearch()) {
if (!_searchLoading) {
setSearchLoading(object_ptr<Ui::FlatLabel>(this, lang(lng_contacts_loading), Ui::FlatLabel::InitType::Simple, st::membersAbout));
}
@ -1027,7 +1079,7 @@ void PeerListBox::Inner::searchQueryChanged(QString query) {
}
}
}
if (_searchMode == PeerListSearchMode::Complex) {
if (_controller->hasComplexSearch()) {
_controller->search(_searchQuery);
}
refreshRows();
@ -1314,7 +1366,7 @@ ChatsListBoxController::ChatsListBoxController(std::unique_ptr<PeerListSearchCon
void ChatsListBoxController::prepare() {
setSearchNoResultsText(lang(lng_blocked_list_not_found));
delegate()->peerListSetSearchMode(PeerListSearchMode::Complex);
delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
prepareViewHook();

View File

@ -163,9 +163,8 @@ private:
};
enum class PeerListSearchMode {
None,
Local,
Complex,
Disabled,
Enabled,
};
class PeerListDelegate {
@ -179,9 +178,11 @@ public:
virtual void peerListAppendSearchRow(std::unique_ptr<PeerListRow> row) = 0;
virtual void peerListAppendFoundRow(gsl::not_null<PeerListRow*> row) = 0;
virtual void peerListPrependRow(std::unique_ptr<PeerListRow> row) = 0;
virtual void peerListPrependRowFromSearchResult(gsl::not_null<PeerListRow*> row) = 0;
virtual void peerListUpdateRow(gsl::not_null<PeerListRow*> row) = 0;
virtual bool peerListIsRowSelected(gsl::not_null<PeerData*> peer) = 0;
virtual void peerListRemoveRow(gsl::not_null<PeerListRow*> row) = 0;
virtual void peerListConvertRowToSearchResult(gsl::not_null<PeerListRow*> row) = 0;
virtual bool peerListIsRowSelected(gsl::not_null<PeerData*> peer) = 0;
virtual void peerListSetRowChecked(gsl::not_null<PeerListRow*> row, bool checked) = 0;
virtual gsl::not_null<PeerListRow*> peerListRowAt(int index) = 0;
virtual void peerListRefreshRows() = 0;
@ -267,6 +268,7 @@ public:
virtual bool searchInLocal() {
return true;
}
bool hasComplexSearch() const;
void search(const QString &query);
void peerListSearchAddRow(gsl::not_null<PeerData*> peer) override;
@ -316,8 +318,10 @@ public:
void peerListAppendSearchRow(std::unique_ptr<PeerListRow> row) override;
void peerListAppendFoundRow(gsl::not_null<PeerListRow*> row) override;
void peerListPrependRow(std::unique_ptr<PeerListRow> row) override;
void peerListPrependRowFromSearchResult(gsl::not_null<PeerListRow*> row) override;
void peerListUpdateRow(gsl::not_null<PeerListRow*> row) override;
void peerListRemoveRow(gsl::not_null<PeerListRow*> row) override;
void peerListConvertRowToSearchResult(gsl::not_null<PeerListRow*> row) override;
void peerListSetRowChecked(gsl::not_null<PeerListRow*> row, bool checked) override;
gsl::not_null<PeerListRow*> peerListRowAt(int index) override;
bool peerListIsRowSelected(gsl::not_null<PeerData*> peer) override;
@ -381,11 +385,13 @@ public:
void appendSearchRow(std::unique_ptr<PeerListRow> row);
void appendFoundRow(gsl::not_null<PeerListRow*> row);
void prependRow(std::unique_ptr<PeerListRow> row);
void prependRowFromSearchResult(gsl::not_null<PeerListRow*> row);
PeerListRow *findRow(PeerListRowId id);
void updateRow(gsl::not_null<PeerListRow*> row) {
updateRow(row, RowIndex());
}
void removeRow(gsl::not_null<PeerListRow*> row);
void convertRowToSearchResult(gsl::not_null<PeerListRow*> row);
int fullRowsCount() const;
gsl::not_null<PeerListRow*> rowAt(int index) const;
void setDescription(object_ptr<Ui::FlatLabel> description);
@ -422,6 +428,7 @@ protected:
private:
void refreshIndices();
void removeRowAtIndex(std::vector<std::unique_ptr<PeerListRow>> &from, int index);
void invalidatePixmapsCache();
@ -494,7 +501,7 @@ private:
void clearSearchRows();
gsl::not_null<PeerListController*> _controller;
PeerListSearchMode _searchMode = PeerListSearchMode::None;
PeerListSearchMode _searchMode = PeerListSearchMode::Disabled;
int _rowHeight = 0;
int _visibleTop = 0;

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<ParticipantsBoxSearchController>(channel, role, &_additional))
ParticipantsBoxController::ParticipantsBoxController(gsl::not_null<ChannelData*> channel, Role role) : PeerListController(CreateSearchController(channel, role, &_additional))
, _channel(channel)
, _role(role) {
if (_channel->mgInfo) {
@ -45,6 +45,14 @@ ParticipantsBoxController::ParticipantsBoxController(gsl::not_null<ChannelData*>
}
}
std::unique_ptr<PeerListSearchController> ParticipantsBoxController::CreateSearchController(gsl::not_null<ChannelData*> channel, Role role, gsl::not_null<Additional*> additional) {
// In admins box complex search is used for adding new admins.
if (role != Role::Admins || channel->canAddAdmins()) {
return std::make_unique<ParticipantsBoxSearchController>(channel, role, additional);
}
return nullptr;
}
void ParticipantsBoxController::Start(gsl::not_null<ChannelData*> channel, Role role) {
auto controller = std::make_unique<ParticipantsBoxController>(channel, role);
auto initBox = [role, channel, controller = controller.get()](PeerListBox *box) {
@ -103,7 +111,6 @@ void ParticipantsBoxController::addNewItem() {
}
void ParticipantsBoxController::peerListSearchAddRow(gsl::not_null<PeerData*> peer) {
Expects(_role != Role::Admins);
PeerListController::peerListSearchAddRow(peer);
if (_role == Role::Restricted && delegate()->peerListFullRowsCount() > 0) {
setDescriptionText(QString());
@ -168,19 +175,17 @@ void ParticipantsBoxController::HandleParticipant(const MTPChannelParticipant &p
}
void ParticipantsBoxController::prepare() {
if (_role == Role::Admins) {
delegate()->peerListSetSearchMode(PeerListSearchMode::Local);
delegate()->peerListSetTitle(langFactory(lng_channel_admins));
} else {
delegate()->peerListSetSearchMode(PeerListSearchMode::Complex);
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));
auto titleKey = [this] {
switch (_role) {
case Role::Admins: return lng_channel_admins;
case Role::Members: return lng_profile_participants_section;
case Role::Restricted: return lng_restricted_list_title;
case Role::Kicked: return lng_banned_list_title;
}
}
Unexpected("Role in ParticipantsBoxController::prepare()");
};
delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
delegate()->peerListSetTitle(langFactory(titleKey()));
setDescriptionText(lang(lng_contacts_loading));
setSearchNoResultsText(lang(lng_blocked_list_not_found));
@ -317,17 +322,16 @@ void ParticipantsBoxController::rowActionClicked(gsl::not_null<PeerListRow*> row
void ParticipantsBoxController::showAdmin(gsl::not_null<UserData*> user) {
auto it = _additional.adminRights.find(user);
if (it == _additional.adminRights.cend()) {
if (user != _additional.creator) {
return;
}
}
auto currentRights = (user == _additional.creator)
auto isCreator = (user == _additional.creator);
auto notAdmin = !isCreator && (it == _additional.adminRights.cend());
auto currentRights = isCreator
? MTP_channelAdminRights(MTP_flags(~MTPDchannelAdminRights::Flag::f_add_admins | MTPDchannelAdminRights::Flag::f_add_admins))
: it->second;
: notAdmin ? MTP_channelAdminRights(MTP_flags(0)) : it->second;
auto weak = base::weak_unique_ptr<ParticipantsBoxController>(this);
auto box = Box<EditAdminBox>(_channel, user, currentRights);
if (_additional.adminCanEdit.find(user) != _additional.adminCanEdit.end()) {
auto canEdit = (_additional.adminCanEdit.find(user) != _additional.adminCanEdit.end());
auto canSave = notAdmin ? _channel->canAddAdmins() : canEdit;
if (canSave) {
box->setSaveCallback([channel = _channel.get(), user, weak](const MTPChannelAdminRights &oldRights, const MTPChannelAdminRights &newRights) {
MTP::send(MTPchannels_EditAdmin(channel->inputChannel, user->inputUser, newRights), rpcDone([channel, user, weak, oldRights, newRights](const MTPUpdates &result) {
AuthSession::Current().api().applyUpdates(result);
@ -477,7 +481,12 @@ bool ParticipantsBoxController::appendRow(gsl::not_null<UserData*> user) {
}
bool ParticipantsBoxController::prependRow(gsl::not_null<UserData*> user) {
if (delegate()->peerListFindRow(user->id)) {
if (auto row = delegate()->peerListFindRow(user->id)) {
if (_role == Role::Admins) {
// Perhaps we've added a new admin from search.
refreshAdminCustomStatus(row);
delegate()->peerListPrependRowFromSearchResult(row);
}
return false;
}
delegate()->peerListPrependRow(createRow(user));
@ -489,7 +498,13 @@ bool ParticipantsBoxController::prependRow(gsl::not_null<UserData*> user) {
bool ParticipantsBoxController::removeRow(gsl::not_null<UserData*> user) {
if (auto row = delegate()->peerListFindRow(user->id)) {
delegate()->peerListRemoveRow(row);
if (_role == Role::Admins) {
// Perhaps we are removing an admin from search results.
row->setCustomStatus(lang(lng_channel_admin_status_not_admin));
delegate()->peerListConvertRowToSearchResult(row);
} else {
delegate()->peerListRemoveRow(row);
}
if (!delegate()->peerListFullRowsCount()) {
setDescriptionText(lang(lng_blocked_list_not_found));
}
@ -501,12 +516,7 @@ bool ParticipantsBoxController::removeRow(gsl::not_null<UserData*> user) {
std::unique_ptr<PeerListRow> ParticipantsBoxController::createRow(gsl::not_null<UserData*> user) const {
auto row = std::make_unique<PeerListRowWithLink>(user);
if (_role == Role::Admins) {
auto promotedBy = _additional.adminPromotedBy.find(user);
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)));
}
refreshAdminCustomStatus(row.get());
}
if (_role == Role::Restricted || (_role == Role::Admins && _additional.adminCanEdit.find(user) != _additional.adminCanEdit.cend())) {
// row->setActionLink(lang(lng_profile_edit_permissions));
@ -522,11 +532,24 @@ std::unique_ptr<PeerListRow> ParticipantsBoxController::createRow(gsl::not_null<
return std::move(row);
}
void ParticipantsBoxController::refreshAdminCustomStatus(gsl::not_null<PeerListRow*> row) const {
auto user = row->peer()->asUser();
auto promotedBy = _additional.adminPromotedBy.find(user);
if (promotedBy == _additional.adminPromotedBy.cend()) {
if (user == _additional.creator) {
row->setCustomStatus(lang(lng_channel_admin_status_creator));
} else {
row->setCustomStatus(lang(lng_channel_admin_status_not_admin));
}
} else {
row->setCustomStatus(lng_channel_admin_status_promoted_by(lt_user, App::peerName(promotedBy->second)));
}
}
ParticipantsBoxSearchController::ParticipantsBoxSearchController(gsl::not_null<ChannelData*> channel, Role role, gsl::not_null<Additional*> additional)
: _channel(channel)
, _role(role)
, _additional(additional) {
Expects(role != Role::Admins);
_timer.setCallback([this] { searchOnServer(); });
}
@ -570,6 +593,7 @@ bool ParticipantsBoxSearchController::loadMoreRows() {
if (!_allLoaded && !isLoading()) {
auto filter = [this] {
switch (_role) {
case Role::Admins: // Search for members, appoint as admin on found.
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));
@ -627,8 +651,9 @@ void ParticipantsBoxSearchController::searchDone(mtpRequestId requestId, const M
// wait for an empty results list unlike the non-search peer list.
_allLoaded = true;
}
auto parseRole = (_role == Role::Admins) ? Role::Members : _role;
for_const (auto &participant, list) {
ParticipantsBoxController::HandleParticipant(participant, _role, _additional, [this](gsl::not_null<UserData*> user) {
ParticipantsBoxController::HandleParticipant(participant, parseRole, _additional, [this](gsl::not_null<UserData*> user) {
delegate()->peerListSearchAddRow(user);
});
}
@ -657,7 +682,7 @@ std::unique_ptr<PeerListRow> AddParticipantBoxController::createSearchRow(gsl::n
}
void AddParticipantBoxController::prepare() {
delegate()->peerListSetSearchMode(PeerListSearchMode::Complex);
delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
auto title = [this] {
switch (_role) {
case Role::Admins: return langFactory(lng_channel_add_admin);

View File

@ -65,6 +65,8 @@ public:
static void HandleParticipant(const MTPChannelParticipant &participant, Role role, gsl::not_null<Additional*> additional, Callback callback);
private:
static std::unique_ptr<PeerListSearchController> CreateSearchController(gsl::not_null<ChannelData*> channel, Role role, gsl::not_null<Additional*> additional);
void showAdmin(gsl::not_null<UserData*> user);
void editAdminDone(gsl::not_null<UserData*> user, const MTPChannelAdminRights &rights);
void showRestricted(gsl::not_null<UserData*> user);
@ -76,6 +78,7 @@ private:
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;
void refreshAdminCustomStatus(gsl::not_null<PeerListRow*> row) const;
bool feedMegagroupLastParticipants();
gsl::not_null<ChannelData*> _channel;